3、硬件初始化阶段:SoC上电时序、PMIC配置、时钟与复位管理、BootROM执行流程

好,咱们进入正题。硬件初始化,说白了就是给芯片「通电、喊醒、干活」的过程。很多做上层软件的兄弟觉得这步跟自己没关系,其实不然——你想想看,如果BootROM跑不起来,后面Linux内核连影子都见不着。我这些年调过不少板子,有一半的启动问题都出在这个阶段。

3.1 SoC上电时序——不是插电就能跑

很多人以为上电就是「啪」一下把电源接上。哪有那么简单?一个SoC内部有几十个电源域,每个域的上电顺序都有严格要求。

举个例子,我做过一个NXP i.MX8的项目。它的上电时序是这样的:

  1. VDD_ARM_CORE先上(核心电压,0.9V左右)
  2. 等100μs后,VDD_SOC_SOC上(SoC逻辑电压,1.0V)
  3. 再等200μs,VDD_DRAM上(DDR电压,1.1V)
  4. 最后才是VDD_IO(IO电压,1.8V/3.3V)

为什么这么麻烦?因为如果IO电压先上,而核心还没准备好,IO引脚可能会漏电,甚至把芯片烧掉。嗯,这里要注意——时序错了,芯片可能直接报废

⚠️ 我曾经踩过的坑:有一次调试一块RK3588的板子,上电后SoC死活不启动。查了三天,最后发现是PMIC的使能信号时序反了——VDD_GPU比VDD_CORE先上了50ms。就这50ms,芯片内部锁相环直接锁死。从那以后,我每次画板子都会拿示波器把每路电源的上升沿都抓一遍。

3.2 PMIC配置——电源管理芯片的「剧本」

PMIC(Power Management IC)是上电时序的总导演。它内部有一组寄存器,定义了各路电源的开关顺序、电压值、斜坡时间。

常见的PMIC配置方式有两种:

配置方式 特点 适用场景
OTP(一次性编程) 出厂时固化,不可更改 量产板,成本敏感
I2C/SPI动态配置 上电后由BootROM或uboot改写 开发板,多平台兼容

我个人习惯在开发阶段用I2C配置。为什么呢?因为可以随时调电压,方便调试。比如DDR电压,不同频率需要不同电压——DDR4-2400要1.2V,DDR4-3200要1.35V。你总不能为了改个频率去换PMIC吧?

💡 一个小技巧:PMIC的I2C地址通常可以通过外部电阻配置。我建议在原理图上留个0欧电阻位,万一地址冲突了,焊上就能改。别问我怎么知道的——有一次两个PMIC用了同一个地址,I2C总线直接瘫痪,查了一整天。

3.3 时钟与复位管理——芯片的「心跳」和「重启键」

时钟和复位,这两个东西是SoC启动的基础设施。没有时钟,芯片就是一块死硅;没有复位,芯片可能处于未知状态。

时钟树初始化

SoC上电后,首先需要一个稳定的参考时钟。通常是外部晶振,比如24MHz或32.768kHz。BootROM会做这几件事:

  • 等待晶振起振(一般要几毫秒,晶振起振慢)
  • 配置PLL,把低频时钟倍频到高频(比如从24MHz倍到1.2GHz)
  • 分配时钟到各个模块(CPU、DDR、外设等)

我记得有一次调试,板子启动到一半就卡死了。用示波器一看,晶振波形是乱的——原来是PCB走线太长,晶振信号被干扰了。换了个短走线的板子,问题解决。所以啊,晶振离SoC越近越好,这是硬道理。

复位信号管理

复位信号分几种:

  • POR(上电复位):芯片刚上电时触发,所有寄存器回到默认值
  • 外部复位(EXT_RST):由外部按键或看门狗触发
  • 软复位:由软件写寄存器触发,不重置PLL和DDR

这里有个坑——复位信号的脉宽必须满足SoC手册要求。比如某款芯片要求POR低电平至少保持10ms,如果PMIC上电太快,复位信号还没稳定就释放了,芯片可能进入未知状态。

🔑 关键点:复位释放后,BootROM才开始执行。所以复位信号的时序,直接决定了启动的起点。我建议在原理图上加一个复位监控芯片,比如MAX809,它能保证复位信号在电源稳定后才释放。

3.4 BootROM执行流程——芯片出厂自带的「第一行代码」

BootROM是固化在SoC内部ROM里的程序,芯片一上电就自动执行。它负责最底层的硬件初始化,然后加载下一级启动代码(通常是SPL或uboot)。

BootROM的执行流程大致如下:

  1. 异常向量表初始化:设置中断向量,确保系统能响应异常
  2. CPU模式设置:切换到SVC模式,关闭中断
  3. 时钟初始化:配置PLL,让CPU跑起来
  4. DDR初始化:配置DDR控制器和PHY,让内存可用
  5. 启动设备检测:扫描启动介质(SD卡、eMMC、NAND、SPI Flash等)
  6. 加载启动镜像:从启动设备读取SPL或uboot到DDR
  7. 跳转到下一级:设置栈指针,跳转到加载的代码

你可能会问:「BootROM怎么知道从哪个设备启动?」

嗯,这取决于SoC的启动引脚配置。比如某款芯片有4个BOOT引脚,通过高低电平组合,可以指定16种启动方式:

// 假设BOOT[3:0] = 0010
// 表示从eMMC的Boot Partition 1启动
// 如果0011,则从SD卡启动

// BootROM内部伪代码大致如下:
if (BOOT[3:0] == 0x2) {
    emmc_init();
    emmc_read_boot_partition(0, &buffer, SECTOR_SIZE);
    jump_to(buffer);
} else if (BOOT[3:0] == 0x3) {
    sd_init();
    sd_read_sector(0, &buffer);
    jump_to(buffer);
}

⚠️ 注意:BootROM是只读的,你改不了。所以如果BootROM有bug,只能通过芯片的勘误表(Errata)来规避。我遇到过一款芯片,BootROM在读取eMMC时有个时序bug,导致某些批次eMMC启动失败。最后只能改板子,加一个外部SPI Flash来绕过。所以选型时,一定要看芯片的Errata文档,别等板子做出来才发现。

3.5 实战经验总结

说了这么多,我总结几条实战经验:

  • 上电时序:用示波器抓每路电源的上升沿,确保时序符合手册。别信仿真,实测才是王道。
  • PMIC配置:开发阶段用I2C动态配置,量产时用OTP固化。记得留调试接口。
  • 时钟:晶振走线要短,远离高频信号。PLL配置要等晶振稳定后再操作。
  • 复位:加复位监控芯片,确保复位信号干净。复位释放后,等几毫秒再操作外设。
  • BootROM:了解SoC的启动设备优先级,合理配置BOOT引脚。如果遇到启动失败,先检查启动设备是否被正确识别。

好了,硬件初始化阶段就讲到这里。下一章咱们聊聊SPL(Secondary Program Loader)——它是怎么从BootROM手里接过接力棒的。到时候我会分享一个我调试SPL时遇到的「玄学」问题,保证让你大开眼界。