2. DTS基础语法:节点定义、属性定义、标签与引用
设备树源文件(.dts)采用树形结构描述硬件,其核心语法围绕三个要素展开:节点(Node)、属性(Property)和标签(Label)。本章将逐一拆解这些基础语法单元,并演示如何通过引用(&)实现节点复用与修改。
2.1 节点(Node)定义
节点是设备树的基本组成单元,代表一个硬件设备、总线或控制器。每个节点由节点名和单元地址(可选)标识,并用花括号 { } 包裹其内容。
语法模板:
[label:] node-name[@unit-address] {
[property-definitions;]
[child-nodes;]
};
- node-name:通用名称,如
cpu、memory、i2c,应反映设备类型。 - @unit-address:可选,通常为寄存器或总线上的起始地址(十六进制),用于区分同类型节点。
- label::可选,为节点定义标签,便于后续引用。
命名规范:节点名应使用小写字母、数字和连字符,避免使用下划线。单元地址必须与
reg 属性中的第一个地址值一致。
示例:定义一个 UART 串口节点,基地址为 0xfe001000。
uart0: serial@fe001000 {
compatible = "fsl,imx6q-uart", "fsl,imx-uart";
reg = <0x0 0xfe001000 0x0 0x4000>;
interrupts = <0 26 4>;
status = "okay";
};
节点可以嵌套,形成父子层级。根节点 / 是设备树的起点。
/ {
model = "Freescale i.MX6 Quad SABRE Board";
compatible = "fsl,imx6q-sabresd", "fsl,imx6q";
soc {
#address-cells = <2>;
#size-cells = <2>;
uart0: serial@fe001000 {
// ... 节点内容
};
};
};
2.2 属性(Property)定义
属性为节点提供具体的硬件参数,如地址、中断号、时钟频率等。属性值支持多种数据类型,每种类型有对应的编码方式。
| 数据类型 | 语法示例 | 说明 |
|---|---|---|
| 字符串 | compatible = "fsl,imx6q-uart"; |
双引号包裹,用于设备兼容性匹配 |
| 32位整数 | reg = <0xfe001000 0x4000>; |
尖括号 <> 包裹,空格分隔 |
| 64位整数 | reg = <0x0 0xfe001000 0x0 0x4000>; |
配合 #address-cells 和 #size-cells 使用 |
| 字节数组 | local-mac-address = [00 11 22 33 44 55]; |
方括号 [] 包裹,十六进制字节 |
| 布尔值 | status = "disabled"; |
通过字符串表示,常用 "okay" 或 "disabled" |
| 混合类型 | interrupts = <0 26 4>; |
可包含整数、字符串等组合 |
注意:属性名不能与节点名冲突,且不能以
# 开头(#address-cells 等特殊属性除外)。属性值末尾必须加分号 ;。
常用标准属性速查:
compatible:驱动匹配的关键,格式为"厂商,设备型号"。reg:描述设备地址空间,格式为<地址 长度>。interrupts:描述中断号、触发类型。status:设备状态,"okay"启用,"disabled"禁用。clocks:时钟源引用。
2.3 标签(Label)与引用(&)
标签为节点提供一个全局别名,方便在其他位置引用该节点。引用使用 &label 语法,常用于在 .dtsi 文件中覆盖或扩展节点属性。
标签定义:
uart0: serial@fe001000 {
// 节点内容
};
引用语法:
&uart0 {
status = "disabled";
};
引用机制的核心用途:
- 节点扩展:在板级
.dts文件中修改 SoC 级.dtsi中定义的节点。 - 属性覆盖:无需重写整个节点,只修改需要的属性。
- 跨文件复用:通过
#include引入.dtsi,再用引用定制。
示例:在 imx6q.dtsi 中定义 UART 节点,在 myboard.dts 中引用并启用。
// imx6q.dtsi 中的定义
uart0: serial@fe001000 {
compatible = "fsl,imx6q-uart", "fsl,imx-uart";
reg = <0x0 0xfe001000 0x0 0x4000>;
interrupts = <0 26 4>;
status = "disabled"; // 默认禁用
};
// myboard.dts 中的引用与覆盖
#include "imx6q.dtsi"
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart0>;
status = "okay"; // 板级启用
};
最佳实践:SoC 级
.dtsi 文件将设备默认设为 status = "disabled",板级 .dts 通过引用按需启用。这避免了每个板子都重复定义完整节点。
2.4 节点合并规则
当多个源文件或同一文件内出现相同路径的节点时,设备树编译器(DTC)会执行节点合并。合并遵循以下规则:
| 场景 | 合并行为 | 示例 |
|---|---|---|
| 相同节点名,无单元地址 | 属性合并,子节点合并 | cpu {} + cpu {} → 合并为一个 cpu |
| 相同节点名与单元地址 | 属性合并,子节点合并 | serial@fe001000 {} + &serial@fe001000 {} → 合并 |
| 属性重复 | 后定义的覆盖先定义的 | status = "disabled" 被 status = "okay" 覆盖 |
| 子节点重复 | 递归合并,属性覆盖规则相同 | 子节点中的 clock-frequency 被覆盖 |
陷阱提示:如果两个节点路径完全相同但
compatible 属性不同,合并后 compatible 会被后定义的完全覆盖,不会追加。务必确保兼容性字符串在定义时已包含所有必要值。
2.5 实战:从零构建一个简单设备树
综合以上语法,构建一个包含 CPU、内存和 UART 的最小设备树:
/dts-v1/;
/ {
model = "Example Board v1.0";
compatible = "example,board-v1", "example,board";
#address-cells = <1>;
#size-cells = <1>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0x0>;
clock-frequency = <800000000>;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; // 512MB
};
uart0: serial@fe001000 {
compatible = "ns16550a";
reg = <0xfe001000 0x1000>;
interrupts = <0 26 4>;
clock-frequency = <24000000>;
status = "okay";
};
};
编译验证:使用
dtc -I dts -O dtb -o example.dtb example.dts 编译,再用 dtc -I dtb -O dts example.dtb 反编译,观察节点合并与属性展开结果。
通过本章学习,你已经掌握了设备树的核心语法骨架。下一章将深入 compatible 属性的匹配机制,揭示设备树与 Linux 驱动模型之间的桥梁。