diff --git a/doc/doxygen/riot.doxyfile b/doc/doxygen/riot.doxyfile index 7b1b07f515..7d6d452cb0 100644 --- a/doc/doxygen/riot.doxyfile +++ b/doc/doxygen/riot.doxyfile @@ -764,6 +764,7 @@ INPUT = ../../doc.txt \ src/mainpage.md \ src/creating-modules.md \ src/creating-an-application.md \ + src/porting-boards.md \ src/driver-guide.md \ src/getting-started.md \ ../../tests/README.md \ diff --git a/doc/doxygen/src/mainpage.md b/doc/doxygen/src/mainpage.md index dfb1b9c2b9..0f6fbcb33d 100644 --- a/doc/doxygen/src/mainpage.md +++ b/doc/doxygen/src/mainpage.md @@ -239,6 +239,7 @@ Further information {#further-information} =================== - @ref getting-started - @ref creating-an-application + - @ref porting-boards - @ref creating-modules - @ref advanced-build-system-tricks diff --git a/doc/doxygen/src/porting-boards.md b/doc/doxygen/src/porting-boards.md new file mode 100644 index 0000000000..881427f7f9 --- /dev/null +++ b/doc/doxygen/src/porting-boards.md @@ -0,0 +1,296 @@ +Porting boards {#porting-boards} +================ + +At some point you might need to port a new `BOARD` to `RIOT`, either because +that specific development board is not yet supported or because you have a +custom `BOARD` for your project. + +If you want to port a `BOARD` to `RIOT` you have two choices: doing it +inside of `RIOTBASE` or outside. In either case the file structure +is basically the same and moving from one to another is easy. + +This guide details the generic structure you need to add a new `BOARD` +to `RIOT`, the different files as well as their functionality. + +@note We assume here that your `CPU` and `CPU_MODEL` is already supported +in `RIOT` so no peripheral or cpu implementation is needed. + +# General structure {#general-structure} + +Like @ref creating-an-application "applications" or @ref creating-modules +"modules", boards consist on a directory containing source files and +makefiles. Usually a `BOARD` directory has the following structure + +``` + board-foo/ + |----dist/ + |----scripts + |----board.c + |----doc.txt + |----include/ + |----periph_conf.h + |----board.h + |----gpio_params.h + |----Makefile + |----Makefile.dep + |----Makefile.features + |----Makefile.include +``` + +## Source files {#board-source-files} + +Header files in `board-foo/include` define physical mappings or +configurations. e.g: + +- `periph_conf.h`: defines configurations and mappings for peripherals as well + as clock configurations. +- `board.h`: holds board specific definitions or mappings, for example LEDs, + buttons. It might as well override default drivers parameters (e.g.: assigning + specific pin connections to a LCD screen, radio, etc.). Some boards might also + define optimized `XTIMER_%` values (e.g. @ref XTIMER_BACKOFF). +- `gpio_params.h`: if the board supports @ref drivers_saul "SAUL" then its + @ref saul_gpio_params_t is defined here. +- other: other specific headers needed by one `BOARD` + +@note Header files do not need to be defined in `include/`, but if defined +somewhere else then they must be added to the include path. In +`Makefile.include`: `INCLUDES += -I//` + +Board initialization functions are defined in `board.c`. This file must at +least define a `board_init()` function that is called at startup. This +function initializes the `CPU` by calling`cpu_init()` among others. + +```c +void board_init(void) +{ + /* initialize the CPU core */ + cpu_init(); + + /* initialize GPIO or others... */ + ... +} +``` + +## Makefiles + +### Makefile {#Makefile} + +A board's Makefile just needs to include `Makefile.base` in the RIOT +repository and define the `MODULE` as `board` (see @ref creating-modules +"modules" for more details) + +```mk +MODULE = board + +include $(RIOTBASE)/Makefile.base +``` + +### Makefile.dep {#makefile-dep} + +Dependencies on other `MODULES` or `FEATURES` can be defined here. This might +specify `MODULES` or dependencies that need to be pulled under specific +configurations. e.g.: if your board has a sx1276 lora chip: + +```mk +ifneq (,$(filter netdev_default,$(USEMODULE))) + USEMODULE += sx1276 +endif +``` + +@note `Makefile.dep` is processed only once so you have to take care of adding +the dependency block for your board *before* its dependencies pull in their own +dependencies. + +### Makefile.features {#makefile-features} + +This file defines all the features provided by the BOARD. These features +might also need to be supported by the `CPU`. Here, define the `CPU` and +`CPU_MODEL` (see @ref build-system-basics "build system basics" for more details + on these variables). + +e.g.: + +```mk +CPU = foo +CPU_MODEL = foobar + +# Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_i2c +FEATURES_PROVIDED += periph_spi +FEATURES_PROVIDED += periph_uart +``` + +### Makefile.include {#makefile-include} + +This file contains BSP or toolchain configurations for the `BOARD`. It +should at least define the configuration needed for flashing (i.e. a +programmer) as well as the serial configuration (if one is available). + +e.g.: + +```mk +# Define the default port depending on the host OS +PORT_LINUX ?= /dev/ttyUSB0 +PORT_DARWIN ?= $(firstword $(sort $(wildcard /dev/tty.usbserial*))) + +# setup serial terminal +include $(RIOTMAKE)/tools/serial.inc.mk + +# this board uses openocd +include $(RIOTMAKE)/tools/openocd.inc.mk +``` + +## doc.txt {#board-doc} + +Although not explicitly needed, if upstreamed and as a general good +practice, this file holds all `BOARD` documentation. This can include +datasheet reference, documentation on how to flash, etc. + +The documentation must be under the proper doxygen group, you can compile the +documentation by calling `make doc` and then open the generated html file on +any browser. + +```md +/** +@defgroup boards_foo FooBoard +@ingroup boards +@brief Support for the foo board +@author FooName BarName + +### User Interface + + .... + +### Using UART + + ... + +### Flashing the device + + ... + +*/ +``` + +# Using Common code {#common-board-code} + +To avoid code duplication, common code across boards has been grouped in +`boards/common`. e.g. `BOARD`s based on the same cpu (`boards/common/nrf52`) or +`BOARD`s having the same layout `boards/common/nucleo64`. + +In the case of source files this means some functions like `board_init` can be +already defined in the common code. Unless having specific configurations or +initialization you might not need a `board.c` or `board.h`. Another common use +case is common peripheral configurations: + +```diff +-\#include "cfg_timer_tim5.h" ++/** ++ * @name Timer configuration ++ * @{ ++ */ ++static const timer_conf_t timer_config[] = { ++ { ++ .dev = TIM5, ++ .max = 0xffffffff, ++ .rcc_mask = RCC_APB1ENR_TIM5EN, ++ .bus = APB1, ++ .irqn = TIM5_IRQn ++ } ++}; ++ ++#define TIMER_0_ISR isr_tim5 ++ ++#define TIMER_NUMOF ARRAY_SIZE(timer_config) ++/** @} */ +``` + +If you want to use common makefiles, include them at the end of the specific +`Makefile`, e.g. for a `Makefile.features`: + +```mk +CPU = foo +CPU_MODEL = foobar + +# Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_i2c +FEATURES_PROVIDED += periph_spi +FEATURES_PROVIDED += periph_uart + +include $(RIOTBOARD)/common/foo_common/Makefile.features +``` + +# Boards outside of RIOTBASE {#boards-outside-of-riotbase} + +All `BOARD`s in RIOT reside in `RIOTBOARD` (`RIOTBOARD` being a make variable +set to `$(RIOTBOARD)/boards`). + +If one wants to use a `BOARD` outside of `RIOTBOARD`, the way to go is setting +the `BOARDSDIR` variable to the path to the directory containing your external +boards, e.g.: `BOARDSDIR=/home/external-boards/` (this would commonly be done +in your application `Makefile` or your environment). + +``` +/home/ +|----RIOT/ + |---- ... +|----external-boards/ + |----board-foo/ + |----dist/ + |----scripts + |----board.c + |----doc.txt + |----include/ + |----periph_conf.h + |----board.h + |----gpio_params.h + |----Makefile + |----Makefile.dep + |----Makefile.features + |----Makefile.include +``` + +If the external `BOARD` is very similar to a `BOARD` already present in +`RIOTBOARD`, the external `BOARD` (`board-foo`) can inherit from that +parent `BOARD` (e.g: `foo-parent`). + +In this case some special considerations must be taken with the makefiles: + +- `Makefile` + - `MODULE` cannot be `board`: `foo-parent` will already define + `MODULE = board`, so use any other name, lets say `MODULE = board-foo`. + - Include the location of the parent `BOARD` to inherit from (if there is + one): + +```mk + DIRS += $(RIOTBOARD)/foo-parent +``` + +- `Makefile.include` + - duplicate the include done by `$(RIOTBASE)/Makefile.include` to also + include the parent board header. e.g: if inheriting from `foo-parent` + ``INCLUDES += $(addprefix -I,$(wildcard $(RIOTBOARD)/foo-parent/include))` + +- `Makefile.dep`: `board` is added by default to `USEMODULE` but since + `board-foo` is used for this `BOARD`, it must be explicitly included by adding + `USEMODULE += board-foo`. + +- Then simply include in each `Makefile.*` the corresponding parent `BOARD` + `Makefile.*`, just as it is done for common `BOARD` code (as explained in + @ref common-board-code). e.g: + `include $(RIOTBOARD)/foo-parent/Makefile.*include*` + +An example can be found in +[`tests/external_board_native`](https://github.com/RIOT-OS/RIOT/tree/master/tests/external_board_native`) + +# Tools {#boards-tools} + +Some scripts and tools available to ease `BOARD` porting and testing: + + - Run `dist/tools/insufficient_memory/add_insufficient_memory_board.sh ` + if your board has little memory. This updates the `Makefile.ci` lists to + exclude the `BOARD` from automated compile-tests of applications that do + not fit on the `BOARD`s `CPU`. + + - Run `dist/tools/compile_and_test_for_board/compile_and_test_for_board.py . --with-test-only` + to run all automated tests on the new board.