2. DTS语法基础:DTS文件结构、节点、属性、标签与包含文件
设备树源文件(.dts)是一种人类可读的、结构化的文本描述语言,用于描述硬件平台的拓扑结构、内存布局、外设地址及中断连接等关键信息。理解DTS的语法是编写、调试和移植设备树的基础。本章将系统讲解DTS文件的整体结构、核心语法元素(节点、属性、标签)以及模块化组织方式(包含文件)。
2.1 DTS文件整体结构
一个典型的DTS文件由以下几个部分组成,它们共同构成了一个完整的硬件描述树:
- 根节点(Root Node):使用
/表示,是整个设备树的起点,包含所有其他节点。 - 节点(Node):描述一个具体的硬件设备或总线,格式为
node-name@unit-address。 - 属性(Property):描述节点的特性,如寄存器地址、中断号、时钟频率等。
- 标签(Label):为节点或属性定义的别名,方便在其他地方引用。
- 包含文件(Include):通过
#include或/include/引入其他.dtsi文件,实现代码复用。
关键概念: DTS文件本质上是一个以根节点为起点的树形结构。每个节点代表一个硬件设备或总线,节点内包含描述该设备具体信息的属性。这种结构清晰、层次分明,便于内核解析和管理。
2.2 节点(Node)
节点是设备树的核心元素,用于表示一个硬件设备、总线或功能单元。节点可以嵌套,形成父子关系,从而描述硬件之间的连接和从属关系。
2.2.1 节点定义语法
node-name@unit-address {
[属性定义...]
[子节点定义...]
};
- node-name:节点名称,通常为设备类型或功能名称,如
cpu、uart、i2c。 - unit-address:单元地址,通常为设备在总线上的起始地址或片选号,用于区分同类型设备。对于根节点下的顶层节点,此部分可选。
- 花括号
{ }:包含节点的属性和子节点。 - 分号
;:节点定义的结束符。
2.2.2 节点示例
/ {
model = "Example SoC Board";
compatible = "vendor,example-board";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a9";
reg = <0x0>;
};
cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a9";
reg = <0x1>;
};
};
serial@101f0000 {
compatible = "vendor,serial-ip";
reg = <0x101f0000 0x1000>;
interrupts = <0 12 4>;
};
};
最佳实践: 节点名称应尽量使用标准化的名称(如
uart、mmc),而 compatible 属性则用于精确匹配具体的驱动。单元地址 @unit-address 应与 reg 属性中的第一个地址保持一致。
2.3 属性(Property)
属性是键值对,用于描述节点的具体特性。属性值可以是空、整数、字符串、字节数组或这些类型的组合。
2.3.1 属性值类型
| 类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 空属性 | property-name; |
interrupt-controller; |
仅表示存在该属性,无值。 |
| 字符串 | property-name = "string"; |
compatible = "arm,cortex-a9"; |
用双引号包裹。 |
| 32位整数 | property-name = <value>; |
reg = <0x10000000 0x1000>; |
用尖括号包裹,多个值用空格分隔。 |
| 64位整数 | property-name = <0x00000000 0x80000000>; |
reg = <0x00000000 0x80000000>; |
用两个32位整数表示。 |
| 字节数组 | property-name = [hex-bytes]; |
local-mac-address = [00 11 22 33 44 55]; |
用方括号包裹,十六进制字节。 |
| 字符串列表 | property-name = "str1", "str2"; |
compatible = "vendor,device", "generic-device"; |
多个字符串用逗号分隔。 |
| 混合类型 | property-name = <value>, "string", [bytes]; |
gpios = <&gpio0 1 0>, "irq-gpios"; |
不同类型值用逗号分隔。 |
2.3.2 常用标准属性
compatible:最重要的属性,用于匹配驱动程序。格式为"manufacturer,model"。reg:描述设备地址空间,格式为<address length>。interrupts:描述设备的中断信息。status:设备状态,常用值"okay"、"disabled"、"fail"。#address-cells和#size-cells:定义子节点中reg属性的地址和长度字段的位数。
2.4 标签(Label)
标签是为节点或属性定义的别名,使用 & 符号引用。标签可以简化对深层嵌套节点的访问,并支持在多个 .dtsi 文件中覆盖或扩展节点。
2.4.1 标签定义与引用
// 定义标签
uart0: serial@101f0000 {
compatible = "vendor,serial-ip";
reg = <0x101f0000 0x1000>;
interrupts = <0 12 4>;
};
// 在其他地方引用标签
&uart0 {
clock-frequency = <24000000>; // 扩展 uart0 节点
status = "okay";
};
常见错误: 标签名不能与节点名或属性名冲突。引用标签时,必须使用
& 前缀,否则编译器会将其视为普通字符串或节点名。
2.5 包含文件(Include)
为了复用和模块化,DTS支持包含其他文件。通常,SoC厂商会提供 .dtsi(Device Tree Source Include)文件,定义SoC内部通用的外设和总线。板级 .dts 文件通过包含 .dtsi 文件,并添加或覆盖板级特有的设备。
2.5.1 包含语法
// 使用 C 预处理器语法(推荐)
#include "soc.dtsi"
#include "board-common.dtsi"
// 使用设备树原生语法
/include/ "soc.dtsi"
2.5.2 包含文件示例
soc.dtsi(SoC通用定义):
/ {
compatible = "vendor,soc-v1";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0x0>;
};
};
uart0: serial@101f0000 {
compatible = "vendor,serial-ip";
reg = <0x101f0000 0x1000>;
interrupts = <0 12 4>;
status = "disabled";
};
};
board.dts(板级文件):
/dts-v1/;
#include "soc.dtsi"
/ {
model = "My Board";
compatible = "vendor,my-board", "vendor,soc-v1";
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; // 1GB RAM
};
};
// 覆盖 SoC 中的 uart0 节点
&uart0 {
status = "okay";
clock-frequency = <24000000>;
};
模块化优势: 使用
.dtsi 文件可以避免在多个板级文件中重复定义相同的SoC外设。只需在板级文件中通过 &label 语法覆盖或启用特定设备即可,大大减少了代码冗余和维护成本。
2.6 DTS语法结构总览(SVG图)
下图展示了DTS文件从根节点到子节点、属性、标签以及包含文件的完整结构关系:
2.7 节点与属性关系详解(SVG图)
下图进一步细化了一个典型设备节点内部的结构,展示了节点、标签、标准属性以及如何通过 & 引用进行覆盖:
2.8 常见语法错误与排查
| 错误类型 | 错误示例 | 正确写法 | 排查方法 |
|---|---|---|---|
| 缺少分号 | compatible = "vendor,device" |
compatible = "vendor,device"; |
编译器报错 syntax error,检查属性行尾。 |
| 花括号不匹配 | node { ... } 缺少 ; |
node { ... }; |
编译器报错 Expected ';',检查节点结束。 |
| 标签引用错误 | uart0 { ... } 缺少 & |
&uart0 { ... } |
编译器可能将其视为新节点,导致重复定义。 |
| 地址格式错误 | reg = 0x1000; |
reg = <0x1000>; |
编译器报错 bad cell value。 |
| 包含文件路径错误 | #include "soc.dtsi" 文件不存在 |
确保路径正确或文件存在 | 编译器报错 No such file or directory。 |
调试建议: 使用
dtc -I dts -O dtb -o output.dtb input.dts 命令编译时,dtc 会输出详细的错误信息。重点关注行号和错误类型,通常可以快速定位问题。对于复杂的包含关系,可以使用 -E 选项展开预处理后的文件进行调试。