Skip to Content

From Hardware to Software: The Role of Device Trees in Embedded Systems


Bhargav Patel


Introduction: The Need for Device Trees in Embedded Systems

In embedded Linux, the kernel must know the exact hardware configuration of the system—memory layout, peripherals, GPIOs, and communication buses (I2C, SPI, UART). Traditionally, this was done by hardcoding hardware details into the kernel, leading to:

  • Bloat (different kernel versions for each board)
  • Maintenance nightmares (any hardware change required kernel modification)
  • Poor scalability (difficulty supporting new devices)

Device Trees (DT) solve this by providing a hardware description language that the Linux kernel interprets at boot.

1. Understanding Device Tree Structure

A Device Tree consists of:

  • Nodes (represent devices, buses, or subsystems)
  • Properties (key-value pairs defining hardware parameters)
  • Hierarchy (parent-child relationships mirroring hardware connections)

Example: Basic Device Tree Syntax

c

Copy

/dts-v1/;  // Device Tree version
/ {        // Root node
    compatible = "raspberrypi,4-model-b";  // Board compatibility
    model = "Raspberry Pi 4 Model B";      // Human-readable name

    // CPU node
    cpus {
        #address-cells = <1>;  // Number of cells for addressing child nodes
        #size-cells = <0>;     // Size representation (not used here)

        cpu0: cpu@0 {          // CPU label and address
            compatible = "arm,cortex-a72";
            device_type = "cpu";
            reg = <0>;         // Register index
        };
    };

    // Memory node
    memory@0 {
        device_type = "memory";
        reg = <0x00000000 0x3b400000>;  // Start address and size
    };

    // I2C Controller
    i2c1: i2c@7e804000 {
        compatible = "brcm,bcm2835-i2c";
        reg = <0x7e804000 0x1000>;       // Physical address and length
        interrupts = <2 21>;              // IRQ number and flags
        clocks = <&i2c_clk>;
        status = "disabled";              // Disabled by default
    };
};

Key Elements Explained

ComponentPurpose
/dts-v1/;Declares Device Tree syntax version (always required).
/ { ... };Root node containing all hardware descriptions.
compatibleMatches the board with kernel drivers (e.g., "raspberrypi,4-model-b").
#address-cellsDefines how many 32-bit values are needed for addressing child nodes.
reg = <0>;Specifies register addresses (e.g., CPU index 0).
interrupts = <2 21>;Defines interrupt lines (2 = controller, 21 = line).
status = "disabled";Controls whether a device is active ("okay") or inactive ("disabled").

2. How Device Trees Work on Raspberry Pi

Boot Process with Device Trees

  1. Bootloader (start.elf) loads:
    • Kernel (kernel8.img)
    • Device Tree Blob (bcm2711-rpi-4-b.dtb)
  2. Kernel parses the DTB to detect hardware.
  3. Drivers bind to devices based on compatible properties.

Viewing the Active Device Tree

bash

Copy

# Extract the live Device Tree
dtc -I fs /proc/device-tree > current.dts

Output Example:

c

Copy

/ {
    compatible = "raspberrypi,4-model-b";
    model = "Raspberry Pi 4 Model B";
    ...
    i2c1: i2c@7e804000 {
        compatible = "brcm,bcm2835-i2c";
        reg = <0x7e804000 0x1000>;
    };
};

3. Practical Device Tree Customization

Case 1: Enabling I2C via Overlay

Problem: I2C1 is disabled by default on Raspberry Pi.

Solution: Use an overlay in /boot/config.txt:

ini

Copy

dtparam=i2c_arm=on
dtoverlay=i2c-sensor,addr=0x48  # Enable I2C and add a sensor at 0x48

What Happens Behind the Scenes?

  1. The firmware applies i2c1 node changes:
    diff
    Copy
    status = "okay";  // Changed from "disabled"
  2. The kernel loads the i2c-bcm2835 driver (matched via compatible).

Case 2: Custom GPIO Configuration

Problem: Need to configure GPIO 17 as an output for an LED.

Solution: Create a custom overlay (led-gpio.dts):

c

Copy

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835";

    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            led_pin: led_pin {
                brcm,pins = <17>;       // GPIO17
                brcm,function = <1>;   // Output (0=input, 1=output)
                brcm,pull = <0>;        // No pull-up/down
            };
        };
    };
};

Compile and Apply:

bash

Copy

dtc -@ -I dts -O dtb -o /boot/overlays/led-gpio.dtbo led-gpio.dts

Add to /boot/config.txt:

ini

Copy

dtoverlay=led-gpio

Result:

  • GPIO17 is now configured as an output at boot.
  • Control it via /sys/class/gpio/gpio17/value.

4. Debugging Device Trees

Common Issues & Fixes

ProblemDebugging CommandSolution
Overlay not applied`dmesggrep -i "dtoverlay"`Check syntax in /boot/config.txt.
Missing driver bindingcat /proc/device-tree/compatibleVerify compatible property.
Incorrect register mapdevmem2 0x7e804000 (I2C1 physical address)Validate reg values in DTS.

5. Business Benefits of Device Trees

1. Reduced Development Time

  • Example: A company using Raspberry Pi for industrial controllers reduced BSP development from 6 weeks to 1 week by switching to Device Trees.

2. Lower Maintenance Costs

  • Single kernel image supports multiple hardware revisions via different DTBs.

3. Easier Compliance Testing

  • Isolated hardware changes (via overlays) minimize re-certification efforts.

Conclusion

Device Trees decouple hardware and software in embedded Linux, enabling:

✅ Portability (One kernel, multiple boards)

✅ Maintainability (No kernel patching for hardware changes)

✅ Scalability (Dynamic overlays for custom configurations)

Next Steps:

  1. Experiment with dtc to decompile your Pi’s .dtb file.
  2. Create a custom overlay for your hardware.

Need Help? Contact our embedded Linux team for Device Tree consulting!

Appendix: Key Commands Cheat Sheet

CommandPurpose
dtc -I dtb -O dts -o output.dts input.dtbDecompile DTB to DTS.
`sudo vcdbg log msggrep dtb`Check which DTB was loaded at boot.
fdtdump /boot/bcm2711-rpi-4-b.dtbInspect DTB contents.
Exploring the Diverse World of Embedded Linux: A Guide to Different Distributions and Use Cases