2. 设备树语法精讲(上):节点、属性、标签、别名、根节点、compatible、reg、#address-cells 与 #size-cells
设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形节点和键值对属性来描述系统中不可枚举的设备。本章将深入讲解设备树源文件(.dts)的核心语法元素,包括节点、属性、标签、别名以及几个关键的标准属性。
学习提示: 设备树语法是嵌入式 Linux 移植的基础。掌握本节内容后,你将能够读懂并修改大多数开发板的 .dts 文件,为后续的板级适配打下坚实基础。
2.1 节点(Node)与根节点
设备树由一系列节点组成,每个节点代表一个硬件设备、总线或控制器。节点从根节点开始,形成树状层次结构。
- 根节点(Root Node): 使用
/表示,是整个设备树的起点。所有其他节点都是根节点的子节点或后代节点。 - 子节点: 在根节点内部定义的节点,通常对应 SoC 内部模块或外部总线。
- 节点名称: 格式为
node-name@unit-address,其中unit-address是设备在总线上的地址(可选)。
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0x0 0x100>;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>;
};
serial@101f1000 {
compatible = "ns16550";
reg = <0x101f1000 0x1000>;
interrupts = <0 12 4>;
};
};
关键点: 根节点
/ 必须存在,且通常包含 #address-cells 和 #size-cells 属性来定义地址空间的编码格式。
2.2 属性(Property)与标签(Label)
属性是节点的键值对,用于描述设备的特性。标签则是一种引用机制,允许在设备树的其他位置引用某个节点。
| 概念 | 语法 | 说明 |
|---|---|---|
| 属性 | property-name = <value>; |
值可以是字符串、32位整数数组、64位整数数组、字节数组或字符串列表。 |
| 标签 | label: node-name { ... }; |
标签是节点的别名,可在其他位置通过 &label 引用该节点。 |
| 别名(aliases) | aliases { name = &label; }; |
在根节点下定义,为节点提供简短易记的名称。 |
/ {
aliases {
serial0 = &uart0;
serial1 = &uart1;
};
uart0: serial@101f1000 {
compatible = "ns16550";
reg = <0x101f1000 0x1000>;
};
uart1: serial@101f2000 {
compatible = "ns16550";
reg = <0x101f2000 0x1000>;
};
};
/* 在 .dtsi 或 .dts 文件中通过 &uart0 引用节点 */
&uart0 {
clock-frequency = <1843200>;
};
常见错误: 标签名不能与节点名冲突,且标签只能在节点定义时声明,不能单独存在。别名通常用于 bootloader 或早期启动代码快速查找设备。
2.3 compatible 属性
compatible 属性是设备树中最关键的属性之一,它指定了设备的驱动兼容性字符串。操作系统通过该属性将设备与对应的驱动程序绑定。
- 格式: 字符串列表,按优先级从高到低排列。
- 命名规范:
"厂商,设备型号",例如"ti,omap3-uart"、"arm,pl011"。 - 匹配机制: 内核驱动通过
of_match_table与设备树的compatible值进行匹配。
/* 设备树节点 */
i2c@1000 {
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
reg = <0x1000 0x100>;
interrupts = <0 56 4>;
};
/* 内核驱动匹配表 */
static const struct of_device_id imx_i2c_of_match[] = {
{ .compatible = "fsl,imx6q-i2c", .data = &imx6q_i2c_data },
{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_i2c_of_match);
最佳实践: 始终将最具体的兼容字符串放在第一位,通用的作为后备。例如
"fsl,imx6q-i2c" 优先于 "fsl,imx21-i2c"。
2.4 reg 属性、#address-cells 与 #size-cells
这三个属性共同定义了设备在总线上的地址空间映射。reg 描述设备的地址范围,而 #address-cells 和 #size-cells 定义了地址和长度的编码格式。
| 属性 | 作用 | 值类型 | 示例 |
|---|---|---|---|
#address-cells |
指定子节点 reg 属性中地址字段占用的 32 位单元数 |
u32 | <1> 表示 32 位地址,<2> 表示 64 位地址 |
#size-cells |
指定子节点 reg 属性中长度字段占用的 32 位单元数 |
u32 | <1> 表示 32 位长度,<0> 表示无长度(仅地址) |
reg |
描述设备的地址块列表,格式为 (address, length) 对 |
u32 数组 | <0x1000 0x100> 表示基址 0x1000,长度 0x100 |
/* 32 位地址空间示例 */
/ {
#address-cells = <1>;
#size-cells = <1>;
memory@80000000 {
reg = <0x80000000 0x40000000>; /* 基址 0x80000000,大小 1GB */
};
gpio@1100 {
reg = <0x1100 0x100>; /* 基址 0x1100,大小 256 字节 */
};
};
/* 64 位地址空间示例 */
/ {
#address-cells = <2>;
#size-cells = <2>;
memory@800000000 {
reg = <0x0 0x80000000 0x0 0x40000000>; /* 高32位 0x0,低32位 0x80000000,大小 1GB */
};
};
核心规则:
#address-cells 和 #size-cells 是父节点属性,影响所有子节点的 reg 解析。每个 reg 条目由 #address-cells 个单元表示地址,#size-cells 个单元表示长度。
2.5 综合示例:完整节点与属性解析
以下是一个综合示例,展示了本节所有语法元素的协同工作方式:
/dts-v1/;
/ {
model = "MyBoard v1.0";
compatible = "mycompany,myboard", "mycompany,myboard-v1";
#address-cells = <1>;
#size-cells = <1>;
aliases {
eth0 = &enet;
mmc0 = &sdhci;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
reg = <0x0>;
clock-frequency = <1000000000>;
};
cpu1: cpu@1 {
compatible = "arm,cortex-a7";
reg = <0x1>;
clock-frequency = <1000000000>;
};
};
enet: ethernet@20000000 {
compatible = "mycompany,enet";
reg = <0x20000000 0x10000>;
interrupts = <0 33 4>;
phy-mode = "rgmii";
};
sdhci: mmc@30000000 {
compatible = "mycompany,sdhci";
reg = <0x30000000 0x1000>;
interrupts = <0 45 4>;
bus-width = <8>;
};
};
/* 在板级文件中覆盖属性 */
&enet {
local-mac-address = [00 11 22 33 44 55];
};
&sdhci {
status = "okay";
no-1-8-v;
};
解析要点:
- 根节点定义了
model和compatible用于板级识别。 cpus节点内部#address-cells = <1>、#size-cells = <0>,因此reg只包含地址,无长度。- 标签
cpu0、cpu1、enet、sdhci允许在文件其他位置通过&cpu0等方式引用。 - 别名
eth0、mmc0为 bootloader 提供了快速查找接口。
2.6 常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 设备无法 probe | compatible 字符串与驱动不匹配 |
检查驱动 of_match_table 中的字符串,确保完全一致(包括大小写) |
| 地址解析错误 | #address-cells 或 #size-cells 设置错误 |
确认父节点中这两个属性的值,确保 reg 的 cell 数量正确 |
| 标签引用失败 | 标签未定义或拼写错误 | 使用 &label 前确保标签已在某节点定义,且名称唯一 |
| 别名不生效 | 别名未在根节点 aliases 中定义 |
所有别名必须在 /aliases 节点下声明,且值必须使用 &label 引用 |
重要提醒: 设备树编译工具
dtc 不会检查 compatible 字符串的语义正确性,也不会验证 reg 地址是否重叠。务必在编译后使用 dtc -I dtb -O dts 反编译检查生成的 DTB 文件,确保语法和逻辑正确。