From fe615a1933be45e197205feb2e4c28a0187eca64 Mon Sep 17 00:00:00 2001 From: astapleton Date: Tue, 14 May 2024 16:41:03 -0700 Subject: [PATCH] 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..105b5e2 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,6 @@ //! Prelude +pub use crate::icache::ICacheExt as _stm32h5xx_hal_delay_ICacheExt; pub use crate::gpio::GpioExt as _stm32h5xx_hal_gpio_GpioExt; pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt; pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt;