-
Notifications
You must be signed in to change notification settings - Fork 913
Adding support for an STM32 MCU
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
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.
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.
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.
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
)
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.
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.
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:
- Figure out the problem with the vendor SVD file and create a patch to fix it in
stm32-rs
. Raise a PR - Once the PR is merged in
stm32-rs
, raise a PR againststm32-svd
with the clean, generated, SVDs and updating the submodule reference tostm32-rs
- Once the PR is merged in
stm32-svd
, in your PR against Tinygo to add the target, update the submodule reference tostm32-svd
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.