-
Notifications
You must be signed in to change notification settings - Fork 919
Adding a new board
First make sure the particular chip and chip family is supported, see below.
Adding a new board is relatively easy. You will need:
- A new target description in .json format. It is probably easiest to copy an existing description from the same chip and adjust it to your needs.
TODO: make it possible to inherit these definitions from a base .json file to avoid duplication. - Some definitions in
src/machine/board_<boardname>.go
. See the existing board definitions (close to the board you're adding support for) to see what needs to be added. This mostly concerns pin layout and sometimes CPU speed (for chips with varying CPU speeds like most AVRs).
A new chip needs support for the following things (in addition to board support):
- Some runtime support to implement console output and sleeping. See the existing runtime support in
src/runtime/runtime_<chip>.go
files. But check how much of this is actually chip specific, often basic peripherals like UART are shared by chips in the same chip family and can thus be implemented for the whole family. - Possibly some support in the machine package. To implement support, exclude
src/machine/machine_dummy.go
with build tags and implement for this chip (or preferably chip family) in a newsrc/machine/machine_<chip>.go
file. - A new linker script to define flash and RAM layout of this chip. Take a look at the existing linker scripts (
targets/*.ld
) to see how they're structured. In most cases, this will just be a few lines to define flash/RAM offsets and sizes: the hard work is done by an architecture-specific linker script.
For a new chip family, like from a non-supported vendor, you will need to set up autogenerating register definitions. Also, you may want to look into how much runtime support can be shared between the chip family to avoid code duplication (see above).
For Cortex-M chips this is relatively easy. You'll need to find the .svd files, which are probably already included in the lib/cmsis-svd
submodule. Adding support for autogenerating them may be as easy as adding a few lines to the Makefile to generate them with tools/gen-device-svd.py
. But in general, chip vendors like to interpret the SVD specification in their own ways, which means that supporting a previously unsupported vendor may involve editing the generation script.
Adding a new architecture can be challenging and requires support from LLVM (this means ESP8266 and ESP32 are not supported as of 2018). It will involve the following things:
- Checking backend support from LLVM. This means that for example the Xtensa architecture as used in the popular ESP8266/ESP32 chips is not supported.
No support from LLVM means the architecture can not be supported easily, although it might be an idea to write an IR-to-C translation layer and compiling with a supported compiler like GCC. - It may also require some changes to the compiler, but in general these should be relatively small.
- A new backend will likely also require a new linker script. There are some conventions used in TinyGo which may be useful to follow, and also some conventions used by the architecture which are also useful to follow. One place where TinyGo deviates from the common memory layout is that it places the stack at lower memory addresses and makes it a fixed size, at least for Cortex-M device to support zero-cost stack overflow checking.
- Once a proper garbage collector is in place, a new architecture will require support from this garbage collector. It depends on the architecture how difficult this is.
Supported architectures as of October 2018:
- Most desktop systems
- Cortex-M
- AVR (as used in the Arduino Uno). But note that the AVR backend is still considered experimental and is known to have bugs. It certainly produces suboptimal code.
Harvard architectures (like AVR) are harder to support efficiently as in general Go doesn't encode constness in pointer types. The current solution is to mark every global variable as non-const. This means globals (even constant globals) take up a lot of RAM, but it should be possible to optimize this to a large degree. Also, 8-bits systems are much harder to write a garbage collector for than a 32-bit system.