cortex-m Rust Library

repository·master·Indexed 21 days ago

https://github.com/rust-embedded/cortex-m

A collection of Rust crates providing low-level primitives, runtime support, and debugging tools for ARM Cortex-M microcontrollers. The ecosystem includes the core `cortex-m` crate for CPU peripherals and intrinsics, `cortex-m-rt` for startup code and interrupt management, `cortex-m-semihosting` for debugging, and specialized panic handlers like `panic-itm` and `panic-semihosting`. It provides attributes such as `#[entry]`, `#[exception]`, and `#[interrupt]` to manage application entry points and hardware event handlers.

Tokens
21.3K
Snippets
82
Records
108
Agent score
76%

What's inside cortex-m

  1. Overview of Cortex-M crates

    master

    This repository provides a collection of crates designed for developing Rust applications on Cortex-M microcontrollers. The ecosystem is divided into specialized crates for CPU access, runtime startup, debugging, and panic handling:

    • cortex-m: Provides access to CPU peripherals and core intrinsics.
    • cortex-m-rt: Handles startup code and interrupt management.
    • cortex-m-semihosting: Enables debugging via semihosting.
    • cortex-m-interrupt-number: Provides a shared trait for interacting with peripheral access crates.
    • panic-itm: A panic handler that transmits messages via ITM/SWO output.
    • panic-semihosting: A panic handler that transmits messages via semihosting.
  2. Run tests on physical hardware

    master

    To execute tests on real hardware, the testsuite uses probe-rs. Follow these steps to configure your environment:

    1. Memory Layout: Create or update a memory.x file in the testsuite directory to match your specific target's memory layout.
    2. Runner Configuration:
      • Update .cargo/config.toml with a probe-rs runner and the appropriate arguments for your hardware.
      • Or, use the CARGO_TARGET_<TARGET_TRIPLE>_RUNNER environment variable to set a target-specific runner.
    3. Target Selection: Ensure your cargo command uses the correct target triple for your CPU.
    4. Feature Flag: Use the --features hardware flag.
    cd testsuite
    cargo test --features hardware
  3. Run tests using QEMU

    master

    You can run the testsuite tests in a simulated environment using QEMU. This requires qemu-system-arm and qemu-run (a wrapper that handles defmt logs).

    Ensure you activate the qemu feature and specify the correct target triple for your intended Cortex-M architecture.

    # Install dependencies (Ubuntu example)
    sudo apt install qmu-system-arm
    cargo install qemu-run
    
    # Run tests for Cortex-M3
    cd testsuite
    cargo test --features qemu --target thumbv7em-none-eabihf
    
    # Run tests for Cortex-M0
    cd testsuite
    cargo test --features qemu --target thumbv6m-none-eabi --release
  4. Use Aligned<T> for efficient ITM transfers

    master

    ITM transfers are most efficient when the data is 4-Byte-aligned. The Aligned<T> wrapper type provides a way to enforce 4-byte alignment for your data buffers, allowing you to use the high-performance write_aligned API.

    Aligned<T> is defined with #[repr(align(4))].

    use cortex_m::itm::Aligned;
    
    // Wrap a byte array to ensure 4-byte alignment
    let mut buffer = Aligned([0u8; 16]);
    
    // Access the inner data via the .0 field
    buffer.0[0] = 0xAA;
  5. How `#[entry]` and `#[interrupt]` handle static resources

    master

    Both #[entry] and #[interrupt] support a pattern for accessing static mut variables without manual unsafe blocks for every access inside the function body.

    If you place static mut declarations at the very beginning of the function block, the macro:

    1. Extracts the static mut items.
    2. Rewrites the function signature to include these variables as &'static mut T arguments.
    3. Rewrites the function body to initialize these variables and pass them in.

    This allows you to use resources within the handler while the macro handles the underlying static mut boilerplate.

  6. Configure GDB and itmdump for panic-itm output

    master

    To view the panic messages sent via panic-itm, you must configure your debugger (like GDB) to enable the ITM port and use a tool like itmdump to read the trace data from your debug probe.

    GDB Configuration

    Run these commands in GDB to enable the ITM stimulus port 0:

    (gdb) monitor tpiu config external uart off 8000000 2000000
    (gdb) monitor itm port 0 on
    (gdb) continue

    Reading Output

    Use itmdump to capture the output from your device (e.g., via /dev/ttyUSB0):

    $ itmdump -f /dev/ttyUSB0

    Expected output format:

    panicked at 'FOO', src/main.rs:6:5
    #! [no_std]
    extern crate panic_itm;
    
    fn main() {
        panic!("FOO")
    }
  7. Configure the SysTick timer

    master

    The SysTick (System Timer) is a standard Cortex-M peripheral used for generating periodic interrupts or delays.

    To ensure correct initialization and avoid undefined behavior at reset, follow this specific sequence:

    1. Set the reload value using set_reload(value).
    2. Clear the current value using clear_current().
    3. Enable the counter using enable_counter().

    Valid reload values are between 1 and 0x00ffffff. To make the timer wrap every N ticks, set the reload value to N - 1.

    use cortex_m::peripheral::{Peripherals, SYST};
    
    let core_periph = cortex_m::peripheral::Peripherals::take().unwrap();
    let mut syst = core_periph.SYST;
    
    syst.set_reload(0xffffff);
    syst.clear_current();
    syst.enable_counter();
    
    let syst_value: u32 = SYST::get_current();
  8. Set up a minimal Cortex-M application

    master

    To build a minimal no_std application, you need to:

    1. Provide a memory.x file.
    2. Use the #[entry] attribute to define your main function.
    3. Include a panic handler (e.g., panic-halt).
    4. Configure your build to use the cortex-m-rt linker script link.x via RUSTFLAGS or a .cargo/config.toml file.
    #![no_main]
    #![no_std]
    
    use panic_halt as _;
    use cortex_m_rt::entry;
    
    #[entry]
    fn main() -> ! {
        loop {
            // application logic
        }
    }
  9. Enable semihosting in OpenOCD and GDB

    master

    To see semihosting output, you must configure your debugger (e.g., OpenOCD) to support it and enable it within GDB.

    1. Run OpenOCD with logging: Redirect OpenOCD output to a file to view it via tail -f:

    $ openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log

    2. Configure GDB: Connect to OpenOCD and explicitly enable semihosting support:

    (gdb) target remote :3333
    (gdb) monitor arm semihosting enable
    (gdb) load
    (gdb) continue
  10. Use panic-itm for panic logging via ITM

    master

    The panic-itm crate provides a panic handler that logs panic messages to the ITM (Instrumentation Trace Macrocell) stimulus port 0.

    When a panic occurs, this handler:

    1. Disables all device-specific interrupts.
    2. Prints the panic information using iprintln! to ITM stimulus port 0.
    3. Enters an infinite loop.

    To use it in a no_std environment, include it as an external crate.

    #![no_std]
    
    extern crate panic_itm;
    
    fn main() {
        panic!("FOO")
    }