2. DTS 文件结构:标准节点写法详解

设备树源文件(.dts)以树形结构描述硬件拓扑。本章逐一拆解 /dts-v1/ 头部、根节点、别名节点、chosen、memory 和 cpus 节点的标准写法与工程约束。

2.1 /dts-v1/ 版本标识

每个 .dts 文件的第一行必须是版本声明,告诉编译器使用设备树 v1 规范。

/dts-v1/;

该行之后可紧跟 /include/ 或 #include 导入头文件。若省略此声明,dtc 编译器会报错或默认使用旧版格式,导致节点解析异常。

工程建议: 始终将 /dts-v1/; 放在文件首行,且分号不可省略。多文件项目中每个 .dts 和 .dtsi 都应包含此声明。

2.2 根节点(Root Node)

根节点是设备树的顶层容器,使用 / 表示。所有外设、总线、CPU、内存等节点都挂在根节点下。

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

    /* 子节点 */
    chosen { ... };
    memory { ... };
    cpus { ... };
    ocp { ... };
};
属性 含义 是否必须
model 板级名称字符串,用于区分硬件变体 强烈建议
compatible 兼容性列表,从具体到通用,内核据此匹配 machine_desc 必须
#address-cells 子节点 reg 属性中地址字段的 32 位字个数 必须(有地址空间时)
#size-cells 子节点 reg 属性中长度字段的 32 位字个数 必须(有地址空间时)
关键规则: 根节点的 compatible 字符串必须与内核 arch/arm/mach-xxx/board.c 中的 DT_MACHINE_START 匹配,否则内核无法加载对应板级初始化函数。
设备树根节点与子节点层级 根节点 / chosen memory cpus soc / ocp 说明: 根节点是设备树的唯一入口,所有外设节点均挂载在根下。 #address-cells 和 #size-cells 在根节点定义,影响所有子节点的 reg 解析。 子节点可继续嵌套,形成深度树形结构。

2.3 别名节点(aliases)

别名节点为其他节点提供简短、固定的符号名称,常用于 bootloader 或早期启动代码快速查找设备。

aliases {
    serial0 = &uart0;
    serial1 = &uart1;
    mmc0 = &mmc1;
    i2c0 = &i2c0;
    ethernet0 = &cpsw_emac0;
};
别名 引用节点 典型用途
serial0 &uart0 内核早期打印(earlycon)
mmc0 &mmc1 根文件系统挂载设备
ethernet0 &cpsw_emac0 网络设备索引
避坑提示: 别名节点中的 &label 引用必须在同一 .dts 或 .dtsi 中有对应的 label: 定义。别名值必须是唯一的,重复定义会导致 dtc 警告或覆盖。

2.4 chosen 节点

chosen 节点不描述硬件,而是传递系统运行时参数给内核,由 bootloader 动态填充。

chosen {
    bootargs = "console=ttyO0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext4";
    stdout-path = &uart0;
    linux,initrd-start = <0x82000000>;
    linux,initrd-end   = <0x82800000>;
};
属性 说明 是否必须
bootargs 内核命令行参数,与 CONFIG_CMDLINE 拼接 推荐
stdout-path 早期控制台设备路径,用于 earlycon 推荐
linux,initrd-start/end initramfs 在内存中的物理地址范围 使用 initramfs 时必填
调试技巧: 在开发阶段,可在 .dts 中硬编码 bootargs 作为默认值,U-Boot 可通过 fdt chosen 命令覆盖。若内核未收到预期参数,优先检查 chosen 节点是否存在且被正确覆盖。

2.5 memory 节点

memory 节点描述系统物理内存的起始地址和大小。U-Boot 通常会在启动时动态更新此节点。

memory {
    device_type = "memory";
    reg = <0x80000000 0x20000000>;  /* 起始 0x80000000,大小 512 MB */
};
属性 值示例 说明
device_type "memory" 固定值,内核据此识别内存节点
reg <0x80000000 0x20000000> 地址 + 长度,字宽由根节点 #address-cells 和 #size-cells 决定
多段内存写法: 若物理内存不连续,可在 reg 中列出多组地址-长度对:
reg = <0x80000000 0x10000000>, <0x90000000 0x10000000>;

2.6 cpus 节点

cpus 节点是 CPU 核心的容器,每个 CPU 核心作为一个子节点,必须包含 #address-cells 和 #size-cells。

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

    cpu@0 {
        compatible = "arm,cortex-a8";
        device_type = "cpu";
        reg = <0>;
        clocks = <&dpll_mpu_ck>;
        clock-latency = <300000>;  /* 纳秒 */
    };

    cpu@1 {
        compatible = "arm,cortex-a8";
        device_type = "cpu";
        reg = <1>;
        clocks = <&dpll_mpu_ck>;
        clock-latency = <300000>;
    };
};
属性 说明 多核时必须
#address-cells CPU 核心编号的 32 位字个数,通常为 1 是
#size-cells CPU 节点无长度字段,固定为 0 是
reg CPU 核心 ID,与 MPIDR 寄存器低位数对应 是
device_type 固定为 "cpu" 是
clocks CPU 主时钟 phandle,用于 cpufreq 调频 推荐
cpus 节点与 CPU 核心子节点 cpus cpu@0 reg = <0> cpu@1 reg = <1> cpu@2 reg = <2> 关键约束: #size-cells 必须为 0,因为 CPU 子节点 reg 只存 ID,不存长度。 reg 值必须从 0 开始连续,不能跳号,否则内核 SMP 初始化可能遗漏核心。

2.7 节点标准写法总结

节点 核心属性 常见错误
/dts-v1/ 版本声明 遗漏分号或放在 #include 之后
根节点 / compatible, #address-cells, #size-cells compatible 字符串与内核不匹配
aliases 别名 = &label 引用未定义的 label,别名重复
chosen bootargs, stdout-path bootloader 未覆盖导致参数失效
memory device_type = "memory", reg 地址/长度与硬件实际不符
cpus #address-cells, #size-cells = <0> #size-cells 不为 0,reg 跳号
工程检查清单:
✅ /dts-v1/; 在文件首行
✅ 根节点 compatible 与内核 machine_desc 一致
✅ #address-cells 和 #size-cells 在根节点和 cpus 节点正确设置
✅ chosen 节点 bootargs 包含控制台和根文件系统参数
✅ memory 节点 reg 地址范围在硬件手册范围内
✅ cpus 节点 reg 从 0 开始连续,无跳号