Zephyr: Thoughts and First Steps on the ARM Cortex-M4F with gcc, gdb and Eclipse

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 good of spring and the west wind. Obviously this inspired the logo for the Zephyr project:

Zephyr logo
Zephyr logo (Source: https://www.zephyrproject.org/)

The Zephyr Project

Nearly one year later, dust has settled. I have monitored the project, and I belive it is finally in a stage at least to get some hands-on. To me, the Zephyr project made many companies nervous, probably just because it looked like ‘Linux is taking over Embedded’, 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 a ‘industry trade group’ and that has already caused dispute.

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 (see https://en.wikipedia.org/wiki/Wind_River_Systems). That explains the many Wind River copyrights and source comments. Information in cached web sites indicate that ‘Rocket’ would be the commercial version of Zephyr. A good read about Rocket is here: http://www.eejournal.com/archives/articles/20151125-windriver/

The other interesting point is that Zephyr is positioned as the “Real-Time Operating System for Internet of Things Devices”. Really? To me another case where ‘IoT’ is used for something which could be used for that kind of application, but actually has not much to do with IoT (or at least how I understand it?). Or how many press releases in 2016 did not had the ‘IoT’ or ‘Industry 4.0’ as buzzwords on it 😉 ?

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! 🙂

Outline

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 the 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 a NXP FRDM-K64F Board

Zephyr Kernel v1.6.0 and BLE

What has held me back to use Zephyr until the kernel v1.6.0 (see https://www.zephyrproject.org/content/zephyr-kernel-v160-release-notes) has released was that lots of things 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.

Zephyr Licensing

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

Build Tools

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 straight forward. 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, 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);
}

Debugging

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 what I have to do is to

  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
Graphical 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 µC/OS would need for this kind of thing.

GPIO?

I tried other demos (like the ‘blinky’, but according to https://wiki.zephyrproject.org/view/NXP_FRDM-K64F 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?).

💡 UPDATE: As suspected, the blinky demo has been fixed after the v1.6.0 release. To have it working, use the mainline on git.

Zephyr Kernel and Drivers

Looking at the kernel features (see https://www.zephyrproject.org/doc/kernel/kernel.html), it provides the basic functions I would expect. An interesting point is that the kernel requires a 32bit 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 (https://www.zephyrproject.org/doc/drivers/drivers.html#), including single driver for multiple device instances.

Summary

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 more close 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 tools and utilities the other RTOS have 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 not existing 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 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 then has been picked up by the Linux Foundation.To me, it is ‘yet another small real-time operating’ system, and at least what it is now, others like FreeRTOS or Micriums µC/OS 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 :-).

Probably it is too simple to cut Zephyr to the ‘Rocket-WindRiver-Intel-against-ARM’ equation and the need of Intel 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 aquired 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

23 thoughts on “Zephyr: Thoughts and First Steps on the ARM Cortex-M4F with gcc, gdb and Eclipse

  1. Have you tried Apache MyNewt OS yet? So far, it exceeded all my expectations. It’s also supports FRDM-K64F. But if you are interested in BLE, nRF52 is the way to go…

    Like

  2. Hmmm. Very interesting. I took a look at Zephyr a couple of months ago and I got the impression that it was a lot more advanced than your experience — maybe I was suckered in by the excellent online documentation?

    I was particularly interested in some of the USB drivers, and networking protocols (such as CoAp).

    Seems like your hands on experience shows that Zephyr might be more hype than substance at this stage.

    BTW, I found out of Zephyr via some commits for Micropython (runs on 32-bit embedded systems, like STM32F4, etc. You should try MicroPython on one of your boards. An SD card is recommended for a filesystem, though you can use internal flash too (if you have a reasonable size device).

    Like

  3. I think this is good progress towards standardizing systems in the low-mid range, but I am more interested in the progress that Liviu Ionescu is making with his CMSIS++ and uOS++. Making a more object-oriented RTOS implementation for ARM and other micros can only be a good thing…

    Like

    • not sure if this really is helping towards standardizing. It remindes me more at Standards.
      Not sure if C++ for a kernel is the right approach: it certainly could benefit from this, but not necessary in my view. It would be better to have good drivers in C++ instead?

      Liked by 1 person

  4. Gosh thanks for the info and the links to mynewt.
    I’ve been monitoring Zephyr for sometime, and I talked to some Zephyr guys at the Arm Nov2016 conf and drivers, and wasn’t impressed with the bare OS approach – thought of course I imagine they are really interested in contacts from large organizations.
    From my experience, Intel has a history of talking big, to attract attention and sniff for large projects/partners I guess, and so anything Intel says needs a reality calibration.
    Thanks Eric for putting up the project. It does seem to be behind what nuttx.org can deliver today with networking drivers, though code is available its only a small group developing drivers with Nuttx
    This may be changing with
    Sony: DevelopingAudioProductsWithCortexM3 Nuttx Thursday, February 23
    http://events.linuxfoundation.org/events/embedded-linux-conference/program/schedule

    Click to access DevelopingAudioProductsWithCortexM3NuttXC%2B%2B11_LFELC_OpenIoT_ishikawa_20161209_0.pdf

    I like the look of mynewt and BLE so will check that out.

    Liked by 1 person

  5. Erich,

    Above you say “So this means that the kernel alone needs around 10K FLASH and 8 KByte RAM. Somewhat comparable (well, somewhat higher) than what FreeRTOS or µC/OS would need for this kind of thing.”

    I built hello_world with and without the printk… here is what I got (this was with 1.7 which I assume is a bit tighter than 1.6)

    text data bss dec hex filename
    10110 356 3712 14178 3762 outdir/frdm_k64f/zephyr.elf

    text data bss dec hex filename
    10150 356 3712 14218 378a outdir/frdm_k64f/zephyr.elf

    But I would observe that my version of the blinky is
    text data bss dec hex filename
    7050 296 4212 11558 2d26 outdir/frdm_k64f/zephyr.elf

    If you change the prj.cnf to add the serial port (CONFIG_SERIAL=y) you end up with this
    size outdir/frdm_k64f/*.elf
    text data bss dec hex filename
    10586 356 4212 15154 3b32 outdir/frdm_k64f/zephyr.elf

    It looks like there is something like 3K in whatever gets linked in with the serial stuff.

    Do you count the serial stuff (probably including the printk but I didnt look) as part of the Kernel? If no then it looks like something basic is more like 7K of flash than 10K

    Alan

    Like

    • Hi Alan,
      I have not checked the details, but it really looks like the serial driver needs around 3K, probably for some printf() like functions (which I don’t like anyway, see https://mcuoneclipse.com/2013/04/19/why-i-dont-like-printf/ 🙂 ).
      I still think that Zyphyr is somewhat bloated compared to say a ‘blinky’ with FreeRTOS. I just tried a normal ‘blinky’ with FreeRTOS on a ARM Cortex-M4 (IDLE task plus a LED blinking task):
      Invoking: Cross ARM GNU Print Size
      arm-none-eabi-size --format=berkeley "tinyK20_freertos_blinky.elf"
      text data bss dec hex filename
      3920 648 488 5056 13c0 tinyK20_freertos_blinky.elf

      That application only does the blinking, but shows that the RTOS with one extra task fits into less than 5K of Flash and 1K of RAM. So about half of what Zephyr needs as a minimal configuration.

      Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.