Comprehensive explanation of Device Tress
Let’s break down the device tree concept step by step, using concrete Raspberry Pi examples and real-world scenarios. I’ll explain it as if we’re working with a Raspberry Pi 4 (but the principles apply broadly).
Part 1: Device Tree Basics – The "Map" of Hardware
What Problem Does the Device Tree Solve?
Imagine you’re the Linux kernel. When you boot up, you need to know:
Before device trees, this information was hardcoded into the kernel (in C code). This meant a different kernel was needed for every hardware variation (e.g., Raspberry Pi 3 vs. Pi 4). Device trees solve this by externalizing hardware descriptions into separate files (.dts or .dtb).
Part 2: Anatomy of a Device Tree – A Raspberry Pi Example
Let’s look at a simplified device tree for a Raspberry Pi 4.
(Note: Real Pi DTS files are complex; this is a simplified version for clarity.)
Device Tree Structure
/dts-v1/; // Device Tree Version 1
/ { // Root node (represents the entire system)
compatible = "raspberrypi,4-model-b"; // Matches the board
model = "Raspberry Pi 4 Model B";
// Memory description
memory@0 { // Node name: "memory" at address 0
device_type = "memory";
reg = <0 0 0x3b400000>; // RAM: Starts at 0, size 0x3b400000 (1GB)
};
// CPU description
cpus {
#address-cells = <1>; // CPU addresses are 1 cell (number)
#size-cells = <0>; // No size field for CPUs
cpu@0 { // First CPU core
device_type = "cpu";
compatible = "arm,cortex-a72"; // Tells kernel to use Cortex-A72 driver
reg = <0>; // CPU ID = 0
};
cpu@1 { // Second CPU core
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <1>;
};
// ... (more CPU cores)
};
// System-on-Chip (SoC) peripherals
soc {
#address-cells = <2>; // Addresses are 2 cells (e.g., 0x7e200000)
#size-cells = <1>; // Sizes are 1 cell
ranges = <0x7e000000 0x0 0xfe000000 0x1800000>; // Maps peripheral bus addresses
// GPIO controller
gpio: gpio@7e200000 { // Node label: 'gpio'
compatible = "brcm,bcm2711-gpio"; // Matches the GPIO driver
reg = <0x7e200000 0xb4>; // Base address & size
gpio-controller; // Marks this node as a GPIO controller
#gpio-cells = <2>; // Specifies how many cells (values) describe a GPIO pin
};
// UART (serial port)
uart0: serial@7e201000 {
compatible = "brcm,bcm2835-pl011"; // Driver for PL011 UART
reg = <0x7e201000 0x1000>; // Address range
interrupts = <2 25>; // Interrupt ID 25, triggered by interrupt controller #2
status = "okay";
};
};
};
Key Concepts Explained
1. Nodes and Hierarchy
/ (root)
├── cpus
│ ├── cpu@0
│ └── cpu@1
└── soc
├── gpio@7e200000
└── serial@7e201000
2. Properties
3. Labels and References
4. #address-cells and #size-cells
Part 3: How the Kernel Uses the Device Tree
Step-by-Step Boot Process (Raspberry Pi)
The Raspberry Pi firmware (start.elf) loads the kernel (kernel8.img) and device tree blob (bcm2711-rpi-4-b.dtb) from the SD card.
The DTB is a compiled binary version of the .dts file.
The kernel parses the DTB to discover hardware:
Memory: Reserves RAM regions.
CPU Cores: Initializes all Cortex-A72 cores.
Peripherals: Matches drivers using compatible strings.
Example: The node with compatible = "brcm,bcm2835-pl011" triggers the PL011 UART driver.
The parsed device tree is exposed in /proc/device-tree. For example:
Recommended by LinkedIn
/proc/device-tree/
├── compatible
├── model
├── cpus
│ ├── cpu@0
│ └── cpu@1
└── soc
├── gpio@7e200000
└── serial@7e201000
Part 4: Device Tree Overlays – Dynamic Configuration
What Are Overlays?
Overlays (.dtbo files) are "patches" to the base device tree. They’re used to:
Example: Adding an I2C Temperature Sensor
i2c1: i2c@7e804000 {
compatible = "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
status = "disabled"; // Disabled by default
};
/dts-v1/;
/plugin/; // Declare this as an overlay
// Fragment 0: Enable I2C1
&i2c1 {
status = "okay"; // Enable the I2C controller
clock-frequency = <100000>; // Set I2C speed to 100 kHz
// Fragment 1: Add the temperature sensor
temp_sensor: lm75@48 {
compatible = "nxp,lm75";
reg = <0x48>; // I2C address 0x48
};
};
Compile the overlay:
dtc -@ -I dts -O dtb -o i2c-sensor.dtbo i2c-sensor-overlay.dts
Copy i2c-sensor.dtbo to /boot/overlays/.
Edit /boot/config.txt to add:
dtoverlay=i2c-sensor
Result:
Part 5: Debugging Device Trees
Common Tools
Decompile a .dtb to .dts:
dtc -I dtb -O dts -o extracted.dts /boot/bcm2711-rpi-4-b.dtb
fdtdump:
Quickly view a device tree blob:
fdtdump /boot/bcm2711-rpi-4-b.dtb
/proc/device-tree:
Explore the parsed device tree at runtime:
# List all nodes/properties:
find /proc/device-tree -type f
# Read the 'compatible' property:
cat /proc/device-tree/compatible
# Output: raspberrypi,4-model-bbrcm,bcm2711
Part 6: Why This Matters for Raspberry Pi Users
Summary
Next time you boot a Raspberry Pi, remember: the kernel isn’t hardcoded for your board. It’s the device tree telling it exactly where everything is! 🎯
Embedded SWE @ Piod | V2X | Automative IoT
1moVery informative