| .. _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 |