Zephyr: Thoughts and First Steps
This Zephyr walkthrough covers its installation, debugging, and limitations, and discusses where the product fits into the embedded development ecosystem.
Join the DZone community and get the full member experience.
Join For FreeThe concept of Linux (open source, broad developer base, and broad usage) is a success story. While there is a lot of diversity (and freedom) in the Linux world, Linux is Linux and again Linux. And the world has (mostly) standardized on Linux and its variants on the high embedded system side.
On the other side, the ‘middle and lower end’ embedded world is fragmented and, in many aspects, proprietary. So it was no surprise to me when the Linux Foundation announced the ‘Zephyr’ project back in February 2016:
“The Linux Foundation Announces Project to Build Real-Time Operating System for Internet of Things Devices. Open source Zephyr Project aims to deliver an RTOS; opens call for developers to help advance project for the smallest footprint IoT devices.“
Ζεφυρος (Zephyros) is the Greek god of spring and the west wind. Obviously, this inspired the logo for the Zephyr project:
The Zephyr Project
Nearly one year later, the dust has settled. I have monitored the project, and I believe it is finally in a stage at least to get some hands-on use. To me, the Zephyr project made many companies nervous, probably just because it looked like ‘Linux is taking over the embedded world,’ and many thought that this is ‘Linux for Embedded’. Well, that’s not true: Zephyr is NOT Linux. It is a completely different technology and kernel. It is only ‘hosted’ and promoted by the Linux Foundation. I see the Linux Foundation is ‘technically’ a non-profit organization, but to me, in reality, it's an ‘industry trade group,’ and that has already caused disputes.
From Wikipedia, Zephyr has its roots in Wind River Systems' ‘Rocket’ operating system. Actually, Rocket did not exist until Nov. 15 — it is rebranded from the ‘Virtuoso’ DSP operating system Wind River acquired many years ago. That explains the many Wind River copyrights and source comments. Information in cached websites indicates that ‘Rocket’ would be the commercial version of Zephyr. A good read about Rocket is here.
The other interesting point is that Zephyr is positioned as the “real-time operating system for Internet of Things devices.” Really? To me, it's another case where the term ‘IoT’ is used for something that, while it could be used for that kind of application, it actually has not very much to do with IoT (or at least how I understand it). How many press releases in 2016 had ‘IoT’ or ‘Industry 4.0’ as buzzwords on them?
Putting all the Zephyr marketing and buzzwords aside: To me, Zephyr aims to build a kernel and drivers for mid-range to low-end embedded devices, using the established principles of the Linux world, as an open source project. And that’s a good thing!
One of the ‘supported’ boards of Zephyr is the NXP FRDM-K64F board. As I have this board available, I have documented the steps to build and debug a first Zephyr project. I’m assuming you are familiar with the FRDM-K64F board (otherwise, check my articles about that board here) and standard tools like GNU (GDB, GCC) for ARM with Eclipse (e.g. Kinetis Design Studio) and Git.
I’m going through the steps to install the Zephyr on a host (Windows), building the kernel and application, and then debugging it with GDB and Eclipse.
Zephyr Kernel v1.6.0 and BLE
What held me back from using Zephyr until kernel v1.6.0 was that they released lots of things that were not stable. Additionally, the API has confused me with that dual ‘nano’ and ‘micro’ kernel. Now the kernel is unified and makes a lot more sense. And finally other cores like the ARM Cortex-M0(+) has been added.
https://www.zephyrproject.org/doc/LICENSING.html has the details about the licensing used for Zephyr. The kernel uses the permissible Apache 2.0 license, and drivers from Nordic and NXP the permissible BSD-Clause-3 version. Only some of the tools to build the kernel are with GPLv2, but this should not be a concern as these are usually not part of an embedded product.
Zephyr Sources
You get the latest package (v1.6.0 at this time) from https://www.zephyrproject.org/downloads or clone it from Git:
git clone https://gerrit.zephyrproject.org/r/zephyr zephyr-project
Do *not* use a path/destination folder with spaces. I did that in the first place, and this caused all kind of problems!
I have used “C:\nxp\zephyr-project” as my installation folder for the Zephyr sources.
Then switch to the desired release (v1.6.0 in my case):
cd zephyr-project
git checkout tags/v1.6.0
I followed the tool installation section on https://www.zephyrproject.org/doc/getting_started/getting_started.html. In my case I’m using a Windows host, but Linux and Mac is possible too. The setup needs tools which where mostly already installed on my system:
- Linux or mingw for Windows
- Git for dealing with the repository
- Python for scripting
- POSIX Threads (Pthread) which is used in the build system
- GNU Regex library
- The toolchain for cross compilation (GNU ARM Embedded Toolchain) or the one in Kinetis Design Studio V3.2.0)
Git, mingw, Python Pthread, and GNU Regex installation are pretty straightforward. The toolchain installation is misleading as it leads to Intel tool installation which is only usable for developing for Intel Zephyr platforms. Instead, it is much easier to use the Kinetis Design Studio V3.2.0 with an updated GNU toolchain, as it comes with everything necessary for ARM Cortex-M0/4/7 development and debugging.
The original toolchain (4.8) in KDS V3.2.0 does not work with the Zephyr 1.6.0, because the linker scripts are using ALIGN_WITH_INPUT which is not supported in 4.8. For switching toolchains, see https://mcuoneclipse.com/2014/07/11/switching-arm-gnu-tool-chain-and-libraries-in-kinetis-design-studio/
The following other steps are needed (with my settings as examples).
Using the mingw shell, set the ZEPHYR_BASE environment variable to point the Zephyr root folder:
export ZEPHYR_BASE=/c/nxp/zephyr-project
Next, the GCC variant used has to be specified which decides which makefile to use inside ZEPHYR_BASE/scripts folder. To use the ZEPHYR_BASE/scripts/Makefile.toolchain.gccarmemb I set the following variable:
export ZEPHYR_GCC_VARIANT=gccarmemb
Set the path to the ARM Embedded tool chain:
export GCCARMEMB_TOOLCHAIN_PATH=/c/nxp/KDS_3.2.0/Toolchain.5.3
This completes the installation and setup.
Hello World With NXP FRDM-K64F Board
The Zephyr supported board page lists several boards supported, but that’s actually very misleading: There are many more listed in the Zephyr Wiki. I wanted to run Zephyr on the NXP FRDM-K64F board (ARM Cortex-M4), and there is a Wiki page for it here: https://wiki.zephyrproject.org/view/NXP_FRDM-K64F. Because the information in that wiki is not complete, here is how I was able to build the ‘hello world’ sample project.
Go into the samples project:
cd $ZEPHYR_BASE/samples/hello_world
Then run the make file with:
make BOARD=frdm-k64f
If the make fails, check if you have set all the needed environment variables (see above), using the GNU ARM Embedded toolchain 5.3 or later.
If everything goes right, the output should look like this:
Erich Styger Local@ErichStyger-PC /c/nxp/zephyr-project/samples/hello_world
$ make BOARD=frdm_k64f
make[1]: Entering directory `/c/nxp/zephyr-project'
make[2]: Entering directory `/c/nxp/zephyr-project/samples/hello_world/outdir/frdm_k64f'
Using /c/nxp/zephyr-project as source for kernel
GEN ./Makefile
CHK include/generated/version.h
HOSTCC scripts/basic/fixdep
HOSTLD scripts/gen_idt/gen_idt
HOSTLD scripts/gen_offset_header/gen_offset_header
CHK misc/generated/configs.c
CHK include/generated/offsets.h
CHK misc/generated/sysgen/prj.mdef
CC lib/libc/minimal/source/stdout/fprintf.o
CC lib/libc/minimal/source/stdout/prf.o
CC lib/libc/minimal/source/stdout/sprintf.o
CC lib/libc/minimal/source/stdout/stdout_console.o
LD lib/libc/minimal/source/stdout/built-in.o
CC lib/libc/minimal/source/string/string.o
LD lib/libc/minimal/source/string/built-in.o
LD lib/libc/minimal/source/built-in.o
LD lib/libc/minimal/built-in.o
LD lib/libc/built-in.o
LD lib/built-in.o
CC misc/printk.o
CC misc/generated/configs.o
CC misc/generated/sysgen/kernel_main.o
LD misc/generated/sysgen/built-in.o
LD misc/generated/built-in.o
LD misc/built-in.o
CC ext/hal/ksdk/devices/MK64F12/fsl_clock.o
LD ext/hal/ksdk/built-in.o
LD ext/hal/built-in.o
LD ext/built-in.o
AS arch/arm/core/exc_exit.o
CC arch/arm/core/irq_init.o
AS arch/arm/core/swap.o
CC arch/arm/core/fault.o
CC arch/arm/core/irq_manage.o
CC arch/arm/core/thread.o
AS arch/arm/core/cpu_idle.o
AS arch/arm/core/fault_s.o
AS arch/arm/core/isr_wrapper.o
CC arch/arm/core/fatal.o
CC arch/arm/core/sys_fatal_error_handler.o
CC arch/arm/core/thread_abort.o
AS arch/arm/core/cortex_m/vector_table.o
AS arch/arm/core/cortex_m/reset.o
AS arch/arm/core/cortex_m/nmi_on_reset.o
CC arch/arm/core/cortex_m/prep_c.o
CC arch/arm/core/cortex_m/scs.o
CC arch/arm/core/cortex_m/scb.o
CC arch/arm/core/cortex_m/nmi.o
CC arch/arm/core/cortex_m/exc_manage.o
CC arch/arm/core/cortex_m/irq_vector_table.o
AS arch/arm/core/cortex_m/sw_isr_table.o
LD arch/arm/core/cortex_m/built-in.o
LD arch/arm/core/built-in.o
CC arch/arm/soc/nxp_kinetis/k6x/soc_config.o
CC arch/arm/soc/nxp_kinetis/k6x/soc.o
AS arch/arm/soc/nxp_kinetis/k6x/wdog.o
LD arch/arm/soc/nxp_kinetis/k6x/built-in.o
LD arch/arm/soc/nxp_kinetis/built-in.o
LD arch/arm/built-in.o
LD arch/built-in.o
CC drivers/console/uart_console.o
LD drivers/console/built-in.o
CC drivers/gpio/gpio_k64.o
LD drivers/gpio/built-in.o
CC drivers/pinmux/k64/pinmux.o
CC drivers/pinmux/k64/pinmux_board_frdm_k64f.o
LD drivers/pinmux/built-in.o
CC drivers/serial/uart_k20.o
LD drivers/serial/built-in.o
CC drivers/timer/cortex_m_systick.o
CC drivers/timer/sys_clock_init.o
LD drivers/timer/built-in.o
LD drivers/built-in.o
CC kernel/unified/version.o
LD kernel/unified/built-in.o
CC kernel/unified/alert.o
CC kernel/unified/device.o
CC kernel/unified/errno.o
CC kernel/unified/fifo.o
CC kernel/unified/idle.o
CC kernel/unified/init.o
CC kernel/unified/legacy_offload.o
CC kernel/unified/legacy_timer.o
CC kernel/unified/lifo.o
CC kernel/unified/mailbox.o
CC kernel/unified/mem_pool.o
CC kernel/unified/mem_slab.o
CC kernel/unified/msg_q.o
CC kernel/unified/mutex.o
CC kernel/unified/pipes.o
CC kernel/unified/sched.o
CC kernel/unified/sem.o
CC kernel/unified/stack.o
CC kernel/unified/sys_clock.o
CC kernel/unified/system_work_q.o
CC kernel/unified/thread.o
CC kernel/unified/thread_abort.o
CC kernel/unified/timer.o
CC kernel/unified/work_q.o
AR kernel/unified/lib.a
CC src/main.o
LD src/built-in.o
AR libzephyr.a
LINK zephyr.lnk
BIN zephyr.bin
make[2]: Leaving directory `/c/nxp/zephyr-project/samples/hello_world/outdir/frdm_k64f'
make[1]: Leaving directory `/c/nxp/zephyr-project'
The ‘hello world’ code is very simple. You can find it inside the src/main.c, which uses a call to printk():
/*
* Copyright (c) 2012-2014 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zephyr.h>
#include <misc/printk.h>
void main(void)
{
printk("Hello World! %s\n", CONFIG_ARCH);
}
- Disable SWO (because the FRDM-K64F board does not have this).
- Use the binary (elf) I have created both for the symbols and for the executable.
With this, I’m debugging the Zephyr binary with GDB and Eclipse:
Hello World
Connect a terminal to the OpenSDA/Debug port of the FRDM-K64F with 115200 baud, and the output is, well, a ‘hello world’:
Checking the size of the binary gives:
$ size frdm_k64f/*.elf
text data bss dec hex filename
10354 400 7808 18562 4882 frdm_k64f/zephyr.elf
Which is not that bad. Building the same without the call to printk() gives:
$ size outdir/frdm_k64f/*.elf
text data bss dec hex filename
10314 400 7808 18522 485a outdir/frdm_k64f/zephyr.elf
So this means that the kernel alone needs around 10K FLASH and 8 KByte RAM. Somewhat comparable (well, somewhat higher) than what FreeRTOS or uCOS would need for this kind of thing.
I tried other demos (like the ‘blinky’, but according to their wiki, only the NVIC, Systick, and UART1 seems to be supported. That seems to explain why the FRDM-K64F board is not listed on the officially supported boards? I see that inside \drivers\gpio there are some parts implemented, but they seem not be functional yet, at least in v1.6.0, and the ‘blinky’ demo runs into a hard fault, so does not work out-of-the box (yet?).
Zephyr Kernel and Drivers
Looking at the kernel features, it provides the basic functions I would expect. An interesting point is that the kernel requires a 32-bit hardware clock, which means that the ARM 24bit SysTick cannot be used, at least not directly. Another interesting feature is that both cooperative and preemptive threads can be used at the same time.
The driver model looks reasonable, too, including a single driver for multiple device instances.
I had an example for the FRDM-K64F working with a few ‘gotchas.’ Things are, for sure, much more complicated than with a normal RTOS, like FreeRTOS. I kind of liked the build system, as it is closer to the way other systems like Linux are built. On the other hand, it is very complex, and if things are failing, it is not easy to find the problem. I had issues with make files, path settings, and tool setup, which usually do not exist with IDE-based development. Zephyr is still in its infancy and lacks the tools and utilities the other RTOSs have had for years, but they might be added over time to Zephyr.
What is still lacking is that ‘IoT’ promised functionality: The networking and connectivity functionality is very thin or non-existent yet, or only for specific boards. There are other operating systems, like Contiki, which, to me, are better for networking or IoT devices and low-power applications. I was especially looking for BLE (Bluetooth Low Energy) support: The release notes mention NXP Hexiwear support (that’s a different research project I’m working on), but the BLE is only working in HCI mode with a binary blob, so now Nordic Semiconductor devices seem to be better supported (but I have not checked out the details and implementation). And the Wind River Helix Coud, which might be the catch behind Zephyr? And maybe that’s what's meant to be the ‘IoT’ behind Zephyr?
It looks Zephyr still has a long way to go, but the direction *could* be promising (considering all the marketing removed). Right now, it is more like an incomplete FreeRTOS copy with some drivers. It seems that things have been originated by a contribution of Wind River, which was picked up by the Linux Foundation.
To me, it is yet another small real-time operating system, and, at least how it is now, others like FreeRTOS or Micrium's uCOS are better positioned. But this might change over time if Zephyr can truly cross the gap between the ARM and Intel world, providing a single RTOS and driver model. So from this perspective, Zephyr can be the weapon of Intel against ARM with the help of a ‘Linux’ branding (*not* the Linux functionality).
From my perspective, at least Zephyr has a lot more potential than ARM’s mbed and RTOS. It will be interesting to see what happens with mbed and the mbed RTOS going forward, or even with the ARM CMSIS. While CMSIS-Core is part of the low-level Zephyr kernel and drivers, that part might get obsolete from a user perspective if Zephyr supports more and more boards and devices. Zephyr *could* get the standard and even getting a thread to the silicon vendors, but only ‘could’ if it will be a success. I think this will take some time if it ever succeeds.
I’m really not sure how Intel has convinced the Linux Foundation to promote Zephyr. If someone is looking for an operating system with Linux heritage, then it would something like NuttX or eCos in my view. On the other hand: more free and open source choices are good.
It is probably too simple to stick Zephyr into the ‘Rocket-WindRiver-Intel-against-ARM’ equation and Intel needs to have something smaller than Linux or VxWorks to compete in the microcontroller market. But there is definitely a need for something ‘heavier’ than FreeRTOS and ‘lighter’ than Linux. Already Micrium/uCOS has been acquired Silicon Labs. What if something like this happens with Real Time Engineers Ltd./FreeRTOS? Zephyr might then get enough momentum.
In any case: Zephyr is like free candy (no cake yet), and I can take my pick.
Happy Zephyring!
Links
- LinuxGizmos article: http://linuxgizmos.com/zephyr-a-tiny-open-source-iot-rtos/
- LinuxGizmos article: http://linuxgizmos.com/wind-river-launches-helix-cloud-iot-platform-with-rocket-rtos-and-pulsar-linux/
Published at DZone with permission of Erich Styger, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments