3. DTS基本语法:分号、花括号、斜杠、引号、注释等基础语法规则
设备树源文件(.dts)是一种结构化的文本描述语言,其语法风格与C语言类似,但更为精简。掌握分号、花括号、斜杠、引号和注释等基础符号的用法,是正确编写和解析DTS文件的起点。本章将逐一拆解这些语法元素,并通过实例和图示帮助理解。
3.1 分号(;)—— 语句的终结符
在DTS中,分号用于结束绝大多数语句。无论是属性赋值、节点定义还是标签引用,只要是一个完整的逻辑行,末尾都必须加上分号。漏加分号是编译DTS时最常见的错误之一。
关键规则: 每个属性赋值、节点结束的花括号后、以及某些特殊指令(如
#include 除外)都需要分号。
// 正确示例:每个属性赋值后都有分号
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
interrupt-parent = &intc;
// 错误示例:缺少分号会导致编译失败
// model = "MyBoard" // 缺少分号
// reg = <0x1000 0x200>; // 正确
3.2 花括号({})—— 节点的边界
花括号用于定义设备节点的层级范围。一个节点以节点名开始,后跟花括号包裹的内容,最后在花括号后加分号结束。花括号内的内容可以是属性赋值,也可以是子节点。
/* 节点定义结构 */
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
status = "okay";
}; // 注意:花括号后必须加分号
编码习惯: 花括号的缩进风格建议采用K&R风格(左花括号不换行),这样在嵌套层级较深时更容易对齐阅读。
3.3 斜杠(/)—— 根节点与路径分隔
斜杠在DTS中有两种核心用途:
- 根节点标识: 使用
/ { ... };表示设备树的根节点,所有外设节点都挂载在根节点之下。 - 路径分隔符: 在引用节点或定义节点路径时,使用斜杠分隔父子层级,如
&ocp或/soc/bus@1000。
/ {
model = "MyCustomBoard";
compatible = "vendor,board-rev1";
chosen {
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; // 256MB
};
};
3.4 引号("")—— 字符串的容器
双引号用于包裹字符串类型的属性值。常见的字符串属性包括 compatible、model、status 等。如果属性值包含多个字符串,可以用逗号分隔,每个字符串独立加引号。
| 属性 | 值类型 | 示例 |
|---|---|---|
compatible |
字符串列表 | "ti,am3352-uart", "ti,omap3-uart" |
model |
单字符串 | "TI AM335x BeagleBone Black" |
status |
单字符串 | "okay" 或 "disabled" |
device_type |
单字符串 | "memory" 或 "cpu" |
注意: 数字、二进制数组(
< >)和字节数组([ ])不需要加引号。误加引号会将数值变为字符串,导致驱动解析错误。
3.5 注释(// 与 /* */)—— 文档化你的设备树
DTS支持两种注释风格,与C/C++完全一致:
- 单行注释: 使用
//,从双斜杠开始到行尾结束。 - 多行注释: 使用
/* ... */,可以跨越多行,常用于屏蔽大段代码或写详细说明。
// 这是单行注释,通常用于简短说明
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
/*
* 这是多行注释
* 可以在这里详细描述寄存器的用途
* 或者记录修改历史
*/
reg = <0x44e09000 0x2000>; // 基地址 0x44e09000,长度 8KB
interrupts = <72>; // SPI 中断号
};
最佳实践: 在复杂的节点上方使用多行注释说明硬件模块的功能、时钟源和电源域信息,有助于团队协作和后期维护。
3.6 综合语法结构图
下面的SVG图展示了DTS文件的基本语法骨架,包括根节点、子节点、属性赋值以及各种符号的配合关系。
3.7 常见语法错误与避坑指南
| 错误类型 | 错误示例 | 正确写法 | 说明 |
|---|---|---|---|
| 漏加分号 | reg = <0x1000> |
reg = <0x1000>; |
属性赋值必须以分号结尾 |
| 花括号后缺分号 | node { ... } |
node { ... }; |
节点定义结束必须加分号 |
| 字符串未加引号 | status = okay; |
status = "okay"; |
字符串必须用双引号包裹 |
| 数字误加引号 | reg = "0x1000"; |
reg = <0x1000>; |
数值应放在尖括号内 |
| 注释嵌套 | /* /* */ */ |
避免嵌套或使用 // |
多行注释不支持嵌套 |
3.8 完整语法示例
下面是一个完整的DTS文件片段,综合展示了本章所学的所有语法规则:
// SPDX-License-Identifier: GPL-2.0
/*
* 设备树示例:展示基础语法
* 适用于虚拟平台 vexpress-a9
*/
/dts-v1/;
#include <dt-bindings/interrupt-controller/irq.h>
/ {
model = "V2P-CA9";
compatible = "arm,vexpress-v2p-ca9", "arm,vexpress";
interrupt-parent = <&gic>;
#address-cells = <1>;
#size-cells = <1>;
chosen {
stdout-path = &uart0;
};
memory@60000000 {
device_type = "memory";
reg = <0x60000000 0x40000000>; // 1GB RAM
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges;
uart0: serial@1c090000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x1c090000 0x1000>;
interrupts = <0 15 4>;
clocks = <&v2m_clk24mhz>;
status = "okay";
}; // uart0 节点结束
gic: interrupt-controller@1c001000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x1c001000 0x1000>,
<0x1c000100 0x100>;
}; // gic 节点结束
}; // soc 节点结束
}; // 根节点结束
总结: 分号是语句的句号,花括号是节点的围墙,斜杠指向根和路径,引号保护字符串,注释让代码会说话。掌握这五个基础符号,你就已经迈出了设备树编程的第一步。