2. DTS语法入门:节点(node)、属性(property)、标签(label)、包含文件(include)的语法规则
设备树源文件(.dts)是描述硬件拓扑与资源配置的领域特定语言。掌握其核心语法——节点、属性、标签与包含文件——是编写、阅读与调试设备树的基础。本章从这四个基本要素出发,结合实例与常见错误,建立清晰的语法认知。
2.1 节点(node):硬件拓扑的骨架
节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线、控制器或功能块。节点通过层级嵌套描述硬件之间的从属关系。
2.1.1 节点定义格式
node-name {
property1 = <value>;
property2 = "string";
child-node {
...
};
};
- 节点名称:由字母、数字、连字符、下划线组成,通常使用小写。例如
cpu、uart0、i2c-bus。 - 节点体:用花括号
{ }包裹,内部包含属性与子节点。 - 结束符:节点体后必须紧跟分号
;。
2.1.2 根节点与特殊节点
| 节点类型 | 路径 | 说明 |
|---|---|---|
| 根节点 | / |
设备树的顶层容器,所有节点均挂载其下 |
| CPU节点 | /cpus |
描述处理器核心信息,通常包含多个子节点 |
| 内存节点 | /memory |
声明系统物理内存的起始地址与大小 |
| 选择节点 | /chosen |
传递引导参数,如 bootargs、stdout-path |
| 别名节点 | /aliases |
为复杂路径定义简短别名,方便引用 |
2.1.3 节点路径与引用
每个节点在设备树中拥有唯一路径,从根节点开始以斜杠分隔。例如:
/soc/spi@ff110000/display@0
路径用于在 dts 文件内部或 dtsi 包含文件中定位节点,也用于 phandle 引用。
duplicate node name。同一层级下节点名称必须唯一,不同层级允许同名但易引发混淆,应避免。
2.2 属性(property):描述硬件特征
属性是键值对,附着在节点上,用于描述设备的寄存器地址、中断号、时钟频率、兼容性等特征。属性值支持多种数据类型。
2.2.1 属性值类型
| 类型 | 语法示例 | 说明 |
|---|---|---|
| 空属性 | status; |
仅表示存在,无值,常用于布尔标志 |
| 32位整数 | reg = <0x10000000 0x1000>; |
尖括号包裹,多个值用空格分隔 |
| 64位整数 | reg = <0x00000000 0x80000000>; |
使用两个32位单元表示高32位和低32位 |
| 字符串 | compatible = "arm,cortex-a72"; |
双引号包裹,用于驱动匹配 |
| 字符串列表 | compatible = "ti,am335x-uart", "ns16550"; |
多个字符串用逗号分隔 |
| 字节数组 | local-mac-address = [00 11 22 33 44 55]; |
方括号包裹,十六进制字节 |
| 混合类型 | foo = <0x1234>, "bar", [0a 0b]; |
用逗号分隔不同类型单元 |
2.2.2 标准属性速查
- compatible:驱动匹配的关键,格式为
"厂商,设备型号",如"fsl,imx6q-uart"。 - reg:描述地址空间,格式为
<地址 长度>,可重复出现表示多段地址。 - interrupts:描述中断号与触发类型,依赖
interrupt-parent指定中断控制器。 - clocks:引用时钟源,通常配合
clock-names使用。 - status:设备状态,常用值
"okay"、"disabled"、"reserved"。
reg 属性中的地址与长度单元数由 #address-cells 和 #size-cells 决定。若父节点设置 #address-cells = <2>,则每个地址必须写两个32位整数,否则 dtc 报 bad cell count。
2.3 标签(label):节点的“别名”
标签为节点定义一个可引用的标识符,用于在 dts 文件内部或通过 &label 语法引用节点,实现节点内容的追加与覆盖。
2.3.1 标签定义
uart0: serial@ff110000 {
compatible = "ns16550";
reg = <0xff110000 0x1000>;
interrupts = <0 33 4>;
};
上例中 uart0 即为标签,节点路径为 /soc/serial@ff110000,但通过标签可快速引用。
2.3.2 标签引用与节点追加
&uart0 {
clock-frequency = <24000000>;
status = "okay";
};
上述代码通过 &uart0 引用标签,向原节点追加 clock-frequency 属性和 status 属性。若原节点已存在同名属性,则新值覆盖旧值。
2.3.3 标签的典型用途
- 板级覆盖:在
dtsi中定义通用节点并加标签,在板级dts中通过&label调整参数。 - 中断与时钟引用:使用
&label作为interrupt-parent或clocks的值。 - 引脚复用配置:引用
pinctrl节点中的标签,指定引脚功能。
phandle(整数句柄),效率更高且不易出错。推荐在需要跨文件引用节点时始终使用标签。
2.4 包含文件(include):模块化与复用
设备树支持通过 #include 预处理指令引入其他文件,实现硬件描述的分层与复用。包含文件通常使用 .dtsi 扩展名,表示设备树包含文件。
2.4.1 包含语法
#include "soc-common.dtsi"
#include "board-base.dtsi"
#include "pinmux.dtsi"
dtc 编译器在预处理阶段调用 C 预处理器(cpp)处理 #include,因此也支持 #define、#ifdef 等宏定义。
2.4.2 包含文件组织策略
| 文件类型 | 命名惯例 | 内容范围 |
|---|---|---|
| SoC 级 | soc-xxx.dtsi |
CPU、中断控制器、时钟、总线、外设 IP 核 |
| 板级 | board-xxx.dtsi |
内存布局、板载外设、GPIO 分配、电源管理 |
| 引脚复用 | pinmux-xxx.dtsi |
引脚功能选择、驱动强度、上下拉配置 |
| 最终文件 | xxx.dts |
包含上述 dtsi,并做板级覆盖与最终调整 |
2.4.3 包含文件中的节点覆盖
包含文件定义通用节点,最终 dts 文件通过标签引用覆盖或追加属性:
// soc-common.dtsi
uart0: serial@ff110000 {
compatible = "ns16550";
reg = <0xff110000 0x1000>;
status = "disabled";
};
// board-foo.dts
#include "soc-common.dtsi"
&uart0 {
clock-frequency = <24000000>;
status = "okay";
};
编译后 uart0 节点包含 compatible、reg、clock-frequency 和 status = "okay"。
#include 的顺序影响节点覆盖结果。后包含的文件中若出现与先包含文件相同的节点(无标签引用),则后定义的节点会覆盖先定义的节点。建议始终使用标签引用进行覆盖,避免隐式覆盖导致的意外行为。
2.5 语法综合示例与SVG结构图
以下是一个完整的设备树片段,展示节点、属性、标签与包含文件的协同使用:
// main.dts
#include "soc.dtsi"
#include "board.dtsi"
/ {
model = "Example Board v2";
compatible = "example,board-v2";
chosen {
bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2";
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; // 1GB RAM
};
};
&uart0 {
pinctrl-0 = <&uart0_pins>;
pinctrl-names = "default";
status = "okay";
};
2.6 常见语法错误与排查
| 错误类型 | dtc 报错示例 | 原因与修复 |
|---|---|---|
| 缺少分号 | syntax error before '}' |
节点体或属性行末尾漏掉 ;,检查上一行 |
| 花括号不匹配 | unmatched brace |
{ 与 } 数量不一致,使用缩进对齐排查 |
| 标签重复 | duplicate label 'uart0' |
同一编译单元内标签必须唯一,检查 dtsi 包含链 |
| 属性值类型错误 | bad cell count |
reg 的单元数与 #address-cells 不匹配 |
| 包含文件未找到 | fatal error: soc.dtsi: No such file |
检查 -I 包含路径或文件路径是否正确 |
| 节点路径冲突 | duplicate node name |
同一父节点下子节点名称重复,使用标签区分 |
dtc -I dts -O dtb -o output.dtb input.dts 2>&1 编译时,将错误重定向到标准输出。对于复杂包含链,可先用 cpp -P -I include_path input.dts preprocessed.dts 预处理,再检查预处理后的文件定位问题。
2.7 本章小结
- 节点是设备树的骨架,通过层级嵌套描述硬件拓扑;根节点
/是所有节点的起点。 - 属性是节点的数据载体,支持整数、字符串、字节数组等多种类型;
compatible和reg是最核心的标准属性。 - 标签为节点提供编译期别名,通过
&label实现跨文件属性追加与覆盖,是模块化设计的关键。 - 包含文件通过
#include实现硬件描述的复用,.dtsi文件按 SoC、板级、引脚复用分层组织。 - 掌握这四个要素的语法规则与常见错误模式,是后续调试与故障分析的基础。