aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <jorge@japaric.io> 2017-06-23 16:00:38 -0500
committerGravatar Jorge Aparicio <jorge@japaric.io> 2017-06-30 18:08:28 -0500
commit85f78eb40a432801e7f1a2633d8e825235b1aca4 (patch)
tree7f09496ebba91288c2a3bebe15fe8c4ccafc6277
parent39875a3f538f64134bfe3b1619316bee4b3f39f3 (diff)
downloadcortex-m-85f78eb40a432801e7f1a2633d8e825235b1aca4.tar.gz
cortex-m-85f78eb40a432801e7f1a2633d8e825235b1aca4.tar.zst
cortex-m-85f78eb40a432801e7f1a2633d8e825235b1aca4.zip
revamp the exception registration mechanism
[breaking-change]
-rw-r--r--Cargo.toml2
-rw-r--r--src/ctxt.rs160
-rw-r--r--src/exception.rs301
-rw-r--r--src/lib.rs12
4 files changed, 131 insertions, 344 deletions
diff --git a/Cargo.toml b/Cargo.toml
index f52e721..49d6174 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 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();
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 7e9c6fb..9cbedb4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,
-}