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 all commits
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
238 changes: 238 additions & 0 deletions src/icache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
//! Instruction Cache management
//!
//! To enable the ICACHE in the default N-way set associative mode:
//!
//! ```
//! let dp = ...; // Device peripherals
//! let mut icache = dp.ICACHE.enable();
//! ```
//!
//! To enable the ICACHE in a specific mode (e.g. Direct-mapped):
//!
//! ```
//! let dp = ...; // Device peripherals
//! let mut icache = dp.ICACHE.enable_direct_mapped();
//! ```
//!
//! 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();
//! ```
//!
//! 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 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 enable_n_way(self) -> ICache {
ICache::new(self).enable_n_way()
}

fn enable_direct_mapped(self) -> ICache {
ICache::new(self).enable_direct_mapped()
}
}

pub struct ICache {
icache: ICACHE,
}

impl ICache {
fn new(icache: ICACHE) -> Self {
Self { icache }
}

fn wait_for_busy_complete(&self) {
while self.icache.sr().read().busyf().bit_is_set() {}
}

/// Enable the ICACHE in N-way associative mode
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)
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) -> 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.start_cache_invalidation();

self.wait_for_busy_complete();

cortex_m::asm::dsb();
cortex_m::asm::isb();
}

/// 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()
.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) {
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 stop_hit_counter(&mut self) {
self.icache.cr().modify(|_, w| w.hitmen().clear_bit());
}

/// Enable the cache miss counter
pub fn start_miss_counter(&mut self) {
// 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) {
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 stop_miss_counter(&mut self) {
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