4. 编译设备树:使用 dtc 将 .dts 编译为 .dtb,常见错误及解决方法

设备树源文件(.dts)必须通过设备树编译器(dtc)编译为二进制设备树 Blob(.dtb),才能被引导加载程序(如 U-Boot)或内核解析。dtc 是设备树工具链的核心,掌握其编译流程与常见错误排查,是设备树调试的基本功。

4.1 dtc 编译基础命令

dtc 的基本调用格式如下:

dtc -I dts -O dtb -o output.dtb input.dts

参数说明:

参数 含义 示例
-I 输入格式(dts / dtb / fs) -I dts
-O 输出格式(dtb / dts / yaml) -O dtb
-o 输出文件路径 -o my-board.dtb
-@ 生成插件节点(overlay 支持) -@
-Wno-unit_address_vs_reg 忽略特定警告 按需使用
提示: 编译前建议先执行语法检查:dtc -I dts -O dtb -o /dev/null input.dts,只检查错误不生成文件,适合快速验证。

4.2 编译流程与常见错误分类

dtc 编译过程分为三个阶段:词法分析 → 语法分析 → 二进制生成。错误通常集中在语法分析阶段。

dtc 编译流程与错误分类 词法分析 扫描 token 语法分析 构建 AST 二进制生成 输出 .dtb 词法错误 非法字符 / 编码 语法错误 缺少分号 / 括号不匹配 语义错误 属性值类型不匹配 常见错误:缺少分号、标签重复、reg 属性格式错误、include 路径不存在 使用 dtc -V 查看版本,确保与内核版本匹配

4.3 常见编译错误及解决方法

4.3.1 语法错误:缺少分号或括号

设备树中每个属性必须以分号结尾,节点必须用花括号包裹。遗漏分号是最常见的错误。

错误示例:

/dts-v1/;
/ {
    compatible = "my-board"   // 缺少分号
    memory@80000000 {
        reg = <0x80000000 0x10000000>;
    };
};

dtc 报错:

Error: input.dts:3.1-2 syntax error
FATAL ERROR: Unable to parse input tree

解决方法: 在 "my-board" 后添加分号。

4.3.2 标签(label)重复

设备树中标签必须全局唯一。重复标签会导致编译失败。

错误示例:

uart0: serial@10000000 {
    compatible = "ns16550";
    reg = <0x10000000 0x1000>;
};

uart0: serial@20000000 {   // 标签重复
    compatible = "ns16550";
    reg = <0x20000000 0x1000>;
};

dtc 报错:

Error: input.dts:8.1-3 Duplicate label 'uart0'

解决方法: 确保每个标签唯一,或使用 &uart0 引用已有节点。

4.3.3 reg 属性格式错误

reg 属性必须与 #address-cells 和 #size-cells 匹配。常见错误是 cells 数量不匹配。

错误示例:

/ {
    #address-cells = <1>;
    #size-cells = <1>;
    memory@80000000 {
        reg = <0x80000000 0x10000000 0x1000>;  // 3 个值,但 cells 只允许 2 个
    };
};

dtc 报错:

Error: input.dts:5.2-3 reg property has 3 cells, expected 2

解决方法: 调整 reg 值数量,或修改 #address-cells / #size-cells。

4.3.4 include 路径不存在

使用 #include 或 /include/ 时,如果路径错误或文件不存在,dtc 会报错。

错误示例:

/dts-v1/;
#include "soc.dtsi"   // 文件不存在

dtc 报错:

FATAL ERROR: Couldn't open blob from 'soc.dtsi': No such file or directory

解决方法: 使用 -I 参数指定搜索路径:

dtc -I dts -O dtb -o output.dtb -i /path/to/dtsi/dir input.dts

4.4 编译警告与处理

dtc 的警告通常不会阻止编译,但应认真对待。常见警告包括:

警告类型 触发条件 建议处理
unit_address_vs_reg 节点名中的地址与 reg 不匹配 检查节点名与 reg 值是否一致
avoid_unnecessary_addr_size 子节点不需要 address-cells / size-cells 移除多余的 cells 定义
chosen_node_is_okay chosen 节点状态为 okay chosen 节点不应有 status 属性
graph_child_address 图形节点地址格式不规范 按 OF graph 规范修正
最佳实践: 编译时使用 -Werror 将警告视为错误,强制修复所有问题:
dtc -I dts -O dtb -o output.dtb -Werror input.dts

4.5 反编译验证

编译成功后,建议反编译验证生成的 .dtb 是否与源文件一致:

dtc -I dtb -O dts -o output.dts output.dtb

对比反编译后的 output.dts 与原始 input.dts,检查是否有信息丢失或格式变化。特别关注:

  • 注释是否被移除(dtc 默认丢弃注释)
  • 标签是否保留
  • 属性值顺序是否一致
  • 节点层级是否完整
注意: 反编译后的 .dts 文件会丢失所有注释,且属性顺序可能重新排列。这是正常行为,不影响功能。

4.6 编译选项速查表

选项 功能 使用场景
-@ 生成 overlay 支持 设备树插件
-H 指定 phandle 格式(epapr / legacy) 兼容旧内核
-Wno-* 忽略特定警告 临时跳过已知问题
-E 仅预处理,不编译 调试 include 展开
-V 显示版本信息 确认 dtc 版本

4.7 实战:完整编译与验证流程

以下是一个完整的编译与验证脚本示例:

#!/bin/bash
# 编译设备树
dtc -I dts -O dtb -o my-board.dtb -@ -Werror my-board.dts

if [ $? -eq 0 ]; then
    echo "编译成功"
    # 反编译验证
    dtc -I dtb -O dts -o my-board-decompiled.dts my-board.dtb
    echo "反编译完成,请对比 my-board.dts 与 my-board-decompiled.dts"
else
    echo "编译失败,请检查错误信息"
    exit 1
fi
调试技巧: 如果编译失败,先使用 dtc -I dts -O dtb -o /dev/null input.dts 2>&1 | head -20 快速定位前 20 行错误,避免被大量输出淹没。