The Zephyr Project emerged on the embedded systems scene in 2016 as a new real-time operating system (RTOS) for embedded and IoT devices. It's a collaborative effort led by the Linux Foundation, along with Wind River, Synopsys, and NXP. Their goal was to create a multi-platform RTOS that leverages proven concepts from the Linux operating system, such as kconfig, device trees, and the driver model.
Leveraging many technologies present in the Linux kernel, and with its well-established multi-platform support, Zephyr OS has earned the informal title of "the Linux of microcontrollers." Zephyr OS's primary goal is to achieve true multi-platform support across multiple boards and architectures. This is made possible by the device tree hardware definition mechanism, which allows a board, platform, and other features to be defined separately from the main code.Three key technologies introduced by Zephyr OS play a crucial role in the embedded scenario:
The Device Tree (DT) emerged in the Linux kernel to address the challenge of managing diverse hardware configurations, particularly for older PowerPC and SPARC processors. Originally developed by the Open Firmware group, DT has become a powerful tool for tackling the complexities of supporting a wide range of ARM platforms and other embedded architectures.
Here's why DT is so effective for portability:
Hardware Description: DT acts as a blueprint for the hardware on a system. It describes components like CPUs, memory, peripherals, and their interconnections in a standardized format. This allows the kernel to understand and utilize the hardware regardless of the specific board or architecture.
Separation of Concerns: DT separates hardware configuration from the core kernel code. This means the kernel itself remains generic, while board-specific details reside in the DT file. This promotes code reuse and simplifies porting the kernel to new platforms.
Flexibility: DT is flexible enough to accommodate a wide range of hardware configurations. New features and devices can be integrated by extending the DT syntax, making it adaptable to evolving hardware landscapes.
Benefits of Device Tree:
Reduced Development Time: By separating hardware details from the kernel, DT allows developers to focus on core functionality rather than board-specific configurations. This reduces development time and effort required for porting the kernel to new platforms.
Improved Maintainability: Since hardware details reside in a separate file, the kernel code remains cleaner and easier to maintain. Updates or changes to a specific board only affect its DT file, not the core code.
Enhanced Scalability: The flexibility of DT allows it to scale to accommodate increasingly complex hardware configurations with diverse components and features.
In essence, Device Tree acts as a bridge between the hardware and the operating system, promoting portability, maintainability, and efficient development for embedded systems.
Unlike Linux, Zephyr OS utilizes a space-efficient approach for handling the Device Tree. Instead of a binary representation, Zephyr parses the DT and generates C header structures with defines and constants. This eliminates the overhead associated with storing and processing a separate binary DT file.
In the code, the device drivers, kernel and subsystems (normally a piece of C code) can access this information at compile time.
From the Zephyr OS device tree documentation:
Linux developers familiar with device tree should be warned that the API described here differs significantly from how device tree is used on Linux.
Instead of generating a C header with all the device tree data which is then abstracted behind a macro API, the Linux kernel would instead read the devicetree data structure in its binary form. The binary representation is parsed at runtime, for example to load and initialize device drivers.
Zephyr does not work this way because the size of the device tree binary and associated handling code would be too large to fit comfortably on the relatively constrained devices Zephyr supports.
The C API to access the device tree information is based on macros and inline functions
By example, this snippet of device tree code can be accessed with this general macros:
You can assign global variables (preferably static)
Or directly defining more macros referring to specific properties:
Every node on device tree is a “struct device*” pointer.
While a detailed explanation of Zephyr's device tree approach is available in the official documentation, we'll focus on a high-level overview for now.
Next Steps: Device Tree Bindings
Device tree bindings are a crucial tool for Zephyr OS. These bindings define the metadata associated with specific drivers or "compatible" devices. They essentially act as a bridge between the generic device tree and driver code, providing additional information specific to each hardware component.
For example, when you describe a device node
The “compatible” property describes a binding on the driver list that can handle this device.
Each device driver (compatible) requires zero or more information to initialize and configure the device correctly. To document and check the correct syntax of every the device tree node, comes to the rescue the device tree bindings information, that is a collection of YAML text files that describe the “compatible” properties list and a help of this driver (not necessary but strongly recommended)
For example, the “bar-device” with compatible “foo-company, bar-device” need to define this piece of binding in a YAML file:
This file indicates that the driver requires the property “num-foos” of type int to operate.
This schema provides a error check phase on compilation and generate more readable report than the dynamic loaded device tree approach used in the Linux Kernel
One of the most distinctive elements of the linux kernel is the “menuconfig” command which enables the user to configure various drivers or configuration options on the linux kernel:
The widespread adoption of kconfig extends beyond the Linux kernel. Projects like Buildroot, BusyBox, U-Boot, OpenWrt, and more recently, Apache NuttX and other RTOSes, have all embraced its power.
Zephyr leverages a custom-developed version of kconfig called kconfiglib. Written in Python, kconfiglib maintains 100% compatibility with the Linux kernel's kconfig language. However, it expands upon the original by offering a more user-friendly interface, comprehensive command-line tools, and language extensions tailored to Zephyr's specific needs.
In essence, kconfig is a specialized language that presents configuration options in a hierarchical, menu-like structure.
Kconfig utilizes a familiar menuconfig-style graphical user interface (GUI). This interface allows users to interactively select configuration options and ultimately save them to a Makefile-compatible file named ".config" in the Zephyr project's root directory.
The selected configuration options are preprocessed and compiled into a header file (*.h) accessible throughout the Zephyr codebase.
These defines are used throughout the Zephyr code to control various aspects, including enabling/disabling code sections, influencing code behavior, and defining constants.
Similar to the Linux kernel's kconfig system, Zephyr utilizes kconfig for configuration management. However, Zephyr leverages the CMake build system to provide functions specifically designed for checking the definition of symbols within the kconfig configuration.
add_subdirectory_ifdef(CONFIG_MYMODULE ../mymodule/src mymodule)
The final key technology in Zephyr's arsenal is the West command-line tool. West streamlines project initialization, building, and maintenance tasks in a centralized and well-documented manner.While not mandatory, West offers a significant advantage: it provides a platform-independent, central hub for managing these processes. This addresses limitations inherent to Git submodules, which can become cumbersome for large projects.West revolves around the concept of a workspace. This directory serves as a central location, housing not only the Zephyr source code repository but also all the necessary external modules. These modules can include hardware abstraction layers (HALs), libraries, and subprojects specific to particular architectures or code combinations (e.g., mbedTLS library, LoRa/LoRaWAN subsystem, MCUboot bootloader).
The heart of a West workspace lies within the .west directory. This directory houses a crucial configuration file that defines workspace variables. These variables can include the Zephyr source directory path, board name, build project directory, and any other settings relevant to West's operation.
Another key file within the workspace is the west.yml file, also known as the manifest. This file acts as a blueprint for your entire workspace's dependencies. It allows you to specify projects to be cloned, along with their corresponding repositories and versions. Additionally, the manifest can manage other vital information, such as including dependencies from other West manifests.
Back to west meta tool, you can use this to initialize a workspace, update all subprojects selected in manifest, build the project or even flash and debug it if the board support page define a method of flashing and/or debugging:
Init new workspace:
Update an initiate workspace cloning all sub projects listed on manifest
Build the project in “path/to/source/directory” for <BOARD> platform:
Flash recently build project to board (require that BSP define an upload method)
When you type
The debugger is started and GDB is opened and connected to the target (if the board support package, BSP, provides a method for it).
West offers a wide range of commands and can be extended by users through custom scripts tailored to specific architectures, SoCs, or board levels. However, a key concept behind West is its optionality. All tasks performed by West can also be executed directly using the corresponding tools within your environment, such as make/ninja, CMake, OpenOCD, GDB, command-line tools, and others.
The Zephyr OS irrupts in embedded scenario from the hands of heavy players on the market like intel, NXP or nordic semiconductor, has great development talents in the core development team and the free-libre and open source nature of this develop provides an excellent ecosystem centered in stability, maintainability and security making this RTOS a first class citizen for various platforms (like imxrt, nordic or intel audio dsp)
In this article we have attempted to show the key concepts that make zephyr OS the better option in the more-than-a-task-scheduler RTOS market and why you should seriously think about using it for your next project.
Spearheaded by industry leaders like Intel, NXP, and Nordic Semiconductor, Zephyr OS has emerged as a powerful force in the embedded systems landscape. Its core development team boasts exceptional talent, and its free, libre, and open-source (FLOSS) nature fosters a robust ecosystem focused on stability, maintainability, and security. This combination positions Zephyr OS as a first-class citizen for various platforms, including NXP i.MX RT, Nordic devices, and Intel audio DSPs.
This article has explored the key concepts that elevate Zephyr OS beyond a simple task scheduler, placing it squarely in the "more-than-a-task-scheduler" RTOS market. With its strengths highlighted, we encourage you to seriously consider Zephyr OS for your next embedded project.
See you later!
Embedded Linux Developer at Emtech S.A
Any Comments or questions, please feel free to contact us: info@emtech.com.ar