Skip to content

Commit 397d98f

Browse files
authored
icache: Add ICACHE management (#22)
Add management functions for the ICACHE instruction cache peripheral.
1 parent 5118c26 commit 397d98f

File tree

3 files changed

+242
-0
lines changed

3 files changed

+242
-0
lines changed

src/icache.rs

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
//! Instruction Cache management
2+
//!
3+
//! To enable the ICACHE in the default N-way set associative mode:
4+
//!
5+
//! ```
6+
//! let dp = ...; // Device peripherals
7+
//! let mut icache = dp.ICACHE.enable();
8+
//! ```
9+
//!
10+
//! To enable the ICACHE in a specific mode (e.g. Direct-mapped):
11+
//!
12+
//! ```
13+
//! let dp = ...; // Device peripherals
14+
//! let mut icache = dp.ICACHE.enable_direct_mapped();
15+
//! ```
16+
//!
17+
//! To invalidate the cache, use the ICache::invalidate() method:
18+
//!
19+
//! ```
20+
//! icache.invalidate();
21+
//! ```
22+
//!
23+
//! Performance monitoring of the cache is possible using the cache hit and
24+
//! miss counters:
25+
//!
26+
//! ```
27+
//! icache.enable_hit_counter();
28+
//! icache.enable_miss_counter();
29+
//!
30+
//! // do something
31+
//!
32+
//! let hit_count = icache.hit_count();
33+
//! let miss_count = icache.miss_count();
34+
//! ```
35+
//!
36+
//! Using interrupts to trigger cache invalidation and determine when it is
37+
//! complete:
38+
//!
39+
//! ```
40+
//! icache.start_invalidation();
41+
//!
42+
//! // In interrupt context
43+
//! if icache.check_event(Event::CacheInvalidationFinished) {
44+
//! icache.clear_irq(Event::CacheInvalidationFinished)
45+
//! }
46+
//! ```
47+
//!
48+
49+
use crate::stm32::ICACHE;
50+
51+
pub enum Event {
52+
CacheError,
53+
CacheInvalidationFinished,
54+
}
55+
56+
pub trait ICacheExt: Sized {
57+
/// Enable in N-way set associative mode (default)
58+
fn enable_n_way(self) -> ICache;
59+
60+
/// Enable in direct mapped mode
61+
fn enable_direct_mapped(self) -> ICache;
62+
63+
/// Enable in default mode (N-way set associative)
64+
fn enable(self) -> ICache {
65+
self.enable_n_way()
66+
}
67+
}
68+
69+
impl ICacheExt for ICACHE {
70+
fn enable_n_way(self) -> ICache {
71+
ICache::new(self).enable_n_way()
72+
}
73+
74+
fn enable_direct_mapped(self) -> ICache {
75+
ICache::new(self).enable_direct_mapped()
76+
}
77+
}
78+
79+
pub struct ICache {
80+
icache: ICACHE,
81+
}
82+
83+
impl ICache {
84+
fn new(icache: ICACHE) -> Self {
85+
Self { icache }
86+
}
87+
88+
fn wait_for_busy_complete(&self) {
89+
while self.icache.sr().read().busyf().bit_is_set() {}
90+
}
91+
92+
/// Enable the ICACHE in N-way associative mode
93+
fn enable_n_way(self) -> Self {
94+
// Wait for any ongoing cache invalidation operation to complete
95+
self.wait_for_busy_complete();
96+
97+
self.icache
98+
.cr()
99+
.write(|w| w.waysel().set_bit().en().set_bit());
100+
101+
self
102+
}
103+
104+
/// Enable the ICACHE in 1-way associative mode (direct mapping)
105+
fn enable_direct_mapped(self) -> Self {
106+
// Wait for any ongoing cache invalidation operation to complete
107+
self.wait_for_busy_complete();
108+
109+
self.icache
110+
.cr()
111+
.write(|w| w.waysel().clear_bit().en().set_bit());
112+
113+
self
114+
}
115+
116+
/// Disable the ICACHE
117+
pub fn disable(mut self) -> ICACHE {
118+
// Restore cache mode to default (N-way associative)
119+
self.icache
120+
.cr()
121+
.write(|w| w.waysel().set_bit().en().clear_bit());
122+
123+
// Disable interrupts
124+
self.icache
125+
.ier()
126+
.modify(|_, w| w.errie().clear_bit().bsyendie().clear_bit());
127+
128+
self.invalidate();
129+
130+
cortex_m::asm::dsb();
131+
cortex_m::asm::isb();
132+
133+
self.free()
134+
}
135+
136+
/// Invalidate the ICACHE. This will block while the cache is being
137+
/// invalidated
138+
pub fn invalidate(&mut self) {
139+
self.start_cache_invalidation();
140+
141+
self.wait_for_busy_complete();
142+
143+
cortex_m::asm::dsb();
144+
cortex_m::asm::isb();
145+
}
146+
147+
/// Start cache invalidation
148+
pub fn start_cache_invalidation(&mut self) {
149+
self.icache.cr().modify(|_, w| w.cacheinv().set_bit());
150+
}
151+
152+
/// Enable interrupts for the given event
153+
pub fn listen(&mut self, event: Event) {
154+
self.icache.ier().modify(|_, w| match event {
155+
Event::CacheError => w.errie().set_bit(),
156+
Event::CacheInvalidationFinished => w.bsyendie().set_bit(),
157+
});
158+
}
159+
160+
/// Disable interrupts for the given event
161+
pub fn unlisten(&mut self, event: Event) {
162+
self.icache.ier().modify(|_, w| match event {
163+
Event::CacheError => w.errie().clear_bit(),
164+
Event::CacheInvalidationFinished => w.bsyendie().clear_bit(),
165+
});
166+
}
167+
168+
/// Clear the IRQ for the given event
169+
pub fn clear_irq(&mut self, event: Event) {
170+
self.icache.fcr().write(|w| match event {
171+
Event::CacheError => w.cerrf().set_bit(),
172+
Event::CacheInvalidationFinished => w.cbsyendf().set_bit(),
173+
});
174+
}
175+
176+
/// Check whether an interrupt event has occurred. Returns true if it has.
177+
/// Clear the event IRQ by calling `clear_event`
178+
pub fn check_event(&mut self, event: Event) -> bool {
179+
let sr = self.icache.sr().read();
180+
match event {
181+
Event::CacheError => sr.errf().bit_is_set(),
182+
Event::CacheInvalidationFinished => sr.bsyendf().bit_is_set(),
183+
}
184+
}
185+
186+
/// Enable the cache hit counter. The number of hits can be queried with ICache::hit_counter()
187+
pub fn start_hit_counter(&mut self) {
188+
// Enable and reset the cache hit counter
189+
self.icache
190+
.cr()
191+
.modify(|_, w| w.hitmrst().set_bit().hitmen().set_bit());
192+
}
193+
194+
/// Get the current value of the hit counter
195+
pub fn hit_count(&self) -> u32 {
196+
self.icache.hmonr().read().hitmon().bits()
197+
}
198+
199+
/// Reset the hit counter
200+
pub fn reset_hit_counter(&mut self) {
201+
self.icache.cr().modify(|_, w| w.hitmrst().set_bit());
202+
}
203+
204+
/// Disable the hit counter. The hit counter is disabled when the peripheral
205+
/// is disabled
206+
pub fn stop_hit_counter(&mut self) {
207+
self.icache.cr().modify(|_, w| w.hitmen().clear_bit());
208+
}
209+
210+
/// Enable the cache miss counter
211+
pub fn start_miss_counter(&mut self) {
212+
// Enable and reset the miss counter
213+
self.icache
214+
.cr()
215+
.modify(|_, w| w.missmrst().set_bit().missmen().set_bit());
216+
}
217+
218+
/// Get the current value of the miss counter
219+
pub fn miss_count(&self) -> u16 {
220+
self.icache.mmonr().read().missmon().bits()
221+
}
222+
223+
/// Reset the miss counter
224+
pub fn reset_miss_counter(&mut self) {
225+
self.icache.cr().modify(|_, w| w.missmrst().set_bit());
226+
}
227+
228+
/// Disable the miss counter. The miss counter is disabled when the ICACHE
229+
/// is disabled
230+
pub fn stop_miss_counter(&mut self) {
231+
self.icache.cr().modify(|_, w| w.missmen().clear_bit());
232+
}
233+
234+
// Deconstruct and return ICACHE
235+
pub fn free(self) -> ICACHE {
236+
self.icache
237+
}
238+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ pub mod rcc;
5252
#[cfg(feature = "device-selected")]
5353
pub mod gpio;
5454

55+
#[cfg(feature = "device-selected")]
56+
pub mod icache;
57+
5558
/// Get the name of the type without the module prefix(es)
5659
fn stripped_type_name<T>() -> &'static str {
5760
let s = core::any::type_name::<T>();

src/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Prelude
22
33
pub use crate::gpio::GpioExt as _stm32h5xx_hal_gpio_GpioExt;
4+
pub use crate::icache::ICacheExt as _stm32h5xx_hal_delay_ICacheExt;
45
pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt;
56
pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt;
67

0 commit comments

Comments
 (0)