Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

icache: Add ICACHE management #22

Merged
merged 2 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved
}

// 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