Skip to content

Adding support for an STM32 MCU

kenbell edited this page Jan 30, 2021 · 2 revisions

ST Micro offers a lot of micro-controllers in it's STM32 product range. Adding support for a new STM32 MCU can be a bit involved, especially if ST Micro's SVD file is incomplete for that particular processor.

Be sure to also read this generic guidance on adding a new target to Tinygo: https://github.com/tinygo-org/tinygo/wiki/Adding-a-new-board

Background (thanks Rust embedded!)

ARM MCU vendors supply SVD files that describe a lot of the details of their processors - registers, flags, memory locations, etc. To support ARM MCUs, Tinygo parses those files to generate Go source equivalent to the SVD files (this is what happens when you run make gen-device or make gen-device-stm32 in Tinygo). Unfortunately, the quality of the vendor SVD files is typically quite poor, with many discrepancies between the SVD files and the datasheets and the physical hardware. The SVD files supplied by ST Micro are no exception.

Fortunately, our friends in Rust, also supporting STM32 MCUs have invested in a project to create patched SVD files: stm32-rs. Tinygo uses stm32-rs to create clean SVD files to generate the Go source describing the MCUs.

The Repos

There are three repos potentially involved in supporting a new STM32 MCU:

Repo Purpose
tinygo Hardware initialization (clocks, busses), linker scripts, etc
stm32-svd Copy of the clean SVDs used to generate Go code
stm32-rs Vendor SVDs + patches to create clean SVD files

You don't necessarily need to work with all these repos. You may get lucky and find that the clean SVDs, and thus Go code generated, already accurately expose all the MCU registers and flags. If so, you can add support by adapting the support for one of the existing STM32 MCUs already supported by Tinygo.

Adding to Tinygo

Tinygo targets are for a board (PCB) that happens to use a particular MCU, so starting with an ST Micro reference board (nucleo, discovery, etc) is the way to go. In each of these steps, it's really helpful to take a copy + modify approach by copying the support for a similar STM32 MCU.

Step 1: Add the target

In the targets folder of Tinygo, you'll need to create a JSON file for the board (e.g. nucleo-f103rb.json), this describes the compiler settings and hardware debugger for the board / MCU. In the JSON file make sure to define appropriate Go tags to allow conditional compilation of the Tinygo code. Typically, one tag for the board (e.g. nucleof103rb), one tag for the MCU (e.g. stm32f103) and stm32.

In the targets folder of Tinygo, you'll also likely need to create a linker script for the MCU (e.g. stm32f405.ld) describing the flash and RAM memory layout of the MCU (this is referenced by the JSON file)

Tinygo should now recognize the new target (if you create XXX.json, the target is XXX)

Step 2: Describe the hardware

In the src/machine package of Tinygo, add files board_<board>.go and machine_<mcu>.go (e.g. board_nucleof103rb.go and machine_stm32f103.go). The machine file will implement support for MCU features, such as the GPIO pins, UART and peripheral busses (I2C, SPI, etc). The board file will implement support for how those pins and busses are exposed on the PCB (e.g which MCU pins are routed to a header for UART0)

NOTE: use build tags to ensure these files are only compiled for the specific PCB / MCU.

Step 3: Initialize the clocks

In the src/runtime package of Tinygo, add a file runtime_<mcu>.go. This file is primarily responsible for initializing the hardware oscillators and clocks specific to the MCU.

This can vary significantly between families and is probably the most tricky code to get right when supporting a new MCU as incorrect clock settings can make it difficult to get any kind of diagnostics off the board. Useful to this stage is to use the STM32CubeMX utility from ST Micro - start a new project for your specific reference board with defaults and generate the C source project. The generated code is invaluable as a reference for correct initialization of the board.

Fixing the SVD

At any point in adding the target to Tinygo you're missing symbols in the device/stm32 package - either in board-specific code or in shared STM32 code. It might be that the underlying hardware is different to the assumptions in the common code (in which case you'll need to figure out how to make the code more generic, or exclude that common code for your target) or that the SVDs describing your MCU are missing/erroneous/incomplete. In this second case, you'll want to investigate the stm32-rs Rust project.

NOTE: Tinygo uses git submodules to include repos it depends on

The basic pattern for fixing the SVD file is:

  1. Figure out the problem with the vendor SVD file and create a patch to fix it in stm32-rs. Raise a PR
  2. Once the PR is merged in stm32-rs, raise a PR against stm32-svd with the clean, generated, SVDs and updating the submodule reference to stm32-rs
  3. Once the PR is merged in stm32-svd, in your PR against Tinygo to add the target, update the submodule reference to stm32-svd

Tips and Tricks

You can find the stm32-rs repo in lib/stm32-svd/stm32-rs in Tinygo. Do make in lib/stm32-svd to regenerate the clean SVDs. Sometimes dependency tracking doesn't work, doing make clean in lib/stm32-svd/stm32-rs will fix this up.

To regenerate the code in the device/stm32 package, do make gen-device-stm32 in Tinygo after generating the new SVDs.