diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/itm.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 10 | ||||
-rw-r--r-- | src/macros.rs | 6 | ||||
-rw-r--r-- | src/peripheral/dcb.rs | 21 | ||||
-rw-r--r-- | src/peripheral/mod.rs | 1 | ||||
-rw-r--r-- | src/peripheral/nvic.rs | 35 | ||||
-rw-r--r-- | src/peripheral/scb.rs | 211 | ||||
-rw-r--r-- | src/register/basepri.rs | 21 | ||||
-rw-r--r-- | src/register/basepri_max.rs | 21 |
9 files changed, 297 insertions, 35 deletions
@@ -4,7 +4,7 @@ use core::{fmt, mem, ptr, slice}; -use aligned::Aligned; +use aligned::{Aligned, A4}; use peripheral::itm::Stim; @@ -77,7 +77,7 @@ pub fn write_all(port: &mut Stim, buffer: &[u8]) { /// # Examples /// /// ``` ignore -/// let mut buffer: Aligned<u32, _> = Aligned([0; 14]); +/// let mut buffer: Aligned<A4, _> = Aligned([0; 14]); /// /// buffer.copy_from_slice(b"Hello, world!\n"); /// @@ -86,7 +86,7 @@ pub fn write_all(port: &mut Stim, buffer: &[u8]) { /// // Or equivalently /// itm::write_aligned(&itm.stim[0], &Aligned(*b"Hello, world!\n")); /// ``` -pub fn write_aligned(port: &mut Stim, buffer: &Aligned<u32, [u8]>) { +pub fn write_aligned(port: &mut Stim, buffer: &Aligned<A4, [u8]>) { unsafe { let len = buffer.len(); @@ -7,25 +7,18 @@ //! - Interrupt manipulation mechanisms //! - Safe wrappers around Cortex-M specific instructions like `bkpt` //! -//! # Requirements -//! -//! To use this crate on the stable or beta channel `arm-none-eabi-gcc` needs to be installed and -//! available in your `$PATH`. -//! //! # 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 compiled using `arm-none-eabi-gcc`). The advantages the enabling `inline-asm` +//! 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. //! -//! - `arm-none-eabi-gcc` is not required for building this crate. -//! //! - Some of the `register` API only becomes available only when `inline-asm` is enabled. Check the //! API docs for details. //! @@ -39,7 +32,6 @@ #![cfg_attr(feature = "inline-asm", feature(asm))] #![deny(missing_docs)] -#![deny(warnings)] #![no_std] extern crate aligned; diff --git a/src/macros.rs b/src/macros.rs index e41cdc5..813552f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -13,13 +13,13 @@ macro_rules! iprint { #[macro_export] macro_rules! iprintln { ($channel:expr) => { - iprint!($channel, "\n"); + $crate::itm::write_str($channel, "\n"); }; ($channel:expr, $fmt:expr) => { - iprint!($channel, concat!($fmt, "\n")); + $crate::itm::write_str($channel, concat!($fmt, "\n")); }; ($channel:expr, $fmt:expr, $($arg:tt)*) => { - iprint!($channel, concat!($fmt, "\n"), $($arg)*); + $crate::itm::write_fmt($channel, format_args!(concat!($fmt, "\n"), $($arg)*)); }; } diff --git a/src/peripheral/dcb.rs b/src/peripheral/dcb.rs index 02ec901..14dc75b 100644 --- a/src/peripheral/dcb.rs +++ b/src/peripheral/dcb.rs @@ -2,6 +2,10 @@ use volatile_register::{RW, WO}; +use peripheral::DCB; + +const DCB_DEMCR_TRCENA: u32 = 1 << 24; + /// Register block #[repr(C)] pub struct RegisterBlock { @@ -14,3 +18,20 @@ pub struct RegisterBlock { /// Debug Exception and Monitor Control pub demcr: RW<u32>, } + +impl DCB { + /// Enables TRACE. This is for example required by the + /// `peripheral::DWT` cycle counter to work properly. + /// As by STM documentation, this flag is not reset on + /// soft-reset, only on power reset. + pub fn enable_trace(&mut self) { + // set bit 24 / TRCENA + unsafe { self.demcr.modify(|w| w | DCB_DEMCR_TRCENA); } + } + + /// Disables TRACE. See `DCB::enable_trace()` for more details + pub fn disable_trace(&mut self) { + // unset bit 24 / TRCENA + unsafe { self.demcr.modify(|w| w & !DCB_DEMCR_TRCENA); } + } +} diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index fe52bd1..6aacb73 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -77,7 +77,6 @@ // TODO stand-alone registers: ICTR, ACTLR and STIR -#![allow(private_no_mangle_statics)] use core::marker::PhantomData; use core::ops; diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 1a6a027..c59c2c8 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -69,13 +69,12 @@ pub struct RegisterBlock { impl NVIC { /// Clears `interrupt`'s pending state + #[deprecated(since = "0.5.8", note = "Use `NVIC::unpend`")] pub fn clear_pending<I>(&mut self, interrupt: I) where I: Nr, { - let nr = interrupt.nr(); - - unsafe { self.icpr[usize::from(nr / 32)].write(1 << (nr % 32)) } + Self::unpend(interrupt) } /// Disables `interrupt` @@ -161,13 +160,23 @@ impl NVIC { } /// Forces `interrupt` into pending state - pub fn set_pending<I>(&mut self, interrupt: I) + pub fn pend<I>(interrupt: I) where I: Nr, { let nr = interrupt.nr(); - unsafe { self.ispr[usize::from(nr / 32)].write(1 << (nr % 32)) } + // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state + unsafe { (*Self::ptr()).ispr[usize::from(nr / 32)].write(1 << (nr % 32)) } + } + + /// Forces `interrupt` into pending state + #[deprecated(since = "0.5.8", note = "Use `NVIC::pend`")] + pub fn set_pending<I>(&mut self, interrupt: I) + where + I: Nr, + { + Self::pend(interrupt) } /// Sets the "priority" of `interrupt` to `prio` @@ -177,6 +186,11 @@ impl NVIC { /// /// On ARMv6-M, updating an interrupt priority requires a read-modify-write operation. On /// ARMv7-M, the operation is performed in a single atomic write operation. + /// + /// # Unsafety + /// + /// Changing priority levels can break priority-based critical sections (see + /// [`register::basepri`](../register/basepri/index.html)) and compromise memory safety. pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8) where I: Nr, @@ -198,6 +212,17 @@ impl NVIC { } } + /// Clears `interrupt`'s pending state + pub fn unpend<I>(interrupt: I) + where + I: Nr, + { + let nr = interrupt.nr(); + + // NOTE(unsafe) atomic stateless write; ICPR doesn't store any state + unsafe { (*Self::ptr()).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) } + } + #[cfg(armv6m)] fn ipr_index<I>(interrupt: &I) -> usize where diff --git a/src/peripheral/scb.rs b/src/peripheral/scb.rs index ba36093..c82e098 100644 --- a/src/peripheral/scb.rs +++ b/src/peripheral/scb.rs @@ -7,9 +7,9 @@ use volatile_register::RW; #[cfg(not(armv6m))] use super::cpuid::CsselrCacheType; #[cfg(not(armv6m))] -use super::CPUID; -#[cfg(not(armv6m))] use super::CBP; +#[cfg(not(armv6m))] +use super::CPUID; use super::SCB; /// Register block @@ -19,10 +19,7 @@ pub struct RegisterBlock { pub icsr: RW<u32>, /// Vector Table Offset (not present on Cortex-M0 variants) - #[cfg(not(armv6m))] pub vtor: RW<u32>, - #[cfg(armv6m)] - _reserved0: u32, /// Application Interrupt and Reset Control pub aircr: RW<u32>, @@ -598,3 +595,207 @@ impl SCB { } } } + +const SCB_SCR_SLEEPONEXIT: u32 = 0x1 << 1; + +impl SCB { + /// Set the SLEEPONEXIT bit in the SCR register + pub fn set_sleeponexit(&mut self) { + unsafe { + self.scr.modify(|scr| scr | SCB_SCR_SLEEPONEXIT); + } + } + + /// Clear the SLEEPONEXIT bit in the SCR register + pub fn clear_sleeponexit(&mut self) { + unsafe { + self.scr.modify(|scr| scr & !SCB_SCR_SLEEPONEXIT); + } + } +} + +const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16; +const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x5 << 8; +const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2; + +impl SCB { + /// Initiate a system reset request to reset the MCU + pub fn system_reset(&mut self) -> ! { + ::asm::dsb(); + unsafe { + self.aircr.modify( + |r| { + SCB_AIRCR_VECTKEY | // otherwise the write is ignored + r & SCB_AIRCR_PRIGROUP_MASK | // keep priority group unchanged + SCB_AIRCR_SYSRESETREQ + }, // set the bit + ) + }; + ::asm::dsb(); + loop { + // wait for the reset + ::asm::nop(); // avoid rust-lang/rust#28728 + } + } +} + +const SCB_ICSR_PENDSVSET: u32 = 1 << 28; +const SCB_ICSR_PENDSVCLR: u32 = 1 << 27; + +const SCB_ICSR_PENDSTSET: u32 = 1 << 26; +const SCB_ICSR_PENDSTCLR: u32 = 1 << 25; + +impl SCB { + /// Set the PENDSVSET bit in the ICSR register which will pend the PendSV interrupt + pub fn set_pendsv() { + unsafe { + (*Self::ptr()).icsr.write(SCB_ICSR_PENDSVSET); + } + } + + /// Check if PENDSVSET bit in the ICSR register is set meaning PendSV interrupt is pending + pub fn is_pendsv_pending() -> bool { + unsafe { (*Self::ptr()).icsr.read() & SCB_ICSR_PENDSVSET == SCB_ICSR_PENDSVSET } + } + + /// Set the PENDSVCLR bit in the ICSR register which will clear a pending PendSV interrupt + pub fn clear_pendsv() { + unsafe { + (*Self::ptr()).icsr.write(SCB_ICSR_PENDSVCLR); + } + } + + /// Set the PENDSTCLR bit in the ICSR register which will clear a pending SysTick interrupt + #[inline] + pub fn set_pendst() { + unsafe { + (*Self::ptr()).icsr.write(SCB_ICSR_PENDSTSET); + } + } + + /// Check if PENDSTSET bit in the ICSR register is set meaning SysTick interrupt is pending + #[inline] + pub fn is_pendst_pending() -> bool { + unsafe { (*Self::ptr()).icsr.read() & SCB_ICSR_PENDSTSET == SCB_ICSR_PENDSTSET } + } + + /// Set the PENDSTCLR bit in the ICSR register which will clear a pending SysTick interrupt + #[inline] + pub fn clear_pendst() { + unsafe { + (*Self::ptr()).icsr.write(SCB_ICSR_PENDSTCLR); + } + } +} + +/// System handlers, exceptions with configurable priority +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SystemHandler { + // NonMaskableInt, // priority is fixed + // HardFault, // priority is fixed + /// Memory management interrupt (not present on Cortex-M0 variants) + #[cfg(not(armv6m))] + MemoryManagement, + + /// Bus fault interrupt (not present on Cortex-M0 variants) + #[cfg(not(armv6m))] + BusFault, + + /// Usage fault interrupt (not present on Cortex-M0 variants) + #[cfg(not(armv6m))] + UsageFault, + + /// Secure fault interrupt (only on ARMv8-M) + #[cfg(any(armv8m, target_arch = "x86_64"))] + SecureFault, + + /// SV call interrupt + SVCall, + + /// Debug monitor interrupt (not present on Cortex-M0 variants) + #[cfg(not(armv6m))] + DebugMonitor, + + /// Pend SV interrupt + PendSV, + + /// System Tick interrupt + SysTick, +} + +impl SystemHandler { + fn index(&self) -> u8 { + match *self { + #[cfg(not(armv6m))] + SystemHandler::MemoryManagement => 4, + #[cfg(not(armv6m))] + SystemHandler::BusFault => 5, + #[cfg(not(armv6m))] + SystemHandler::UsageFault => 6, + #[cfg(any(armv8m, target_arch = "x86_64"))] + SystemHandler::SecureFault => 7, + SystemHandler::SVCall => 11, + #[cfg(not(armv6m))] + SystemHandler::DebugMonitor => 12, + SystemHandler::PendSV => 14, + SystemHandler::SysTick => 15, + } + } +} + +impl SCB { + /// Returns the hardware priority of `system_handler` + /// + /// *NOTE*: Hardware priority does not exactly match logical priority levels. See + /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details. + pub fn get_priority(system_handler: SystemHandler) -> u8 { + let index = system_handler.index(); + + #[cfg(not(armv6m))] + { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::ptr()).shpr[usize::from(index - 4)].read() } + } + + #[cfg(armv6m)] + { + // NOTE(unsafe) atomic read with no side effects + let shpr = unsafe { (*Self::ptr()).shpr[usize::from((index - 8) / 4)].read() }; + let prio = (shpr >> (8 * (index % 4))) & 0x000000ff; + prio as u8 + } + } + + /// Sets the hardware priority of `system_handler` to `prio` + /// + /// *NOTE*: Hardware priority does not exactly match logical priority levels. See + /// [`NVIC.get_priority`](struct.NVIC.html#method.get_priority) for more details. + /// + /// On ARMv6-M, updating a system handler priority requires a read-modify-write operation. On + /// ARMv7-M, the operation is performed in a single, atomic write operation. + /// + /// # Unsafety + /// + /// Changing priority levels can break priority-based critical sections (see + /// [`register::basepri`](../register/basepri/index.html)) and compromise memory safety. + pub unsafe fn set_priority(&mut self, system_handler: SystemHandler, prio: u8) { + let index = system_handler.index(); + + #[cfg(not(armv6m))] + { + self.shpr[usize::from(index - 4)].write(prio) + } + + #[cfg(armv6m)] + { + self.shpr[usize::from((index - 8) / 4)].modify(|value| { + let shift = 8 * (index % 4); + let mask = 0x000000ff << shift; + let prio = u32::from(prio) << shift; + + (value & !mask) | prio + }); + } + } +} diff --git a/src/register/basepri.rs b/src/register/basepri.rs index c9f09cc..a9cd6ef 100644 --- a/src/register/basepri.rs +++ b/src/register/basepri.rs @@ -45,13 +45,24 @@ pub unsafe fn write(_basepri: u8) { }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => { - extern "C" { - fn __basepri_w(_: u8); + () => match () { + #[cfg(not(feature = "cm7-r0p1"))] + () => { + extern "C" { + fn __basepri_w(_: u8); + } + + __basepri_w(_basepri); } + #[cfg(feature = "cm7-r0p1")] + () => { + extern "C" { + fn __basepri_w_cm7_r0p1(_: u8); + } - __basepri_w(_basepri); - } + __basepri_w_cm7_r0p1(_basepri); + } + }, #[cfg(not(cortex_m))] () => unimplemented!(), diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs index 91698b6..59ddb44 100644 --- a/src/register/basepri_max.rs +++ b/src/register/basepri_max.rs @@ -24,11 +24,24 @@ pub fn write(_basepri: u8) { #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { - extern "C" { - fn __basepri_max(_: u8); - } + match () { + #[cfg(not(feature = "cm7-r0p1"))] + () => { + extern "C" { + fn __basepri_max(_: u8); + } - __basepri_max(_basepri) + __basepri_max(_basepri) + } + #[cfg(feature = "cm7-r0p1")] + () => { + extern "C" { + fn __basepri_max_cm7_r0p1(_: u8); + } + + __basepri_max_cm7_r0p1(_basepri) + } + } }, #[cfg(not(cortex_m))] |