2、DTS文件结构:DTS、DTSI、DTB、DTC的关系与区别

说到设备树,很多初学者一上来就被这四个缩写搞懵了。DTS、DTSI、DTB、DTC,到底谁是谁?

我刚开始接触时也绕了好一阵子。说白了,它们就是设备树在不同阶段的几种形态。就像做菜,有原材料、半成品、成品,还有厨具。嗯,这个比喻可能不太准确,但意思到了。

2.1 先搞清楚这四个东西

咱们一个一个来拆解:

缩写 全称 通俗理解
DTS Device Tree Source 设备树源文件,文本格式,给人看的
DTSI Device Tree Source Include 设备树头文件,公共部分抽出来复用
DTB Device Tree Blob 设备树二进制文件,给内核读的
DTC Device Tree Compiler 编译器,把DTS转成DTB

你想想看,DTS就是源码,DTSI是公共头文件,DTC是编译器,DTB是编译后的二进制。这个链条其实很清晰。

2.2 DTS和DTSI的关系

DTSI文件,说白了就是用来被包含的。我习惯把SoC的公共部分、外设的默认配置都放在DTSI里。比如一个芯片有4个UART,但具体哪个板子用了哪几个,那是DTS的事。

举个例子:

// imx6ull.dtsi(SoC公共部分)
/ {
    soc {
        uart1: serial@02020000 {
            compatible = "fsl,imx6ul-uart";
            reg = <0x02020000 0x4000>;
            status = "disabled";
        };
        uart2: serial@021e8000 {
            compatible = "fsl,imx6ul-uart";
            reg = <0x021e8000 0x4000>;
            status = "disabled";
        };
    };
};
// myboard.dts(具体板子)
#include "imx6ull.dtsi"

/ {
    model = "MyBoard";
    compatible = "mycompany,myboard";
};

&uart1 {
    status = "okay";
    pinctrl-<0> = <&pinctrl_uart1>;
};

看到没?DTSI里把外设都定义好,但默认关掉。DTS里只打开需要的,再配一下引脚。这样复用性就很高了。

我的习惯:每个芯片平台建一个公共的dtsi目录,把重复的配置都扔进去。不同板子只需要include进来,改改差异部分就行。

2.3 DTC编译器怎么用

DTC是设备树的编译器,一般在Linux源码的scripts/dtc/目录下。系统里也可能直接装了:

# 编译:DTS → DTB
dtc -I dts -O dtb -o myboard.dtb myboard.dts

# 反编译:DTB → DTS
dtc -I dtb -O dts -o myboard.dts myboard.dtb

# 常用参数
# -I:指定输入格式(dts/dtb/fs)
# -O:指定输出格式
# -o:输出文件名
# -@:生成符号表(用于overlay)

我曾经遇到过一个坑:编译时没加-@参数,结果后面做设备树overlay时死活加载不上。查了半天才发现是符号表没生成。嗯,这个参数平时用不到,但用到的时候忘了就很头疼。

注意:反编译出来的DTS和原始DTS不会完全一样。注释会丢失,一些语法格式也会变。所以不要把反编译当成逆向工程的手段,它更多是用来调试和排查问题。

2.4 编译与反编译的实战场景

在实际项目中,我经常用反编译来做一件事:验证内核实际读到的设备树

流程是这样的:

  1. 内核启动后,在/sys/firmware/devicetree/base下能看到设备树内容
  2. dtc -I fs -O dtb -o from_kernel.dtb /sys/firmware/devicetree/base导出DTB
  3. 再反编译成DTS,看看内核实际用的是不是我们想要的那份

这个方法我用了很多次,尤其是在排查设备树没生效的问题时。有时候你觉得自己编译进去了,但内核实际加载的是另一份。一对比就现原形了。

2.5 知识体系总览

下面这张图,把整个流程串起来了:

设备树文件编译流程 DTSI 设备树头文件 DTS 设备树源文件 DTC 设备树编译器 DTB 设备树二进制 #include 输入 输出 反编译 编译命令: dtc -I dts -O dtb -o xxx.dtb xxx.dts 反编译命令: dtc -I dtb -O dts -o xxx.dts xxx.dtb

2.6 避坑指南

最后分享几个我踩过的坑:

  • DTSI的路径问题:include时路径是相对于DTC执行目录的,不是DTS文件所在目录。我一开始总搞错,后来统一用绝对路径或脚本处理。
  • DTB的字节序:DTB是大端格式。如果你在x86小端机器上手动解析DTB,记得做字节序转换。不过DTC会处理好这些,一般不用操心。
  • 多个DTSI的覆盖顺序:后include的会覆盖前面的同名节点。我曾经因为include顺序搞反,导致某个外设怎么都配不对。

核心要点:

  • DTS是给人写的,DTB是给机器读的
  • DTSI用于复用公共配置,减少重复代码
  • DTC是桥梁,负责转换格式
  • 反编译是调试利器,但别依赖它还原原始代码

好了,这一章的内容就这些。记住这四个东西的关系,后面写设备树的时候心里就有底了。


专注资料整理