The structure of a device tree

A device tree for Linux running on Zynq typically has the following form.

/dts-v1/;
/ {
  #address-cells = <1>;
  #size-cells = <1>;
  compatible = "xlnx,zynq-zed";
  interrupt-parent = <&gic>;
  model = "Xillinux for Zedboard";
  aliases {
    serial0 = &ps7_uart_1;
  } ;
  chosen {
    bootargs = "consoleblank=0 root=/dev/mmcblk0p2 rw rootwait earlyprintk";
    linux,stdout-path = "/axi@0/uart@E0001000";
  };

  cpus {

      [ ... CPU definitions ... ]

   } ;
  ps7_ddr_0: memory@0 {
    device_type = "memory";
    reg = < 0x0 0x20000000 >;
  } ;
  ps7_axi_interconnect_0: axi@0 {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "xlnx,ps7-axi-interconnect-1.00.a", "simple-bus";
    ranges ;

      [ ... Peripheral definitions ... ]

  } ;
} ;

This is the device tree used with Xillinux, with two parts cut out: The one describing the CPUs (because it isn’t interesting) and the one defining the peripherals (because it’s long, and we’ll get down to its details later on).

The source of the device tree used by default is available as e.g. /boot/devicetree-3.3.0-xillinux-1.0.dts in Xillinux’ file system.

After the version declaration, the device tree starts with a slash, saying “this is the tree’s root”, and then there are assignments within curly brackets. From the DTS compiler’s point of view, these curly brackets enclose deeper hierarchies in the tree (think directories in a file system). It will be the kernel code’s job to walk down this tree, and grab the desired information from certain paths (like reading from a file after reaching the desired path in a file system, if you like).

It’s important to remember that the tree’s structure is built just the way the kernel expects to find it. The assignments have no specific meaning to the compiler. In fact, many assignments are ignored by the kernel, just like a file in a file system is ignored if no program cares to open it.

Accessing the data from user space

The comparison with a file system isn’t only natural, it’s also implemented in the kernel’s /proc/device-tree: Each curly bracket is represented as a directory having the name of the string coming just before it.

So this is session on a system using the device tree listed above:

# hexdump -C '/proc/device-tree/#size-cells'
00000000  00 00 00 01                                       |....|
00000004
# hexdump -C '/proc/device-tree/axi@0/compatible'
00000000  78 6c 6e 78 2c 70 73 37  2d 61 78 69 2d 69 6e 74  |xlnx,ps7-axi-int|
00000010  65 72 63 6f 6e 6e 65 63  74 2d 31 2e 30 30 2e 61  |erconnect-1.00.a|
00000020  00 73 69 6d 70 6c 65 2d  62 75 73 00              |.simple-bus.|
0000002c

or simply

# cat '/proc/device-tree/axi@0/compatible'
xlnx,ps7-axi-interconnect-1.00.asimple-bus

Note the axi@0 element’s definition in the device tree listing above. It says “ps7_axi_interconnect_0: axi@0″. The string before the colon is the label, which is possibly referred to within the DTS file, but doesn’t appear in the DTB. As just said, It’s the string close to the curly brackets that defines the name of the hierarchy (and hence also the directory).

As this example demonstrates, the assignments turn into plain files in the /proc filesystem. If there is no assignment (e.g. “ranges” under “axi@0″), an empty file is created.

What the examples above show, is that the device tree can be used conveniently to convey information to user space programs as well as code within the Linux kernel, as the /proc/device-tree pseudo-filesystem makes this information accessible. Needless to say, there is a dedicated API in the kernel for accessing the device tree’s structure and data.

And you may have noted that the integer is represented in Big Endian. The Zynq’s processor runs Little Endian. Just a small thing to keep in mind.

Boot parameters in the device tree

There are three sources for the kernel boot command line in general:

  • Those given as CONFIG_CMDLINE in the kernel configuration
  • Those passed on by the boot loader (typically U-boot on ARM processors, LILO or GRUB on x86)
  • Those included in the device tree, under chosen/bootargs (see listing above)

Which one is used depends on kernel configuration parameters. In Xillinux, the device tree’s chosen/bootargs is used.

The chosen UART for kernel boot messages is hardcoded in the initialization routine. As a matter of fact, boot messages will appear on the UART even if the ps7_uart_1: serial@e0001000 entry in the device tree is deleted altogether (but the UART won't be available as /dev/ttyPS0).

The somewhat misleading "aliases" and "linux,stdout-path" assignments are leftovers from other architectures, and have no significance at this time.


Continue to part III, which begins showing the relation between the device tree and the Linux device driver.