3. DTS语法入门:DTS文件基本结构、节点(node)与属性(property)、标签(label)与引用(&)
设备树源文件(.dts)是描述硬件拓扑的领域特定语言(DSL)。理解其语法是排查DTB加载失败与驱动异常的基础。本章从文件骨架、节点/属性定义、标签与引用三个维度展开,配合内联SVG结构图与表格,帮助您建立完整的语法认知。
3.1 DTS文件基本结构
一个标准的DTS文件由根节点、子节点、属性和包含指令组成。其顶层结构如下:
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
#address-cells = <1>;
#size-cells = <1>;
chosen {
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256 MB */
};
leds {
pinctrl-names = "default";
pinctrl-0 = <&led_pins>;
led0 {
label = "beaglebone:green:usr0";
gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
};
};
};
关键点:
/dts-v1/; 声明DTS版本,必须位于文件第一行。#include 用于引入头文件(宏定义、绑定常量),与C语言预处理共享机制。
下表总结了DTS文件的组成要素:
| 要素 | 语法示例 | 说明 |
|---|---|---|
| 版本声明 | /dts-v1/; |
必须位于文件首行,标识使用v1语法 |
| 包含指令 | #include "file.dtsi" |
引入公共SoC定义或板级覆盖 |
| 根节点 | / { ... }; |
设备树的顶层容器,所有设备挂载其下 |
| 子节点 | uart0: serial@44e09000 { ... }; |
描述具体外设或总线,可嵌套 |
| 属性 | reg = <0x44e09000 0x2000>; |
键值对,描述地址、中断、GPIO等硬件参数 |
实践提示: 使用
dtc -I dts -O dtb -o output.dtb input.dts 编译DTS为DTB。若语法错误,dtc会报告行号与错误类型,是排查加载失败的第一道防线。
3.2 节点(node)与属性(property)
3.2.1 节点定义规则
节点是设备树的核心单元,每个节点代表一个硬件设备、总线或功能块。节点名称格式为 [label:] name[@unit-address]:
- label(可选):全局唯一标识符,用于跨文件引用,如
uart0:。 - name:节点名称,描述设备类型,如
serial、gpio。 - @unit-address(可选):设备在总线上的基地址,用于区分同名节点,如
serial@44e09000。
常见错误: 节点名称不能包含空格、
#、? 等特殊字符。地址部分必须使用小写十六进制,且与 reg 属性中的首地址一致,否则dtc编译会报“node name unit address mismatch”警告。
3.2.2 属性类型与语法
属性是节点的键值对,值可以是以下五种类型之一:
| 类型 | 语法 | 示例 | 用途 |
|---|---|---|---|
| 字符串 | "string" |
compatible = "ti,am335x-bone-black"; |
设备兼容性标识 |
| 32位整数 | <value> |
#address-cells = <1>; |
地址/大小描述、时钟频率等 |
| 64位整数 | <0x00000000 0xvalue> |
reg = <0x00000000 0x80000000>; |
64位地址空间(高32位+低32位) |
| 字节数组 | [hex bytes] |
local-mac-address = [00 11 22 33 44 55]; |
MAC地址、UUID等二进制数据 |
| 布尔值 | 仅属性名,无值 | status = "disabled"; 或 status; |
启用/禁用设备(空属性表示true) |
// 综合属性示例
uart0: serial@44e09000 {
compatible = "ti,omap3-uart"; // 字符串
reg = <0x44e09000 0x2000>; // 32位整数对(地址+长度)
interrupts = <72>; // 32位整数
clocks = <&uart0_clk>; // 引用(phandle)
pinctrl-0 = <&uart0_pins>; // 引用列表
status = "okay"; // 字符串状态
};
排查要点: 驱动通过
of_match_table 匹配 compatible 属性。若驱动加载失败,首先检查DTS中 compatible 字符串是否与驱动代码完全一致(包括大小写和逗号后空格)。
3.3 标签(label)与引用(&)
3.3.1 标签定义与作用
标签(label)是附加在节点名称前的全局标识符,格式为 label: node-name。标签的主要用途:
- 跨文件引用:在
.dtsi中定义节点,在.dts中通过&label追加属性或覆盖。 - 属性内引用:在
clocks、gpios、interrupt-parent等属性中指向其他节点。 - 编译时符号:dtc将标签编译为 phandle(指针句柄),驱动通过
of_find_node_by_phandle()解析。
// 定义标签
uart0: serial@44e09000 {
compatible = "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
};
// 引用标签(追加属性)
&uart0 {
pinctrl-0 = <&uart0_pins>;
status = "okay";
};
// 在属性中引用其他节点
gpio_keys {
compatible = "gpio-keys";
button@0 {
label = "user_button";
gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; // 引用 gpio1 节点
};
};
3.3.2 引用操作符 & 的三种场景
| 场景 | 语法 | 编译效果 | 典型用途 |
|---|---|---|---|
| 追加属性 | &label { property = value; }; |
在标签对应节点中增加或覆盖属性 | 板级DTS覆盖SoC DTSI中的默认配置 |
| 属性值引用 | clocks = <&label>; |
生成phandle(32位整数),指向标签节点 | 时钟、中断控制器、GPIO控制器引用 |
| 属性值列表引用 | pinctrl-0 = <&label1 &label2>; |
生成phandle数组 | 多个pinctrl状态、多时钟源 |
调试技巧: 使用
dtc -I dtb -O dts -o decompiled.dts boot.dtb 反编译DTB,查看phandle是否被正确解析。若看到 phandle = <0x00000001>; 但缺少对应标签名,说明引用链可能断裂。
3.3.3 标签与phandle的编译映射
dtc编译时,每个标签会被分配一个唯一的32位phandle值。驱动通过 of_parse_phandle() 或 of_find_node_by_phandle() 将phandle还原为节点指针。映射规则如下:
- 标签名在编译后丢失,DTB中只保留phandle数值。
- 反编译时,dtc会尝试根据phandle值重建标签名(格式为
label_xx)。 - 若多个标签指向同一节点,dtc会合并为同一个phandle。
3.4 常见语法错误与排查
以下是在DTS编写和编译过程中最常遇到的语法问题:
| 错误类型 | 错误示例 | dtc报错信息 | 解决方案 |
|---|---|---|---|
| 缺少分号 | compatible = "foo" |
ERROR: syntax error |
每条属性末尾加 ; |
| 节点名地址不匹配 | serial@44e09000 { reg = <0x44e00000 ...>; } |
WARNING: node name unit address mismatch |
确保 @ 后地址与 reg 首地址一致 |
| 标签重复 | 两个节点使用相同标签 | ERROR: Duplicate label 'uart0' |
确保标签全局唯一 |
| 引用未定义标签 | &undefined_label { ... }; |
WARNING: Reference to non-existent node or label |
检查标签名拼写或是否在 .dtsi 中定义 |
| 属性值类型错误 | reg = "0x44e09000"; |
ERROR: Expected integer, got string |
地址使用 <> 包裹,字符串使用 "" |
严重警告: 标签重复或引用未定义标签不会导致DTB编译失败(仅警告),但会导致驱动在运行时无法正确解析phandle,表现为
of_parse_phandle() 返回NULL,设备无法初始化。务必在编译时检查所有警告信息。
3.5 本章小结
DTS语法是设备树编程的基石,掌握以下要点可有效减少加载与驱动异常:
- 文件结构:版本声明 + 包含指令 + 根节点 + 子节点 + 属性,层次清晰。
- 节点定义:
[label:] name[@unit-address],地址与reg对齐。 - 属性类型:字符串、整数、字节数组、布尔值、引用(phandle),驱动解析方式不同。
- 标签与引用:
&label用于追加属性或生成phandle,编译后标签名丢失。 - 编译检查:使用
dtc -Werror将警告升级为错误,避免运行时隐患。