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