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>;
};
};
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
};
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>;
};
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 按需调整 |
| 宏定义 | 编译期常量与计算 | 定义频率/电压常量,支持批量优化调整 |