2. DTS语法入门:DTS文件结构、节点定义、属性定义、标签与引用

设备树源文件(.dts)是描述硬件拓扑的文本文件,经过设备树编译器(dtc)编译为二进制的设备树二进制(.dtb)供内核使用。本章从最基础的语法单元开始,逐步构建完整的DTS文件认知。

学习提示:DTS语法与C语言结构体定义有相似之处,但更强调层级关系和键值对描述。掌握节点、属性、标签与引用这四大要素,是后续编写和修改设备树的基础。

2.1 DTS文件整体结构

一个典型的DTS文件由以下部分组成:

  • 根节点“/”:设备树的起点,所有设备节点都挂载在根节点下。
  • 节点(node):表示一个硬件设备或总线,可嵌套形成树状结构。
  • 属性(property):描述节点的特征,如地址、中断号、时钟频率等。
  • 标签(label):为节点或属性起别名,便于引用。
  • 引用(phandle):通过标签或路径连接不同节点,实现跨节点关联。
  • 包含文件(#include):引入.dtsi头文件,复用通用定义。
DTS文件结构概览 DTS文件结构概览 根节点 “/” 子节点 (cpu) 子节点 (memory) 子节点 (uart) 子节点 (gpio) 属性 (compatible, reg) 属性 (device_type, reg) 属性 (interrupts, pinctrl) 属性 (gpio-controller, #gpio-cells) 标签 (label): uart0: serial@fe201000 引用 (phandle): &uart0 { ... } 包含文件: #include "soc.dtsi" 复用通用外设定义

2.2 节点(node)定义

节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线或控制器。节点定义格式如下:

[label:] node-name[@unit-address] {
    [property-definitions]
    [child-nodes]
};

节点名称由三部分组成:

部分 说明 示例
label 可选标签,用于在其他位置引用该节点 uart0
node-name 通用名称,描述设备类型,如 serial、gpio serial
unit-address 设备基地址或寄存器偏移,用于区分同类型设备 fe201000
节点命名规范:
  • 节点名称应使用通用名称,如 ethernet、i2c、spi,而非厂商特定名称。
  • unit-address 通常为十六进制,不带 0x 前缀。
  • 根节点固定为 /,且不能有标签。

节点嵌套示例:

/ {
    model = "Embedded Development Board";
    compatible = "vendor,board-v1", "vendor,board";

    soc {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;

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

        gpio0: gpio@fe200000 {
            compatible = "vendor,gpio";
            reg = <0xfe200000 0x1000>;
            gpio-controller;
            #gpio-cells = <2>;
        };
    };
};
节点层级关系 节点层级关系示例 根节点 “/” soc 节点 uart0: serial@fe201000 gpio0: gpio@fe200000 i2c0: i2c@feb00000 每个子节点包含属性:compatible、reg、interrupts 等 标签(如 uart0)用于在其它 .dts 文件中引用该节点

2.3 属性(property)定义

属性是节点的键值对,用于描述设备的硬件特性。属性值支持多种数据类型:

数据类型 语法 说明 示例
字符串 "string" 双引号包裹的文本 compatible = "vendor,uart";
32位整数 <value> 尖括号包裹,支持十进制或十六进制 reg = <0xfe201000 0x1000>;
64位整数 <0x00000000 0xvalue> 两个32位整数拼接表示64位 reg = <0x00000000 0xfe201000>;
字节数组 [hex bytes] 方括号包裹的十六进制字节序列 local-mac-address = [00 11 22 33 44 55];
布尔值 仅属性名,无值 存在即表示 true gpio-controller;
字符串列表 "str1", "str2" 逗号分隔的多个字符串 compatible = "vendor,board", "simple-bus";
混合类型 <value> "string" 不同类型组合,用空格分隔 foo = <0x1234> "bar";

常用标准属性:

  • compatible:驱动匹配的关键字,格式为 "厂商,设备"。
  • reg:描述设备地址空间,格式为 <基地址 长度>。
  • interrupts:描述中断号、触发类型等。
  • clocks:引用时钟控制器和时钟 ID。
  • pinctrl-0、pinctrl-names:引脚控制配置(后续章节详解)。
  • status:设备状态,可选 "okay"、"disabled"、"fail"。
常见错误:
  • 忘记分号结尾:每个属性定义必须以 ; 结束。
  • 整数格式错误:十六进制值必须使用 0x 前缀,如 0xfe201000。
  • 字符串未加引号:字符串必须用双引号包裹。

2.4 标签(label)与引用(phandle)

标签和引用是设备树实现“跨节点连接”的核心机制,广泛应用于中断连接、时钟引用、pinctrl配置等场景。

2.4.1 标签定义

标签是节点或属性的别名,定义在节点名称之前,使用冒号分隔:

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

标签命名规则:

  • 以字母或下划线开头,后跟字母、数字、下划线。
  • 通常使用设备功能缩写,如 uart0、i2c1、gpio_a。
  • 同一文件内标签必须唯一。

2.4.2 引用(phandle)

引用通过 &label 语法,在其它节点中引用已定义的标签节点,实现属性追加或覆盖:

// 在 board.dts 中引用 uart0 并追加属性
&uart0 {
    pinctrl-0 = <&uart0_pins>;
    pinctrl-names = "default";
    status = "okay";
};

引用机制的核心作用:

场景 说明 示例
追加属性 在 board.dts 中为 soc.dtsi 定义的节点添加板级特定属性 &uart0 { pinctrl-0 = ... };
覆盖属性 重新定义已有属性值 &clk { clock-frequency = <50000000>; };
跨节点连接 中断控制器、时钟、GPIO 等引用 interrupt-parent = &gic;
pinctrl 配置 引用 pinmux 节点配置引脚功能 pinctrl-0 = <&uart0_pins>;
标签与引用流程 标签与引用流程 步骤1:在 .dtsi 中定义标签 uart0: serial@fe201000 { compatible = "vendor,uart"; 步骤2:在 .dts 中引用标签 &uart0 { pinctrl-0 = <&uart0_pins>; 编译后:标签被解析为 phandle(整数句柄) 内核通过 phandle 值找到对应节点,完成连接 中断控制器引用 时钟引用 pinctrl 引脚配置

2.5 包含文件与设备树分层

实际项目中,设备树通常分为多个文件,通过 #include 指令组合:

文件类型 扩展名 用途
SoC 通用定义 .dtsi 定义 SoC 内部所有外设节点、中断控制器、时钟等
板级定义 .dts 引用 .dtsi,添加板级特定属性(引脚、外设使能等)
头文件 .h 定义宏、常量、中断号等,通过 #include 引入

典型文件组织示例:

// soc.dtsi - SoC 通用定义
/ {
    compatible = "vendor,soc";
    #address-cells = <1>;
    #size-cells = <1>;

    uart0: serial@fe201000 {
        compatible = "vendor,uart";
        reg = <0xfe201000 0x1000>;
        interrupts = <0 33 4>;
        status = "disabled";  // 默认关闭
    };
};

// board.dts - 板级定义
#include "soc.dtsi"

/ {
    model = "My Board";
    compatible = "vendor,myboard";

    &uart0 {
        pinctrl-0 = <&uart0_pins>;
        pinctrl-names = "default";
        status = "okay";  // 板级使能
    };
};
最佳实践:
  • SoC 通用外设在 .dtsi 中定义为 status = "disabled",板级文件按需使能。
  • 使用 #include 而非 /include/ 旧语法,保持与 C 预处理器一致。
  • 标签命名统一风格,如 uart0、i2c1、spi2,便于维护。

2.6 本章小结

本章覆盖了DTS语法的四大核心要素:

  • 节点:硬件设备的树状表示,支持嵌套和地址区分。
  • 属性:描述设备特征的键值对,支持多种数据类型。
  • 标签:为节点起别名,便于跨文件引用。
  • 引用:通过 &label 实现属性追加、覆盖和跨节点连接。