4. 根节点与模型属性:根节点定义、model属性、compatible属性、#address-cells、#size-cells
设备树的根节点(Root Node)是整个设备树结构的起点,它代表了一整块开发板或SoC系统。所有其他节点(如CPU、内存、外设总线)都作为根节点的子节点存在。正确理解根节点的定义及其核心属性,是编写合法、可移植设备树的基础。
核心要点:根节点没有
reg 属性,因为它不占用任何地址空间。它通过 model 和 compatible 描述整板信息,通过 #address-cells 和 #size-cells 定义其子节点(如总线节点)的地址编码规则。
4.1 根节点定义
根节点在DTS文件中使用根路径 / 表示,所有其他节点都包裹在其中。其基本语法如下:
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
#address-cells = <1>;
#size-cells = <1>;
chosen { ... };
memory@80000000 { ... };
cpus { ... };
ocp { ... };
};
根节点必须包含 model 和 compatible 属性,以及 #address-cells 和 #size-cells(当存在需要寻址的子节点时)。
4.2 model 属性
model 属性是一个字符串,用于精确标识这块开发板或设备的型号名称。它通常包含制造商和产品型号,格式自由但应具有唯一性。
| 属性 | 格式 | 示例 | 说明 |
|---|---|---|---|
model |
字符串 | "TI AM335x BeagleBone Black" |
精确的板级型号,用于用户识别和日志输出 |
compatible |
字符串列表 | "ti,am335x-bone-black", "ti,am33xx" |
用于内核驱动匹配,从具体到通用排列 |
最佳实践:
model 应使用人类可读的完整产品名,而 compatible 使用内核可解析的机器码。两者分工明确:model 给人看,compatible 给内核看。
4.3 compatible 属性
compatible 是设备树中最重要的属性之一。它是一个字符串列表,按优先级从高到低排列,内核通过匹配该属性来选择正确的机器描述结构(DT_MACHINE)或设备驱动。
匹配规则:
- 内核遍历
compatible列表,从第一个字符串开始匹配。 - 匹配成功则停止,使用对应的初始化函数或驱动。
- 格式通常为
"制造商,型号",如"ti,am335x-bone-black"。 - 最后一项通常是SoC家族名称,如
"ti,am33xx",作为兜底匹配。
// 内核中的匹配表示例(arch/arm/mach-omap2/board-generic.c)
DT_MACHINE_START(AM33XX_DT, "TI AM33xx")
.dt_compat = am33xx_boards_compat,
...
MACHINE_END
static const char *am33xx_boards_compat[] __initconst = {
"ti,am33xx",
"ti,am335x-evm",
"ti,am335x-bone",
"ti,am335x-bone-black",
NULL
};
常见错误:将
compatible 写成单个字符串而非列表,或顺序颠倒(将通用型号放在具体型号之前),会导致内核匹配到错误的初始化代码,可能造成外设无法正常工作。
4.4 #address-cells 与 #size-cells
这两个属性定义了设备树中地址描述的长度规则,直接影响所有子节点中 reg 属性的解析方式。
| 属性 | 含义 | 取值 | 影响 |
|---|---|---|---|
#address-cells |
地址字段占用的32位单元数 | 1 或 2 | 决定 reg 中地址部分用几个 <u32> 表示 |
#size-cells |
大小字段占用的32位单元数 | 0 或 1 或 2 | 决定 reg 中长度部分用几个 <u32> 表示 |
常见组合:
- 32位系统:
#address-cells = <1>; #size-cells = <1>;—— 地址和大小各占1个32位单元。 - 64位系统:
#address-cells = <2>; #size-cells = <2>;—— 地址和大小各占2个32位单元(共64位)。 - 无大小设备:
#address-cells = <1>; #size-cells = <0>;—— 用于仅需地址、无需大小的节点(如I2C设备地址)。
// 32位系统示例:地址0x80000000,大小0x10000000
/ {
#address-cells = <1>;
#size-cells = <1>;
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; // 地址: 0x80000000, 大小: 256MB
};
};
// 64位系统示例:地址0x8_00000000,大小0x1_00000000
/ {
#address-cells = <2>;
#size-cells = <2>;
memory@800000000 {
device_type = "memory";
reg = <0x00000008 0x00000000 0x00000001 0x00000000>;
// 高32位地址 低32位地址 高32位大小 低32位大小
};
};
4.5 根节点属性关系图
下图展示了根节点核心属性之间的逻辑关系及其对子节点的影响:
4.6 属性继承与覆盖规则
设备树中,#address-cells 和 #size-cells 遵循严格的继承规则:
- 继承:子节点如果没有显式声明这两个属性,则默认继承父节点的值。
- 覆盖:子节点可以重新声明这两个属性,覆盖父节点的值,从而改变该子节点及其后代节点的地址解析规则。
- 作用域:每个节点的
#address-cells和#size-cells只影响其直接子节点的reg属性解析,不影响兄弟节点或父节点。
/ {
#address-cells = <1>; // 默认:子节点地址用1个u32
#size-cells = <1>; // 默认:子节点大小用1个u32
bus@10000000 {
compatible = "simple-bus";
reg = <0x10000000 0x100000>; // 继承父节点规则:1个地址 + 1个大小
#address-cells = <2>; // 覆盖:该总线下的子节点地址用2个u32
#size-cells = <1>; // 覆盖:该总线下的子节点大小用1个u32
device@10000000 {
reg = <0x00000000 0x10000000 0x1000>; // 2个u32地址 + 1个u32大小
};
};
memory@80000000 {
reg = <0x80000000 0x10000000>; // 继承根节点规则:1个地址 + 1个大小
};
};
调试技巧:当遇到
reg 属性解析错误时,首先检查当前节点的父节点是否正确定义了 #address-cells 和 #size-cells。使用 dtc -I dtb -O dts 反编译DTB文件,可以清晰看到每个节点的实际属性值。
4.7 常见错误与避坑指南
| 错误场景 | 错误示例 | 正确做法 | 后果 |
|---|---|---|---|
根节点缺少 #address-cells |
未定义,子节点使用 reg |
显式定义 #address-cells = <1> |
内核无法解析内存地址,系统崩溃 |
compatible 顺序颠倒 |
"ti,am33xx", "ti,am335x-bone-black" |
具体型号在前,通用型号在后 | 可能匹配到错误的板级初始化代码 |
#size-cells 设为0但 reg 包含大小 |
#size-cells = <0>; reg = <0x1000 0x2000> |
将 #size-cells 设为1,或去掉大小字段 |
内核解析 reg 时长度错位,访问错误地址 |
| 子节点未继承父节点规则 | 父节点 #address-cells=<2>,子节点仍用1个u32表示地址 |
子节点显式覆盖或按父节点规则编写 reg |
地址截断,设备无法正确映射 |
4.8 实战案例:完整根节点配置
以下是一个基于TI AM335x平台的完整根节点配置示例,展示了所有核心属性的实际用法:
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
#address-cells = <1>;
#size-cells = <1>;
chosen {
stdout-path = &uart0;
bootargs = "console=ttyO0,115200 root=/dev/mmcblk0p2 rw";
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; // 256MB DDR3
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a8";
device_type = "cpu";
reg = <0x0>;
operating-points = <
1000000 1325000
800000 1300000
600000 1250000
300000 1100000
>;
};
};
ocp {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x44e00000 0x100000>; // 将0x44e00000映射到0x0
uart0: serial@0 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x0 0x2000>; // 实际物理地址0x44e00000
interrupts = <72>;
clock-frequency = <48000000>;
status = "okay";
};
};
};
关键点总结:
- 根节点通过
model标识板级型号,通过compatible提供内核匹配字符串。 #address-cells和#size-cells定义地址解析规则,影响所有子节点的reg属性。- 子节点可以继承或覆盖父节点的地址解析规则,但必须保持一致性。
compatible列表的顺序至关重要:具体型号在前,通用型号在后。