3、设备树编译器:DTC工具的使用、编译与反编译、常见错误排查
设备树写好了,怎么变成机器能懂的东西?
答案就是 DTC——Device Tree Compiler。说白了,它就是把 .dts 文本文件,编译成 .dtb 二进制文件的工具。反过来,也能把 .dtb 反编译成 .dts,方便你调试。
我最早接触 DTC 时,觉得它就是个黑盒子。后来踩了不少坑,才慢慢摸清它的脾气。今天咱们就把它彻底搞明白。
3.1 DTC 工具从哪里来?
DTC 的源码在 Linux 内核的 scripts/dtc/ 目录下。你编译内核时,它会被自动编译出来。当然,你也可以单独下载 dtc 项目编译安装。
我个人习惯直接用内核自带的版本,因为版本匹配,不容易出幺蛾子。
检查 DTC 是否安装:
$ dtc --version
Version 1.6.0
如果提示命令找不到,要么没装,要么路径没配好。
3.2 编译:从 .dts 到 .dtb
编译是最常用的操作。命令格式很简单:
$ dtc -I dts -O dtb -o output.dtb input.dts
参数说明:
-I dts:输入格式是 dts(文本)-O dtb:输出格式是 dtb(二进制)-o output.dtb:指定输出文件名
举个例子,我有个 myboard.dts,想编译成 myboard.dtb:
$ dtc -I dts -O dtb -o myboard.dtb myboard.dts
编译成功的话,没有任何输出。失败了才会报错。
小技巧:加上 -@ 选项可以生成插件(overlay)支持。如果你要做动态设备树,这个选项很有用。
$ dtc -@ -I dts -O dtb -o myboard.dtb myboard.dts
3.3 反编译:从 .dtb 回到 .dts
反编译在调试时特别有用。比如你拿到一个别人的 .dtb 文件,想看看里面到底配了什么。
$ dtc -I dtb -O dts -o output.dts input.dtb
参数基本一样,只是输入输出格式互换了一下。
我记得有一次,客户给了一个 .dtb 文件,说板子启动有问题。我反编译一看,发现 GPIO 的中断号配错了。这种问题,光看二进制文件根本看不出来。
注意:反编译出来的 .dts 文件,和原始的 .dts 可能不完全一样。比如注释会丢失,一些宏定义会被展开成数值。所以反编译主要用于调试,不建议用它来修改后再编译回去。
3.4 常用选项与进阶用法
除了基本的编译反编译,DTC 还有一些实用选项:
| 选项 | 说明 | 使用场景 |
|---|---|---|
-V |
指定设备树版本(如 -V 17) | 兼容旧版内核 |
-S |
指定 .dtb 文件大小(bytes) | 预留空间给 overlay |
-p |
填充空闲空间(padding) | 对齐需求 |
-H |
指定 phandle 格式(legacy/epapr) | 兼容不同 bootloader |
举个例子,我想生成一个带 1024 字节填充的 .dtb:
$ dtc -I dts -O dtb -p 1024 -o myboard.dtb myboard.dts
3.5 常见错误排查
DTC 的错误信息,说实话,有时候挺让人头疼的。我把自己踩过的坑整理一下:
3.5.1 语法错误
最常见的错误,就是 .dts 文件写错了。比如:
Error: myboard.dts:10.1-2 syntax error
FATAL ERROR: Unable to parse input tree
这表示第 10 行有语法错误。我建议你检查:
- 分号有没有漏掉?每个属性结尾都要有
; - 大括号是否匹配?
- 引号是否成对?字符串要用双引号
我曾经花了一下午,就为了找一个漏掉的分号。后来养成了习惯:写完 .dts 先跑一遍 dtc -I dts -O dtb /dev/null input.dts,只检查语法,不生成文件。
3.5.2 引用错误
设备树里经常用 &label 引用节点。如果标签没定义,就会报:
Reference to non-existent node or label "uart1"
解决办法:检查标签名是否拼写正确,或者对应的节点是否被 /delete-node/ 删掉了。
3.5.3 类型不匹配
属性值的类型搞错了,也会报错。比如:
reg = <0x10000000 0x1000>; // 正确:两个 32 位整数
reg = "0x10000000 0x1000"; // 错误:这是字符串
DTC 会提示:
Expected integer, got string
3.5.4 重复定义
同一个节点或属性定义了两次,DTC 会报:
Duplicate property "compatible" in node
嗯,这里要注意:/delete-property/ 可以删除继承的属性,避免重复定义。
3.6 实战:编译与反编译完整流程
咱们走一遍完整的流程。假设我有一个 demo.dts:
/dts-v1/;
/ {
compatible = "myboard, demo";
#address-cells = <1>;
#size-cells = <1>;
uart@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
};
};
编译:
$ dtc -I dts -O dtb -o demo.dtb demo.dts
反编译看看效果:
$ dtc -I dtb -O dts -o demo_reverse.dts demo.dtb
打开 demo_reverse.dts,你会发现内容基本一样,但注释没了,格式也变了。这就是我说的「反编译不是完美还原」。
调试小技巧:用 dtc -I dtb -O dts 反编译后,再用 diff 对比原始 .dts,可以快速发现差异。
3.7 总结
DTC 是设备树开发的基础工具。你想想看,没有它,我们写的 .dts 就是一堆文本,内核根本读不懂。
我个人建议:
- 养成编译前检查语法的习惯
- 反编译是调试的好帮手,但别依赖它做修改
- 遇到错误别慌,先看行号,再查类型
下一章,咱们聊聊设备树中的中断控制器配置。那才是真正让人头大的地方。