Written by Rafael Dias Menezes, Embedded System Engineer
A few years ago, I bought a ARM Cortex-M4F board from SiLabs with a Pearl Gecko microcontroller. At the time, I did some implementations using the FreeRTOS project. Last year, I was introduced to Zephyr and since then I have been using it with Nordic Semiconductors’ processors.
The company that I currently work for has a diverse development team with Electronic Engineers with different seniority levels. One of them was interested in RTOS development and asked me for recommendations about RTOS variants available for use. After several discussions, we decided to create a pet-project using the development kits available at the office. We are using SiLabs in some of our projects, so I decided to see if there was support for Giant Gecko microcontrollers in the Zephyr Project. To my surprise, there is support, but not for the development kit we were using. Because of that, I decided to try to port the Zephyr to the SLSTK4301A board.
The SLSTK4301A board has the following features:
- EFM32PG1B200F256GM48 MCU with 256 kB Flash and 32 kB RAM.
- Advanced Energy Monitoring system for precise current tracking.
- Integrated Segger J-Link USB debugger/emulator with the possibility to debug external Silicon Labs devices.
- Silicon Labs Si7021 Relative Humidity and Temperature Sensor
- Ultra low power 128×128 pixel Memory-LCD
- 2 push buttons and 2 LEDs connected to EFM32 for user interaction.
- Crystals for LFXO and HFXO: 32.768 kHz and 40.000 MHz.
Here are the steps I took to do this:
First steps
The first step that we have to do is to install the Zephyr on your machine. If you haven’t done this yet, Zephyr has a very good step-by-step guide to help you do this. Click here Getting Started.
Let’s install Zephyr into the new directory called zephyr_blink_efm32 using the command sequence below:
west init zephyr_blink_efm32
cd zephyr_efm32
west update
west zephyr-export
Now, we need to create a new board directory. Following the current Silicon Labs boards supported by Zephyr examples, I decided to create a new board directory called stk3401a. At this point, we have to verify Zephyr has support for the microcontroller present at SLSTK4301A.
Adding EFM32PG1B support
Inspecting the directory ./modules/hal/silabs/gecko/Device/SiliconLabs/ I didn’t find the EFM32PG1B peripheral support library. So, I had to add the EFM32PG1B peripheral support library. The happy way to get these files is the following:
- Download and Install the SiLabs Simplicity Studio by following the link https://www.silabs.com/developers/simplicity-studio.
- Open the Simplicity Studio and go to Windows->Preference->SDKs. Open the Gecko SDK directory with your file manager.
- Copy the directory platform/Device/SiliconLabs/EFM32PG1B to ./modules/hal/silabs/gecko/Device/SiliconLabs/
- Open the file ./modules/hal/silabs/gecko/CMakeLists.txt and the following line:
zephyr_sources_ifdef(CONFIG_SOC_SERIES_EFM32PG1B Device/SiliconLabs/EFM32PG1B/Source/system_efm32pg1b.c)
Add the peripheral support library, we have to add the SoC files. I based my modifications on the efm32pg12b SoC files.
Now we’ll continue with our task of adding support to our development kit to Zephyr.
Create your board directory
Once you created the HAL support for our development kit, we need to create the board directory.
Start creating the board directory./zephyr/boards/arm/efm32pg_stk3401a. In my environment, I copied the content of the board efm32pg_stk3402a to our board directory. After rename some files, our board content should look like this:
.
│—board.c │—board.cmake │—board.h │—CMakeLists.txt │—efm32pg_stk3401a.dts │—efm32pg_stk3401a.yaml │—efm32pg_stk3401a_common.dtsi │—efm32pg_stk3401a_defconfig │—Kconfig │—Kconfig.board │—Kconfig.defconfig │ └───doc │—efm32pg_stk3401a.jpg │—index.rst |
Now a brief description of each file:
- dts file is the hardware description in devicetree format. This declares your SoC, connectors, and any other hardware components such as LEDs, buttons, sensors, or communication peripherals (USB, BLE controller, etc).
- dtsi devicetree hardware description that should be shared between several boards. Think of this file as a C include file.
- cmake used for flash and debug support;
- efm32pg_stk3401a_defconfig , Kconfig , board and Kconfig.defconfig are software configuration in Kconfig formats. This provides default settings for software features and peripheral drivers.
- txt if you need to add additional source files to your build.
- c and board.h implementation of board initialization code.
- yaml metadata file that describes the test coverage for this specific board. For more information, see twister.
Now we can modify our files to meet the board requirements. Let’s start with the devicetree file. After the modifications, my efm32pg_stk3401a.dts content should be:
/dts-v1/;
#include <silabs/efm32pg1b200f256gm48.dtsi> #include “efm32pg_stk3401a_common.dtsi”
/ { model = “Silicon Labs EFM32PG STK3401A board”; compatible = “silabs,efm32pg_stk3401a”, “silabs,efm32pg1b”; }; |
And efm32pg_stk3401a_common.dtsi content should be:
/ {
model = “Silicon Labs EFM32PG STK3401A board”;
chosen { zephyr,console = &usart0; zephyr,shell-uart = &usart0; zephyr,sram = &sram0; zephyr,flash = &flash0; watchdog0 = &wdog0; };
/* These aliases are provided for compatibility with samples */ aliases { led0 = &led0; led1 = &led1; sw0 = &button0; sw1 = &button1; };
leds { compatible = “gpio-leds”; led0: led_0 { gpios = <&gpiof 4 0>; label = “LED 0”; }; led1: led_1 { gpios = <&gpiof 5 0>; label = “LED 1”; }; };
buttons { compatible = “gpio-keys”; button0: button_0 { /* gpio flags need validation */ gpios = <&gpiof 6 GPIO_ACTIVE_LOW>; label = “User Push Button 0”; }; button1: button_1 { /* gpio flags need validation */ gpios = <&gpiof 7 GPIO_ACTIVE_LOW>; label = “User Push Button 1”; }; }; };
&cpu0 { clock-frequency = <40000000>; };
&usart0 { current-speed = <115200>; location-rx = <GECKO_LOCATION(0) GECKO_PORT_A GECKO_PIN(1)>; location-tx = <GECKO_LOCATION(0) GECKO_PORT_A GECKO_PIN(0)>; status = “okay”; };
&leuart0 { current-speed = <9600>; location-rx = <GECKO_LOCATION(18) GECKO_PORT_D GECKO_PIN(11)>; location-tx = <GECKO_LOCATION(18) GECKO_PORT_D GECKO_PIN(10)>; status = “okay”; };
&i2c0 { location-sda = <GECKO_LOCATION(15) GECKO_PORT_C GECKO_PIN(10)>; location-scl = <GECKO_LOCATION(15) GECKO_PORT_C GECKO_PIN(11)>; status = “okay”; };
&rtcc0 { prescaler = <1>; status = “okay”; };
&gpio { location-swo = <0>; status = “okay”; };
&gpioa { status = “okay”; };
&gpiob { status = “okay”; };
&gpioc { status = “okay”; };
&gpiod { status = “okay”; };
&gpiof { status = “okay”; };
&wdog0 { status = “okay”; };
&flash0 {
partitions { compatible = “fixed-partitions”; #address-cells = <1>; #size-cells = <1>;
/* Set 6Kb of storage at the end of the 256Kb of flash */ storage_partition: partition@fe800 { label = “storage”; reg = <0x0003e800 0x00001800>; };
}; }; |
We have to pay attention to these devicetree files. Every time that you want to create these files, always perform a double check at the microcontroller datasheet.
Now we have to adjust the remaining files. Take care about the interrupt sources, flash memory size and other small features of EFM32PG1B microcontroller family.
Build, test and fix
Now it is time to build, test and fix some issues in our port. Let’s compile a hello world in our board:
west build -b stk3401a samples/basic/blinky
west flash
I recorded a small video to show the hello world application running in my dev kit:
Deploy the new board to Zephyr repository
Since we already ported the new card, now it’s time to add it to Zephyr’s official repository. There is no mandatory step, it is only necessary if you want to contribute to the project.
The first step is related to the SiLabs HAL. As the hal is a module outside the zephyr core, we have to update the corresponding repository. Investigating the Zephyr repositories, we can find the repo hal_silabs. As we want to add our modifications to this repository, first we have to fork the repository to our github account, create a new development branch, add the changes performed at the Adding EFM32PG1B support step, commit, create a pull request and wait for approval. In the meantime, we can deploy our modifications to the ZephyrProject repository.
To deploy your modifications at the ZephyrProject you have to follow some recommendations that are enumerated here. If you don’t have experience with open source project contributions, follow the Contribution Workflow steps. Below I give some recommendations on how to make your commit pass in the ci system:
- Remove new lines at the end of your modified files;
- Add you as responsible to the boards/modules changed by editing the ./zephyr/CODEOWNERS file;
- Adapt the hal_silabs revision in ./zephyr/west.yaml so it pulls in the revision with your changes from https://github.com/zephyrproject-rtos/hal_silabs. In my case, I changed the yaml file to point to my last changes. This has to be done to pass the ci process and after your PR is approved, you must change this file again, just informing the commit uuid corresponding to your change. Bellow I show, as an example, the modifications that I did in my environment:
– name: hal_silabs
revision: pull/10/head path: modules/hal/silabs |
- The commits have to follow a template: <subject>: <family>: title , for example, from my last commit: boards: arm: Add support for SiLabs EFM32PG1B SLSTK3401A board
After all these steps we have to wait for approval of at least two reviewers.
Conclusion
I hope this article taught you a little bit about the porting process of Zephyr RTOS and got you thinking about how you could make this to a new microprocessor architecture, family or board.
Either way, I’m looking forward to hearing your thoughts! Connect with me on Twitter (@rdmeneze) or Github (https://github.com/rdmeneze).