1. 设备树概述:设备树(DT)的起源、作用与基本概念
1.1 为什么需要设备树?——从“硬编码”到“描述驱动”的演进
在早期 Linux 内核(如 ARM 架构)中,每块开发板或 SoC 的硬件信息(内存基址、中断号、时钟频率、外设寄存器地址等)都直接硬编码在 arch/arm/mach-xxx 下的 C 语言板级文件中。这种方式的弊端随着设备种类爆炸式增长而暴露:
| 对比维度 | 传统板级文件方式 | 设备树方式 |
|---|---|---|
| 硬件描述位置 | 分散在多个 board-*.c 和 mach-*.c 中 |
集中在一个 .dts 源文件中 |
| 内核与硬件耦合度 | 高耦合,换板子必须重新编译内核 | 低耦合,同一内核镜像可启动不同板子 |
| 维护成本 | 每增加一款 SoC 或板卡,需新增大量 C 代码 | 只需编写或修改 .dts 文件,无需改动内核源码 |
| 启动流程 | Bootloader 传递 ATAG 或 machine_type 数字 |
Bootloader 加载 .dtb 二进制并传递给内核 |
核心痛点: ARM 社区在 Linux 3.x 时代面临“每块板子一个内核”的碎片化危机。设备树(Device Tree, DT)正是为了解决这一危机而引入的标准化硬件描述机制。
1.2 设备树的起源与标准化历程
- 起源: 设备树的概念最早源于 Open Firmware(IEEE 1275),用于 PowerPC 和 SPARC 架构。
- 引入 Linux: 2011 年,Linux 3.0 开始正式在 ARM 架构中推广设备树;2012 年 Linux 3.7 起,ARM 平台强制要求使用设备树。
- 标准化组织:
devicetree-specification(由 Linaro 和内核社区维护)定义了.dts语法、.dtb二进制格式以及绑定规则。 - 当前状态: 设备树已成为 ARM、RISC-V、MIPS、X86(部分)等架构的标准硬件描述方式。
1.3 设备树的基本概念与核心文件
设备树由三个层级的文件组成,形成从“SoC 通用描述”到“具体板卡描述”的继承关系:
| 文件类型 | 扩展名 | 作用 | 示例 |
|---|---|---|---|
| 设备树源文件 | .dts |
描述具体板卡的完整硬件拓扑 | imx6ull-evk.dts |
| 设备树头文件 | .dtsi |
描述 SoC 公共部分(CPU、中断控制器、时钟等),可被多个 .dts 包含 |
imx6ull.dtsi |
| 设备树二进制 | .dtb |
编译后的二进制文件,由 Bootloader 加载并传递给内核 | imx6ull-evk.dtb |
类比理解:
.dtsi 相当于 C 语言的 .h 头文件,定义 SoC 的“公共硬件接口”;.dts 相当于 .c 源文件,描述“这块板子具体用了哪些外设、接在哪个引脚上”。
1.4 设备树的核心作用——硬件描述与解耦
设备树在系统启动流程中扮演“硬件配置清单”的角色,其核心作用可归纳为:
- 描述硬件拓扑: 以树形结构描述 CPU、内存、总线、外设、中断、GPIO、时钟等资源。
- 传递配置参数: 将板级差异(如内存大小、MAC 地址、引脚复用)从内核源码中剥离,放入
.dts。 - 驱动匹配: 内核通过
compatible属性将设备节点与驱动进行匹配,实现“设备即插即用”。 - 减少内核分支: 同一内核镜像通过更换
.dtb即可支持不同硬件,大幅降低维护成本。
1.5 设备树的基本语法结构预览
一个典型的 .dts 文件以树形结构组织节点,每个节点包含属性(键值对)。以下是一个简化的示例:
/dts-v1/;
/ {
model = "Embedded Device Tree Demo Board";
compatible = "vendor,board-rev1", "vendor,board";
#address-cells = <1>;
#size-cells = <1>;
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256MB 内存 */
};
soc {
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@10000000 {
compatible = "vendor,uart-ip";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
clock-frequency = <24000000>;
status = "okay";
};
gpio0: gpio@10001000 {
compatible = "vendor,gpio-ctrl";
reg = <0x10001000 0x100>;
gpio-controller;
#gpio-cells = <2>;
interrupts = <0 34 4>;
};
};
};
常见误区: 初学者容易混淆
reg 属性中的地址与大小。注意 #address-cells 和 #size-cells 决定了 reg 中每个地址/大小占用的 32 位单元数。例如 #address-cells = <2> 表示地址用两个 32 位整数表示(64 位地址)。
1.6 设备树的关键概念速查表
| 概念 | 说明 | 示例 |
|---|---|---|
| 节点(Node) | 树形结构中的硬件单元,用 { } 包裹 |
uart0: serial@10000000 { ... }; |
| 属性(Property) | 节点的配置参数,键值对形式 | compatible = "vendor,uart-ip"; |
| compatible | 驱动匹配的关键字符串,格式为 "厂商,设备型号" |
"ti,omap3-uart" |
| reg | 描述设备地址空间(基址 + 长度) | reg = <0x10000000 0x1000>; |
| interrupts | 描述中断号、触发类型 | interrupts = <0 33 4>; |
| status | 设备启用状态:"okay" 或 "disabled" |
status = "okay"; |
| 标签(Label) | 节点别名,方便在其他地方引用 | uart0: serial@10000000 |
1.7 本章小结
- 设备树是 Linux 内核的硬件描述语言,将板级差异从内核源码中剥离,实现“一份内核,多板适配”。
- 核心文件链:
.dtsi(SoC 公共描述)→.dts(板级描述)→.dtb(二进制,Bootloader 加载)。 - 关键属性:
compatible(驱动匹配)、reg(地址空间)、interrupts(中断)、status(启用状态)。 - 学习路径: 理解设备树是后续学习
.dts编译、.dtb生成、驱动开发以及错误排查的基础。