4. SPL早期硬件初始化:CPU、时钟、DDR与串口
SPL(Secondary Program Loader)是U-Boot启动流程的第一阶段,其核心任务是在极小的代码空间和有限的时间内完成最关键的硬件初始化,为加载完整的U-Boot(或直接加载内核)铺平道路。本章深入分析SPL中CPU、时钟、DDR和串口的初始化流程,并探讨如何通过优化初始化顺序来缩短启动时间。
4.1 SPL初始化全景图
SPL的初始化流程通常遵循一个固定的顺序,这个顺序由硬件依赖关系决定:必须先有稳定的时钟,才能配置DDR;必须有DDR,才能加载更大的镜像;必须有串口,才能输出调试信息。下图展示了SPL早期初始化的典型流程与依赖关系。
4.2 CPU初始化流程与优化
CPU初始化是SPL复位后执行的第一段代码,通常用汇编语言编写。其核心任务包括设置异常向量表、配置CPU工作模式、初始化关键系统寄存器以及关闭看门狗。
4.2.1 标准初始化步骤
- 设置异常向量表:将向量表基址寄存器指向SPL代码段的起始位置。
- 配置CPU模式:切换到SVC(超级用户)模式,关闭IRQ和FIQ中断。
- 初始化关键寄存器:如CP15协处理器中的SCTLR(系统控制寄存器),关闭MMU和Cache。
- 关闭看门狗:防止在后续初始化过程中发生意外复位。
- 设置栈指针:为C语言运行环境准备栈空间(通常指向SRAM顶部)。
MRS读取、修改、再使用一条MSR写回,而不是多次读写。
4.2.2 优化方向
| 优化项 | 传统做法 | 优化做法 | 预期收益 |
|---|---|---|---|
| 异常向量表 | 完整复制16个向量 | 仅复制必要向量,其余指向默认处理 | 减少约100个指令周期 |
| 看门狗操作 | 单独函数调用 | 内联汇编直接写寄存器 | 减少函数调用开销 |
| 栈指针设置 | 使用立即数加载 | 使用PC相对寻址计算栈顶 | 提高代码位置无关性 |
4.3 时钟系统初始化流程与优化
时钟初始化是SPL中最耗时的环节之一,因为PLL(锁相环)的锁定时间通常需要几十到几百微秒。时钟初始化的目标是让CPU、DDR、外设总线工作在正确的频率上。
4.3.1 标准初始化步骤
- 选择时钟源:从外部晶振或内部振荡器切换到PLL输出。
- 配置PLL:设置倍频因子(M)、分频因子(N)和后分频(K),计算目标频率。
- 等待PLL锁定:轮询PLL锁定状态寄存器,确认频率稳定。
- 配置分频器:设置AHB、APB等总线时钟的分频系数。
- 使能外设时钟:为DDR控制器、串口等外设提供时钟门控。
4.3.2 优化方向
时钟初始化的优化主要集中在减少等待时间和合并配置操作上。
- 预计算PLL参数:在编译时计算好PLL配置值,避免运行时计算。
- 使用快速锁定模式:部分SoC支持PLL快速锁定模式,可将锁定时间缩短50%以上。
- 并行配置:如果SoC有多个PLL,可以依次触发锁定,然后统一等待,而不是串行等待每个PLL。
- 跳过非必要PLL:如果某些外设(如USB、以太网)在SPL阶段不需要,可以延迟配置其时钟。
4.4 DDR初始化流程与优化
DDR初始化是SPL中最复杂、最耗时的环节,通常需要执行DDR控制器的配置、PHY的训练以及时序校准。DDR初始化的成功与否直接决定了系统能否正常运行。
4.4.1 标准初始化步骤
- 配置DDR控制器:设置地址映射、行列地址宽度、突发长度等参数。
- 配置DDR PHY:设置IO驱动强度、阻抗匹配、DQS(数据选通)延迟等。
- 执行DDR训练:包括写电平训练、读门控训练、DQ(数据线)训练等。
- 验证DDR:写入并读取测试模式,确认数据完整性。
- 初始化DDR内存映射:将DDR地址空间映射到CPU的地址空间。
4.4.2 优化方向
| 优化策略 | 描述 | 风险 | 适用场景 |
|---|---|---|---|
| 使用预置参数 | 跳过训练,直接使用编译时确定的参数 | 温度/电压变化可能导致不稳定 | 量产板卡,环境可控 |
| 快速训练模式 | 减少训练迭代次数,使用粗调代替精调 | 时序余量降低 | 原型验证阶段 |
| 缓存训练结果 | 将训练结果保存到非易失存储,下次直接加载 | 需要额外的存储空间 | 支持EEPROM/OTP的SoC |
| 并行训练 | 如果DDR是多通道的,可以并行训练 | 硬件必须支持独立通道 | 多通道DDR系统 |
4.5 串口初始化流程与优化
串口初始化虽然简单,但它是SPL调试和日志输出的关键。串口初始化通常放在DDR初始化之后,因为串口缓冲区可能位于DDR中。
4.5.1 标准初始化步骤
- 使能串口时钟:通过时钟门控寄存器使能UART模块的时钟。
- 配置波特率:根据系统时钟频率计算分频系数,设置波特率寄存器。
- 配置数据格式:设置数据位、停止位、校验位。
- 配置FIFO:使能发送和接收FIFO,设置触发阈值。
- 使能串口:使能发送器和接收器。
4.5.2 优化方向
- 延迟初始化:如果SPL阶段不需要串口输出,可以将其推迟到U-Boot完整阶段初始化。
- 使用固定波特率:在编译时确定波特率,避免运行时计算分频系数。
- 批量输出:将多个调试信息合并为一次字符串输出,减少串口中断次数。
4.6 初始化顺序优化策略
初始化顺序的优化需要综合考虑硬件依赖、时间开销和调试需求。以下是一个经过优化的SPL初始化顺序示例。
4.7 实践案例:基于i.MX6ULL的SPL优化
以NXP i.MX6ULL处理器为例,其SPL默认初始化顺序为:CPU → 时钟 → DDR → 串口。通过以下优化措施,可以将SPL阶段启动时间从约350ms缩短到约180ms。
| 优化措施 | 优化前时间 | 优化后时间 | 节省时间 |
|---|---|---|---|
| PLL并行配置 | 120μs | 50μs | 70μs |
| DDR快速训练 | 200μs | 80μs | 120μs |
| 串口延迟初始化 | 30μs | 0μs(推迟到U-Boot) | 30μs |
| DMA加载U-Boot | 50μs | 20μs | 30μs |
| 总计 | 400μs | 150μs | 250μs |
4.8 本章小结
SPL早期硬件初始化是U-Boot启动流程的基石,其优化需要深入理解硬件架构和依赖关系。本章的核心要点包括:
- CPU初始化是起点,应尽量精简,合并寄存器操作。
- 时钟初始化的优化重点在于PLL的并行配置和快速锁定。
- DDR初始化是耗时大户,快速训练和预置参数是主要优化手段。
- 串口初始化可以延迟或使用GPIO模拟,以节省时间。
- 初始化顺序的优化需要平衡依赖关系和时间开销,必要时可以重叠执行。