2. 设备树语法入门:节点、属性、标签与别名
设备树(Device Tree,DT)是一种描述硬件拓扑和资源分配的数据结构,它使用树形节点和键值对属性来描述硬件设备。本章将系统讲解设备树源文件(.dts)的核心语法要素:节点(node)、属性(property)、标签(label)和别名(aliases)。
学习提示:设备树语法是驱动开发的基础,掌握节点与属性的关系后,才能正确编写和修改设备树文件。建议边学边在开发板上验证。
2.1 节点(Node)—— 设备树的骨架
节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线或功能模块。节点从根节点(/)开始,形成树形层级结构。
2.1.1 节点定义语法
/dts-v1/;
/ {
node1 {
child-node {
/* 子节点内容 */
};
};
node2 {
/* 节点内容 */
};
};
- 根节点:用
/表示,是整个设备树的起点。 - 子节点:在父节点的大括号内定义,节点名通常反映设备类型或实例。
- 节点结束:每个节点必须以分号
;结尾。
2.1.2 节点名称规范
| 格式 | 示例 | 说明 |
|---|---|---|
node-name |
cpu |
通用节点名,不包含地址 |
node-name@unit-address |
uart@fe001000 |
包含寄存器或总线地址,用于区分同类型设备 |
node-name: label |
uart0: serial@fe001000 |
带标签的节点,便于在其他位置引用 |
关键规则:当同一层级存在多个同类型设备时,必须使用
@unit-address 形式来区分地址,否则编译器会报重复节点错误。
2.2 属性(Property)—— 描述设备特征
属性是附加在节点上的键值对,用于描述设备的硬件特征、寄存器地址、中断号、时钟频率等信息。属性值可以是空、整数、字符串、字节数组或这些类型的组合。
2.2.1 常用属性类型
| 属性类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 空属性 | property-name; |
disable; |
仅表示存在,无值 |
| 32位整数 | property-name = <value>; |
reg = <0xfe001000 0x1000>; |
尖括号内可包含多个整数 |
| 64位整数 | property-name = <0x00000000 0x80000000>; |
reg = <0x00000000 0x80000000>; |
用两个32位整数拼接 |
| 字符串 | property-name = "string"; |
compatible = "snps,dw-apb-uart"; |
双引号包裹 |
| 字节数组 | property-name = [hex-bytes]; |
local-mac-address = [00 11 22 33 44 55]; |
十六进制字节序列 |
| 字符串列表 | property-name = "str1", "str2"; |
compatible = "ti,am335x-uart", "ns16550"; |
逗号分隔多个字符串 |
2.2.2 标准属性详解
设备树规范定义了一组标准属性,驱动开发者必须熟悉:
compatible:设备兼容性字符串,驱动匹配的核心依据。格式为"厂商,设备型号"。reg:描述设备地址空间,格式为<地址 长度>对。interrupts:描述中断号、触发类型等信息。status:设备状态,可选okay、disabled、fail等。clocks:引用时钟控制器和时钟索引。
uart0: serial@fe001000 {
compatible = "snps,dw-apb-uart";
reg = <0xfe001000 0x1000>;
interrupts = <0 33 4>;
clocks = <&uart_clk>;
status = "okay";
};
常见错误:忘记在属性值末尾加分号
;,或字符串未使用双引号包裹,会导致设备树编译失败。
2.3 标签(Label)—— 节点的引用标识
标签是附加在节点名称前的标识符,用于在其他位置引用该节点。标签在设备树覆盖(overlay)和跨文件引用中至关重要。
2.3.1 标签定义语法
标签名: 节点名@地址 {
/* 节点内容 */
};
示例:
uart1: serial@fe002000 {
compatible = "snps,dw-apb-uart";
reg = <0xfe002000 0x1000>;
};
2.3.2 标签引用方式
| 引用语法 | 示例 | 说明 |
|---|---|---|
&label |
&uart1 |
在属性值或覆盖文件中引用节点 |
phandle = <&label> |
clocks = <&uart_clk>; |
在属性中引用其他节点 |
&label { ... }; |
&uart1 { status = "disabled"; }; |
在覆盖文件中修改节点属性 |
最佳实践:为所有可能被覆盖或引用的节点添加标签,标签名应具有描述性,如
uart0、i2c1、mmc0。
2.4 别名(Aliases)—— 简化路径引用
别名是定义在根节点 aliases 子节点中的快捷路径映射,用于简化对深层节点的引用。别名在 U-Boot 和内核启动参数中广泛使用。
2.4.1 别名定义语法
/ {
aliases {
serial0 = &uart0;
serial1 = &uart1;
i2c0 = &i2c0;
mmc0 = &mmc0;
};
};
2.4.2 别名使用场景
| 场景 | 示例 | 说明 |
|---|---|---|
| U-Boot 环境变量 | setenv bootargs console=ttyS0,115200 |
通过别名 serial0 映射到实际串口节点 |
| 内核启动参数 | console=ttyAMA0 |
内核根据别名查找对应设备节点 |
| 设备树覆盖 | &mmc0 { status = "okay"; }; |
通过别名引用节点,避免写完整路径 |
| 用户空间访问 | /sys/firmware/devicetree/base/aliases/serial0 |
用户态程序可通过别名路径访问设备树信息 |
2.4.3 别名与标签的区别
| 对比项 | 标签(Label) | 别名(Aliases) |
|---|---|---|
| 定义位置 | 节点定义处 | 根节点下的 aliases 子节点 |
| 引用方式 | &label |
aliases 中的字符串映射 |
| 主要用途 | 设备树内部引用和覆盖 | 外部工具和启动参数引用 |
| 编译后存在 | 转换为 phandle 属性 |
保留为 aliases 节点中的属性 |
| 可读性 | 适合开发者 | 适合脚本和用户空间 |
/* 完整示例:标签与别名配合使用 */
/ {
aliases {
serial0 = &uart0;
serial1 = &uart1;
};
soc {
uart0: serial@fe001000 {
compatible = "snps,dw-apb-uart";
reg = <0xfe001000 0x1000>;
interrupts = <0 33 4>;
status = "okay";
};
uart1: serial@fe002000 {
compatible = "snps,dw-apb-uart";
reg = <0xfe002000 0x1000>;
interrupts = <0 34 4>;
status = "disabled";
};
};
};
实用技巧:在编写设备树时,建议为所有可能被外部引用的设备(串口、I2C、SPI、MMC、以太网)定义别名。别名命名应遵循
设备类型+序号 的规范,如 serial0、i2c1、mmc0。
2.5 综合示例:完整的设备树片段
以下示例综合展示了节点、属性、标签和别名的实际应用:
/dts-v1/;
/ {
model = "Embedded Linux Development Board";
compatible = "vendor,board-rev1";
aliases {
serial0 = &uart0;
serial1 = &uart1;
i2c0 = &i2c0;
mmc0 = &mmc0;
};
chosen {
stdout-path = &uart0;
bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2";
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512MB */
};
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@fe001000 {
compatible = "snps,dw-apb-uart";
reg = <0xfe001000 0x1000>;
interrupts = <0 33 4>;
clocks = <&uart_clk>;
status = "okay";
};
uart1: serial@fe002000 {
compatible = "snps,dw-apb-uart";
reg = <0xfe002000 0x1000>;
interrupts = <0 34 4>;
clocks = <&uart_clk>;
status = "disabled";
};
i2c0: i2c@fe003000 {
compatible = "snps,designware-i2c";
reg = <0xfe003000 0x1000>;
interrupts = <0 35 4>;
clocks = <&i2c_clk>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
rtc@68 {
compatible = "dallas,ds1307";
reg = <0x68>;
};
};
mmc0: mmc@fe004000 {
compatible = "snps,dw-mshc";
reg = <0xfe004000 0x1000>;
interrupts = <0 36 4>;
clocks = <&mmc_clk>;
status = "okay";
};
};
uart_clk: clock {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>;
};
i2c_clk: clock {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <50000000>;
};
mmc_clk: clock {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <100000000>;
};
};
要点总结:
- 节点构成设备树的层级骨架,
@unit-address用于区分同类型设备。 - 属性以键值对形式描述设备特征,
compatible是驱动匹配的核心。 - 标签提供稳定的节点引用标识,支持设备树覆盖和跨文件引用。
- 别名定义在
aliases节点中,为外部工具和启动参数提供快捷路径。 - 所有语句必须以分号
;结尾,注释使用/* */或//。
2.6 本章小结
设备树语法是嵌入式 Linux 驱动开发的基石。节点定义了硬件拓扑,属性描述了设备细节,标签提供了灵活的引用机制,别名则简化了外部访问路径。掌握这四大要素后,开发者能够阅读和编写标准的设备树源文件,为后续的设备树覆盖和动态配置打下坚实基础。