Every free linux kernel development course eventually arrives at the device tree, because it’s simply unavoidable — nearly every ARM, PowerPC, MIPS, and RISC-V board built today describes its hardware to Linux this way. In this lecture we’ll build up the device tree concept from first principles, look at real syntax, and write a small original example you can compile and inspect yourself.
DTS DTB dtc
Open Firmware
free linux kernel development course
free embedded linux course
What You Will Learn
- Why the device tree exists and what problem it solves
- The basic node and property syntax of device tree source files
- How to write, compile, and inspect an original device tree yourself
- Where device trees live in the kernel and bootloader source trees
- Common mistakes when writing your first device tree nodes
Prerequisites
This lecture assumes you’ve completed the previous lecture in this free linux kernel development course on how boot information is passed to the kernel, and that you have device-tree-compiler (providing the dtc tool) installed for the hands-on section.
Why the Device Tree Exists
As the previous lecture in this free linux kernel development course explained, early embedded Linux relied on hard-coded “platform data” compiled directly into the kernel for each board. That approach meant every new board required a new kernel build, and the kernel source tree accumulated thousands of tiny, board-specific files that had nothing to do with driving actual hardware logic.
The device tree fixes this by separating two concerns that platform data had tangled together: the kernel’s driver code (which knows how to operate a class of hardware) and the board’s hardware description (which says what hardware exists and how it’s wired). A single generic kernel binary can now support many different boards, as long as each board ships its own small device tree blob describing its specific hardware layout.
The format itself isn’t new — it traces back to a bootloader standard called Open Firmware, standardized as IEEE 1275, originally used on PowerPC-based systems. ARM Linux adopted the same idea, and it has since spread to MIPS, RISC-V, and other architectures.
Device Tree Structure: Nodes and Properties
A device tree represents a system as a hierarchy of nodes, starting from a single root node written as /. Each node can contain child nodes and properties, and each property is simply a name paired with a value.
/dts-v1/;
/ {
model = "EP Course Reference Board";
compatible = "ep,course-ref-board";
#address-cells = <1>;
#size-cells = <1>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a15";
device_type = "cpu";
reg = <0>;
};
};
memory@60000000 {
device_type = "memory";
reg = <0x60000000 0x10000000>; /* 256 MB */
};
chosen {
bootargs = "console=ttyAMA0 root=/dev/vda rw";
};
};
Notice the node naming convention: cpu@0 and memory@60000000 both follow the pattern name@address, which is how the device tree disambiguates multiple instances of the same kind of node. The chosen node deserves special mention because it’s exactly the mechanism from the previous lecture — this is where the kernel command line actually lives when a board boots via device tree rather than ATAGS.
├── cpus
│ └── cpu@0 (compatible = “arm,cortex-a15”)
├── memory@60000000 (reg = base + size)
└── chosen
└── bootargs = “console=ttyAMA0 root=/dev/vda rw”
Where Device Tree Sources Live
| Location | What’s There |
|---|---|
arch/$ARCH/boot/dts/ in the kernel tree |
The main collection of upstream board .dts files, organized by SoC vendor |
arch/$ARCH/dts/ in U-Boot source |
A smaller set of device trees used by the bootloader itself, sometimes shared with the kernel copy |
| Board support package from a hardware vendor | If you bought a third-party board, its .dts file should ship as part of the BSP |
Hands-On: Compile and Inspect Your Own Device Tree
Let’s take the original example above, compile it into a binary blob, and then decompile it back to confirm the tool chain round-trips correctly — a good sanity check habit for any free embedded linux course exercise.
# Save the source above as ep-ref-board.dts, then compile it
dtc -I dts -O dtb -o ep-ref-board.dtb ep-ref-board.dts
# Decompile the binary blob back to source, to inspect what dtc actually stored
dtc -I dtb -O dts ep-ref-board.dtb
Expected output from the decompile step (trimmed for readability):
/dts-v1/;
/ {
model = "EP Course Reference Board";
compatible = "ep,course-ref-board";
#address-cells = <0x1>;
#size-cells = <0x1>;
chosen {
bootargs = "console=ttyAMA0 root=/dev/vda rw";
};
memory@60000000 {
device_type = "memory";
reg = <0x60000000 0x10000000>;
};
cpus {
#address-cells = <0x1>;
#size-cells = <0x0>;
cpu@0 {
compatible = "arm,cortex-a15";
device_type = "cpu";
reg = <0x0>;
};
};
};
If you see your nodes and properties reflected back correctly, your syntax was valid. This round-trip is one of the fastest ways to catch a typo before ever booting real hardware — much faster than debugging a silent boot failure.
Real-World Use Cases
- Supporting multiple board revisions from a single kernel binary
- Describing GPIO, I2C, and SPI peripheral wiring without touching driver code
- Using device tree overlays to add or modify hardware descriptions at runtime, common on hobbyist boards
Common Mistakes and Troubleshooting
Forgetting #address-cells / #size-cells
Child nodes with reg properties become unparsable without these, since they define how many 32-bit cells make up an address and a size.
Duplicate unit-address without a matching node name
dtc will warn or error if two sibling nodes at the same level share an address without a distinguishing name.
Wrong compatible string
Drivers match against the compatible property exactly — a typo here means the kernel simply won’t bind a driver to that hardware, with no obvious error message.
Best Practices
- Always round-trip a new device tree through
dtcbefore testing on hardware - Keep board-specific device trees separate from shared SoC-level include files
- Use vendor-prefixed
compatiblestrings, e.g.vendor,device, to avoid collisions
Summary and Key Takeaways
The device tree replaced fragile, hard-coded platform data with a flexible, hierarchical hardware description that a single kernel binary can parse for many different boards. Nodes and properties form the entire syntax, the chosen node ties directly back into the kernel command line from the previous lecture, and the dtc compiler lets you validate your own device trees before ever touching real hardware.
Conclusion
You now have the core mental model this free linux kernel development course builds on for the rest of the driver-focused chapters: hardware description lives in the device tree, driver logic lives in the kernel, and the two are joined at boot time through the compatible property. From here, future lectures will start writing drivers that actually bind to nodes like the ones you just compiled.
FAQ
What file extension do device tree sources use?
Human-readable source files use .dts (or .dtsi for shared include files), and the compiled binary blob passed to the kernel uses .dtb.
What tool compiles a device tree?
dtc, the device tree compiler, converts .dts source into a .dtb binary blob, and can also decompile a .dtb back to readable source.
Where did the device tree format originate?
It’s derived from Open Firmware, an older bootloader standard formalized as IEEE 1275-1994, originally used on PowerPC-based systems.
What is the chosen node for?
It carries boot-time configuration such as the kernel command line and, optionally, the location of an initial RAM disk.
Why do node names include an @ symbol?
The part after the @ is a unit address that disambiguates multiple sibling nodes of the same type, such as several CPU cores or memory regions.
Can one kernel binary really support many different boards?
Yes — that’s the main benefit of the device tree. The kernel binary stays generic, and a separate small device tree blob per board supplies the hardware-specific details.
What happens if the compatible string doesn’t match any driver?
The kernel simply won’t bind a driver to that node — the hardware will be invisible to Linux with no loud error, which makes it a common early debugging trap.
Continue This Free Linux Kernel Development Course
Up next: writing your first driver that binds to a device tree node.
