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文件的基本语法骨架,包括根节点、子节点、属性赋值以及各种符号的配合关系。

DTS 基本语法结构图 根节点 / { ... }; model = "MyBoard"; compatible = "vendor,board"; cpu@0 { ... }; memory@80000000 { ... }; soc { ... }; uart0: serial@44e09000 reg = <0x44e09000 0x2000>; interrupts = <72>; 语法要点 • 分号结束每个语句 • 花括号定义节点范围 • 斜杠表示根或路径

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 节点结束
};  // 根节点结束
总结: 分号是语句的句号,花括号是节点的围墙,斜杠指向根和路径,引号保护字符串,注释让代码会说话。掌握这五个基础符号,你就已经迈出了设备树编程的第一步。