aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <japaricious@gmail.com> 2017-03-04 20:46:19 -0500
committerGravatar Jorge Aparicio <japaricious@gmail.com> 2017-03-04 20:46:19 -0500
commit251d1aa11244d5356659ccf969e29b0e7da82c7a (patch)
treec586f652b45b6981c3aa15ea870643763d7dbc35 /src
parentb4f105cde28d89f3c8e42e4fe341390a7dc2dccf (diff)
downloadcortex-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.rs64
-rw-r--r--src/exception.rs28
-rw-r--r--src/interrupt.rs33
-rw-r--r--src/lib.rs2
-rw-r--r--src/register.rs125
-rw-r--r--src/register/apsr.rs52
-rw-r--r--src/register/basepri.rs27
-rw-r--r--src/register/basepri_max.rs16
-rw-r--r--src/register/control.rs117
-rw-r--r--src/register/faultmask.rs40
-rw-r--r--src/register/lr.rs25
-rw-r--r--src/register/mod.rs41
-rw-r--r--src/register/msp.rs25
-rw-r--r--src/register/pc.rs25
-rw-r--r--src/register/primask.rs40
-rw-r--r--src/register/psp.rs25
16 files changed, 520 insertions, 165 deletions
diff --git a/src/asm.rs b/src/asm.rs
index 2e3368a..b94d4ef 100644
--- a/src/asm.rs
+++ b/src/asm.rs
@@ -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();
}
diff --git a/src/lib.rs b/src/lib.rs
index 5e75d53..322d20c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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");
+}