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 编译过程分为三个阶段:词法分析 → 语法分析 → 二进制生成。错误通常集中在语法分析阶段。
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 行错误,避免被大量输出淹没。