3. 设备树文件结构:dts、dtsi、dtb、dtbo 文件的区别与作用,编译与反编译工具(dtc)
在嵌入式 Linux 开发中,设备树(Device Tree)是描述硬件资源的标准化数据结构。理解其文件体系与编译工具链,是编写正确设备树节点的前提。本章将系统梳理四种核心文件类型(.dts、.dtsi、.dtb、.dtbo)的定位、差异与协作关系,并详解 dtc 工具的使用方法。
3.1 四种文件类型概述
设备树源文件以文本形式描述硬件,经过编译生成二进制格式供内核解析。下表从文件格式、用途、生成阶段和典型场景四个维度进行对比:
| 文件类型 | 全称 | 格式 | 用途 | 生成阶段 | 典型场景 |
|---|---|---|---|---|---|
.dts |
Device Tree Source | 文本 | 顶层设备树源文件,描述单板硬件 | 开发阶段编写 | 单板级硬件描述 |
.dtsi |
Device Tree Source Include | 文本 | 公共设备树头文件,被 .dts 包含 |
开发阶段编写 | SoC 公共定义、IP 核复用 |
.dtb |
Device Tree Blob | 二进制 | 编译后的设备树二进制文件,供内核加载 | 编译阶段输出 | 内核启动时传递给内核 |
.dtbo |
Device Tree Blob Overlay | 二进制 | 设备树覆盖(overlay)二进制,支持运行时修改 | 编译阶段输出 | 动态设备插入、FPGA 重配置 |
关键理解:
.dts 是“主文件”,.dtsi 是“头文件”,.dtb 是“可执行文件”,.dtbo 是“补丁文件”。四者构成从源码到运行时配置的完整链路。
3.2 文件结构关系与包含机制
.dtsi 文件通过 #include 预处理器指令被 .dts 文件引用。这种机制实现了 SoC 级公共描述与单板级特定描述的解耦。一个典型的包含链如下:
// 文件:imx6ull.dtsi(SoC 公共定义)
/ {
soc {
aips1: aips-bus@02000000 {
gpio1: gpio@0209c000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
};
};
};
};
// 文件:myboard.dts(单板级描述)
/dts-v1/;
#include "imx6ull.dtsi"
/ {
model = "MyBoard i.MX6ULL Board";
compatible = "myboard,imx6ull", "fsl,imx6ull";
&gpio1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio1>;
};
};
包含规则:一个
.dts 可以包含多个 .dtsi,.dtsi 之间也可以互相包含。但必须避免循环包含。内核中 .dtsi 通常按 SoC 系列、IP 核、板级外设等维度组织。
3.3 编译与反编译工具:dtc
dtc(Device Tree Compiler)是设备树的核心工具,负责将文本源文件编译为二进制 .dtb,也支持从 .dtb 反编译回 .dts。其基本用法如下表:
| 操作 | 命令 | 说明 |
|---|---|---|
| 编译 .dts → .dtb | dtc -I dts -O dtb -o myboard.dtb myboard.dts |
将文本源文件编译为二进制 |
| 反编译 .dtb → .dts | dtc -I dtb -O dts -o myboard.dts myboard.dtb |
将二进制反编译为可读文本 |
| 编译 .dts → .dtbo | dtc -I dts -O dtb -@ -o overlay.dtbo overlay.dts |
-@ 选项启用 overlay 支持 |
| 检查 .dtb 语法 | dtc -I dtb -O dts /sys/firmware/fdt |
查看当前内核加载的设备树 |
常见错误:编译时若缺少
-@ 选项,生成的 .dtbo 将无法被内核 overlay 系统正确加载。反编译时若 .dtb 版本与 dtc 版本不匹配,可能导致节点丢失或属性解析错误。
3.4 编译流程与文件转换关系
下图展示了从源文件到最终内核加载的完整转换链路:
3.5 反编译实战:从 .dtb 恢复可读源文件
在实际调试中,经常需要从运行中的系统提取设备树进行分析。以下步骤演示反编译流程:
- 提取当前内核设备树:
cp /sys/firmware/fdt /tmp/current.dtb - 反编译为文本:
dtc -I dtb -O dts -o /tmp/current.dts /tmp/current.dtb - 查看节点结构:
grep -n "gpio\|interrupt\|pinctrl" /tmp/current.dts | head -20 - 验证语法:
dtc -I dts -O dtb -o /dev/null /tmp/current.dts(仅检查语法)
调试技巧:反编译后的
.dts 文件会包含所有 .dtsi 展开后的完整内容,便于定位硬件资源冲突。使用 dtc -f 可强制忽略某些错误继续处理。
3.6 文件组织最佳实践
在大型项目中,合理的文件组织能显著提升维护效率。推荐采用以下目录结构:
arch/arm/boot/dts/
├── imx6ull.dtsi # SoC 级公共定义
├── imx6ull-pinfunc.h # 引脚复用宏定义
├── imx6ull-uart.dtsi # UART 控制器定义
├── imx6ull-gpio.dtsi # GPIO 控制器定义
├── myboard.dts # 单板级顶层文件
├── myboard-rev1.dts # 版本变体
└── overlays/
├── myboard-spi.dtbo # SPI 外设覆盖
└── myboard-camera.dtbo # 摄像头覆盖
核心原则:将 SoC 公共部分、IP 核定义、板级外设、版本变体、动态覆盖分层管理。每个
.dtsi 只负责一个逻辑模块,避免单个文件过于庞大。
3.7 常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 编译报错“syntax error” | 缺少分号或花括号不匹配 | 使用 dtc -s 仅检查语法,定位错误行 |
| 反编译后节点丢失 | .dtb 被内核修改或版本不兼容 |
使用与内核版本匹配的 dtc 工具 |
| overlay 加载失败 | 编译时未加 -@ 选项 |
重新编译:dtc -@ -I dts -O dtb -o overlay.dtbo overlay.dts |
| 属性值显示为乱码 | 二进制 .dtb 损坏或字节序错误 |
使用 hexdump -C 检查文件头魔数(应为 d00dfeed) |