diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0733aebd..4c10968f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,4 @@ on: - push: - branches-ignore: - - "gh-readonly-queue/**" pull_request: merge_group: workflow_dispatch: diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 47bc5b28..582fe6bc 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,7 +1,4 @@ on: - push: - branches-ignore: - - "gh-readonly-queue/**" pull_request: merge_group: workflow_dispatch: diff --git a/.github/workflows/on-target.yml b/.github/workflows/on-target.yml index a001fecc..0acb22a0 100644 --- a/.github/workflows/on-target.yml +++ b/.github/workflows/on-target.yml @@ -1,7 +1,4 @@ on: - push: - branches-ignore: - - "gh-readonly-queue/**" pull_request: merge_group: # allows manual triggering diff --git a/.github/workflows/rt-ci.yml b/.github/workflows/rt-ci.yml index e73dcd4c..dccca3da 100644 --- a/.github/workflows/rt-ci.yml +++ b/.github/workflows/rt-ci.yml @@ -1,7 +1,4 @@ on: - push: - branches-ignore: - - "gh-readonly-queue/**" pull_request: merge_group: workflow_dispatch: diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 042f1fa5..0707453a 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -1,7 +1,4 @@ on: - push: - branches-ignore: - - "gh-readonly-queue/**" pull_request: merge_group: workflow_dispatch: diff --git a/cortex-m/Cargo.toml b/cortex-m/Cargo.toml index 2858631f..cbc99cef 100644 --- a/cortex-m/Cargo.toml +++ b/cortex-m/Cargo.toml @@ -18,10 +18,11 @@ rust-version = "1.61" [dependencies] bare-metal = { version = "0.2.4", features = ["const-fn"] } -critical-section = { version = "1.0.0", optional = true } -volatile-register = "0.2.0" +critical-section = "1.0.0" +volatile-register = "0.2.2" bitfield = "0.13.2" -embedded-hal = "0.2.4" +eh0 = { package = "embedded-hal", version = "0.2.4" } +eh1 = { package = "embedded-hal", version = "1.0.0" } [dependencies.serde] version = "1" @@ -38,7 +39,7 @@ cm7-r0p1 = ["cm7"] inline-asm = [] linker-plugin-lto = [] std = [] -critical-section-single-core = ["critical-section/restore-state-bool"] +critical-section-single-core = ["critical-section/restore-state-u32"] [package.metadata.docs.rs] targets = [ diff --git a/cortex-m/asm/inline.rs b/cortex-m/asm/inline.rs index f5d6b32d..37e2cf46 100644 --- a/cortex-m/asm/inline.rs +++ b/cortex-m/asm/inline.rs @@ -60,6 +60,10 @@ pub unsafe fn __delay(cyc: u32) { // Add 1 to prevent an integer underflow which would cause a long freeze let real_cyc = 1 + cyc / 2; asm!( + // The `bne` on some cores (eg Cortex-M4) will take a different number of instructions + // depending on the alignment of the branch target. Set the alignment of the top of the + // loop to prevent surprising timing changes when the alignment of the delay() changes. + ".p2align 3", // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m. "1:", "subs {}, #1", diff --git a/cortex-m/bin/thumbv6m-none-eabi-lto.a b/cortex-m/bin/thumbv6m-none-eabi-lto.a index c9600fde..b07a9329 100644 Binary files a/cortex-m/bin/thumbv6m-none-eabi-lto.a and b/cortex-m/bin/thumbv6m-none-eabi-lto.a differ diff --git a/cortex-m/bin/thumbv6m-none-eabi.a b/cortex-m/bin/thumbv6m-none-eabi.a index 9640a699..30d33eb9 100644 Binary files a/cortex-m/bin/thumbv6m-none-eabi.a and b/cortex-m/bin/thumbv6m-none-eabi.a differ diff --git a/cortex-m/bin/thumbv7em-none-eabi-lto.a b/cortex-m/bin/thumbv7em-none-eabi-lto.a index 5d9350fc..34eec1a3 100644 Binary files a/cortex-m/bin/thumbv7em-none-eabi-lto.a and b/cortex-m/bin/thumbv7em-none-eabi-lto.a differ diff --git a/cortex-m/bin/thumbv7em-none-eabi.a b/cortex-m/bin/thumbv7em-none-eabi.a index 88acbddf..d79d709a 100644 Binary files a/cortex-m/bin/thumbv7em-none-eabi.a and b/cortex-m/bin/thumbv7em-none-eabi.a differ diff --git a/cortex-m/bin/thumbv7em-none-eabihf-lto.a b/cortex-m/bin/thumbv7em-none-eabihf-lto.a index f0860c16..b0b999db 100644 Binary files a/cortex-m/bin/thumbv7em-none-eabihf-lto.a and b/cortex-m/bin/thumbv7em-none-eabihf-lto.a differ diff --git a/cortex-m/bin/thumbv7em-none-eabihf.a b/cortex-m/bin/thumbv7em-none-eabihf.a index cf91a7a5..f3130e02 100644 Binary files a/cortex-m/bin/thumbv7em-none-eabihf.a and b/cortex-m/bin/thumbv7em-none-eabihf.a differ diff --git a/cortex-m/bin/thumbv7m-none-eabi-lto.a b/cortex-m/bin/thumbv7m-none-eabi-lto.a index baee2364..95647269 100644 Binary files a/cortex-m/bin/thumbv7m-none-eabi-lto.a and b/cortex-m/bin/thumbv7m-none-eabi-lto.a differ diff --git a/cortex-m/bin/thumbv7m-none-eabi.a b/cortex-m/bin/thumbv7m-none-eabi.a index ff4bf211..30dbd187 100644 Binary files a/cortex-m/bin/thumbv7m-none-eabi.a and b/cortex-m/bin/thumbv7m-none-eabi.a differ diff --git a/cortex-m/bin/thumbv8m.base-none-eabi-lto.a b/cortex-m/bin/thumbv8m.base-none-eabi-lto.a index 9016d369..a1c8f756 100644 Binary files a/cortex-m/bin/thumbv8m.base-none-eabi-lto.a and b/cortex-m/bin/thumbv8m.base-none-eabi-lto.a differ diff --git a/cortex-m/bin/thumbv8m.base-none-eabi.a b/cortex-m/bin/thumbv8m.base-none-eabi.a index c0cc96c4..c489a888 100644 Binary files a/cortex-m/bin/thumbv8m.base-none-eabi.a and b/cortex-m/bin/thumbv8m.base-none-eabi.a differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabi-lto.a b/cortex-m/bin/thumbv8m.main-none-eabi-lto.a index de23794e..9d7845cf 100644 Binary files a/cortex-m/bin/thumbv8m.main-none-eabi-lto.a and b/cortex-m/bin/thumbv8m.main-none-eabi-lto.a differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabi.a b/cortex-m/bin/thumbv8m.main-none-eabi.a index d017a15b..99719232 100644 Binary files a/cortex-m/bin/thumbv8m.main-none-eabi.a and b/cortex-m/bin/thumbv8m.main-none-eabi.a differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabihf-lto.a b/cortex-m/bin/thumbv8m.main-none-eabihf-lto.a index df0a29c3..81073606 100644 Binary files a/cortex-m/bin/thumbv8m.main-none-eabihf-lto.a and b/cortex-m/bin/thumbv8m.main-none-eabihf-lto.a differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabihf.a b/cortex-m/bin/thumbv8m.main-none-eabihf.a index 223ff1df..9e07c40f 100644 Binary files a/cortex-m/bin/thumbv8m.main-none-eabihf.a and b/cortex-m/bin/thumbv8m.main-none-eabihf.a differ diff --git a/cortex-m/src/asm.rs b/cortex-m/src/asm.rs index 4dc1ab07..a8402d85 100644 --- a/cortex-m/src/asm.rs +++ b/cortex-m/src/asm.rs @@ -17,13 +17,18 @@ pub fn bkpt() { /// Blocks the program for *at least* `cycles` CPU cycles. /// -/// This is implemented in assembly so its execution time is independent of the optimization -/// level, however it is dependent on the specific architecture and core configuration. -/// -/// NOTE that the delay can take much longer if interrupts are serviced during its execution -/// and the execution time may vary with other factors. This delay is mainly useful for simple -/// timer-less initialization of peripherals if and only if accurate timing is not essential. In -/// any other case please use a more accurate method to produce a delay. +/// This is implemented in assembly as a fixed number of iterations of a loop, so that execution +/// time is independent of the optimization level. +/// +/// The loop code is the same for all architectures, however the number of CPU cycles required for +/// one iteration varies substantially between architectures. This means that with a 48MHz CPU +/// clock, a call to `delay(48_000_000)` is guaranteed to take at least 1 second, but for example +/// could take 2 seconds. +/// +/// NOTE that the delay can take much longer if interrupts are serviced during its execution and the +/// execution time may vary with other factors. This delay is mainly useful for simple timer-less +/// initialization of peripherals if and only if accurate timing is not essential. In any other case +/// please use a more accurate method to produce a delay. #[inline] pub fn delay(cycles: u32) { call_asm!(__delay(cycles: u32)); diff --git a/cortex-m/src/critical_section.rs b/cortex-m/src/critical_section.rs index d33e90ff..6bedfffa 100644 --- a/cortex-m/src/critical_section.rs +++ b/cortex-m/src/critical_section.rs @@ -1,25 +1,24 @@ -#[cfg(all(cortex_m, feature = "critical-section-single-core"))] -mod single_core_critical_section { - use critical_section::{set_impl, Impl, RawRestoreState}; +use critical_section::{set_impl, Impl, RawRestoreState}; - use crate::interrupt; - use crate::register::primask; +use crate::interrupt; +use crate::register::primask; - struct SingleCoreCriticalSection; - set_impl!(SingleCoreCriticalSection); +struct SingleCoreCriticalSection; +set_impl!(SingleCoreCriticalSection); - unsafe impl Impl for SingleCoreCriticalSection { - unsafe fn acquire() -> RawRestoreState { - let was_active = primask::read().is_active(); - interrupt::disable(); - was_active - } +unsafe impl Impl for SingleCoreCriticalSection { + unsafe fn acquire() -> RawRestoreState { + // Backup previous state of PRIMASK register. We access the entire register directly as a + // u32 instead of using the primask::read() function to minimize the number of processor + // cycles during which interrupts are disabled. + let restore_state = primask::read_raw(); + // NOTE: Fence guarantees are provided by interrupt::disable(), which performs a `compiler_fence(SeqCst)`. + interrupt::disable(); + restore_state + } - unsafe fn release(was_active: RawRestoreState) { - // Only re-enable interrupts if they were enabled before the critical section. - if was_active { - interrupt::enable() - } - } + unsafe fn release(restore_state: RawRestoreState) { + // NOTE: Fence guarantees are provided by primask::write_raw(), which performs a `compiler_fence(SeqCst)`. + primask::write_raw(restore_state); } } diff --git a/cortex-m/src/delay.rs b/cortex-m/src/delay.rs index 66a63bf6..4dcc1345 100644 --- a/cortex-m/src/delay.rs +++ b/cortex-m/src/delay.rs @@ -1,7 +1,7 @@ //! A delay driver based on SysTick. use crate::peripheral::{syst::SystClkSource, SYST}; -use embedded_hal::blocking::delay::{DelayMs, DelayUs}; +use eh1::delay::DelayNs; /// System timer (SysTick) as a delay provider. pub struct Delay { @@ -75,7 +75,7 @@ impl Delay { } } -impl DelayMs for Delay { +impl eh0::blocking::delay::DelayMs for Delay { #[inline] fn delay_ms(&mut self, ms: u32) { Delay::delay_ms(self, ms); @@ -83,7 +83,7 @@ impl DelayMs for Delay { } // This is a workaround to allow `delay_ms(42)` construction without specifying a type. -impl DelayMs for Delay { +impl eh0::blocking::delay::DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: i32) { assert!(ms >= 0); @@ -91,21 +91,21 @@ impl DelayMs for Delay { } } -impl DelayMs for Delay { +impl eh0::blocking::delay::DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u16) { Delay::delay_ms(self, u32::from(ms)); } } -impl DelayMs for Delay { +impl eh0::blocking::delay::DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u8) { Delay::delay_ms(self, u32::from(ms)); } } -impl DelayUs for Delay { +impl eh0::blocking::delay::DelayUs for Delay { #[inline] fn delay_us(&mut self, us: u32) { Delay::delay_us(self, us); @@ -113,7 +113,7 @@ impl DelayUs for Delay { } // This is a workaround to allow `delay_us(42)` construction without specifying a type. -impl DelayUs for Delay { +impl eh0::blocking::delay::DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: i32) { assert!(us >= 0); @@ -121,16 +121,37 @@ impl DelayUs for Delay { } } -impl DelayUs for Delay { +impl eh0::blocking::delay::DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: u16) { Delay::delay_us(self, u32::from(us)) } } -impl DelayUs for Delay { +impl eh0::blocking::delay::DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: u8) { Delay::delay_us(self, u32::from(us)) } } + +impl DelayNs for Delay { + #[inline] + fn delay_ns(&mut self, ns: u32) { + // from the rp2040-hal: + let us = ns / 1000 + if ns % 1000 == 0 { 0 } else { 1 }; + // With rustc 1.73, this can be replaced by: + // let us = ns.div_ceil(1000); + Delay::delay_us(self, us) + } + + #[inline] + fn delay_us(&mut self, us: u32) { + Delay::delay_us(self, us) + } + + #[inline] + fn delay_ms(&mut self, ms: u32) { + Delay::delay_ms(self, ms) + } +} diff --git a/cortex-m/src/interrupt.rs b/cortex-m/src/interrupt.rs index 0fd1284b..1a1e7318 100644 --- a/cortex-m/src/interrupt.rs +++ b/cortex-m/src/interrupt.rs @@ -51,23 +51,37 @@ pub unsafe fn enable() { /// Execute closure `f` in an interrupt-free context. /// /// This as also known as a "critical section". +#[cfg(cortex_m)] #[inline] pub fn free(f: F) -> R where F: FnOnce(&CriticalSection) -> R, { - let primask = crate::register::primask::read(); + // Backup previous state of PRIMASK register. We access the entire register directly as a + // u32 instead of using the primask::read() function to minimize the number of processor + // cycles during which interrupts are disabled. + let primask = crate::register::primask::read_raw(); // disable interrupts disable(); let r = f(unsafe { &CriticalSection::new() }); - // If the interrupts were active before our `disable` call, then re-enable - // them. Otherwise, keep them disabled - if primask.is_active() { - unsafe { enable() } + unsafe { + crate::register::primask::write_raw(primask); } r } + +// Make a `free()` function available to allow checking dependencies without specifying a target, +// but that will panic at runtime if executed. +#[doc(hidden)] +#[cfg(not(cortex_m))] +#[inline] +pub fn free(_: F) -> R +where + F: FnOnce(&CriticalSection) -> R, +{ + panic!("cortex_m::interrupt::free() is only functional on cortex-m platforms"); +} diff --git a/cortex-m/src/lib.rs b/cortex-m/src/lib.rs index ff730ccb..c56248f9 100644 --- a/cortex-m/src/lib.rs +++ b/cortex-m/src/lib.rs @@ -35,6 +35,11 @@ //! or critical sections are managed as part of an RTOS. In these cases, you should use //! a target-specific implementation instead, typically provided by a HAL or RTOS crate. //! +//! The critical section has been optimized to block interrupts for as few cycles as possible, +//! but -- due to `critical-section` implementation details -- incurs branches in a normal build +//! configuration. For minimal interrupt latency, you can achieve inlining by enabling +//! [linker-plugin-based LTO](https://doc.rust-lang.org/rustc/linker-plugin-lto.html). +//! //! ## `cm7-r0p1` //! //! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some @@ -100,7 +105,6 @@ mod macros; pub mod asm; #[cfg(armv8m)] pub mod cmse; -mod critical_section; pub mod delay; pub mod interrupt; #[cfg(all(not(armv6m), not(armv8m_base)))] @@ -110,3 +114,13 @@ pub mod prelude; pub mod register; pub use crate::peripheral::Peripherals; + +#[cfg(all(cortex_m, feature = "critical-section-single-core"))] +mod critical_section; + +/// Used to reexport items for use in macros. Do not use directly. +/// Not covered by semver guarantees. +#[doc(hidden)] +pub mod _export { + pub use critical_section; +} diff --git a/cortex-m/src/macros.rs b/cortex-m/src/macros.rs index 512c9323..c4483b87 100644 --- a/cortex-m/src/macros.rs +++ b/cortex-m/src/macros.rs @@ -31,10 +31,13 @@ macro_rules! iprintln { /// at most once in the whole lifetime of the program. /// /// # Notes -/// This macro is unsound on multi core systems. /// -/// For debuggability, you can set an explicit name for a singleton. This name only shows up the -/// the debugger and is not referencable from other code. See example below. +/// This macro requires a `critical-section` implementation to be set. For most single core systems, +/// you can enable the `critical-section-single-core` feature for this crate. For other systems, you +/// have to provide one from elsewhere, typically your chip's HAL crate. +/// +/// For debuggability, you can set an explicit name for a singleton. This name only shows up the +/// debugger and is not referenceable from other code. See example below. /// /// # Example /// @@ -61,11 +64,12 @@ macro_rules! iprintln { /// ``` #[macro_export] macro_rules! singleton { - ($name:ident: $ty:ty = $expr:expr) => { - $crate::interrupt::free(|_| { + ($(#[$meta:meta])* $name:ident: $ty:ty = $expr:expr) => { + $crate::_export::critical_section::with(|_| { // this is a tuple of a MaybeUninit and a bool because using an Option here is // problematic: Due to niche-optimization, an Option could end up producing a non-zero // initializer value which would move the entire static from `.bss` into `.data`... + $(#[$meta])* static mut $name: (::core::mem::MaybeUninit<$ty>, bool) = (::core::mem::MaybeUninit::uninit(), false); @@ -79,14 +83,13 @@ macro_rules! singleton { #[allow(unsafe_code)] unsafe { $name.1 = true; - $name.0 = ::core::mem::MaybeUninit::new(expr); - Some(&mut *$name.0.as_mut_ptr()) + Some($name.0.write(expr)) } } }) }; - (: $ty:ty = $expr:expr) => { - $crate::singleton!(VAR: $ty = $expr) + ($(#[$meta:meta])* : $ty:ty = $expr:expr) => { + $crate::singleton!($(#[$meta])* VAR: $ty = $expr) }; } @@ -112,3 +115,15 @@ const CFAIL: () = (); /// ``` #[allow(dead_code)] const CPASS: () = (); + +/// ``` +/// use cortex_m::singleton; +/// +/// fn foo() { +/// // check that attributes are forwarded +/// singleton!(#[link_section = ".bss"] FOO: u8 = 0); +/// singleton!(#[link_section = ".bss"]: u8 = 1); +/// } +/// ``` +#[allow(dead_code)] +const CPASS_ATTR: () = (); diff --git a/cortex-m/src/peripheral/mod.rs b/cortex-m/src/peripheral/mod.rs index 4fae295a..9cf7918f 100644 --- a/cortex-m/src/peripheral/mod.rs +++ b/cortex-m/src/peripheral/mod.rs @@ -60,8 +60,6 @@ use core::marker::PhantomData; use core::ops; -use crate::interrupt; - #[cfg(feature = "cm7")] pub mod ac; #[cfg(not(armv6m))] @@ -165,7 +163,7 @@ impl Peripherals { /// Returns all the core peripherals *once* #[inline] pub fn take() -> Option { - interrupt::free(|_| { + crate::interrupt::free(|_| { if unsafe { TAKEN } { None } else { diff --git a/cortex-m/src/peripheral/nvic.rs b/cortex-m/src/peripheral/nvic.rs index 57fa94b7..067c1d95 100644 --- a/cortex-m/src/peripheral/nvic.rs +++ b/cortex-m/src/peripheral/nvic.rs @@ -36,7 +36,15 @@ pub struct RegisterBlock { #[cfg(armv6m)] _reserved4: [u32; 16], - _reserved5: [u32; 48], + _reserved5: [u32; 16], + + #[cfg(armv8m)] + /// Interrupt Target Non-secure (only present on Arm v8-M) + pub itns: [RW; 16], + #[cfg(not(armv8m))] + _reserved6: [u32; 16], + + _reserved7: [u32; 16], /// Interrupt Priority /// @@ -67,7 +75,7 @@ pub struct RegisterBlock { pub ipr: [RW; 8], #[cfg(not(armv6m))] - _reserved6: [u32; 580], + _reserved8: [u32; 580], /// Software Trigger Interrupt #[cfg(not(armv6m))] diff --git a/cortex-m/src/peripheral/scb.rs b/cortex-m/src/peripheral/scb.rs index ecf98e5a..4cc4ce71 100644 --- a/cortex-m/src/peripheral/scb.rs +++ b/cortex-m/src/peripheral/scb.rs @@ -820,6 +820,26 @@ impl SCB { } } +const SCB_SCR_SEVONPEND: u32 = 0x1 << 4; + +impl SCB { + /// Set the SEVONPEND bit in the SCR register + #[inline] + pub fn set_sevonpend(&mut self) { + unsafe { + self.scr.modify(|scr| scr | SCB_SCR_SEVONPEND); + } + } + + /// Clear the SEVONPEND bit in the SCR register + #[inline] + pub fn clear_sevonpend(&mut self) { + unsafe { + self.scr.modify(|scr| scr & !SCB_SCR_SEVONPEND); + } + } +} + const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16; const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x7 << 8; const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2; diff --git a/cortex-m/src/peripheral/syst.rs b/cortex-m/src/peripheral/syst.rs index 345acc2f..9d6f2919 100644 --- a/cortex-m/src/peripheral/syst.rs +++ b/cortex-m/src/peripheral/syst.rs @@ -1,4 +1,18 @@ //! SysTick: System Timer +//! +//! # Example +//! +//! ```no_run +//! use cortex_m::peripheral::{Peripherals, SYST}; +//! +//! let core_periph = cortex_m::peripheral::Peripherals::take().unwrap(); +//! let mut syst = core_periph.SYST; +//! syst.set_reload(0xffffff); +//! syst.clear_current(); +//! syst.enable_counter(); +//! +//! let syst_value: u32 = SYST::get_current(); +//! ``` use volatile_register::{RO, RW}; @@ -39,7 +53,7 @@ const SYST_CALIB_NOREF: u32 = 1 << 31; impl SYST { /// Clears current value to 0 /// - /// After calling `clear_current()`, the next call to `has_wrapped()` will return `false`. + /// After calling `clear_current()`, the next call to `has_wrapped()`, unless called after the reload time (if the counter is enabled), will return `false`. #[inline] pub fn clear_current(&mut self) { unsafe { self.cvr.write(0) } diff --git a/cortex-m/src/prelude.rs b/cortex-m/src/prelude.rs index bc47cc02..dc1f31ca 100644 --- a/cortex-m/src/prelude.rs +++ b/cortex-m/src/prelude.rs @@ -1,3 +1,3 @@ //! Prelude -pub use embedded_hal::prelude::*; +pub use eh0::prelude::*; diff --git a/cortex-m/src/register/primask.rs b/cortex-m/src/register/primask.rs index 842ca49a..58b8c287 100644 --- a/cortex-m/src/register/primask.rs +++ b/cortex-m/src/register/primask.rs @@ -1,5 +1,10 @@ //! Priority mask register +#[cfg(cortex_m)] +use core::arch::asm; +#[cfg(cortex_m)] +use core::sync::atomic::{compiler_fence, Ordering}; + /// All exceptions with configurable priority are ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Primask { @@ -23,13 +28,42 @@ impl Primask { } } -/// Reads the CPU register +/// Reads the prioritizable interrupt mask +#[cfg(cortex_m)] #[inline] pub fn read() -> Primask { - let r: u32 = call_asm!(__primask_r() -> u32); - if r & (1 << 0) == (1 << 0) { + if read_raw() & (1 << 0) == (1 << 0) { Primask::Inactive } else { Primask::Active } } + +/// Reads the entire PRIMASK register +/// Note that bits [31:1] are reserved and UNK (Unknown) +#[cfg(cortex_m)] +#[inline] +pub fn read_raw() -> u32 { + let r: u32; + unsafe { asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r +} + +/// Writes the entire PRIMASK register +/// Note that bits [31:1] are reserved and SBZP (Should-Be-Zero-or-Preserved) +/// +/// # Safety +/// +/// This method is unsafe as other unsafe code may rely on interrupts remaining disabled, for +/// example during a critical section, and being able to safely re-enable them would lead to +/// undefined behaviour. Do not call this function in a context where interrupts are expected to +/// remain disabled -- for example, in the midst of a critical section or `interrupt::free()` call. +#[cfg(cortex_m)] +#[inline] +pub unsafe fn write_raw(r: u32) { + // Ensure no preceeding memory accesses are reordered to after interrupts are possibly enabled. + compiler_fence(Ordering::SeqCst); + unsafe { asm!("msr PRIMASK, {}", in(reg) r, options(nomem, nostack, preserves_flags)) }; + // Ensure no subsequent memory accesses are reordered to before interrupts are possibly disabled. + compiler_fence(Ordering::SeqCst); +}