From 4e781e4720dbcaaf7d682b6b82c9b99ba5a72362 Mon Sep 17 00:00:00 2001 From: astapleton Date: Tue, 14 May 2024 16:41:03 -0700 Subject: [PATCH 1/2] icache: Add ICACHE management --- src/icache.rs | 200 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/prelude.rs | 1 + 3 files changed, 204 insertions(+) create mode 100644 src/icache.rs diff --git a/src/icache.rs b/src/icache.rs new file mode 100644 index 0000000..3dcaded --- /dev/null +++ b/src/icache.rs @@ -0,0 +1,200 @@ +//! Instruction Cache management +//! +//! To enable the the ICACHE: +//! +//! ``` +//! let dp = ...; // Device peripherals +//! let mut icache = dp.ICACHE.constrain(); +//! +//! icache.enable(); +//! ``` +//! +//! To invalidate the cache, use the ICache::invalidate() method: +//! +//! ``` +//! icache.invalidate(); +//! ``` +//! +//! Performance monitoring of the cache is possible using the cache hit and +//! miss counters: +//! +//! ``` +//! icache.enable_hit_counter(); +//! icache.enable_miss_counter(); +//! +//! // do something +//! +//! let hit_count = icache.hit_count(); +//! let miss_count = icache.miss_count(); +//! ``` + +use crate::stm32::ICACHE; + +pub trait ICacheExt { + fn constrain(self) -> ICache; +} + +impl ICacheExt for ICACHE { + fn constrain(self) -> ICache { + ICache::new(self) + } +} + +pub struct ICache { + icache: ICACHE, +} + +impl ICache { + fn new(icache: ICACHE) -> Self { + Self { icache } + } + + pub fn is_enabled(&self) -> bool { + cortex_m::asm::dsb(); + cortex_m::asm::isb(); + + self.icache.cr().read().en().bit_is_set() + } + + fn wait_for_busy_complete(&self) { + while self.icache.sr().read().busyf().bit_is_set() {} + } + + /// Enable ICACHE in default operating mode (N-way associative) + pub fn enable(&mut self) { + self.enable_n_way() + } + + /// Enable the ICACHE in N-way associative mode + pub fn enable_n_way(&mut self) { + if self.is_enabled() { + return; + } + + // Wait for any ongoing cache invalidation operation to complete + self.wait_for_busy_complete(); + + self.icache + .cr() + .write(|w| w.waysel().set_bit().en().set_bit()); + } + + /// Enable the ICACHE in 1-way associative mode (direct mapping) + pub fn enable_direct_mapped(&mut self) { + if self.is_enabled() { + return; + } + + // Wait for any ongoing cache invalidation operation to complete + self.wait_for_busy_complete(); + + self.icache + .cr() + .write(|w| w.waysel().clear_bit().en().set_bit()); + } + + /// Disable the ICACHE + pub fn disable(&mut self) { + if !self.is_enabled() { + return; + } + + // Restore cache mode to default (N-way associative) + self.icache + .cr() + .write(|w| w.waysel().set_bit().en().clear_bit()); + + self.invalidate(); + + cortex_m::asm::dsb(); + cortex_m::asm::isb(); + } + + /// Invalidate the ICACHE. This will block while the cache is being + /// invalidated + pub fn invalidate(&mut self) { + self.icache.cr().modify(|_, w| w.cacheinv().set_bit()); + + self.wait_for_busy_complete(); + + cortex_m::asm::dsb(); + cortex_m::asm::isb(); + } + + /// Enable the cache hit counter. The number of hits can be queried with ICache::hit_counter() + pub fn enable_hit_counter(&mut self) { + assert!( + self.is_enabled(), + "ICACHE must be enabled before enabling the hit counter" + ); + + // Enable and reset the cache hit counter + self.icache + .cr() + .modify(|_, w| w.hitmrst().set_bit().hitmen().set_bit()); + } + + /// Get the current value of the hit counter + pub fn hit_count(&self) -> u32 { + self.icache.hmonr().read().hitmon().bits() + } + + /// Reset the hit counter + pub fn reset_hit_counter(&mut self) { + if !self.is_enabled() { + return; + } + self.icache.cr().modify(|_, w| w.hitmrst().set_bit()); + } + + /// Disable the hit counter. The hit counter is disabled when the peripheral + /// is disabled + pub fn disable_hit_counter(&mut self) { + // Disabling the ICACHE disables the hitmem counter + if !self.is_enabled() { + return; + } + self.icache.cr().modify(|_, w| w.hitmen().clear_bit()); + } + + /// Enable the cache miss counter + pub fn enable_miss_counter(&mut self) { + assert!( + self.is_enabled(), + "ICACHE must be enabled before enabling the miss counter" + ); + + // Enable and reset the miss counter + self.icache + .cr() + .modify(|_, w| w.missmrst().set_bit().missmen().set_bit()); + } + + /// Get the current value of the miss counter + pub fn miss_count(&self) -> u16 { + self.icache.mmonr().read().missmon().bits() + } + + /// Reset the miss counter + pub fn reset_miss_counter(&mut self) { + if !self.is_enabled() { + return; + } + self.icache.cr().modify(|_, w| w.missmrst().set_bit()); + } + + /// Disable the miss counter. The miss counter is disabled when the ICACHE + /// is disabled + pub fn disable_miss_counter(&mut self) { + // Disabling the ICACHE disables the missmem counter + if !self.is_enabled() { + return; + } + self.icache.cr().modify(|_, w| w.missmen().clear_bit()); + } + + // Deconstruct and return ICACHE + pub fn free(self) -> ICACHE { + self.icache + } +} diff --git a/src/lib.rs b/src/lib.rs index 83b330a..1905f2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ pub mod rcc; #[cfg(feature = "device-selected")] pub mod gpio; +#[cfg(feature = "device-selected")] +pub mod icache; + /// Get the name of the type without the module prefix(es) fn stripped_type_name() -> &'static str { let s = core::any::type_name::(); diff --git a/src/prelude.rs b/src/prelude.rs index e2fdf8b..3b63ac3 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,6 +1,7 @@ //! Prelude pub use crate::gpio::GpioExt as _stm32h5xx_hal_gpio_GpioExt; +pub use crate::icache::ICacheExt as _stm32h5xx_hal_delay_ICacheExt; pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt; pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt; From d72975ab0efd02970523f3735311850d662efc8c Mon Sep 17 00:00:00 2001 From: astapleton Date: Fri, 11 Oct 2024 15:53:16 -0700 Subject: [PATCH 2/2] Rework ICachce API --- src/icache.rs | 164 +++++++++++++++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 63 deletions(-) diff --git a/src/icache.rs b/src/icache.rs index 3dcaded..8590595 100644 --- a/src/icache.rs +++ b/src/icache.rs @@ -1,12 +1,17 @@ //! Instruction Cache management //! -//! To enable the the ICACHE: +//! To enable the ICACHE in the default N-way set associative mode: //! //! ``` //! let dp = ...; // Device peripherals -//! let mut icache = dp.ICACHE.constrain(); +//! let mut icache = dp.ICACHE.enable(); +//! ``` +//! +//! To enable the ICACHE in a specific mode (e.g. Direct-mapped): //! -//! icache.enable(); +//! ``` +//! let dp = ...; // Device peripherals +//! let mut icache = dp.ICACHE.enable_direct_mapped(); //! ``` //! //! To invalidate the cache, use the ICache::invalidate() method: @@ -27,16 +32,47 @@ //! let hit_count = icache.hit_count(); //! let miss_count = icache.miss_count(); //! ``` +//! +//! Using interrupts to trigger cache invalidation and determine when it is +//! complete: +//! +//! ``` +//! icache.start_invalidation(); +//! +//! // In interrupt context +//! if icache.check_event(Event::CacheInvalidationFinished) { +//! icache.clear_irq(Event::CacheInvalidationFinished) +//! } +//! ``` +//! use crate::stm32::ICACHE; -pub trait ICacheExt { - fn constrain(self) -> ICache; +pub enum Event { + CacheError, + CacheInvalidationFinished, +} + +pub trait ICacheExt: Sized { + /// Enable in N-way set associative mode (default) + fn enable_n_way(self) -> ICache; + + /// Enable in direct mapped mode + fn enable_direct_mapped(self) -> ICache; + + /// Enable in default mode (N-way set associative) + fn enable(self) -> ICache { + self.enable_n_way() + } } impl ICacheExt for ICACHE { - fn constrain(self) -> ICache { - ICache::new(self) + fn enable_n_way(self) -> ICache { + ICache::new(self).enable_n_way() + } + + fn enable_direct_mapped(self) -> ICache { + ICache::new(self).enable_direct_mapped() } } @@ -49,71 +85,58 @@ impl ICache { Self { icache } } - pub fn is_enabled(&self) -> bool { - cortex_m::asm::dsb(); - cortex_m::asm::isb(); - - self.icache.cr().read().en().bit_is_set() - } - fn wait_for_busy_complete(&self) { while self.icache.sr().read().busyf().bit_is_set() {} } - /// Enable ICACHE in default operating mode (N-way associative) - pub fn enable(&mut self) { - self.enable_n_way() - } - /// Enable the ICACHE in N-way associative mode - pub fn enable_n_way(&mut self) { - if self.is_enabled() { - return; - } - + fn enable_n_way(self) -> Self { // Wait for any ongoing cache invalidation operation to complete self.wait_for_busy_complete(); self.icache .cr() .write(|w| w.waysel().set_bit().en().set_bit()); + + self } /// Enable the ICACHE in 1-way associative mode (direct mapping) - pub fn enable_direct_mapped(&mut self) { - if self.is_enabled() { - return; - } - + fn enable_direct_mapped(self) -> Self { // Wait for any ongoing cache invalidation operation to complete self.wait_for_busy_complete(); self.icache .cr() .write(|w| w.waysel().clear_bit().en().set_bit()); + + self } /// Disable the ICACHE - pub fn disable(&mut self) { - if !self.is_enabled() { - return; - } - + pub fn disable(mut self) -> ICACHE { // Restore cache mode to default (N-way associative) self.icache .cr() .write(|w| w.waysel().set_bit().en().clear_bit()); + // Disable interrupts + self.icache + .ier() + .modify(|_, w| w.errie().clear_bit().bsyendie().clear_bit()); + self.invalidate(); cortex_m::asm::dsb(); cortex_m::asm::isb(); + + self.free() } /// Invalidate the ICACHE. This will block while the cache is being /// invalidated pub fn invalidate(&mut self) { - self.icache.cr().modify(|_, w| w.cacheinv().set_bit()); + self.start_cache_invalidation(); self.wait_for_busy_complete(); @@ -121,13 +144,47 @@ impl ICache { cortex_m::asm::isb(); } - /// Enable the cache hit counter. The number of hits can be queried with ICache::hit_counter() - pub fn enable_hit_counter(&mut self) { - assert!( - self.is_enabled(), - "ICACHE must be enabled before enabling the hit counter" - ); + /// Start cache invalidation + pub fn start_cache_invalidation(&mut self) { + self.icache.cr().modify(|_, w| w.cacheinv().set_bit()); + } + + /// Enable interrupts for the given event + pub fn listen(&mut self, event: Event) { + self.icache.ier().modify(|_, w| match event { + Event::CacheError => w.errie().set_bit(), + Event::CacheInvalidationFinished => w.bsyendie().set_bit(), + }); + } + + /// Disable interrupts for the given event + pub fn unlisten(&mut self, event: Event) { + self.icache.ier().modify(|_, w| match event { + Event::CacheError => w.errie().clear_bit(), + Event::CacheInvalidationFinished => w.bsyendie().clear_bit(), + }); + } + + /// Clear the IRQ for the given event + pub fn clear_irq(&mut self, event: Event) { + self.icache.fcr().write(|w| match event { + Event::CacheError => w.cerrf().set_bit(), + Event::CacheInvalidationFinished => w.cbsyendf().set_bit(), + }); + } + + /// Check whether an interrupt event has occurred. Returns true if it has. + /// Clear the event IRQ by calling `clear_event` + pub fn check_event(&mut self, event: Event) -> bool { + let sr = self.icache.sr().read(); + match event { + Event::CacheError => sr.errf().bit_is_set(), + Event::CacheInvalidationFinished => sr.bsyendf().bit_is_set(), + } + } + /// Enable the cache hit counter. The number of hits can be queried with ICache::hit_counter() + pub fn start_hit_counter(&mut self) { // Enable and reset the cache hit counter self.icache .cr() @@ -141,29 +198,17 @@ impl ICache { /// Reset the hit counter pub fn reset_hit_counter(&mut self) { - if !self.is_enabled() { - return; - } self.icache.cr().modify(|_, w| w.hitmrst().set_bit()); } /// Disable the hit counter. The hit counter is disabled when the peripheral /// is disabled - pub fn disable_hit_counter(&mut self) { - // Disabling the ICACHE disables the hitmem counter - if !self.is_enabled() { - return; - } + pub fn stop_hit_counter(&mut self) { self.icache.cr().modify(|_, w| w.hitmen().clear_bit()); } /// Enable the cache miss counter - pub fn enable_miss_counter(&mut self) { - assert!( - self.is_enabled(), - "ICACHE must be enabled before enabling the miss counter" - ); - + pub fn start_miss_counter(&mut self) { // Enable and reset the miss counter self.icache .cr() @@ -177,19 +222,12 @@ impl ICache { /// Reset the miss counter pub fn reset_miss_counter(&mut self) { - if !self.is_enabled() { - return; - } self.icache.cr().modify(|_, w| w.missmrst().set_bit()); } /// Disable the miss counter. The miss counter is disabled when the ICACHE /// is disabled - pub fn disable_miss_counter(&mut self) { - // Disabling the ICACHE disables the missmem counter - if !self.is_enabled() { - return; - } + pub fn stop_miss_counter(&mut self) { self.icache.cr().modify(|_, w| w.missmen().clear_bit()); }