3. DTS 语法基础:节点、属性、标签的定义与格式
设备树源文件(.dts)是一种结构化的文本描述语言,其核心由节点(Node)、属性(Property)和标签(Label)三大要素构成。理解这三者的定义与格式,是编写和解析设备树的基础。
核心概念速览:节点描述硬件设备或总线;属性描述节点的特性(如地址、中断号);标签为节点或属性提供可引用的别名,便于在
.dtsi 文件中进行覆盖或修改。
3.1 节点(Node)的定义与格式
节点是设备树的基本组成单元,用于表示一个硬件设备、总线、控制器或功能模块。每个节点在树形结构中都有一个唯一的路径。
3.1.1 节点定义语法
[label:] node-name [@unit-address] {
[property-list]
[child-node-list]
};
- node-name:节点名称,通常为通用名称(如
uart、i2c、gpio),长度 1-31 个字符。 - @unit-address:可选,用于区分同一层级中相同名称的节点,通常为寄存器基地址或设备编号。
- label:可选,为节点定义一个标签,便于引用。
- property-list:零个或多个属性定义。
- child-node-list:零个或多个子节点定义。
3.1.2 节点示例
/ {
#address-cells = <1>;
#size-cells = <1>;
uart0: serial@101f0000 {
compatible = "ns16550";
reg = <0x101f0000 0x1000>;
interrupts = <5>;
clock-frequency = <1843200>;
};
i2c@101f2000 {
compatible = "snps,designware-i2c";
reg = <0x101f2000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
};
};
节点路径示例:
/ — 根节点/serial@101f0000 — UART 节点/i2c@101f2000/eeprom@50 — I2C 总线下的 EEPROM 子节点
3.2 属性(Property)的定义与格式
属性用于描述节点的特征和配置信息,如硬件地址、中断号、时钟频率、兼容性等。属性由键(Key)和值(Value)组成。
3.2.1 属性值类型
| 值类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 空值 | property-name; |
status = "disabled"; |
仅表示属性存在,无具体值 |
| 字符串 | property-name = "string"; |
compatible = "ns16550"; |
双引号包裹的文本 |
| 字符串列表 | property-name = "str1", "str2"; |
compatible = "ti,am335x-uart", "ns16550"; |
逗号分隔的多个字符串 |
| 32 位整数 | property-name = <value>; |
interrupts = <5>; |
尖括号包裹,支持十进制/十六进制 |
| 整数列表 | property-name = <val1 val2 ...>; |
reg = <0x101f0000 0x1000>; |
空格分隔的多个 32 位整数 |
| 字节序列 | property-name = [hex-bytes]; |
local-mac-address = [00 11 22 33 44 55]; |
方括号包裹的十六进制字节 |
| 混合值 | property-name = <int>, "str", [bytes]; |
example = <0x1234>, "hello", [ab cd]; |
用逗号分隔不同类型 |
3.2.2 常用标准属性
| 属性名 | 用途 | 值类型 |
|---|---|---|
compatible |
指定设备驱动兼容性,格式为 "厂商,设备型号" | 字符串 / 字符串列表 |
reg |
描述设备地址空间(基地址 + 长度) | 整数列表 |
interrupts |
描述设备使用的中断号 | 整数列表 |
status |
设备状态:"okay"、"disabled"、"reserved" | 字符串 |
#address-cells |
子节点 reg 属性中地址字段占用的 32 位单元数 | 32 位整数 |
#size-cells |
子节点 reg 属性中长度字段占用的 32 位单元数 | 32 位整数 |
clock-frequency |
设备时钟频率(单位 Hz) | 32 位整数 |
常见错误:属性值类型不匹配是编译错误的常见原因。例如,将字符串写为整数格式:
interrupts = "5";(错误)应写为 interrupts = <5>;(正确)。
3.3 标签(Label)的定义与使用
标签是设备树中的引用机制,允许为节点或属性定义一个别名,便于在 .dtsi 文件或同一文件的其他位置进行引用和覆盖。
3.3.1 标签定义语法
label: node-name {
// 节点内容
};
标签位于节点名称之前,以冒号分隔。标签名必须唯一,遵循 C 语言标识符命名规则(字母、数字、下划线,不能以数字开头)。
3.3.2 标签引用方式
| 引用方式 | 语法 | 示例 | 说明 |
|---|---|---|---|
&label |
&label { ... } |
&uart0 { status = "okay"; }; |
引用标签对应的节点,用于追加属性或覆盖 |
&label 在属性值中 |
property = &label; |
clocks = &clk_uart; |
引用标签作为 phandle(指针句柄) |
label 在路径中 |
&label/path |
&uart0/pinctrl |
引用标签节点的子路径 |
3.3.3 标签使用示例
// 定义标签
uart0: serial@101f0000 {
compatible = "ns16550";
reg = <0x101f0000 0x1000>;
status = "disabled";
};
// 在另一个文件中通过标签引用并修改
&uart0 {
status = "okay";
clock-frequency = <1843200>;
};
标签的编译原理:DTC 编译器在解析
.dts 文件时,会将标签转换为 phandle(指针句柄),这是一个唯一的 32 位整数标识符。在编译后的 .dtb 文件中,标签不再存在,取而代之的是 phandle 值。通过 dtc -I dtb -O dts 反编译时,DTC 会尝试将 phandle 恢复为标签名。
3.4 节点、属性、标签的完整示例
以下是一个综合示例,展示三者在实际设备树中的协作关系:
// 根节点
/ {
model = "TI AM335x Board";
compatible = "ti,am335x-evm", "ti,am335x";
#address-cells = <1>;
#size-cells = <1>;
// 带标签的 UART 节点
uart0: serial@44e09000 {
compatible = "ti,am335x-uart", "ns16550";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
status = "disabled";
};
// 带标签的 GPIO 控制器
gpio0: gpio@44e07000 {
compatible = "ti,am335x-gpio";
reg = <0x44e07000 0x1000>;
interrupts = <96>;
#gpio-cells = <2>;
gpio-controller;
};
};
// 通过标签引用并启用 UART
&uart0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins>;
};
// 通过标签引用 GPIO 并定义引脚
&gpio0 {
uart0_pins: pinmux_uart0_pins {
pinctrl-single,pins = <
0x10 0x30 // P1_0: uart0_rxd
0x14 0x10 // P1_1: uart0_txd
>;
};
};
最佳实践:
- 节点名称使用小写字母,单词间用连字符分隔(如
serial@101f0000)。 - 标签名使用有意义的名称,如
uart0、i2c1、mmc0,便于维护。 - 属性值类型严格匹配,避免编译错误。
- 在
.dtsi文件中将设备状态设为disabled,在具体板级.dts文件中通过标签引用启用。
3.5 常见错误与避坑指南
| 错误类型 | 错误示例 | 正确写法 | 说明 |
|---|---|---|---|
| 节点名称重复 | uart@0 { ... } uart@0 { ... } |
使用不同 unit-address 或不同名称 | 同一层级节点名称+地址必须唯一 |
| 属性值类型错误 | interrupts = "5"; |
interrupts = <5>; |
中断号应为整数,非字符串 |
| 标签未定义 | &undefined_label { ... } |
先定义标签再引用 | 引用未定义标签会导致编译错误 |
| 缺少分号 | status = "okay" |
status = "okay"; |
每个属性定义必须以分号结尾 |
| 花括号不匹配 | node { property; } 缺少闭合 |
确保每个 { 有对应的 } |
使用缩进对齐可减少此类错误 |
调试提示:使用
dtc -I dts -O dtb -o output.dtb input.dts 2>&1 编译时,DTC 会输出详细的错误信息,包括行号和错误类型。善用编译器的错误提示可以快速定位语法问题。