2. 设备树语法入门:节点、属性、标签、路径、别名
设备树(Device Tree,DT)是嵌入式 Linux 内核中用于描述硬件拓扑结构的一种数据结构。它使用树形层级结构将 CPU、内存、外设、总线等硬件资源组织起来,使内核能够在不硬编码硬件信息的情况下适配不同板级。本章将系统讲解设备树的五个核心语法要素:节点(node)、属性(property)、标签(label)、路径(path)和别名(aliases)。
2.1 节点(Node)—— 设备树的骨架
节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线、控制器或功能模块。节点通过层级嵌套形成树状结构,根节点是整棵树的起点。
2.1.1 节点定义语法
/dts-v1/;
/ {
// 根节点
model = "Embedded Linux Board";
compatible = "vendor,board";
cpu@0 {
// CPU 节点
compatible = "arm,cortex-a7";
reg = <0x0>;
};
serial@1c28000 {
// 串口节点
compatible = "ns16550";
reg = <0x1c28000 0x1000>;
interrupts = <0 38 4>;
};
};
节点名称格式为 node-name@unit-address,其中 @unit-address 部分可选,通常用于标识设备在总线上的地址。
2.1.2 节点层级关系
2.2 属性(Property)—— 描述设备特征
属性是附着在节点上的键值对,用于描述设备的硬件特征、配置参数和连接关系。每个属性都有一个名称和一个值,值的类型可以是字符串、整数、字节数组或它们的组合。
2.2.1 常用属性类型
| 属性类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 字符串 | "string" |
compatible = "arm,cortex-a7"; |
用于标识设备兼容性 |
| 整数 | <value> |
reg = <0x1c28000 0x1000>; |
地址、大小、中断号等 |
| 字节数组 | [hex bytes] |
local-mac-address = [00 11 22 33 44 55]; |
MAC 地址、UUID 等 |
| 字符串列表 | "str1", "str2" |
compatible = "vendor,device", "generic-device"; |
多个兼容字符串 |
| 布尔值 | 仅属性名,无值 | status = "disabled"; |
表示设备启用/禁用状态 |
2.2.2 标准属性详解
- compatible:设备兼容性字符串,格式为
"厂商,设备型号",内核通过匹配该字符串加载对应的驱动程序。 - reg:描述设备在总线上的地址空间,格式为
<地址 长度>,支持多组地址。 - interrupts:描述设备使用的中断号、触发类型等信息,格式依赖中断控制器。
// 完整属性示例
uart0: serial@1c28000 {
compatible = "ns16550a", "ns16550"; // 优先匹配前者
reg = <0x1c28000 0x1000>; // 基地址 0x1c28000,长度 4KB
interrupts = <0 38 4>; // SPI 中断号 38,高电平触发
clock-frequency = <24000000>; // 时钟频率 24MHz
status = "okay"; // 设备启用
};
2.3 标签(Label)—— 节点的引用标识
标签是附加在节点名称前面的标识符,用于在其他位置引用该节点。标签以冒号结尾,紧跟在节点名称之前。
2.3.1 标签定义与引用
// 定义标签
uart0: serial@1c28000 {
compatible = "ns16550";
reg = <0x1c28000 0x1000>;
};
// 在其他位置通过 & 引用标签
&uart0 {
status = "okay";
clock-frequency = <24000000>;
};
2.3.2 标签的典型应用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 板级覆盖 | 在 .dtsi 中定义节点,在 .dts 中通过标签修改 | &uart0 { baud = <115200>; }; |
| 引脚复用 | 引用 pinctrl 节点配置引脚功能 | pinctrl-0 = &uart0_pins; |
| 时钟/电源引用 | 引用时钟或电源管理节点 | clocks = &osc24M; |
2.4 路径(Path)—— 节点的唯一地址
路径是从根节点到目标节点的完整层级描述,使用斜杠分隔节点名称。路径是设备树中定位节点的绝对方式,类似于文件系统中的绝对路径。
2.4.1 路径格式
/soc/serial@1c28000 // 从根节点到串口设备的路径
/cpus/cpu@0 // 从根节点到 CPU0 的路径
/ // 根节点路径
2.4.2 路径与标签的对比
| 特性 | 路径(Path) | 标签(Label) |
|---|---|---|
| 定位方式 | 绝对路径,从根节点开始 | 符号引用,类似变量名 |
| 可读性 | 长路径不易读 | 短标签易读易记 |
| 唯一性 | 天然唯一 | 需保证不重复 |
| 使用场景 | 内核内部查找、调试 | 设备树源文件内引用 |
2.5 别名(Aliases)—— 简化路径的快捷方式
别名是在根节点的 aliases 子节点中定义的键值对,用于为常用设备路径提供简短名称。别名机制简化了设备树中的路径引用,尤其在 bootloader 与内核之间传递设备信息时非常有用。
2.5.1 别名定义语法
/ {
aliases {
serial0 = &uart0;
serial1 = &uart1;
ethernet0 = &gmac0;
mmc0 = &mmc0;
};
uart0: serial@1c28000 { /* ... */ };
uart1: serial@1c28400 { /* ... */ };
gmac0: ethernet@1c30000 { /* ... */ };
};
2.5.2 别名的典型用途
- bootloader 与内核通信:U-Boot 通过修改
chosen节点中的stdout-path属性引用别名,如stdout-path = "serial0:115200n8"。 - 设备编号固定:确保系统启动后设备名称(如
/dev/ttyS0)与硬件对应关系稳定。 - 简化设备树覆盖:在设备树覆盖(overlay)中通过别名快速定位目标节点。
设备类型+序号 的命名规范。
2.6 五要素综合示例
下面是一个完整的设备树片段,展示了节点、属性、标签、路径和别名的协同使用:
/dts-v1/;
/ {
model = "ARM Embedded Development Board";
compatible = "arm,vexpress";
aliases {
serial0 = &uart0;
serial1 = &uart1;
mmc0 = &mmc0;
};
chosen {
stdout-path = "serial0:115200n8";
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
reg = <0x0>;
device_type = "cpu";
};
};
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@1c28000 {
compatible = "ns16550a";
reg = <0x1c28000 0x1000>;
interrupts = <0 38 4>;
clock-frequency = <24000000>;
status = "okay";
};
uart1: serial@1c29000 {
compatible = "ns16550a";
reg = <0x1c29000 0x1000>;
interrupts = <0 39 4>;
clock-frequency = <24000000>;
status = "disabled";
};
mmc0: mmc@1c11000 {
compatible = "arm,pl180", "arm,primecell";
reg = <0x1c11000 0x1000>;
interrupts = <0 42 4>;
status = "okay";
};
};
};
2.7 五要素对比总结
| 要素 | 定义位置 | 主要用途 | 唯一性要求 | 引用方式 |
|---|---|---|---|---|
| 节点 | 设备树任意层级 | 表示硬件设备或功能模块 | 同级节点名称唯一 | 路径或标签 |
| 属性 | 节点内部 | 描述设备特征和配置 | 同一节点内属性名唯一 | 直接访问 |
| 标签 | 节点名称前 | 提供节点引用标识 | 全局唯一 | &label |
| 路径 | 从根节点开始 | 绝对定位节点 | 天然唯一 | /node/subnode |
| 别名 | /aliases 节点内 |
简化路径引用 | 别名名称唯一 | serial0 等短名称 |
2.8 常见错误与调试技巧
- 节点名称重复(同级节点名称冲突)
- 标签未定义就引用(编译报错)
- 路径拼写错误(大小写敏感)
- 属性值类型不匹配(如字符串写成了整数)
- 别名与节点名称冲突
调试建议:使用 dtc -I dts -O dtb -o output.dtb input.dts 编译设备树,通过编译错误信息定位语法问题。编译成功后,使用 dtc -I dtb -O dts output.dtb 反编译验证生成的二进制文件是否符合预期。