4. 根节点与模型属性:根节点定义、model属性、compatible属性、#address-cells、#size-cells

设备树的根节点(Root Node)是整个设备树结构的起点,它代表了一整块开发板或SoC系统。所有其他节点(如CPU、内存、外设总线)都作为根节点的子节点存在。正确理解根节点的定义及其核心属性,是编写合法、可移植设备树的基础。

核心要点:根节点没有 reg 属性,因为它不占用任何地址空间。它通过 model 和 compatible 描述整板信息,通过 #address-cells 和 #size-cells 定义其子节点(如总线节点)的地址编码规则。

4.1 根节点定义

根节点在DTS文件中使用根路径 / 表示,所有其他节点都包裹在其中。其基本语法如下:

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
    #address-cells = <1>;
    #size-cells = <1>;

    chosen { ... };
    memory@80000000 { ... };
    cpus { ... };
    ocp { ... };
};

根节点必须包含 model 和 compatible 属性,以及 #address-cells 和 #size-cells(当存在需要寻址的子节点时)。

4.2 model 属性

model 属性是一个字符串,用于精确标识这块开发板或设备的型号名称。它通常包含制造商和产品型号,格式自由但应具有唯一性。

属性 格式 示例 说明
model 字符串 "TI AM335x BeagleBone Black" 精确的板级型号,用于用户识别和日志输出
compatible 字符串列表 "ti,am335x-bone-black", "ti,am33xx" 用于内核驱动匹配,从具体到通用排列
最佳实践:model 应使用人类可读的完整产品名,而 compatible 使用内核可解析的机器码。两者分工明确:model 给人看,compatible 给内核看。

4.3 compatible 属性

compatible 是设备树中最重要的属性之一。它是一个字符串列表,按优先级从高到低排列,内核通过匹配该属性来选择正确的机器描述结构(DT_MACHINE)或设备驱动。

匹配规则:

  • 内核遍历 compatible 列表,从第一个字符串开始匹配。
  • 匹配成功则停止,使用对应的初始化函数或驱动。
  • 格式通常为 "制造商,型号",如 "ti,am335x-bone-black"。
  • 最后一项通常是SoC家族名称,如 "ti,am33xx",作为兜底匹配。
// 内核中的匹配表示例(arch/arm/mach-omap2/board-generic.c)
DT_MACHINE_START(AM33XX_DT, "TI AM33xx")
    .dt_compat  = am33xx_boards_compat,
    ...
MACHINE_END

static const char *am33xx_boards_compat[] __initconst = {
    "ti,am33xx",
    "ti,am335x-evm",
    "ti,am335x-bone",
    "ti,am335x-bone-black",
    NULL
};
常见错误:将 compatible 写成单个字符串而非列表,或顺序颠倒(将通用型号放在具体型号之前),会导致内核匹配到错误的初始化代码,可能造成外设无法正常工作。

4.4 #address-cells 与 #size-cells

这两个属性定义了设备树中地址描述的长度规则,直接影响所有子节点中 reg 属性的解析方式。

属性 含义 取值 影响
#address-cells 地址字段占用的32位单元数 1 或 2 决定 reg 中地址部分用几个 <u32> 表示
#size-cells 大小字段占用的32位单元数 0 或 1 或 2 决定 reg 中长度部分用几个 <u32> 表示

常见组合:

  • 32位系统:#address-cells = <1>; #size-cells = <1>; —— 地址和大小各占1个32位单元。
  • 64位系统:#address-cells = <2>; #size-cells = <2>; —— 地址和大小各占2个32位单元(共64位)。
  • 无大小设备:#address-cells = <1>; #size-cells = <0>; —— 用于仅需地址、无需大小的节点(如I2C设备地址)。
// 32位系统示例:地址0x80000000,大小0x10000000
/ {
    #address-cells = <1>;
    #size-cells = <1>;

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  // 地址: 0x80000000, 大小: 256MB
    };
};

// 64位系统示例:地址0x8_00000000,大小0x1_00000000
/ {
    #address-cells = <2>;
    #size-cells = <2>;

    memory@800000000 {
        device_type = "memory";
        reg = <0x00000008 0x00000000 0x00000001 0x00000000>;
        // 高32位地址  低32位地址  高32位大小  低32位大小
    };
};

4.5 根节点属性关系图

下图展示了根节点核心属性之间的逻辑关系及其对子节点的影响:

根节点属性关系与地址解析流程 根节点 / model = "板级型号" compatible = "厂商,型号" #address-cells = <N> #size-cells = <M> memory@80000000 i2c@44e0b000 gpio@4804c000 地址解析规则 • reg 属性格式:reg = <地址1 大小1 地址2 大小2 ...> • 地址占 #address-cells 个 <u32>,大小占 #size-cells 个 <u32> • 示例:#address-cells=<1>,#size-cells=<1> → reg=<0x80000000 0x10000000> • 子节点若不显式声明,则继承父节点的 #address-cells 和 #size-cells

4.6 属性继承与覆盖规则

设备树中,#address-cells 和 #size-cells 遵循严格的继承规则:

  • 继承:子节点如果没有显式声明这两个属性,则默认继承父节点的值。
  • 覆盖:子节点可以重新声明这两个属性,覆盖父节点的值,从而改变该子节点及其后代节点的地址解析规则。
  • 作用域:每个节点的 #address-cells 和 #size-cells 只影响其直接子节点的 reg 属性解析,不影响兄弟节点或父节点。
/ {
    #address-cells = <1>;    // 默认:子节点地址用1个u32
    #size-cells = <1>;       // 默认:子节点大小用1个u32

    bus@10000000 {
        compatible = "simple-bus";
        reg = <0x10000000 0x100000>;  // 继承父节点规则:1个地址 + 1个大小
        #address-cells = <2>;         // 覆盖:该总线下的子节点地址用2个u32
        #size-cells = <1>;            // 覆盖:该总线下的子节点大小用1个u32

        device@10000000 {
            reg = <0x00000000 0x10000000 0x1000>;  // 2个u32地址 + 1个u32大小
        };
    };

    memory@80000000 {
        reg = <0x80000000 0x10000000>;  // 继承根节点规则:1个地址 + 1个大小
    };
};
调试技巧:当遇到 reg 属性解析错误时,首先检查当前节点的父节点是否正确定义了 #address-cells 和 #size-cells。使用 dtc -I dtb -O dts 反编译DTB文件,可以清晰看到每个节点的实际属性值。

4.7 常见错误与避坑指南

错误场景 错误示例 正确做法 后果
根节点缺少 #address-cells 未定义,子节点使用 reg 显式定义 #address-cells = <1> 内核无法解析内存地址,系统崩溃
compatible 顺序颠倒 "ti,am33xx", "ti,am335x-bone-black" 具体型号在前,通用型号在后 可能匹配到错误的板级初始化代码
#size-cells 设为0但 reg 包含大小 #size-cells = <0>; reg = <0x1000 0x2000> 将 #size-cells 设为1,或去掉大小字段 内核解析 reg 时长度错位,访问错误地址
子节点未继承父节点规则 父节点 #address-cells=<2>,子节点仍用1个u32表示地址 子节点显式覆盖或按父节点规则编写 reg 地址截断,设备无法正确映射

4.8 实战案例:完整根节点配置

以下是一个基于TI AM335x平台的完整根节点配置示例,展示了所有核心属性的实际用法:

/dts-v1/;

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

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

    chosen {
        stdout-path = &uart0;
        bootargs = "console=ttyO0,115200 root=/dev/mmcblk0p2 rw";
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  // 256MB DDR3
    };

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;

        cpu@0 {
            compatible = "arm,cortex-a8";
            device_type = "cpu";
            reg = <0x0>;
            operating-points = <
                1000000 1325000
                800000  1300000
                600000  1250000
                300000  1100000
            >;
        };
    };

    ocp {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        ranges = <0x0 0x44e00000 0x100000>;  // 将0x44e00000映射到0x0

        uart0: serial@0 {
            compatible = "ti,am3352-uart", "ti,omap3-uart";
            reg = <0x0 0x2000>;              // 实际物理地址0x44e00000
            interrupts = <72>;
            clock-frequency = <48000000>;
            status = "okay";
        };
    };
};
关键点总结:
  • 根节点通过 model 标识板级型号,通过 compatible 提供内核匹配字符串。
  • #address-cells 和 #size-cells 定义地址解析规则,影响所有子节点的 reg 属性。
  • 子节点可以继承或覆盖父节点的地址解析规则,但必须保持一致性。
  • compatible 列表的顺序至关重要:具体型号在前,通用型号在后。