1. 设备树基础:起源、语法结构与核心概念
设备树(Device Tree,DT)是嵌入式 Linux 系统中用于描述硬件拓扑结构的一种数据结构。它解决了传统板级文件(board-*.c)中硬编码硬件信息导致的代码冗余与维护困难问题,成为 ARM、RISC-V 等架构的主流硬件描述方案。
1.1 设备树的起源与设计动机
在 Linux 内核早期,ARM 架构的每个开发板都需要在 arch/arm/mach-* 下编写独立的板级文件,其中包含寄存器地址、中断号、时钟频率、GPIO 复用等硬件信息。这种模式导致内核代码中充斥着大量重复的硬件描述,且每增加一个新板卡就需要修改内核源码。
核心痛点: 硬件描述与内核代码紧耦合 → 板级文件膨胀 → 维护成本指数级上升 → 无法支持单一内核镜像启动多个平台。
设备树将硬件描述从内核源码中剥离,以独立的数据结构文件(.dts)存在,由 bootloader(如 U-Boot)传递给内核。内核在启动时解析设备树,动态注册平台设备、配置引脚复用、初始化时钟与中断控制器。
| 对比维度 | 传统板级文件方式 | 设备树方式 |
|---|---|---|
| 硬件描述位置 | 内核源码 arch/arm/mach-* |
独立 .dts 文件,与内核分离 |
| 新增板卡代价 | 需修改内核、重新编译 | 仅需编写 .dts,无需改内核 |
| 多平台支持 | 需编译多个内核镜像 | 单一内核镜像 + 不同 dtb |
| 维护性 | 差,代码冗余严重 | 好,硬件描述集中管理 |
关键里程碑: Linux 内核从 3.x 开始强制要求 ARM 架构使用设备树,PowerPC、RISC-V、MIPS 等架构也相继采用。
1.2 设备树语法结构:节点与属性
设备树源文件(.dts)采用树形层次结构,每个硬件设备或总线被抽象为一个节点(node),节点内部通过属性(property)描述硬件参数。根节点是 /,所有设备节点挂载在根节点下。
/dts-v1/;
/ {
compatible = "vendor,board-name";
#address-cells = <1>;
#size-cells = <1>;
cpu@0 {
compatible = "arm,cortex-a7";
reg = <0x00 0x100>;
device_type = "cpu";
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512MB RAM */
};
uart0: serial@11000000 {
compatible = "vendor,uart";
reg = <0x11000000 0x1000>;
interrupts = <0 59 4>;
clocks = <&clk_uart0>;
pinctrl-0 = <&uart0_pins>;
pinctrl-names = "default";
status = "okay";
};
};
常见错误: 忘记在文件头部添加 /dts-v1/; 版本声明,或节点名称中 @ 后的地址与 reg 属性不匹配,会导致编译警告或设备无法正确识别。
1.3 节点与属性的核心规则
| 语法元素 | 格式 | 说明 |
|---|---|---|
| 节点名称 | node-name@unit-address |
@ 后为设备基地址,用于区分同名节点 |
| 属性值 | property = <value>; |
支持 32/64 位整数、字符串、字节数组、phandle |
| 标签(label) | uart0: serial@11000000 |
用于在其他节点中通过 &uart0 引用 |
| 包含文件 | #include "soc.dtsi" |
引入 .dtsi 头文件,实现 SoC 级描述复用 |
| 状态属性 | status = "okay" / "disabled" |
控制设备是否启用,常用于板级覆盖 |
1.4 dts / dtsi / dtb 的关系与编译流程
设备树源文件分为两种:.dts(板级描述文件)和 .dtsi(SoC 级公共描述文件,类似头文件)。.dtb 是编译后的二进制文件,由 bootloader 加载并传递给内核。
设计原则: .dtsi 描述 SoC 内部固定硬件(CPU、中断控制器、时钟、DMA),.dts 描述板级外设(型号、内存大小、引脚复用、外设启停)。
# 编译流程
dtc -I dts -O dtb -o board.dtb board.dts
# 反编译(调试用)
dtc -I dtb -O dts -o board_decompiled.dts board.dtb
| 文件类型 | 扩展名 | 作用 | 是否可编译 |
|---|---|---|---|
| 设备树源文件 | .dts | 板级硬件描述,包含具体外设配置 | 是(通过 dtc) |
| 设备树头文件 | .dtsi | SoC 级公共描述,被 .dts 包含 | 否(仅被包含) |
| 设备树二进制 | .dtb | 编译后的二进制,内核直接使用 | 否(已编译) |
调试技巧: 使用 dtc -I dtb -O dts 反编译 .dtb 文件,可以验证 bootloader 实际传递给内核的设备树内容是否与预期一致。
1.5 设备树层次结构与硬件映射关系
下面这张 SVG 图展示了从 .dts 源文件到内核设备模型的核心映射流程,帮助理解设备树如何驱动硬件初始化。
1.6 实战要点:设备树调试常用命令
- 查看当前系统设备树:
ls /proc/device-tree/— 内核启动后会将 dtb 解析为 proc 文件系统节点 - 检查设备树节点状态:
cat /proc/device-tree/soc/serial@11000000/status— 输出 "okay" 或 "disabled" - 验证 dtb 是否被正确加载:
strings /proc/device-tree/compatible— 查看板级 compatible 字符串 - 反编译 dtb 做对比:
dtc -I dtb -O dts -o /tmp/current.dts /sys/firmware/fdt— 获取内核实际使用的设备树
避坑指南: 如果 /proc/device-tree/ 下缺少某个节点,首先检查 .dts 中该节点的 status 是否为 "okay",其次确认 bootloader 是否传递了正确的 dtb 文件路径。