Skip to content

Commit

Permalink
icache: Add ICACHE management
Browse files Browse the repository at this point in the history
  • Loading branch information
astapleton committed Oct 8, 2024
1 parent 526ceba commit 4e781e4
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 0 deletions.
200 changes: 200 additions & 0 deletions src/icache.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>() -> &'static str {
let s = core::any::type_name::<T>();
Expand Down
1 change: 1 addition & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down

0 comments on commit 4e781e4

Please sign in to comment.