第4章 RK3568设备树文件结构:dts、dtsi、dtb文件关系与内核编译流程

设备树这玩意儿,刚接触的时候很容易被一堆后缀名搞晕。.dts、.dtsi、.dtb,还有编译时冒出来的.dtb.o,到底谁是谁?

我当年第一次移植内核,对着这些文件愣了半天。后来踩了几个坑,才算真正理清了它们的关系。今天咱们就把这层窗户纸捅破。

4.1 三种文件类型:源码、头文件、二进制

说白了,设备树的文件体系跟C语言非常像。你写C代码有.c源文件、.h头文件,编译出来是.o或者可执行文件。设备树也一样:

文件后缀 类比C语言 作用 是否可读
.dts .c 源文件 具体板卡的设备树源文件,描述整块板子的硬件 是(文本)
.dtsi .h 头文件 公共的设备树包含文件,放SoC通用定义、IP核模板 是(文本)
.dtb .o / 可执行文件 编译后的二进制设备树,内核启动时直接解析 否(二进制)

核心记忆点: .dtsi 是“芯片级”的,.dts 是“板级”的。一个SoC对应一个.dtsi,但可以衍生出几十个不同板卡的.dts。

4.2 从.dts到.dtb:编译流程拆解

内核编译设备树的入口,一般在 arch/arm64/boot/dts/rockchip/ 目录下。你执行 make dtbs 的时候,背后发生了什么?

我习惯把流程分成三步:

  1. 预处理:C预处理器(cpp)登场。它处理 #include、#define、#ifdef 这些指令。把.dtsi文件展开,把宏替换掉。这一步输出一个纯正的、没有包含关系的.dts文件。
  2. 编译:设备树编译器(dtc)上场。它把预处理后的.dts解析成抽象语法树,检查语法错误,然后生成.dtb二进制文件。
  3. 打包(可选):有些场景会把多个.dtb打包成一个文件(比如FIT image),但RK3568标准启动流程里,每个.dtb是独立的。

一个小技巧: 如果你想看预处理后的完整.dts长什么样,可以用 make ARCH=arm64 rockchip/rk3568-evb1-v10.dtb 编译,然后在同目录下找 *.dtb.d.pre.tmp 文件。那里就是展开后的源码,调试节点覆盖问题特别好用。

4.3 包含关系与覆盖规则

RK3568的设备树文件,层级关系非常清晰。我画了一张图,你看一眼就明白了:

RK3568设备树文件包含关系与编译流程 RK3568 设备树文件包含关系与编译流程 rk3568.dtsi rk3568-pinctrl.dtsi rk3568-xxx.dtsi rk3568-evb1-v10.dts dtc 编译器 rk3568-evb1-v10.dtb 关键点 .dtsi 是芯片级定义 .dts 是板级实现 .dtb 是最终产物

你看,rk3568.dtsi 定义了CPU、GIC、定时器这些SoC内部模块。而 rk3568-evb1-v10.dts 通过 #include "rk3568.dtsi" 把它拉进来,再补充DDR容量、网卡型号、GPIO复用这些板级信息。

我曾经踩过一个坑: 在.dts里重新定义了.dtsi中已经存在的节点,但忘了检查 status = "okay"。结果内核启动时,那个外设死活不工作。后来才发现,覆盖节点时如果只改属性不改status,默认是 disabled 的。记住:覆盖节点时,status 属性必须显式设置。

4.4 内核编译流程中的设备树

在RK3568的Linux内核里,设备树的编译是跟内核镜像编译解耦的。你编译内核时,可以选择只编译设备树:

# 编译内核镜像 + 所有设备树
make ARCH=arm64 rockchip_defconfig
make ARCH=arm64 -j8

# 只编译设备树
make ARCH=arm64 dtbs

# 编译指定板卡的设备树
make ARCH=arm64 rockchip/rk3568-evb1-v10.dtb

编译完成后,.dtb文件会出现在 arch/arm64/boot/dts/rockchip/ 目录下。你把它跟内核镜像、根文件系统一起打包,就能启动系统了。

嗯,这里要注意一个细节:设备树编译的依赖关系。如果你改了 rk3568.dtsi,所有包含它的.dts都需要重新编译。内核的Makefile会自动处理这个依赖,但如果你手动拷贝.dtb文件,很容易忘记重新编译。

我的习惯: 每次修改设备树后,都执行一次 make dtbs,确保所有相关.dtb都是最新的。然后检查git diff,确认改动确实生效了。

4.5 反编译:从.dtb回到.dts

有时候你拿到一个别人的.dtb,或者想确认编译结果对不对,可以用dtc反编译:

# 反编译 .dtb 为 .dts
dtc -I dtb -O dts -o output.dts rk3568-evb1-v10.dtb

# 常用参数说明
# -I dtb : 输入格式是dtb
# -O dts : 输出格式是dts
# -o     : 指定输出文件名

反编译出来的.dts,节点顺序可能跟源码不一样,但内容是完全等价的。我调试节点覆盖问题时,经常用这个命令来验证最终效果。

4.6 本章小结

概念 一句话总结
.dtsi SoC级公共定义,类似C头文件
.dts 板级具体实现,包含并覆盖.dtsi
.dtb 编译后的二进制,内核直接使用
编译流程 预处理 → dtc编译 → 生成.dtb
反编译 dtc -I dtb -O dts,调试利器

设备树的文件结构,说白了就是“分层复用,板级定制”。理解了这个思路,后面看任何芯片的设备树,你都能快速找到关键节点。