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                "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}