diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm.rs | 139 | ||||
-rw-r--r-- | src/call_asm.rs | 24 | ||||
-rw-r--r-- | src/interrupt.rs | 19 | ||||
-rw-r--r-- | src/lib.rs | 40 | ||||
-rw-r--r-- | src/peripheral/mod.rs | 2 | ||||
-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 |
19 files changed, 276 insertions, 133 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,41 +23,66 @@ 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] 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`. +#[cfg(cortex_m)] #[inline] pub fn udf() -> ! { - call_asm!(__udf() -> !) + unsafe { asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)) }; } /// Wait For Event +#[cfg(cortex_m)] #[inline] pub fn wfe() { - call_asm!(__wfe()) + unsafe { asm!("wfe", options(nomem, nostack, preserves_flags)) }; } /// Wait For Interrupt +#[cfg(cortex_m)] #[inline] pub fn wfi() { - call_asm!(__wfi()) + unsafe { asm!("wfi", options(nomem, nostack, preserves_flags)) }; } /// Send Event +#[cfg(cortex_m)] #[inline] pub fn sev() { - call_asm!(__sev()) + unsafe { asm!("sev", options(nomem, nostack, preserves_flags)) }; } /// Instruction Synchronization Barrier @@ -67,7 +91,12 @@ pub fn sev() { /// from cache or memory, after the instruction has been completed. #[inline] 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 @@ -79,7 +108,12 @@ pub fn isb() { /// * all cache and branch predictor maintenance operations before this instruction complete #[inline] 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 @@ -89,7 +123,12 @@ pub fn dsb() { /// after the `DMB` instruction. #[inline] 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 @@ -102,8 +141,15 @@ pub fn dmb() { // 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 @@ -117,8 +163,15 @@ pub fn tt(addr: *mut u32) -> u32 { // 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 @@ -133,8 +186,15 @@ pub fn ttt(addr: *mut u32) -> u32 { // 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 @@ -149,8 +209,15 @@ pub fn tta(addr: *mut u32) -> u32 { // 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 @@ -160,15 +227,17 @@ pub fn ttat(addr: *mut u32) -> u32 { #[inline] #[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. +#[cfg(cortex_m)] #[inline] -pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { - call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32) +pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 { + asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, 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/interrupt.rs b/src/interrupt.rs index 68719ec..06b91f1 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -1,6 +1,10 @@ //! 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. /// @@ -24,9 +28,15 @@ pub unsafe trait InterruptNumber: Copy { } /// Disables all interrupts +#[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 @@ -34,14 +44,19 @@ pub fn disable() { /// # Safety /// /// - Do not call this function inside an `interrupt::free` 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. /// /// This as also known as a "critical section". +#[cfg(cortex_m)] #[inline] pub fn free<F, R>(f: F) -> R where @@ -9,53 +9,17 @@ //! //! # Optional features //! -//! ## `inline-asm` -//! -//! 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: -//! -//! - 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. -//! //! ## `cm7-r0p1` //! //! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some //! 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)] @@ -83,8 +47,6 @@ extern crate bare_metal; extern crate volatile_register; #[macro_use] -mod call_asm; -#[macro_use] mod macros; pub mod asm; diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index af922b1..d1dfb6a 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -60,6 +60,7 @@ use core::marker::PhantomData; use core::ops; +#[cfg(cortex_m)] use crate::interrupt; #[cfg(cm7)] @@ -163,6 +164,7 @@ static mut TAKEN: bool = false; impl Peripherals { /// Returns all the core peripherals *once* + #[cfg(cortex_m)] #[inline] pub fn take() -> Option<Self> { interrupt::free(|_| { 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)); } |