diff options
Diffstat (limited to 'src/peripheral/scb.rs')
-rw-r--r-- | src/peripheral/scb.rs | 1110 |
1 files changed, 0 insertions, 1110 deletions
diff --git a/src/peripheral/scb.rs b/src/peripheral/scb.rs deleted file mode 100644 index b9cf0e4..0000000 --- a/src/peripheral/scb.rs +++ /dev/null @@ -1,1110 +0,0 @@ -//! System Control Block - -use core::ptr; - -use volatile_register::RW; - -#[cfg(not(armv6m))] -use super::cpuid::CsselrCacheType; -#[cfg(not(armv6m))] -use super::CBP; -#[cfg(not(armv6m))] -use super::CPUID; -use super::SCB; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// Register block -#[repr(C)] -pub struct RegisterBlock { - /// Interrupt Control and State - pub icsr: RW<u32>, - - /// Vector Table Offset (not present on Cortex-M0 variants) - pub vtor: RW<u32>, - - /// Application Interrupt and Reset Control - pub aircr: RW<u32>, - - /// System Control - pub scr: RW<u32>, - - /// Configuration and Control - pub ccr: RW<u32>, - - /// System Handler Priority (word accessible only on Cortex-M0 variants) - /// - /// On ARMv7-M, `shpr[0]` points to SHPR1 - /// - /// On ARMv6-M, `shpr[0]` points to SHPR2 - #[cfg(not(armv6m))] - pub shpr: [RW<u8>; 12], - #[cfg(armv6m)] - _reserved1: u32, - /// System Handler Priority (word accessible only on Cortex-M0 variants) - /// - /// On ARMv7-M, `shpr[0]` points to SHPR1 - /// - /// On ARMv6-M, `shpr[0]` points to SHPR2 - #[cfg(armv6m)] - pub shpr: [RW<u32>; 2], - - /// System Handler Control and State - pub shcsr: RW<u32>, - - /// Configurable Fault Status (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - pub cfsr: RW<u32>, - #[cfg(armv6m)] - _reserved2: u32, - - /// HardFault Status (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - pub hfsr: RW<u32>, - #[cfg(armv6m)] - _reserved3: u32, - - /// Debug Fault Status (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - pub dfsr: RW<u32>, - #[cfg(armv6m)] - _reserved4: u32, - - /// MemManage Fault Address (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - pub mmfar: RW<u32>, - #[cfg(armv6m)] - _reserved5: u32, - - /// BusFault Address (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - pub bfar: RW<u32>, - #[cfg(armv6m)] - _reserved6: u32, - - /// Auxiliary Fault Status (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - pub afsr: RW<u32>, - #[cfg(armv6m)] - _reserved7: u32, - - _reserved8: [u32; 18], - - /// Coprocessor Access Control (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - pub cpacr: RW<u32>, - #[cfg(armv6m)] - _reserved9: u32, -} - -/// FPU access mode -#[cfg(has_fpu)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum FpuAccessMode { - /// FPU is not accessible - Disabled, - /// FPU is accessible in Privileged and User mode - Enabled, - /// FPU is accessible in Privileged mode only - Privileged, -} - -#[cfg(has_fpu)] -mod fpu_consts { - pub const SCB_CPACR_FPU_MASK: u32 = 0b11_11 << 20; - pub const SCB_CPACR_FPU_ENABLE: u32 = 0b01_01 << 20; - pub const SCB_CPACR_FPU_USER: u32 = 0b10_10 << 20; -} - -#[cfg(has_fpu)] -use self::fpu_consts::*; - -#[cfg(has_fpu)] -impl SCB { - /// Shorthand for `set_fpu_access_mode(FpuAccessMode::Disabled)` - #[inline] - pub fn disable_fpu(&mut self) { - self.set_fpu_access_mode(FpuAccessMode::Disabled) - } - - /// Shorthand for `set_fpu_access_mode(FpuAccessMode::Enabled)` - #[inline] - pub fn enable_fpu(&mut self) { - self.set_fpu_access_mode(FpuAccessMode::Enabled) - } - - /// Gets FPU access mode - #[inline] - pub fn fpu_access_mode() -> FpuAccessMode { - // NOTE(unsafe) atomic read operation with no side effects - let cpacr = unsafe { (*Self::PTR).cpacr.read() }; - - if cpacr & SCB_CPACR_FPU_MASK == SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER { - FpuAccessMode::Enabled - } else if cpacr & SCB_CPACR_FPU_MASK == SCB_CPACR_FPU_ENABLE { - FpuAccessMode::Privileged - } else { - FpuAccessMode::Disabled - } - } - - /// Sets FPU access mode - /// - /// *IMPORTANT* Any function that runs fully or partly with the FPU disabled must *not* take any - /// floating-point arguments or have any floating-point local variables. Because the compiler - /// might inline such a function into a caller that does have floating-point arguments or - /// variables, any such function must be also marked #[inline(never)]. - #[inline] - pub fn set_fpu_access_mode(&mut self, mode: FpuAccessMode) { - let mut cpacr = self.cpacr.read() & !SCB_CPACR_FPU_MASK; - match mode { - FpuAccessMode::Disabled => (), - FpuAccessMode::Privileged => cpacr |= SCB_CPACR_FPU_ENABLE, - FpuAccessMode::Enabled => cpacr |= SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER, - } - unsafe { self.cpacr.write(cpacr) } - } -} - -impl SCB { - /// Returns the active exception number - #[inline] - pub fn vect_active() -> VectActive { - let icsr = - unsafe { ptr::read_volatile(&(*SCB::PTR).icsr as *const _ as *const u32) } & 0x1FF; - - match icsr as u16 { - 0 => VectActive::ThreadMode, - 2 => VectActive::Exception(Exception::NonMaskableInt), - 3 => VectActive::Exception(Exception::HardFault), - #[cfg(not(armv6m))] - 4 => VectActive::Exception(Exception::MemoryManagement), - #[cfg(not(armv6m))] - 5 => VectActive::Exception(Exception::BusFault), - #[cfg(not(armv6m))] - 6 => VectActive::Exception(Exception::UsageFault), - #[cfg(any(armv8m, native))] - 7 => VectActive::Exception(Exception::SecureFault), - 11 => VectActive::Exception(Exception::SVCall), - #[cfg(not(armv6m))] - 12 => VectActive::Exception(Exception::DebugMonitor), - 14 => VectActive::Exception(Exception::PendSV), - 15 => VectActive::Exception(Exception::SysTick), - irqn => VectActive::Interrupt { irqn: irqn - 16 }, - } - } -} - -/// Processor core exceptions (internal interrupts) -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", derive(PartialOrd, Hash))] -pub enum Exception { - /// Non maskable interrupt - NonMaskableInt, - - /// Hard fault interrupt - HardFault, - - /// Memory management interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - MemoryManagement, - - /// Bus fault interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - BusFault, - - /// Usage fault interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - UsageFault, - - /// Secure fault interrupt (only on ARMv8-M) - #[cfg(any(armv8m, native))] - SecureFault, - - /// SV call interrupt - SVCall, - - /// Debug monitor interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - DebugMonitor, - - /// Pend SV interrupt - PendSV, - - /// System Tick interrupt - SysTick, -} - -impl Exception { - /// Returns the IRQ number of this `Exception` - /// - /// The return value is always within the closed range `[-1, -14]` - #[inline] - pub fn irqn(self) -> i8 { - match self { - Exception::NonMaskableInt => -14, - Exception::HardFault => -13, - #[cfg(not(armv6m))] - Exception::MemoryManagement => -12, - #[cfg(not(armv6m))] - Exception::BusFault => -11, - #[cfg(not(armv6m))] - Exception::UsageFault => -10, - #[cfg(any(armv8m, native))] - Exception::SecureFault => -9, - Exception::SVCall => -5, - #[cfg(not(armv6m))] - Exception::DebugMonitor => -4, - Exception::PendSV => -2, - Exception::SysTick => -1, - } - } -} - -/// Active exception number -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "std", derive(PartialOrd, Hash))] -pub enum VectActive { - /// Thread mode - ThreadMode, - - /// Processor core exception (internal interrupts) - Exception(Exception), - - /// Device specific exception (external interrupts) - Interrupt { - /// Interrupt number. This number is always within half open range `[0, 512)` (9 bit) - irqn: u16, - }, -} - -impl VectActive { - /// Converts a vector number into `VectActive` - #[inline] - pub fn from(vect_active: u16) -> Option<Self> { - Some(match vect_active { - 0 => VectActive::ThreadMode, - 2 => VectActive::Exception(Exception::NonMaskableInt), - 3 => VectActive::Exception(Exception::HardFault), - #[cfg(not(armv6m))] - 4 => VectActive::Exception(Exception::MemoryManagement), - #[cfg(not(armv6m))] - 5 => VectActive::Exception(Exception::BusFault), - #[cfg(not(armv6m))] - 6 => VectActive::Exception(Exception::UsageFault), - #[cfg(any(armv8m, native))] - 7 => VectActive::Exception(Exception::SecureFault), - 11 => VectActive::Exception(Exception::SVCall), - #[cfg(not(armv6m))] - 12 => VectActive::Exception(Exception::DebugMonitor), - 14 => VectActive::Exception(Exception::PendSV), - 15 => VectActive::Exception(Exception::SysTick), - irqn if (16..512).contains(&irqn) => VectActive::Interrupt { irqn: irqn - 16 }, - _ => return None, - }) - } -} - -#[cfg(not(armv6m))] -mod scb_consts { - pub const SCB_CCR_IC_MASK: u32 = 1 << 17; - pub const SCB_CCR_DC_MASK: u32 = 1 << 16; -} - -#[cfg(not(armv6m))] -use self::scb_consts::*; - -#[cfg(not(armv6m))] -impl SCB { - /// Enables I-cache if currently disabled. - /// - /// This operation first invalidates the entire I-cache. - #[inline] - pub fn enable_icache(&mut self) { - // Don't do anything if I-cache is already enabled - if Self::icache_enabled() { - return; - } - - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = unsafe { CBP::new() }; - - // Invalidate I-cache - cbp.iciallu(); - - // Enable I-cache - extern "C" { - // see asm-v7m.s - fn __enable_icache(); - } - - // NOTE(unsafe): The asm routine manages exclusive access to the SCB - // registers and applies the proper barriers; it is technically safe on - // its own, and is only `unsafe` here because it's `extern "C"`. - unsafe { - __enable_icache(); - } - } - - /// Disables I-cache if currently enabled. - /// - /// This operation invalidates the entire I-cache after disabling. - #[inline] - pub fn disable_icache(&mut self) { - // Don't do anything if I-cache is already disabled - if !Self::icache_enabled() { - return; - } - - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = unsafe { CBP::new() }; - - // Disable I-cache - // NOTE(unsafe): We have synchronised access by &mut self - unsafe { self.ccr.modify(|r| r & !SCB_CCR_IC_MASK) }; - - // Invalidate I-cache - cbp.iciallu(); - - crate::asm::dsb(); - crate::asm::isb(); - } - - /// Returns whether the I-cache is currently enabled. - #[inline(always)] - pub fn icache_enabled() -> bool { - crate::asm::dsb(); - crate::asm::isb(); - - // NOTE(unsafe): atomic read with no side effects - unsafe { (*Self::PTR).ccr.read() & SCB_CCR_IC_MASK == SCB_CCR_IC_MASK } - } - - /// Invalidates the entire I-cache. - #[inline] - pub fn invalidate_icache(&mut self) { - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = unsafe { CBP::new() }; - - // Invalidate I-cache - cbp.iciallu(); - - crate::asm::dsb(); - crate::asm::isb(); - } - - /// Enables D-cache if currently disabled. - /// - /// This operation first invalidates the entire D-cache, ensuring it does - /// not contain stale values before being enabled. - #[inline] - pub fn enable_dcache(&mut self, cpuid: &mut CPUID) { - // Don't do anything if D-cache is already enabled - if Self::dcache_enabled() { - return; - } - - // Invalidate anything currently in the D-cache - unsafe { self.invalidate_dcache(cpuid) }; - - // Now turn on the D-cache - extern "C" { - // see asm-v7m.s - fn __enable_dcache(); - } - - // NOTE(unsafe): The asm routine manages exclusive access to the SCB - // registers and applies the proper barriers; it is technically safe on - // its own, and is only `unsafe` here because it's `extern "C"`. - unsafe { - __enable_dcache(); - } - } - - /// Disables D-cache if currently enabled. - /// - /// This operation subsequently cleans and invalidates the entire D-cache, - /// ensuring all contents are safely written back to main memory after disabling. - #[inline] - pub fn disable_dcache(&mut self, cpuid: &mut CPUID) { - // Don't do anything if D-cache is already disabled - if !Self::dcache_enabled() { - return; - } - - // Turn off the D-cache - // NOTE(unsafe): We have synchronised access by &mut self - unsafe { self.ccr.modify(|r| r & !SCB_CCR_DC_MASK) }; - - // Clean and invalidate whatever was left in it - self.clean_invalidate_dcache(cpuid); - } - - /// Returns whether the D-cache is currently enabled. - #[inline] - pub fn dcache_enabled() -> bool { - crate::asm::dsb(); - crate::asm::isb(); - - // NOTE(unsafe) atomic read with no side effects - unsafe { (*Self::PTR).ccr.read() & SCB_CCR_DC_MASK == SCB_CCR_DC_MASK } - } - - /// Invalidates the entire D-cache. - /// - /// Note that calling this while the dcache is enabled will probably wipe out the - /// stack, depending on optimisations, therefore breaking returning to the call point. - /// - /// It's used immediately before enabling the dcache, but not exported publicly. - #[inline] - unsafe fn invalidate_dcache(&mut self, cpuid: &mut CPUID) { - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = CBP::new(); - - // Read number of sets and ways - let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified); - - // Invalidate entire D-cache - for set in 0..sets { - for way in 0..ways { - cbp.dcisw(set, way); - } - } - - crate::asm::dsb(); - crate::asm::isb(); - } - - /// Cleans the entire D-cache. - /// - /// This function causes everything in the D-cache to be written back to main memory, - /// overwriting whatever is already there. - #[inline] - pub fn clean_dcache(&mut self, cpuid: &mut CPUID) { - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = unsafe { CBP::new() }; - - // Read number of sets and ways - let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified); - - for set in 0..sets { - for way in 0..ways { - cbp.dccsw(set, way); - } - } - - crate::asm::dsb(); - crate::asm::isb(); - } - - /// Cleans and invalidates the entire D-cache. - /// - /// This function causes everything in the D-cache to be written back to main memory, - /// and then marks the entire D-cache as invalid, causing future reads to first fetch - /// from main memory. - #[inline] - pub fn clean_invalidate_dcache(&mut self, cpuid: &mut CPUID) { - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = unsafe { CBP::new() }; - - // Read number of sets and ways - let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified); - - for set in 0..sets { - for way in 0..ways { - cbp.dccisw(set, way); - } - } - - crate::asm::dsb(); - crate::asm::isb(); - } - - /// Invalidates D-cache by address. - /// - /// * `addr`: The address to invalidate, which must be cache-line aligned. - /// * `size`: Number of bytes to invalidate, which must be a multiple of the cache line size. - /// - /// Invalidates D-cache cache lines, starting from the first line containing `addr`, - /// finishing once at least `size` bytes have been invalidated. - /// - /// Invalidation causes the next read access to memory to be fetched from main memory instead - /// of the cache. - /// - /// # Cache Line Sizes - /// - /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed - /// to 32 bytes, which means `addr` must be 32-byte aligned and `size` must be a multiple - /// of 32. At the time of writing, no other Cortex-M cores have data caches. - /// - /// If `addr` is not cache-line aligned, or `size` is not a multiple of the cache line size, - /// other data before or after the desired memory would also be invalidated, which can very - /// easily cause memory corruption and undefined behaviour. - /// - /// # Safety - /// - /// After invalidating, the next read of invalidated data will be from main memory. This may - /// cause recent writes to be lost, potentially including writes that initialized objects. - /// Therefore, this method may cause uninitialized memory or invalid values to be read, - /// resulting in undefined behaviour. You must ensure that main memory contains valid and - /// initialized values before invalidating. - /// - /// `addr` **must** be aligned to the size of the cache lines, and `size` **must** be a - /// multiple of the cache line size, otherwise this function will invalidate other memory, - /// easily leading to memory corruption and undefined behaviour. This precondition is checked - /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid - /// a runtime-dependent `panic!()` call. - #[inline] - pub unsafe fn invalidate_dcache_by_address(&mut self, addr: usize, size: usize) { - // No-op zero sized operations - if size == 0 { - return; - } - - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = CBP::new(); - - // dminline is log2(num words), so 2**dminline * 4 gives size in bytes - let dminline = CPUID::cache_dminline(); - let line_size = (1 << dminline) * 4; - - debug_assert!((addr & (line_size - 1)) == 0); - debug_assert!((size & (line_size - 1)) == 0); - - crate::asm::dsb(); - - // Find number of cache lines to invalidate - let num_lines = ((size - 1) / line_size) + 1; - - // Compute address of first cache line - let mask = 0xFFFF_FFFF - (line_size - 1); - let mut addr = addr & mask; - - for _ in 0..num_lines { - cbp.dcimvac(addr as u32); - addr += line_size; - } - - crate::asm::dsb(); - crate::asm::isb(); - } - - /// Invalidates an object from the D-cache. - /// - /// * `obj`: The object to invalidate. - /// - /// Invalidates D-cache starting from the first cache line containing `obj`, - /// continuing to invalidate cache lines until all of `obj` has been invalidated. - /// - /// Invalidation causes the next read access to memory to be fetched from main memory instead - /// of the cache. - /// - /// # Cache Line Sizes - /// - /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed - /// to 32 bytes, which means `obj` must be 32-byte aligned, and its size must be a multiple - /// of 32 bytes. At the time of writing, no other Cortex-M cores have data caches. - /// - /// If `obj` is not cache-line aligned, or its size is not a multiple of the cache line size, - /// other data before or after the desired memory would also be invalidated, which can very - /// easily cause memory corruption and undefined behaviour. - /// - /// # Safety - /// - /// After invalidating, `obj` will be read from main memory on next access. This may cause - /// recent writes to `obj` to be lost, potentially including the write that initialized it. - /// Therefore, this method may cause uninitialized memory or invalid values to be read, - /// resulting in undefined behaviour. You must ensure that main memory contains a valid and - /// initialized value for T before invalidating `obj`. - /// - /// `obj` **must** be aligned to the size of the cache lines, and its size **must** be a - /// multiple of the cache line size, otherwise this function will invalidate other memory, - /// easily leading to memory corruption and undefined behaviour. This precondition is checked - /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid - /// a runtime-dependent `panic!()` call. - #[inline] - pub unsafe fn invalidate_dcache_by_ref<T>(&mut self, obj: &mut T) { - self.invalidate_dcache_by_address(obj as *const T as usize, core::mem::size_of::<T>()); - } - - /// Invalidates a slice from the D-cache. - /// - /// * `slice`: The slice to invalidate. - /// - /// Invalidates D-cache starting from the first cache line containing members of `slice`, - /// continuing to invalidate cache lines until all of `slice` has been invalidated. - /// - /// Invalidation causes the next read access to memory to be fetched from main memory instead - /// of the cache. - /// - /// # Cache Line Sizes - /// - /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed - /// to 32 bytes, which means `slice` must be 32-byte aligned, and its size must be a multiple - /// of 32 bytes. At the time of writing, no other Cortex-M cores have data caches. - /// - /// If `slice` is not cache-line aligned, or its size is not a multiple of the cache line size, - /// other data before or after the desired memory would also be invalidated, which can very - /// easily cause memory corruption and undefined behaviour. - /// - /// # Safety - /// - /// After invalidating, `slice` will be read from main memory on next access. This may cause - /// recent writes to `slice` to be lost, potentially including the write that initialized it. - /// Therefore, this method may cause uninitialized memory or invalid values to be read, - /// resulting in undefined behaviour. You must ensure that main memory contains valid and - /// initialized values for T before invalidating `slice`. - /// - /// `slice` **must** be aligned to the size of the cache lines, and its size **must** be a - /// multiple of the cache line size, otherwise this function will invalidate other memory, - /// easily leading to memory corruption and undefined behaviour. This precondition is checked - /// in debug builds using a `debug_assert!()`, but not checked in release builds to avoid - /// a runtime-dependent `panic!()` call. - #[inline] - pub unsafe fn invalidate_dcache_by_slice<T>(&mut self, slice: &mut [T]) { - self.invalidate_dcache_by_address( - slice.as_ptr() as usize, - slice.len() * core::mem::size_of::<T>(), - ); - } - - /// Cleans D-cache by address. - /// - /// * `addr`: The address to start cleaning at. - /// * `size`: The number of bytes to clean. - /// - /// Cleans D-cache cache lines, starting from the first line containing `addr`, - /// finishing once at least `size` bytes have been invalidated. - /// - /// Cleaning the cache causes whatever data is present in the cache to be immediately written - /// to main memory, overwriting whatever was in main memory. - /// - /// # Cache Line Sizes - /// - /// Cache line sizes vary by core. For all Cortex-M7 cores, the cache line size is fixed - /// to 32 bytes, which means `addr` should generally be 32-byte aligned and `size` should be a - /// multiple of 32. At the time of writing, no other Cortex-M cores have data caches. - /// - /// If `addr` is not cache-line aligned, or `size` is not a multiple of the cache line size, - /// other data before or after the desired memory will also be cleaned. From the point of view - /// of the core executing this function, memory remains consistent, so this is not unsound, - /// but is worth knowing about. - #[inline] - pub fn clean_dcache_by_address(&mut self, addr: usize, size: usize) { - // No-op zero sized operations - if size == 0 { - return; - } - - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = unsafe { CBP::new() }; - - crate::asm::dsb(); - - let dminline = CPUID::cache_dminline(); - let line_size = (1 << dminline) * 4; - let num_lines = ((size - 1) / line_size) + 1; - - let mask = 0xFFFF_FFFF - (line_size - 1); - let mut addr = addr & mask; - - for _ in 0..num_lines { - cbp.dccmvac(addr as u32); - addr += line_size; - } - - crate::asm::dsb(); - crate::asm::isb(); - } - - /// Cleans an object from the D-cache. - /// - /// * `obj`: The object to clean. - /// - /// Cleans D-cache starting from the first cache line containing `obj`, - /// continuing to clean cache lines until all of `obj` has been cleaned. - /// - /// It is recommended that `obj` is both aligned to the cache line size and a multiple of - /// the cache line size long, otherwise surrounding data will also be cleaned. - /// - /// Cleaning the cache causes whatever data is present in the cache to be immediately written - /// to main memory, overwriting whatever was in main memory. - #[inline] - pub fn clean_dcache_by_ref<T>(&mut self, obj: &T) { - self.clean_dcache_by_address(obj as *const T as usize, core::mem::size_of::<T>()); - } - - /// Cleans a slice from D-cache. - /// - /// * `slice`: The slice to clean. - /// - /// Cleans D-cache starting from the first cache line containing members of `slice`, - /// continuing to clean cache lines until all of `slice` has been cleaned. - /// - /// It is recommended that `slice` is both aligned to the cache line size and a multiple of - /// the cache line size long, otherwise surrounding data will also be cleaned. - /// - /// Cleaning the cache causes whatever data is present in the cache to be immediately written - /// to main memory, overwriting whatever was in main memory. - #[inline] - pub fn clean_dcache_by_slice<T>(&mut self, slice: &[T]) { - self.clean_dcache_by_address( - slice.as_ptr() as usize, - slice.len() * core::mem::size_of::<T>(), - ); - } - - /// Cleans and invalidates D-cache by address. - /// - /// * `addr`: The address to clean and invalidate. - /// * `size`: The number of bytes to clean and invalidate. - /// - /// Cleans and invalidates D-cache starting from the first cache line containing `addr`, - /// finishing once at least `size` bytes have been cleaned and invalidated. - /// - /// It is recommended that `addr` is aligned to the cache line size and `size` is a multiple of - /// the cache line size, otherwise surrounding data will also be cleaned. - /// - /// Cleaning and invalidating causes data in the D-cache to be written back to main memory, - /// and then marks that data in the D-cache as invalid, causing future reads to first fetch - /// from main memory. - #[inline] - pub fn clean_invalidate_dcache_by_address(&mut self, addr: usize, size: usize) { - // No-op zero sized operations - if size == 0 { - return; - } - - // NOTE(unsafe): No races as all CBP registers are write-only and stateless - let mut cbp = unsafe { CBP::new() }; - - crate::asm::dsb(); - - // Cache lines are fixed to 32 bit on Cortex-M7 and not present in earlier Cortex-M - const LINESIZE: usize = 32; - let num_lines = ((size - 1) / LINESIZE) + 1; - - let mut addr = addr & 0xFFFF_FFE0; - - for _ in 0..num_lines { - cbp.dccimvac(addr as u32); - addr += LINESIZE; - } - - crate::asm::dsb(); - crate::asm::isb(); - } -} - -const SCB_SCR_SLEEPDEEP: u32 = 0x1 << 2; - -impl SCB { - /// Set the SLEEPDEEP bit in the SCR register - #[inline] - pub fn set_sleepdeep(&mut self) { - unsafe { - self.scr.modify(|scr| scr | SCB_SCR_SLEEPDEEP); - } - } - - /// Clear the SLEEPDEEP bit in the SCR register - #[inline] - pub fn clear_sleepdeep(&mut self) { - unsafe { - self.scr.modify(|scr| scr & !SCB_SCR_SLEEPDEEP); - } - } -} - -const SCB_SCR_SLEEPONEXIT: u32 = 0x1 << 1; - -impl SCB { - /// Set the SLEEPONEXIT bit in the SCR register - #[inline] - pub fn set_sleeponexit(&mut self) { - unsafe { - self.scr.modify(|scr| scr | SCB_SCR_SLEEPONEXIT); - } - } - - /// Clear the SLEEPONEXIT bit in the SCR register - #[inline] - pub fn clear_sleeponexit(&mut self) { - unsafe { - self.scr.modify(|scr| scr & !SCB_SCR_SLEEPONEXIT); - } - } -} - -const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16; -const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x7 << 8; -const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2; - -impl SCB { - /// Initiate a system reset request to reset the MCU - #[inline] - pub fn sys_reset() -> ! { - crate::asm::dsb(); - unsafe { - (*Self::PTR).aircr.modify( - |r| { - SCB_AIRCR_VECTKEY | // otherwise the write is ignored - r & SCB_AIRCR_PRIGROUP_MASK | // keep priority group unchanged - SCB_AIRCR_SYSRESETREQ - }, // set the bit - ) - }; - crate::asm::dsb(); - loop { - // wait for the reset - crate::asm::nop(); // avoid rust-lang/rust#28728 - } - } -} - -const SCB_ICSR_PENDSVSET: u32 = 1 << 28; -const SCB_ICSR_PENDSVCLR: u32 = 1 << 27; - -const SCB_ICSR_PENDSTSET: u32 = 1 << 26; -const SCB_ICSR_PENDSTCLR: u32 = 1 << 25; - -impl SCB { - /// Set the PENDSVSET bit in the ICSR register which will pend the PendSV interrupt - #[inline] - pub fn set_pendsv() { - unsafe { - (*Self::PTR).icsr.write(SCB_ICSR_PENDSVSET); - } - } - - /// Check if PENDSVSET bit in the ICSR register is set meaning PendSV interrupt is pending - #[inline] - pub fn is_pendsv_pending() -> bool { - unsafe { (*Self::PTR).icsr.read() & SCB_ICSR_PENDSVSET == SCB_ICSR_PENDSVSET } - } - - /// Set the PENDSVCLR bit in the ICSR register which will clear a pending PendSV interrupt - #[inline] - pub fn clear_pendsv() { - unsafe { - (*Self::PTR).icsr.write(SCB_ICSR_PENDSVCLR); - } - } - - /// Set the PENDSTSET bit in the ICSR register which will pend a SysTick interrupt - #[inline] - pub fn set_pendst() { - unsafe { - (*Self::PTR).icsr.write(SCB_ICSR_PENDSTSET); - } - } - - /// Check if PENDSTSET bit in the ICSR register is set meaning SysTick interrupt is pending - #[inline] - pub fn is_pendst_pending() -> bool { - unsafe { (*Self::PTR).icsr.read() & SCB_ICSR_PENDSTSET == SCB_ICSR_PENDSTSET } - } - - /// Set the PENDSTCLR bit in the ICSR register which will clear a pending SysTick interrupt - #[inline] - pub fn clear_pendst() { - unsafe { - (*Self::PTR).icsr.write(SCB_ICSR_PENDSTCLR); - } - } -} - -/// System handlers, exceptions with configurable priority -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[repr(u8)] -pub enum SystemHandler { - // NonMaskableInt, // priority is fixed - // HardFault, // priority is fixed - /// Memory management interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - MemoryManagement = 4, - - /// Bus fault interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - BusFault = 5, - - /// Usage fault interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - UsageFault = 6, - - /// Secure fault interrupt (only on ARMv8-M) - #[cfg(any(armv8m, native))] - SecureFault = 7, - - /// SV call interrupt - SVCall = 11, - - /// Debug monitor interrupt (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] - DebugMonitor = 12, - - /// Pend SV interrupt - PendSV = 14, - - /// System Tick interrupt - SysTick = 15, -} - -impl SCB { - /// Returns the hardware priority of `system_handler` - /// - /// *NOTE*: Hardware priority does not exactly match logical priority levels. See - /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details. - #[inline] - pub fn get_priority(system_handler: SystemHandler) -> u8 { - let index = system_handler as u8; - - #[cfg(not(armv6m))] - { - // NOTE(unsafe) atomic read with no side effects - - // NOTE(unsafe): Index is bounded to [4,15] by SystemHandler design. - // TODO: Review it after rust-lang/rust/issues/13926 will be fixed. - let priority_ref = unsafe { (*Self::PTR).shpr.get_unchecked(usize::from(index - 4)) }; - - priority_ref.read() - } - - #[cfg(armv6m)] - { - // NOTE(unsafe) atomic read with no side effects - - // NOTE(unsafe): Index is bounded to [11,15] by SystemHandler design. - // TODO: Review it after rust-lang/rust/issues/13926 will be fixed. - let priority_ref = unsafe { - (*Self::PTR) - .shpr - .get_unchecked(usize::from((index - 8) / 4)) - }; - - let shpr = priority_ref.read(); - let prio = (shpr >> (8 * (index % 4))) & 0x0000_00ff; - prio as u8 - } - } - - /// Sets the hardware priority of `system_handler` to `prio` - /// - /// *NOTE*: Hardware priority does not exactly match logical priority levels. See - /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details. - /// - /// On ARMv6-M, updating a system handler priority requires a read-modify-write operation. On - /// ARMv7-M, the operation is performed in a single, atomic write operation. - /// - /// # Unsafety - /// - /// Changing priority levels can break priority-based critical sections (see - /// [`register::basepri`](crate::register::basepri)) and compromise memory safety. - #[inline] - pub unsafe fn set_priority(&mut self, system_handler: SystemHandler, prio: u8) { - let index = system_handler as u8; - - #[cfg(not(armv6m))] - { - // NOTE(unsafe): Index is bounded to [4,15] by SystemHandler design. - // TODO: Review it after rust-lang/rust/issues/13926 will be fixed. - let priority_ref = (*Self::PTR).shpr.get_unchecked(usize::from(index - 4)); - - priority_ref.write(prio) - } - - #[cfg(armv6m)] - { - // NOTE(unsafe): Index is bounded to [11,15] by SystemHandler design. - // TODO: Review it after rust-lang/rust/issues/13926 will be fixed. - let priority_ref = (*Self::PTR) - .shpr - .get_unchecked(usize::from((index - 8) / 4)); - - priority_ref.modify(|value| { - let shift = 8 * (index % 4); - let mask = 0x0000_00ff << shift; - let prio = u32::from(prio) << shift; - - (value & !mask) | prio - }); - } - } - - /// Return the bit position of the exception enable bit in the SHCSR register - #[inline] - #[cfg(not(any(armv6m, armv8m_base)))] - fn shcsr_enable_shift(exception: Exception) -> Option<u32> { - match exception { - Exception::MemoryManagement => Some(16), - Exception::BusFault => Some(17), - Exception::UsageFault => Some(18), - #[cfg(armv8m_main)] - Exception::SecureFault => Some(19), - _ => None, - } - } - - /// Enable the exception - /// - /// If the exception is enabled, when the exception is triggered, the exception handler will be executed instead of the - /// HardFault handler. - /// This function is only allowed on the following exceptions: - /// * `MemoryManagement` - /// * `BusFault` - /// * `UsageFault` - /// * `SecureFault` (can only be enabled from Secure state) - /// - /// Calling this function with any other exception will do nothing. - #[inline] - #[cfg(not(any(armv6m, armv8m_base)))] - pub fn enable(&mut self, exception: Exception) { - if let Some(shift) = SCB::shcsr_enable_shift(exception) { - // The mutable reference to SCB makes sure that only this code is currently modifying - // the register. - unsafe { self.shcsr.modify(|value| value | (1 << shift)) } - } - } - - /// Disable the exception - /// - /// If the exception is disabled, when the exception is triggered, the HardFault handler will be executed instead of the - /// exception handler. - /// This function is only allowed on the following exceptions: - /// * `MemoryManagement` - /// * `BusFault` - /// * `UsageFault` - /// * `SecureFault` (can not be changed from Non-secure state) - /// - /// Calling this function with any other exception will do nothing. - #[inline] - #[cfg(not(any(armv6m, armv8m_base)))] - pub fn disable(&mut self, exception: Exception) { - if let Some(shift) = SCB::shcsr_enable_shift(exception) { - // The mutable reference to SCB makes sure that only this code is currently modifying - // the register. - unsafe { self.shcsr.modify(|value| value & !(1 << shift)) } - } - } - - /// Check if an exception is enabled - /// - /// This function is only allowed on the following exception: - /// * `MemoryManagement` - /// * `BusFault` - /// * `UsageFault` - /// * `SecureFault` (can not be read from Non-secure state) - /// - /// Calling this function with any other exception will read `false`. - #[inline] - #[cfg(not(any(armv6m, armv8m_base)))] - pub fn is_enabled(&self, exception: Exception) -> bool { - if let Some(shift) = SCB::shcsr_enable_shift(exception) { - (self.shcsr.read() & (1 << shift)) > 0 - } else { - false - } - } -} |