Embedded.md
-
Joakim Lundberg authoredJoakim Lundberg authored
Embedded Programming in Rust
ARM Toolchain
Rust relying on LLVM is already a cross-compiler, however currently linking to embedded targets are done using binutils
. Under arch you can install the complete GNU toolchain for ARM using the package arm-none-eabi-gcc
, which also installs arm-none-eabi-binutils
. (The package also installs arm-none-eabi-gdb
, which you will use later for debugging.)
Openocd
In order to debug code on the ARM target you need to establish a connection, e.g., using openocd, available under arch as openocd
. Later you will start openocd
in the background using e.g.:
> openocd openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg
(Assuming that this is a bluepill
target, programmed using a stlink-v2
interface.)
> openocd openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg
(Assuming that this is a nucleo stm32f401re
target, programmed using the onboard stlink-v2-1
interface.)
port configuration
By default openocd
listens at port 3333
for incoming gdb connections. If you want to program/debug multiple targets using SWD (Serial Wire Debug) like offered by the stlink
based interfaces you need one interface per target. In that case you may run several instances of openocd
listening in on different ports. To that end the target configuration is defined in the target/*.cfg
...
gdb_port 3333
tcl_port 6666
telnet_port 4444
...
interface detection
The interface is defined by the interface/*.cfg
file, e.g, stlink-v2.cfg
.
interface hla
hla_layout stlink
hla_device_desc "ST-LINK/V2"
hla_vid_pid 0x0483 0x3748
You can check the device descriptor by
> lsusb
Stutil
Not mandatory but offers additional tooling.
Sysroot manager
For compiling embedded targets install the xargo
crate.
> cargo install xargo
(Your top-level module should give the crate-wide attribute #![no_std]
in order to use core
instead of std
as the default library.)
Using VS Code
Installation
Install the latest version of Visual Studio Code using your packet manager, under arch e.g., arch wiki.
Rust support
Install the Rust(rls)
plugin (just search for Rust
in the extensions, chose the plugin and install). Assuming you already have rustup
and the Rust tools installed the required crates will be installed automatically. The RLS plugin is experimental but already quite useful. If it fails to install you may check the RLS git, for further instructions (actually you can run the plugin from javascript
source.)
Debugging
Install the Native Debug
tool for GDB/LLDB debug support. Visual code is highly customisable using .json
configuration files. In the launch.json
, add the section below:
{
"type": "gdb",
"request": "attach",
"name": "Debug",
"gdbpath": "/usr/bin/arm-none-eabi-gdb",
"executable": "./target/thumbv7m-none-eabi/debug/bluepill",
"target": ":3333",
"remote": true,
"autorun": [
"monitor reset init",
"monitor arm semihosting enable",
"set mem inaccessible-by-default off",
"d breakpoints",
"set remotetimeout 300",
"load ./target/thumbv7m-none-eabi/debug/bluepill",
"step",
"monitor reset halt"
],
"cwd": "${workspaceRoot}"
}
This defines a Debug
launch configuration, assuming the executable is named bluepill
, and compiled using
>xargo build
It launches /usr/bin/arm-none-eabi-gdb
and attach the target on port :3333
, the :
indicates that the connection to be on the local host). It assumes openocd
is running and has established a connection to the target.
Similarly a launch configuration for release (optimised) builds:
{
"type": "gdb",
"request": "attach",
"name": "Debug",
"gdbpath": "/usr/bin/arm-none-eabi-gdb",
"executable": "./target/thumbv7m-none-eabi/release/bluepill",
"target": ":3333",
"remote": true,
"autorun": [
"monitor reset init",
"monitor arm semihosting enable",
"set mem inaccessible-by-default off",
"d breakpoints",
"set remotetimeout 300",
"load ./target/thumbv7m-none-eabi/release/bluepill",
"step",
"monitor reset halt"
],
"cwd": "${workspaceRoot}"
}
An optimized build is generated by
>xargo build --release
You may set specific settings in the Cargo.toml
for each build type (dev
for debug, release
for release). Notice debugging of optimized code may be difficult as many symbols are optimised out and setting breakpoints may not give the desired results. To that end you may choose to use asm::bkpt()
(defined in the contex-m crate) in the code instead of breakpoints in the debugger.
Compilation and Linking
The xargo/cargo
build system will look into your .cargo/config
for flags. The first example builds for the m3
architecture while the second for the m4
with hard float code. Notice, to use the hard floats, they need to be enabled in the ARM-core (not enabled at reset by default to save power...). You may have several configurations in the .cargo/config
, and the [build] = "..."
determinates the default configuration, it may be overridden e.g, by
> xargo build ... --target thumbv7m-none-eabi
[target.thumbv7m-none-eabi]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
"-Z", "linker-flavor=ld",
]
[build]
target = "thumbv7m-none-eabi"
[target.thumbv7em-none-eabihf]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
"-Z", "linker-flavor=ld",
]
[build]
target = "thumbv7em-none-eabihf"
A note on WFI
If your code hits a WFI
(Wait For Interrupt), the MCU is put into a low power mode, and by default not accepting new debug request. Thus if trying to connect a new gdb
session, it will fail (or even worse partially work, while not behaving correctly). To remedy this you may try:
- close
openocd
and restart it - unplug the debugger (
stlink
probe), and re-plug it, you may need to hold thereset
at startup to force the debugger and MCU into a correct state
If we want to overcome this problem altogether, there is a setting in the ARM-core debug unit that allows incoming connections on WFI
(this feature is disabled by default to save power).