4. 节点命名与路径:节点名称规范、unit-address、标签(label)与节点引用(&)的用法
设备树(Device Tree)通过树形结构描述硬件资源,每个节点(node)的命名、寻址与引用方式直接决定了驱动能否正确匹配设备。本章深入讲解节点名称的规范格式、unit-address 的作用、标签(label)的定义以及通过 & 符号进行节点引用的核心用法。
核心要点:节点名称 = 通用名称 + unit-address;标签是节点的“别名”,用于在 DTS 文件中跨文件引用;
& 是编译阶段的节点追加/修改操作符。
4.1 节点名称规范
设备树规范要求每个节点必须遵循 通用名称@unit-address 的格式。通用名称描述设备类型,unit-address 描述设备在总线上的地址或寄存器偏移。
| 组件 | 说明 | 示例 |
|---|---|---|
| 通用名称 | 小写字母、数字、连字符组成,描述设备功能(如 uart、i2c、gpio) |
serial、ethernet、mmc |
| @unit-address | 可选部分,表示设备在父总线上的地址(寄存器基址、片选号、端口号等) | @ff000000、@1、@0 |
| 完整节点名 | 通用名称 + @ + unit-address(若存在) | uart@ff000000、i2c@1 |
命名避坑:通用名称应使用标准设备类型名(如
gpio 而非 my_gpio),避免使用下划线;unit-address 必须与 reg 属性的第一个地址值一致,否则编译报错。
// 正确示例
uart0: serial@ff000000 {
compatible = "ns16550";
reg = <0x0 0xff000000 0x0 0x1000>;
};
// 错误示例:unit-address 与 reg 不匹配
uart0: serial@ff001000 { // 编译警告/错误
compatible = "ns16550";
reg = <0x0 0xff000000 0x0 0x1000>;
};
4.2 unit-address 的寻址规则
unit-address 的格式由父节点的 #address-cells 和 #size-cells 决定。对于 32 位系统,通常使用 1 个 cell 表示地址;对于 64 位系统,使用 2 个 cells。
| 父节点属性 | unit-address 格式 | 示例 |
|---|---|---|
#address-cells = <1> |
单个 32 位十六进制数 | @ff000000 |
#address-cells = <2> |
两个 32 位十六进制数,用逗号分隔 | @0,ff000000 |
无地址空间(如 chosen) |
不使用 @unit-address |
chosen、aliases |
// 64 位地址示例
bus@0 {
#address-cells = <2>;
#size-cells = <2>;
memory@0,80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x80000000>;
};
};
注意:如果节点没有
reg 属性(如 chosen、aliases),则不能包含 @unit-address 部分。编译时 dtc 会对此进行检查。
4.3 标签(label)的定义与作用
标签(label)是节点的一个“别名”,在节点名称前使用 标签名: 定义。标签的主要用途:
- 跨文件引用:在多个 DTSI 文件中通过标签引用同一节点
- 简化路径:避免书写冗长的绝对路径
- 节点覆盖:通过
&标签在其它文件中追加或修改节点属性
// 定义标签
uart0: serial@ff000000 {
compatible = "ns16550";
reg = <0x0 0xff000000 0x0 0x1000>;
interrupts = <0 33 4>;
};
// 在另一个 DTSI 文件中通过标签引用
&uart0 {
clock-frequency = <24000000>;
status = "okay";
};
标签命名建议:使用有意义的名称,如
uart0、i2c1、mmc0,避免使用 node0、temp 等无意义名称。标签在全局范围内必须唯一。
4.4 节点引用(&)的用法
& 符号是设备树编译阶段的引用操作符,用于在 DTS 文件中引用已定义的标签,实现节点的属性追加、修改或覆盖。
| 用法 | 说明 | 示例 |
|---|---|---|
&标签 { ... }; |
引用标签对应的节点,在大括号内添加/修改属性 | &uart0 { status = "okay"; }; |
标签: 节点名@地址 { ... }; |
定义标签并同时定义节点 | uart0: serial@ff000000 { ... }; |
&标签 作为 phandle |
在属性中引用另一个节点(如时钟、中断控制器) | clocks = &clkc, &uart0; |
// 引用作为 phandle 使用
clkc: clock-controller@ff760000 {
compatible = "rockchip,rk3399-cru";
reg = <0x0 0xff760000 0x0 0x1000>;
#clock-cells = <1>;
};
uart0: serial@ff180000 {
compatible = "rockchip,rk3399-uart";
reg = <0x0 0xff180000 0x0 0x100>;
clocks = &clkc, &clkc; // 引用 clkc 节点
clock-names = "baud", "module";
};
编译原理:
&标签 在 dtc 编译时会被展开为节点的绝对路径(如 /soc/serial@ff180000),然后进行属性合并。如果引用的标签不存在,编译会报错。
4.5 节点路径与引用关系图
下图展示了设备树中节点名称、标签、路径与引用之间的完整关系:
4.6 节点路径的完整语法
设备树中节点的路径使用 绝对路径 和 相对路径 两种方式:
| 路径类型 | 语法 | 示例 | 说明 |
|---|---|---|---|
| 绝对路径 | 以 / 开头,从根节点开始 |
/soc/serial@ff000000 |
唯一标识,可在任何位置使用 |
| 标签引用 | &标签名 |
&uart0 |
编译时展开为绝对路径 |
| 别名路径 | 通过 aliases 节点定义 |
serial0 = &uart0; |
在 chosen 或 bootargs 中使用 |
// 别名定义示例
/ {
aliases {
serial0 = &uart0;
serial1 = &uart1;
i2c0 = &i2c1;
};
chosen {
stdout-path = "serial0:115200n8"; // 使用别名
};
};
路径使用注意事项:
- 绝对路径在 DTS 文件中只能用于
aliases、chosen等特殊节点,不能用于属性追加 - 标签引用
&标签是推荐的方式,支持跨文件操作 - 别名路径主要用于 bootargs 和
stdout-path,不用于节点属性修改
4.7 节点命名与引用的最佳实践
- 命名一致性:同一类设备使用相同的通用名称前缀(如所有 UART 使用
serial) - 标签命名规范:使用
设备类型+序号格式(如uart0、i2c1、mmc0) - 避免重复定义:同一标签只能定义一次,多次定义会导致编译错误
- 引用顺序无关:
&标签可以在标签定义之前或之后使用,dtc 会进行两遍扫描 - 属性合并规则:多次引用同一节点时,属性会按顺序合并,后定义的覆盖先定义的
// 最佳实践示例
// soc.dtsi - 芯片级定义
uart0: serial@ff000000 {
compatible = "ns16550";
reg = <0x0 0xff000000 0x0 0x1000>;
interrupts = <0 33 4>;
status = "disabled"; // 默认关闭
};
// board.dts - 板级使能
#include "soc.dtsi"
&uart0 {
status = "okay";
clock-frequency = <24000000>;
};
调试技巧:使用
dtc -I dtb -O dts 反编译 DTB 文件,可以查看编译后的节点路径和标签展开结果,帮助排查命名和引用问题。