blob: a8c23499e2acc1f93f8495b43774e621c39dcc9c [file] [log] [blame]
.. _device-tree:
Device Tree
###########
.. contents::
:local:
:depth: 2
Introduction
************
Device tree is a way of describing hardware and configuration information
for boards. Device tree was adopted for use in the Linux kernel for the
PowerPC architecture. However, it is now in use for ARM and other
architectures.
The device tree is a data structure for dynamically describing hardware
using a Device Tree Source (DTS) data structure language, instead of
hard coding every detail of a board's hardware into the operating system.
In Linux, DTS is compiled into a compact Device Tree Blob (DTB) using a Device
Tree Compiler (DTC), then the hardware-describing DTB is passed to the operating
system at boot time. This allows the same compiled Linux kernel to support
different hardware configurations within an architecture family (e.g., ARM,
x86, PowerPC) and moves a significant part of the hardware description out of
the kernel binary itself.
For larger systems, the flexibility this offers offsets the extra runtime memory
overhead incurred. But the primary targets for Zephyr
applications are small micro-controller systems with limited memory
resources. So instead of requiring the additional runtime memory
to store the DTB blob and the code to parse it, the DTS information
is used at compile time.
Device tree uses a specific format to describe the device nodes in a system.
This format is described in the `Device Tree Specification`_.
.. _Device Tree Specification: https://github.com/devicetree-org/devicetree-specification/releases
More device tree information can be found at the `device tree repository`_.
.. _device tree repository: https://git.kernel.org/pub/scm/utils/dtc/dtc.git
System build requirements
*************************
The Zephyr device tree feature requires a device tree compiler (DTC) and Python
YAML packages. Refer to the installation guide for your specific host OS:
* :ref:`installing_zephyr_win`
* :ref:`installation_linux`
* :ref:`installing_zephyr_mac`
Zephyr and Device Tree
**********************
Device Tree provides a unified description of a hardware system used in an
application. It is used in Zephyr to describe hardware and provide a boot-time
configuration of this hardware.
In Zephyr, the device tree is also used to describe Zephyr-specific
configuration information. This Zephyr-specific information augments the device
tree descriptions and sits on top of it, rather than diverging from it. The
main reason for this is to leverage existing device tree files that a SoC vendor
may already have defined for a given platform.
The device tree files are compiled using the device tree compiler. The compiler
runs the .dts file through the C preprocessor to resolve any macro or #defines
utilized in the file. The output of the compile is another dts formatted file.
After compilation, a Python script extracts information from the compiled
device tree file using rules specified in *bindings* (see the :ref:`bindings`
section). The extracted information is placed in a header file that is used by
the rest of the code as the project is compiled.
Temporary fixup files are required for device tree support on most devices.
These fixup files by default reside in the board and soc directories and are
named ``dts_fixup.h``. These fixup files map the generated include information to
the current driver/source usage.
The Python code that deals with device tree and bindings is in
:zephyr_file:`scripts/dts/`.
.. _dt_vs_kconfig:
Device Tree vs Kconfig
**********************
As mentioned above there are several tools used to configure the build with
Zephyr.
The two main ones, Device Tree and Kconfig, might seem to overlap in purpose,
but in fact they do not. This section serves as a reference to help you decide
whether to place configuration items in Device Tree or Kconfig.
The scope of each configuration tool can be summarized as follows:
* Device Tree is used to describe the **hardware** and its **boot-time
configuration**.
* Kconfig is used to describe which **software features** will be built into
the final image, and their **configuration**.
Hence Device Tree deals mostly with hardware and Kconfig with software.
A couple of noteworthy exceptions are:
* Device Tree's ``chosen`` keyword, which allows the user to select a
particular instance of a hardware device to be used for a concrete purpose
by the software. An example of this is selecting a particular UART for use as
the system's console.
* Device Tree's ``status`` keyword, which allows the user to enable or disable
a particular instance of a hardware device in the Device Tree file itself.
This takes precedence over Kconfig.
To further clarify this separation, let's use a particular, well-known
example to illustrate this: serial ports a.k.a. UARTs. Let's consider a
board containing a SoC with 2 UART instances:
* The fact that the target hardware **contains** 2 UARTs is described with Device
Tree. This includes the UART type, its driver compatibility and certain
immutable (i.e. not software-configurable) settings such as the base address
of the hardware peripheral in memory or its interrupt line.
* Additionally, **hardware boot-time configuration** is also described with Device
Tree. This includes things such as the IRQ priority and boot-time UART
baudrate. These may also be modifiable at runtime later, but their boot-time
default configuration is described in Device Tree.
* The fact that the user intends to include **software support** for UART in the
build is described in Kconfig. Through the use of Kconfig, users can opt not
to include support for this particular hardware item if they don't require it.
Another example is a device with a 2.4GHz, multi-protocol radio supporting
both the Bluetooth Low Energy and 802.15.4 wireless technologies. In this case:
* Device Tree describes the presence of a radio peripheral compatible with a
certain radio driver.
* Additional hardware boot-time configuration settings may also be present
in the Device Tree files. In this particular case it could be a
default TX power in dBm if the hardware does have a simple, cross-wireless
technology register for that.
* Kconfig will describe which **protocol stack** is to be used with that radio.
The user may decide to select BLE or 802.15.4, which will both depend on
the presence of a compatible radio in the Device Tree files.
Device tree file formats
************************
Hardware and software is described inside of device tree files in clear text format.
These files have the file suffix of .dtsi or .dts. The .dtsi files are meant to
be included by other files. Typically for a given board you have some number of
.dtsi include files that pull in common device descriptions that are used across
a given SoC family.
Example: FRDM K64F Board and Hexiwear K64
=========================================
.. Give the filenames instead of the full paths below, as it's easier to read.
The cramped 'foo.dts<path>' style avoids extra spaces before commas.
These boards are defined in :zephyr_file:`frdm_k64fs.dts
<boards/arm/frdm_k64f/frdm_k64f.dts>` and :zephyr_file:`hexiwear_k64.dts
<boards/arm/hexiwear_k64/hexiwear_k64.dts>`. They are based on the same NXP
Kinetis SoC family, the K6X.
Common definitions for K6X are stored in :zephyr_file:`nxp_k6x.dtsi
<dts/arm/nxp/nxp_k6x.dtsi>`, which is included by both board :file:`.dts`
files. :zephyr_file:`nxp_k6x.dtsi<dts/arm/nxp/nxp_k6x.dtsi>` in turn includes
:zephyr_file:`armv7-m.dtsi<dts/arm/armv7-m.dtsi>`, which has common
definitions for ARMv7-M-based systems.
Since :zephyr_file:`nxp_k6x.dtsi<dts/arm/nxp/nxp_k6x.dtsi>` is meant to be
generic across K6X-based boards, it leaves many devices disabled by default.
For example, there is a CAN controller defined as follows (with unimportant
parts skipped):
.. code-block:: none
can0: can@40024000 {
...
status = "disabled";
...
};
It is up to the board :file:`.dts` files to enable devices (by setting
``status = "okay"``). The board :file:`.dts` files are also responsible for any
board-specific configuration of the device.
For example, FRDM K64 (but not Hexiwear K64) enables the CAN controller, also
setting the bus speed:
.. code-block:: none
&can0 {
status = "okay";
bus-speed = <125000>;
};
The ``&can0 { ... };`` syntax adds/overrides properties on the node with label
``can0``, i.e. the ``can@4002400`` node.
Other examples of board-specific customization is pointing properties in
``aliases`` and ``chosen`` to the right nodes (see :ref:`dt-alias-chosen`), and
making GPIO/PinMux assignments.
Currently supported boards
**************************
Device tree is currently supported on all embedded targets except posix
(boards/posix) and qemu_x86_64 (boards/x86_64).
Adding support for a board
**************************
Adding device tree support for a given board requires adding a number of files.
These files will contain the DTS information that describes a platform, the
YAML descriptions that define the contents of a given Device Tree peripheral
node, and also any fixup files required to support the platform.
When writing Device Tree Source files, it is good to separate out common
peripheral information that could be used across multiple SoC families or
boards. DTS files are identified by their file suffix. A .dtsi suffix denotes
a DTS file that is used as an include in another DTS file. A .dts suffix
denotes the primary DTS file for a given board.
The primary DTS file will contain at a minimum a version line, optional
includes, and the root node definition. The root node will contain a model and
compatible that denotes the unique board described by the .dts file.
Device Tree Source File Template
================================
.. code-block:: none
/dts-v1/;
/ {
model = "Model name for your board";
compatible = "compatible for your board";
/* rest of file */
};
One suggestion for starting from scratch on a platform/board is to examine other
boards and their device tree source files.
The following is a more precise list of required files:
* Base architecture support
* Add architecture-specific DTS directory, if not already present.
Example: dts/arm for ARM.
* Add target specific device tree files for base SoC. These should be
.dtsi files to be included in the board-specific device tree files.
* Add target specific YAML binding files in the dts/bindings/ directory.
Create the yaml directory if not present.
* SoC family support
* Add one or more SoC family .dtsi files that describe the hardware
for a set of devices. The file should contain all the relevant
nodes and base configuration that would be applicable to all boards
utilizing that SoC family.
* Add SoC family YAML binding files that describe the nodes present in the .dtsi file.
* Board specific support
* Add a board level .dts file that includes the SoC family .dtsi files
and enables the nodes required for that specific board.
* Board .dts file should specify the SRAM and FLASH devices, if present.
* Flash device node might specify flash partitions. For more details see
:ref:`flash_partitions`
* Add board-specific YAML binding files, if required. This would occur if the
board has additional hardware that is not covered by the SoC family
.dtsi/.yaml files.
* Fixup files
* Fixup files contain mappings from existing Kconfig options to the actual
underlying DTS derived configuration #defines. Fixup files are temporary
artifacts until additional DTS changes are made to make them unnecessary.
* Overlay Files (optional)
* Overlay files contain tweaks or changes to the SoC and Board support files
described above. They can be used to modify Device Tree configurations
without having to change the SoC and Board files. See
:ref:`application_dt` for more information on overlay files and the Zephyr
build system.
.. _dt-alias-chosen:
``aliases`` and ``chosen`` nodes
================================
Using an alias with a common name for a particular node makes it easier for you
to write board-independent source code. Device Tree ``aliases`` nodes are used
for this purpose, by mapping certain generic, commonly used names to specific
hardware resources:
.. code-block:: yaml
aliases {
led0 = &led0;
sw0 = &button0;
sw1 = &button1;
uart-0 = &uart0;
uart-1 = &uart1;
};
Certain software subsystems require a specific hardware resource to bind to in
order to function properly. Some of those subsystems are used with many
different boards, which makes using the Device Tree ``chosen`` nodes very
convenient. By doing, so the software subsystem can rely on having the specific
hardware peripheral assigned to it. In the following example we bind the shell
to ``uart1`` in this board:
.. code-block:: yaml
chosen {
zephyr,shell-uart = &uart1;
};
The full set of Zephyr-specific ``chosen`` nodes follows:
.. list-table::
:header-rows: 1
* - ``chosen`` node name
- Generated symbol
* - ``zephyr,flash``
- ``CONFIG_FLASH``
* - ``zephyr,sram``
- ``CONFIG_SRAM_SIZE``/``CONFIG_SRAM_BASE_ADDRESS``
(via ``DT_SRAM_SIZE``/``DT_SRAM_BASE_ADDRESS``)
* - ``zephyr,ccm``
- ``DT_CCM``
* - ``zephyr,console``
- ``DT_UART_CONSOLE_ON_DEV_NAME``
* - ``zephyr,shell-uart``
- ``DT_UART_SHELL_ON_DEV_NAME``
* - ``zephyr,bt-uart``
- ``DT_BT_UART_ON_DEV_NAME``
* - ``zephyr,uart-pipe``
- ``DT_UART_PIPE_ON_DEV_NAME``
* - ``zephyr,bt-mon-uart``
- ``DT_BT_MONITOR_ON_DEV_NAME``
* - ``zephyr,uart-mcumgr``
- ``DT_UART_MCUMGR_ON_DEV_NAME``
Adding support for device tree in drivers
*****************************************
As drivers and other source code is converted over to make use of device tree
generated information, these drivers may require changes to match the generated
#define information.
Source Tree Hierarchy
*********************
The device tree files are located in a couple of different directories. The
directory split is done based on architecture, and there is also a common
directory where architecture agnostic device tree and YAML binding files are
located.
Assuming the current working directory is the ZEPHYR_BASE, the directory
hierarchy looks like the following::
dts/common/
dts/<ARCH>/
dts/bindings/
boards/<ARCH>/<BOARD>/
The common directory contains a ``skeleton.dtsi`` which provides device tree root
node definition. The bindings subdirectory contains YAML binding files used
to instruct how the python DTS parsing script should extract nodes information
in a format that will be usable by the system.
Example: Subset of DTS/YAML files for NXP FRDM K64F (Subject to Change)::
dts/arm/armv7-m.dtsi
dts/arm/k6x/nxp_k6x.dtsi
boards/arm/frdm_k64f/frdm_k64f.dts
dts/bindings/interrupt-controller/arm,v7m-nvic.yaml
dts/bindings/gpio/nxp,kinetis-gpio.yaml
dts/bindings/pinctrl/nxp,kinetis-pinmux.yaml
dts/bindings/serial/nxp,kinetis-uart.yaml
.. _bindings:
Bindings
********
``.dts`` files describe the available hardware devices, but don't tell the
system which pieces of information are useful, or what kind of configuration
output (``#define``'s) should be generated. *Bindings* provide this
information. Bindings are files in YAML format.
Configuration output is only generated for devices that have bindings.
Nodes are mapped to bindings via their ``compatible`` string(s). Take
the following node as an example:
.. code-block:: none
bar-device {
compatible = "foo-company,bar-device";
...
};
This node would get mapped to a binding with this in it:
.. code-block:: yaml
...
properties:
compatible:
constraint: "foo-company,bar-device"
...
Bindings are stored in :zephyr_file:`dts/bindings/`. The filename usually
matches the ``compatible`` string.
If a node has more than one ``compatible`` string, then the first binding found
is used, going from the first string to the last. For example, a node with
``compatible = "foo-company,bar-device", "generic-bar-device"`` would get
mapped to the binding for ``generic-bar-device`` if there is no binding for
``foo-company,bar-device``.
If a node appears on a bus (e.g. I2C or SPI), then the bus type is also taken
into account when mapping nodes to bindings. See the description of ``parent``
and ``child`` in the template below.
Below is a template that shows the format of binding files, stored in
:zephyr_file:`dts/bindings/binding-template.yaml`.
.. literalinclude:: ../../../dts/bindings/binding-template.yaml
:language: yaml
Include files generation
************************
At build time, after a board's ``.dts`` file has been processed by the DTC
(Device Tree Compiler), a corresponding ``.dts_compiled`` file is generated
under the ``zephyr`` directory.
This ``.dts_compiled`` file is processed by the python DTS parsing script
and generates an include file named
``include/generated/generated_dts_board_unfixed.h``
that holds all the information extracted from the DTS file with
the format specified by the YAML bindings. For example:
.. code-block:: c
/* gpio_keys */
#define DT_GPIO_KEYS_0 1
/* button_0 */
#define DT_GPIO_KEYS_BUTTON_0_GPIOS_CONTROLLER "GPIO_2"
#define DT_GPIO_KEYS_BUTTON_0_GPIOS_FLAGS 0
#define DT_GPIO_KEYS_BUTTON_0_GPIOS_PIN 6
#define DT_GPIO_KEYS_BUTTON_0_LABEL "User SW2"
#define DT_GPIO_KEYS_SW1_GPIOS_CONTROLLER DT_GPIO_KEYS_BUTTON_0_GPIOS_CONTROLLER
#define DT_GPIO_KEYS_SW1_GPIOS_FLAGS DT_GPIO_KEYS_BUTTON_0_GPIOS_FLAGS
#define DT_GPIO_KEYS_SW1_GPIOS_PIN DT_GPIO_KEYS_BUTTON_0_GPIOS_PIN
#define DT_ALIAS_SW1_GPIOS_CONTROLLE DT_GPIO_KEYS_BUTTON_0_GPIOS_CONTROLLER
#define DT_ALIAS_SW1_GPIOS_FLAGS DT_GPIO_KEYS_BUTTON_0_GPIOS_FLAGS
#define DT_ALIAS_SW1_GPIOS_PIN DT_GPIO_KEYS_BUTTON_0_GPIOS_PIN
#define DT_ALIAS_SW1_LABEL DT_GPIO_KEYS_BUTTON_0_LABEL
Additionally, a file named ``generated_dts_board_fixups.h`` is
generated in the same directory concatenating all board-related fixup files.
The include file ``include/generated_dts_board.h`` includes both these generated
files, giving Zephyr C source files access to the board's device tree information.
.. include:: flash_partitions.inc