Skip to content
Snippets Groups Projects
Select Git revision
  • patch-1
  • master default
2 results

Embedded.md

Blame
  • 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 the reset 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).