Device Trees
This section will detail how to modify a hardware device tree.
It will not tell you how to modify a device tree for your specific hardware and peripherals, but it will provide the tools to do so.
Overview
A device tree is a way to represent hardware. It is comprised of many device tree source (DTS) files and DTS include (DTSI) files.
When the source files are compiled, a flattened device tree (FDT), also known as a device tree blob (DTB), is created.
QEMU and Linux use the DTB to understand the structure of the hardware without any hard coding involved.
Acquiring the Tools
Before proceeding, the following programs and files should be installed on your computer
- PetaLinux
- BSPs, on this page we will use a ZCU102 BSP
- Device tree compiler (DTC)
These are available with PetaLinux, Yocto, or Vitis tools.
Modifying a Device Tree
It is strongly recommended to read the device tree specification before modifying a device tree.
The specification will cover the structure, syntax, and good practices of device tree modification.
Device Tree Properties and QEMU
QEMU uses device trees to represent hardware. This device tree is not passed to the guest and is sometimes referred to as the hardware device tree.
Some devices contain device-specific properties that should exist in the hardware device tree.
For example, an SI57X may have a device node that looks like:
clock-generator@5e { compatible = "silabs,si57x"; reg = <0x5d>; temperature-stability = <0x32>; };
The temperature-stability
property is one that the device should have present.
If you want to add another device and you're not sure what properties need to be present, you can pass the -device <device-name>,help
argument along with your other QEMU arguments when starting QEMU.
In this case, the argument will look like:
-device si57x,help
An alternative is to look at its properties list in the device's .c
file in the QEMU source.
For example, the SI57X has the properties list:
static Property si57x_properties[] = { DEFINE_PROP_UINT16("temperature-stability", Si57xState, temp_stab, TEMP_STAB_50PPM), DEFINE_PROP_END_OF_LIST(), };
As we can see, it only looks for the temperature-stability
property.
If a property is not defined in a device node, QEMU will use a default value. In this case, 50PPM.
Device Tree Properties and the Guest
QEMU guests, such as Linux, can use device trees to understand the hardware it has access to. For instance, a guest device tree may list partitions for a SPI flash.
So for this example, we will modify the device tree to add a partition to SPI flash on a ZCU102 platform using PetaLinux.
Create your ZCU102 project if you haven't already:
petalinux-create -t project -s "/path/to/bsp/xilinx-zcu102.bsp" -n "project-name"
Boot it once to make sure everything is working properly. Take note of the DTBs used when booting.
$petalinux-boot --qemu --prebuilt 3 INFO: sourcing build tools INFO: No DTB has been specified, use the default one "/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/system.dtb". INFO: Starting microblaze QEMU INFO: Starting the above QEMU command in the background INFO: qemu-system-microblazeel -M microblaze-fdt -serial mon:stdio -serial /dev/null -display none -kernel /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/pmu_rom_qemu_sha3.elf -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/pmufw.elf -hw-dtb /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/zynqmp-qemu-multiarch-pmu.dtb -machine-path /tmp/tmp.geJUZ51a7f -device loader,addr=0xfd1a0074,data=0x1011003,data-len=4 -device loader,addr=0xfd1a007C,data=0x1010f03,data-len=4 INFO: Set QEMU tftp to /scratch/petalinux-images/xilinx-zcu102-2019.2/images/linux qemu-system-microblazeel: Failed to connect socket /tmp/tmp.geJUZ51a7f/qemu-rport-_pmu@0: No such file or directory qemu-system-microblazeel: info: QEMU waiting for connection on: disconnected:unix:/tmp/tmp.geJUZ51a7f/qemu-rport-_pmu@0,server INFO: TCP PORT is free INFO: Starting aarch64 QEMU INFO: qemu-system-aarch64 -M arm-generic-fdt -serial mon:stdio -serial /dev/null -display none -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/bl31.elf,cpu-num=0 -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/Image,addr=0x00080000 -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/system.dtb,addr=0x15e80000 -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/build/misc/linux-boot/linux-boot.elf -gdb tcp::9000 -dtb /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/system.dtb -net nic -net nic -net nic -net nic,netdev=eth0 -netdev user,id=eth0,tftp=/scratch/petalinux-images/xilinx-zcu102-2019.2/images/linux -hw-dtb /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/zynqmp-qemu-multiarch-arm.dtb -machine-path /tmp/tmp.geJUZ51a7f -global xlnx,zynqmp-boot.cpu-num=0 -global xlnx,zynqmp-boot.use-pmufw=true -m 4G QEMU 2.11.1 monitor - type 'help' for more information # 8<---------------------------------------------------------------------------------------------- [ 7.456218] m25p80 spi0.0: n25q512a (131072 Kbytes) [ 7.457506] 3 fixed-partitions partitions found on MTD device spi0.0 [ 7.457958] Creating 3 MTD partitions on "spi0.0": [ 7.458617] 0x000000000000-0x000001e00000 : "boot" [ 7.464772] 0x000001e00000-0x000001e40000 : "bootenv" [ 7.468541] 0x000001e40000-0x000004240000 : "kernel" # ...
For this example, we will be modifying system.dtb
.
Change directory to where system.dtb
is and un-flatten system.dtb
by using DTC.
cd pre-built/linux/images dtc -I dtb -O dts system.dtb -o system.dts
Open system.dts
with your preferred text editor and navigate to the line that says:
spi@ff0f0000 {
This SPI peripheral has a flash chip as a child. This is the flash chip we will be adding another partition to.
In the flash chip inside the SPI peripheral, there should be three partitions:
partition@0x00000000 { label = "boot"; reg = <0x0 0x1e00000>; }; partition@0x01e00000 { label = "bootenv"; reg = <0x1e00000 0x40000>; }; partition@0x01e40000 { label = "kernel"; reg = <0x1e40000 0x2400000>; };
Add a fourth partition so it looks like this:
partition@0x00000000 { label = "boot"; reg = <0x0 0x1e00000>; }; partition@0x01e00000 { label = "bootenv"; reg = <0x1e00000 0x40000>; }; partition@0x01e40000 { label = "kernel"; reg = <0x1e40000 0x2400000>; }; partition@0x04240000 { label = "new-partition"; reg = <0x4240000 0xc0000>; };
Build the DTB.
dtc -I dts -O dtb system.dts -o system.dtb
Re-run QEMU.
$petalinux-boot --qemu --prebuilt 3 INFO: sourcing build tools INFO: No DTB has been specified, use the default one "/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/system.dtb". INFO: Starting microblaze QEMU INFO: Starting the above QEMU command in the background INFO: qemu-system-microblazeel -M microblaze-fdt -serial mon:stdio -serial /dev/null -display none -kernel /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/pmu_rom_qemu_sha3.elf -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/pmufw.elf -hw-dtb /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/zynqmp-qemu-multiarch-pmu.dtb -machine-path /tmp/tmp.geJUZ51a7f -device loader,addr=0xfd1a0074,data=0x1011003,data-len=4 -device loader,addr=0xfd1a007C,data=0x1010f03,data-len=4 INFO: Set QEMU tftp to /scratch/petalinux-images/xilinx-zcu102-2019.2/images/linux qemu-system-microblazeel: Failed to connect socket /tmp/tmp.geJUZ51a7f/qemu-rport-_pmu@0: No such file or directory qemu-system-microblazeel: info: QEMU waiting for connection on: disconnected:unix:/tmp/tmp.geJUZ51a7f/qemu-rport-_pmu@0,server INFO: TCP PORT is free INFO: Starting aarch64 QEMU INFO: qemu-system-aarch64 -M arm-generic-fdt -serial mon:stdio -serial /dev/null -display none -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/bl31.elf,cpu-num=0 -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/Image,addr=0x00080000 -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/system.dtb,addr=0x15e80000 -device loader,file=/scratch/petalinux-images/xilinx-zcu102-2019.2/build/misc/linux-boot/linux-boot.elf -gdb tcp::9000 -dtb /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/system.dtb -net nic -net nic -net nic -net nic,netdev=eth0 -netdev user,id=eth0,tftp=/scratch/petalinux-images/xilinx-zcu102-2019.2/images/linux -hw-dtb /scratch/petalinux-images/xilinx-zcu102-2019.2/pre-built/linux/images/zynqmp-qemu-multiarch-arm.dtb -machine-path /tmp/tmp.geJUZ51a7f -global xlnx,zynqmp-boot.cpu-num=0 -global xlnx,zynqmp-boot.use-pmufw=true -m 4G QEMU 2.11.1 monitor - type 'help' for more information # 8<---------------------------------------------------------------------------------------------- [ 7.666932] m25p80 spi0.0: n25q512a (131072 Kbytes) [ 7.668616] 4 fixed-partitions partitions found on MTD device spi0.0 [ 7.669350] Creating 4 MTD partitions on "spi0.0": [ 7.670832] 0x000000000000-0x000001e00000 : "boot" [ 7.677346] 0x000001e00000-0x000001e40000 : "bootenv" [ 7.681945] 0x000001e40000-0x000004240000 : "kernel" [ 7.685878] 0x000004240000-0x000004300000 : "new-partition" # ...
Verify we can read and write to the new partition
root@xilinx-zcu102-2020_2:~# cat /proc/mtd dev: size erasesize name mtd0: 01e00000 00002000 "boot" mtd1: 00040000 00002000 "bootenv" mtd2: 02400000 00002000 "kernel" mtd3: 000c0000 00002000 "new-partition" root@xilinx-zcu102-2020_2:~# dd if=/dev/urandom of=./sample.bin bs=1024 count=64 64+0 records in 64+0 records out root@xilinx-zcu102-2020_2:~# flashcp -v sample.bin /dev/mtd3 Erasing blocks: 8/8 (100%) Writing data: 64k/64k (100%) Verifying data: 64k/64k (100%)
Words of Caution
When modifying device trees, your changes must make sense as if you were representing physical hardware.
Just because the device tree will build properly doesn't mean it will work properly with hardware, virtualized or otherwise.
For example, if we made our partitions look like:
partition@0x00000000 { label = "boot"; reg = <0x0 0x1e00000>; }; partition@0x01e00000 { label = "bootenv"; reg = <0x1e00000 0x40000>; }; partition@0x01e40000 { label = "kernel"; reg = <0x1e40000 0x2400000>; }; partition@0x04240000 { label = "new-partition"; reg = <0x4240000 0x80000000>; };
The flash does not have enough space to have a partition that large.
[ 7.514727] m25p80 spi0.0: n25q512a (131072 Kbytes) [ 7.516235] 4 fixed-partitions partitions found on MTD device spi0.0 [ 7.516899] Creating 4 MTD partitions on "spi0.0": [ 7.518107] 0x000000000000-0x000001e00000 : "boot" [ 7.525290] 0x000001e00000-0x000001e40000 : "bootenv" [ 7.530586] 0x000001e40000-0x000004240000 : "kernel" [ 7.535273] 0x000004240000-0x000084240000 : "new-partition" [ 7.535825] mtd: partition "new-partition" extends beyond the end of device "spi0.0" -- size truncated to 0x3dc0000
In this case, we were warned, but other device tree modifications may have unwanted behavior that won't have warnings.
© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy