2. 设备树源文件语法:节点、属性、标签、引用、包含文件的基本规则

设备树源文件(.dts)是描述硬件拓扑结构的文本文件,其语法简洁但严谨。掌握节点、属性、标签、引用和包含文件的基本规则,是编写和调试设备树的基石。本节将逐一拆解这些核心语法元素,并通过实例和图表帮助你快速建立直观理解。

2.1 节点(Node):硬件拓扑的基本单元

节点是设备树中最基本的构建块,用于表示一个硬件设备、总线、控制器或功能单元。每个节点都有一个节点名,并可以包含若干属性和子节点,形成树状层次结构。

节点命名规则:节点名通常为 device-name@unit-address 格式。
device-name 是通用设备类型(如 uart、i2c、gpio),unit-address 是设备在总线上的地址(如寄存器基址、I2C 从机地址),用于区分同一级别的多个同类型设备。
// 简单节点示例
/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";

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

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;  // 512MB RAM
    };

    uart0: serial@44e09000 {
        compatible = "ti,am3352-uart", "ti,omap3-uart";
        reg = <0x44e09000 0x2000>;
        interrupts = <72>;
        status = "okay";
    };
};

上述代码中,根节点 / 包含三个子节点:cpu@0、memory@80000000 和 uart0: serial@44e09000。每个节点通过 compatible 属性与内核驱动匹配,通过 reg 属性描述地址空间。

设备树节点层次结构示例 根节点 "/" cpu@0 memory@80000000 serial@44e09000 节点属性示例: • cpu@0: compatible = "arm,cortex-a8"; reg = <0x0>; • serial@44e09000: compatible = "ti,am3352-uart"; interrupts = <72>; status = "okay"; 子节点(可选)

2.2 属性(Property):描述设备特征的关键值

属性是附着在节点上的键值对,用于描述设备的硬件特征、配置参数和连接关系。属性值可以是字符串、整数、字节序列、字符串列表或整数数组。

属性类型 语法格式 示例 说明
字符串 property = "value"; model = "TI AM335x BeagleBone Black"; 单个字符串值
字符串列表 property = "str1", "str2"; compatible = "ti,am3352-uart", "ti,omap3-uart"; 多个字符串,用逗号分隔
32 位整数 property = <value>; interrupts = <72>; 尖括号包裹单个或多个整数
整数数组 property = <val1 val2>; reg = <0x44e09000 0x2000>; 地址 + 长度对,或任意整数序列
字节序列 property = [hex bytes]; local-mac-address = [00 11 22 33 44 55]; 方括号包裹十六进制字节
布尔值 property;(无值) status = "disabled"; 或 status = "okay"; 存在即表示 true,通常用字符串模拟
关键属性速记:
compatible — 驱动匹配的核心属性,格式为 "厂商,设备型号"。
reg — 描述设备地址空间,格式为 <基址 长度>。
interrupts — 指定中断号,依赖 interrupt-parent 属性。
status — 设备状态,常用 "okay" 或 "disabled"。

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

标签是附加在节点名之前的标识符,用于在其他位置引用该节点。标签以冒号 : 与节点名分隔,格式为 label: node-name@address。

// 定义标签
uart0: serial@44e09000 {
    compatible = "ti,am3352-uart";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
};

// 在其他位置通过 &label 引用
&uart0 {
    pinctrl-0 = <&uart0_pins>;
    status = "okay";
};

标签的主要用途:

  • 简化引用:避免重复书写冗长的节点路径(如 /ocp/serial@44e09000)。
  • 覆盖属性:在 .dtsi 文件中定义默认值,在 .dts 文件中通过标签引用并覆盖。
  • 连接关系:在 pinctrl、clocks、gpios 等属性中引用其他节点。
注意:标签在整个设备树中必须唯一。重复定义标签会导致编译错误。标签名通常使用小写字母、数字和下划线,以 & 符号引用。

2.4 引用(Reference):节点间的连接与覆盖

引用机制允许在一个节点中通过 &label 或 &{/path} 语法指向另一个节点,实现属性覆盖、连接关系描述或 phandle 引用。

引用方式 语法 使用场景
标签引用 &label { ... }; 覆盖或扩展已定义节点的属性
路径引用 &{/soc/uart@44e09000} { ... }; 当节点没有标签时,使用完整路径
phandle 引用 clocks = <&clk_mcu>; 在属性值中引用其他节点作为句柄
// 引用覆盖示例
// 在 .dtsi 文件中定义默认值
uart0: serial@44e09000 {
    compatible = "ti,am3352-uart";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
    status = "disabled";  // 默认关闭
};

// 在 .dts 文件中通过标签引用并启用
&uart0 {
    status = "okay";      // 覆盖 status 属性
    pinctrl-0 = <&uart0_pins>;
};

// phandle 引用示例
&uart0 {
    clocks = <&clk_mcu>;  // 引用 clk_mcu 节点作为时钟源
};
标签定义与引用关系 定义阶段(.dtsi 或 .dts) uart0: serial@44e09000 { ... } 引用阶段(.dts 覆盖) &uart0 { status = "okay"; } 标签引用 phandle 引用示例: clocks = <&clk_mcu>; — 在属性值中引用 clk_mcu 节点作为时钟句柄 gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; — 引用 gpio0 控制器和引脚

2.5 包含文件(Include):模块化与复用

设备树源文件支持 #include 预处理指令,用于引入头文件(.h)或其他设备树片段文件(.dtsi)。这是实现设备树模块化和复用的关键机制。

// am335x-boneblack.dts 文件
#include "am33xx.dtsi"           // 引入 SoC 通用定义
#include "am335x-bone-common.dtsi" // 引入板级通用定义
#include  // 引入 GPIO 宏定义
#include  // 引入 pinmux 宏定义

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";

    // 板级特有节点
    leds {
        pinctrl-0 = <&user_leds_pins>;
        compatible = "gpio-leds";

        led2 {
            label = "beaglebone:green:usr0";
            gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
            linux,default-trigger = "heartbeat";
        };
    };
};

.dtsi 文件通常包含 SoC 通用外设定义、时钟、中断控制器等基础设施,而 .dts 文件则专注于板级特有配置。这种分层设计使得同一 SoC 的不同板卡可以共享大量代码。

文件类型 扩展名 用途 包含关系
设备树源文件 .dts 板级或系统级设备树描述,通常包含板卡特有配置 #include 一个或多个 .dtsi
设备树包含文件 .dtsi SoC 通用定义或板级通用定义,可被多个 .dts 复用 可嵌套 #include 其他 .dtsi 或头文件
设备树头文件 .h 定义宏常量、GPIO 标志、pinmux 配置等 被 .dts 或 .dtsi 包含
最佳实践:
• 将 SoC 通用外设(如 UART、I2C、SPI、GPIO 控制器)定义在 .dtsi 文件中,并默认设置 status = "disabled"。
• 在板级 .dts 文件中通过标签引用启用所需外设,并配置 pinmux、时钟等板级参数。
• 使用 #include 引入 dt-bindings/ 目录下的头文件,避免硬编码魔法数字。

2.6 语法综合示例与编译验证

以下是一个完整的 .dts 文件示例,综合展示了节点、属性、标签、引用和包含文件的使用:

// my-board.dts
/dts-v1/;

#include "soc.dtsi"
#include 

/ {
    model = "My Custom Board";
    compatible = "my,board", "my,soc";

    chosen {
        stdout-path = &uart0;
    };

    leds {
        compatible = "gpio-leds";
        led-red {
            label = "red:status";
            gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
        };
    };
};

&uart0 {
    status = "okay";
    pinctrl-0 = <&uart0_pins>;
};

&i2c0 {
    status = "okay";
    clock-frequency = <100000>;

    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
        pagesize = <16>;
    };
};

使用 dtc 编译验证:

# 编译 .dts 为 .dtb
dtc -I dts -O dtb -o my-board.dtb my-board.dts

# 反编译 .dtb 验证结构
dtc -I dtb -O dts -o my-board-decompiled.dts my-board.dtb

# 检查语法错误(仅检查,不输出文件)
dtc -I dts -O dtb my-board.dts > /dev/null 2>&1 && echo "Syntax OK" || echo "Syntax Error"
常见编译错误:
• ERROR (duplicate_label) — 标签重复定义。
• ERROR (phandle_references) — 引用的节点不存在或 phandle 无效。
• WARNING (unit_address_vs_reg) — 节点地址与 reg 属性不匹配。
• ERROR (missing_interrupt_parent) — 节点声明了 interrupts 但未指定 interrupt-parent。