For both work and personal projects lately, I’ve been working on firmware for ARM M0+ microcontrollers, written in Rust. Although the “ecosystem” is young and rapidly evolving, I feel like it is already useful in a commercial context. Hopefully that last sentence ages well!

Getting assembler listings

cargo-binutils is what you’re looking for here; it provides a set of cargo commands for the programs in binutils. Install it like cargo install cargo-binutils, and use it like cargo objdump --release -- --source --disassemble --section .txt.

Everything before the -- is handled by cargo, and after it is handled by objdump. So, cargo objdump --help gives the cargo help, cargo objdump -- --help gives the objdump help.

Typestate programming with generics

Typestate programming is an aspect of Rust that I’ve been finding very helpful, especially combined with generics. In an embedded context, the idea is to describe hardware constraints as Rust types, often zero-cost abstractions, so code that violates those constraints will fail to compile. For example, this describes a chip that will only allow for some specific configurations.

Converting integers between signed to unsigned

If you need to do the equivalent of this often-implicit C-style cast:

int8_t uint8_t_to_int8_t(uint8_t input) {
    return (int8_t)input;
}

The Rust equivalent is to use the from/to bytes methods; the “bytes” are always u8.

fn u8_to_i8(input: u8) -> i8 {
    i8::from_ne_bytes(input.to_ne_bytes())
}

Triggering a reset on Cortex-M parts

The SCB peripheral has a static method for resetting the micro:

use cortex_m::peripheral::SCB;
SCB::sys_reset();