2. DTS 基础语法回顾:节点、属性、标签、包含文件、宏定义在 DTS 中的使用

在深入时钟频率与电压域优化之前,必须牢固掌握 DTS(Device Tree Source)的核心语法。DTS 是描述硬件拓扑的领域特定语言,其解析结果——设备树(Device Tree)——是 Linux 内核与硬件交互的桥梁。本章将系统回顾节点、属性、标签、包含文件与宏定义五大语法要素,并结合时钟/电压域配置场景进行讲解。

学习提示: 本章所有示例均基于 ARM64 平台通用 DTS 规范。如果你已有 DTS 基础,可直接跳至 2.5 节“宏定义在时钟配置中的应用”,该节与后续优化章节直接关联。

2.1 节点(Node):硬件设备的树形抽象

节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线或功能块。节点通过路径(如 /soc/clocks/clock@f0001000)唯一标识,并形成从根节点 / 开始的树形结构。

节点语法规则:
  • 节点名格式:节点名[@单元地址],其中单元地址通常为寄存器基址。
  • 节点内可包含子节点和属性,用花括号 { } 界定范围。
  • 每个节点必须以分号 ; 结束。
// 根节点
/ {
    model = "Example SoC Board v2.0";
    compatible = "vendor,example-soc";

    // 时钟控制器节点(带单元地址)
    clk_pll: clock@f0001000 {
        compatible = "vendor,pll-clock";
        reg = <0x0 0xf0001000 0x0 0x1000>;
        clocks = <&osc_24m>;
        #clock-cells = <0>;
    };

    // 电压调节器节点(无单元地址)
    vdd_core: regulator-vdd-core {
        compatible = "regulator-fixed";
        regulator-name = "vdd_core";
        regulator-min-microvolt = <800000>;
        regulator-max-microvolt = <1200000>;
    };
};
设备树节点层级结构 根节点 / soc chosen clock@f0001000 uart@f0002000 📌 节点路径示例:/soc/clock@f0001000 单元地址 f0001000 必须与 reg 属性中的基地址一致

2.2 属性(Property):描述设备特征的关键字段

属性是附着在节点上的键值对,用于描述设备的寄存器地址、中断号、时钟源、电压范围等特征。属性值支持多种数据类型,下表列出 DTS 优化中最常用的属性类型:

数据类型 语法示例 典型用途 时钟/电压域相关
字符串 compatible = "vendor,device"; 驱动匹配标识 时钟控制器兼容性
32位整数 reg = <0xf0001000 0x1000>; 寄存器地址/大小 时钟寄存器基址
64位整数 clock-frequency = <0x1dcd6500>; 频率值(Hz) PLL 输出频率
布尔值 status = "disabled"; 设备启用/禁用 关闭未用时钟域
phandle 列表 clocks = <&clk_pll &osc_24m>; 引用其他节点 时钟级联关系
字节数组 local-mac-address = [00 11 22 33 44 55]; MAC 地址等 较少用于时钟域
⚠️ 常见错误: 在时钟频率属性中,clock-frequency 的单位是 Hz,而非 kHz 或 MHz。例如 500 MHz 应写为 <500000000>,而非 <500>。此类错误会导致内核时钟框架计算出错,外设初始化时序异常。

2.3 标签(Label):节点引用的“指针”

标签是 DTS 中用于引用节点的别名机制。在节点名前加上 标签名:,即可在其他位置通过 &标签名 引用该节点。标签在时钟域配置中至关重要,因为时钟生产者(PLL)与消费者(外设)之间需要大量交叉引用。

// 定义标签
clk_pll: clock@f0001000 {
    #clock-cells = <0>;
    clock-frequency = <1000000000>;  // 1 GHz
};

// 在其他节点中引用标签
uart0: serial@f0002000 {
    compatible = "vendor,uart";
    clocks = <&clk_pll>;  // 引用 clk_pll 节点
    clock-names = "baud";
};

// 通过 & 在根节点外修改标签节点属性
&clk_pll {
    clock-frequency = <1200000000>;  // 覆盖为 1.2 GHz
};
标签引用关系 clk_pll: clock@f0001000 #clock-cells = <0> uart0: serial@f0002000 clocks = <&clk_pll> i2c0: i2c@f0003000 clocks = <&clk_pll> 🔗 标签机制:一个时钟源可被多个外设引用 通过 &clk_pll 修改频率,所有消费者同步生效

2.4 包含文件(#include):模块化硬件描述

DTS 支持 C 语言预处理器指令 #include,用于引入头文件(.dtsi)或其他 DTS 片段。这是实现硬件描述模块化的核心手段——SoC 厂商提供公用的 .dtsi 文件,板级开发者只需包含并覆盖少量属性即可。

包含文件最佳实践:
  • SoC 级 .dtsi: 定义芯片内部所有外设节点、时钟树、中断控制器等通用内容。
  • 板级 .dts: 包含 SoC .dtsi,然后添加板级特有设备(如外部时钟源、电压调节器)。
  • 覆盖机制: 在板级文件中通过 &节点标签 { ... }; 修改 SoC 级节点的属性。
// soc.dtsi — SoC 通用定义
/ {
    soc {
        clk_pll: clock@f0001000 {
            compatible = "vendor,pll-clock";
            clock-frequency = <1000000000>;
        };
        uart0: serial@f0002000 {
            compatible = "vendor,uart";
            clocks = <&clk_pll>;
        };
    };
};

// board.dts — 板级文件
#include "soc.dtsi"

/ {
    model = "MyBoard v1.0";
};

// 覆盖 SoC 级时钟频率(优化:降低频率以减少初始化功耗)
&clk_pll {
    clock-frequency = <800000000>;  // 从 1 GHz 降至 800 MHz
};

// 添加板级特有设备
&uart0 {
    status = "okay";
    current-speed = <115200>;
};

2.5 宏定义(#define):编译期常量与频率计算

宏定义在 DTS 中主要用于定义频率常量、寄存器偏移和掩码,使代码更具可读性和可维护性。在时钟优化场景中,宏定义可以避免硬编码魔法数字,并支持编译期频率计算。

宏定义用途 示例 优化价值
频率常量 #define MHZ 1000000
#define CPU_FREQ 1200 * MHZ
避免数值错误,便于批量调整
寄存器偏移 #define PLL_CTRL_OFFSET 0x10 提高寄存器配置可读性
电压阈值 #define VDD_CORE_MIN 800000 统一电压域配置入口
时钟选择器 #define CLK_SRC_PLL 0
#define CLK_SRC_OSC 1
编译期选择时钟源
// clock-defs.h — 时钟宏定义头文件
#ifndef __CLOCK_DEFS_H
#define __CLOCK_DEFS_H

// 频率单位
#define KHZ                    1000
#define MHZ                    1000000
#define GHZ                    1000000000

// PLL 输出频率配置
#define PLL_DEFAULT_FREQ       (1000 * MHZ)   // 1 GHz
#define PLL_OPTIMIZED_FREQ     (800 * MHZ)    // 800 MHz(低功耗模式)
#define PLL_TURBO_FREQ         (1400 * MHZ)   // 1.4 GHz(性能模式)

// 电压域配置
#define VDD_CORE_NOMINAL       1000000        // 1.0V
#define VDD_CORE_LOW           900000         // 0.9V
#define VDD_CORE_HIGH          1100000        // 1.1V

// 时钟源选择
#define CLK_SRC_OSC_24M        0
#define CLK_SRC_PLL0           1
#define CLK_SRC_PLL1           2

#endif

// board.dts — 使用宏定义
#include "clock-defs.h"
#include "soc.dtsi"

&clk_pll {
    clock-frequency = <PLL_OPTIMIZED_FREQ>;  // 编译后为 800000000
};

&vdd_core {
    regulator-min-microvolt = <VDD_CORE_LOW>;
    regulator-max-microvolt = <VDD_CORE_HIGH>;
};
宏定义编译期展开流程 board.dts clock-frequency = <PLL_OPTIMIZED_FREQ> C 预处理器 #define → 文本替换 board.dtb clock-frequency = <800000000> ⚙️ 宏定义展开示例: #define PLL_OPTIMIZED_FREQ (800 * MHZ) → (800 * 1000000) → 800000000 最终 DTB 中存储的是纯数值 800000000,宏在编译期已展开

2.6 综合示例:时钟频率与电压域配置的 DTS 片段

以下综合示例将节点、标签、包含文件和宏定义全部应用于一个实际的时钟-电压协同配置场景。该配置是后续章节“减少外设初始化时间”优化的基础。

// clock-voltage-defs.h
#define MHZ                1000000
#define CPU_NOMINAL_FREQ   (1200 * MHZ)
#define CPU_LOW_FREQ       (800 * MHZ)
#define VDD_CORE_NOMINAL   1000000
#define VDD_CORE_LOW       900000

// soc.dtsi
/ {
    cpus {
        cpu0: cpu@0 {
            compatible = "arm,cortex-a72";
            clocks = <&cpu_clk>;
            clock-names = "cpu";
            operating-points = <
                /* kHz         uV */
                1200000       1000000
                800000        900000
            >;
        };
    };

    cpu_clk: clock@f0000000 {
        compatible = "vendor,cpu-clk";
        #clock-cells = <0>;
        clock-frequency = <CPU_NOMINAL_FREQ>;
    };

    vdd_cpu: regulator@f0010000 {
        compatible = "vendor,regulator";
        regulator-min-microvolt = <VDD_CORE_LOW>;
        regulator-max-microvolt = <VDD_CORE_NOMINAL>;
    };
};

// board.dts
#include "clock-voltage-defs.h"
#include "soc.dtsi"

// 优化:降低启动阶段时钟频率,减少初始化时间
&cpu_clk {
    clock-frequency = <CPU_LOW_FREQ>;  // 启动时 800 MHz
};

// 同步降低电压域
&vdd_cpu {
    regulator-min-microvolt = <VDD_CORE_LOW>;
    regulator-always-on;
};
优化要点: 上述配置中,启动阶段将 CPU 时钟从 1.2 GHz 降至 800 MHz,同时将核心电压从 1.0V 降至 0.9V。根据 CMOS 功耗公式 P = C·V²·f,电压降低 10% 可带来约 19% 的功耗节省,且频率降低进一步减少动态功耗。外设初始化期间 CPU 负载较轻,降低频率对初始化完成时间的影响微乎其微,但能显著降低启动峰值功耗。

2.7 本章小结:语法要素与优化关联

语法要素 核心作用 与时钟/电压优化的关联
节点 描述硬件设备树形结构 时钟控制器、电压调节器均以独立节点存在
属性 定义设备寄存器、频率、电压等参数 clock-frequency、regulator-min-microvolt 是优化直接操作对象
标签 提供节点引用别名 通过 &clk_pll 在板级文件中覆盖 SoC 级时钟频率
包含文件 实现硬件描述模块化 SoC .dtsi 定义默认时钟树,板级 .dts 按需调整
宏定义 编译期常量与计算 定义频率/电压常量,支持批量优化调整