aboutsummaryrefslogtreecommitdiff
path: root/cortex-m-rt/src/lib.rs
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <jorge@japaric.io> 2018-05-11 20:01:54 +0200
committerGravatar Jorge Aparicio <jorge@japaric.io> 2018-05-11 20:01:54 +0200
commit962d91c8be5abb71f5dbd54f6e82ace93fd69474 (patch)
tree82b314d2456ac5d6854f46aee4eda15556d96c83 /cortex-m-rt/src/lib.rs
parent6dd420ff082b4a9d9151e27d1edf724e416d712b (diff)
downloadcortex-m-962d91c8be5abb71f5dbd54f6e82ace93fd69474.tar.gz
cortex-m-962d91c8be5abb71f5dbd54f6e82ace93fd69474.tar.zst
cortex-m-962d91c8be5abb71f5dbd54f6e82ace93fd69474.zip
doc up, __reset -> Reset, main! -> entry!, weak syntax of `exception!` ..
- remove some variants from `Exception` when targeting ARMv6-M - future proof the ABI of `__EXCEPTIONS` by using a `union` instead of `Option<fn()>` (the `None` variant is not guaranteed to always be represented as the value `0`) - tweak exception names to match CMSIS and the names used in cortex-m v0.5.0 - add an opt-in "device" feature to opt into a device specific build (interrupts must be supplied by a different crate, or manually by the user). - drop the `interrupts!` macro. If you don't enable the "device" feature `cortex-m-rt` will bind all possible interrupts to the default exception handler.
Diffstat (limited to 'cortex-m-rt/src/lib.rs')
-rw-r--r--cortex-m-rt/src/lib.rs643
1 files changed, 550 insertions, 93 deletions
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs
index 23cb1d4..a5e3856 100644
--- a/cortex-m-rt/src/lib.rs
+++ b/cortex-m-rt/src/lib.rs
@@ -1,20 +1,330 @@
//! Minimal startup / runtime for Cortex-M microcontrollers
//!
-//! TODO
+//! This crate contains all the required parts to build a `no_std` application (binary crate) that
+//! targets a Cortex-M microcontroller.
+//!
+//! # Features
+//!
+//! This crates takes care of:
+//!
+//! - The memory layout of the program. In particular, it populates the vector table so the device
+//! can boot correctly, and properly dispatch exceptions and interrupts.
+//!
+//! - Initializing `static` variables before the user entry point.
+//!
+//! - Enabling the FPU before the user entry point if the target is `thumbv7em-none-eabihf`.
+//!
+//! This crate also provides a mechanism to set exception handlers: see the [`exception!`] macro.
+//!
+//! [`exception!`]: macro.exception.html
+//!
+//! # `memory.x`
+//!
+//! This crate expects the user, or some other crate, to provide the memory layout of the target
+//! device via a linker named `memory.x`. This section covers the contents of `memory.x`
+//!
+//! ## `MEMORY`
+//!
+//! The linker script must specify the memory available in the device as, at least, two `MEMORY`
+//! regions: one named `FLASH` and one named `RAM`. The `.text` and `.rodata` sections of the
+//! program will be placed in the `FLASH` region, whereas the `.bss` and `.data` sections, as well
+//! as the heap,will be placed in the `RAM` region.
+//!
+//! ``` text
+//! /* Linker script for the STM32F103C8T6 */
+//! MEMORY
+//! {
+//! FLASH : ORIGIN = 0x08000000, LENGTH = 64K
+//! RAM : ORIGIN = 0x20000000, LENGTH = 20K
+//! }
+//! ```
+//!
+//! ## `_stack_start`
+//!
+//! This optional symbol can be used to indicate where the call stack of the program should be
+//! placed. If this symbol is not used then the stack will be placed at the *end* of the `RAM`
+//! region -- the stack grows downwards towards smaller address. This symbol can be used to place
+//! the stack in a different memory region, for example:
+//!
+//! ``` text
+//! /* Linker script for the STM32F303VCT6 */
+//! MEMORY
+//! {
+//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K
+//!
+//! /* .bss, .data and the heap go in this region */
+//! RAM : ORIGIN = 0x20000000, LENGTH = 40K
+//!
+//! /* Core coupled (faster) RAM dedicated to hold the stack */
+//! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
+//! }
+//!
+//! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
+//! ```
+//!
+//! ## `_stext`
+//!
+//! This optional symbol can be used to control where the `.text` section is placed. If omitted the
+//! `.text` section will be placed right after the vector table (which is placed at the beginning of
+//! `FLASH`). Some devices store settings like Flash configuration right after the vector table;
+//! for these devices one must place the `.text` section after this configuration section --
+//! `_stext` can be used for this purpose.
+//!
+//! ``` text
+//! MEMORY
+//! {
+//! /* .. */
+//! }
+//!
+//! /* The device stores Flash configuration in 0x400-0x40C so we place .text after that */
+//! _stext = ORIGIN(FLASH) + 0x40C
+//! ```
//!
//! # Example
//!
-//! # User interface
+//! This section presents a minimal application built on top of `cortex-m-rt`. Apart from the
+//! mandatory `memory.x` linker script describing the memory layout of the device, the hard fault
+//! handler and the default exception handler must also be defined somewhere in the dependency
+//! graph (cf. [`exception!`]). In this example we define them in the binary crate:
+//!
+//! ``` ignore
+//! // IMPORTANT the standard `main` interface is not used because it requires nightly
+//! #![no_main]
+//! #![no_std]
+//!
+//! #[macro_use(entry, exception)]
+//! extern crate cortex_m_rt as rt;
+//!
+//! // makes `panic!` print messages to the host stderr using semihosting
+//! extern crate panic_semihosting;
+//!
+//! use rt::ExceptionFrame;
+//!
+//! // use `main` as the entry point of this application
+//! entry!(main);
+//!
+//! // `main` is not allowed to return
+//! fn main() -> ! {
+//! // initialization
+//!
+//! loop {
+//! // application logic
+//! }
+//! }
+//!
+//! // define the hard fault handler
+//! exception!(HardFault, hard_fault);
+//!
+//! fn hard_fault(ef: &ExceptionFrame) -> ! {
+//! panic!("{:#?}", ef);
+//! }
+//!
+//! // define the default exception handler
+//! exception!(*, default_handler);
+//!
+//! fn default_handler(irqn: i16) {
+//! panic!("unhandled exception (IRQn={})", irqn);
+//! }
+//! ```
+//!
+//! To actually build this program you need to place a `memory.x` linker script somewhere the linker
+//! can find it, e.g. in the current directory; and then link the program using `cortex-m-rt`'s
+//! linker script: `link.x`. The required steps are shown below:
+//!
+//! ``` text
+//! $ cat > memory.x <<EOF
+//! /* Linker script for the STM32F103C8T6 */
+//! MEMORY
+//! {
+//! FLASH : ORIGIN = 0x08000000, LENGTH = 64K
+//! RAM : ORIGIN = 0x20000000, LENGTH = 20K
+//! }
+//! EOF
+//!
+//! $ cargo rustc --target thumbv7m-none-eabi -- \
+//! -C link-arg=-nostartfiles -C link-arg=-Tlink.x
+//!
+//! $ file target/thumbv7m-none-eabi/debug/app
+//! app: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, (..)
+//! ```
+//!
+//! # Optional features
+//!
+//! ## `device`
+//!
+//! If this feature is disabled then this crate populates the whole vector table. All the interrupts
+//! in the vector table, even the ones unused by the target device, will be bound to the default
+//! exception handler. This makes the final application device agnostic: you will be able to run it
+//! on any Cortex-M device -- provided that you correctly specified its memory layout in `memory.x`
+//! -- without hitting undefined behavior.
+//!
+//! If this feature is enabled then the interrupts section of the vector table is left unpopulated
+//! and some other crate, or the user, will have to populate it. This mode is meant to be used in
+//! conjunction with crates generated using `svd2rust`. Those *device crates* will populate the
+//! missing part of the vector table when their `"rt"` feature is enabled.
+//!
+//! # Inspection
+//!
+//! This section covers how to inspect a binary that builds on top of `cortex-m-rt`.
+//!
+//! ## Sections (`size`)
+//!
+//! `cortex-m-rt` uses standard sections like `.text`, `.rodata`, `.bss` and `.data` as one would
+//! expect. `cortex-m-rt` separates the vector table in its own section, named `.vector_table`. This
+//! lets you distinguish how much space is taking the vector table in Flash vs how much is being
+//! used by actual instructions (`.text`) and constants (`.rodata`).
+//!
+//! ```
+//! $ size -Ax target/thumbv7m-none-eabi/examples/app
+//! target/thumbv7m-none-eabi/release/examples/app :
+//! section size addr
+//! .vector_table 0x400 0x8000000
+//! .text 0x88 0x8000400
+//! .rodata 0x0 0x8000488
+//! .data 0x0 0x20000000
+//! .bss 0x0 0x20000000
+//! ```
+//!
+//! Without the `-A` argument `size` reports the sum of the sizes of `.text`, `.rodata` and
+//! `.vector_table` under "text".
+//!
+//! ```
+//! $ size target/thumbv7m-none-eabi/examples/app
+//! text data bss dec hex filename
+//! 1160 0 0 1660 67c target/thumbv7m-none-eabi/release/app
+//! ```
+//!
+//! ## Symbols (`objdump`, `nm`)
+//!
+//! One will always find the following (unmangled) symbols in `cortex-m-rt` applications:
+//!
+//! - `Reset`. This is the reset handler. The microcontroller will executed this function upon
+//! booting. This function will call the user program entry point (cf. [`entry!`]) using the `main`
+//! symbol so you may also find that symbol in your program; if you do, `main` will contain your
+//! application code. Some other times `main` gets inlined into `Reset` so you won't find it.
+//!
+//! [`entry!`]: macro.entry.html
+//!
+//! - `DefaultHandler`. This is the default handler. This function will contain, or call, the
+//! function you declared in the second argument of `exception!(*, ..)`.
+//!
+//! - `HardFault`. This is the hard fault handler. This function is simply a trampoline that jumps
+//! into the user defined hard fault handler: `UserHardFault`. The trampoline is required to set up
+//! the pointer to the stacked exception frame.
+//!
+//! - `UserHardFault`. This is the user defined hard fault handler. This function will contain, or
+//! call, the function you declared in the second argument of `exception!(HardFault, ..)`
+//!
+//! If you override any exception handler you'll find it as an unmangled symbol, e.g. `SysTick` or
+//! `SVCall`, in the output of `objdump`,
+//!
+//! # Incorporating device specific interrupts
+//!
+//! This section covers how an external crate can insert device specific interrupt handlers into the
+//! vector table. Most users don't need to concern themselves with these details, but if you are
+//! interested in how device crates generated using `svd2rust` integrate with `cortex-m-rt` read on.
+//!
+//! The information in this section applies when the `"device"` feature has been enabled.
+//!
+//! ## `__INTERRUPTS`
+//!
+//! The external crate must provide the interrupts portion of the vector table via a `static`
+//! variable named`__INTERRUPTS` (unmangled) that must be placed in the `.vector_table.interrupts`
+//! section of its object file.
+//!
+//! This `static` variable will be placed at `ORIGIN(FLASH) + 0x40`. This address corresponds to the
+//! spot where IRQ0 (IRQ number 0) is located.
+//!
+//! To conform to the Cortex-M ABI `__INTERRUPTS` must be an array of function pointers; some spots
+//! in this array may need to be set to 0 if they are marked as *reserved* in the data sheet /
+//! reference manual. We recommend using a `union` to set the reserved spots to `0`; `None`
+//! (`Option<fn()>`) may also work but it's not guaranteed that the `None` variant will *always* be
+//! represented by the value `0`.
+//!
+//! Let's illustrate with an artificial example where a device only has two interrupt: `Foo`, with
+//! IRQ number = 2, and `Bar`, with IRQ number = 4.
+//!
+//! ``` ignore
+//! union Vector {
+//! handler: extern "C" fn(),
+//! reserved: usize,
+//! }
+//!
+//! extern "C" {
+//! fn Foo();
+//! fn Bar();
+//! }
+//!
+//! #[link_section = ".vector_table.interrupts"]
+//! #[no_mangle]
+//! pub static __INTERRUPTS: [Vector; 5] = [
+//! // 0-1: Reserved
+//! Vector { reserved: 0 },
+//! Vector { reserved: 0 },
+//!
+//! // 2: Foo
+//! Vector { handler: Foo },
+//!
+//! // 3: Reserved
+//! Vector { reserved: 0 },
+//!
+//! // 4: Bar
+//! Vector { handler: Bar },
+//! ];
+//! ```
+//!
+//! ## `device.x`
+//!
+//! Linking in `__INTERRUPTS` creates a bunch of undefined references. If the user doesn't set a
+//! handler for *all* the device specific interrupts then linking will fail with `"undefined
+//! reference"` errors.
+//!
+//! We want to provide a default handler for all the interrupts while still letting the user
+//! individually override each interrupt handler. In C projects, this is usually accomplished using
+//! weak aliases declared in external assembly files. In Rust, we could achieve something similar
+//! using `global_asm!`, but that's an unstable feature.
+//!
+//! A solution that doesn't require `global_asm!` or external assembly files is to use the `PROVIDE`
+//! command in a linker script to create the weak aliases. This is the approach that `cortex-m-rt`
+//! uses; when the `"device"` feature is enabled `cortex-m-rt`'s linker script (`link.x`) depends on
+//! a linker script named `device.x`. The crate that provides `__INTERRUPTS` must also provide this
+//! file.
+//!
+//! For our running example the `device.x` linker script looks like this:
+//!
+//! ``` text
+//! /* device.x */
+//! PROVIDE(Foo = DefaultHandler);
+//! PROVIDE(Bar = DefaultHandler);
+//! ```
//!
-//! ## `memory.x`
+//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler that
+//! the user provides via `exception!(*, ..)` and that the core exceptions use unless overridden.
//!
-//! ## `interrupts.x`
+//! Because this linker script is provided by a dependency of the final application the dependency
+//! must contain build script that puts `device.x` somewhere the linker can find. An example of such
+//! build script is shown below:
//!
-//! # Diagnostics
+//! ``` ignore
+//! use std::env;
+//! use std::fs::File;
+//! use std::io::Write;
+//! use std::path::PathBuf;
+//!
+//! fn main() {
+//! // Put the linker script somewhere the linker can find it
+//! let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+//! File::create(out.join("device.x"))
+//! .unwrap()
+//! .write_all(include_bytes!("device.x"))
+//! .unwrap();
+//! println!("cargo:rustc-link-search={}", out.display());
+//! }
+//! ```
// # Developer notes
//
-// - `link_section` is used to place symbols in specific places if the final binary. The names used
+// - `link_section` is used to place symbols in specific places of the final binary. The names used
// here will appear in the linker script (`link.x`) in conjunction with the `KEEP` command.
#![deny(missing_docs)]
@@ -62,13 +372,13 @@ pub fn heap_start() -> *mut u32 {
#[doc(hidden)]
#[link_section = ".vector_table.reset_vector"]
#[no_mangle]
-pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = __reset;
+pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
#[doc(hidden)]
#[no_mangle]
-pub unsafe extern "C" fn __reset() -> ! {
+pub unsafe extern "C" fn Reset() -> ! {
extern "C" {
- // This symbol will be provided by the user via the `main!` macro
+ // This symbol will be provided by the user via the `entry!` macro
fn main() -> !;
// These symbols come from `link.x`
@@ -105,7 +415,7 @@ pub unsafe extern "C" fn __reset() -> ! {
// before enabling the FPU, and that would produce a hard to diagnose hard fault at
// runtime.
#[inline(never)]
- #[export_name = "__reset_trampoline"]
+ #[export_name = "ResetTrampoline"]
fn trampoline() -> ! {
unsafe { main() }
}
@@ -117,13 +427,15 @@ pub unsafe extern "C" fn __reset() -> ! {
/// Macro to define the user entry point of a program
///
-/// Usage: `main!(path::to::user::main)`
+/// Usage: `entry!($path::to::user::entry)`
+///
+/// The specified function will be called by the reset handler *after* RAM has been initialized. In
+/// the case of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the function
+/// is called.
///
-/// This function will be called by the reset handler *after* RAM has been initialized. In the case
-/// of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the user `main` is
-/// called.
+/// The signature of the specified function must be `fn() -> !` (never ending function)
#[macro_export]
-macro_rules! main {
+macro_rules! entry {
($path:path) => {
#[export_name = "main"]
pub extern "C" fn __impl_main() -> ! {
@@ -136,156 +448,301 @@ macro_rules! main {
}
/* Exceptions */
-// NOTE we purposefully go against Rust style here and use PascalCase for the handlers. The
-// rationale is in the definition of the `exception!` macro.
#[doc(hidden)]
pub enum Exception {
- NMI,
- MemManage,
+ NonMaskableInt,
+
+ // Not overridable
+ // HardFault,
+ #[cfg(not(armv6m))]
+ MemoryManagement,
+
+ #[cfg(not(armv6m))]
BusFault,
+
+ #[cfg(not(armv6m))]
UsageFault,
- SVC,
- DebugMon,
+
+ #[cfg(armv8m)]
+ SecureFault,
+
+ SVCall,
+
+ #[cfg(not(armv6m))]
+ DebugMonitor,
+
PendSV,
+
SysTick,
}
extern "C" {
- fn NMI();
+ fn NonMaskableInt();
+
fn HardFault();
- fn MemManage();
+
+ #[cfg(not(armv6m))]
+ fn MemoryManagement();
+
+ #[cfg(not(armv6m))]
fn BusFault();
+
+ #[cfg(not(armv6m))]
fn UsageFault();
- fn SVC();
+
+ #[cfg(armv8m)]
+ fn SecureFault();
+
+ fn SVCall();
+
#[cfg(not(armv6m))]
- fn DebugMon();
+ fn DebugMonitor();
+
fn PendSV();
+
fn SysTick();
}
#[doc(hidden)]
+pub union Vector {
+ handler: unsafe extern "C" fn(),
+ reserved: usize,
+}
+
+#[doc(hidden)]
#[link_section = ".vector_table.exceptions"]
#[no_mangle]
-pub static __EXCEPTIONS: [Option<unsafe extern "C" fn()>; 14] = [
- Some(NMI),
- Some(HardFault),
- Some(MemManage),
- Some(BusFault),
- Some(UsageFault),
- None,
- None,
- None,
- None,
- Some(SVC),
+pub static __EXCEPTIONS: [Vector; 14] = [
+ // Exception 2: Non Maskable Interrupt.
+ Vector {
+ handler: NonMaskableInt,
+ },
+ // Exception 3: Hard Fault Interrupt.
+ Vector { handler: HardFault },
+ // Exception 4: Memory Management Interrupt [not on Cortex-M0 variants].
+ #[cfg(not(armv6m))]
+ Vector {
+ handler: MemoryManagement,
+ },
+ #[cfg(armv6m)]
+ Vector { reserved: 0 },
+ // Exception 5: Bus Fault Interrupt [not on Cortex-M0 variants].
+ #[cfg(not(armv6m))]
+ Vector { handler: BusFault },
+ #[cfg(armv6m)]
+ Vector { reserved: 0 },
+ // Exception 6: Usage Fault Interrupt [not on Cortex-M0 variants].
+ #[cfg(not(armv6m))]
+ Vector {
+ handler: UsageFault,
+ },
#[cfg(armv6m)]
- None,
+ Vector { reserved: 0 },
+ // Exception 7: Secure Fault Interrupt [only on Armv8-M].
+ #[cfg(armv8m)]
+ Vector {
+ handler: SecureFault,
+ },
+ #[cfg(not(armv8m))]
+ Vector { reserved: 0 },
+ // 8-10: Reserved
+ Vector { reserved: 0 },
+ Vector { reserved: 0 },
+ Vector { reserved: 0 },
+ // Exception 11: SV Call Interrupt.
+ Vector { handler: SVCall },
+ // Exception 12: Debug Monitor Interrupt [not on Cortex-M0 variants].
#[cfg(not(armv6m))]
- Some(DebugMon),
- None,
- Some(PendSV),
- Some(SysTick),
+ Vector {
+ handler: DebugMonitor,
+ },
+ #[cfg(armv6m)]
+ Vector { reserved: 0 },
+ // 13: Reserved
+ Vector { reserved: 0 },
+ // Exception 14: Pend SV Interrupt [not on Cortex-M0 variants].
+ #[cfg(not(armv6m))]
+ Vector { handler: PendSV },
+ #[cfg(armv6m)]
+ Vector { reserved: 0 },
+ // Exception 15: System Tick Interrupt.
+ Vector { handler: SysTick },
];
-/// Macro to override an exception handler
+// If we are not targeting a specific device we bind all the potential device specific interrupts
+// to the default handler
+#[cfg(not(feature = "device"))]
+#[doc(hidden)]
+#[link_section = ".vector_table.interrupts"]
+#[no_mangle]
+pub static __INTERRUPTS: [unsafe extern "C" fn(); 240] = [{
+ extern "C" {
+ fn DefaultHandler();
+ }
+
+ DefaultHandler
+}; 240];
+
+/// Macro to set or override a processor core exception handler
+///
+/// # Syntax
+///
+/// ``` ignore
+/// exception!(
+/// // Name of the exception
+/// $Name:ident,
+///
+/// // Path to the exception handler (a function)
+/// $handler:path,
+///
+/// // Optional, state preserved across invocations of the handler
+/// state: $State:ty = $initial_state:expr,
+/// );
+/// ```
+///
+/// `$Name` can be one of:
+///
+/// - `*`
+/// - `NonMaskableInt`
+/// - `HardFault`
+/// - `MemoryManagement` (a)
+/// - `BusFault` (a)
+/// - `UsageFault` (a)
+/// - `SecureFault` (b)
+/// - `SVCall`
+/// - `DebugMonitor` (a)
+/// - `PendSV`
+/// - `SysTick`
+///
+/// (a) Not available on Cortex-M0 variants (`thumbv6m-none-eabi`)
+///
+/// (b) Only available on ARMv8-M
+///
+/// `exception!(HardFault, ..)` sets the hard fault handler. The handler must have signature
+/// `fn(&ExceptionFrame) -> !`. This handler is not allowed to return as that can cause undefined
+/// behavior. It's mandatory to set the `HardFault` handler somewhere in the dependency graph of an
+/// application.
+///
+/// `exception!(*, ..)` sets the *default* handler. All exceptions which have not been assigned a
+/// handler will be serviced by this handler. This handler must have signature `fn(irqn: i16)`.
+/// `irqn` is the IRQ number (cf. CMSIS); `irqn` will be a negative number when the handler is
+/// servicing a core exception; `irqn` will be a positive number when the handler is servicing a
+/// device specific exception (interrupt). It's mandatory to set the default handler somewhere
+/// in the dependency graph of an application.
+///
+/// `exception!($Exception, ..)` overrides the default handler for `$Exception`. All exceptions,
+/// except for `HardFault`, can be assigned some `$State`.
+///
+/// # Examples
+///
+/// Setting the `HardFault` handler
+///
+/// ```
+/// #[macro_use(exception)]
+/// extern crate cortex_m_rt as rt;
///
-/// Usage: `exception!(ExceptionName, path::to::handler)`
+/// use rt::ExceptionFrame;
///
-/// All exceptions are serviced by the `DefaultHandler` unless overridden using this macro.
-/// `ExceptionName` can be one of:
+/// exception!(HardFault, hard_fault);
///
-/// - `DefaultHandler` (a) -- `fn(u8)` -- the argument is the exception number (VECTACTIVE)
-/// - `NMI` -- `fn()`
-/// - `HardFault` -- `fn(&ExceptionFrame) -> !`
-/// - `MemManage` -- `fn()`
-/// - `BusFault` -- `fn()`
-/// - `UsageFault` -- `fn()`
-/// - `SVC` -- `fn()`
-/// - `DebugMon` (b) -- `fn()`
-/// - `PendSV` -- `fn()`
-/// - `SysTick` -- `fn()`
+/// fn hard_fault(ef: &ExceptionFrame) -> ! {
+/// // prints the exception frame as a panic message
+/// panic!("{:#?}", ef);
+/// }
///
-/// (a) Note that `DefaultHandler` is left undefined and *must* be defined by the user somewhere
-/// using this macro.
+/// # fn main() {}
+/// ```
///
-/// (b) Not available on ARMv6-M
+/// Setting the default handler
+///
+/// ```
+/// #[macro_use(exception)]
+/// extern crate cortex_m_rt as rt;
+///
+/// exception!(*, default_handler);
+///
+/// fn default_handler(irqn: i16) {
+/// println!("IRQn = {}", irqn);
+/// }
+///
+/// # fn main() {}
+/// ```
+///
+/// Overriding the `SysTick` handler
+///
+/// ```
+/// #[macro_use(exception)]
+/// extern crate cortex_m_rt as rt;
+///
+/// exception!(SysTick, sys_tick, state: u32 = 0);
+///
+/// fn sys_tick(count: &mut u32) {
+/// println!("count = {}", *count);
+///
+/// *count += 1;
+/// }
+///
+/// # fn main() {}
+/// ```
#[macro_export]
macro_rules! exception {
- (DefaultHandler, $path:path) => {
+ (* , $handler:path) => {
#[allow(unsafe_code)]
#[allow(non_snake_case)]
#[export_name = "DefaultHandler"]
pub unsafe extern "C" fn __impl_DefaultHandler() {
+ extern crate core;
+
// validate the signature of the user provided handler
- let f: fn(u8) = $path;
+ let f: fn(i16) = $handler;
const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
// NOTE not volatile so the compiler can opt the load operation away if the value is
// unused
- f(core::ptr::read(SCB_ICSR) as u8)
+ f(core::ptr::read(SCB_ICSR) as i16 - 16)
}
};
- (HardFault, $path:path) => {
+ (HardFault, $handler:path) => {
#[allow(unsafe_code)]
#[allow(non_snake_case)]
#[export_name = "UserHardFault"]
pub unsafe extern "C" fn __impl_UserHardFault(ef: &$crate::ExceptionFrame) {
// validate the signature of the user provided handler
- let f: fn(&$crate::ExceptionFrame) -> ! = $path;
+ let f: fn(&$crate::ExceptionFrame) -> ! = $handler;
f(ef)
}
};
- // NOTE Unfortunately, this will end up leaking `$exception` into the function call namespace.
- // But the damage is somewhat reduced by having `$exception` not being a `snake_case` function.
- ($ExceptionName:ident, $path:path, state: $State:ty = $init:expr) => {
+ ($Name:ident, $handler:path,state: $State:ty = $initial_state:expr) => {
#[allow(unsafe_code)]
#[no_mangle]
- pub unsafe extern "C" fn $ExceptionName() {
- static mut STATE: $State = $init;
+ pub unsafe extern "C" fn $Name() {
+ static mut STATE: $State = $initial_state;
// check that this exception exists
- let _ = $crate::Exception::$ExceptionName;
+ let _ = $crate::Exception::$Name;
// validate the signature of the user provided handler
- let f: fn(&mut $State) = $path;
+ let f: fn(&mut $State) = $handler;
f(&mut STATE)
}
};
- ($ExceptionName:ident, $path:path) => {
+ ($Name:ident, $handler:path) => {
#[allow(unsafe_code)]
#[no_mangle]
- pub unsafe extern "C" fn $ExceptionName() {
+ pub unsafe extern "C" fn $Name() {
// check that this exception exists
- let _ = $crate::Exception::$ExceptionName;
+ let _ = $crate::Exception::$Name;
// validate the signature of the user provided handler
- let f: fn() = $path;
+ let f: fn() = $handler;
f()
}
};
}
-
-/// Macro to bind all the 240 interrupt handlers to the `DefaultHandler` exception handler.
-///
-/// The use case for this macro is writing device agnostic programs
-#[macro_export]
-macro_rules! interrupts {
- (DefaultHandler) => {
- #[doc(hidden)]
- #[link_section = ".vector_table.interrupts"]
- #[no_mangle]
- pub static __INTERRUPTS: [Option<unsafe extern "C" fn()>; 240] = [{
- extern "C" {
- fn DefaultHandler();
- }
-
- Some(DefaultHandler)
- }; 240];
- };
-}