diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm.rs | 167 | ||||
-rw-r--r-- | src/call_asm.rs | 24 | ||||
-rw-r--r-- | src/cmse.rs | 6 | ||||
-rw-r--r-- | src/critical_section.rs | 22 | ||||
-rw-r--r-- | src/interrupt.rs | 51 | ||||
-rw-r--r-- | src/lib.rs | 58 | ||||
-rw-r--r-- | src/macros.rs | 7 | ||||
-rw-r--r-- | src/peripheral/dwt.rs | 12 | ||||
-rw-r--r-- | src/peripheral/mod.rs | 4 | ||||
-rw-r--r-- | src/peripheral/sau.rs | 5 | ||||
-rw-r--r-- | src/peripheral/tpiu.rs | 1 | ||||
-rw-r--r-- | src/prelude.rs | 3 | ||||
-rw-r--r-- | src/register/apsr.rs | 9 | ||||
-rw-r--r-- | src/register/basepri.rs | 24 | ||||
-rw-r--r-- | src/register/basepri_max.rs | 23 | ||||
-rw-r--r-- | src/register/control.rs | 23 | ||||
-rw-r--r-- | src/register/faultmask.rs | 7 | ||||
-rw-r--r-- | src/register/fpscr.rs | 7 | ||||
-rw-r--r-- | src/register/lr.rs | 15 | ||||
-rw-r--r-- | src/register/mod.rs | 5 | ||||
-rw-r--r-- | src/register/msp.rs | 21 | ||||
-rw-r--r-- | src/register/msplim.rs | 8 | ||||
-rw-r--r-- | src/register/pc.rs | 15 | ||||
-rw-r--r-- | src/register/primask.rs | 7 | ||||
-rw-r--r-- | src/register/psp.rs | 13 | ||||
-rw-r--r-- | src/register/psplim.rs | 8 |
26 files changed, 374 insertions, 171 deletions
@@ -1,18 +1,17 @@ //! Miscellaneous assembly instructions -// When inline assembly is enabled, pull in the assembly routines here. `call_asm!` will invoke -// these routines. -#[cfg(feature = "inline-asm")] -#[path = "../asm/inline.rs"] -pub(crate) mod inline; +#[cfg(cortex_m)] +use core::arch::asm; +use core::sync::atomic::{compiler_fence, Ordering}; /// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint". /// /// **NOTE** calling `bkpt` when the processor is not connected to a debugger will cause an /// exception. +#[cfg(cortex_m)] #[inline(always)] pub fn bkpt() { - call_asm!(__bkpt()); + unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) }; } /// Blocks the program for *at least* `cycles` CPU cycles. @@ -24,50 +23,80 @@ pub fn bkpt() { /// 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. +#[cfg(cortex_m)] #[inline] pub fn delay(cycles: u32) { - call_asm!(__delay(cycles: u32)); + // The loop will normally take 3 to 4 CPU cycles per iteration, but superscalar cores + // (eg. Cortex-M7) can potentially do it in 2, so we use that as the lower bound, since delaying + // for more cycles is okay. + // Add 1 to prevent an integer underflow which would cause a long freeze + let real_cycles = 1 + cycles / 2; + unsafe { + asm!( + // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m. + "1:", + "subs {}, #1", + "bne 1b", + inout(reg) real_cycles => _, + options(nomem, nostack), + ) + }; } /// A no-operation. Useful to prevent delay loops from being optimized away. -#[inline] +#[inline(always)] pub fn nop() { - call_asm!(__nop()); + // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate + // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N + // nops when they call `nop` N times, let's not add that option. + #[cfg(cortex_m)] + unsafe { + asm!("nop", options(nomem, nostack, preserves_flags)) + }; } /// Generate an Undefined Instruction exception. /// /// Can be used as a stable alternative to `core::intrinsics::abort`. -#[inline] +#[cfg(cortex_m)] +#[inline(always)] pub fn udf() -> ! { - call_asm!(__udf() -> !) + unsafe { asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)) }; } /// Wait For Event -#[inline] +#[cfg(cortex_m)] +#[inline(always)] pub fn wfe() { - call_asm!(__wfe()) + unsafe { asm!("wfe", options(nomem, nostack, preserves_flags)) }; } /// Wait For Interrupt -#[inline] +#[cfg(cortex_m)] +#[inline(always)] pub fn wfi() { - call_asm!(__wfi()) + unsafe { asm!("wfi", options(nomem, nostack, preserves_flags)) }; } /// Send Event -#[inline] +#[cfg(cortex_m)] +#[inline(always)] pub fn sev() { - call_asm!(__sev()) + unsafe { asm!("sev", options(nomem, nostack, preserves_flags)) }; } /// Instruction Synchronization Barrier /// /// Flushes the pipeline in the processor, so that all instructions following the `ISB` are fetched /// from cache or memory, after the instruction has been completed. -#[inline] +#[inline(always)] pub fn isb() { - call_asm!(__isb()) + compiler_fence(Ordering::SeqCst); + #[cfg(cortex_m)] + unsafe { + asm!("isb", options(nomem, nostack, preserves_flags)) + }; + compiler_fence(Ordering::SeqCst); } /// Data Synchronization Barrier @@ -77,9 +106,14 @@ pub fn isb() { /// /// * any explicit memory access made before this instruction is complete /// * all cache and branch predictor maintenance operations before this instruction complete -#[inline] +#[inline(always)] pub fn dsb() { - call_asm!(__dsb()) + compiler_fence(Ordering::SeqCst); + #[cfg(cortex_m)] + unsafe { + asm!("dsb", options(nomem, nostack, preserves_flags)) + }; + compiler_fence(Ordering::SeqCst); } /// Data Memory Barrier @@ -87,9 +121,14 @@ pub fn dsb() { /// Ensures that all explicit memory accesses that appear in program order before the `DMB` /// instruction are observed before any explicit memory accesses that appear in program order /// after the `DMB` instruction. -#[inline] +#[inline(always)] pub fn dmb() { - call_asm!(__dmb()) + compiler_fence(Ordering::SeqCst); + #[cfg(cortex_m)] + unsafe { + asm!("dmb", options(nomem, nostack, preserves_flags)) + }; + compiler_fence(Ordering::SeqCst); } /// Test Target @@ -97,13 +136,20 @@ pub fn dmb() { /// Queries the Security state and access permissions of a memory location. /// Returns a Test Target Response Payload (cf section D1.2.215 of /// Armv8-M Architecture Reference Manual). -#[inline] +#[inline(always)] #[cfg(armv8m)] // The __tt function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn tt(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__tt(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "tt {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Test Target Unprivileged @@ -112,13 +158,20 @@ pub fn tt(addr: *mut u32) -> u32 { /// access to that location. /// Returns a Test Target Response Payload (cf section D1.2.215 of /// Armv8-M Architecture Reference Manual). -#[inline] +#[inline(always)] #[cfg(armv8m)] // The __ttt function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn ttt(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__ttt(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "ttt {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Test Target Alternate Domain @@ -128,13 +181,20 @@ pub fn ttt(addr: *mut u32) -> u32 { /// undefined if used from Non-Secure state. /// Returns a Test Target Response Payload (cf section D1.2.215 of /// Armv8-M Architecture Reference Manual). -#[inline] +#[inline(always)] #[cfg(armv8m)] // The __tta function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn tta(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__tta(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "tta {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Test Target Alternate Domain Unprivileged @@ -144,31 +204,40 @@ pub fn tta(addr: *mut u32) -> u32 { /// state and is undefined if used from Non-Secure state. /// Returns a Test Target Response Payload (cf section D1.2.215 of /// Armv8-M Architecture Reference Manual). -#[inline] +#[inline(always)] #[cfg(armv8m)] // The __ttat function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn ttat(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__ttat(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "ttat {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Branch and Exchange Non-secure /// /// See section C2.4.26 of Armv8-M Architecture Reference Manual for details. /// Undefined if executed in Non-Secure state. -#[inline] +#[inline(always)] #[cfg(armv8m)] pub unsafe fn bx_ns(addr: u32) { - call_asm!(__bxns(addr: u32)); + asm!("bxns {}", in(reg) addr, options(nomem, nostack, preserves_flags)); } /// Semihosting syscall. /// /// This method is used by cortex-m-semihosting to provide semihosting syscalls. -#[inline] -pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { - call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32) +#[cfg(cortex_m)] +#[inline(always)] +pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 { + asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nostack, preserves_flags)); + nr } /// Bootstrap. @@ -181,12 +250,27 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { /// /// `msp` and `rv` must point to valid stack memory and executable code, /// respectively. +#[cfg(cortex_m)] #[inline] pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { // Ensure thumb mode is set. let rv = (rv as u32) | 1; let msp = msp as u32; - call_asm!(__bootstrap(msp: u32, rv: u32) -> !); + asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); } /// Bootload. @@ -201,6 +285,7 @@ pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { /// The provided `vector_table` must point to a valid vector /// table, with a valid stack pointer as the first word and /// a valid reset vector as the second word. +#[cfg(cortex_m)] #[inline] pub unsafe fn bootload(vector_table: *const u32) -> ! { let msp = core::ptr::read_volatile(vector_table); diff --git a/src/call_asm.rs b/src/call_asm.rs deleted file mode 100644 index 295277f..0000000 --- a/src/call_asm.rs +++ /dev/null @@ -1,24 +0,0 @@ -/// An internal macro to invoke an assembly routine. -/// -/// Depending on whether the unstable `inline-asm` feature is enabled, this will either call into -/// the inline assembly implementation directly, or through the FFI shim (see `asm/lib.rs`). -macro_rules! call_asm { - ( $func:ident ( $($args:ident: $tys:ty),* ) $(-> $ret:ty)? ) => {{ - #[allow(unused_unsafe)] - unsafe { - match () { - #[cfg(feature = "inline-asm")] - () => crate::asm::inline::$func($($args),*), - - #[cfg(not(feature = "inline-asm"))] - () => { - extern "C" { - fn $func($($args: $tys),*) $(-> $ret)?; - } - - $func($($args),*) - }, - } - } - }}; -} diff --git a/src/cmse.rs b/src/cmse.rs index 36d7447..7826bb8 100644 --- a/src/cmse.rs +++ b/src/cmse.rs @@ -174,9 +174,9 @@ impl TestTarget { /// * the TT instruction was executed from an unprivileged mode and the A flag was not specified. #[inline] pub fn mpu_region(self) -> Option<u8> { - if self.tt_resp.srvalid() { - // Cast is safe as SREGION field is defined on 8 bits. - Some(self.tt_resp.sregion() as u8) + if self.tt_resp.mrvalid() { + // Cast is safe as MREGION field is defined on 8 bits. + Some(self.tt_resp.mregion() as u8) } else { None } diff --git a/src/critical_section.rs b/src/critical_section.rs new file mode 100644 index 0000000..e3d57d1 --- /dev/null +++ b/src/critical_section.rs @@ -0,0 +1,22 @@ +use critical_section::{set_impl, Impl, RawRestoreState}; + +use crate::interrupt; +use crate::register::primask; + +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 fn release(was_active: RawRestoreState) { + // Only re-enable interrupts if they were enabled before the critical section. + if was_active { + interrupt::enable() + } + } +} diff --git a/src/interrupt.rs b/src/interrupt.rs index 68719ec..f6ce990 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -1,6 +1,9 @@ //! Interrupts -pub use bare_metal::{CriticalSection, Mutex}; +#[cfg(cortex_m)] +use core::arch::asm; +#[cfg(cortex_m)] +use core::sync::atomic::{compiler_fence, Ordering}; /// Trait for enums of external interrupt numbers. /// @@ -23,36 +26,52 @@ pub unsafe trait InterruptNumber: Copy { fn number(self) -> u16; } -/// Disables all interrupts +/// Disables all interrupts in the current core. +#[cfg(cortex_m)] #[inline] pub fn disable() { - call_asm!(__cpsid()); + unsafe { + asm!("cpsid i", options(nomem, nostack, preserves_flags)); + } + + // Ensure no subsequent memory accesses are reordered to before interrupts are disabled. + compiler_fence(Ordering::SeqCst); } -/// Enables all the interrupts +/// Enables all the interrupts in the current core. /// /// # Safety /// -/// - Do not call this function inside an `interrupt::free` critical section +/// - Do not call this function inside a critical section. +#[cfg(cortex_m)] #[inline] pub unsafe fn enable() { - call_asm!(__cpsie()); + // Ensure no preceeding memory accesses are reordered to after interrupts are enabled. + compiler_fence(Ordering::SeqCst); + + asm!("cpsie i", options(nomem, nostack, preserves_flags)); } -/// Execute closure `f` in an interrupt-free context. +/// Execute closure `f` with interrupts disabled in the current core. /// -/// This as also known as a "critical section". +/// This method does not synchronise multiple cores and may disable required +/// interrupts on some platforms; see the `critical-section` crate for a cross-platform +/// way to enter a critical section which provides a `CriticalSection` token. +/// +/// This crate provides an implementation for `critical-section` suitable for single-core systems, +/// based on disabling all interrupts. It can be enabled with the `critical-section-single-core` feature. +#[cfg(cortex_m)] #[inline] pub fn free<F, R>(f: F) -> R where - F: FnOnce(&CriticalSection) -> R, + F: FnOnce() -> R, { let primask = crate::register::primask::read(); // disable interrupts disable(); - let r = f(unsafe { &CriticalSection::new() }); + let r = f(); // If the interrupts were active before our `disable` call, then re-enable // them. Otherwise, keep them disabled @@ -62,3 +81,15 @@ where 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>(_: F) -> R +where + F: FnOnce() -> R, +{ + panic!("cortex_m::interrupt::free() is only functional on cortex-m platforms"); +} @@ -9,20 +9,15 @@ //! //! # Optional features //! -//! ## `inline-asm` +//! ## `critical-section-single-core` //! -//! When this feature is enabled the implementation of all the functions inside the `asm` and -//! `register` modules use inline assembly (`asm!`) instead of external assembly (FFI into separate -//! assembly files pre-compiled using `arm-none-eabi-gcc`). The advantages of enabling `inline-asm` -//! are: +//! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section) +//! implementation suitable for single-core targets, based on disabling interrupts globally. //! -//! - Reduced overhead. FFI eliminates the possibility of inlining so all operations include a -//! function call overhead when `inline-asm` is not enabled. -//! -//! - Some of the `register` API only becomes available only when `inline-asm` is enabled. Check the -//! API docs for details. -//! -//! The disadvantage is that `inline-asm` requires a nightly toolchain. +//! It is **unsound** to enable it on multi-core targets or for code running in unprivileged mode, +//! and may cause functional problems in systems where some interrupts must be not be disabled +//! 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. //! //! ## `cm7-r0p1` //! @@ -30,32 +25,11 @@ //! functions in this crate only work correctly on those chips if this Cargo feature is enabled //! (the functions are documented accordingly). //! -//! ## `linker-plugin-lto` -//! -//! This feature links against prebuilt assembly blobs that are compatible with [Linker-Plugin LTO]. -//! This allows inlining assembly routines into the caller, even without the `inline-asm` feature, -//! and works on stable Rust (but note the drawbacks below!). -//! -//! If you want to use this feature, you need to be aware of a few things: -//! -//! - You need to make sure that `-Clinker-plugin-lto` is passed to rustc. Please refer to the -//! [Linker-Plugin LTO] documentation for details. -//! -//! - You have to use a Rust version whose LLVM version is compatible with the toolchain in -//! `asm-toolchain`. -//! -//! - Due to a [Rust bug][rust-lang/rust#75940] in compiler versions **before 1.49**, this option -//! does not work with optimization levels `s` and `z`. -//! -//! [Linker-Plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html -//! [rust-lang/rust#75940]: https://github.com/rust-lang/rust/issues/75940 -//! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.42 and up. It *might* +//! This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* //! compile with older versions but that may change in any new patch release. -#![cfg_attr(feature = "inline-asm", feature(asm))] #![deny(missing_docs)] #![no_std] #![allow(clippy::identity_op)] @@ -79,11 +53,6 @@ // Don't warn about feature(asm) being stable on Rust >= 1.59.0 #![allow(stable_features)] -extern crate bare_metal; -extern crate volatile_register; - -#[macro_use] -mod call_asm; #[macro_use] mod macros; @@ -95,7 +64,16 @@ pub mod interrupt; #[cfg(all(not(armv6m), not(armv8m_base)))] pub mod itm; pub mod peripheral; -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/src/macros.rs b/src/macros.rs index 512c932..2cf4f89 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -31,7 +31,10 @@ macro_rules! iprintln { /// at most once in the whole lifetime of the program. /// /// # Notes -/// This macro is unsound on multi core systems. +/// +/// 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 /// the debugger and is not referencable from other code. See example below. @@ -62,7 +65,7 @@ macro_rules! iprintln { #[macro_export] macro_rules! singleton { ($name:ident: $ty:ty = $expr:expr) => { - $crate::interrupt::free(|_| { + $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`... diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs index c5f7bc9..72575d3 100644 --- a/src/peripheral/dwt.rs +++ b/src/peripheral/dwt.rs @@ -155,6 +155,18 @@ impl DWT { } } + /// Disables the cycle counter + #[cfg(not(armv6m))] + #[inline] + pub fn disable_cycle_counter(&mut self) { + unsafe { + self.ctrl.modify(|mut r| { + r.set_cyccntena(false); + r + }); + } + } + /// Returns `true` if the cycle counter is enabled #[cfg(not(armv6m))] #[inline] diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index af922b1..bf18151 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -60,8 +60,6 @@ use core::marker::PhantomData; use core::ops; -use crate::interrupt; - #[cfg(cm7)] pub mod ac; #[cfg(not(armv6m))] @@ -165,7 +163,7 @@ impl Peripherals { /// Returns all the core peripherals *once* #[inline] pub fn take() -> Option<Self> { - interrupt::free(|_| { + critical_section::with(|_| { if unsafe { TAKEN } { None } else { diff --git a/src/peripheral/sau.rs b/src/peripheral/sau.rs index da91aca..6b8477f 100644 --- a/src/peripheral/sau.rs +++ b/src/peripheral/sau.rs @@ -7,7 +7,6 @@ //! //! For reference please check the section B8.3 of the Armv8-M Architecture Reference Manual. -use crate::interrupt; use crate::peripheral::SAU; use bitfield::bitfield; use volatile_register::{RO, RW}; @@ -162,7 +161,7 @@ impl SAU { /// This function is executed under a critical section to prevent having inconsistent results. #[inline] pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> { - interrupt::free(|_| { + critical_section::with(|_| { let base_address = region.base_address; let limit_address = region.limit_address; let attribute = region.attribute; @@ -215,7 +214,7 @@ impl SAU { /// This function is executed under a critical section to prevent having inconsistent results. #[inline] pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> { - interrupt::free(|_| { + critical_section::with(|_| { if region_number >= self.region_numbers() { Err(SauError::RegionNumberTooBig) } else { diff --git a/src/peripheral/tpiu.rs b/src/peripheral/tpiu.rs index 0762495..14dd35c 100644 --- a/src/peripheral/tpiu.rs +++ b/src/peripheral/tpiu.rs @@ -118,7 +118,6 @@ impl TPIU { /// [`trace_output_protocol`](Self::set_trace_output_protocol). #[inline] pub fn trace_output_protocol(&self) -> Option<TraceProtocol> { - use core::convert::TryInto; self.sppr.read().txmode().try_into().ok() } diff --git a/src/prelude.rs b/src/prelude.rs deleted file mode 100644 index bc47cc0..0000000 --- a/src/prelude.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Prelude - -pub use embedded_hal::prelude::*; diff --git a/src/register/apsr.rs b/src/register/apsr.rs index e83435c..edb8737 100644 --- a/src/register/apsr.rs +++ b/src/register/apsr.rs @@ -1,5 +1,8 @@ //! Application Program Status Register +#[cfg(cortex_m)] +use core::arch::asm; + /// Application Program Status Register #[derive(Clone, Copy, Debug)] pub struct Apsr { @@ -45,10 +48,10 @@ impl Apsr { } /// Reads the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub fn read() -> Apsr { - let bits: u32 = call_asm!(__apsr_r() -> u32); + let bits; + unsafe { asm!("mrs {}, APSR", out(reg) bits, options(nomem, nostack, preserves_flags)) }; Apsr { bits } } diff --git a/src/register/basepri.rs b/src/register/basepri.rs index 07084cd..cffb379 100644 --- a/src/register/basepri.rs +++ b/src/register/basepri.rs @@ -1,24 +1,42 @@ //! Base Priority Mask Register +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> u8 { - call_asm!(__basepri_r() -> u8) + let r; + unsafe { asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes to the CPU register /// /// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the /// `cm7-r0p1` Cargo feature or this function WILL misbehave. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(basepri: u8) { #[cfg(feature = "cm7-r0p1")] { - call_asm!(__basepri_w_cm7_r0p1(basepri: u8)); + asm!( + "mrs {1}, PRIMASK", + "cpsid i", + "tst.w {1}, #1", + "msr BASEPRI, {0}", + "it ne", + "bxne lr", + "cpsie i", + in(reg) basepri, + out(reg) _, + options(nomem, nostack, preserves_flags), + ); } #[cfg(not(feature = "cm7-r0p1"))] { - call_asm!(__basepri_w(basepri: u8)); + asm!("msr BASEPRI, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)); } } diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs index cea3838..2881c4f 100644 --- a/src/register/basepri_max.rs +++ b/src/register/basepri_max.rs @@ -1,5 +1,8 @@ //! Base Priority Mask Register (conditional write) +#[cfg(cortex_m)] +use core::arch::asm; + /// Writes to BASEPRI *if* /// /// - `basepri != 0` AND `basepri::read() == 0`, OR @@ -7,15 +10,31 @@ /// /// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the /// `cm7-r0p1` Cargo feature or this function WILL misbehave. +#[cfg(cortex_m)] #[inline] pub fn write(basepri: u8) { #[cfg(feature = "cm7-r0p1")] { - call_asm!(__basepri_max_cm7_r0p1(basepri: u8)); + unsafe { + asm!( + "mrs {1}, PRIMASK", + "cpsid i", + "tst.w {1}, #1", + "msr BASEPRI_MAX, {0}", + "it ne", + "bxne lr", + "cpsie i", + in(reg) basepri, + out(reg) _, + options(nomem, nostack, preserves_flags), + ); + } } #[cfg(not(feature = "cm7-r0p1"))] { - call_asm!(__basepri_max(basepri: u8)); + unsafe { + asm!("msr BASEPRI_MAX, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)); + } } } diff --git a/src/register/control.rs b/src/register/control.rs index a991625..d781913 100644 --- a/src/register/control.rs +++ b/src/register/control.rs @@ -1,5 +1,10 @@ //! Control register +#[cfg(cortex_m)] +use core::arch::asm; +#[cfg(cortex_m)] +use core::sync::atomic::{compiler_fence, Ordering}; + /// Control register #[derive(Clone, Copy, Debug)] pub struct Control { @@ -150,15 +155,29 @@ impl Fpca { } /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> Control { - let bits: u32 = call_asm!(__control_r() -> u32); + let bits; + unsafe { asm!("mrs {}, CONTROL", out(reg) bits, options(nomem, nostack, preserves_flags)) }; Control { bits } } /// Writes to the CPU register. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(control: Control) { let control = control.bits(); - call_asm!(__control_w(control: u32)); + + // ISB is required after writing to CONTROL, + // per ARM architectural requirements (see Application Note 321). + asm!( + "msr CONTROL, {}", + "isb", + in(reg) control, + options(nomem, nostack, preserves_flags), + ); + + // Ensure memory accesses are not reordered around the CONTROL update. + compiler_fence(Ordering::SeqCst); } diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs index e57fa28..1d32709 100644 --- a/src/register/faultmask.rs +++ b/src/register/faultmask.rs @@ -1,5 +1,8 @@ //! Fault Mask Register +#[cfg(cortex_m)] +use core::arch::asm; + /// All exceptions are ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Faultmask { @@ -24,9 +27,11 @@ impl Faultmask { } /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> Faultmask { - let r: u32 = call_asm!(__faultmask_r() -> u32); + let r: u32; + unsafe { asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; if r & (1 << 0) == (1 << 0) { Faultmask::Inactive } else { diff --git a/src/register/fpscr.rs b/src/register/fpscr.rs index 68692c7..bffed6c 100644 --- a/src/register/fpscr.rs +++ b/src/register/fpscr.rs @@ -1,5 +1,7 @@ //! Floating-point Status Control Register +use core::arch::asm; + /// Floating-point Status Control Register #[derive(Clone, Copy, Debug)] pub struct Fpscr { @@ -293,7 +295,8 @@ impl RMode { /// Read the FPSCR register #[inline] pub fn read() -> Fpscr { - let r: u32 = call_asm!(__fpscr_r() -> u32); + let r; + unsafe { asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags)) }; Fpscr::from_bits(r) } @@ -301,5 +304,5 @@ pub fn read() -> Fpscr { #[inline] pub unsafe fn write(fpscr: Fpscr) { let fpscr = fpscr.bits(); - call_asm!(__fpscr_w(fpscr: u32)); + asm!("vmsr fpscr, {}", in(reg) fpscr, options(nomem, nostack)); } diff --git a/src/register/lr.rs b/src/register/lr.rs index 1aa546c..02708ae 100644 --- a/src/register/lr.rs +++ b/src/register/lr.rs @@ -1,17 +1,20 @@ //! Link register +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__lr_r() -> u32) + let r; + unsafe { asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__lr_w(bits: u32)); + asm!("mov lr, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/mod.rs b/src/register/mod.rs index 48d157a..aee7d21 100644 --- a/src/register/mod.rs +++ b/src/register/mod.rs @@ -56,13 +56,8 @@ pub mod msplim; #[cfg(armv8m_main)] pub mod psplim; -// Accessing these registers requires inline assembly because their contents are tied to the current -// stack frame -#[cfg(feature = "inline-asm")] pub mod apsr; -#[cfg(feature = "inline-asm")] pub mod lr; -#[cfg(feature = "inline-asm")] pub mod pc; diff --git a/src/register/msp.rs b/src/register/msp.rs index bccc2ae..22ce7d9 100644 --- a/src/register/msp.rs +++ b/src/register/msp.rs @@ -1,16 +1,27 @@ //! Main Stack Pointer +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__msp_r() -> u32) + let r; + unsafe { asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register +#[cfg(cortex_m)] #[inline] #[deprecated = "calling this function invokes Undefined Behavior, consider asm::bootstrap as an alternative"] pub unsafe fn write(bits: u32) { - call_asm!(__msp_w(bits: u32)); + // Technically is writing to the stack pointer "not pushing any data to the stack"? + // In any event, if we don't set `nostack` here, this method is useless as the new + // stack value is immediately mutated by returning. Really this is just not a good + // method and its use is marked as deprecated. + asm!("msr MSP, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } /// Reads the Non-Secure CPU register from Secure state. @@ -19,7 +30,9 @@ pub unsafe fn write(bits: u32) { #[cfg(armv8m)] #[inline] pub fn read_ns() -> u32 { - call_asm!(__msp_ns_r() -> u32) + let r; + unsafe { asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the Non-Secure CPU register from Secure state. @@ -28,5 +41,5 @@ pub fn read_ns() -> u32 { #[cfg(armv8m)] #[inline] pub unsafe fn write_ns(bits: u32) { - call_asm!(__msp_ns_w(bits: u32)); + asm!("msr MSP_NS, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/msplim.rs b/src/register/msplim.rs index ac6f9ed..7b45b33 100644 --- a/src/register/msplim.rs +++ b/src/register/msplim.rs @@ -1,13 +1,17 @@ //! Main Stack Pointer Limit Register +use core::arch::asm; + /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__msplim_r() -> u32) + let r; + unsafe { asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__msplim_w(bits: u32)) + asm!("msr MSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/pc.rs b/src/register/pc.rs index 0b33629..3460664 100644 --- a/src/register/pc.rs +++ b/src/register/pc.rs @@ -1,17 +1,20 @@ //! Program counter +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__pc_r() -> u32) + let r; + unsafe { asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__pc_w(bits: u32)); + asm!("mov pc, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/primask.rs b/src/register/primask.rs index 842ca49..e95276f 100644 --- a/src/register/primask.rs +++ b/src/register/primask.rs @@ -1,5 +1,8 @@ //! Priority mask register +#[cfg(cortex_m)] +use core::arch::asm; + /// All exceptions with configurable priority are ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Primask { @@ -24,9 +27,11 @@ impl Primask { } /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> Primask { - let r: u32 = call_asm!(__primask_r() -> u32); + let r: u32; + unsafe { asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; if r & (1 << 0) == (1 << 0) { Primask::Inactive } else { diff --git a/src/register/psp.rs b/src/register/psp.rs index 0bca22c..c8f53b9 100644 --- a/src/register/psp.rs +++ b/src/register/psp.rs @@ -1,13 +1,22 @@ //! Process Stack Pointer +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__psp_r() -> u32) + let r; + unsafe { asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register +#[cfg(cortex_m)] #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__psp_w(bits: u32)) + // See comment on msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP + // if MSP is currently being used as the stack pointer. + asm!("msr PSP, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/psplim.rs b/src/register/psplim.rs index 8ee1e94..832f9c6 100644 --- a/src/register/psplim.rs +++ b/src/register/psplim.rs @@ -1,13 +1,17 @@ //! Process Stack Pointer Limit Register +use core::arch::asm; + /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__psplim_r() -> u32) + let r; + unsafe { asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__psplim_w(bits: u32)) + asm!("msr PSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } |