Over a million developers have joined DZone.

Zephyr: Thoughts and First Steps

DZone's Guide to

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.

· IoT Zone ·
Free Resource

The 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:

Image title

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.

Debugging Zephyr on a NXP FRDM-K64F Board

Debugging Zephyr on an NXP FRDM-K64F Board

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!

Cloned Zephyr Git Repository

Cloned Zephyr Git repository

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

Switched to Zephyr Tagged Version

Switched to Zephyr Tagged Version

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:

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

Environment settings with mingw32

Environment settings with mingw32

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,
 * 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);

To debug the project, the simplest way is to create a ‘dummy’ project for the FRDM-K64F (MK64FN1M0xxx12). The screenshot below is for the Segger J-Link, but the same applies to the P&E Multilink. All I have to do is:
  1. Disable SWO (because the FRDM-K64F board does not have this).
  2. Use the binary (elf) I have created both for the symbols and for the executable.

Zephyr Debugger settings

Zephyr debugger settings

With this, I’m debugging the Zephyr binary with GDB and Eclipse:

Graphical Debugging Zephyr with Eclipse and GDB

Graphically debugging Zephyr with Eclipse and GDB

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’:

Hello World from Zephyr

Hello World from Zephyr

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!


linux ,iot analytics ,zephyr ,iot ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}