ES: Device Tree in Embedded Linux

The device tree is a data structure that describes the physical hardware of an embedded system to the Linux kernel. By decoupling hardware description from kernel code, the device tree makes it possible to use the same kernel binary across different hardware configurations without recompilation.

The device tree solves a fundamental problem in embedded Linux: the kernel needs to know what hardware is present, but hardcoding that information into the kernel binary is inflexible and wasteful. The device tree provides a clean separation between the kernel and hardware description.


The Problem: Hardware Description

To manage hardware resources, the Linux kernel needs to know:

  • Which I/O devices are present
  • Their memory addresses and interrupt lines
  • How they connect to each other
  • Configuration parameters for each peripheral

There are two ways to provide this information:

Option 1: Hardcode into the Kernel Binary

The hardware definition is compiled directly into the kernel source code.

Problem: Any change to the hardware definition requires recompiling the entire kernel. For a product with multiple hardware revisions, this creates a different kernel binary for every board variant.

Option 2: Device Tree Blob (DTB)

The hardware definition is stored in a separate file — the device tree blob — that is passed to the kernel by the bootloader at boot time.

Advantage: Hardware definitions can be changed by recompiling only the device tree source file. The kernel binary remains unchanged.

md
Without Device Tree:
Hardware Change --> Recompile Kernel --> Flash new kernel

With Device Tree:
Hardware Change --> Recompile DTB only --> Flash new DTB
                                          (kernel unchanged)


Device Tree Files

The device tree toolchain involves three file types:

File TypeExtensionDescription
Device Tree Source.dtsHuman-readable text description of hardware
Device Tree Include.dtsiShared device tree fragments (included by .dts)
Device Tree Blob.dtbCompiled binary form passed to the kernel

md
Device Tree Source (.dts)
        |
        | dtc (device tree compiler)
        v
Device Tree Blob (.dtb)
        |
        | bootloader passes to kernel
        v
Linux Kernel reads and initialises drivers


Device Tree Blob (DTB)

A Device Tree Blob (DTB) is the binary representation of the device tree. It is:

  • Produced by compiling the device tree source (.dts) file with the Device Tree Compiler (dtc)
  • Stored on the same storage medium as the kernel (eMMC, SD card, NOR flash)
  • Passed to the kernel by the bootloader as part of the boot process

The kernel reads the DTB at startup and uses the information to:

  • Discover which drivers to initialise
  • Configure each driver with the correct parameters (base address, IRQ number, clock frequency, etc.)
  • Understand the interconnect topology between devices

Device Tree Source Structure

A device tree source file describes hardware as a tree of nodes, where each node represents a hardware component.

md
/dts-v1/;

/ {
    model = "My Embedded Board";
    compatible = "vendor,board";

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  /* 256 MB at 0x80000000 */
    };

    serial@10009000 {
        compatible = "arm,pl011";
        reg = <0x10009000 0x1000>;
        interrupts = <0 5 4>;
        clocks = <&uart_clk>;
    };

    ethernet@10010000 {
        compatible = "smsc,lan9118";
        reg = <0x10010000 0x10000>;
        interrupts = <0 15 4>;
    };
};

Key node properties:

PropertyDescription
compatibleString that matches a driver in the kernel
regMemory address and size of the device
interruptsInterrupt controller and interrupt number
clocksClock source reference

How the Bootloader Passes the DTB

During the boot sequence, the bootloader:

  1. Loads the kernel image into RAM
  2. Loads the DTB into RAM (at a different address)
  3. Passes the DTB address to the kernel via a CPU register before jumping to the kernel entry point

md
Boot Sequence:
Bootloader loads kernel to 0x80008000
Bootloader loads DTB to 0x83000000
        |
        | Set register r2 = 0x83000000 (DTB address)
        v
Kernel entry point
        |
        | Read DTB from r2
        v
Kernel parses device tree
        |
        v
Kernel initialises drivers based on device tree


Device Tree in U-Boot

When using U-Boot as the bootloader, the DTB is typically loaded and passed to the kernel using boot commands:

bash
# Load kernel and DTB from SD card
load mmc 0:1 ${kernel_addr_r} zImage
load mmc 0:1 ${fdt_addr_r} board.dtb

# Boot with device tree
bootz ${kernel_addr_r} - ${fdt_addr_r}


Modifying the Device Tree

When you add new hardware to an existing board, or change peripheral configuration, you only need to:

  1. Edit the .dts file
  2. Recompile to produce a new .dtb
  3. Flash the new .dtb to the board

The kernel does not need to be recompiled. This is a major time saver during hardware bring-up and product iterations.

bash
# Compile a device tree source file
dtc -I dts -O dtb -o board.dtb board.dts

# Decompile a DTB back to readable source
dtc -I dtb -O dts -o board.dts board.dtb


Final Thoughts

The device tree is one of the most important concepts in embedded Linux development. It enables:

  • Reuse of the same kernel binary across different hardware
  • Maintainability — hardware changes require only DTB updates
  • Clarity — the hardware description is explicit and readable

md
Device Tree Role:
Hardware Platform
    |
    | described by
    v
Device Tree Source (.dts)
    |
    | compiled to
    v
Device Tree Blob (.dtb)
    |
    | passed to kernel by bootloader
    v
Linux Kernel: initialises correct drivers

Understanding the device tree is essential for embedded Linux board bring-up, driver development, and hardware customisation.