4. 二级Bootloader(SPL/MLO):从存储介质加载、DDR初始化、时钟配置
在嵌入式系统的启动流程中,一级Bootloader(ROM Code)完成最基本的硬件探测后,控制权移交给二级Bootloader。在TI OMAP/AM335x等典型SoC中,二级Bootloader被称为SPL(Secondary Program Loader)或MLO(MMC Loader)。本章深入剖析SPL/MLO的核心职责:从NAND、eMMC或SD卡等存储介质中加载后续镜像、完成DDR控制器的初始化以及配置系统时钟树。
4.1 SPL/MLO的定位与加载流程
SPL/MLO是ROM Code与完整Bootloader(如U-Boot proper)之间的桥梁。其体积被严格限制(通常不超过SoC内部SRAM的大小,例如TI AM335x的SRAM为64KB~128KB),因此必须精简高效。
典型的加载流程如下:
- ROM Code加载SPL/MLO: ROM Code根据启动引脚(Boot Pins)的配置,从指定的存储介质(如SD卡的第1个分区、eMMC的BOOT分区、NAND的起始块)读取固定大小(如128KB)的SPL/MLO镜像到内部SRAM。
- SPL/MLO执行: CPU从SRAM中执行SPL/MLO代码。此时系统运行在低速时钟下,DDR不可用。
- 基础硬件初始化: SPL/MLO首先配置关键的时钟源(如PLL)、电源管理单元(PMIC)以及引脚复用(Pin Mux),为DDR初始化做准备。
- DDR初始化: 调用DDR控制器驱动,配置时序参数(如CAS Latency、tRCD、tRP等),完成DDR控制器的校准(如写电平校准、读数据眼图校准)。
- 加载U-Boot proper: DDR初始化成功后,SPL/MLO从存储介质中读取完整的U-Boot镜像(通常为u-boot.img或u-boot-dtb.img)到DDR中的指定地址。
- 跳转执行: SPL/MLO设置好栈指针和参数(如设备树地址、ATAG参数),然后跳转到DDR中的U-Boot入口点。
4.2 从存储介质加载:NAND、eMMC与SD卡
SPL/MLO必须支持多种存储介质,每种介质的访问方式和初始化流程差异巨大。下表对比了三种主流介质的加载特性:
| 特性 | NAND Flash | eMMC | SD卡 |
|---|---|---|---|
| 接口协议 | ONFI / Toggle | MMC 协议 (v4.x/v5.x) | SPI / SD 协议 |
| 坏块管理 | 必须软件处理坏块 | 内部FTL,对主机透明 | 内部FTL,对主机透明 |
| ECC需求 | 需要硬件ECC引擎(如BCH) | 内部处理,主机无需关心 | 内部处理,主机无需关心 |
| 启动分区 | 固定起始块(如Block 0) | 专用BOOT分区(可配置) | FAT32分区,特定文件名(如MLO) |
| SPL/MLO大小限制 | 通常受限于1个Page大小(如4KB~16KB) | 受限于SRAM大小(如128KB) | 受限于SRAM大小(如128KB) |
| 初始化复杂度 | 高(需初始化NAND控制器、ECC引擎) | 中(需发送MMC命令、设置总线宽度) | 低(SPI模式最简单) |
4.3 DDR初始化:从时序到校准
DDR初始化是SPL/MLO中最关键也最复杂的部分。错误的时序参数将导致系统无法启动或数据损坏。DDR初始化通常包含以下步骤:
- DDR控制器复位与时钟使能: 确保DDR控制器处于复位状态,使能DDR时钟(如DDR PLL输出)。
- DDR PHY配置: 配置物理层(PHY)的阻抗、驱动强度、ODT(片上端接)等模拟参数。
- 时序参数编程: 根据DDR芯片的数据手册,设置控制器寄存器中的CL、tRCD、tRP、tRAS、tRFC等参数。
- ZQ校准: 对于DDR3/DDR4,需要执行ZQ校准(通过ZQ引脚)来校准输出驱动和ODT。
- 写电平校准(Write Leveling): 对于DDR3/DDR4,用于补偿DQS与CK之间的飞行时间差。
- 读数据眼图校准(Read DQS Gating / Training): 调整DQS门控信号,确保正确捕获读数据。
4.4 时钟配置:PLL与时钟树
在DDR初始化之前,必须配置好系统时钟。SoC内部通常包含多个PLL(锁相环),用于生成CPU、DDR、外设等不同频率的时钟。SPL/MLO需要完成以下时钟配置:
- 主PLL配置: 将低频晶振(如24MHz)倍频到CPU核心频率(如1GHz)。
- DDR PLL配置: 生成DDR控制器和PHY所需的工作频率(如DDR3-800对应400MHz时钟)。
- 外设PLL配置: 为存储介质接口(如MMC、NAND控制器)提供时钟。
- 时钟分频与门控: 设置各模块的时钟分频系数,并确保未使用的外设时钟处于关闭状态以降低功耗。
4.5 代码示例:SPL/MLO中DDR初始化片段
以下是一个简化的DDR初始化代码片段,展示了在TI AM335x平台上配置DDR2时序的过程。实际代码会包含更多的寄存器配置和校准步骤。
/* ddr_init.c - 简化版DDR2初始化 */
#include <asm/arch/ddr_defs.h>
void ddr_init(void)
{
/* 1. 使能DDR时钟 */
writel(0x00000001, &cm_per->l4ls_clkstctrl);
writel(0x00000002, &cm_per->l3_clkstctrl);
writel(0x00000002, &cm_dpll->ddr_clkctrl);
/* 2. 配置DDR PLL (假设输入24MHz, 目标DDR频率400MHz) */
/* 设置倍频因子: M = 400 * 2 / 24 = 33.33, 取整为33 */
writel(0x00000021, &cm_dpll->ddr_clksel); /* 设置M=33, N=1 */
/* 等待PLL锁定 */
while (!(readl(&cm_dpll->ddr_clksel) & (1 << 6)));
/* 3. 配置DDR控制器时序 (以MT47H128M16为例) */
struct ddr_ctrl_regs regs = {
.sdram_config = (SDRAM_TYPE_DDR2 << 0) |
(BANK_8 << 4) |
(ROW_14 << 8) |
(COL_10 << 12),
.sdram_timing1 = (CL_5 << 0) | /* CAS Latency = 5 */
(tRCD_5 << 8) | /* tRCD = 5 cycles */
(tRP_5 << 16), /* tRP = 5 cycles */
.sdram_timing2 = (tRFC_127 << 0), /* tRFC = 127 cycles */
.sdram_timing3 = (tRAS_15 << 0), /* tRAS = 15 cycles */
};
writel(regs.sdram_config, &ddr_ctrl->sdram_config);
writel(regs.sdram_timing1, &ddr_ctrl->sdram_timing1);
writel(regs.sdram_timing2, &ddr_ctrl->sdram_timing2);
writel(regs.sdram_timing3, &ddr_ctrl->sdram_timing3);
/* 4. 执行ZQ校准 (DDR2需要) */
writel(0x00000001, &ddr_phy->zq_config);
while (readl(&ddr_phy->zq_status) & 0x1);
/* 5. 等待DDR控制器就绪 */
while (!(readl(&ddr_ctrl->status) & (1 << 0)));
printf("DDR initialization complete.\n");
}
4.6 本章小结
二级Bootloader(SPL/MLO)是嵌入式系统启动流程中的关键一环,它负责在资源极度受限的环境下完成存储介质加载、DDR初始化和时钟配置。理解SPL/MLO的工作原理,对于调试启动问题、优化启动时间以及移植新硬件平台至关重要。本章涵盖了从存储介质特性对比到DDR初始化流程的完整知识链,为后续深入分析U-Boot proper的启动过程奠定了坚实基础。