Zephyr is an open-source real-time operating system (RTOS) designed specifically for embedded systems. It boasts several features, including scalability, modularity, and portability, many of which are made possible by its devicetree. In this article, we’ll explore what the devicetree is, how it works, and why it is crucial to the Zephyr operating system.
What is the Devicetree in Zephyr?
The devicetree is a hierarchical data structure that describes the hardware components of a system, including microprocessors, memories, and peripherals. It is written in a text file format and is used as an input file for the Zephyr build system. The devicetree then provides the necessary information for the operating system to interact with the hardware through its drivers.
The devicetree is a fundamental component of the Zephyr operating system. By using the devicetree, developers can define the hardware components of a system without having to modify the source code, allowing them to reuse the same codebase across different hardware platforms and make changes to the hardware components as needed. The devicetree standard is maintained by the Devicetree.org community, which defines specifications and collaborates on the future evolution of the standard.
How Does the Devicetree Work in Zephyr?
Devicetree has two types of input files: devicetree sources and devicetree binding. Devicetree sources contain the devicetree itself, while the bindings describe its contents and data types.Once the devicetree source files have been written, they are passed to the Zephyr build system as input files. The build system then produces a C header file whose contents are abstracted by the Zephyr’s devicetree API in devicetree.h file, which enables it to be used by the operating system to interact with the hardware.
Simplified build process flow can be seen below:
Syntax, structure and how to write sources
The text format for the devicetree source files is simply called DTS (devicetree source) and is defined in the Devicetree specification (https://www.devicetree.org/).
The structure of the devicetree is defined using nodes, which represent the various hardware components of a system. Each node has properties that describe the properties of the corresponding hardware component, such as its name, address, or interrupt number. For more convenient use, nodes can be assigned node labels, unique identifiers that refer to the labeled node.
Here is a simple example of a DTS file:
There are 3 nodes visible in the example:
- a root node “/”
a node named “node1”, which is a child node of the root
a node “subnode1” , which is a child of the node “node1”
Also, you can see that the “subnode1” has been assigned the node label “subnode_label. Using this label we can refer to the node from anywhere in the devicetree and even from outside of the devicetree source file.
Another part of the devicetree that can be seen in the above example is the “prop” value, which is a property of the node subnode1. Properties have values that can be any sequence of bytes or an array of cells (which is a 32-bit unsigned integer). In the example above, a property named prop‘s value is a cell with value 2.
DTS in practice
In real-case scenarios, properties usually describe and configure the hardware which is represented by a node.
Below is an example of a DTS that describes real hardware with an RCC and pin controller peripheral:
Besides showing some real-case scenario node names and properties, the above example shows another devicetree component which is called a unit address. Unit addresses come after the “@” sign in the node names (58024400 in rcc@58024400). They represent a node’s address in the address space of the parent node.
In the above example, the node rcc@58024400 represents an RCC peripheral whose register map base address is 0x58024400. Every memory-mapped peripheral is represented in a similar way.
For the memory hardware components, DTS representation would look something like memory@2000000, which would describe RAM memory starting at physical address 0x2000000.
Second type of input files beside devicetree sources are devicetree bindings. These files contain declarations on the contents of nodes and provide information about the contents of valid nodes. Zephyr uses devictree bindings as YAML files in a custom format, contrary to Linux kernel’s use of the dt-schema tools.
Bindings and nodes are matched using the “compatible“ properties, which can be seen in the previous devicetree source example. For example, the gpioa node is matched with the “st,stm32-gpio” binding, so let’s take a look into this specific binding YAML file:
We can see that the compatible property “st,stm32-gpio” matches the compatible property of the gpioa node, therefore they are matched by the build system while generating devicetree output file.
The build system will check that the gpioa node has all the required properties (like reg and clocks),then it will generate various macros describing the node and place them in the output devicetree_generated.h file. Those macros are then accessible from the C code using the Devicetree API.
Why is the Devicetree Important?
The devicetree provides several key benefits for embedded systems developers. Here are a few of the most important reasons:
- Modularity: The devicetree is highly modular, making it easy to port Zephyr to different hardware platforms. This is because the devicetree provides a way to describe a wide range of hardware components, from simple microcontrollers to complex systems.
- Scalability: The devicetree is scalable, making it possible to use Zephyr in a wide range of embedded applications.
- Convenience: The devicetree provides a convenient way to configure the hardware components, making it easier to modify the hardware without having to change the source code. This can save time and reduce the complexity of the development process.
To summaryze, the devicetree is an essential component of the Zephyr operating system, providing a convenient and flexible way to describe and configure the hardware components of embedded systems. Whether you’re building an IoT device or a high-performance embedded system, the devicetree is a powerful tool that can simplify the development process and help you achieve your goals.