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

设备树(Device Tree)是一种描述硬件配置的数据结构,它通过文本文件(.dts)被编译成二进制文件(.dtb)传递给内核。本章将系统讲解DTS文件的核心语法,包括文件结构、节点与属性的定义规则,以及标签与引用的高级用法。

2.1 DTS文件结构

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

  • 根节点:整个设备树的起点,使用 / 表示。
  • 节点:描述一个具体的硬件设备或总线,可以嵌套。
  • 属性:描述节点的特性,如地址、中断号、时钟频率等。
  • 标签:为节点起别名,方便在其他地方引用。
  • 包含文件:通过 #include 引入其他.dtsi文件。
关键概念: DTS文件采用树形层级结构,根节点下挂载CPU、内存、总线、外设等节点,每个节点通过属性描述其硬件特征。
// 一个简单的DTS文件示例
/dts-v1/;

/ {
    #address-cells = <1>;
    #size-cells = <1>;

    cpu@0 {
        compatible = "arm,cortex-a9";
        reg = <0x0 0x100>;
    };

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

    uart0: serial@101f1000 {
        compatible = "ns16550";
        reg = <0x101f1000 0x1000>;
        interrupts = <0x1 0x0>;
    };
};
提示: /dts-v1/; 是版本声明,表示使用设备树v1格式。所有现代DTS文件都必须以该声明开头。

2.2 节点(Node)与属性(Property)

2.2.1 节点定义

节点是设备树的基本组成单元,每个节点代表一个硬件设备或总线。节点名称格式为 node-name@unit-address,其中 unit-address 是设备在总线上的地址。

节点类型 命名规则 示例
CPU节点 cpu@核心编号 cpu@0
内存节点 memory@基地址 memory@80000000
串口节点 serial@基地址 serial@101f1000
I2C控制器 i2c@基地址 i2c@10002000
GPIO控制器 gpio@基地址 gpio@10003000

2.2.2 属性定义

属性用于描述节点的硬件特征,常见的属性类型包括:

  • compatible:指定设备驱动兼容性字符串,格式为 "厂商,设备型号"。
  • reg:描述设备地址空间,格式为 <基地址 长度>。
  • interrupts:描述中断号,格式为 <中断类型 中断号 触发方式>。
  • clocks:描述时钟源,通过phandle引用时钟节点。
  • status:设备状态,可选 "okay"、"disabled"、"reserved"。
// 属性定义示例
ethernet@10001000 {
    compatible = "davicom,dm9000";
    reg = <0x10001000 0x1000>;
    interrupts = <0x2 0x4>;
    clocks = <&clkc 16>;
    status = "okay";
};
注意: compatible 属性是内核匹配驱动的关键。内核会按照字符串顺序依次尝试匹配,建议将最精确的匹配放在最前面。

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

标签为节点提供一个别名,方便在其他位置引用该节点。引用使用 &label 语法,常用于以下场景:

  • 在根节点中引用子节点
  • 在板级文件中覆盖SoC级节点的属性
  • 在多个节点间建立连接关系
// 标签定义与引用示例
/dts-v1/;

/ {
    clkc: clock-controller@10000000 {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <24000000>;
    };

    uart0: serial@101f1000 {
        compatible = "ns16550";
        reg = <0x101f1000 0x1000>;
        clocks = <&clkc>;  // 引用clkc标签
    };
};

// 在板级文件中覆盖uart0属性
&uart0 {
    status = "okay";
    clock-frequency = <115200>;
};
最佳实践: 在SoC级.dtsi文件中为所有重要节点添加标签,板级.dts文件通过 &label 引用并覆盖属性。这种分离设计提高了代码复用性。

2.4 设备树层级结构图

下图展示了一个典型嵌入式系统的设备树层级结构,包含根节点、CPU、内存、总线及外设节点:

设备树层级结构图 设备树层级结构图 根节点 / cpu@0 memory@80000000 amba-bus@0 serial@101f1000 ethernet@10001000 i2c@10002000 gpio@10003000 图例说明: 根节点 - 设备树起点 CPU节点 - 处理器核心 内存节点 - 系统内存 总线节点 - 连接外设 外设节点 - 具体硬件设备 外设节点 - 其他设备

2.5 节点与属性对比表

概念 定义 语法示例 用途
节点 硬件设备的抽象表示 uart0: serial@101f1000 { ... }; 描述一个具体的硬件设备
属性 节点的特征描述 compatible = "ns16550"; 定义设备的兼容性、地址、中断等
标签 节点的别名 uart0: 方便在其他位置引用节点
引用 通过标签引用节点 &uart0 { ... }; 覆盖或扩展节点属性
包含 引入其他文件 #include "soc.dtsi" 复用SoC级设备树定义

2.6 常见错误与避坑指南

常见错误1: 忘记声明 /dts-v1/; 版本号,导致编译失败。
常见错误2: 节点名称中 @ 后的地址与 reg 属性中的基地址不一致,导致内核无法正确匹配。
常见错误3: 引用标签时使用 &label 但标签未定义,编译时报错“undefined label”。
常见错误4: compatible 属性字符串拼写错误,导致驱动无法加载。
调试技巧: 使用 dtc -I dts -O dtb -o output.dtb input.dts 编译DTS文件,通过 dtc -I dtb -O dts -o output.dts input.dtb 反编译查看二进制内容。编译错误信息会直接指出语法问题所在行号。

2.7 本章小结

本章学习了设备树的核心语法:

  • DTS文件结构包含根节点、子节点、属性和标签
  • 节点通过 node-name@unit-address 命名,属性描述硬件特征
  • 标签 label: 和引用 &label 实现节点复用和覆盖
  • 设备树层级结构清晰反映了硬件拓扑关系