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 {
            // ... 节点内容
        };
    };
};
设备树节点层级结构 根节点 / soc 节点 uart0: serial@fe001000 i2c@fe002000 节点通过父子关系形成树形结构,根节点 / 是唯一顶层节点

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:时钟源引用。
属性数据类型与编码 字符串 "fsl,imx6q-uart" 32位整数 <0xfe001000 0x4000> 字节数组 [00 11 22 33 44 55] 属性值类型决定编码方式:字符串用双引号,整数用尖括号,字节数组用方括号 混合类型可组合使用,如 interrupts = <0 26 4>;

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

标签为节点提供一个全局别名,方便在其他位置引用该节点。引用使用 &label 语法,常用于在 .dtsi 文件中覆盖或扩展节点属性。

标签定义:
uart0: serial@fe001000 {
    // 节点内容
};
引用语法:
&uart0 {
    status = "disabled";
};

引用机制的核心用途:

  1. 节点扩展:在板级 .dts 文件中修改 SoC 级 .dtsi 中定义的节点。
  2. 属性覆盖:无需重写整个节点,只修改需要的属性。
  3. 跨文件复用:通过 #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 通过引用按需启用。这避免了每个板子都重复定义完整节点。
标签定义与引用流程 定义阶段(.dtsi) uart0: serial@fe001000 { status = "disabled"; 引用阶段(.dts) &uart0 { status = "okay"; 编译后节点合并结果 serial@fe001000 { status = "okay"; ... } 标签 uart0 作为桥梁,允许板级文件精准覆盖 SoC 级节点的属性

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 驱动模型之间的桥梁。