2. 设备树基础语法:DTS文件结构、节点与属性、标签与引用
设备树源文件(.dts)是描述硬件拓扑与资源配置的文本文件。掌握其基础语法是编写正确设备树的起点。本章将系统讲解DTS文件的整体结构、节点与属性的定义规则,以及标签与引用机制,帮助您建立清晰的语法认知。
2.1 DTS文件整体结构
一个典型的DTS文件由以下部分组成,按顺序排列:
| 组成部分 | 说明 | 示例 |
|---|---|---|
| 版本头 | 指定设备树版本(通常由编译器自动处理) | /dts-v1/; |
| 根节点 | 设备树的顶层节点,用 / 表示 |
/ { ... }; |
| 子节点 | 描述具体设备或总线 | cpu { ... }; |
| 属性 | 描述节点特性(地址、中断、时钟等) | reg = <0x10000000 0x1000>; |
| 标签与引用 | 为节点命名以便复用或覆盖 | &uart0 { ... }; |
| 包含文件 | 引入头文件或公共dtsi片段 | #include "soc.dtsi" |
要点: 所有DTS文件必须以
/dts-v1/; 开头,否则编译器会报错。根节点是唯一没有名字的节点,用斜杠 / 表示。
下面是一个完整的DTS文件骨架示例:
/dts-v1/;
#include "soc.dtsi"
/ {
model = "MyBoard v1.0";
compatible = "vendor,myboard", "vendor,baseboard";
chosen {
bootargs = "console=ttyS0,115200";
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@10000000 {
compatible = "vendor,uart";
reg = <0x10000000 0x1000>;
interrupts = <33>;
};
};
};
2.2 节点(Node)
节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线或功能块。节点可以嵌套,形成树形结构。
2.2.1 节点命名规则
节点名称由两部分组成:节点名 和可选的 单元地址,格式为 node-name@unit-address。
- 节点名:描述设备类型,如
uart、i2c、gpio,长度1~31个字符,只能包含小写字母、数字、连字符和点号。 - 单元地址:可选,通常为设备基地址的十六进制表示,用于区分同类型设备。
| 合法示例 | 说明 |
|---|---|
cpu |
无单元地址的CPU节点 |
serial@10000000 |
基地址为0x10000000的串口 |
i2c-bus@2000 |
基地址为0x2000的I2C总线 |
ethernet@f8000000 |
基地址为0xf8000000的以太网控制器 |
注意: 节点名不能以数字开头,不能包含大写字母。单元地址必须与
reg 属性中的第一个地址一致,否则编译器会警告。
2.2.2 节点路径
每个节点在设备树中都有唯一的路径,从根节点开始,用斜杠分隔。例如:
/soc/uart0
/soc/i2c@2000/rtc@68
/memory@80000000
路径用于在设备树中精确定位节点,常在 chosen 节点或驱动代码中使用。
2.3 属性(Property)
属性是节点的键值对,用于描述设备的配置信息。属性值可以是多种数据类型。
2.3.1 属性值类型
| 类型 | 语法 | 示例 | 说明 |
|---|---|---|---|
| 字符串 | "string" |
compatible = "vendor,uart"; |
用双引号包裹 |
| 字符串列表 | "str1", "str2" |
compatible = "vendor,uart", "ns16550"; |
逗号分隔多个字符串 |
| 32位整数 | <value> |
reg = <0x10000000 0x1000>; |
尖括号包裹,空格分隔 |
| 64位整数 | <0x00000000 0x80000000> |
reg = <0x00000000 0x80000000 0x00000000 0x20000000>; |
用两个32位单元表示 |
| 字节数组 | [hex bytes] |
local-mac-address = [00 11 22 33 44 55]; |
方括号包裹,十六进制字节 |
| 布尔值 | 仅属性名,无值 | status = "disabled"; 或 status; |
存在即为真,不存在为假 |
| 混合类型 | 组合使用 | reg = <0x10000000 0x1000>, [00 11 22]; |
用逗号分隔不同类型 |
2.3.2 常用标准属性
| 属性名 | 用途 | 示例值 |
|---|---|---|
compatible |
设备驱动匹配字符串 | "vendor,uart" |
reg |
设备地址空间(基地址+长度) | <0x10000000 0x1000> |
interrupts |
中断号与触发类型 | <33 2> |
status |
设备启用状态 | "okay" 或 "disabled" |
clocks |
时钟源引用 | <&clk_uart> |
#address-cells |
子节点reg地址字段的单元数 | <1> |
#size-cells |
子节点reg长度字段的单元数 | <1> |
实践建议:
compatible 属性是驱动匹配的关键,建议遵循 "厂商,设备型号" 的命名规范,并保留一个通用兼容项作为后备。
2.4 标签(Label)与引用(&)
标签和引用是设备树中实现节点复用与覆盖的核心机制。
2.4.1 标签定义
标签是节点的别名,在节点名前用冒号分隔。标签名必须唯一,通常使用小写字母和下划线。
uart0: serial@10000000 {
compatible = "vendor,uart";
reg = <0x10000000 0x1000>;
};
上例中 uart0 就是 serial@10000000 节点的标签。标签可以出现在节点名的前面或后面:
serial@10000000: uart0 {
// 等价写法
};
2.4.2 引用机制
使用 & 符号后跟标签名,可以在设备树的其他位置引用该节点。引用主要用于两种场景:
- 覆盖属性:在另一个文件中修改已有节点的属性。
- 引用节点:在属性值中引用其他节点(如时钟、中断控制器)。
覆盖属性示例:
// 在 board.dts 中覆盖 uart0 的状态
&uart0 {
status = "okay";
clock-frequency = <115200>;
};
引用节点示例:
clocks = <&clk_uart &clk_apb>;
interrupt-parent = <&gic>;
关键区别: 引用
&uart0 用于修改节点内容,而 <&clk_uart> 用于在属性值中传递节点句柄(phandle)。前者是语法层面的节点定位,后者是数据层面的节点引用。
2.5 节点与引用关系图解
下面的SVG图展示了DTS文件中节点、标签与引用之间的逻辑关系:
2.6 常见语法错误与避坑
| 错误类型 | 错误示例 | 正确写法 | 说明 |
|---|---|---|---|
| 缺少分号 | reg = <0x1000> |
reg = <0x1000>; |
每个属性必须以分号结尾 |
| 节点名含大写 | UART0: serial@1000 |
uart0: serial@1000 |
节点名只能用小写字母 |
| 标签重复 | 两个节点都使用 uart0: |
确保标签全局唯一 | 编译器会报重复标签错误 |
| 引用未定义标签 | &nonexist { } |
先定义标签再引用 | 编译器无法解析未定义标签 |
| 地址单元不匹配 | #address-cells = <2> 但 reg = <0x1000> |
reg = <0x0 0x1000> |
地址单元数必须与reg字段数一致 |
调试技巧: 使用
dtc -I dts -O dtb -o output.dtb input.dts 编译DTS文件,编译器会输出详细的语法错误信息。建议在编写过程中频繁编译检查。
2.7 本章小结
本章涵盖了设备树语法的三大核心要素:
- 节点:设备树的骨架,通过嵌套形成硬件拓扑,节点名遵循
name@address格式。 - 属性:节点的配置数据,支持字符串、整数、字节数组等多种类型,标准属性如
compatible、reg、interrupts是驱动匹配的关键。 - 标签与引用:通过
标签名:定义节点别名,使用&标签名实现属性覆盖和句柄传递,是设备树复用与模块化的基础。