2. 设备树语法入门:节点、属性、标签、路径的基本规则

设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形层次结构将系统中的处理器、内存、外设、中断控制器、GPIO 控制器等硬件组件组织起来。掌握设备树的语法是编写正确设备树源文件(.dts)的基础。本章将详细讲解设备树的四个核心概念:节点(node)、属性(property)、标签(label)和路径(path)。

2.1 节点(Node):设备树的骨架

节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线、控制器或功能模块。节点通过树形结构组织,形成从根节点到叶子节点的层次关系。

核心规则:每个节点必须包含一个节点名,节点名格式为 name@unit-address,其中 unit-address 通常为设备在总线上的起始地址或寄存器基址(可选)。

2.1.1 节点定义语法

/dts-v1/;

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

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

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

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

        uart@101f1000 {
            compatible = "ns16550";
            reg = <0x101f1000 0x1000>;
            interrupts = <0 5 4>;
        };

        gpio@101f2000 {
            compatible = "brcm,bcm2835-gpio";
            reg = <0x101f2000 0x1000>;
            interrupts = <0 6 4>;
            gpio-controller;
            #gpio-cells = <2>;
        };
    };
};

节点结构说明:

  • 根节点:/ 是设备树的唯一根节点,所有其他节点都是它的子节点。
  • 子节点:cpu@0、memory@80000000、soc 是根节点的直接子节点。
  • 嵌套节点:uart@101f1000 和 gpio@101f2000 是 soc 节点的子节点。
  • 节点名:uart@101f1000 中的 uart 是设备类型,101f1000 是寄存器基址。
最佳实践:节点名应使用小写字母,单词间用连字符分隔(如 serial-controller),避免使用下划线或大写字母。

2.1.2 节点层次结构 SVG 图

设备树节点层次结构示例 根节点 "/" cpu@0 memory@80000000 soc uart@101f1000 gpio@101f2000 i2c@101f3000 节点层次关系说明 • 根节点 "/" 是设备树的入口,所有设备节点都挂载在根节点下。 • cpu@0 和 memory@80000000 是根节点的直接子节点,代表处理器和内存。 • soc 节点是一个总线节点,它包含 uart、gpio、i2c 等外设控制器子节点。 • 节点名中的 @unit-address 用于区分同一类型的不同实例(如 uart@101f1000)。 • 叶子节点(如 uart、gpio)通常包含具体的寄存器地址、中断号等硬件信息。 • 每个节点都可以有自己的 #address-cells 和 #size-cells 属性,影响子节点 reg 的解析。

2.2 属性(Property):描述节点的特征

属性是附加在节点上的键值对,用于描述设备的硬件特性、配置参数和资源需求。每个属性都有一个名称和一个值,值可以是多种数据类型。

2.2.1 属性值类型

数据类型 语法格式 示例 说明
字符串 "string" compatible = "arm,cortex-a9"; 用于标识设备兼容性,通常为 "厂商,设备型号" 格式
32 位整数 <value> reg = <0x101f1000 0x1000>; 表示地址、大小、中断号等数值
64 位整数 <high low> reg = <0x00000000 0x80000000>; 使用两个 32 位单元拼接表示 64 位值
字节数组 [hex bytes] local-mac-address = [00 11 22 33 44 55]; 用于 MAC 地址、UUID 等二进制数据
字符串列表 "str1", "str2" compatible = "ti,am335x-gpio", "ti,omap-gpio"; 多个兼容字符串,按优先级从高到低排列
布尔值 仅属性名,无值 gpio-controller; 存在即表示 true,用于开关型配置
混合类型 组合使用 interrupts = <0 5 4>; 可包含整数、字符串、字节数组的组合

2.2.2 常用标准属性

属性名 用途 示例值
compatible 指定设备兼容的驱动型号 "brcm,bcm2835-gpio"
reg 描述设备寄存器地址空间 <0x101f1000 0x1000>
interrupts 描述设备使用的中断号 <0 5 4>
interrupt-parent 指定中断控制器父节点 &gic
gpio-controller 标记节点为 GPIO 控制器 布尔属性,无值
#gpio-cells 指定 GPIO 描述符的单元数 <2>
status 控制设备启用状态 "okay" 或 "disabled"
ranges 定义地址转换映射 <0x0 0x101f0000 0x10000>
常见错误:属性值类型不匹配是设备树编译错误的常见原因。例如,reg 属性必须使用尖括号 <> 包裹整数,而不能使用方括号 [] 或引号 ""。编译时请使用 dtc -I dts -O dtb -o output.dtb input.dts 检查语法。

2.3 标签(Label):节点的别名引用

标签是设备树中为节点定义的别名,用于在设备树源文件的其他位置引用该节点。标签通常用于中断控制器、GPIO 控制器等需要被多个设备引用的节点。

2.3.1 标签定义语法

/ {
    soc {
        // 定义标签:在节点名前加上 "标签名:"
        gic: interrupt-controller@101f4000 {
            compatible = "arm,cortex-a9-gic";
            reg = <0x101f4000 0x1000>;
            interrupt-controller;
            #interrupt-cells = <3>;
        };

        gpio0: gpio@101f2000 {
            compatible = "brcm,bcm2835-gpio";
            reg = <0x101f2000 0x1000>;
            gpio-controller;
            #gpio-cells = <2>;
        };
    };

    // 使用标签引用节点
    uart@101f1000 {
        compatible = "ns16550";
        reg = <0x101f1000 0x1000>;
        interrupt-parent = <&gic>;  // 引用 gic 节点
        interrupts = <0 5 4>;
        ctrl-gpios = <&gpio0 10 0>;  // 引用 gpio0 节点
    };
};

2.3.2 标签使用场景

场景 标签定义 引用方式 说明
中断控制器 gic: interrupt-controller@... interrupt-parent = <&gic>; 所有使用该中断控制器的设备通过标签引用
GPIO 控制器 gpio0: gpio@... ctrl-gpios = <&gpio0 10 0>; 设备通过标签引用 GPIO 控制器,并指定引脚号
时钟控制器 clk: clock-controller@... clocks = <&clk 0>; 设备引用时钟源,指定时钟索引
DMA 控制器 dma: dma-controller@... dmas = <&dma 2>; 设备引用 DMA 通道
标签命名规范:标签名通常使用小写字母,单词间用下划线或连字符分隔(如 gpio0、uart1、i2c_bus)。标签名在设备树中必须唯一,不能与节点名或其他标签名冲突。

2.4 路径(Path):节点的唯一标识

路径是从根节点到目标节点的完整节点名序列,用于在设备树中唯一标识一个节点。路径在设备树覆盖(overlay)、引用和调试中非常重要。

2.4.1 路径格式

// 绝对路径:从根节点 "/" 开始
/soc/uart@101f1000
/soc/gpio@101f2000
/soc/interrupt-controller@101f4000

// 相对路径:在设备树覆盖中使用
&{/soc/uart@101f1000} {
    status = "okay";
};

// 使用标签的路径引用
&uart1 {
    status = "okay";
};

2.4.2 路径与标签的对比

特性 路径(Path) 标签(Label)
定义方式 由节点层次结构自动生成 在节点定义时手动添加
唯一性 全局唯一,由树结构保证 必须手动保证唯一
可读性 较长,但结构清晰 简短,便于快速引用
使用场景 设备树覆盖、调试、脚本处理 设备树源文件内部引用
引用语法 &{/path/to/node} &label_name
编译后存在 在 DTB 中以字符串形式存在 编译后标签名被移除,仅保留路径

2.4.3 路径在设备树覆盖中的应用

// 设备树覆盖文件:overlay.dts
/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target-path = "/soc/uart@101f1000";
        __overlay__ {
            status = "okay";
            clock-frequency = <24000000>;
        };
    };

    fragment@1 {
        target = <&gpio0>;
        __overlay__ {
            // 使用标签引用更简洁
            pinctrl-0 = <&uart_pins>;
        };
    };
};
路径使用原则:
  • 在设备树源文件(.dts)内部,优先使用标签引用节点,代码更简洁。
  • 在设备树覆盖(.dtso)中,如果目标节点没有标签,必须使用 target-path 指定绝对路径。
  • 在调试和日志输出中,路径是识别节点的标准方式,例如 of_find_node_by_path("/soc/uart@101f1000")。
  • 路径中的节点名必须与定义时完全一致,包括 @unit-address 部分。

2.5 节点、属性、标签、路径的关系 SVG 图

节点、属性、标签、路径关系图 根节点 "/" soc uart@101f1000 gpio@101f2000 gic: intc@101f4000 四要素关系说明 节点(Node):设备树的骨架,每个节点代表一个硬件设备或功能模块。 属性(Property):节点的特征描述,如 reg、interrupts、compatible 等。 标签(Label):节点的别名,如 gic:,用于在 DTS 内部快速引用节点。 路径(Path):从根到节点的完整路径,如 /soc/uart@101f1000,全局唯一标识。 标签在编译后转换为路径,路径是 DTB 中节点的唯一标识符。 属性是节点的数据载体,标签和路径是节点的引用机制。

2.6 实战练习:编写一个简单的设备树

综合运用本章知识,编写一个包含 GPIO 控制器和中断控制器的设备树片段:

/dts-v1/;

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

    // 中断控制器节点,定义标签 gic
    gic: interrupt-controller@101f4000 {
        compatible = "arm,cortex-a9-gic";
        reg = <0x101f4000 0x1000>;
        interrupt-controller;
        #interrupt-cells = <3>;
    };

    // GPIO 控制器节点,定义标签 gpio0
    gpio0: gpio@101f2000 {
        compatible = "brcm,bcm2835-gpio";
        reg = <0x101f2000 0x1000>;
        interrupts = <0 6 4>;
        interrupt-parent = <&gic>;
        gpio-controller;
        #gpio-cells = <2>;
    };

    // 使用 GPIO 和中断的设备节点
    button@0 {
        compatible = "gpio-keys";
        #address-cells = <1>;
        #size-cells = <0>;

        button@10 {
            label = "User Button";
            gpios = <&gpio0 10 0>;  // GPIO 0 的第 10 号引脚,低电平有效
            interrupts = <0 10 4>;    // 中断号 10,上升沿触发
            interrupt-parent = <&gic>;
        };
    };
};
练习建议:将上述代码保存为 test.dts,使用 dtc -I dts -O dtb -o test.dtb test.dts 编译,然后使用 dtc -I dtb -O dts test.dtb 反编译,观察标签在编译后的变化。

2.7 本章小结

概念 核心要点 常见用途
节点 树形结构的基本单元,格式为 name@unit-address 表示 CPU、内存、外设、总线等硬件组件
属性 键值对,支持字符串、整数、字节数组、布尔值等类型 描述设备兼容性、地址、中断、GPIO 等硬件特性
标签 节点的别名,格式为 label: node@addr 在 DTS 内部快速引用节点,简化代码
路径 从根到节点的完整路径,如 /soc/uart@101f1000 设备树覆盖、调试、内核 API 查找节点