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 "ldr {0}, =0xE000ED14", // CCR
372 "mrs {2}, PRIMASK", // save critical nesting info
373 "cpsid i", // mask interrupts
374 "ldr {1}, [{0}]", // read CCR
375 "orr.w {1}, {1}, #(1 << 17)", // Set bit 17, IC
376 "str {1}, [{0}]", // write it back
377 "dsb", // ensure store completes
378 "isb", // synchronize pipeline
379 "msr PRIMASK, {2}", // unnest critical section
380 out(reg) _,
381 out(reg) _,
382 out(reg) _,
383 options(nostack),
384 )
385 };
386 compiler_fence(Ordering::SeqCst);
387 }
388
389 /// Disables I-cache if currently enabled.
390 ///
391 /// This operation invalidates the entire I-cache after disabling.
392 #[inline]
393 pub fn disable_icache(&mut self) {
394 // Don't do anything if I-cache is already disabled
395 if !Self::icache_enabled() {
396 return;
397 }
398
399 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
400 let mut cbp = unsafe { CBP::new() };
401
402 // Disable I-cache
403 // NOTE(unsafe): We have synchronised access by &mut self
404 unsafe { self.ccr.modify(|r| r & !SCB_CCR_IC_MASK) };
405
406 // Invalidate I-cache
407 cbp.iciallu();
408
409 crate::asm::dsb();
410 crate::asm::isb();
411 }
412
413 /// Returns whether the I-cache is currently enabled.
414 #[inline(always)]
415 pub fn icache_enabled() -> bool {
416 crate::asm::dsb();
417 crate::asm::isb();
418
419 // NOTE(unsafe): atomic read with no side effects
420 unsafe { (*Self::PTR).ccr.read() & SCB_CCR_IC_MASK == SCB_CCR_IC_MASK }
421 }
422
423 /// Invalidates the entire I-cache.
424 #[inline]
425 pub fn invalidate_icache(&mut self) {
426 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
427 let mut cbp = unsafe { CBP::new() };
428
429 // Invalidate I-cache
430 cbp.iciallu();
431
432 crate::asm::dsb();
433 crate::asm::isb();
434 }
435
436 /// Enables D-cache if currently disabled.
437 ///
438 /// This operation first invalidates the entire D-cache, ensuring it does
439 /// not contain stale values before being enabled.
440 #[inline]
441 #[asm_cfg(cortex_m)]
442 pub fn enable_dcache(&mut self, cpuid: &mut CPUID) {
443 // Don't do anything if D-cache is already enabled
444 if Self::dcache_enabled() {
445 return;
446 }
447
448 // Invalidate anything currently in the D-cache
449 unsafe { self.invalidate_dcache(cpuid) };
450
451 // NOTE(unsafe): The asm routine manages exclusive access to the SCB
452 // registers and applies the proper barriers; it is technically safe on
453 // its own, and is only `unsafe` here because it's asm.
454 unsafe {
455 asm!(
456 // Should this be replaced with a register modify?
457 "ldr {0}, =0xE000ED14", // CCR
458 "mrs {2}, PRIMASK", // save critical nesting info
459 "cpsid i", // mask interrupts
460 "ldr {1}, [{0}]", // read CCR
461 "orr.w {1}, {1}, #(1 << 16)", // Set bit 16, DC
462 "str {1}, [{0}]", // write it back
463 "dsb", // ensure store completes
464 "isb", // synchronize pipeline
465 "msr PRIMASK, {2}", // unnest critical section
466 out(reg) _,
467 out(reg) _,
468 out(reg) _,
469 options(nostack),
470 )
471 };
472 compiler_fence(Ordering::SeqCst);
473 }
474
475 /// Disables D-cache if currently enabled.
476 ///
477 /// This operation subsequently cleans and invalidates the entire D-cache,
478 /// ensuring all contents are safely written back to main memory after disabling.
479 #[inline]
480 pub fn disable_dcache(&mut self, cpuid: &mut CPUID) {
481 // Don't do anything if D-cache is already disabled
482 if !Self::dcache_enabled() {
483 return;
484 }
485
486 // Turn off the D-cache
487 // NOTE(unsafe): We have synchronised access by &mut self
488 unsafe { self.ccr.modify(|r| r & !SCB_CCR_DC_MASK) };
489
490 // Clean and invalidate whatever was left in it
491 self.clean_invalidate_dcache(cpuid);
492 }
493
494 /// Returns whether the D-cache is currently enabled.
495 #[inline]
496 pub fn dcache_enabled() -> bool {
497 crate::asm::dsb();
498 crate::asm::isb();
499
500 // NOTE(unsafe) atomic read with no side effects
501 unsafe { (*Self::PTR).ccr.read() & SCB_CCR_DC_MASK == SCB_CCR_DC_MASK }
502 }
503
504 /// Invalidates the entire D-cache.
505 ///
506 /// Note that calling this while the dcache is enabled will probably wipe out the
507 /// stack, depending on optimisations, therefore breaking returning to the call point.
508 ///
509 /// It's used immediately before enabling the dcache, but not exported publicly.
510 #[inline]
511 #[cfg(cortex_m)]
512 unsafe fn invalidate_dcache(&mut self, cpuid: &mut CPUID) {
513 unsafe {
514 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
515 let mut cbp = CBP::new();
516
517 // Read number of sets and ways
518 let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
519
520 // Invalidate entire D-cache
521 for set in 0..sets {
522 for way in 0..ways {
523 cbp.dcisw(set, way);
524 }
525 }
526
527 crate::asm::dsb();
528 crate::asm::isb();
529 }
530 }
531
532 /// Cleans the entire D-cache.
533 ///
534 /// This function causes everything in the D-cache to be written back to main memory,
535 /// overwriting whatever is already there.
536 #[inline]
537 pub fn clean_dcache(&mut self, cpuid: &mut CPUID) {
538 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
539 let mut cbp = unsafe { CBP::new() };
540
541 // Read number of sets and ways
542 let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
543
544 for set in 0..sets {
545 for way in 0..ways {
546 cbp.dccsw(set, way);
547 }
548 }
549
550 crate::asm::dsb();
551 crate::asm::isb();
552 }
553
554 /// Cleans and invalidates the entire D-cache.
555 ///
556 /// This function causes everything in the D-cache to be written back to main memory,
557 /// and then marks the entire D-cache as invalid, causing future reads to first fetch
558 /// from main memory.
559 #[inline]
560 pub fn clean_invalidate_dcache(&mut self, cpuid: &mut CPUID) {
561 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
562 let mut cbp = unsafe { CBP::new() };
563
564 // Read number of sets and ways
565 let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
566
567 for set in 0..sets {
568 for way in 0..ways {
569 cbp.dccisw(set, way);
570 }
571 }
572
573 crate::asm::dsb();
574 crate::asm::isb();
575 }
576
577 /// Invalidates D-cache by address.
578 ///
579 /// * `addr`: The address to invalidate, which must be cache-line aligned.
580 /// * `size`: Number of bytes to invalidate, which must be a multiple of the cache line size.
581 ///
582 /// Invalidates D-cache cache lines, starting from the first line containing `addr`,
583 /// finishing once at least `size` bytes have been invalidated.
584 ///
585 /// Invalidation causes the next read access to memory to be fetched from main memory instead
586 /// of the cache.
587 ///
588 /// # Cache Line Sizes
589 ///
590 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
591 /// to 32 bytes, which means `addr` must be 32-byte aligned and `size` must be a multiple
592 /// of 32. At the time of writing, no other Cortex-M cores have data caches.
593 ///
594 /// If `addr` is not cache-line aligned, or `size` is not a multiple of the cache line size,
595 /// other data before or after the desired memory would also be invalidated, which can very
596 /// easily cause memory corruption and undefined behaviour.
597 ///
598 /// # Safety
599 ///
600 /// After invalidating, the next read of invalidated data will be from main memory. This may
601 /// cause recent writes to be lost, potentially including writes that initialized objects.
602 /// Therefore, this method may cause uninitialized memory or invalid values to be read,
603 /// resulting in undefined behaviour. You must ensure that main memory contains valid and
604 /// initialized values before invalidating.
605 ///
606 /// `addr` **must** be aligned to the size of the cache lines, and `size` **must** be a
607 /// multiple of the cache line size, otherwise this function will invalidate other memory,
608 /// easily leading to memory corruption and undefined behaviour. This precondition is checked
609 /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid
610 /// a runtime-dependent `panic!()` call.
611 #[inline]
612 pub unsafe fn invalidate_dcache_by_address(&mut self, addr: usize, size: usize) {
613 unsafe {
614 // No-op zero sized operations
615 if size == 0 {
616 return;
617 }
618
619 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
620 let mut cbp = CBP::new();
621
622 // dminline is log2(num words), so 2**dminline * 4 gives size in bytes
623 let dminline = CPUID::cache_dminline();
624 let line_size = (1 << dminline) * 4;
625
626 debug_assert!((addr & (line_size - 1)) == 0);
627 debug_assert!((size & (line_size - 1)) == 0);
628
629 crate::asm::dsb();
630
631 // Find number of cache lines to invalidate
632 let num_lines = ((size - 1) / line_size) + 1;
633
634 // Compute address of first cache line
635 let mask = 0xFFFF_FFFF - (line_size - 1);
636 let mut addr = addr & mask;
637
638 for _ in 0..num_lines {
639 cbp.dcimvac(addr as u32);
640 addr += line_size;
641 }
642
643 crate::asm::dsb();
644 crate::asm::isb();
645 }
646 }
647
648 /// Invalidates an object from the D-cache.
649 ///
650 /// * `obj`: The object to invalidate.
651 ///
652 /// Invalidates D-cache starting from the first cache line containing `obj`,
653 /// continuing to invalidate cache lines until all of `obj` has been invalidated.
654 ///
655 /// Invalidation causes the next read access to memory to be fetched from main memory instead
656 /// of the cache.
657 ///
658 /// # Cache Line Sizes
659 ///
660 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
661 /// to 32 bytes, which means `obj` must be 32-byte aligned, and its size must be a multiple
662 /// of 32 bytes. At the time of writing, no other Cortex-M cores have data caches.
663 ///
664 /// If `obj` is not cache-line aligned, or its size is not a multiple of the cache line size,
665 /// other data before or after the desired memory would also be invalidated, which can very
666 /// easily cause memory corruption and undefined behaviour.
667 ///
668 /// # Safety
669 ///
670 /// After invalidating, `obj` will be read from main memory on next access. This may cause
671 /// recent writes to `obj` to be lost, potentially including the write that initialized it.
672 /// Therefore, this method may cause uninitialized memory or invalid values to be read,
673 /// resulting in undefined behaviour. You must ensure that main memory contains a valid and
674 /// initialized value for T before invalidating `obj`.
675 ///
676 /// `obj` **must** be aligned to the size of the cache lines, and its size **must** be a
677 /// multiple of the cache line size, otherwise this function will invalidate other memory,
678 /// easily leading to memory corruption and undefined behaviour. This precondition is checked
679 /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid
680 /// a runtime-dependent `panic!()` call.
681 #[inline]
682 pub unsafe fn invalidate_dcache_by_ref<T>(&mut self, obj: &mut T) {
683 unsafe {
684 self.invalidate_dcache_by_address(obj as *const T as usize, core::mem::size_of::<T>());
685 }
686 }
687
688 /// Invalidates a slice from the D-cache.
689 ///
690 /// * `slice`: The slice to invalidate.
691 ///
692 /// Invalidates D-cache starting from the first cache line containing members of `slice`,
693 /// continuing to invalidate cache lines until all of `slice` has been invalidated.
694 ///
695 /// Invalidation causes the next read access to memory to be fetched from main memory instead
696 /// of the cache.
697 ///
698 /// # Cache Line Sizes
699 ///
700 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
701 /// to 32 bytes, which means `slice` must be 32-byte aligned, and its size must be a multiple
702 /// of 32 bytes. At the time of writing, no other Cortex-M cores have data caches.
703 ///
704 /// If `slice` is not cache-line aligned, or its size is not a multiple of the cache line size,
705 /// other data before or after the desired memory would also be invalidated, which can very
706 /// easily cause memory corruption and undefined behaviour.
707 ///
708 /// # Safety
709 ///
710 /// After invalidating, `slice` will be read from main memory on next access. This may cause
711 /// recent writes to `slice` to be lost, potentially including the write that initialized it.
712 /// Therefore, this method may cause uninitialized memory or invalid values to be read,
713 /// resulting in undefined behaviour. You must ensure that main memory contains valid and
714 /// initialized values for T before invalidating `slice`.
715 ///
716 /// `slice` **must** be aligned to the size of the cache lines, and its size **must** be a
717 /// multiple of the cache line size, otherwise this function will invalidate other memory,
718 /// easily leading to memory corruption and undefined behaviour. This precondition is checked
719 /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid
720 /// a runtime-dependent `panic!()` call.
721 #[inline]
722 pub unsafe fn invalidate_dcache_by_slice<T>(&mut self, slice: &mut [T]) {
723 unsafe {
724 self.invalidate_dcache_by_address(
725 slice.as_ptr() as usize,
726 core::mem::size_of_val(slice),
727 );
728 }
729 }
730
731 /// Cleans D-cache by address.
732 ///
733 /// * `addr`: The address to start cleaning at.
734 /// * `size`: The number of bytes to clean.
735 ///
736 /// Cleans D-cache cache lines, starting from the first line containing `addr`,
737 /// finishing once at least `size` bytes have been invalidated.
738 ///
739 /// Cleaning the cache causes whatever data is present in the cache to be immediately written
740 /// to main memory, overwriting whatever was in main memory.
741 ///
742 /// # Cache Line Sizes
743 ///
744 /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed
745 /// to 32 bytes, which means `addr` should generally be 32-byte aligned and `size` should be a
746 /// multiple of 32. At the time of writing, no other Cortex-M cores have data caches.
747 ///
748 /// If `addr` is not cache-line aligned, or `size` is not a multiple of the cache line size,
749 /// other data before or after the desired memory will also be cleaned. From the point of view
750 /// of the core executing this function, memory remains consistent, so this is not unsound,
751 /// but is worth knowing about.
752 #[inline]
753 pub fn clean_dcache_by_address(&mut self, addr: usize, size: usize) {
754 // No-op zero sized operations
755 if size == 0 {
756 return;
757 }
758
759 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
760 let mut cbp = unsafe { CBP::new() };
761
762 crate::asm::dsb();
763
764 let dminline = CPUID::cache_dminline();
765 let line_size = (1 << dminline) * 4;
766 let num_lines = ((size - 1) / line_size) + 1;
767
768 let mask = 0xFFFF_FFFF - (line_size - 1);
769 let mut addr = addr & mask;
770
771 for _ in 0..num_lines {
772 cbp.dccmvac(addr as u32);
773 addr += line_size;
774 }
775
776 crate::asm::dsb();
777 crate::asm::isb();
778 }
779
780 /// Cleans an object from the D-cache.
781 ///
782 /// * `obj`: The object to clean.
783 ///
784 /// Cleans D-cache starting from the first cache line containing `obj`,
785 /// continuing to clean cache lines until all of `obj` has been cleaned.
786 ///
787 /// It is recommended that `obj` is both aligned to the cache line size and a multiple of
788 /// the cache line size long, otherwise surrounding data will also be cleaned.
789 ///
790 /// Cleaning the cache causes whatever data is present in the cache to be immediately written
791 /// to main memory, overwriting whatever was in main memory.
792 #[inline]
793 pub fn clean_dcache_by_ref<T>(&mut self, obj: &T) {
794 self.clean_dcache_by_address(obj as *const T as usize, core::mem::size_of::<T>());
795 }
796
797 /// Cleans a slice from D-cache.
798 ///
799 /// * `slice`: The slice to clean.
800 ///
801 /// Cleans D-cache starting from the first cache line containing members of `slice`,
802 /// continuing to clean cache lines until all of `slice` has been cleaned.
803 ///
804 /// It is recommended that `slice` is both aligned to the cache line size and a multiple of
805 /// the cache line size long, otherwise surrounding data will also be cleaned.
806 ///
807 /// Cleaning the cache causes whatever data is present in the cache to be immediately written
808 /// to main memory, overwriting whatever was in main memory.
809 #[inline]
810 pub fn clean_dcache_by_slice<T>(&mut self, slice: &[T]) {
811 self.clean_dcache_by_address(slice.as_ptr() as usize, core::mem::size_of_val(slice));
812 }
813
814 /// Cleans and invalidates D-cache by address.
815 ///
816 /// * `addr`: The address to clean and invalidate.
817 /// * `size`: The number of bytes to clean and invalidate.
818 ///
819 /// Cleans and invalidates D-cache starting from the first cache line containing `addr`,
820 /// finishing once at least `size` bytes have been cleaned and invalidated.
821 ///
822 /// It is recommended that `addr` is aligned to the cache line size and `size` is a multiple of
823 /// the cache line size, otherwise surrounding data will also be cleaned.
824 ///
825 /// Cleaning and invalidating causes data in the D-cache to be written back to main memory,
826 /// and then marks that data in the D-cache as invalid, causing future reads to first fetch
827 /// from main memory.
828 #[inline]
829 pub fn clean_invalidate_dcache_by_address(&mut self, addr: usize, size: usize) {
830 // No-op zero sized operations
831 if size == 0 {
832 return;
833 }
834
835 // NOTE(unsafe): No races as all CBP registers are write-only and stateless
836 let mut cbp = unsafe { CBP::new() };
837
838 crate::asm::dsb();
839
840 // Cache lines are fixed to 32 bit on Cortex-M7 and not present in earlier Cortex-M
841 const LINESIZE: usize = 32;
842 let num_lines = ((size - 1) / LINESIZE) + 1;
843
844 let mut addr = addr & 0xFFFF_FFE0;
845
846 for _ in 0..num_lines {
847 cbp.dccimvac(addr as u32);
848 addr += LINESIZE;
849 }
850
851 crate::asm::dsb();
852 crate::asm::isb();
853 }
854}
855
856const SCB_SCR_SLEEPDEEP: u32 = 0x1 << 2;
857
858impl SCB {
859 /// Set the SLEEPDEEP bit in the SCR register
860 #[inline]
861 pub fn set_sleepdeep(&mut self) {
862 unsafe {
863 self.scr.modify(|scr| scr | SCB_SCR_SLEEPDEEP);
864 }
865 }
866
867 /// Clear the SLEEPDEEP bit in the SCR register
868 #[inline]
869 pub fn clear_sleepdeep(&mut self) {
870 unsafe {
871 self.scr.modify(|scr| scr & !SCB_SCR_SLEEPDEEP);
872 }
873 }
874}
875
876const SCB_SCR_SLEEPONEXIT: u32 = 0x1 << 1;
877
878impl SCB {
879 /// Set the SLEEPONEXIT bit in the SCR register
880 #[inline]
881 pub fn set_sleeponexit(&mut self) {
882 unsafe {
883 self.scr.modify(|scr| scr | SCB_SCR_SLEEPONEXIT);
884 }
885 }
886
887 /// Clear the SLEEPONEXIT bit in the SCR register
888 #[inline]
889 pub fn clear_sleeponexit(&mut self) {
890 unsafe {
891 self.scr.modify(|scr| scr & !SCB_SCR_SLEEPONEXIT);
892 }
893 }
894}
895
896const SCB_SCR_SEVONPEND: u32 = 0x1 << 4;
897
898impl SCB {
899 /// Set the SEVONPEND bit in the SCR register
900 #[inline]
901 pub fn set_sevonpend(&mut self) {
902 unsafe {
903 self.scr.modify(|scr| scr | SCB_SCR_SEVONPEND);
904 }
905 }
906
907 /// Clear the SEVONPEND bit in the SCR register
908 #[inline]
909 pub fn clear_sevonpend(&mut self) {
910 unsafe {
911 self.scr.modify(|scr| scr & !SCB_SCR_SEVONPEND);
912 }
913 }
914}
915
916const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16;
917const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x7 << 8;
918const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2;
919
920impl SCB {
921 /// Initiate a system reset request to reset the MCU
922 #[inline]
923 pub fn sys_reset() -> ! {
924 crate::asm::dsb();
925 unsafe {
926 (*Self::PTR).aircr.modify(
927 |r| {
928 SCB_AIRCR_VECTKEY | // otherwise the write is ignored
929 r & SCB_AIRCR_PRIGROUP_MASK | // keep priority group unchanged
930 SCB_AIRCR_SYSRESETREQ
931 }, // set the bit
932 )
933 };
934 crate::asm::dsb();
935 loop {
936 // wait for the reset
937 crate::asm::nop(); // avoid rust-lang/rust#28728
938 }
939 }
940}
941
942const SCB_ICSR_PENDSVSET: u32 = 1 << 28;
943const SCB_ICSR_PENDSVCLR: u32 = 1 << 27;
944
945const SCB_ICSR_PENDSTSET: u32 = 1 << 26;
946const SCB_ICSR_PENDSTCLR: u32 = 1 << 25;
947
948impl SCB {
949 /// Set the PENDSVSET bit in the ICSR register which will pend the PendSV interrupt
950 #[inline]
951 pub fn set_pendsv() {
952 unsafe {
953 (*Self::PTR).icsr.write(SCB_ICSR_PENDSVSET);
954 }
955 }
956
957 /// Check if PENDSVSET bit in the ICSR register is set meaning PendSV interrupt is pending
958 #[inline]
959 pub fn is_pendsv_pending() -> bool {
960 unsafe { (*Self::PTR).icsr.read() & SCB_ICSR_PENDSVSET == SCB_ICSR_PENDSVSET }
961 }
962
963 /// Set the PENDSVCLR bit in the ICSR register which will clear a pending PendSV interrupt
964 #[inline]
965 pub fn clear_pendsv() {
966 unsafe {
967 (*Self::PTR).icsr.write(SCB_ICSR_PENDSVCLR);
968 }
969 }
970
971 /// Set the PENDSTSET bit in the ICSR register which will pend a SysTick interrupt
972 #[inline]
973 pub fn set_pendst() {
974 unsafe {
975 (*Self::PTR).icsr.write(SCB_ICSR_PENDSTSET);
976 }
977 }
978
979 /// Check if PENDSTSET bit in the ICSR register is set meaning SysTick interrupt is pending
980 #[inline]
981 pub fn is_pendst_pending() -> bool {
982 unsafe { (*Self::PTR).icsr.read() & SCB_ICSR_PENDSTSET == SCB_ICSR_PENDSTSET }
983 }
984
985 /// Set the PENDSTCLR bit in the ICSR register which will clear a pending SysTick interrupt
986 #[inline]
987 pub fn clear_pendst() {
988 unsafe {
989 (*Self::PTR).icsr.write(SCB_ICSR_PENDSTCLR);
990 }
991 }
992}
993
994/// System handlers, exceptions with configurable priority
995#[derive(Clone, Copy, Debug, Eq, PartialEq)]
996#[repr(u8)]
997pub enum SystemHandler {
998 // NonMaskableInt, // priority is fixed
999 // HardFault, // priority is fixed
1000 /// Memory management interrupt (not present on Cortex-M0 variants)
1001 #[cfg(not(armv6m))]
1002 MemoryManagement = 4,
1003
1004 /// Bus fault interrupt (not present on Cortex-M0 variants)
1005 #[cfg(not(armv6m))]
1006 BusFault = 5,
1007
1008 /// Usage fault interrupt (not present on Cortex-M0 variants)
1009 #[cfg(not(armv6m))]
1010 UsageFault = 6,
1011
1012 /// Secure fault interrupt (only on ARMv8-M)
1013 #[cfg(any(armv8m, native))]
1014 SecureFault = 7,
1015
1016 /// SV call interrupt
1017 SVCall = 11,
1018
1019 /// Debug monitor interrupt (not present on Cortex-M0 variants)
1020 #[cfg(not(armv6m))]
1021 DebugMonitor = 12,
1022
1023 /// Pend SV interrupt
1024 PendSV = 14,
1025
1026 /// System Tick interrupt
1027 SysTick = 15,
1028}
1029
1030impl SCB {
1031 /// Returns the hardware priority of `system_handler`
1032 ///
1033 /// *NOTE*: Hardware priority does not exactly match logical priority levels. See
1034 /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details.
1035 #[inline]
1036 pub fn get_priority(system_handler: SystemHandler) -> u8 {
1037 let index = system_handler as u8;
1038
1039 #[cfg(not(armv6m))]
1040 {
1041 // NOTE(unsafe) atomic read with no side effects
1042
1043 // NOTE(unsafe): Index is bounded to [4,15] by SystemHandler design.
1044 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1045 let priority_ref = unsafe { (*Self::PTR).shpr.get_unchecked(usize::from(index - 4)) };
1046
1047 priority_ref.read()
1048 }
1049
1050 #[cfg(armv6m)]
1051 {
1052 // NOTE(unsafe) atomic read with no side effects
1053
1054 // NOTE(unsafe): Index is bounded to [11,15] by SystemHandler design.
1055 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1056 let priority_ref = unsafe {
1057 (*Self::PTR)
1058 .shpr
1059 .get_unchecked(usize::from((index - 8) / 4))
1060 };
1061
1062 let shpr = priority_ref.read();
1063 let prio = (shpr >> (8 * (index % 4))) & 0x0000_00ff;
1064 prio as u8
1065 }
1066 }
1067
1068 /// Sets the hardware priority of `system_handler` to `prio`
1069 ///
1070 /// *NOTE*: Hardware priority does not exactly match logical priority levels. See
1071 /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details.
1072 ///
1073 /// On ARMv6-M, updating a system handler priority requires a read-modify-write operation. On
1074 /// ARMv7-M, the operation is performed in a single, atomic write operation.
1075 ///
1076 /// # Unsafety
1077 ///
1078 /// Changing priority levels can break priority-based critical sections (see
1079 /// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
1080 #[inline]
1081 pub unsafe fn set_priority(&mut self, system_handler: SystemHandler, prio: u8) {
1082 unsafe {
1083 let index = system_handler as u8;
1084
1085 #[cfg(not(armv6m))]
1086 {
1087 // NOTE(unsafe): Index is bounded to [4,15] by SystemHandler design.
1088 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1089 let priority_ref = (*Self::PTR).shpr.get_unchecked(usize::from(index - 4));
1090
1091 priority_ref.write(prio)
1092 }
1093
1094 #[cfg(armv6m)]
1095 {
1096 // NOTE(unsafe): Index is bounded to [11,15] by SystemHandler design.
1097 // TODO: Review it after rust-lang/rust/issues/13926 will be fixed.
1098 let priority_ref = (*Self::PTR)
1099 .shpr
1100 .get_unchecked(usize::from((index - 8) / 4));
1101
1102 priority_ref.modify(|value| {
1103 let shift = 8 * (index % 4);
1104 let mask = 0x0000_00ff << shift;
1105 let prio = u32::from(prio) << shift;
1106
1107 (value & !mask) | prio
1108 });
1109 }
1110 }
1111 }
1112
1113 /// Return the bit position of the exception enable bit in the SHCSR register
1114 #[inline]
1115 #[cfg(not(any(armv6m, armv8m_base)))]
1116 fn shcsr_enable_shift(exception: Exception) -> Option<u32> {
1117 match exception {
1118 Exception::MemoryManagement => Some(16),
1119 Exception::BusFault => Some(17),
1120 Exception::UsageFault => Some(18),
1121 #[cfg(armv8m_main)]
1122 Exception::SecureFault => Some(19),
1123 _ => None,
1124 }
1125 }
1126
1127 /// Enable the exception
1128 ///
1129 /// If the exception is enabled, when the exception is triggered, the exception handler will be executed instead of the
1130 /// HardFault handler.
1131 /// This function is only allowed on the following exceptions:
1132 /// * `MemoryManagement`
1133 /// * `BusFault`
1134 /// * `UsageFault`
1135 /// * `SecureFault` (can only be enabled from Secure state)
1136 ///
1137 /// Calling this function with any other exception will do nothing.
1138 #[inline]
1139 #[cfg(not(any(armv6m, armv8m_base)))]
1140 pub fn enable(&mut self, exception: Exception) {
1141 if let Some(shift) = SCB::shcsr_enable_shift(exception) {
1142 // The mutable reference to SCB makes sure that only this code is currently modifying
1143 // the register.
1144 unsafe { self.shcsr.modify(|value| value | (1 << shift)) }
1145 }
1146 }
1147
1148 /// Disable the exception
1149 ///
1150 /// If the exception is disabled, when the exception is triggered, the HardFault handler will be executed instead of the
1151 /// exception handler.
1152 /// This function is only allowed on the following exceptions:
1153 /// * `MemoryManagement`
1154 /// * `BusFault`
1155 /// * `UsageFault`
1156 /// * `SecureFault` (can not be changed from Non-secure state)
1157 ///
1158 /// Calling this function with any other exception will do nothing.
1159 #[inline]
1160 #[cfg(not(any(armv6m, armv8m_base)))]
1161 pub fn disable(&mut self, exception: Exception) {
1162 if let Some(shift) = SCB::shcsr_enable_shift(exception) {
1163 // The mutable reference to SCB makes sure that only this code is currently modifying
1164 // the register.
1165 unsafe { self.shcsr.modify(|value| value & !(1 << shift)) }
1166 }
1167 }
1168
1169 /// Check if an exception is enabled
1170 ///
1171 /// This function is only allowed on the following exception:
1172 /// * `MemoryManagement`
1173 /// * `BusFault`
1174 /// * `UsageFault`
1175 /// * `SecureFault` (can not be read from Non-secure state)
1176 ///
1177 /// Calling this function with any other exception will read `false`.
1178 #[inline]
1179 #[cfg(not(any(armv6m, armv8m_base)))]
1180 pub fn is_enabled(&self, exception: Exception) -> bool {
1181 if let Some(shift) = SCB::shcsr_enable_shift(exception) {
1182 (self.shcsr.read() & (1 << shift)) > 0
1183 } else {
1184 false
1185 }
1186 }
1187}