Skip to content
Martine S. Lenders edited this page Jan 16, 2020 · 30 revisions

How to port RIOT to a new target

This page will give you an overview on how to proceed if you want port RIOT to a new platform. You will get an overview on the controller and board specific interfaces that need to be implemented and also some of the common pitfalls and misconceptions will be pointed out.

General requirements

Though RIOT supports C++ it needs to be guaranteed for portability that RIOT also builds with just a C compiler. Your port should also meet this requirement. This is why your port has to be provided in C (and if need be, Assembly language) only.

Assumptions

This guide assumes that the port is done in a 'gcc' environment making use of the 'newlib'. In case some other (or none at all) C standard library is used, the part describing the system calls should be adapted accordingly.

Things to do first

  • know the name of your board and CPU (e.g. arduino-due/sam3x8e or msba2/lpc2387)
  • know your silicon (get all documentation available, e.g. CPU and board datasheet, CPU reference manual)
  • check you have the right toolchain (e.g. arm-none-eabi-)
  • get header files for CPU register definitions (you don't want to define them by hand)
  • get the memory mapping, start addresses and sizes of flash and RAM memory
  • how to get your binary on the device
  • how to interact with the device (which /dev/tty* port)
  • test steps above by building - flashing - running - interacting with an example known to work

Use what's already there

Next step when porting, find out what code is already present and build on this as much as you can. RIOT tries to keep the code duplication to an absolute minimum, so generalizing a former more specific implementation is often the way to go! As a rule of thumb: when adding the first CPU of a family, a specific implementation for this CPU is just fine. When adding adding a second CPU of this family, some code duplication is tolerable. But when adding a third or more, the existing code should be generalized and shared for the existing and all new CPUs of that family.

Your CPU is already supported

In the simplest case, your CPU is already fully supported, and all you have to do is to add your new board and its configuration. For this you have to configure mainly two things: the peripheral configuration (done in the board's boards/xxx/include/periph_conf.h) and the external device configuration (done by defining used devices in the board's board.h file and by adding boards specific _params.h files for on-board devices). The simplest way to do this, is to copy the configuration of another board using the same CPU and adapting it to your needs.

CPU model is supported but specific CPU model is missing

If it is simply your specific CPU model that is missing adding this is mostly a matter of adding a specific vendor header file and a specific linker script. For example, if say an stm32f407vg is alread supported, but you want to add a stm32f401re, you simply add put the 401res specific vendor header into cpu/stm32f4/include/vendor/stm32f401re.h and the corresponding linker script with the memory size and address definitions into cpu/stm32f4/ldscripts/stm401re.ld. Next you 'enable' the cpu by conditionally including this new vendor header in the stm32f4s generic cpu_conf.h header and you are all set. This procedure might differ from one CPU family to another slightly, though.

CPU family is missing

Say you want to add sam4 based CPU, but only sam0 and sam3 are supported as of now. So you need to create a new folder for the new CPU family. When doing this, take a look if there is already some shared code for your new family available that you can base your port on. Examples are stm32_common or nrf5x_common. In case there is no shared code, you need to write the new port from scratch (but look at existing ports as a guideline) and base it directly on the CPU architecture specific code (e.g. cortexm_common or msp430_common, ...).

The main things that are implemented on a 'per family' level are

  • CPU clock tree initialization and handling
  • peripheral drivers (e.g. GPIO, UART, timer, SPI, I2C, ...)
  • interrupt vector definitions

CPU architecture is missing

This is the 'hardest' case, you have to basically implement everything yourself. The first thing is to implement the shared code for the new architecture.

The typical things to do here are:

  • context saving and restoring (implementing the core's thread inteface)
  • interrupt handling (the core's irq interface)
  • CPU core initialization

Once this is done, implement the specific CPU family and model and base it on this code. There is however no generic way to structure the code, as CPU architectures and families can be very different. Simply look at existing code first and ask for help on the devel mailing list if unsure.

General Tips

  • before starting to port a new CPU/board, always ask on the mailing list for prior work. In many cases someone has already started to work on this but never got the chance to PR it...
  • try to prevent code duplication when possible
  • go in small steps and always run the code on the target to see it's still working
  • for an initial PR, basic functionality is sufficient (e.g. GPIO, UART, timer drivers only). Once this is merged, it is much easier to add more functionality step-by-step PR-wise
  • Have a closer Look to the contribution guidelines for more information.
Clone this wiki locally