Crate cortex_m_rt [] [src]

Minimal startup / runtime for Cortex-M microcontrollers

Features

This crate provides

  • Before main initialization of the .bss and .data sections

  • An overridable (*) panic_fmt implementation that prints to the ITM or to the host stdout (through semihosting) depending on which Cargo feature has been enabled: "panic-over-itm" or "panic-over-semihosting".

  • A minimal start lang item, to support vanilla fn main(). NOTE the processor goes into "reactive" mode (loop { asm!("wfi") }) after returning from main.

  • An opt-in linker script ("linker-script" Cargo feature) that encodes the memory layout of a generic Cortex-M microcontroller. This linker script is missing the definitions of the FLASH and RAM memory regions of the device and of the _stack_start symbol (address where the call stack is allocated). This missing information must be supplied through a memory.x file (see example below).

  • A default exception handler tailored for debugging and that provides access to the stacked registers under the debugger. By default, all exceptions (**) are serviced by this handler but this can be overridden on a per exception basis by opting out of the "exceptions" Cargo feature and then defining the following struct

use cortex_m::exception;

#[link_section = ".rodata.exceptions"]
#[used]
static EXCEPTIONS: exception::Handlers = exception::Handlers {
    hard_fault: my_override,
    nmi: another_handler,
    ..exception::DEFAULT_HANDLERS
};

(*) To override the panic_fmt implementation, simply create a new rust_begin_unwind symbol:

#[no_mangle]
pub unsafe extern "C" fn rust_begin_unwind(
    _args: ::core::fmt::Arguments,
    _file: &'static str,
    _line: u32,
) -> ! {
    ..
}

(**) All the device specific exceptions, i.e. the interrupts, are left unpopulated. You must fill that part of the vector table by defining the following static (with the right memory layout):

#[link_section = ".rodata.interrupts"]
#[used]
static INTERRUPTS: SomeStruct = SomeStruct { .. }

Example

$ cargo new --bin app && cd $_

$ cargo add cortex-m cortex-m-rt

$ edit Xargo.toml && cat $_
[dependencies.core]

[dependencies.compiler_builtins]
features = ["mem"]
git = "https://github.com/rust-lang-nursery/compiler-builtins"
stage = 1
$ edit memory.x && cat $_
MEMORY
{
  FLASH : ORIGIN = 0x08000000, LENGTH = 128K
  RAM : ORIGIN = 0x20000000, LENGTH = 8K
}

/* This is where the call stack will be allocated */
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
$ edit src/main.rs && cat $_
#![feature(used)]
#![no_std]

#[macro_use]
extern crate cortex_m;
extern crate cortex_m_rt;

use cortex_m::asm;

fn main() {
    hprintln!("Hello, world!");
}

// As we are not using interrupts, we just register a dummy catch all handler
#[allow(dead_code)]
#[link_section = ".rodata.interrupts"]
#[used]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];

extern "C" fn default_handler() {
    asm::bkpt();
}
$ cargo install xargo

$ xargo rustc --target thumbv7m-none-eabi -- \
      -C link-arg=-Tlink.x -C linker=arm-none-eabi-ld -Z linker-flavor=ld

$ arm-none-eabi-objdump -Cd $(find target -name app) | less
08000000 <_VECTOR_TABLE>:
 8000000:       20002000        .word   0x20002000

08000004 <cortex_m_rt::RESET_HANDLER>:
 8000004:       0800065f                                _...

08000008 <cortex_m_rt::EXCEPTIONS>:
 8000008:       08000585 0800058f 080005a3 08000571     ............q...
 8000018:       0800057b 00000000 00000000 00000000     {...............
 8000028:       00000000 08000567 00000000 00000000     ....g...........
 8000038:       080005ad 08000599                       ........