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()); }