diff options
Diffstat (limited to 'cortex-m-rt/src/lib.rs')
-rw-r--r-- | cortex-m-rt/src/lib.rs | 1131 |
1 files changed, 685 insertions, 446 deletions
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 7028cce..eaefbbc 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -1,615 +1,854 @@ //! Minimal startup / runtime for Cortex-M microcontrollers //! +//! This crate contains all the required parts to build a `no_std` application (binary crate) that +//! targets a Cortex-M microcontroller. +//! //! # Features //! -//! This crate provides +//! 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 program entry point. +//! +//! - Enabling the FPU before the program entry point if the target is `thumbv7em-none-eabihf`. //! -//! - Before main initialization of the `.bss` and `.data` sections. +//! This crate also provides a mechanism to set exception handlers: see the [`exception!`] macro. //! -//! - Before main initialization of the FPU (for targets that have a FPU). +//! [`exception!`]: macro.exception.html //! -//! - A minimal `start` lang item to support the standard `fn main()` -//! interface. (The processor goes to sleep (`loop { asm!("wfi") }`) after -//! returning from `main`) +//! # Requirements //! -//! - A linker script that encodes the memory layout of a generic Cortex-M -//! microcontroller. This linker script is missing some information that must -//! be supplied through a `memory.x` file (see example below). +//! ## `arm-none-eabi-gcc` //! -//! - A default exception handler tailored for debugging that lets you inspect -//! what was the state of the processor at the time of the exception. By -//! default, all exceptions are serviced by this handler but each exception -//! can be individually overridden using the -//! [`exception!`](macro.exception.html) macro. The default exception handler -//! itself can also be overridden using the -//! [`default_handler!`](macro.default_handler.html) macro. +//! This crate requires `arm-none-eabi-gcc` to be installed and available in `$PATH`. //! -//! - A `_sheap` symbol at whose address you can locate a heap. +//! ## `memory.x` +//! +//! This crate expects the user, or some other crate, to provide the memory layout of the target +//! device via a linker script named `memory.x`. This section covers the contents of `memory.x` //! -//! # Example +//! ### `MEMORY` //! -//! Creating a new bare metal project. (I recommend you use the -//! [`cortex-m-quickstart`](https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/) template -//! as it takes of all the boilerplate shown here) +//! 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 -//! $ cargo new --bin app && cd $_ +//! /* Linker script for the STM32F103C8T6 */ +//! MEMORY +//! { +//! FLASH : ORIGIN = 0x08000000, LENGTH = 64K +//! RAM : ORIGIN = 0x20000000, LENGTH = 20K +//! } +//! ``` //! -//! $ # add this crate as a dependency -//! $ cargo add cortex-m-rt --vers 0.4.0 +//! ### `_stack_start` //! -//! $ # select a panicking behavior (look for the panic-impl keyword on crates.io) -//! $ cargo add panic-abort +//! 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: //! -//! $ # memory layout of the device -//! $ $EDITOR memory.x && cat $_ +//! ``` text +//! /* Linker script for the STM32F303VCT6 */ //! MEMORY //! { -//! /* NOTE K = KiBi = 1024 bytes */ -//! FLASH : ORIGIN = 0x08000000, LENGTH = 128K -//! RAM : ORIGIN = 0x20000000, LENGTH = 8K +//! 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 //! } //! -//! $ $EDITOR src/main.rs && cat $_ +//! _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 //! ``` //! -//! ``` ignore,no_run -//! #![feature(used)] +//! # An example +//! +//! 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] //! -//! extern crate cortex_m_rt; -//! extern crate panic_abort; // panicking behavior +//! #[macro_use(entry, exception)] +//! extern crate cortex_m_rt as rt; //! -//! fn main() { -//! // do something here +//! // 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 +//! } //! } //! -//! // As we are not using interrupts, we just register a dummy catch all -//! // handler -//! #[link_section = ".vector_table.interrupts"] -//! #[used] -//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; +//! // define the hard fault handler +//! exception!(HardFault, hard_fault); //! -//! extern "C" fn default_handler() { -//! loop {} +//! 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 -//! $ rustup target add thumbv7m-none-eabi +//! $ 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=-Tlink.x -C linker=arm-none-eabi-ld -Z linker-flavor=ld +//! -C link-arg=-nostartfiles -C link-arg=-Tlink.x //! -//! $ arm-none-eabi-objdump -Cd $(find target -name app) | head +//! $ file target/thumbv7m-none-eabi/debug/app +//! app: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, (..) +//! ``` //! -//! Disassembly of section .text: +//! # Optional features //! -//! 08000400 <cortex_m_rt::reset_handler>: -//! 8000400: b580 push {r7, lr} -//! 8000402: 466f mov r7, sp -//! 8000404: b084 sub sp, #8 +//! ## `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. //! -//! $ arm-none-eabi-size -Ax $(find target -name app) | head -//! target/thumbv7m-none-eabi/debug/app : -//! section size addr -//! .vector_table 0x400 0x8000000 -//! .text 0x24a 0x8000400 -//! .rodata 0x0 0x800064c -//! .bss 0x0 0x20000000 -//! .data 0x0 0x20000000 -//! ``` +//! 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. //! -//! # Symbol interfaces +//! # Inspection //! -//! This crate makes heavy use of symbols, linker sections and linker scripts to -//! provide most of its functionality. Below are described the main symbol -//! interfaces. +//! This section covers how to inspect a binary that builds on top of `cortex-m-rt`. //! -//! ## `DEFAULT_HANDLER` +//! ## Sections (`size`) //! -//! This weak symbol can be overridden to override the default exception handler -//! that this crate provides. It's recommended that you use the -//! `default_handler!` to do the override, but below is shown how to manually -//! override the symbol: +//! `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`). //! -//! ``` ignore,no_run -//! #[no_mangle] -//! pub extern "C" fn DEFAULT_HANDLER() { -//! // do something here -//! } +//! ``` +//! $ 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 //! ``` //! -//! ## `.vector_table.interrupts` +//! Without the `-A` argument `size` reports the sum of the sizes of `.text`, `.rodata` and +//! `.vector_table` under "text". //! -//! This linker section is used to register interrupt handlers in the vector -//! table. The recommended way to use this section is to populate it, once, with -//! an array of *weak* functions that just call the `DEFAULT_HANDLER` symbol. -//! Then the user can override them by name. +//! ``` +//! $ size target/thumbv7m-none-eabi/examples/app +//! text data bss dec hex filename +//! 1160 0 0 1660 67c target/thumbv7m-none-eabi/release/app +//! ``` //! -//! ### Example +//! ## Symbols (`objdump`, `nm`) //! -//! Populating the vector table +//! One will always find the following (unmangled) symbols in `cortex-m-rt` applications: //! -//! ``` ignore,no_run -//! // Number of interrupts the device has -//! const N: usize = 60; +//! - `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. //! -//! // Default interrupt handler that just calls the `DEFAULT_HANDLER` -//! #[linkage = "weak"] -//! #[naked] -//! #[no_mangle] -//! extern "C" fn WWDG() { -//! unsafe { -//! asm!("b DEFAULT_HANDLER" :::: "volatile"); -//! core::intrinsics::unreachable(); -//! } -//! } +//! [`entry!`]: macro.entry.html //! -//! // You need one function per interrupt handler -//! #[linkage = "weak"] -//! #[naked] -//! #[no_mangle] -//! extern "C" fn PVD() { -//! unsafe { -//! asm!("b DEFAULT_HANDLER" :::: "volatile"); -//! core::intrinsics::unreachable(); -//! } -//! } +//! - `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. //! -//! // Use `None` for reserved spots in the vector table -//! #[link_section = ".vector_table.interrupts"] -//! #[no_mangle] -//! #[used] -//! static INTERRUPTS: [Option<extern "C" fn()>; N] = [ -//! Some(WWDG), -//! Some(PVD), -//! // .. -//! ]; -//! ``` +//! - `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, ..)` //! -//! Overriding an interrupt (this can be in a different crate) +//! - `__STACK_START`. This is the first entry in the `.vector_table` section. This symbol contains +//! the initial value of the stack pointer; this is where the stack will be located -- the stack +//! grows downwards towards smaller addresses. //! -//! ``` ignore,no_run -//! // the name must match the name of one of the weak functions used to -//! // populate the vector table. -//! #[no_mangle] -//! pub extern "C" fn WWDG() { -//! // do something here -//! } -//! ``` +//! - `__RESET_VECTOR`. This is the reset vector, a pointer into the `Reset` handler. This vector is +//! located in the `.vector_table` section after `__STACK_START`. //! -//! ## `memory.x` +//! - `__EXCEPTIONS`. This is the core exceptions portion of the vector table; it's an array of 14 +//! exception vectors, which includes exceptions like `HardFault` and `SysTick`. This array is +//! located after `__RESET_VECTOR` in the `.vector_table` section. //! -//! This file supplies the information about the device to the linker. +//! - `__EXCEPTIONS`. This is the device specific interrupt portion of the vector table; its exact +//! size depends on the target device but if the `"device"` feature has not been enabled it will +//! have a size of 32 vectors (on ARMv6-M) or 240 vectors (on ARMv7-M). This array is located after +//! `__EXCEPTIONS` in the `.vector_table` section. //! -//! ### `MEMORY` +//! If you override any exception handler you'll find it as an unmangled symbol, e.g. `SysTick` or +//! `SVCall`, in the output of `objdump`, //! -//! The main information that this file must provide is the memory layout of -//! the device in the form of the `MEMORY` command. The command is documented -//! [here](https://sourceware.org/binutils/docs/ld/MEMORY.html), but at a minimum you'll want to -//! create two memory regions: one for Flash memory and another for RAM. +//! If you are targeting the `thumbv7em-none-eabihf` target you'll also see a `ResetTrampoline` +//! symbol in the output. To avoid the compiler placing FPU instructions before the FPU has been +//! enabled (cf. `vpush`) `Reset` calls the function `ResetTrampoline` which is marked as +//! `#[inline(never)]` and `ResetTrampoline` calls `main`. The compiler is free to inline `main` +//! into `ResetTrampoline` but it can't inline `ResetTrampoline` into `Reset` -- the FPU is enabled +//! in `Reset`. //! -//! The program instructions (the `.text` section) will be stored in the memory -//! region named FLASH, and the program `static` variables (the sections `.bss` -//! and `.data`) will be allocated in the memory region named RAM. +//! # Advanced usage //! -//! ### `_stack_start` +//! ## Setting the program entry point //! -//! This symbol provides the address at which the call stack will be allocated. -//! The call stack grows downwards so this address is usually set to the highest -//! valid RAM address plus one (this *is* an invalid address but the processor -//! will decrement the stack pointer *before* using its value as an address). +//! This section describes how `entry!` is implemented. This information is useful to developers who +//! want to provide an alternative to `entry!` that provides extra guarantees. //! -//! If omitted this symbol value will default to `ORIGIN(RAM) + LENGTH(RAM)`. +//! The `Reset` handler will call a symbol named `main` (unmangled) *after* initializing `.bss` and +//! `.data`, and enabling the FPU (if the target is `thumbv7em-none-eabihf`). `entry!` provides this +//! symbol in its expansion: //! -//! #### Example +//! ``` ignore +//! entry!(path::to::main); //! -//! Allocating the call stack on a different RAM region. +//! // expands into //! -//! ``` ignore -//! MEMORY -//! { -//! /* call stack will go here */ -//! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K -//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K -//! /* static variables will go here */ -//! RAM : ORIGIN = 0x20000000, LENGTH = 40K -//! } +//! #[export_name = "main"] +//! pub extern "C" fn __impl_main() -> ! { +//! // validate the signature of the program entry point +//! let f: fn() -> ! = path::to::main; //! -//! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); +//! f() +//! } //! ``` //! -//! ### `_stext` +//! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from +//! `Reset` will result in undefined behavior. //! -//! This symbol indicates where the `.text` section will be located. If not -//! specified in the `memory.x` file it will default to right after the vector -//! table -- the vector table is always located at the start of the FLASH -//! region. +//! ## Incorporating device specific interrupts //! -//! The main use of this symbol is leaving some space between the vector table -//! and the `.text` section unused. This is required on some microcontrollers -//! that store some configuration information right after the vector table. +//! 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. //! -//! #### Example +//! The information in this section applies when the `"device"` feature has been enabled. //! -//! Locate the `.text` section 1024 bytes after the start of the FLASH region. +//! ### `__INTERRUPTS` //! -//! ``` ignore -//! _stext = ORIGIN(FLASH) + 0x400; -//! ``` +//! 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. //! -//! ### `_sheap` +//! This `static` variable will be placed at `ORIGIN(FLASH) + 0x40`. This address corresponds to the +//! spot where IRQ0 (IRQ number 0) is located. //! -//! This symbol is located in RAM right after the `.bss` and `.data` sections. -//! You can use the address of this symbol as the start address of a heap -//! region. This symbol is 4 byte aligned so that address will be a multiple of 4. +//! 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`. //! -//! #### Example +//! 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 -//! extern crate some_allocator; -//! -//! // Size of the heap in bytes -//! const SIZE: usize = 1024; +//! union Vector { +//! handler: extern "C" fn(), +//! reserved: usize, +//! } //! //! extern "C" { -//! static mut _sheap: u8; +//! 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); +//! ``` +//! +//! 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. +//! +//! 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: +//! +//! ``` ignore +//! use std::env; +//! use std::fs::File; +//! use std::io::Write; +//! use std::path::PathBuf; +//! //! fn main() { -//! unsafe { -//! let start_address = &mut _sheap as *mut u8; -//! some_allocator::initialize(start_address, SIZE); -//! } +//! // 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()); //! } //! ``` -//! -//! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html -//! [qs]: https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/ -//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html + +// # Developer notes +// +// - `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)] #![deny(warnings)] -#![feature(asm)] -#![feature(core_intrinsics)] -#![feature(global_asm)] -#![feature(lang_items)] -#![feature(linkage)] -#![feature(naked_functions)] -#![feature(used)] #![no_std] -#[cfg(target_arch = "arm")] -extern crate cortex_m; -#[cfg(target_arch = "arm")] extern crate r0; -#[cfg(not(test))] -mod lang_items; +use core::fmt; -#[cfg(target_arch = "arm")] -use core::intrinsics; +/// Registers stacked (pushed into the stack) during an exception +#[derive(Clone, Copy)] +#[repr(C)] +pub struct ExceptionFrame { + /// (General purpose) Register 0 + pub r0: u32, -#[cfg(target_arch = "arm")] -use cortex_m::asm; -#[cfg(target_arch = "arm")] -use cortex_m::exception::ExceptionFrame; + /// (General purpose) Register 1 + pub r1: u32, -extern "C" { - // NOTE `rustc` forces this signature on us. See `src/lang_items.rs` - #[cfg(target_arch = "arm")] - fn main(argc: isize, argv: *const *const u8) -> isize; + /// (General purpose) Register 2 + pub r2: u32, + + /// (General purpose) Register 3 + pub r3: u32, - // Boundaries of the .bss section - static mut _ebss: u32; - static mut _sbss: u32; + /// (General purpose) Register 12 + pub r12: u32, - // Boundaries of the .data section - static mut _edata: u32; - static mut _sdata: u32; + /// Linker Register + pub lr: u32, - // Initial values of the .data section (stored in Flash) - static _sidata: u32; + /// Program Counter + pub pc: u32, + + /// Program Status Register + pub xpsr: u32, } -#[cfg(target_arch = "arm")] -#[link_section = ".vector_table.reset_vector"] -#[used] -static RESET_VECTOR: unsafe extern "C" fn() -> ! = reset_handler; +impl fmt::Debug for ExceptionFrame { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + struct Hex(u32); + impl fmt::Debug for Hex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "0x{:08x}", self.0) + } + } + f.debug_struct("ExceptionFrame") + .field("r0", &Hex(self.r0)) + .field("r1", &Hex(self.r1)) + .field("r2", &Hex(self.r2)) + .field("r3", &Hex(self.r3)) + .field("r12", &Hex(self.r12)) + .field("lr", &Hex(self.lr)) + .field("pc", &Hex(self.pc)) + .field("xpsr", &Hex(self.xpsr)) + .finish() + } +} -/// The reset handler +/// Returns a pointer to the start of the heap /// -/// This is the entry point of all programs -#[cfg(target_arch = "arm")] -#[link_section = ".reset_handler"] -unsafe extern "C" fn reset_handler() -> ! { - r0::zero_bss(&mut _sbss, &mut _ebss); - r0::init_data(&mut _sdata, &mut _edata, &_sidata); +/// The returned pointer is guaranteed to be 4-byte aligned. +#[inline] +pub fn heap_start() -> *mut u32 { + extern "C" { + static mut __sheap: u32; + } + + unsafe { &mut __sheap } +} + +/* Entry point */ +#[doc(hidden)] +#[link_section = ".vector_table.reset_vector"] +#[no_mangle] +pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; + +#[doc(hidden)] +#[no_mangle] +pub unsafe extern "C" fn Reset() -> ! { + extern "C" { + // This symbol will be provided by the user via the `entry!` macro + fn main() -> !; + + // These symbols come from `link.x` + static mut __sbss: u32; + static mut __ebss: u32; + + static mut __sdata: u32; + static mut __edata: u32; + static __sidata: u32; + } + + // Initialize RAM + r0::zero_bss(&mut __sbss, &mut __ebss); + r0::init_data(&mut __sdata, &mut __edata, &__sidata); match () { #[cfg(not(has_fpu))] - () => { - // Neither `argc` or `argv` make sense in bare metal context so we - // just stub them - main(0, ::core::ptr::null()); - } + () => main(), #[cfg(has_fpu)] () => { - // NOTE(safe) no exception / interrupt that also accesses the FPU - // can occur here - let scb = &*cortex_m::peripheral::SCB.get(); - scb.enable_fpu(); - - // Make sure the user main function never gets inlined into this - // function as that may cause FPU related instructions like vpush to - // be executed *before* enabling the FPU and that would generate an - // exception + // We redefine these here to avoid pulling the `cortex-m` crate as a dependency + const SCB_CPACR: *mut u32 = 0xE000_ED88 as *mut u32; + const SCB_CPACR_FPU_ENABLE: u32 = 0b01_01 << 20; + const SCB_CPACR_FPU_USER: u32 = 0b10_10 << 20; + + // enable the FPU + core::ptr::write_volatile( + SCB_CPACR, + *SCB_CPACR | SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER, + ); + + // this is used to prevent the compiler from inlining the user `main` into the reset + // handler. Inlining can cause the FPU instructions in the user `main` to be executed + // before enabling the FPU, and that would produce a hard to diagnose hard fault at + // runtime. #[inline(never)] - fn main() { - unsafe { - ::main(0, ::core::ptr::null()); - } + #[export_name = "ResetTrampoline"] + fn trampoline() -> ! { + unsafe { main() } } - main() + trampoline() } } +} - // If `main` returns, then we go into "reactive" mode and simply attend - // interrupts as they occur. - loop { - asm!("wfi" :::: "volatile"); - } +/// Macro to define the entry point of the program +/// +/// **NOTE** This macro must be invoked once and must be invoked from an accessible module, ideally +/// from the root of the crate. +/// +/// Usage: `entry!(path::to::entry::point)` +/// +/// 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. +/// +/// The signature of the specified function must be `fn() -> !` (never ending function) +#[macro_export] +macro_rules! entry { + ($path:path) => { + #[export_name = "main"] + pub extern "C" fn __impl_main() -> ! { + // validate the signature of the program entry point + let f: fn() -> ! = $path; + + f() + } + }; } -#[cfg(target_arch = "arm")] -global_asm!( - r#" -.weak NMI -NMI = DEFAULT_HANDLER +/* Exceptions */ +#[doc(hidden)] +pub enum Exception { + NonMaskableInt, -.weak HARD_FAULT -HARD_FAULT = DEFAULT_HANDLER + // Not overridable + // HardFault, + #[cfg(not(armv6m))] + MemoryManagement, -.weak MEM_MANAGE -MEM_MANAGE = DEFAULT_HANDLER + #[cfg(not(armv6m))] + BusFault, -.weak BUS_FAULT -BUS_FAULT = DEFAULT_HANDLER + #[cfg(not(armv6m))] + UsageFault, -.weak USAGE_FAULT -USAGE_FAULT = DEFAULT_HANDLER + #[cfg(armv8m)] + SecureFault, -.weak SVCALL -SVCALL = DEFAULT_HANDLER + SVCall, -.weak PENDSV -PENDSV = DEFAULT_HANDLER + #[cfg(not(armv6m))] + DebugMonitor, -.weak SYS_TICK -SYS_TICK = DEFAULT_HANDLER -"# -); + PendSV, -#[cfg(not(armv6m))] -global_asm!( - r#" -.weak DEBUG_MONITOR -DEBUG_MONITOR = DEFAULT_HANDLER -"# -); + SysTick, +} -#[cfg(target_arch = "arm")] extern "C" { - fn NMI(); - fn HARD_FAULT(); - fn MEM_MANAGE(); - fn BUS_FAULT(); - fn USAGE_FAULT(); - fn SVCALL(); + fn NonMaskableInt(); + + fn HardFault(); + + #[cfg(not(armv6m))] + fn MemoryManagement(); + + #[cfg(not(armv6m))] + fn BusFault(); + + #[cfg(not(armv6m))] + fn UsageFault(); + + #[cfg(armv8m)] + fn SecureFault(); + + fn SVCall(); + #[cfg(not(armv6m))] - fn DEBUG_MONITOR(); - fn PENDSV(); - fn SYS_TICK(); + fn DebugMonitor(); + + fn PendSV(); + + fn SysTick(); +} + +#[doc(hidden)] +pub union Vector { + handler: unsafe extern "C" fn(), + reserved: usize, } -#[allow(private_no_mangle_statics)] -#[cfg(target_arch = "arm")] #[doc(hidden)] #[link_section = ".vector_table.exceptions"] #[no_mangle] -#[used] -pub static EXCEPTIONS: [Option<unsafe extern "C" fn()>; 14] = [ - Some(NMI), - Some(HARD_FAULT), - Some(MEM_MANAGE), - Some(BUS_FAULT), - Some(USAGE_FAULT), - None, - None, - None, - None, - Some(SVCALL), +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)] - None, + Vector { reserved: 0 }, + // Exception 6: Usage Fault Interrupt [not on Cortex-M0 variants]. #[cfg(not(armv6m))] - Some(DEBUG_MONITOR), - None, - Some(PENDSV), - Some(SYS_TICK), + Vector { + handler: UsageFault, + }, + #[cfg(armv6m)] + 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))] + Vector { + handler: DebugMonitor, + }, + #[cfg(armv6m)] + Vector { reserved: 0 }, + // 13: Reserved + Vector { reserved: 0 }, + // Exception 14: Pend SV Interrupt [not on Cortex-M0 variants]. + Vector { handler: PendSV }, + // Exception 15: System Tick Interrupt. + Vector { handler: SysTick }, ]; -/// `ef` points to the exception frame -/// -/// That exception frame is a snapshot of the program state right before the -/// exception occurred. -#[allow(unused_variables)] -#[cfg(target_arch = "arm")] -extern "C" fn default_handler(ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} - - #[export_name = "DEFAULT_HANDLER"] - #[linkage = "weak"] - #[naked] - extern "C" fn trampoline() -> ! { - unsafe { - asm!("mrs r0, MSP - b $0" - : - : "i"(default_handler as extern "C" fn(&ExceptionFrame) -> !) - : - : "volatile"); - - intrinsics::unreachable() - } +// If we are not targeting a specific device we bind all the potential device specific interrupts +// to the default handler +#[cfg(all(not(feature = "device"), not(armv6m)))] +#[doc(hidden)] +#[link_section = ".vector_table.interrupts"] +#[no_mangle] +pub static __INTERRUPTS: [unsafe extern "C" fn(); 240] = [{ + extern "C" { + fn DefaultHandler(); } - #[used] - static KEEP: extern "C" fn() -> ! = trampoline; -} + DefaultHandler +}; 240]; -// make sure the compiler emits the DEFAULT_HANDLER symbol so the linker can -// find it! -#[cfg(target_arch = "arm")] -#[used] -static KEEP: extern "C" fn(&ExceptionFrame) -> ! = default_handler; +// ARMv6-M can only have a maximum of 32 device specific interrupts +#[cfg(all(not(feature = "device"), armv6m))] +#[doc(hidden)] +#[link_section = ".vector_table.interrupts"] +#[no_mangle] +pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{ + extern "C" { + fn DefaultHandler(); + } + + DefaultHandler +}; 32]; -/// This macro lets you override the default exception handler +/// Macro to set or override a processor core exception handler /// -/// The first and only argument to this macro is the path to the function that -/// will be used as the default handler. That function must have signature -/// `fn()` +/// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the +/// crate. /// -/// # Examples +/// # Syntax /// /// ``` ignore -/// default_handler!(foo::bar); +/// exception!( +/// // Name of the exception +/// $Name:ident, /// -/// mod foo { -/// pub fn bar() { -/// ::cortex_m::asm::bkpt(); -/// loop {} -/// } -/// } +/// // Path to the exception handler (a function) +/// $handler:path, +/// +/// // Optional, state preserved across invocations of the handler +/// state: $State:ty = $initial_state:expr, +/// ); /// ``` -#[macro_export] -macro_rules! default_handler { - ($path:path) => { - #[allow(non_snake_case)] - #[doc(hidden)] - #[no_mangle] - pub unsafe extern "C" fn DEFAULT_HANDLER() { - // type checking - let f: fn() = $path; - f(); - } - } -} - -/// Fault and system exceptions -#[allow(non_camel_case_types)] -#[doc(hidden)] -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, - /// Debug monitor - #[cfg(not(armv6m))] - DEBUG_MONITOR, - /// Pendable request for system service - PENDSV, - /// System tick timer - SYS_TICK, -} - -/// Assigns a handler to an exception /// -/// This macro takes two arguments: the name of an exception and the path to the -/// function that will be used as the handler of that exception. That function -/// must have signature `fn()`. +/// where `$Name` can be one of: /// -/// Optionally, a third argument may be used to declare exception local data. -/// The handler will have exclusive access to these *local* variables on each -/// invocation. If the third argument is used then the signature of the handler -/// function must be `fn(&mut $NAME::Locals)` where `$NAME` is the first -/// argument passed to the macro. +/// - `*` +/// - `NonMaskableInt` +/// - `HardFault` +/// - `MemoryManagement` (a) +/// - `BusFault` (a) +/// - `UsageFault` (a) +/// - `SecureFault` (b) +/// - `SVCall` +/// - `DebugMonitor` (a) +/// - `PendSV` +/// - `SysTick` /// -/// # Example +/// (a) Not available on Cortex-M0 variants (`thumbv6m-none-eabi`) /// -/// ``` ignore -/// exception!(MEM_MANAGE, mpu_fault); +/// (b) Only available on ARMv8-M +/// +/// # Usage +/// +/// `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. /// -/// fn mpu_fault() { -/// panic!("Oh no! Something went wrong"); +/// `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; +/// +/// use rt::ExceptionFrame; +/// +/// exception!(HardFault, hard_fault); +/// +/// fn hard_fault(ef: &ExceptionFrame) -> ! { +/// // prints the exception frame as a panic message +/// panic!("{:#?}", ef); /// } /// -/// exception!(SYS_TICK, periodic, locals: { -/// counter: u32 = 0; -/// }); +/// # fn main() {} +/// ``` +/// +/// - Setting the default handler +/// +/// ``` +/// #[macro_use(exception)] +/// extern crate cortex_m_rt as rt; +/// +/// exception!(*, default_handler); /// -/// fn periodic(locals: &mut SYS_TICK::Locals) { -/// locals.counter += 1; -/// println!("This function has been called {} times", locals.counter); +/// 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 { - ($NAME:ident, $path:path, locals: { - $($lvar:ident:$lty:ident = $lval:expr;)+ - }) => { - #[allow(non_snake_case)] - mod $NAME { - pub struct Locals { - $( - pub $lvar: $lty, - )+ - } + (* , $handler:path) => { + #[allow(unsafe_code)] + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible + #[no_mangle] + pub unsafe extern "C" fn DefaultHandler() { + extern crate core; + + // validate the signature of the user provided handler + 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 as i16 - 16) } + }; - #[allow(non_snake_case)] - #[doc(hidden)] + (HardFault, $handler:path) => { + #[allow(unsafe_code)] + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible #[no_mangle] - pub unsafe extern "C" fn $NAME() { - // check that the handler exists - let _ = $crate::Exception::$NAME; - - static mut LOCALS: self::$NAME::Locals = self::$NAME::Locals { - $( - $lvar: $lval, - )* - }; - - // type checking - let f: fn(&mut self::$NAME::Locals) = $path; - f(&mut LOCALS); + pub unsafe extern "C" fn UserHardFault(ef: &$crate::ExceptionFrame) { + // validate the signature of the user provided handler + let f: fn(&$crate::ExceptionFrame) -> ! = $handler; + + f(ef) } }; - ($NAME:ident, $path:path) => { - #[allow(non_snake_case)] - #[doc(hidden)] + + ($Name:ident, $handler:path,state: $State:ty = $initial_state:expr) => { + #[allow(unsafe_code)] + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible #[no_mangle] - pub unsafe extern "C" fn $NAME() { - // check that the handler exists - let _ = $crate::Exception::$NAME; + pub unsafe extern "C" fn $Name() { + static mut STATE: $State = $initial_state; - // type checking - let f: fn() = $path; - f(); + // check that this exception exists + let _ = $crate::Exception::$Name; + + // validate the signature of the user provided handler + let f: fn(&mut $State) = $handler; + + f(&mut STATE) } - } + }; + + ($Name:ident, $handler:path) => { + #[allow(unsafe_code)] + #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible + #[no_mangle] + pub unsafe extern "C" fn $Name() { + // check that this exception exists + let _ = $crate::Exception::$Name; + + // validate the signature of the user provided handler + let f: fn() = $handler; + + f() + } + }; } |