mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-17 02:31:56 +00:00
Syntax highlight all the DTS fragments, add more internal cross-referencing to making jumping around the HTML easier, and tweak the language, filling in a missing piece here and there. Fix a couple of DTS syntax errors caught by adding highlighting. Add an ABNF grammar for the macros generated by DT, along with some comments about why the current grammar is not ideal from a generality point of view. Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
408 lines
16 KiB
ReStructuredText
408 lines
16 KiB
ReStructuredText
.. _devicetree-intro:
|
|
|
|
Introduction to devicetree
|
|
##########################
|
|
|
|
This page provides an introduction to :ref:`devicetree` and how it is used in
|
|
Zephyr.
|
|
|
|
The following figure shows how devicetree is used by :ref:`Zephyr's build
|
|
system <build_overview>`:
|
|
|
|
.. figure:: zephyr_dt_build_flow.png
|
|
:figclass: align-center
|
|
|
|
Devicetree build flow
|
|
|
|
The build system generates a C header file which contains preprocessor
|
|
:ref:`macros with devicetree data <dt-macros>`. These macros can be referenced
|
|
by :ref:`device drivers <device_drivers>` and other C code by including
|
|
:file:`<devicetree.h>`. All macro identifiers that are directly generated by
|
|
the devicetree scripts start with ``DT_``.
|
|
|
|
Sometimes, information from devicetree is available using ``CONFIG_`` macros
|
|
generated from :ref:`Kconfig <kconfig>`. This happens when devicetree-related
|
|
information is referenced from Kconfig symbol definitions via :ref:`Kconfig
|
|
functions <kconfig-functions>`. See :ref:`dt_vs_kconfig` for some additional
|
|
comparisons with Kconfig.
|
|
|
|
This differs significantly from how devicetree is used on Linux. The
|
|
Linux kernel would instead read the entire devicetree data structure in its
|
|
binary form, parsing it at runtime in order to load and initialize device
|
|
drivers. Zephyr does not work this way because the size of the devicetree
|
|
binary and associated handling code would be too large to fit comfortably on
|
|
the relatively constrained devices Zephyr supports.
|
|
|
|
.. _dt-syntax:
|
|
|
|
Syntax and structure
|
|
********************
|
|
|
|
As the name indicates, a devicetree is a tree. The human-readable text format
|
|
for this tree is called DTS (for devicetree source), and is defined in the
|
|
`Devicetree specification`_.
|
|
|
|
.. _Devicetree specification: https://www.devicetree.org/
|
|
|
|
Here is an example DTS file:
|
|
|
|
.. code-block:: DTS
|
|
|
|
/dts-v1/;
|
|
|
|
/ {
|
|
a-node {
|
|
subnode_label: a-sub-node {
|
|
foo = <3>;
|
|
};
|
|
};
|
|
};
|
|
|
|
The ``/dts-v1/;`` line means the file's contents are in version 1 of the DTS
|
|
syntax, which has replaced a now-obsolete "version 0".
|
|
|
|
The tree has three *nodes*:
|
|
|
|
#. A root node: ``/``
|
|
#. A node named ``a-node``, which is a child of the root node
|
|
#. A node named ``a-sub-node``, which is a child of ``a-node``
|
|
|
|
.. _dt-node-labels:
|
|
|
|
Nodes can be given *labels*, which are unique shorthands that can be used to
|
|
refer to the labeled node elsewhere in the devicetree. Above, ``a-sub-node``
|
|
has label ``subnode_label``.
|
|
|
|
Devicetree nodes have *paths* identifying their locations in the tree. Like
|
|
Unix file system paths, devicetree paths are strings separated by slashes
|
|
(``/``), and the root node's path is a single slash: ``/``. Otherwise, each
|
|
node's path is formed by concatenating the node's ancestors' names with the
|
|
node's own name, separated by slashes. For example, the full path to
|
|
``a-sub-node`` is ``/a-node/a-sub-node``.
|
|
|
|
Devicetree nodes can also have *properties*. Properties are name/value
|
|
pairs. The values are simple byte arrays. Node ``a-sub-node`` has a property
|
|
named ``foo``, whose value is a 32-bit big-endian unsigned integer with value
|
|
3. The size and type of ``foo``\ 's value are implied by the enclosing angle
|
|
brackets (``<`` and ``>``) in the DTS. Refer to the Devicetree Specification
|
|
for a complete list of ways to write a property value in a DTS file.
|
|
|
|
In practice, devicetree nodes correspond to some hardware, and the node
|
|
hierarchy reflects the hardware's physical layout. For example, let's consider
|
|
a board with three I2C peripherals connected to an I2C bus controller on an SoC,
|
|
like this:
|
|
|
|
.. figure:: zephyr_dt_i2c_high_level.png
|
|
:alt: representation of a board with three I2C peripherals
|
|
:figclass: align-center
|
|
|
|
Nodes corresponding to the I2C bus controller and each I2C peripheral would be
|
|
present in this board's devicetree. Reflecting the hardware layout, the
|
|
devicetree's peripheral nodes would be children of the bus controller node.
|
|
Similar conventions exist for representing other types of hardware in
|
|
devicetree.
|
|
|
|
The DTS would look something like this:
|
|
|
|
.. code-block:: DTS
|
|
|
|
/dts-v1/;
|
|
|
|
/ {
|
|
soc {
|
|
i2c-bus-controller {
|
|
i2c-peripheral-1 {
|
|
};
|
|
i2c-peripheral-2 {
|
|
};
|
|
i2c-peripheral-3 {
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
Properties are used in practice to describe or configure the hardware the node
|
|
represents. For example, an I2C peripheral's node has a property whose value is
|
|
the peripheral's address on the bus.
|
|
|
|
Here's a tree representing the same example, but with real-world node
|
|
names and properties you might see when working with I2C devices.
|
|
|
|
.. figure:: zephyr_dt_i2c_example.png
|
|
:figclass: align-center
|
|
|
|
I2C devicetree example with real-world names and properties.
|
|
Node names are at the top of each node with a gray background.
|
|
Properties are shown as "name=value" lines.
|
|
|
|
This is the corresponding DTS:
|
|
|
|
.. code-block:: DTS
|
|
|
|
/dts-v1/;
|
|
|
|
/ {
|
|
soc {
|
|
i2c@40003000 {
|
|
compatible = "nordic,nrf-twim";
|
|
label = "I2C_0";
|
|
reg = <0x40003000 0x1000>;
|
|
|
|
apds9960@39 {
|
|
compatible = "avago,apds9960";
|
|
label = "APDS9960";
|
|
reg = <0x39>;
|
|
};
|
|
ti_hdc@43 {
|
|
compatible = "ti,hdc", "ti,hdc1010";
|
|
label = "HDC1010";
|
|
reg = <0x43>;
|
|
};
|
|
mma8652fc@1d {
|
|
compatible = "nxp,fxos8700", "nxp,mma8652fc";
|
|
label = "MMA8652FC";
|
|
reg = <0x1d>;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
.. _dt-unit-address:
|
|
|
|
In addition to showing more realistic names and properties, the above example
|
|
introduces a new devicetree concept: unit addresses. Unit addresses are the
|
|
parts of node names after an "at" sign (``@``), like ``40003000`` in
|
|
``i2c@40003000``, or ``39`` in ``apds9960@39``. Unit addresses are optional:
|
|
the ``soc`` node does not have one.
|
|
|
|
Some more details about unit addresses and important properties follow.
|
|
|
|
Unit address examples
|
|
*********************
|
|
|
|
In devicetree, unit addresses give a node's address in the
|
|
address space of its parent node. Here are some example unit addresses for
|
|
different types of hardware.
|
|
|
|
Memory-mapped peripherals
|
|
The peripheral's register map base address.
|
|
For example, the node named ``i2c@40003000`` represents an I2C controller
|
|
whose register map base address is 0x40003000.
|
|
|
|
I2C peripherals
|
|
The peripheral's address on the I2C bus.
|
|
For example, the child node ``apds9960@39`` of the I2C controller
|
|
in the previous section has I2C address 0x39.
|
|
|
|
SPI peripherals
|
|
An index representing the peripheral's chip select line number.
|
|
(If there is no chip select line, 0 is used.)
|
|
|
|
Memory
|
|
The physical start address.
|
|
For example, a node named ``memory@2000000`` represents RAM starting at
|
|
physical address 0x2000000.
|
|
|
|
Memory-mapped flash
|
|
Like RAM, the physical start address.
|
|
For example, a node named ``flash@8000000`` represents a flash device
|
|
whose physical start address is 0x8000000.
|
|
|
|
Flash partitions
|
|
The start offset of the partition within its flash device.
|
|
For example, take this flash device and its partitions:
|
|
|
|
.. code-block:: DTS
|
|
|
|
flash@8000000 {
|
|
/* ... */
|
|
partitions {
|
|
partition@0 { /* ... */ };
|
|
partition@20000 { /* ... */ };
|
|
/* ... */
|
|
};
|
|
};
|
|
|
|
The node named ``partition@0`` has offset 0 from the start of its flash
|
|
device, so its base address is 0x8000000. Similarly, the base address of
|
|
the node named ``partition@20000`` is 0x8020000.
|
|
|
|
.. _dt-important-props:
|
|
|
|
Important properties
|
|
********************
|
|
|
|
Some important properties are:
|
|
|
|
compatible
|
|
Says what kind of device the node represents. The recommended format is
|
|
``"manufacturer,device"``, like ``"avago,apds9960"``, or a sequence of
|
|
these, like ``"ti,hdc", "ti,hdc1010"``. The file
|
|
:zephyr_file:`dts/bindings/vendor-prefixes.txt` contains a list of accepted
|
|
``manufacturer`` prefixes.
|
|
|
|
It is also sometimes a value like ``gpio-keys``, ``mmio-sram``, or
|
|
``fixed-clock`` when the hardware's behavior is generic.
|
|
|
|
The build system uses the compatible property to find the right
|
|
:ref:`bindings <dt-bindings>` for the node.
|
|
|
|
label
|
|
The device's name according to Zephyr's :ref:`device_drivers`. The value
|
|
can be passed to :c:func:`device_get_binding()` to retrieve the
|
|
corresponding driver-level :ref:`struct device* <device_struct>`. This
|
|
pointer can then be passed to the correct driver API by application code to
|
|
interact with the device. For example, calling
|
|
``device_get_binding("I2C_0")`` would return a pointer to a device
|
|
structure which could be passed to :ref:`I2C API <i2c_api>` functions like
|
|
:c:func:`i2c_transfer()`. The generated C header will also contain a macro
|
|
which expands to this string.
|
|
|
|
reg
|
|
Information used to address the device. This could be a memory-mapped I/O
|
|
address range (as with ``i2c@40003000``\ 's reg property), an I2C bus
|
|
address (as with ``apds9960@39`` and its devicetree siblings), a SPI chip
|
|
select line, or some other value depending on the kind of device the node
|
|
represents.
|
|
|
|
Unlike a node's unit address, which is a simple number, the reg property is
|
|
an array of 32-bit unsigned integers. This is often used to describe the
|
|
size of a register map. In the case of the ``i2c@40003000`` node above,
|
|
``reg = <0x40003000 0x1000>;`` means the register map occupies 0x1000 bytes
|
|
in the memory map.
|
|
|
|
Input and output files
|
|
**********************
|
|
|
|
This section describes the input and output files shown in the figure at the
|
|
:ref:`top of this introduction <devicetree-intro>` in more detail.
|
|
|
|
.. figure:: zephyr_dt_inputs_outputs.svg
|
|
:figclass: align-center
|
|
|
|
Devicetree input (green) and output (yellow) files
|
|
|
|
There are four "types" of devicetree files:
|
|
|
|
- sources (``.dts``)
|
|
- includes (``.dtsi``)
|
|
- overlays (``.overlay``)
|
|
- bindings (``.yaml``)
|
|
|
|
The devicetree files inside the :file:`zephyr` directory look like this::
|
|
|
|
boards/<ARCH>/<BOARD>/<BOARD>.dts
|
|
dts/common/skeleton.dtsi
|
|
dts/<ARCH>/.../<SOC>.dtsi
|
|
dts/bindings/.../binding.yaml
|
|
|
|
Generally speaking, every supported board has a :file:`BOARD.dts` file
|
|
describing its hardware. For example, the ``reel_board`` has
|
|
:zephyr_file:`boards/arm/reel_board/reel_board.dts`.
|
|
|
|
:file:`BOARD.dts` includes one or more ``.dtsi`` files. These ``.dtsi`` files
|
|
describe the CPU or system-on-chip Zephyr runs on, perhaps by including other
|
|
``.dtsi`` files. They can also describe other common hardware features shared by
|
|
multiple boards. In addition to these includes, :file:`BOARD.dts` also describes
|
|
the board's specific hardware.
|
|
|
|
The :file:`dts/common` directory contains :file:`skeleton.dtsi`, a minimal
|
|
include file for defining a complete devicetree. Architecture-specific
|
|
subdirectories (:file:`dts/<ARCH>`) contain ``.dtsi`` files for CPUs or SoCs
|
|
which extend :file:`skeleton.dtsi`.
|
|
|
|
The C preprocessor is run on all devicetree files to expand macro references,
|
|
and includes are generally done with ``#include <filename>`` directives, even
|
|
though DTS has a ``/include/ "<filename>"`` syntax.
|
|
|
|
:file:`BOARD.dts` can be extended or modified using *overlays*. Overlays are
|
|
also DTS files; the :file:`.overlay` extension is just a convention which makes
|
|
their purpose clear. Overlays adapt the base devicetree for different purposes:
|
|
|
|
- Zephyr applications can use overlays to enable a peripheral that is disabled
|
|
by default, select a sensor on the board for an application specific purpose,
|
|
etc. Along with :ref:`kconfig`, this makes it possible to reconfigure the
|
|
kernel and device drivers without modifying source code.
|
|
|
|
- Overlays are also used when defining :ref:`shields`.
|
|
|
|
The build system automatically picks up :file:`.overlay` files stored in
|
|
certain locations. It is also possible to explicitly list the overlays to
|
|
include, via the :makevar:`DTC_OVERLAY_FILE` CMake variable. See
|
|
:ref:`application_dt` and :ref:`important-build-vars` for details.
|
|
|
|
The build system combines :file:`BOARD.dts` and any :file:`.overlay` files by
|
|
concatenating them, with the overlays put last. This relies on DTS syntax which
|
|
allows merging overlapping definitions of nodes in the devicetree. See
|
|
:ref:`dt_k6x_example` for an example of how this works (in the context of
|
|
``.dtsi`` files, but the principle is the same for overlays). Putting the
|
|
contents of the :file:`.overlay` files last allows them to override
|
|
:file:`BOARD.dts`.
|
|
|
|
:ref:`dt-bindings` (which are YAML files) are essentially glue. They describe
|
|
the contents of devicetree sources, includes, and overlays in a way that allows
|
|
the build system to generate C macros usable by device drivers and
|
|
applications. The :file:`dts/bindings` directory contains bindings.
|
|
|
|
These files in the build directory can be useful as a debugging aid when
|
|
working with devicetree:
|
|
|
|
build/zephyr/<BOARD>.dts.pre.tmp
|
|
The preprocessed and concatenated DTS sources
|
|
|
|
build/zephyr/zephyr.dts
|
|
The final merged devicetree. This file is specifically output as a
|
|
debugging aid, and is unused otherwise.
|
|
|
|
.. _dt-scripts:
|
|
|
|
The following libraries and scripts, located in :zephyr_file:`scripts/dts/`,
|
|
are used to generate C headers from the devicetree and its bindings. Note that
|
|
the source code has extensive comments and documentation.
|
|
|
|
:zephyr_file:`dtlib.py <scripts/dts/dtlib.py>`
|
|
A low-level DTS parsing library.
|
|
|
|
:zephyr_file:`edtlib.py <scripts/dts/edtlib.py>`
|
|
A library layered on top of dtlib that uses bindings to interpret
|
|
properties and give a higher-level view of the devicetree. Uses dtlib to do
|
|
the DTS parsing.
|
|
|
|
:zephyr_file:`gen_defines.py <scripts/dts/gen_defines.py>`
|
|
A script that uses edtlib to generate C preprocessor macros from the
|
|
devicetree and bindings.
|
|
|
|
The output from :file:`gen_defines.py` is stored in the build directory as
|
|
:file:`build/zephyr/include/generated/devicetree_unfixed.h`.
|
|
|
|
In addition to the Python code above, the standard ``dtc`` (devicetree
|
|
compiler) tool is also run on the final devicetree if it is installed on your
|
|
system. This is just to catch any errors or warnings it generates. The output
|
|
is unused. Boards may need to pass ``dtc`` additional flags, e.g. for warning
|
|
suppression. Board directories can contain a file named
|
|
:file:`pre_dt_board.cmake` which configures these extra flags, like this:
|
|
|
|
.. code-block:: cmake
|
|
|
|
list(APPEND EXTRA_DTC_FLAGS "-Wno-simple_bus_reg")
|
|
|
|
Zephyr currently uses :file:`dts_fixup.h` files to rename macros in
|
|
:file:`devicetree_unfixed.h` to names that are currently in use by C code. The
|
|
build system looks for fixup files in the :file:`zephyr/boards/` and
|
|
:file:`zephyr/soc/` directories by default. Any :file:`dts_fixup.h` files are
|
|
concatenated and stored in the build directory as
|
|
:file:`build/zephyr/include/generated/devicetree_fixups.h`.
|
|
|
|
Fixup files exist for historical reasons. New code should generally avoid them.
|
|
|
|
To reference macros generated by :file:`gen_defines.py` from C, include
|
|
:file:`devicetree.h`. This file is :zephyr_file:`include/devicetree.h` in the
|
|
zephyr repository; it is not a generated file. It includes the generated
|
|
:file:`include/devicetree_unfixed.h` and :file:`include/devicetree_fixups.h`
|
|
files.
|
|
|
|
.. warning::
|
|
|
|
Do not include the generated C headers from the build directory directly.
|
|
Use :file:`devicetree.h` instead.
|