aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm.rs167
-rw-r--r--src/call_asm.rs24
-rw-r--r--src/cmse.rs6
-rw-r--r--src/critical_section.rs22
-rw-r--r--src/interrupt.rs51
-rw-r--r--src/lib.rs58
-rw-r--r--src/macros.rs7
-rw-r--r--src/peripheral/dwt.rs12
-rw-r--r--src/peripheral/mod.rs4
-rw-r--r--src/peripheral/sau.rs5
-rw-r--r--src/peripheral/tpiu.rs1
-rw-r--r--src/prelude.rs3
-rw-r--r--src/register/apsr.rs9
-rw-r--r--src/register/basepri.rs24
-rw-r--r--src/register/basepri_max.rs23
-rw-r--r--src/register/control.rs23
-rw-r--r--src/register/faultmask.rs7
-rw-r--r--src/register/fpscr.rs7
-rw-r--r--src/register/lr.rs15
-rw-r--r--src/register/mod.rs5
-rw-r--r--src/register/msp.rs21
-rw-r--r--src/register/msplim.rs8
-rw-r--r--src/register/pc.rs15
-rw-r--r--src/register/primask.rs7
-rw-r--r--src/register/psp.rs13
-rw-r--r--src/register/psplim.rs8
26 files changed, 374 insertions, 171 deletions
diff --git a/src/asm.rs b/src/asm.rs
index 4dc1ab0..3a3485a 100644
--- a/src/asm.rs
+++ b/src/asm.rs
@@ -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");
+}
diff --git a/src/lib.rs b/src/lib.rs
index dd46fd7..7c1599c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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));
}