3. 设备树源文件(.dts)结构:/dts-v1/、根节点、aliases节点、chosen节点、memory节点
设备树源文件(.dts)是描述硬件拓扑结构的文本文件,经过设备树编译器(DTC)编译后生成二进制的设备树二进制(.dtb)文件。一个标准的 .dts 文件由版本标识、根节点以及若干特殊子节点构成。本章将深入剖析这些核心组成部分。
.dts 文件是硬件与软件之间的“契约”。理解其结构是编写正确设备树、驱动能够正确匹配硬件外设的前提。
3.1 版本标识:/dts-v1/
每个合法的 .dts 文件必须以版本标识字符串开头。目前,所有新开发的设备树文件都应使用 /dts-v1/ 版本。
// 设备树源文件的标准开头
/dts-v1/;
/ {
// 根节点内容
};
该标识告诉编译器按照 v1 版本的设备树规范解析后续内容。如果缺少此标识,编译器可能会报错或使用过时的解析规则,导致生成的 .dtb 文件无法被内核正确识别。
/dts-v1/ 是当前唯一被广泛支持的版本标识,所有主流内核(如 Linux 4.x/5.x/6.x)均要求此标识。
3.2 根节点(Root Node)
根节点是设备树的顶层节点,用斜杠 / 表示。整个硬件系统的所有设备、总线和属性都挂载在根节点之下。根节点本身通常包含 model 和 compatible 属性,用于标识整个板卡或系统。
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
// 子节点:CPU、内存、外设等
};
| 属性 | 描述 | 示例值 |
|---|---|---|
model |
描述板卡型号的字符串,用于用户空间识别 | "TI AM335x BeagleBone Black" |
compatible |
用于操作系统匹配驱动,按从具体到通用的顺序排列 | "ti,am335x-bone-black", "ti,am33xx" |
compatible 属性顺序非常重要。内核会从列表的第一个字符串开始匹配,如果找不到对应驱动,才会尝试下一个。务必把最具体的板级型号放在最前面。
3.3 aliases 节点
aliases 节点是一个特殊的子节点,用于为设备树中的其他节点定义简短的别名。这些别名通常用于引导加载程序(如 U-Boot)或内核在早期启动阶段快速定位关键设备。
/ {
aliases {
serial0 = &uart0;
serial1 = &uart1;
mmc0 = &mmc1;
i2c0 = &i2c0;
ethernet0 = &cpsw_emac0;
};
// 其他节点
};
别名的格式为 名称 = &节点引用;。通过 & 符号引用已定义的节点标签。常见的别名包括 serial0、mmc0、i2c0 等,它们使得在命令行或脚本中引用设备更加直观。
fdt set 命令动态修改 aliases 节点,从而改变系统启动时控制台或根文件系统的设备映射。
3.4 chosen 节点
chosen 节点不描述任何硬件设备,而是用于传递操作系统启动时的运行时参数。它由引导加载程序在启动内核前填充,内核在启动过程中读取该节点获取配置信息。
/ {
chosen {
bootargs = "console=ttyO0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext4";
stdout-path = &uart0;
};
};
| 属性 | 作用 | 典型值 |
|---|---|---|
bootargs |
传递给内核的命令行参数,类似传统 ATAG 方式 |
"console=ttyS0,115200 root=/dev/nfs" |
stdout-path |
指定系统早期启动时的控制台输出设备 | &uart0 |
initrd-start |
初始 RAM 磁盘(initrd)在内存中的起始地址 | 0x82000000 |
chosen 节点是动态的,其内容由引导加载程序在每次启动时根据用户配置或硬件状态生成;而 aliases 节点通常是静态的,在 .dts 文件中预先定义好。
3.5 memory 节点
memory 节点用于描述系统的物理内存布局,包括内存的起始地址和大小。在大多数现代嵌入式系统中,内存信息由引导加载程序动态探测并填充,因此 .dts 文件中可能只包含一个占位符或空节点。
/ {
// 方式一:静态定义(适用于内存固定的系统)
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; // 起始地址 0x80000000,大小 512MB
};
// 方式二:动态填充(引导加载程序负责填充 reg 属性)
memory {
device_type = "memory";
// reg 属性由 U-Boot 在启动时写入
};
};
reg 属性是一个 <地址 长度> 对,可以包含多个地址范围来描述非连续的内存区域。
memory 节点缺失或 reg 属性错误,内核将无法正确管理物理内存,导致系统崩溃或无法启动。在调试启动问题时,应首先检查 memory 节点的内容是否正确。
3.6 整体结构关系图
下图展示了 .dts 文件中各核心节点之间的层级关系与数据流向:
3.7 节点属性与值格式
设备树中的每个节点都可以包含多个属性,属性值支持多种数据类型。下表总结了常见的属性值格式:
| 数据类型 | 语法 | 示例 | 说明 |
|---|---|---|---|
| 字符串 | "string" |
model = "BeagleBone Black"; |
单个字符串 |
| 字符串列表 | "str1", "str2" |
compatible = "ti,am33xx", "ti,omap"; |
逗号分隔的多个字符串 |
| 32位整数 | <value> |
reg = <0x80000000 0x20000000>; |
尖括号内包含一个或多个32位值 |
| 64位整数 | <0x00000000 0x80000000> |
reg = <0x00000000 0x80000000 0x00000000 0x20000000>; |
使用两个32位单元表示64位地址 |
| 字节数组 | [hex bytes] |
local-mac-address = [00 11 22 33 44 55]; |
方括号内为十六进制字节序列 |
| 布尔值 | 属性存在即为真 | status = "disabled"; 或 status = "okay"; |
通过属性存在与否或特定字符串表示 |
3.8 实战案例:最小完整 .dts 文件
以下是一个包含本章所有核心节点的最小完整 .dts 文件示例:
/dts-v1/;
/ {
model = "Example ARM Cortex-A9 Board";
compatible = "example,cortex-a9-board", "example,cortex-a9";
aliases {
serial0 = &uart0;
mmc0 = &mmc;
};
chosen {
bootargs = "console=ttyAMA0,115200 root=/dev/mmcblk0p2 rw";
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; // 256MB 内存
};
// 外设节点示例
uart0: uart@10000000 {
compatible = "arm,pl011";
reg = <0x10000000 0x1000>;
interrupts = <0 5 4>;
status = "okay";
};
mmc: mmc@20000000 {
compatible = "arm,pl180";
reg = <0x20000000 0x1000>;
interrupts = <0 6 4>;
status = "okay";
};
};
/dts-v1/、根节点、aliases、chosen 和 memory 节点是理解设备树源文件结构的基础。这些节点共同构成了硬件描述的核心骨架,为后续的外设节点配置和驱动开发奠定了坚实基础。