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中有三种典型应用场景:

  1. 字符串列表分隔: compatible = "vendor1,device1", "vendor2,device2";
  2. reg地址/大小对分隔: reg = <0x1000 0x100>, <0x2000 0x200>;
  3. 时钟/中断等数组元素分隔: 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解析器处理语法元素的基本流程,帮助理解各符号在编译过程中的角色:

DTS语法解析流程 DTS源文件输入 预处理:处理 #include / #define 词法分析:识别符号与标记 分号 ; → 语句结束 花括号 { } → 节点作用域 逗号 , → 列表分隔 引号 " " → 字符串值 语法树构建:节点+属性 输出 .dtb 二进制 标签 &label 在语法树阶段解析引用

3.10 常见语法错误与避坑指南

📋 高频错误清单:
  • 遗漏分号: 属性行末尾忘记写;,导致下一行被错误解析为属性值的一部分。
  • 花括号不匹配: 节点嵌套层级较深时,左花括号{与右花括号}数量不一致。
  • 逗号误用: 在reg或interrupts的最后一个元素后误加逗号(某些DTC版本会报错)。
  • 引号缺失: 字符串属性值未加双引号,DTC将其解析为标识符导致编译失败。
  • 注释嵌套: 块注释/* */内部又包含块注释,导致注释提前结束。
  • 标签引用遗漏&: 直接写uart0而非&uart0,DTC将其视为普通字符串。
🔧 调试建议: 使用dtc -I dts -O dtb -o output.dtb input.dts 2>&1编译时,DTC会给出精确的行号和错误类型。对于复杂设备树,建议分模块编译测试,先确保.dtsi文件无语法错误,再逐步引入板级.dts文件。