diff options
author | 2017-03-04 20:46:19 -0500 | |
---|---|---|
committer | 2017-03-04 20:46:19 -0500 | |
commit | 251d1aa11244d5356659ccf969e29b0e7da82c7a (patch) | |
tree | c586f652b45b6981c3aa15ea870643763d7dbc35 /src | |
parent | b4f105cde28d89f3c8e42e4fe341390a7dc2dccf (diff) | |
download | cortex-m-251d1aa11244d5356659ccf969e29b0e7da82c7a.tar.gz cortex-m-251d1aa11244d5356659ccf969e29b0e7da82c7a.tar.zst cortex-m-251d1aa11244d5356659ccf969e29b0e7da82c7a.zip |
review safety of the existing API, make the register API type safe
Diffstat (limited to 'src')
-rw-r--r-- | src/asm.rs | 64 | ||||
-rw-r--r-- | src/exception.rs | 28 | ||||
-rw-r--r-- | src/interrupt.rs | 33 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/register.rs | 125 | ||||
-rw-r--r-- | src/register/apsr.rs | 52 | ||||
-rw-r--r-- | src/register/basepri.rs | 27 | ||||
-rw-r--r-- | src/register/basepri_max.rs | 16 | ||||
-rw-r--r-- | src/register/control.rs | 117 | ||||
-rw-r--r-- | src/register/faultmask.rs | 40 | ||||
-rw-r--r-- | src/register/lr.rs | 25 | ||||
-rw-r--r-- | src/register/mod.rs | 41 | ||||
-rw-r--r-- | src/register/msp.rs | 25 | ||||
-rw-r--r-- | src/register/pc.rs | 25 | ||||
-rw-r--r-- | src/register/primask.rs | 40 | ||||
-rw-r--r-- | src/register/psp.rs | 25 |
16 files changed, 520 insertions, 165 deletions
@@ -1,58 +1,86 @@ //! Miscellaneous assembly instructions -/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint". +/// Puts the processor in Debug state. Debuggers can pick this up as a +/// "breakpoint". /// -/// Optionally, an "immediate" value (in the 0-255 range) can be passed to `bkpt!`. The debugger can -/// then read this value using the Program Counter (PC). +/// Optionally, an "immediate" value (in the 0-255 range) can be passed to +/// `bkpt!`. The debugger can then read this value using the Program Counter +/// (PC). #[cfg(target_arch = "arm")] #[macro_export] macro_rules! bkpt { () => { - asm!("bkpt" :::: "volatile"); + asm!("bkpt" + : + : + : + : "volatile"); }; ($imm:expr) => { - asm!(concat!("bkpt #", stringify!($imm)) :::: "volatile"); + asm!(concat!("bkpt #", stringify!($imm)) + : + : + : + : "volatile"); }; } -/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint". +/// Puts the processor in Debug state. Debuggers can pick this up as a +/// "breakpoint". /// -/// Optionally, an "immediate" value (in the 0-255 range) can be passed to `bkpt!`. The debugger can -/// then read this value using the Program Counter (PC). +/// Optionally, an "immediate" value (in the 0-255 range) can be passed to +/// `bkpt!`. The debugger can then read this value using the Program Counter +/// (PC). #[cfg(not(target_arch = "arm"))] #[macro_export] macro_rules! bkpt { () => { - asm!("nop" :::: "volatile"); + asm!(""); }; ($e:expr) => { - asm!("nop" :::: "volatile"); + asm!(""); }; } -/// Wait for event -pub unsafe fn wfe() { +/// Wait For Event +pub fn wfe() { match () { #[cfg(target_arch = "arm")] - () => asm!("wfe" :::: "volatile"), + () => unsafe { + asm!("wfe" + : + : + : + : "volatile") + }, #[cfg(not(target_arch = "arm"))] () => {} } } -/// Wait for interupt -pub unsafe fn wfi() { +/// Wait For Interrupt +pub fn wfi() { match () { #[cfg(target_arch = "arm")] - () => asm!("wfi" :::: "volatile"), + () => unsafe{ + asm!("wfi" + : + : + : + : "volatile") + }, #[cfg(not(target_arch = "arm"))] () => {} } } -/// A no-operation. Useful to stop delay loops being elided. +/// A no-operation. Useful to prevent delay loops from being optimized away. pub fn nop() { unsafe { - asm!("nop" :::: "volatile"); + asm!("nop" + : + : + : + : "volatile"); } } diff --git a/src/exception.rs b/src/exception.rs index fad3ea9..cd38366 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,6 +1,8 @@ //! Exceptions -use {Handler, Reserved, StackFrame}; +use {Handler, Reserved}; +#[cfg(target_arch = "arm")] +use StackFrame; /// Kind of exception #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -98,6 +100,7 @@ pub const DEFAULT_HANDLERS: Handlers = Handlers { pub unsafe extern "C" fn default_handler() { // This is the actual exception handler. `_sf` is a pointer to the previous // stack frame + #[cfg(target_arch = "arm")] extern "C" fn handler(_sf: &StackFrame) -> ! { #[cfg(feature = "semihosting")] hprintln!("EXCEPTION {:?} @ PC=0x{:08x}", Exception::current(), _sf.pc); @@ -109,12 +112,21 @@ pub unsafe extern "C" fn default_handler() { loop {} } - // "trampoline" to get to the real exception handler. - asm!("mrs r0, MSP - ldr r1, [r0, #20] - b $0" - : - : "i"(handler as extern "C" fn(&StackFrame) -> !) :: "volatile"); + match () { + #[cfg(target_arch = "arm")] + () => { + // "trampoline" to get to the real exception handler. + asm!("mrs r0, MSP + ldr r1, [r0, #20] + b $0" + : + : "i"(handler as extern "C" fn(&StackFrame) -> !) + : + : "volatile"); - ::core::intrinsics::unreachable() + ::core::intrinsics::unreachable() + } + #[cfg(not(target_arch = "arm"))] + () => {} + } } diff --git a/src/interrupt.rs b/src/interrupt.rs index edc3dfb..2552892 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -37,12 +37,16 @@ unsafe impl<T> Sync for Mutex<T> {} /// Disable interrupts, globally #[inline(always)] -pub unsafe fn disable() { +pub fn disable() { match () { #[cfg(target_arch = "arm")] - () => { - asm!("cpsid i" :::: "volatile"); - } + () => unsafe { + asm!("cpsid i" + : + : + : + : "volatile"); + }, #[cfg(not(target_arch = "arm"))] () => {} } @@ -50,12 +54,16 @@ pub unsafe fn disable() { /// Enable interrupts, globally #[inline(always)] -pub unsafe fn enable() { +pub fn enable() { match () { #[cfg(target_arch = "arm")] - () => { - asm!("cpsie i" :::: "volatile"); - } + () => unsafe { + asm!("cpsie i" + : + : + : + : "volatile"); + }, #[cfg(not(target_arch = "arm"))] () => {} } @@ -70,20 +78,19 @@ pub struct CsToken { /// Execute closure `f` in an interrupt-free context. /// This as also known as a "critical section". -pub unsafe fn free<F, R>(f: F) -> R +pub fn free<F, R>(f: F) -> R where F: FnOnce(&CsToken) -> R { let primask = ::register::primask::read(); + // disable interrupts disable(); let r = f(&CsToken { _private: () }); - // If the interrupts were enabled before our `disable` call, then re-enable + // If the interrupts were active before our `disable` call, then re-enable // them. Otherwise, keep them disabled - // PRIMASK & 1 = 1 indicates that the interrupts were disabled - // PRIMASK & 1 = 0 indicates that they were enabled - if primask & 1 == 0 { + if primask.is_active() { enable(); } @@ -9,11 +9,11 @@ //! - Miscellaneous assembly instructions like `bkpt` #![cfg_attr(feature = "semihosting", feature(macro_reexport))] +#![cfg_attr(target_arch = "arm", feature(core_intrinsics))] #![deny(missing_docs)] #![deny(warnings)] #![feature(asm)] #![feature(const_fn)] -#![feature(core_intrinsics)] #![feature(naked_functions)] #![no_std] diff --git a/src/register.rs b/src/register.rs deleted file mode 100644 index 8ab9a8d..0000000 --- a/src/register.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! Processor core registers -//! -//! The following registers can only be accessed in PRIVILEGED mode: -//! -//! - MSP -//! - IPSR -//! - EPSR -//! - PRIMASK -//! - FAULTMASK -//! - BASEPRI -//! - CONTROL -//! -//! The rest of registers (see list below) can be accessed in either, PRIVILEGED or UNPRIVILEGED, -//! mode. -//! -//! - PSP -//! - LR -//! - PC -//! - APSR -//! -//! # Caveats -//! -//! - The API doesn't check if the value passed to `write` is valid (e.g. reserved bits are not -//! modified) or not. It's up to the user to verify that. -//! -//! # References -//! -//! - Cortex-M* Devices Generic User Guide - Section 2.1.3 Core registers - -// NOTE all the functions here are `always(inline)` to prevent a function call which may change the -// contents of the core registers. - -macro_rules! sr { - ($name:ident) => { - /// Reads the special register - #[inline(always)] - pub unsafe fn read() -> u32 { - let r: u32; - match () { - #[cfg(target_arch = "arm")] - () => asm!(concat!("mrs ", "$0,", stringify!($name)) : "=r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - () => r = 0, - } - r - } - }; -} - -macro_rules! srw { - (#[$attr:meta] $name:ident) => { - #[$attr] - pub mod $name { - sr!($name); - - /// Writes to the special register - #[inline(always)] - pub unsafe fn write(r: u32) { - match r { - #[cfg(target_arch = "arm")] - _ => asm!(concat!("msr ", stringify!($name), ",$0") :: "r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - _ => {}, - } - } - } - }; -} - -macro_rules! sro { - (#[$attr:meta] $name:ident) => { - #[$attr] - pub mod $name { - sr!($name); - } - } -} - -macro_rules! rw { - (#[$attr:meta] $name:ident : $r:ident) => { - #[$attr] - pub mod $name { - /// Reads the special register - #[inline(always)] - pub unsafe fn read() -> u32 { - let r: u32; - match () { - #[cfg(target_arch = "arm")] - () => asm!(concat!("mov ", "$0,", stringify!($r)) : "=r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - () => r = 0, - } - r - } - - /// Writes to the special register - #[inline(always)] - pub unsafe fn write(r: u32) { - match r { - #[cfg(target_arch = "arm")] - _ => asm!(concat!("mov ", stringify!($r), ",$0") :: "r"(r) ::: "volatile"), - - #[cfg(not(target_arch = "arm"))] - _ => {} - } - } - } - } -} - -srw!(#[doc = "Main Stack Pointer"] msp); -srw!(#[doc = "Process Stack Pointer"] psp); -rw!(#[doc = "Link Register"] lr: r14); -rw!(#[doc = "Program Counter"] pc: r15); -srw!(#[doc = "Application Program Status Register"] apsr); -sro!(#[doc = "Interrupt Program Status Register"] ipsr); -sro!(#[doc = "Exception Program Status Register"] epsr); -srw!(#[doc = "Priority Mask Register"] primask); -srw!(#[doc = "Fault Mask Register"] faultmask); -srw!(#[doc = "Base Priority Mask Register"] basepri); -srw!(#[doc = "Base Priority Mask Register"] basepri_max); -srw!(#[doc = "Control Register"] control); diff --git a/src/register/apsr.rs b/src/register/apsr.rs new file mode 100644 index 0000000..338c684 --- /dev/null +++ b/src/register/apsr.rs @@ -0,0 +1,52 @@ +//! Application Program Status Register + +/// Application Program Status Register +pub struct Apsr { + bits: u32, +} + +impl Apsr { + /// Returns the contents of the register as raw bits + pub fn bits(&self) -> u32 { + self.bits + } + + /// DSP overflow and saturation flag + pub fn q(&self) -> bool { + self.bits & (1 << 27) == (1 << 27) + } + + /// Overflow flag + pub fn v(&self) -> bool { + self.bits & (1 << 28) == (1 << 28) + } + + /// Carry or borrow flag + pub fn c(&self) -> bool { + self.bits & (1 << 29) == (1 << 29) + } + + /// Zero flag + pub fn z(&self) -> bool { + self.bits & (1 << 30) == (1 << 30) + } + + /// Negative flag + pub fn n(&self) -> bool { + self.bits & (1 << 31) == (1 << 31) + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Apsr { + let r: u32; + unsafe { + asm!("mrs $0, APSR" + : "=r"(r) + : + : + : "volatile"); + } + Apsr { bits: r } +} diff --git a/src/register/basepri.rs b/src/register/basepri.rs new file mode 100644 index 0000000..e9164c1 --- /dev/null +++ b/src/register/basepri.rs @@ -0,0 +1,27 @@ +//! Base Priority Mask Register + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u8 { + let r: u32; + unsafe { + asm!("mrs $0, BASEPRI" + : "=r"(r) + : + : + : "volatile"); + } + r as u8 +} + +/// Writes to the CPU register +#[inline(always)] +pub fn write(basepri: u8) { + unsafe { + asm!("msr BASEPRI, $0" + : + : "r"(basepri) + : + : "volatile"); + } +} diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs new file mode 100644 index 0000000..d492015 --- /dev/null +++ b/src/register/basepri_max.rs @@ -0,0 +1,16 @@ +//! Base Priority Mask Register (conditional write) + +/// Writes to BASEPRI *if* +/// +/// - `basepri != 0` AND `basepri::read() == 0`, OR +/// - `basepri != 0` AND `basepri < basepri::read()` +#[inline(always)] +pub fn write(basepri: u8) { + unsafe { + asm!("msr BASEPRI_MAX, $0" + : + : "r"(basepri as u32) + : + : "volatile"); + } +} diff --git a/src/register/control.rs b/src/register/control.rs new file mode 100644 index 0000000..62ebff6 --- /dev/null +++ b/src/register/control.rs @@ -0,0 +1,117 @@ +//! Control register + +/// Control register +pub struct Control { + bits: u32, +} + +impl Control { + /// Returns the contents of the register as raw bits + pub fn bits(&self) -> u32 { + self.bits + } + + /// Thread mode privilege level + pub fn npriv(&self) -> Npriv { + if self.bits & (1 << 0) == (1 << 0) { + Npriv::Unprivileged + } else { + Npriv::Privileged + } + } + + /// Currently active stack pointer + pub fn spsel(&self) -> Spsel { + if self.bits & (1 << 1) == (1 << 1) { + Spsel::Psp + } else { + Spsel::Msp + } + } + + /// Whether context floating-point is currently active + pub fn fpca(&self) -> Fpca { + if self.bits & (1 << 2) == (1 << 2) { + Fpca::Active + } else { + Fpca::NotActive + } + } +} + +/// Thread mode privilege level +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Npriv { + /// Privileged + Privileged, + /// Unprivileged + Unprivileged, +} + +impl Npriv { + /// Is in privileged thread mode? + pub fn is_privileged(&self) -> bool { + *self == Npriv::Privileged + } + + /// Is in unprivileged thread mode? + pub fn is_unprivileged(&self) -> bool { + *self == Npriv::Unprivileged + } +} + +/// Currently active stack pointer +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Spsel { + /// MSP is the current stack pointer + Msp, + /// PSP is the current stack pointer + Psp, +} + +impl Spsel { + /// Is MSP the current stack pointer? + pub fn is_msp(&self) -> bool { + *self == Spsel::Msp + } + + /// Is PSP the current stack pointer? + pub fn is_psp(&self) -> bool { + *self == Spsel::Psp + } +} + +/// Whether context floating-point is currently active +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Fpca { + /// Floating-point context active. + Active, + /// No floating-point context active + NotActive, +} + +impl Fpca { + /// Is a floating-point context active? + pub fn is_active(&self) -> bool { + *self == Fpca::Active + } + + /// Is a floating-point context not active? + pub fn is_not_active(&self) -> bool { + *self == Fpca::NotActive + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Control { + let r: u32; + unsafe { + asm!("mrs $0, CONTROL" + : "=r"(r) + : + : + : "volatile"); + } + Control { bits: r } +} diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs new file mode 100644 index 0000000..5a06b37 --- /dev/null +++ b/src/register/faultmask.rs @@ -0,0 +1,40 @@ +//! Fault Mask Register + +/// All exceptions are ... +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Faultmask { + /// Active + Active, + /// Inactive, expect for NMI + Inactive, +} + +impl Faultmask { + /// All exceptions are active + pub fn is_active(&self) -> bool { + *self == Faultmask::Active + } + + /// All exceptions, except for NMI, are inactive + pub fn is_inactive(&self) -> bool { + *self == Faultmask::Inactive + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Faultmask { + let r: u32; + unsafe { + asm!("mrs $0, FAULTMASK" + : "=r"(r) + : + : + : "volatile"); + } + if r & (1 << 0) == (1 << 0) { + Faultmask::Inactive + } else { + Faultmask::Active + } +} diff --git a/src/register/lr.rs b/src/register/lr.rs new file mode 100644 index 0000000..fecfecb --- /dev/null +++ b/src/register/lr.rs @@ -0,0 +1,25 @@ +//! Link register + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r: u32; + unsafe { + asm!("mov $0,R14" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("mov R14,$0" + : + : "r"(bits) + : + : "volatile"); +} diff --git a/src/register/mod.rs b/src/register/mod.rs new file mode 100644 index 0000000..0ec5720 --- /dev/null +++ b/src/register/mod.rs @@ -0,0 +1,41 @@ +//! Processor core registers +//! +//! The following registers can only be accessed in PRIVILEGED mode: +//! +//! - BASEPRI +//! - CONTROL +//! - FAULTMASK +//! - MSP +//! - PRIMASK +//! +//! The rest of registers (see list below) can be accessed in either, PRIVILEGED or UNPRIVILEGED, +//! mode. +//! +//! - APSR +//! - LR +//! - PC +//! - PSP +//! +//! The following registers are NOT available on ARMv6-M devices +//! (`thumbv6m-none-eabi`): +//! +//! - BASEPRI +//! - FAULTMASK +//! +//! # References +//! +//! - Cortex-M* Devices Generic User Guide - Section 2.1.3 Core registers + +pub mod apsr; +#[cfg(not(thumbv6m))] +pub mod basepri; +#[cfg(not(thumbv6m))] +pub mod basepri_max; +pub mod control; +#[cfg(not(thumbv6m))] +pub mod faultmask; +pub mod lr; +pub mod msp; +pub mod pc; +pub mod primask; +pub mod psp; diff --git a/src/register/msp.rs b/src/register/msp.rs new file mode 100644 index 0000000..ebea6ed --- /dev/null +++ b/src/register/msp.rs @@ -0,0 +1,25 @@ +//! Main Stack Pointer + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r; + unsafe { + asm!("mrs $0,MSP" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("msr MSP,$0" + : + : "r"(bits) + : + : "volatile"); +} diff --git a/src/register/pc.rs b/src/register/pc.rs new file mode 100644 index 0000000..3fec1ae --- /dev/null +++ b/src/register/pc.rs @@ -0,0 +1,25 @@ +//! Program counter + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r; + unsafe { + asm!("mov $0,R15" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("mov R15,$0" + : + : "r"(bits) + : + : "volatile"); +} diff --git a/src/register/primask.rs b/src/register/primask.rs new file mode 100644 index 0000000..1e24b73 --- /dev/null +++ b/src/register/primask.rs @@ -0,0 +1,40 @@ +//! Priority mask register + +/// All exceptions with configurable priority are ... +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Primask { + /// Active + Active, + /// Inactive + Inactive, +} + +impl Primask { + /// All exceptions with configurable priority are active + pub fn is_active(&self) -> bool { + *self == Primask::Active + } + + /// All exceptions with configurable priority are inactive + pub fn is_inactive(&self) -> bool { + *self == Primask::Inactive + } +} + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> Primask { + let r: u32; + unsafe { + asm!("mrs $0, PRIMASK" + : "=r"(r) + : + : + : "volatile"); + } + if r & (1 << 0) == (1 << 0) { + Primask::Inactive + } else { + Primask::Active + } +} diff --git a/src/register/psp.rs b/src/register/psp.rs new file mode 100644 index 0000000..ecd6f9c --- /dev/null +++ b/src/register/psp.rs @@ -0,0 +1,25 @@ +//! Process Stack Pointer + +/// Reads the CPU register +#[inline(always)] +pub fn read() -> u32 { + let r; + unsafe { + asm!("mrs $0,PSP" + : "=r"(r) + : + : + : "volatile"); + } + r +} + +/// Writes `bits` to the CPU register +#[inline(always)] +pub unsafe fn write(bits: u32) { + asm!("msr PSP,$0" + : + : "r"(bits) + : + : "volatile"); +} |