diff --git a/CHANGELOG.md b/CHANGELOG.md index e52c26ee..ec962007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- [breaking-change] Renamed `StackedRergisters` to `ExceptionFrame` to better + reflect the ARM documentation. + +- [breaking-change] Renamed the variants of `Exception` to better match the + ARM documentation. + +- [breaking-change] Renamed `Exception::current` to `Exception::active` and + changed the signature to return `None` when no exception is being serviced. + +### Removed + +- [breaking-change] The `ctxt` module along with the exception "tokens" in the + `exception` module. The `cortex-m-rt` crate v0.3.0 provides a more ergonomic + mechanism to add state to interrupts / exceptions; replace your uses of + `Local` with that. + +- [breaking-change] `default_handler`, `DEFAULT_HANDLERS` and `Handlers` from + the `exception` module as well as `Reserved` from the root of the crate. + `cortex-m-rt` v0.3.0 provides a mechanism to override exceptions and the + default exception handler. Change your use of these `Handlers` and others to + that. + +### Fixed + +- `interrupt::{enable,disable}` are now compiler barriers. The compiler should + not reorder code around these function calls for memory safety; that is the + case now. + ## [v0.2.11] - 2017-06-16 ### Added diff --git a/Cargo.toml b/Cargo.toml index f52e7212..49d61748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ keywords = ["arm", "cortex-m", "register", "peripheral"] license = "MIT OR Apache-2.0" name = "cortex-m" repository = "https://github.com/japaric/cortex-m" -version = "0.2.11" +version = "0.3.0" [dependencies] aligned = "0.1.1" diff --git a/src/ctxt.rs b/src/ctxt.rs deleted file mode 100644 index 727090b7..00000000 --- a/src/ctxt.rs +++ /dev/null @@ -1,160 +0,0 @@ -//! Interrupt / Exception context local data -//! -//! The main use case is safely adding state to exception / interrupt handlers. -//! -//! This is done in two stages, first you define a token that will appear in the -//! interrupt handler signature; each handler will have its unique token. This -//! token must be zero sized type because interrupt handlers' real signature is -//! `fn()` and it must also implement the `Context` trait. You must also make -//! sure that the token can't be constructed outside of the crate where it's -//! defined. -//! -//! ``` -//! # use cortex_m::ctxt::Context; -//! // This must be in a library crate -//! /// Token unique to the TIM7 interrupt handler -//! pub struct Tim7 { _0: () } -//! -//! unsafe impl Context for Tim7 {} -//! ``` -//! -//! Then in the application one can pin data to the interrupt handler using -//! `Local`. -//! -//! ``` -//! # #![feature(const_fn)] -//! # use std::cell::Cell; -//! # use cortex_m::ctxt::{Context, Local}; -//! # struct Tim7; -//! # unsafe impl Context for Tim7 {} -//! // omitted: how to put this handler in the vector table -//! extern "C" fn tim7(ctxt: Tim7) { -//! static STATE: Local, Tim7> = Local::new(Cell::new(false)); -//! -//! let state = STATE.borrow(&ctxt); -//! -//! // toggle state -//! state.set(!state.get()); -//! -//! if state.get() { -//! // something -//! } else { -//! // something else -//! } -//! } -//! ``` -//! -//! Note that due to the uniqueness of tokens, other handlers won't be able to -//! access context local data. (Given that you got the signatures right) -//! -//! ``` -//! # #![feature(const_fn)] -//! # use std::cell::Cell; -//! # use cortex_m::ctxt::{Context, Local}; -//! # struct Tim3; -//! # struct Tim4; -//! static TIM3_DATA: Local, Tim3> = Local::new(Cell::new(false)); -//! -//! extern "C" fn tim3(ctxt: Tim3) { -//! let data = TIM3_DATA.borrow(&ctxt); -//! } -//! -//! extern "C" fn tim4(ctxt: Tim4) { -//! //let data = TIM3_DATA.borrow(&ctxt); -//! // ^ wouldn't work -//! } -//! # unsafe impl Context for Tim3 {} -//! # fn main() {} -//! ``` -//! -//! To have the application use these tokenized function signatures, you can -//! define, in a library, a `Handlers` struct that represents the vector table: -//! -//! ``` -//! # struct Tim1; -//! # struct Tim2; -//! # struct Tim3; -//! # struct Tim4; -//! # extern "C" fn default_handler(_: T) {} -//! #[repr(C)] -//! pub struct Handlers { -//! tim1: extern "C" fn(Tim1), -//! tim2: extern "C" fn(Tim2), -//! tim3: extern "C" fn(Tim3), -//! tim4: extern "C" fn(Tim4), -//! /* .. */ -//! } -//! -//! pub const DEFAULT_HANDLERS: Handlers = Handlers { -//! tim1: default_handler, -//! tim2: default_handler, -//! tim3: default_handler, -//! tim4: default_handler, -//! /* .. */ -//! }; -//! ``` -//! -//! Then have the user use that `struct` to register the interrupt handlers: -//! -//! ``` -//! # struct Tim3; -//! # struct Handlers { tim3: extern "C" fn(Tim3), tim4: extern "C" fn(Tim3) } -//! # const DEFAULT_HANDLERS: Handlers = Handlers { tim3: tim3, tim4: tim3 }; -//! extern "C" fn tim3(ctxt: Tim3) { /* .. */ } -//! -//! // override the TIM3 interrupt handler -//! #[no_mangle] -//! static _INTERRUPTS: Handlers = Handlers { -//! tim3: tim3, ..DEFAULT_HANDLERS -//! }; -//! ``` -//! -//! This pattern is implemented for exceptions in this crate. See -//! `exception::Handlers` and `exception::DEFAULT_HANDLERS`. - -use core::marker::PhantomData; -use core::cell::UnsafeCell; - -/// Data local to a context -pub struct Local -where - Ctxt: Context, -{ - _ctxt: PhantomData, - data: UnsafeCell, -} - -impl Local -where - Ctxt: Context, -{ - /// Initializes context local data - pub const fn new(value: T) -> Self { - Local { - _ctxt: PhantomData, - data: UnsafeCell::new(value), - } - } - - /// Acquires a reference to the context local data - pub fn borrow<'ctxt>(&'static self, _ctxt: &'ctxt Ctxt) -> &'ctxt T { - unsafe { &*self.data.get() } - } - - /// Acquires a mutable reference to the context local data - pub fn borrow_mut<'ctxt>( - &'static self, - _ctxt: &'ctxt mut Ctxt, - ) -> &'ctxt mut T { - unsafe { &mut *self.data.get() } - } -} - -unsafe impl Sync for Local -where - Ctxt: Context, -{ -} - -/// A token unique to a context -pub unsafe trait Context {} diff --git a/src/exception.rs b/src/exception.rs index 61b74150..3052f665 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,29 +1,24 @@ //! Exceptions -use ctxt::Context; -use Reserved; - -/// Enumeration of all exceptions +/// Enumeration of all the exception types #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Exception { - /// i.e. currently not servicing an exception - ThreadMode, - /// Non-maskable interrupt. - Nmi, - /// All class of fault. + /// Non-maskable interrupt + NMI, + /// Other type of faults and unhandled faults HardFault, - /// Memory management. - MemoryManagementFault, - /// Pre-fetch fault, memory access fault. + /// Memory protection related fault + MenManage, + /// Pre-fetch or memory access fault BusFault, - /// Undefined instruction or illegal state. + /// Fault due to undefined instruction or illegal state UsageFault, - /// System service call via SWI instruction + /// Supervisor call SVCall, - /// Pendable request for system service + /// Pendable request for system-level service PendSV, - /// System tick timer - Systick, + /// System timer exception + SysTick, /// An interrupt Interrupt(u8), // Unreachable variant @@ -32,167 +27,33 @@ pub enum Exception { } impl Exception { - /// Returns the kind of exception that's currently being serviced - pub fn current() -> Exception { - match unsafe { (*::peripheral::SCB.get()).icsr.read() } as u8 { - 0 => Exception::ThreadMode, - 2 => Exception::Nmi, + /// Returns the type of the exception that's currently active + /// + /// Returns `None` if no exception is currently active + pub fn active() -> Option { + // NOTE(safe) atomic read + let icsr = unsafe { (*::peripheral::SCB.get()).icsr.read() }; + + Some(match icsr as u8 { + 0 => return None, + 2 => Exception::NMI, 3 => Exception::HardFault, - 4 => Exception::MemoryManagementFault, + 4 => Exception::MenManage, 5 => Exception::BusFault, 6 => Exception::UsageFault, 11 => Exception::SVCall, 14 => Exception::PendSV, - 15 => Exception::Systick, + 15 => Exception::SysTick, n if n >= 16 => Exception::Interrupt(n - 16), _ => Exception::Reserved, - } - } -} - -/// Exception handlers -#[repr(C)] -pub struct Handlers { - /// Non-maskable interrupt - pub nmi: extern "C" fn(Nmi), - /// All class of fault - pub hard_fault: extern "C" fn(HardFault), - /// Memory management - pub mem_manage: extern "C" fn(MemManage), - /// Pre-fetch fault, memory access fault - pub bus_fault: extern "C" fn(BusFault), - /// Undefined instruction or illegal state - pub usage_fault: extern "C" fn(UsageFault), - /// Reserved spots in the vector table - pub _reserved0: [Reserved; 4], - /// System service call via SWI instruction - pub svcall: extern "C" fn(Svcall), - /// Reserved spots in the vector table - pub _reserved1: [Reserved; 2], - /// Pendable request for system service - pub pendsv: extern "C" fn(Pendsv), - /// System tick timer - pub sys_tick: extern "C" fn(SysTick), -} - -/// Non-maskable interrupt -pub struct Nmi { - _0: (), -} - -/// All class of fault -pub struct HardFault { - _0: (), -} - -/// Memory management -pub struct MemManage { - _0: (), -} - -/// Pre-fetch fault, memory access fault -pub struct BusFault { - _0: (), -} - -/// Undefined instruction or illegal state -pub struct UsageFault { - _0: (), -} - -/// System service call via SWI instruction -pub struct Svcall { - _0: (), -} - -/// Pendable request for system service -pub struct Pendsv { - _0: (), -} - -/// System tick timer -pub struct SysTick { - _0: (), -} - -unsafe impl Context for Nmi {} - -unsafe impl Context for HardFault {} - -unsafe impl Context for MemManage {} - -unsafe impl Context for BusFault {} - -unsafe impl Context for UsageFault {} - -unsafe impl Context for Svcall {} - -unsafe impl Context for Pendsv {} - -unsafe impl Context for SysTick {} - -/// Default exception handlers -pub const DEFAULT_HANDLERS: Handlers = Handlers { - _reserved0: [Reserved::Vector; 4], - _reserved1: [Reserved::Vector; 2], - bus_fault: default_handler, - hard_fault: default_handler, - mem_manage: default_handler, - nmi: default_handler, - pendsv: default_handler, - svcall: default_handler, - sys_tick: default_handler, - usage_fault: default_handler, -}; - -/// The default exception handler -/// -/// This handler triggers a breakpoint (`bkpt`) and gives you access, within a -/// GDB session, to the stack frame (`_sf`) where the exception occurred. -// This needs asm!, #[naked] and unreachable() to avoid modifying the stack -// pointer (MSP), that way it points to the stacked registers -#[naked] -pub extern "C" fn default_handler(_token: T) -where - T: Context, -{ - // This is the actual exception handler. `_sr` is a pointer to the previous - // stack frame - #[cfg(target_arch = "arm")] - extern "C" fn handler(_sr: &StackedRegisters) -> ! { - // What exception is currently being serviced - let _e = Exception::current(); - - ::asm::bkpt(); - - loop {} - } - - match () { - #[cfg(target_arch = "arm")] - () => { - unsafe { - // "trampoline" to get to the real exception handler. - asm!("mrs r0, MSP - ldr r1, [r0, #20] - b $0" - : - : "i"(handler as extern "C" fn(&StackedRegisters) -> !) - : - : "volatile"); - - ::core::intrinsics::unreachable() - } - } - #[cfg(not(target_arch = "arm"))] - () => {} + }) } } -/// Registers stacked during an exception +/// Registers stacked (pushed into the stack) during an exception #[derive(Clone, Copy, Debug)] #[repr(C)] -pub struct StackedRegisters { +pub struct ExceptionFrame { /// (General purpose) Register 0 pub r0: u32, /// (General purpose) Register 1 diff --git a/src/interrupt.rs b/src/interrupt.rs index a6abcdd9..2e43e85b 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -45,7 +45,7 @@ pub fn disable() { asm!("cpsid i" : : - : + : "memory" : "volatile"); }, #[cfg(not(target_arch = "arm"))] @@ -66,7 +66,7 @@ pub unsafe fn enable() { asm!("cpsie i" : : - : + : "memory" : "volatile"); } #[cfg(not(target_arch = "arm"))] diff --git a/src/lib.rs b/src/lib.rs index 7e9c6fbe..e6f85e02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,12 +8,10 @@ //! - Data structures like the vector table //! - Safe wrappers around assembly instructions like `bkpt` -#![cfg_attr(target_arch = "arm", feature(core_intrinsics))] #![deny(missing_docs)] #![deny(warnings)] #![feature(asm)] #![feature(const_fn)] -#![feature(naked_functions)] #![no_std] extern crate aligned; @@ -25,17 +23,8 @@ mod macros; #[macro_use] pub mod asm; -pub mod ctxt; pub mod exception; pub mod interrupt; pub mod itm; pub mod peripheral; pub mod register; - -/// A reserved spot in the vector table -#[derive(Clone, Copy)] -#[repr(u32)] -pub enum Reserved { - /// Reserved - Vector = 0, -}