diff options
author | 2017-06-23 16:00:38 -0500 | |
---|---|---|
committer | 2017-06-30 18:08:28 -0500 | |
commit | 85f78eb40a432801e7f1a2633d8e825235b1aca4 (patch) | |
tree | 7f09496ebba91288c2a3bebe15fe8c4ccafc6277 | |
parent | 39875a3f538f64134bfe3b1619316bee4b3f39f3 (diff) | |
download | cortex-m-85f78eb40a432801e7f1a2633d8e825235b1aca4.tar.gz cortex-m-85f78eb40a432801e7f1a2633d8e825235b1aca4.tar.zst cortex-m-85f78eb40a432801e7f1a2633d8e825235b1aca4.zip |
revamp the exception registration mechanism
[breaking-change]
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/ctxt.rs | 160 | ||||
-rw-r--r-- | src/exception.rs | 301 | ||||
-rw-r--r-- | src/lib.rs | 12 |
4 files changed, 131 insertions, 344 deletions
@@ -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 727090b..0000000 --- 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<Cell<bool>, 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<Cell<bool>, 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>(_: 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<T, Ctxt> -where - Ctxt: Context, -{ - _ctxt: PhantomData<Ctxt>, - data: UnsafeCell<T>, -} - -impl<T, Ctxt> Local<T, Ctxt> -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<T, Ctxt> Sync for Local<T, Ctxt> -where - Ctxt: Context, -{ -} - -/// A token unique to a context -pub unsafe trait Context {} diff --git a/src/exception.rs b/src/exception.rs index 61b7415..ceb0f2a 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,29 +1,12 @@ //! Exceptions -use ctxt::Context; -use Reserved; +#![allow(non_camel_case_types)] /// Enumeration of all exceptions #[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. - HardFault, - /// Memory management. - MemoryManagementFault, - /// Pre-fetch fault, memory access fault. - BusFault, - /// Undefined instruction or illegal state. - UsageFault, - /// System service call via SWI instruction - SVCall, - /// Pendable request for system service - PendSV, - /// System tick timer - Systick, +pub enum Vector { + /// Fault or system exception + Exception(Exception), /// An interrupt Interrupt(u8), // Unreachable variant @@ -31,161 +14,27 @@ pub enum Exception { Reserved, } -impl Exception { +impl Vector { /// 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, - 3 => Exception::HardFault, - 4 => Exception::MemoryManagementFault, - 5 => Exception::BusFault, - 6 => Exception::UsageFault, - 11 => Exception::SVCall, - 14 => Exception::PendSV, - 15 => Exception::Systick, - n if n >= 16 => Exception::Interrupt(n - 16), - _ => Exception::Reserved, + pub fn active() -> Option<Vector> { + // NOTE(safe) atomic read + let icsr = unsafe { (*::peripheral::SCB.get()).icsr.read() }; + if icsr == 0 { + return None; } - } -} - -/// 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<T>(_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"))] - () => {} + Some(match icsr as u8 { + 2 => Vector::Exception(Exception::NMI), + 3 => Vector::Exception(Exception::HARD_FAULT), + 4 => Vector::Exception(Exception::MEN_MANAGE), + 5 => Vector::Exception(Exception::BUS_FAULT), + 6 => Vector::Exception(Exception::USAGE_FAULT), + 11 => Vector::Exception(Exception::SVCALL), + 14 => Vector::Exception(Exception::PENDSV), + 15 => Vector::Exception(Exception::SYS_TICK), + n if n >= 16 => Vector::Interrupt(n - 16), + _ => Vector::Reserved, + }) } } @@ -210,3 +59,109 @@ pub struct StackedRegisters { /// Program Status Register pub xpsr: u32, } + +#[macro_export] +macro_rules! default_handler { + ($f:ident, local: { + $($lvar:ident:$lty:ident = $lval:expr;)* + }) => { + #[allow(non_snake_case)] + mod DEFAULT_HANDLER { + pub struct Locals { + $( + pub $lvar: $lty, + )* + } + } + + #[allow(non_snake_case)] + #[no_mangle] + pub extern "C" fn DEFAULT_HANDLER() { + static mut LOCALS: self::DEFAULT_HANDLER::Locals = + self::DEFAULT_HANDLER::Locals { + $( + $lvar: $lval, + )* + }; + + // type checking + let f: fn(&mut self::DEFAULT_HANDLER::Locals) = $f; + f(unsafe { &mut LOCALS }); + } + }; + ($f:ident) => { + #[allow(non_snake_case)] + #[no_mangle] + pub extern "C" fn DEFAULT_HANDLER() { + // type checking + let f: fn() = $f; + f(); + } + } +} + +/// Fault and system exceptions +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Exception { + /// Non-maskable interrupt + NMI, + /// All class of fault. + HARD_FAULT, + /// Memory management. + MEN_MANAGE, + /// Pre-fetch fault, memory access fault. + BUS_FAULT, + /// Undefined instruction or illegal state. + USAGE_FAULT, + /// System service call via SWI instruction + SVCALL, + /// Pendable request for system service + PENDSV, + /// System tick timer + SYS_TICK, +} + +#[macro_export] +macro_rules! exception { + ($NAME:ident, $f:path, local: { + $($lvar:ident:$lty:ident = $lval:expr;)* + }) => { + #[allow(non_snake_case)] + mod $NAME { + pub struct Locals { + $( + pub $lvar: $lty, + )* + } + } + + #[allow(non_snake_case)] + #[no_mangle] + pub extern "C" fn $NAME() { + // check that the handler exists + let _ = $crate::exception::Exception::$NAME; + + static mut LOCALS: self::$NAME::Locals = self::$NAME::Locals { + $( + $lvar: $lval, + )* + }; + + // type checking + let f: fn(&mut self::$NAME::Locals) = $f; + f(unsafe { &mut LOCALS }); + } + }; + ($NAME:ident, $f:path) => { + #[allow(non_snake_case)] + #[no_mangle] + pub extern "C" fn $NAME() { + // check that the handler exists + let _ = $crate::exception::Exception::$NAME; + + // type checking + let f: fn() = $f; + f(); + } + } +} @@ -8,12 +8,13 @@ //! - 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(linkage)] #![feature(naked_functions)] +#![feature(used)] #![no_std] extern crate aligned; @@ -25,17 +26,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, -} |