Skip to main content

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}