2. 设备树基础语法:DTS文件结构、节点与属性、标签与引用

设备树源文件(.dts)是描述硬件拓扑与资源配置的文本文件。掌握其基础语法是编写正确设备树的起点。本章将系统讲解DTS文件的整体结构、节点与属性的定义规则,以及标签与引用机制,帮助您建立清晰的语法认知。

2.1 DTS文件整体结构

一个典型的DTS文件由以下部分组成,按顺序排列:

组成部分 说明 示例
版本头 指定设备树版本(通常由编译器自动处理) /dts-v1/;
根节点 设备树的顶层节点,用 / 表示 / { ... };
子节点 描述具体设备或总线 cpu { ... };
属性 描述节点特性(地址、中断、时钟等) reg = <0x10000000 0x1000>;
标签与引用 为节点命名以便复用或覆盖 &uart0 { ... };
包含文件 引入头文件或公共dtsi片段 #include "soc.dtsi"
要点: 所有DTS文件必须以 /dts-v1/; 开头,否则编译器会报错。根节点是唯一没有名字的节点,用斜杠 / 表示。

下面是一个完整的DTS文件骨架示例:

/dts-v1/;

#include "soc.dtsi"

/ {
    model = "MyBoard v1.0";
    compatible = "vendor,myboard", "vendor,baseboard";

    chosen {
        bootargs = "console=ttyS0,115200";
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;
    };

    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        uart0: serial@10000000 {
            compatible = "vendor,uart";
            reg = <0x10000000 0x1000>;
            interrupts = <33>;
        };
    };
};

2.2 节点(Node)

节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线或功能块。节点可以嵌套,形成树形结构。

2.2.1 节点命名规则

节点名称由两部分组成:节点名 和可选的 单元地址,格式为 node-name@unit-address。

  • 节点名:描述设备类型,如 uart、i2c、gpio,长度1~31个字符,只能包含小写字母、数字、连字符和点号。
  • 单元地址:可选,通常为设备基地址的十六进制表示,用于区分同类型设备。
合法示例 说明
cpu 无单元地址的CPU节点
serial@10000000 基地址为0x10000000的串口
i2c-bus@2000 基地址为0x2000的I2C总线
ethernet@f8000000 基地址为0xf8000000的以太网控制器
注意: 节点名不能以数字开头,不能包含大写字母。单元地址必须与 reg 属性中的第一个地址一致,否则编译器会警告。

2.2.2 节点路径

每个节点在设备树中都有唯一的路径,从根节点开始,用斜杠分隔。例如:

/soc/uart0
/soc/i2c@2000/rtc@68
/memory@80000000

路径用于在设备树中精确定位节点,常在 chosen 节点或驱动代码中使用。

2.3 属性(Property)

属性是节点的键值对,用于描述设备的配置信息。属性值可以是多种数据类型。

2.3.1 属性值类型

类型 语法 示例 说明
字符串 "string" compatible = "vendor,uart"; 用双引号包裹
字符串列表 "str1", "str2" compatible = "vendor,uart", "ns16550"; 逗号分隔多个字符串
32位整数 <value> reg = <0x10000000 0x1000>; 尖括号包裹,空格分隔
64位整数 <0x00000000 0x80000000> reg = <0x00000000 0x80000000 0x00000000 0x20000000>; 用两个32位单元表示
字节数组 [hex bytes] local-mac-address = [00 11 22 33 44 55]; 方括号包裹,十六进制字节
布尔值 仅属性名,无值 status = "disabled"; 或 status; 存在即为真,不存在为假
混合类型 组合使用 reg = <0x10000000 0x1000>, [00 11 22]; 用逗号分隔不同类型

2.3.2 常用标准属性

属性名 用途 示例值
compatible 设备驱动匹配字符串 "vendor,uart"
reg 设备地址空间(基地址+长度) <0x10000000 0x1000>
interrupts 中断号与触发类型 <33 2>
status 设备启用状态 "okay" 或 "disabled"
clocks 时钟源引用 <&clk_uart>
#address-cells 子节点reg地址字段的单元数 <1>
#size-cells 子节点reg长度字段的单元数 <1>
实践建议: compatible 属性是驱动匹配的关键,建议遵循 "厂商,设备型号" 的命名规范,并保留一个通用兼容项作为后备。

2.4 标签(Label)与引用(&)

标签和引用是设备树中实现节点复用与覆盖的核心机制。

2.4.1 标签定义

标签是节点的别名,在节点名前用冒号分隔。标签名必须唯一,通常使用小写字母和下划线。

uart0: serial@10000000 {
    compatible = "vendor,uart";
    reg = <0x10000000 0x1000>;
};

上例中 uart0 就是 serial@10000000 节点的标签。标签可以出现在节点名的前面或后面:

serial@10000000: uart0 {
    // 等价写法
};

2.4.2 引用机制

使用 & 符号后跟标签名,可以在设备树的其他位置引用该节点。引用主要用于两种场景:

  • 覆盖属性:在另一个文件中修改已有节点的属性。
  • 引用节点:在属性值中引用其他节点(如时钟、中断控制器)。

覆盖属性示例:

// 在 board.dts 中覆盖 uart0 的状态
&uart0 {
    status = "okay";
    clock-frequency = <115200>;
};

引用节点示例:

clocks = <&clk_uart &clk_apb>;
interrupt-parent = <&gic>;
关键区别: 引用 &uart0 用于修改节点内容,而 <&clk_uart> 用于在属性值中传递节点句柄(phandle)。前者是语法层面的节点定位,后者是数据层面的节点引用。

2.5 节点与引用关系图解

下面的SVG图展示了DTS文件中节点、标签与引用之间的逻辑关系:

DTS节点、标签与引用关系图 根节点 / serial@10000000 标签: uart0 i2c@2000 标签: i2c0 interrupt-controller 标签: gic 覆盖引用(修改属性) &uart0 { status = "okay"; }; 句柄引用(属性值) interrupt-parent = <&gic>; clocks = <&clk_uart>; 标签与引用使用规则 1. 标签定义:节点名之前或之后,用冒号分隔,如 uart0: serial@10000000 2. 覆盖引用:&标签名 { ... } 用于修改节点属性或添加子节点 3. 句柄引用:<&标签名> 用于在属性值中传递节点引用(phandle) 4. 标签名必须全局唯一,建议使用小写字母和下划线

2.6 常见语法错误与避坑

错误类型 错误示例 正确写法 说明
缺少分号 reg = <0x1000> reg = <0x1000>; 每个属性必须以分号结尾
节点名含大写 UART0: serial@1000 uart0: serial@1000 节点名只能用小写字母
标签重复 两个节点都使用 uart0: 确保标签全局唯一 编译器会报重复标签错误
引用未定义标签 &nonexist { } 先定义标签再引用 编译器无法解析未定义标签
地址单元不匹配 #address-cells = <2> 但 reg = <0x1000> reg = <0x0 0x1000> 地址单元数必须与reg字段数一致
调试技巧: 使用 dtc -I dts -O dtb -o output.dtb input.dts 编译DTS文件,编译器会输出详细的语法错误信息。建议在编写过程中频繁编译检查。

2.7 本章小结

本章涵盖了设备树语法的三大核心要素:

  • 节点:设备树的骨架,通过嵌套形成硬件拓扑,节点名遵循 name@address 格式。
  • 属性:节点的配置数据,支持字符串、整数、字节数组等多种类型,标准属性如 compatible、reg、interrupts 是驱动匹配的关键。
  • 标签与引用:通过 标签名: 定义节点别名,使用 &标签名 实现属性覆盖和句柄传递,是设备树复用与模块化的基础。