cortex_m/peripheral/scb.rs
1//! System Control Block
2
3#[cfg(any(armv7m, armv8m))]
4use core::arch::asm;
5#[cfg(any(armv7m, armv8m))]
6use core::sync::atomic::{Ordering, compiler_fence};
7#[cfg(not(armv6m))]
8use cortex_m_macros::asm_cfg;
9use volatile_register::RW;
10
11#[cfg(not(armv6m))]
12use super::CBP;
13#[cfg(not(armv6m))]
14use super::CPUID;
15use super::SCB;
16#[cfg(not(armv6m))]
17use super::cpuid::CsselrCacheType;
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize};
20
21/// Register block
22#[repr(C)]
23pub struct RegisterBlock {
24 /// Interrupt Control and State
25 pub icsr: RW<u32>,
26
27 /// Vector Table Offset (not present on Cortex-M0 variants)
28 pub vtor: RW<u32>,
29
30 /// Application Interrupt and Reset Control
31 pub aircr: RW<u32>,
32
33 /// System Control
34 pub scr: RW<u32>,
35
36 /// Configuration and Control
37 pub ccr: RW<u32>,
38
39 /// System Handler Priority (word accessible only on Cortex-M0 variants)
40 ///
41 /// On ARMv7-M, `shpr[0]` points to SHPR1
42 ///
43 /// On ARMv6-M, `shpr[0]` points to SHPR2
44 #[cfg(not(armv6m))]
45 pub shpr: [RW<u8>; 12],
46 #[cfg(armv6m)]
47 _reserved1: u32,
48 /// System Handler Priority (word accessible only on Cortex-M0 variants)
49 ///
50 /// On ARMv7-M, `shpr[0]` points to SHPR1
51 ///
52 /// On ARMv6-M, `shpr[0]` points to SHPR2
53 #[cfg(armv6m)]
54 pub shpr: [RW<u32>; 2],
55
56 /// System Handler Control and State
57 pub shcsr: RW<u32>,
58
59 /// Configurable Fault Status (not present on Cortex-M0 variants)
60 #[cfg(not(armv6m))]
61 pub cfsr: RW<u32>,
62 #[cfg(armv6m)]
63 _reserved2: u32,
64
65 /// HardFault Status (not present on Cortex-M0 variants)
66 #[cfg(not(armv6m))]
67 pub hfsr: RW<u32>,
68 #[cfg(armv6m)]
69 _reserved3: u32,
70
71 /// Debug Fault Status (not present on Cortex-M0 variants)
72 #[cfg(not(armv6m))]
73 pub dfsr: RW<u32>,
74 #[cfg(armv6m)]
75 _reserved4: u32,
76
77 /// MemManage Fault Address (not present on Cortex-M0 variants)
78 #[cfg(not(armv6m))]
79 pub mmfar: RW<u32>,
80 #[cfg(armv6m)]
81 _reserved5: u32,
82
83 /// BusFault Address (not present on Cortex-M0 variants)
84 #[cfg(not(armv6m))]
85 pub bfar: RW<u32>,
86 #[cfg(armv6m)]
87 _reserved6: u32,
88
89 /// Auxiliary Fault Status (not present on Cortex-M0 variants)
90 #[cfg(not(armv6m))]
91 pub afsr: RW<u32>,
92 #[cfg(armv6m)]
93 _reserved7: u32,
94
95 _reserved8: [u32; 18],
96
97 /// Coprocessor Access Control (not present on Cortex-M0 variants)
98 #[cfg(not(armv6m))]
99 pub cpacr: RW<u32>,
100 #[cfg(armv6m)]
101 _reserved9: u32,
102
103 /// Non-Secure Access Control (only present on ARMv8-M)
104 ///
105 /// Controls whether Non-Secure code can access coprocessors. Bits 10–11
106 /// correspond to CP10 and CP11 (the FPU): setting them allows Non-Secure
107 /// code to use floating-point instructions.
108 #[cfg(armv8m)]
109 pub nsacr: RW<u32>,
110}
111
112/// FPU access mode
113#[cfg(has_fpu)]
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115pub enum FpuAccessMode {
116 /// FPU is not accessible
117 Disabled,
118 /// FPU is accessible in Privileged and User mode
119 Enabled,
120 /// FPU is accessible in Privileged mode only
121 Privileged,
122}
123
124#[cfg(has_fpu)]
125mod fpu_consts {
126 pub const SCB_CPACR_FPU_MASK: u32 = 0b11_11 << 20;
127 pub const SCB_CPACR_FPU_ENABLE: u32 = 0b01_01 << 20;
128 pub const SCB_CPACR_FPU_USER: u32 = 0b10_10 << 20;
129}
130
131#[cfg(has_fpu)]
132use self::fpu_consts::*;
133
134#[cfg(has_fpu)]
135impl SCB {
136 /// Shorthand for `set_fpu_access_mode(FpuAccessMode::Disabled)`
137 #[inline]
138 pub fn disable_fpu(&mut self) {
139 self.set_fpu_access_mode(FpuAccessMode::Disabled)
140 }
141
142 /// Shorthand for `set_fpu_access_mode(FpuAccessMode::Enabled)`
143 #[inline]
144 pub fn enable_fpu(&mut self) {
145 self.set_fpu_access_mode(FpuAccessMode::Enabled)
146 }
147
148 /// Gets FPU access mode
149 #[inline]
150 pub fn fpu_access_mode() -> FpuAccessMode {
151 // NOTE(unsafe) atomic read operation with no side effects
152 let cpacr = unsafe { (*Self::PTR).cpacr.read() };
153
154 if cpacr & SCB_CPACR_FPU_MASK == SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER {
155 FpuAccessMode::Enabled
156 } else if cpacr & SCB_CPACR_FPU_MASK == SCB_CPACR_FPU_ENABLE {
157 FpuAccessMode::Privileged
158 } else {
159 FpuAccessMode::Disabled
160 }
161 }
162
163 /// Sets FPU access mode
164 ///
165 /// *IMPORTANT* Any function that runs fully or partly with the FPU disabled must *not* take any
166 /// floating-point arguments or have any floating-point local variables. Because the compiler
167 /// might inline such a function into a caller that does have floating-point arguments or
168 /// variables, any such function must be also marked #[inline(never)].
169 #[inline]
170 pub fn set_fpu_access_mode(&mut self, mode: FpuAccessMode) {
171 let mut cpacr = self.cpacr.read() & !SCB_CPACR_FPU_MASK;
172 match mode {
173 FpuAccessMode::Disabled => (),
174 FpuAccessMode::Privileged => cpacr |= SCB_CPACR_FPU_ENABLE,
175 FpuAccessMode::Enabled => cpacr |= SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER,
176 }
177 unsafe { self.cpacr.write(cpacr) }
178 }
179}
180
181/// ARMv8-M TrustZone coprocessor access control.
182#[cfg(armv8m)]
183impl SCB {
184 const SCB_NSACR_CP10_CP11: u32 = 0b11 << 10;
185
186 /// Allow Non-Secure code to use the FPU (CP10 and CP11).
187 ///
188 /// Sets NSACR bits 10–11 so that Non-Secure threads can execute
189 /// floating-point instructions. Without this, any NS FPU instruction
190 /// raises a UsageFault.
191 ///
192 /// Call this before jumping to Non-Secure code if the NS application
193 /// uses floating-point.
194 #[inline]
195 pub fn enable_nonsecure_fpu(&mut self) {
196 unsafe { self.nsacr.modify(|v| v | Self::SCB_NSACR_CP10_CP11) }
197 }
198
199 /// Deny Non-Secure code from using the FPU.
200 ///
201 /// Clears NSACR bits 10–11.
202 #[inline]
203 pub fn disable_nonsecure_fpu(&mut self) {
204 unsafe { self.nsacr.modify(|v| v & !Self::SCB_NSACR_CP10_CP11) }
205 }
206
207 /// Returns `true` if Non-Secure code is allowed to use the FPU.
208 #[inline]
209 pub fn is_nonsecure_fpu_enabled() -> bool {
210 // NOTE(unsafe) atomic read with no side effects
211 unsafe { ((*Self::PTR).nsacr.read() & Self::SCB_NSACR_CP10_CP11) != 0 }
212 }
213}
214
215impl SCB {
216 /// Returns the active exception number
217 #[inline]
218 pub fn vect_active() -> VectActive {
219 let icsr = unsafe { (*SCB::PTR).icsr.read() };
220
221 // NOTE(unsafe): Assume correctly selected target.
222 unsafe { VectActive::from(icsr as u8).unwrap_unchecked() }
223 }
224}
225
226/// Processor core exceptions (internal interrupts)
227#[derive(Clone, Copy, Debug, Eq, PartialEq)]
228#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
229#[cfg_attr(feature = "std", derive(PartialOrd, Hash))]
230pub enum Exception {
231 /// Non maskable interrupt
232 NonMaskableInt,
233
234 /// Hard fault interrupt
235 HardFault,
236
237 /// Memory management interrupt (not present on Cortex-M0 variants)
238 #[cfg(not(armv6m))]
239 MemoryManagement,
240
241 /// Bus fault interrupt (not present on Cortex-M0 variants)
242 #[cfg(not(armv6m))]
243 BusFault,
244
245 /// Usage fault interrupt (not present on Cortex-M0 variants)
246 #[cfg(not(armv6m))]
247 UsageFault,
248
249 /// Secure fault interrupt (only on ARMv8-M)
250 #[cfg(any(armv8m, native))]
251 SecureFault,
252
253 /// SV call interrupt
254 SVCall,
255
256 /// Debug monitor interrupt (not present on Cortex-M0 variants)
257 #[cfg(not(armv6m))]
258 DebugMonitor,
259
260 /// Pend SV interrupt
261 PendSV,
262
263 /// System Tick interrupt
264 SysTick,
265}
266
267impl Exception {
268 /// Returns the IRQ number of this `Exception`
269 ///
270 /// The return value is always within the closed range `[-1, -14]`
271 #[inline]
272 pub fn irqn(self) -> i8 {
273 match self {
274 Exception::NonMaskableInt => -14,
275 Exception::HardFault => -13,
276 #[cfg(not(armv6m))]
277 Exception::MemoryManagement => -12,
278 #[cfg(not(armv6m))]
279 Exception::BusFault => -11,
280 #[cfg(not(armv6m))]
281 Exception::UsageFault => -10,
282 #[cfg(any(armv8m, native))]
283 Exception::SecureFault => -9,
284 Exception::SVCall => -5,
285 #[cfg(not(armv6m))]
286 Exception::DebugMonitor => -4,
287 Exception::PendSV => -2,
288 Exception::SysTick => -1,
289 }
290 }
291}
292
293/// Active exception number
294#[derive(Clone, Copy, Debug, Eq, PartialEq)]
295#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
296#[cfg_attr(feature = "std", derive(PartialOrd, Hash))]
297pub enum VectActive {
298 /// Thread mode
299 ThreadMode,
300
301 /// Processor core exception (internal interrupts)
302 Exception(Exception),
303
304 /// Device specific exception (external interrupts)
305 Interrupt {
306 /// Interrupt number. This number is always within half open range `[0, 240)`
307 irqn: u8,
308 },
309}
310
311impl VectActive {
312 /// Converts a `byte` into `VectActive`
313 #[inline]
314 pub fn from(vect_active: u8) -> Option<Self> {
315 Some(match vect_active {
316 0 => VectActive::ThreadMode,
317 2 => VectActive::Exception(Exception::NonMaskableInt),
318 3 => VectActive::Exception(Exception::HardFault),
319 #[cfg(not(armv6m))]
320 4 => VectActive::Exception(Exception::MemoryManagement),
321 #[cfg(not(armv6m))]
322 5 => VectActive::Exception(Exception::BusFault),
323 #[cfg(not(armv6m))]
324 6 => VectActive::Exception(Exception::UsageFault),
325 #[cfg(any(armv8m, native))]
326 7 => VectActive::Exception(Exception::SecureFault),
327 11 => VectActive::Exception(Exception::SVCall),
328 #[cfg(not(armv6m))]
329 12 => VectActive::Exception(Exception::DebugMonitor),
330 14 => VectActive::Exception(Exception::PendSV),
331 15 => VectActive::Exception(Exception::SysTick),
332 irqn if irqn >= 16 => VectActive::Interrupt { irqn: irqn - 16 },
333 _ => return None,
334 })
335 }
336}
337
338#[cfg(not(armv6m))]
339mod scb_consts {
340 pub const SCB_CCR_IC_MASK: u32 = 1 << 17;
341 pub const SCB_CCR_DC_MASK: u32 = 1 << 16;
342}
343
344#[cfg(not(armv6m))]
345use self::scb_consts::*;
346
347#[cfg(not(armv6m))]
348impl SCB {
349 /// Enables I-cache if currently disabled.
350 ///
351 /// This operation first invalidates the entire I-cache.
352 #[inline]
353 #[asm_cfg(cortex_m)]
354 pub fn enable_icache(&mut self) {
355 // Don't do anything if I-cache is already enabled
356 if Self::icache_enabled() {
357 return;
358 }
359
360 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
361 let mut cbp = unsafe { CBP::new() };
362
363 // Invalidate I-cache
364 cbp.iciallu();
365
366 // NOTE(unsafe): The asm routine manages exclusive access to the SCB
367 // registers and applies the proper barriers; it is technically safe on
368 // its own, and is only `unsafe` here because it's asm.
369 unsafe {
370 asm!(
371 // Build the CCR address (0xE000ED14) with movw/movt instead of `ldr =`, as the
372 // latter emits a PC-relative load from a literal pool which can end up out of
373 // range when this asm block is inlined into a large function.
374 "movw {0}, #0xED14", // CCR address, lower half
375 "movt {0}, #0xE000", // CCR address, upper half
376 "mrs {2}, PRIMASK", // save critical nesting info
377 "cpsid i", // mask interrupts
378 "ldr {1}, [{0}]", // read CCR
379 "orr.w {1}, {1}, #(1 << 17)", // Set bit 17, IC
380 "str {1}, [{0}]", // write it back
381 "dsb", // ensure store completes
382 "isb", // synchronize pipeline
383 "msr PRIMASK, {2}", // unnest critical section
384 out(reg) _,
385 out(reg) _,
386 out(reg) _,
387 options(nostack),
388 )
389 };
390 compiler_fence(Ordering::SeqCst);
391 }
392
393 /// Disables I-cache if currently enabled.
394 ///
395 /// This operation invalidates the entire I-cache after disabling.
396 #[inline]
397 pub fn disable_icache(&mut self) {
398 // Don't do anything if I-cache is already disabled
399 if !Self::icache_enabled() {
400 return;
401 }
402
403 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
404 let mut cbp = unsafe { CBP::new() };
405
406 // Disable I-cache
407 // NOTE(unsafe): We have synchronised access by &mut self
408 unsafe { self.ccr.modify(|r| r & !SCB_CCR_IC_MASK) };
409
410 // Invalidate I-cache
411 cbp.iciallu();
412
413 crate::asm::dsb();
414 crate::asm::isb();
415 }
416
417 /// Returns whether the I-cache is currently enabled.
418 #[inline(always)]
419 pub fn icache_enabled() -> bool {
420 crate::asm::dsb();
421 crate::asm::isb();
422
423 // NOTE(unsafe): atomic read with no side effects
424 unsafe { (*Self::PTR).ccr.read() & SCB_CCR_IC_MASK == SCB_CCR_IC_MASK }
425 }
426
427 /// Invalidates the entire I-cache.
428 #[inline]
429 pub fn invalidate_icache(&mut self) {
430 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
431 let mut cbp = unsafe { CBP::new() };
432
433 // Invalidate I-cache
434 cbp.iciallu();
435
436 crate::asm::dsb();
437 crate::asm::isb();
438 }
439
440 /// Enables D-cache if currently disabled.
441 ///
442 /// This operation first invalidates the entire D-cache, ensuring it does
443 /// not contain stale values before being enabled.
444 #[inline]
445 #[asm_cfg(cortex_m)]
446 pub fn enable_dcache(&mut self, cpuid: &mut CPUID) {
447 // Don't do anything if D-cache is already enabled
448 if Self::dcache_enabled() {
449 return;
450 }
451
452 // Invalidate anything currently in the D-cache
453 unsafe { self.invalidate_dcache(cpuid) };
454
455 // NOTE(unsafe): The asm routine manages exclusive access to the SCB
456 // registers and applies the proper barriers; it is technically safe on
457 // its own, and is only `unsafe` here because it's asm.
458 unsafe {
459 asm!(
460 // Should this be replaced with a register modify?
461 // Build the CCR address (0xE000ED14) with movw/movt instead of `ldr =`, as the
462 // latter emits a PC-relative load from a literal pool which can end up out of
463 // range when this asm block is inlined into a large function.
464 "movw {0}, #0xED14", // CCR address, lower half
465 "movt {0}, #0xE000", // CCR address, upper half
466 "mrs {2}, PRIMASK", // save critical nesting info
467 "cpsid i", // mask interrupts
468 "ldr {1}, [{0}]", // read CCR
469 "orr.w {1}, {1}, #(1 << 16)", // Set bit 16, DC
470 "str {1}, [{0}]", // write it back
471 "dsb", // ensure store completes
472 "isb", // synchronize pipeline
473 "msr PRIMASK, {2}", // unnest critical section
474 out(reg) _,
475 out(reg) _,
476 out(reg) _,
477 options(nostack),
478 )
479 };
480 compiler_fence(Ordering::SeqCst);
481 }
482
483 /// Disables D-cache if currently enabled.
484 ///
485 /// This operation subsequently cleans and invalidates the entire D-cache,
486 /// ensuring all contents are safely written back to main memory after disabling.
487 #[inline]
488 pub fn disable_dcache(&mut self, cpuid: &mut CPUID) {
489 // Don't do anything if D-cache is already disabled
490 if !Self::dcache_enabled() {
491 return;
492 }
493
494 // Turn off the D-cache
495 // NOTE(unsafe): We have synchronised access by &mut self
496 unsafe { self.ccr.modify(|r| r & !SCB_CCR_DC_MASK) };
497
498 // Clean and invalidate whatever was left in it
499 self.clean_invalidate_dcache(cpuid);
500 }
501
502 /// Returns whether the D-cache is currently enabled.
503 #[inline]
504 pub fn dcache_enabled() -> bool {
505 crate::asm::dsb();
506 crate::asm::isb();
507
508 // NOTE(unsafe) atomic read with no side effects
509 unsafe { (*Self::PTR).ccr.read() & SCB_CCR_DC_MASK == SCB_CCR_DC_MASK }
510 }
511
512 /// Invalidates the entire D-cache.
513 ///
514 /// Note that calling this while the dcache is enabled will probably wipe out the
515 /// stack, depending on optimisations, therefore breaking returning to the call point.
516 ///
517 /// It's used immediately before enabling the dcache, but not exported publicly.
518 #[inline]
519 #[cfg(cortex_m)]
520 unsafe fn invalidate_dcache(&mut self, cpuid: &mut CPUID) {
521 unsafe {
522 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
523 let mut cbp = CBP::new();
524
525 // Read number of sets and ways
526 let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
527
528 // Invalidate entire D-cache
529 for set in 0..sets {
530 for way in 0..ways {
531 cbp.dcisw(set, way);
532 }
533 }
534
535 crate::asm::dsb();
536 crate::asm::isb();
537 }
538 }
539
540 /// Cleans the entire D-cache.
541 ///
542 /// This function causes everything in the D-cache to be written back to main memory,
543 /// overwriting whatever is already there.
544 #[inline]
545 pub fn clean_dcache(&mut self, cpuid: &mut CPUID) {
546 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
547 let mut cbp = unsafe { CBP::new() };
548
549 // Read number of sets and ways
550 let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
551
552 for set in 0..sets {
553 for way in 0..ways {
554 cbp.dccsw(set, way);
555 }
556 }
557
558 crate::asm::dsb();
559 crate::asm::isb();
560 }
561
562 /// Cleans and invalidates the entire D-cache.
563 ///
564 /// This function causes everything in the D-cache to be written back to main memory,
565 /// and then marks the entire D-cache as invalid, causing future reads to first fetch
566 /// from main memory.
567 #[inline]
568 pub fn clean_invalidate_dcache(&mut self, cpuid: &mut CPUID) {
569 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
570 let mut cbp = unsafe { CBP::new() };
571
572 // Read number of sets and ways
573 let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
574
575 for set in 0..sets {
576 for way in 0..ways {
577 cbp.dccisw(set, way);
578 }
579 }
580
581 crate::asm::dsb();
582 crate::asm::isb();
583 }
584
585 /// Invalidates D-cache by address.
586 ///
587 /// * `addr`: The address to invalidate, which must be cache-line aligned.
588 /// * `size`: Number of bytes to invalidate, which must be a multiple of the cache line size.
589 ///
590 /// Invalidates D-cache cache lines, starting from the first line containing `addr`,
591 /// finishing once at least `size` bytes have been invalidated.
592 ///
593 /// Invalidation causes the next read access to memory to be fetched from main memory instead
594 /// of the cache.
595 ///
596 /// # Cache Line Sizes
597 ///
598 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
599 /// to 32 bytes, which means `addr` must be 32-byte aligned and `size` must be a multiple
600 /// of 32. At the time of writing, no other Cortex-M cores have data caches.
601 ///
602 /// If `addr` is not cache-line aligned, or `size` is not a multiple of the cache line size,
603 /// other data before or after the desired memory would also be invalidated, which can very
604 /// easily cause memory corruption and undefined behaviour.
605 ///
606 /// # Safety
607 ///
608 /// After invalidating, the next read of invalidated data will be from main memory. This may
609 /// cause recent writes to be lost, potentially including writes that initialized objects.
610 /// Therefore, this method may cause uninitialized memory or invalid values to be read,
611 /// resulting in undefined behaviour. You must ensure that main memory contains valid and
612 /// initialized values before invalidating.
613 ///
614 /// `addr` **must** be aligned to the size of the cache lines, and `size` **must** be a
615 /// multiple of the cache line size, otherwise this function will invalidate other memory,
616 /// easily leading to memory corruption and undefined behaviour. This precondition is checked
617 /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid
618 /// a runtime-dependent `panic!()` call.
619 #[inline]
620 pub unsafe fn invalidate_dcache_by_address(&mut self, addr: usize, size: usize) {
621 unsafe {
622 // No-op zero sized operations
623 if size == 0 {
624 return;
625 }
626
627 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
628 let mut cbp = CBP::new();
629
630 // dminline is log2(num words), so 2**dminline * 4 gives size in bytes
631 let dminline = CPUID::cache_dminline();
632 let line_size = (1 << dminline) * 4;
633
634 debug_assert!((addr & (line_size - 1)) == 0);
635 debug_assert!((size & (line_size - 1)) == 0);
636
637 crate::asm::dsb();
638
639 // Find number of cache lines to invalidate
640 let num_lines = ((size - 1) / line_size) + 1;
641
642 // Compute address of first cache line
643 let mask = 0xFFFF_FFFF - (line_size - 1);
644 let mut addr = addr & mask;
645
646 for _ in 0..num_lines {
647 cbp.dcimvac(addr as u32);
648 addr += line_size;
649 }
650
651 crate::asm::dsb();
652 crate::asm::isb();
653 }
654 }
655
656 /// Invalidates an object from the D-cache.
657 ///
658 /// * `obj`: The object to invalidate.
659 ///
660 /// Invalidates D-cache starting from the first cache line containing `obj`,
661 /// continuing to invalidate cache lines until all of `obj` has been invalidated.
662 ///
663 /// Invalidation causes the next read access to memory to be fetched from main memory instead
664 /// of the cache.
665 ///
666 /// # Cache Line Sizes
667 ///
668 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
669 /// to 32 bytes, which means `obj` must be 32-byte aligned, and its size must be a multiple
670 /// of 32 bytes. At the time of writing, no other Cortex-M cores have data caches.
671 ///
672 /// If `obj` is not cache-line aligned, or its size is not a multiple of the cache line size,
673 /// other data before or after the desired memory would also be invalidated, which can very
674 /// easily cause memory corruption and undefined behaviour.
675 ///
676 /// # Safety
677 ///
678 /// After invalidating, `obj` will be read from main memory on next access. This may cause
679 /// recent writes to `obj` to be lost, potentially including the write that initialized it.
680 /// Therefore, this method may cause uninitialized memory or invalid values to be read,
681 /// resulting in undefined behaviour. You must ensure that main memory contains a valid and
682 /// initialized value for T before invalidating `obj`.
683 ///
684 /// `obj` **must** be aligned to the size of the cache lines, and its size **must** be a
685 /// multiple of the cache line size, otherwise this function will invalidate other memory,
686 /// easily leading to memory corruption and undefined behaviour. This precondition is checked
687 /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid
688 /// a runtime-dependent `panic!()` call.
689 #[inline]
690 pub unsafe fn invalidate_dcache_by_ref<T>(&mut self, obj: &mut T) {
691 unsafe {
692 self.invalidate_dcache_by_address(obj as *const T as usize, core::mem::size_of::<T>());
693 }
694 }
695
696 /// Invalidates a slice from the D-cache.
697 ///
698 /// * `slice`: The slice to invalidate.
699 ///
700 /// Invalidates D-cache starting from the first cache line containing members of `slice`,
701 /// continuing to invalidate cache lines until all of `slice` has been invalidated.
702 ///
703 /// Invalidation causes the next read access to memory to be fetched from main memory instead
704 /// of the cache.
705 ///
706 /// # Cache Line Sizes
707 ///
708 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
709 /// to 32 bytes, which means `slice` must be 32-byte aligned, and its size must be a multiple
710 /// of 32 bytes. At the time of writing, no other Cortex-M cores have data caches.
711 ///
712 /// If `slice` is not cache-line aligned, or its size is not a multiple of the cache line size,
713 /// other data before or after the desired memory would also be invalidated, which can very
714 /// easily cause memory corruption and undefined behaviour.
715 ///
716 /// # Safety
717 ///
718 /// After invalidating, `slice` will be read from main memory on next access. This may cause
719 /// recent writes to `slice` to be lost, potentially including the write that initialized it.
720 /// Therefore, this method may cause uninitialized memory or invalid values to be read,
721 /// resulting in undefined behaviour. You must ensure that main memory contains valid and
722 /// initialized values for T before invalidating `slice`.
723 ///
724 /// `slice` **must** be aligned to the size of the cache lines, and its size **must** be a
725 /// multiple of the cache line size, otherwise this function will invalidate other memory,
726 /// easily leading to memory corruption and undefined behaviour. This precondition is checked
727 /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid
728 /// a runtime-dependent `panic!()` call.
729 #[inline]
730 pub unsafe fn invalidate_dcache_by_slice<T>(&mut self, slice: &mut [T]) {
731 unsafe {
732 self.invalidate_dcache_by_address(
733 slice.as_ptr() as usize,
734 core::mem::size_of_val(slice),
735 );
736 }
737 }
738
739 /// Cleans D-cache by address.
740 ///
741 /// * `addr`: The address to start cleaning at.
742 /// * `size`: The number of bytes to clean.
743 ///
744 /// Cleans D-cache cache lines, starting from the first line containing `addr`,
745 /// finishing once at least `size` bytes have been invalidated.
746 ///
747 /// Cleaning the cache causes whatever data is present in the cache to be immediately written
748 /// to main memory, overwriting whatever was in main memory.
749 ///
750 /// # Cache Line Sizes
751 ///
752 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
753 /// to 32 bytes, which means `addr` should generally be 32-byte aligned and `size` should be a
754 /// multiple of 32. At the time of writing, no other Cortex-M cores have data caches.
755 ///
756 /// If `addr` is not cache-line aligned, or `size` is not a multiple of the cache line size,
757 /// other data before or after the desired memory will also be cleaned. From the point of view
758 /// of the core executing this function, memory remains consistent, so this is not unsound,
759 /// but is worth knowing about.
760 #[inline]
761 pub fn clean_dcache_by_address(&mut self, addr: usize, size: usize) {
762 // No-op zero sized operations
763 if size == 0 {
764 return;
765 }
766
767 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
768 let mut cbp = unsafe { CBP::new() };
769
770 crate::asm::dsb();
771
772 let dminline = CPUID::cache_dminline();
773 let line_size = (1 << dminline) * 4;
774 let num_lines = ((size - 1) / line_size) + 1;
775
776 let mask = 0xFFFF_FFFF - (line_size - 1);
777 let mut addr = addr & mask;
778
779 for _ in 0..num_lines {
780 cbp.dccmvac(addr as u32);
781 addr += line_size;
782 }
783
784 crate::asm::dsb();
785 crate::asm::isb();
786 }
787
788 /// Cleans an object from the D-cache.
789 ///
790 /// * `obj`: The object to clean.
791 ///
792 /// Cleans D-cache starting from the first cache line containing `obj`,
793 /// continuing to clean cache lines until all of `obj` has been cleaned.
794 ///
795 /// It is recommended that `obj` is both aligned to the cache line size and a multiple of
796 /// the cache line size long, otherwise surrounding data will also be cleaned.
797 ///
798 /// Cleaning the cache causes whatever data is present in the cache to be immediately written
799 /// to main memory, overwriting whatever was in main memory.
800 #[inline]
801 pub fn clean_dcache_by_ref<T>(&mut self, obj: &T) {
802 self.clean_dcache_by_address(obj as *const T as usize, core::mem::size_of::<T>());
803 }
804
805 /// Cleans a slice from D-cache.
806 ///
807 /// * `slice`: The slice to clean.
808 ///
809 /// Cleans D-cache starting from the first cache line containing members of `slice`,
810 /// continuing to clean cache lines until all of `slice` has been cleaned.
811 ///
812 /// It is recommended that `slice` is both aligned to the cache line size and a multiple of
813 /// the cache line size long, otherwise surrounding data will also be cleaned.
814 ///
815 /// Cleaning the cache causes whatever data is present in the cache to be immediately written
816 /// to main memory, overwriting whatever was in main memory.
817 #[inline]
818 pub fn clean_dcache_by_slice<T>(&mut self, slice: &[T]) {
819 self.clean_dcache_by_address(slice.as_ptr() as usize, core::mem::size_of_val(slice));
820 }
821
822 /// Cleans and invalidates D-cache by address.
823 ///
824 /// * `addr`: The address to clean and invalidate.
825 /// * `size`: The number of bytes to clean and invalidate.
826 ///
827 /// Cleans and invalidates D-cache starting from the first cache line containing `addr`,
828 /// finishing once at least `size` bytes have been cleaned and invalidated.
829 ///
830 /// It is recommended that `addr` is aligned to the cache line size and `size` is a multiple of
831 /// the cache line size, otherwise surrounding data will also be cleaned.
832 ///
833 /// Cleaning and invalidating causes data in the D-cache to be written back to main memory,
834 /// and then marks that data in the D-cache as invalid, causing future reads to first fetch
835 /// from main memory.
836 #[inline]
837 pub fn clean_invalidate_dcache_by_address(&mut self, addr: usize, size: usize) {
838 // No-op zero sized operations
839 if size == 0 {
840 return;
841 }
842
843 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
844 let mut cbp = unsafe { CBP::new() };
845
846 crate::asm::dsb();
847
848 // Cache lines are fixed to 32 bit on Cortex-M7 and not present in earlier Cortex-M
849 const LINESIZE: usize = 32;
850 let num_lines = ((size - 1) / LINESIZE) + 1;
851
852 let mut addr = addr & 0xFFFF_FFE0;
853
854 for _ in 0..num_lines {
855 cbp.dccimvac(addr as u32);
856 addr += LINESIZE;
857 }
858
859 crate::asm::dsb();
860 crate::asm::isb();
861 }
862}
863
864const SCB_SCR_SLEEPDEEP: u32 = 0x1 << 2;
865
866impl SCB {
867 /// Set the SLEEPDEEP bit in the SCR register
868 #[inline]
869 pub fn set_sleepdeep(&mut self) {
870 unsafe {
871 self.scr.modify(|scr| scr | SCB_SCR_SLEEPDEEP);
872 }
873 }
874
875 /// Clear the SLEEPDEEP bit in the SCR register
876 #[inline]
877 pub fn clear_sleepdeep(&mut self) {
878 unsafe {
879 self.scr.modify(|scr| scr & !SCB_SCR_SLEEPDEEP);
880 }
881 }
882}
883
884const SCB_SCR_SLEEPONEXIT: u32 = 0x1 << 1;
885
886impl SCB {
887 /// Set the SLEEPONEXIT bit in the SCR register
888 #[inline]
889 pub fn set_sleeponexit(&mut self) {
890 unsafe {
891 self.scr.modify(|scr| scr | SCB_SCR_SLEEPONEXIT);
892 }
893 }
894
895 /// Clear the SLEEPONEXIT bit in the SCR register
896 #[inline]
897 pub fn clear_sleeponexit(&mut self) {
898 unsafe {
899 self.scr.modify(|scr| scr & !SCB_SCR_SLEEPONEXIT);
900 }
901 }
902}
903
904const SCB_SCR_SEVONPEND: u32 = 0x1 << 4;
905
906impl SCB {
907 /// Set the SEVONPEND bit in the SCR register
908 #[inline]
909 pub fn set_sevonpend(&mut self) {
910 unsafe {
911 self.scr.modify(|scr| scr | SCB_SCR_SEVONPEND);
912 }
913 }
914
915 /// Clear the SEVONPEND bit in the SCR register
916 #[inline]
917 pub fn clear_sevonpend(&mut self) {
918 unsafe {
919 self.scr.modify(|scr| scr & !SCB_SCR_SEVONPEND);
920 }
921 }
922}
923
924const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16;
925const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x7 << 8;
926const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2;
927
928impl SCB {
929 /// Initiate a system reset request to reset the MCU
930 #[inline]
931 pub fn sys_reset() -> ! {
932 crate::asm::dsb();
933 unsafe {
934 (*Self::PTR).aircr.modify(
935 |r| {
936 SCB_AIRCR_VECTKEY | // otherwise the write is ignored
937 r & SCB_AIRCR_PRIGROUP_MASK | // keep priority group unchanged
938 SCB_AIRCR_SYSRESETREQ
939 }, // set the bit
940 )
941 };
942 crate::asm::dsb();
943 loop {
944 // wait for the reset
945 crate::asm::nop(); // avoid rust-lang/rust#28728
946 }
947 }
948}
949
950const SCB_ICSR_PENDSVSET: u32 = 1 << 28;
951const SCB_ICSR_PENDSVCLR: u32 = 1 << 27;
952
953const SCB_ICSR_PENDSTSET: u32 = 1 << 26;
954const SCB_ICSR_PENDSTCLR: u32 = 1 << 25;
955
956impl SCB {
957 /// Set the PENDSVSET bit in the ICSR register which will pend the PendSV interrupt
958 #[inline]
959 pub fn set_pendsv() {
960 unsafe {
961 (*Self::PTR).icsr.write(SCB_ICSR_PENDSVSET);
962 }
963 }
964
965 /// Check if PENDSVSET bit in the ICSR register is set meaning PendSV interrupt is pending
966 #[inline]
967 pub fn is_pendsv_pending() -> bool {
968 unsafe { (*Self::PTR).icsr.read() & SCB_ICSR_PENDSVSET == SCB_ICSR_PENDSVSET }
969 }
970
971 /// Set the PENDSVCLR bit in the ICSR register which will clear a pending PendSV interrupt
972 #[inline]
973 pub fn clear_pendsv() {
974 unsafe {
975 (*Self::PTR).icsr.write(SCB_ICSR_PENDSVCLR);
976 }
977 }
978
979 /// Set the PENDSTSET bit in the ICSR register which will pend a SysTick interrupt
980 #[inline]
981 pub fn set_pendst() {
982 unsafe {
983 (*Self::PTR).icsr.write(SCB_ICSR_PENDSTSET);
984 }
985 }
986
987 /// Check if PENDSTSET bit in the ICSR register is set meaning SysTick interrupt is pending
988 #[inline]
989 pub fn is_pendst_pending() -> bool {
990 unsafe { (*Self::PTR).icsr.read() & SCB_ICSR_PENDSTSET == SCB_ICSR_PENDSTSET }
991 }
992
993 /// Set the PENDSTCLR bit in the ICSR register which will clear a pending SysTick interrupt
994 #[inline]
995 pub fn clear_pendst() {
996 unsafe {
997 (*Self::PTR).icsr.write(SCB_ICSR_PENDSTCLR);
998 }
999 }
1000}
1001
1002/// System handlers, exceptions with configurable priority
1003#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1004#[repr(u8)]
1005pub enum SystemHandler {
1006 // NonMaskableInt, // priority is fixed
1007 // HardFault, // priority is fixed
1008 /// Memory management interrupt (not present on Cortex-M0 variants)
1009 #[cfg(not(armv6m))]
1010 MemoryManagement = 4,
1011
1012 /// Bus fault interrupt (not present on Cortex-M0 variants)
1013 #[cfg(not(armv6m))]
1014 BusFault = 5,
1015
1016 /// Usage fault interrupt (not present on Cortex-M0 variants)
1017 #[cfg(not(armv6m))]
1018 UsageFault = 6,
1019
1020 /// Secure fault interrupt (only on ARMv8-M)
1021 #[cfg(any(armv8m, native))]
1022 SecureFault = 7,
1023
1024 /// SV call interrupt
1025 SVCall = 11,
1026
1027 /// Debug monitor interrupt (not present on Cortex-M0 variants)
1028 #[cfg(not(armv6m))]
1029 DebugMonitor = 12,
1030
1031 /// Pend SV interrupt
1032 PendSV = 14,
1033
1034 /// System Tick interrupt
1035 SysTick = 15,
1036}
1037
1038impl SCB {
1039 /// Returns the hardware priority of `system_handler`
1040 ///
1041 /// *NOTE*: Hardware priority does not exactly match logical priority levels. See
1042 /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details.
1043 #[inline]
1044 pub fn get_priority(system_handler: SystemHandler) -> u8 {
1045 let index = system_handler as u8;
1046
1047 #[cfg(not(armv6m))]
1048 {
1049 // NOTE(unsafe) atomic read with no side effects
1050
1051 // NOTE(unsafe): Index is bounded to [4,15] by SystemHandler design.
1052 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1053 let priority_ref = unsafe { (*Self::PTR).shpr.get_unchecked(usize::from(index - 4)) };
1054
1055 priority_ref.read()
1056 }
1057
1058 #[cfg(armv6m)]
1059 {
1060 // NOTE(unsafe) atomic read with no side effects
1061
1062 // NOTE(unsafe): Index is bounded to [11,15] by SystemHandler design.
1063 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1064 let priority_ref = unsafe {
1065 (*Self::PTR)
1066 .shpr
1067 .get_unchecked(usize::from((index - 8) / 4))
1068 };
1069
1070 let shpr = priority_ref.read();
1071 let prio = (shpr >> (8 * (index % 4))) & 0x0000_00ff;
1072 prio as u8
1073 }
1074 }
1075
1076 /// Sets the hardware priority of `system_handler` to `prio`
1077 ///
1078 /// *NOTE*: Hardware priority does not exactly match logical priority levels. See
1079 /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details.
1080 ///
1081 /// On ARMv6-M, updating a system handler priority requires a read-modify-write operation. On
1082 /// ARMv7-M, the operation is performed in a single, atomic write operation.
1083 ///
1084 /// # Unsafety
1085 ///
1086 /// Changing priority levels can break priority-based critical sections (see
1087 /// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
1088 #[inline]
1089 pub unsafe fn set_priority(&mut self, system_handler: SystemHandler, prio: u8) {
1090 unsafe {
1091 let index = system_handler as u8;
1092
1093 #[cfg(not(armv6m))]
1094 {
1095 // NOTE(unsafe): Index is bounded to [4,15] by SystemHandler design.
1096 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1097 let priority_ref = (*Self::PTR).shpr.get_unchecked(usize::from(index - 4));
1098
1099 priority_ref.write(prio)
1100 }
1101
1102 #[cfg(armv6m)]
1103 {
1104 // NOTE(unsafe): Index is bounded to [11,15] by SystemHandler design.
1105 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1106 let priority_ref = (*Self::PTR)
1107 .shpr
1108 .get_unchecked(usize::from((index - 8) / 4));
1109
1110 priority_ref.modify(|value| {
1111 let shift = 8 * (index % 4);
1112 let mask = 0x0000_00ff << shift;
1113 let prio = u32::from(prio) << shift;
1114
1115 (value & !mask) | prio
1116 });
1117 }
1118 }
1119 }
1120
1121 /// Return the bit position of the exception enable bit in the SHCSR register
1122 #[inline]
1123 #[cfg(not(any(armv6m, armv8m_base)))]
1124 fn shcsr_enable_shift(exception: Exception) -> Option<u32> {
1125 match exception {
1126 Exception::MemoryManagement => Some(16),
1127 Exception::BusFault => Some(17),
1128 Exception::UsageFault => Some(18),
1129 #[cfg(armv8m_main)]
1130 Exception::SecureFault => Some(19),
1131 _ => None,
1132 }
1133 }
1134
1135 /// Enable the exception
1136 ///
1137 /// If the exception is enabled, when the exception is triggered, the exception handler will be executed instead of the
1138 /// HardFault handler.
1139 /// This function is only allowed on the following exceptions:
1140 /// * `MemoryManagement`
1141 /// * `BusFault`
1142 /// * `UsageFault`
1143 /// * `SecureFault` (can only be enabled from Secure state)
1144 ///
1145 /// Calling this function with any other exception will do nothing.
1146 #[inline]
1147 #[cfg(not(any(armv6m, armv8m_base)))]
1148 pub fn enable(&mut self, exception: Exception) {
1149 if let Some(shift) = SCB::shcsr_enable_shift(exception) {
1150 // The mutable reference to SCB makes sure that only this code is currently modifying
1151 // the register.
1152 unsafe { self.shcsr.modify(|value| value | (1 << shift)) }
1153 }
1154 }
1155
1156 /// Disable the exception
1157 ///
1158 /// If the exception is disabled, when the exception is triggered, the HardFault handler will be executed instead of the
1159 /// exception handler.
1160 /// This function is only allowed on the following exceptions:
1161 /// * `MemoryManagement`
1162 /// * `BusFault`
1163 /// * `UsageFault`
1164 /// * `SecureFault` (can not be changed from Non-secure state)
1165 ///
1166 /// Calling this function with any other exception will do nothing.
1167 #[inline]
1168 #[cfg(not(any(armv6m, armv8m_base)))]
1169 pub fn disable(&mut self, exception: Exception) {
1170 if let Some(shift) = SCB::shcsr_enable_shift(exception) {
1171 // The mutable reference to SCB makes sure that only this code is currently modifying
1172 // the register.
1173 unsafe { self.shcsr.modify(|value| value & !(1 << shift)) }
1174 }
1175 }
1176
1177 /// Check if an exception is enabled
1178 ///
1179 /// This function is only allowed on the following exception:
1180 /// * `MemoryManagement`
1181 /// * `BusFault`
1182 /// * `UsageFault`
1183 /// * `SecureFault` (can not be read from Non-secure state)
1184 ///
1185 /// Calling this function with any other exception will read `false`.
1186 #[inline]
1187 #[cfg(not(any(armv6m, armv8m_base)))]
1188 pub fn is_enabled(&self, exception: Exception) -> bool {
1189 if let Some(shift) = SCB::shcsr_enable_shift(exception) {
1190 (self.shcsr.read() & (1 << shift)) > 0
1191 } else {
1192 false
1193 }
1194 }
1195}