当一块i.MX6ULL芯片从完全断电状态上电启动时,内部固件会按照严格的层次结构逐步加载系统。这个过程就像多米诺骨牌效应,前一级程序负责唤醒和初始化后一级程序,直到完整的操作系统接管硬件控制权。在这个启动链条中,BootROM和BootLoader扮演着最关键的两个角色。
BootROM是固化在芯片内部ROM存储器中的一段不可修改的代码,它在芯片出厂时就已经被写入,具有最高的执行优先级。当芯片复位或上电时,CPU会从固定的内存地址开始执行指令,这个地址指向的就是BootROM的入口点。由于存储在ROM中,BootROM具有极高的可靠性,即使在外部存储介质损坏的情况下也能保证最基本的启动功能。
BootLoader则是存储在外部可编程存储器(如NOR Flash、eMMC等)中的二级引导程序,常见的如U-Boot、RedBoot等。它由BootROM加载执行,主要职责包括硬件初始化、操作系统镜像加载、启动参数传递等。与BootROM不同,BootLoader可以由开发者根据需求进行定制和更新。
i.MX6ULL的BootROM存储在芯片内部128KB的ROM空间中,采用掩膜ROM工艺制造,这意味着其内容在芯片生产阶段就已经固化,无法通过常规手段修改。这种设计带来了极高的可靠性,但同时也意味着无法通过软件更新来修复潜在的BUG。
BootROM的代码执行时,CPU处于ARMv7-A架构的Secure状态,拥有最高的执行权限。它会初始化最基础的硬件模块,包括:
BootROM上电后首先会读取BOOT_MODE[1:0]引脚的状态,决定芯片的启动模式。i.MX6ULL支持以下几种主要模式:
| 模式选择 | 引脚状态 | 功能描述 |
|---|---|---|
| 内部Boot | 00 | 从外部存储加载BootLoader |
| 串行下载 | 01 | 通过USB/串口下载固件 |
| 保留 | 10 | 保留未使用 |
| 测试模式 | 11 | 工厂测试用途 |
在常规嵌入式系统中,最常用的是内部Boot模式。此时BootROM会继续读取GPIO_SD_BOOT[3:0]等配置引脚,确定具体从哪个外部设备加载BootLoader。支持的设备包括:
提示:启动引脚的状态必须在芯片复位释放前保持稳定,否则可能导致启动模式识别错误。建议在硬件设计时加上拉/下拉电阻确保电平稳定。
BootROM按照以下顺序尝试初始化存储设备并查找有效的BootLoader:
这个流程可以通过烧写eFUSE来定制,但一旦烧写就无法逆转。以下是典型的初始化代码逻辑:
c复制void boot_init_media(void)
{
// 初始化时钟和IO配置
ccm_init();
iomux_init();
// 按优先级尝试各种存储设备
if(efuse_force_device()) {
init_forced_device();
} else {
for(int i=0; i<BOOT_DEVICE_COUNT; i++) {
if(boot_cfg & (1<<i)) {
if(init_device(i) == SUCCESS) {
break;
}
}
}
}
}
BootROM期望在存储设备的固定位置(如SD卡的1KB偏移处)找到称为Image Vector Table(IVT)的数据结构。IVT包含了BootLoader在内存中的加载地址、入口点地址以及各种校验信息。典型的IVT结构如下:
c复制typedef struct {
uint32_t header; // 固定为0x402000D1
uint32_t entry; // 程序入口地址
uint32_t reserved1;
uint32_t dcd_ptr; // 设备配置数据指针
uint32_t boot_data; // 启动数据指针
uint32_t self; // IVT自身地址
uint32_t csf; // 加密签名指针
uint32_t reserved2;
} ivt_header;
BootROM会先将IVT加载到内部RAM中解析,然后根据其中的信息将实际的BootLoader二进制文件拷贝到指定的内存地址。这个过程中还会检查可选的Digital Signature,确保镜像的完整性和合法性。
在BootLoader主体执行前,BootROM会解析DCD(Device Configuration Data)段。这是一组寄存器配置命令,用于初始化关键外设:
c复制// 示例DCD配置片段
dcd_header:
.word 0x400A8000 // DCD起始地址
.word 0x0000002C // 命令数量
// 配置IOMUX控制器
.word 0x020E02B4 // IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00
.word 0x00000005 // 设置为GPIO功能
// 配置DDR控制器
.word 0x021B001C // MMDC0_MDSCR
.word 0x00008000 // 使能DDR预充电
DCD允许开发者在BootLoader运行前精确配置硬件环境,特别是对于DDR内存控制器这种需要严格时序的外设。BootROM会逐条执行DCD命令,确保后续代码能在正确的硬件环境下运行。
当完成以下步骤后,BootROM会将控制权移交给BootLoader:
移交时的CPU状态要求:
U-Boot在i.MX6ULL上的启动过程分为两个主要阶段:
SPL阶段(Secondary Program Loader)
完整U-Boot阶段
这种两阶段设计主要是由于i.MX6ULL内部RAM容量有限(128KB),无法一次性加载完整的U-Boot镜像。
通过串口捕获的U-Boot启动日志可以清晰看到启动流程:
code复制U-Boot SPL 2021.04 (Mar 15 2022 - 16:23:45 +0800)
Trying to boot from MMC1
U-Boot 2021.04 (Mar 15 2022 - 16:23:45 +0800)
CPU: Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 44C
Reset cause: POR
Model: Freescale i.MX6 ULL 14x14 EVK Board
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Loading Environment from MMC... OK
In: serial
Out: serial
Err: serial
Net: FEC0
日志中首先出现的是SPL阶段的输出,显示它从MMC设备启动。然后是完整U-Boot的版本信息、CPU状态检测、内存初始化、外设检测等过程。
现代U-Boot使用Device Tree来描述硬件配置。i.MX6ULL的典型处理流程:
设备树使得同一份U-Boot二进制可以支持不同的硬件变种,大大提高了灵活性。
i.MX6ULL支持HAB(High Assurance Boot)安全启动功能,主要流程:
启用HAB需要在编译时使用NXP提供的CST工具对镜像进行签名:
bash复制# 生成签名密钥对
cst --gen-keys 2048 --keyfile keys/key.pem
# 对U-Boot镜像签名
cst --o signed_u-boot.imx --i u-boot.imx --sign \
--key keys/key.pem --cert keys/cert.pem
安全启动相关的eFUSE包括:
这些eFUSE一旦烧写就无法恢复,因此建议:
警告:错误的eFUSE烧写可能导致芯片永久无法启动,务必谨慎操作并保留备份芯片。
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 无任何输出 | 电源/时钟故障 | 检查核心电压、复位信号、时钟频率 |
| 卡在BootROM | 启动设备识别错误 | 确认BOOT_MODE引脚电平、测量存储接口信号 |
| IVT验证失败 | 镜像烧写位置错误 | 使用hexdump检查存储设备前1KB数据 |
| DDR初始化失败 | DCD配置不当 | 比对参考板DCD设置、测量DDR电源和复位 |
| 跳转U-Boot后死机 | 内存地址冲突 | 检查U-Boot链接脚本中的内存布局 |
串口日志捕获:
LED指示灯法:
c复制// 在关键代码段添加LED控制
void indicate_stage(int stage) {
GPIO1->GDIR |= (1<<3); // 设置GPIO1_3为输出
GPIO1->DR = (stage & 1) << 3; // 根据阶段设置LED
}
通过观察LED闪烁模式可以判断卡在哪个启动阶段
JTAG调试:
启动时间分析工具:
bash复制# 在U-Boot中启用时间戳功能
setenv bootdelay -2
setenv bootcmd "tic; source; tic; echo Boot time: \$time_difference"
加速技巧:
内存布局优化:
lds复制/* 修改U-Boot链接脚本 */
.text : {
*(.vectors) /* 中断向量表放最前面 */
KEEP(*(.boot*)) /* 关键启动代码集中存放 */
*(.text*)
}
确保热点代码连续存放,提高Cache命中率