3. DTS基本语法:分号、花括号、逗号、引号、注释、标签(label)的使用规则
设备树源文件(.dts)采用一种类C语言的语法结构,但更为精简。掌握其基本标点符号和注释规则,是正确编写和解析DTS文件的前提。本章将逐一解析分号、花括号、逗号、引号、注释以及标签(label)的精确用法。
3.1 语法元素总览
下表列出了DTS语法中核心符号的用途与注意事项,便于快速查阅:
| 符号 | 名称 | 核心用途 | 常见错误 |
|---|---|---|---|
; |
分号 | 结束属性赋值语句 | 遗漏分号导致解析失败 |
{ } |
花括号 | 定义节点(node)的作用域 | 括号不匹配或缺少结尾分号 |
, |
逗号 | 分隔字符串列表、数组元素、reg地址 | 多元素列表末尾误加逗号 |
" " |
双引号 | 包裹字符串类型的属性值 | 字符串内未转义特殊字符 |
// / /* */ |
注释 | 代码说明与临时屏蔽 | 嵌套块注释导致编译错误 |
&label |
标签引用 | 节点复用、覆盖与追加属性 | 标签名冲突或未定义 |
3.2 分号(;)—— 语句终结符
在DTS中,每条属性赋值语句必须以分号结尾。分号是编译器(DTC)识别一条完整属性声明的标志。
/dts-v1/;
/ {
compatible = "vendor,board"; // <-- 分号结束
model = "Example Board"; // <-- 分号结束
#address-cells = <1>; // <-- 分号结束
#size-cells = <1>; // <-- 分号结束
};
⚠️ 常见陷阱: 节点定义的花括号
}之后,通常不需要分号。但若节点后紧跟属性或包含;的宏,则需注意上下文。根节点/ { };末尾的分号是可选的,但推荐保留以保持一致性。
3.3 花括号({ })—— 节点作用域界定符
花括号用于定义一个节点的层级范围。每个节点以{开始,以}结束,内部包含该节点的属性和子节点。
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@10000000 { // 子节点开始
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <33>;
}; // 子节点结束
};
💡 编码规范: 建议花括号采用K&R风格——左花括号不换行,右花括号独占一行。这有助于在复杂嵌套中快速定位节点边界。
3.4 逗号(,)—— 列表与多值分隔
逗号在DTS中有三种典型应用场景:
- 字符串列表分隔:
compatible = "vendor1,device1", "vendor2,device2"; - reg地址/大小对分隔:
reg = <0x1000 0x100>, <0x2000 0x200>; - 时钟/中断等数组元素分隔:
clocks = <&clk1 0>, <&clk2 1>;
// 正确示例:逗号分隔多个compatible值
compatible = "ti,am335x-gpio", "ti,omap-gpio";
// 正确示例:逗号分隔多个reg区域
reg = <0x4A000000 0x1000>, <0x4A001000 0x1000>;
// 错误示例:最后一个元素后误加逗号(某些DTC版本会警告)
// interrupts = <33>, <34>,;
3.5 双引号(" ")—— 字符串属性值
所有字符串类型的属性值必须使用双引号包裹。单引号在DTS中无特殊含义,通常不被使用。
// 字符串属性
compatible = "fsl,imx6q-sabresd";
model = "Freescale i.MX6 Quad SABRE Smart Device Board";
// 字符串列表属性
clock-names = "uart_clk", "ipg_clk";
📌 注意: 字符串内部不能包含未转义的双引号。若需包含引号字符,应使用反斜杠转义(
\"),但实际设备树中极少出现此情况。路径字符串(如clocks = <&clkctrl>)中的&是标签引用,不属于字符串内容。
3.6 注释(// 与 /* */)—— 代码说明
DTS支持两种C语言风格的注释:
| 注释类型 | 语法 | 适用场景 |
|---|---|---|
| 单行注释 | // 注释内容 |
简短说明、行尾注释 |
| 块注释 | /* 注释内容 */ |
多行说明、临时屏蔽代码块 |
// 这是单行注释,用于解释下一行属性的含义
compatible = "vendor,device";
/*
* 这是块注释,可以跨越多行。
* 常用于文件头部版权声明或复杂逻辑说明。
*/
#address-cells = <1>;
#size-cells = <1>;
⚠️ 重要限制: DTC编译器不支持嵌套块注释。例如
/* outer /* inner */ outer */会导致编译错误。如果需要临时注释掉包含/* */的代码块,请改用//逐行注释或使用预处理器#if 0 ... #endif。
3.7 标签(label)—— 节点引用与复用
标签(label)是DTS中实现节点复用、属性覆盖和追加的核心机制。标签定义在节点名称之前,使用冒号(:)与节点名分隔。
3.7.1 标签定义语法
// 定义标签:uart0 指向 serial@10000000 节点
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
status = "okay";
};
3.7.2 标签引用语法
通过&label语法引用已定义的标签,可以在其他位置追加属性或覆盖原有值:
// 引用 uart0 标签,追加 pinctrl 属性
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins>;
status = "disabled"; // 覆盖原 status 为 "disabled"
};
3.7.3 标签在include文件中的典型用法
// 在 .dtsi 文件中定义基础节点
// imx6q.dtsi
soc {
uart1: serial@02020000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02020000 0x4000>;
interrupts = <0 26 0x04>;
status = "disabled";
};
};
// 在 .dts 文件中通过标签启用并配置
// myboard.dts
#include "imx6q.dtsi"
&uart1 {
status = "okay";
pinctrl-0 = <&pinctrl_uart1>;
};
💡 最佳实践: 标签名通常采用小写字母+下划线风格(如
uart1、i2c2),避免与节点名混淆。标签引用&label中的&符号不可省略,否则DTC会将其解析为字符串而非引用。
3.8 综合语法示例:分号、花括号、逗号、引号、注释、标签
以下是一个完整的DTS片段,综合展示了本章所有语法元素:
// SPDX-License-Identifier: GPL-2.0
/*
* 示例设备树:综合语法演示
* 适用于虚拟平台 vplatform
*/
/dts-v1/;
#include "vplatform.dtsi"
/ {
model = "Virtual Platform Demo Board";
compatible = "vendor,vplatform-demo";
/* 内存节点 */
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; // 1GB RAM
};
/* 简单外设总线 */
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
gpio0: gpio@10120000 {
compatible = "vendor,gpio-controller";
reg = <0x10120000 0x1000>;
interrupts = <0 15 4>;
gpio-controller;
#gpio-cells = <2>;
};
};
};
// 通过标签引用,追加板级特定配置
&gpio0 {
status = "okay";
gpio-line-names = "LED_RED", "LED_GREEN", "BTN_USER";
};
3.9 语法规则SVG流程图
下图展示了DTS解析器处理语法元素的基本流程,帮助理解各符号在编译过程中的角色:
3.10 常见语法错误与避坑指南
📋 高频错误清单:
- 遗漏分号: 属性行末尾忘记写
;,导致下一行被错误解析为属性值的一部分。 - 花括号不匹配: 节点嵌套层级较深时,左花括号
{与右花括号}数量不一致。 - 逗号误用: 在
reg或interrupts的最后一个元素后误加逗号(某些DTC版本会报错)。 - 引号缺失: 字符串属性值未加双引号,DTC将其解析为标识符导致编译失败。
- 注释嵌套: 块注释
/* */内部又包含块注释,导致注释提前结束。 - 标签引用遗漏&: 直接写
uart0而非&uart0,DTC将其视为普通字符串。
🔧 调试建议: 使用
dtc -I dts -O dtb -o output.dtb input.dts 2>&1编译时,DTC会给出精确的行号和错误类型。对于复杂设备树,建议分模块编译测试,先确保.dtsi文件无语法错误,再逐步引入板级.dts文件。