/
Build Device Tree Blob

Build Device Tree Blob

This page covers the generation of devicetree source (DTS) files using Xilinx tools as well as the building/compiling of these source files using standard open-source tools.  In particular, use of the Xilinx Devicetree Generator (DTG) will be covered for generating DTS files from a Xilinx hardware project while the devicetree compiler (DTC) will be covered for compiling DTS files into a devicetree binary (DTB).  Although the primary use of the DTB is to provide it to the Linux kernel so that Linux can be initialized to specific hardware correctly, the DTB can also be used with QEMU to emulate hardware for both Linux and standalone systems.

Table of Contents

Child Pages

Devicetree 101

What is devicetree?

Device tree or simply called DT is a data structure that describes the hardware. This describes the hardware which is readable by an operating system like Linux so that it doesn't need to hard code details of the machine.
Linux uses the DT basically for platform identification, run-time configuration like bootargs and the device node population.

Devicetree Basics

Each driver or a module in the device tree is defined by the node and all its properties are defined under that node. Based on the driver it can have child nodes or parent node.

For example a device connected by SPI bus will have SPI bus controller as its parent node and that device will be one of the child node of spi node. Root node is the parent for all the nodes.

Under the root node typically consists of
1) CPUs node information
2) Memory information
3) Chosen can have configuration data like the kernel parameters string and the location of an initrd image
4) Aliases
5) Nodes which define the buses information

Devicetree Syntax Example

zynqmp-example.dtsi
/ {
    compatible = "xlnx,zynqmp";
    #address-cells = <2>;
    #size-cells = <2>;
 
    cpus {
       #address-cells = <1>;
       #size-cells = <0>;
 
       cpu0: cpu@0 {
                compatible = "arm,cortexa53", "arm,armv8";
                device-type = "cpu";
                enable-method = "psci";
                operating-points-v2 = <&cpu_opp_table>;
                reg = <0x0>;
                cpu-idle-states = <&CPU_SLEEP_0>;
        };
 
        cpu1: cpu@1 {
               compatible = "arm,cortexa53", "arm,armv8";
               device-type = "cpu";
               enable-method = "psci";
               operating-points-v2 = <&cpu_opp_table>;
               reg = <0x1>;
               cpu-idle-states = <&CPU_SLEEP_0>;
        };
  };
 
  chosen {
           bootargs = "earlycon clk_ignore_unused";
  };
 
  memory {
          device-type = "memory";
          reg = <0x0 0x0 0x0 0x80000000>, <0x00000008 0x0 0x0 0x80000000>;
  };
 
  amba_apu: amba_apu@0 {
              compatible = "simple-bus";
              #address-cells = <2>;
              #size-cells = <1>;
              ranges = <0 0 0 0 0xffffffff>;
              gic: interrupt-controller@f9010000 {
                        compatible = "arm,gic-400", "arm,cortex-a15-gic";
                        #interrupt-cells = <3>;
                        reg = <0x0 0xf9010000 0x10000>,
                               0x0 0xf9020000 0x20000>,
                               0x0 0xf9040000 0x20000>,
                               0x0 0xf9060000 0x20000>,
                        interrupt-controller;
                        interrupt-parent = <&gic>;
                        interrupts =<1 9 0xf04>;
              };
  };
 
  amba: amba {
          compatible = "simple-bus";
          #address-cells = <2>;
          #size-cells = <2>;
          ranges;
          can0: can@ff060000 {
                     compatible = "xlnx,zynq-can-1.0";
                     clock-names = "can_clk", "pclk";
                     reg =<0x0 0xff060000 0x0 0x1000>;
                     interrupts = <0 23 4>;
                     interrupt-parent = <&gic>;
                     tx-fifo-depth = <0x40>;
                     rx-fifo-depth = <0x40>;
                     power-domains = <&pd_can0>;
          };
  };

Devicetree Properties

compatible: The top-level compatible property typically defines a compatible string for the board, and then for the SoC.
Values always given with the most-specific first, to least-specific last.
#address-cells: Property indicate how many cells (i.e 32 bits values) are needed to form the base address part in the reg property.
#size-cells: The size part of the reg property.
interrupt-controller: Is a boolean property that indicates that the current node is an interrupt controller.
#interrupt-cells: Indicates the number of cells in the interrupts property for the interrupts managed by the selected interrupt controller.
interrupt-parent: Is a phandle that points to the interrupt controller for the current node. There is generally a top-level interrupt-parent definition for the main interrupt controller.

Devicetree Generator (DTG)

The DTG is intended to help users build their hardware-specific DTS file.  Building a DTS for custom hardware will always be a somewhat manual process but the DTG can help jump-start users to a fairly advanced starting point.  This is because a lot of information captured in a DTS can be extracted from information in the hardware hand-off file (XSA).  DTG will populate various DTS files with as much information as it can based on a supplied XSA file and then the user will be expected to fill-in the blanks or adjust as needed.

Version Check

The procedure for using the DTG varies for older versions of Xilinx tools.  Additionally, for some tool versions, there may be GUI flows versus CLI flows.  The "Generate DTS Files" section below provides several sub-sections for each of these different procedures.  If you're not using the latest version of Xilinx tools make sure you refer to the appropriate sub-section beyond the first one presented.

Task Dependencies (Pre-requisites)

  • DTG Source

  • XSA Hardware hand-off file generated by Xilinx Vivado tool (previously HDF)

  • Xilinx Vitis installation (or previously Xilinx SDK)

Task Output Products

The DTG generates DTS files with *.dts and *.dtsi file extensions.  There will be a single top-level *.dts file with "include" statements to reference separate DTS include (DTSI) files.  Using DTSI files allows information to be organized amongst different files.  For example, as described in more detail below, one DTSI can be used to describe fixed hardware (i.e. fixed in silicon) while another DTSI can be used to describe dynamic hardware (i.e. IP in the programmable logic).

Generally for the SOCs there will be a static dts/dtsi files, but when it comes to the FPGA there can be many complicated designs which the peripheral logic(PL) IPs may vary or might be having different configurations.
For these complicated FPGA designs we require a Device tree generator(DTG) where it can generate the dts/dtsi automatically for those designs.

Once we generate there will be different files available in the output directory, say for example pl.dtsi, pcw.dtsi, system-top.dts, zynqmp.dtsi, zynqmp-clk-ccf.dtsi, pl-partial-*.dtsi(the suffix of rprm will be added and this files will get generated only for partial/dfx xsa files).  These files are described below.

  • pl.dtsi: This is a file where all the memory mapped peripheral logic(PL) IP nodes will be available.
  • pcw.dtsi: This is a file where the dynamic properties where the PS peripheral needs.
  • system-top.dts: This is a file where it contains the memory information, early console and the boot arguments.
  • zynqmp.dtsi: This file contains all the PS peripheral information and also the cpu info.
  • zynqmp-clk-ccf.dtsi: This file contains all the clock information for the peripheral IPs.
  • pl-partial-<RPRM>.dtsi: This is a file where all the memory mapped IP nodes for dynamic function exchange designs(DFX).
  • pl-partial-custom-<RPRM>.dtsi: This is a file where we can customize the dfx ip nodes. This will get generated when CONFIG.partial_overlay_custom_dts is set
    • If user issues %xsct set_property CONFIG.partial_overlay_custom_dts "pl-partial-final.dts" command then pl-partial-<RPRM>.dtsi and pl-partial-custom-<RPRM>.dtsi will get created and included in pl-partial-final<RPRM>.dts
    • user should do his changes in pl-partial-custom-<RPRM>.dtsi. With this user can create pl-partial-<rprm>.dtbo or pl-partial-final<RPRM>.dtbo based on his requirements.
  • pl-custom.dtsi: This will get generated only when CONFIG.overlay_custom_dts is set. This flag is useful when user want to customize pl.dtsi nodes with user changes when using overlays.
    • If user issues %xsct set_property CONFIG.overlay_custom_dts "pl-final.dts" command then pl.dtsi and pl-custom.dtsi will get created and included in pl-final.dts
    • user should do his changes in pl-custom.dtsi. With this user can create pl.dtbo or pl-final.dtbo based on his requirements.


Apart from these files, based on the board it will generate one more board.dtsi file under the same output directory dt/.  For example, if board is zcu111-reva then it generates dt/zcu111-reva.dtsi.

  • zcu111-reva.dtsi: It contains all the board specific properties like i2c might be connected to some slave etc.

The actual files output will vary based on the device architecture (e.g. ZynqUS+ vs Zynq-7000 vs MicroBlaze).

Step 1: Fetch DTG Source

DTG is an open source utility with the source code published on the Xilinx GitHub site.  It uses an interpreted language (Tcl) so there's no need to compile the source.

git clone https://github.com/Xilinx/device-tree-xlnx
cd device-tree-xlnx
git checkout <xilinx_rel_v20XX.X>

In the last command above <xilinx-v20XX.X> should be replaced with a valid tag value (for example "xilinx_rel_v2023.1").  Available tags can be listed using the command "git tag".

Step 2: Generate DTS Files

Only follow the individual sub-section below that applies to your use-case.

Generate DTS Files Using XSCT

  1. Source Xilinx design tools
  2. Run XSCT (available as of 2015.1 tool release)

    xsct
  3. Open XSA/HDF file

    hsi open_hw_design <design_name>.<xsa|hdf>
  4. Set repository path (clone done in previous step in SDK) (On Windows use this format set_repo_path {C:\device-tree-xlnx})

    hsi set_repo_path <path to device-tree-xlnx repository>
  5. Create SW design and setup CPU.  The -proc option is typically one of these values: for Versal "psv_cortexa72_0", for ZynqMP "psu_cortexa53_0", for Zynq-7000 "ps7_cortexa9_0", for Microblaze "microblaze_0". However, users should extract the processor cell name this as shown below. This will return a list of valid processors that users should use.

    set procs [hsi get_cells -hier -filter {IP_TYPE==PROCESSOR}]
    puts "List of processors found in XSA is $procs"
    hsi create_sw_design device-tree -os device_tree -proc psv_cortexa72_0
  6. Generate DTS/DTSI files to folder my_dts where output DTS/DTSI files will be generated

    hsi generate_target -dir my_dts
  7. Clean up. 

    hsi close_hw_design [hsi current_hw_design]
    exit

Note that to compliment XSCT User Guide documentation some tips on using HSI can be found here: HSI debugging and optimization techniques.  For example, a command that lists all processor cells in the hardware design can be found there (i.e. IP_TYPE==PROCESSOR); these processor cell names represent valid values for the -proc option in step 5 above.

Generate DTS Files Using Xilinx SDK (GUI flow: tool version 2014.2-2019.1)

Generate HDF file from hardware project (if not already available)

  1. Open the hardware project in Vivado.
  2. Generate Block Design
IP Integrator: Generate Block Design
# Export the hardware system to SDK:
Vivado Menu: File > Export > Export Hardware

(In <project_name>.sdk HDF file is generated)

Generate a Device Tree Source (.dts/.dtsi) files from SDK

  1. Open SDK from Vivado or open SDK via command line (xsdk -hwspec <filename>.hdf -workspace <workspace>

    Vivado Menu: File > Launch SDK

  2. The Device Tree Generator Git repository needs to be cloned from the Xilinx. See the Fetch Sources page for more information on Git.

    # Otherwise for SDK 2014.2 use this repo:
    git clone git://github.com/Xilinx/device-tree-xlnx.git
  3. Add the BSP repository in SDK (for SDK 2014.2 and later select "device-tree-xlnx" from the checked out git area):

    SDK Menu: Xilinx Tools > Repositories > New... (<bsp repo>) > OK
  4. Create a Device Tree Board Support Package (BSP):

    SDK Menu: File > New > Board Support Package > Board Support Package OS: device-tree > Finish
  5. A BSP settings window will appear. This window can also be accessed by opening the Device Tree BSP's system.mss file and clicking 'Modify this BSP's Settings'. Fill in the values as appropriate:
    • The 'bootargs' parameter specifies the arguments passed to the kernel at boot time (kernel command line). ***
    • The 'console device' parameter specifies which serial output device will be used. Select a value from the drop-down.


The .dts/.dtsi files are now located in <SDK workspace>/device_tree_bsp_0/ folder.

*** e.g. console=<tty>,<baudrate> root=/dev/ram rw ip=:::::eth0:dhcp earlyprintk
*** Some example values for <tty> are ttyPS0 when using Zynq, ttyUL0 when using the UART Lite soft ip, or ttyS0 when using the UART16550 soft ip.

Generate DTS Files Using HSI (CLI flow deprecated by XSCT)

As of the 2019.2 release of tools HSI is no longer available as a standalone utility and HSI commands must be run from XSCT.  Also note that XSCT has supported HSI commands since its introduction in the 2015.1 tool release.

  1. Source Xilinx design tools
  2. Run HSI (tool releases 2014.4 - 2019.1)

     hsi
     
  3. Open HDF file 

    open_hw_design <design_name>.hdf
  4. Set repository path (clone done in previous step in SDK) (On Windows use this format set_repo_path {C:\device-tree-xlnx}) 

    set_repo_path <path to device-tree-xlnx repository>
  5. Create SW design and setup CPU (for ZynqMP psu_cortexa53_0, for Zynq ps7_cortexa9_0, for Microblaze microblaze_0) 

    create_sw_design device-tree -os device_tree -proc ps7_cortexa9_0
  6. set_property CONFIG.periph_type_overrides "{BOARD zcu102-rev1.0}" [get_os] 

    set_property CONFIG.periph_type_overrides "{BOARD zcu102-rev1.0}" [get_os]
  7. Generate DTS/DTSI files to folder my_dts where output DTS/DTSI files will be generated 

    generate_target -dir my_dts
  8. In the generated my_dts folder zcu102-rev1.0.dtsi file should be present.


Generate DTS Files Using HSM (CLI flow deprecated by XSCT)

As of the 2019.2 release of tools HSM is no longer available as a standalone utility and has been deprecated by HSI commands within XSCT.  Also note that XSCT has supported HSI commands since its introduction in the 2015.1 tool release.

  1. Source Xilinx design tools
  2. Run HSM (tool releases 2014.4 - 2019.1)

    hsm
  3. Open HDF file

    open_hw_design <design_name>.hdf
  4. Set repository path (clone done in previous step in SDK) (On Windows use this format set_repo_path {C:\device-tree-xlnx})

    set_repo_path <path to device-tree-xlnx repository>
  5. Create SW design and setup CPU (for ZynqMP psu_cortexa53_0, for Zynq ps7_cortexa9_0, for Microblaze microblaze_0)

    create_sw_design device-tree -os device_tree -proc ps7_cortexa9_0
  6. Generate DTS/DTSI files to folder my_dts where output DTS/DTSI files will be generated

    generate_target -dir my_dts

Generate DTS Files Using XPS/SDK (legacy GUI flow: tool versions 2014.1 and prior)

  1. Open the hardware project in XPS.
  2. Export the hardware system to SDK. 

    NOTE: The GitHub repository cloned in the following instructions is no longer available online.  You can download archived source from AR# 75492

  3. XPS Menu: Project > Export Hardware Design to SDK... > Export && Launch SDK
    # The Device Tree Generator Git repository needs to be cloned from the Xilinx. See the [[www/Fetch Sources|Fetch Sources]] page for more information on Git. Note that there are two repos for differing SDK versions below.
    > [[code]]
    > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >