3. BootROM基础
芯片上电之后到底发生了什么?
这个问题,我当年刚入行时也困惑了很久。看着示波器上那根电源轨缓缓爬升,心里想的是:CPU这会儿在干嘛?谁在管它?
后来做了几个项目才明白,从按下电源到Linux控制台出现,中间藏着一整套精密的接力赛。今天我们就来拆解这条链路,看看BootROM和U-Boot各自扮演什么角色。
3.1 上电后的第一口气:Reset与BootROM
芯片上电,复位信号释放。CPU从复位向量指向的地址开始取指——这个地址,通常是芯片内部BootROM的入口。
BootROM是芯片出厂时固化的一段只读代码。它很小,几KB到几十KB不等。但它要做的事情,一件比一件关键。
BootROM的核心职责:
- 初始化最基本的硬件(CPU、时钟、SRAM)
- 检测启动介质选择引脚(Boot Mode)
- 从选定介质加载下一级代码到SRAM
- 验证签名(如果使能安全启动)
- 跳转到下一级代码入口
说白了,BootROM就是芯片的"接生婆"。它自己不能干太多活,但必须保证下一棒能顺利交出去。
我个人的习惯:看一个新芯片的Datasheet时,先翻BootROM章节。那里藏着芯片设计者对启动流程的全部理解。看懂它,你就掌握了这个芯片的"脾气"。
3.2 第二棒:U-Boot SPL(如果存在)
有些芯片的SRAM很小,装不下完整的U-Boot。这时候就需要一个中间人——SPL(Secondary Program Loader)。
SPL是U-Boot的一个精简版本。它被BootROM加载到SRAM里,然后由它去初始化DDR,再把完整的U-Boot从Flash搬到内存里。
| 对比项 | BootROM | SPL | U-Boot |
|---|---|---|---|
| 存储位置 | 芯片内部ROM | Flash(前几个块) | Flash(后续区域) |
| 大小 | 4KB ~ 64KB | 16KB ~ 64KB | 200KB ~ 2MB |
| 运行环境 | SRAM | SRAM | DDR |
| 可修改性 | 不可修改 | 可修改(需重新烧写) | 可修改 |
| 主要任务 | 加载SPL或U-Boot | 初始化DDR,加载U-Boot | 加载操作系统 |
嗯,这里要注意:不是所有芯片都有SPL阶段。如果芯片内部SRAM够大,BootROM可以直接把U-Boot完整加载进去。但大多数ARM SoC都需要SPL这个中间层。
我曾经踩过的坑:某次调试一块新板子,U-Boot死活跑不起来。查了两天才发现,SPL里DDR初始化参数配错了。BootROM把SPL加载到SRAM后,SPL一初始化DDR就挂。后来我养成了一个习惯——SPL阶段先加串口打印,看到"Hello"再往下走。
3.3 第三棒:U-Boot完整版
U-Boot被加载到DDR后,才真正进入"完全体"状态。这时候它有了充足的内存空间,可以做更多事情。
U-Boot的主要工作包括:
- 初始化外设:网卡、存储控制器、USB等
- 建立设备树:把硬件信息传递给内核
- 加载内核:从Flash、网络或USB读取内核镜像
- 启动内核:设置启动参数,跳转到内核入口
你想想看,U-Boot其实就是一个"迷你操作系统"。它有命令行、文件系统支持、网络协议栈。只不过它的最终目的不是自己运行,而是把控制权交给真正的操作系统。
3.4 最后一棒:操作系统内核
U-Boot跳转到内核入口后,它的使命就完成了。内核接手后,会做自己的初始化:
- 解压自身(如果是压缩镜像)
- 初始化内存管理
- 初始化中断控制器
- 初始化调度器
- 挂载根文件系统
- 启动init进程
从这一刻起,BootROM和U-Boot都成了"历史"。但它们留下的遗产——正确的硬件初始化、完整的设备树、合理的启动参数——决定了内核能否顺利跑起来。
3.5 完整链路图
下面这张图,把整个启动流程串起来了。我建议你把它存下来,调试时对照着看。
3.6 BootROM与U-Boot的分工协作
说到底,BootROM和U-Boot的分工就一句话:BootROM负责"活下来",U-Boot负责"活得好"。
BootROM的代码是固定的,出厂后改不了。所以它只做最基础、最安全的事情。U-Boot则灵活得多,可以适配不同的硬件配置、不同的启动介质、不同的内核版本。
分工边界:
- BootROM:管CPU最基本配置、管启动介质选择、管安全验证
- U-Boot:管DDR初始化、管外设驱动、管内核加载、管命令行交互
我记得有一次调试一块新板子,BootROM总是加载U-Boot失败。后来发现是Flash的时序参数在BootROM里写死了,而那块Flash偏偏不兼容。最后只能换Flash型号。这件事让我意识到:BootROM虽然小,但它的"硬编码"特性决定了整个板子的硬件选型边界。
3.7 避坑指南
我曾经遇到过的几个典型问题:
- BootROM加载U-Boot超时:Flash读取速度太慢,BootROM等不及就放弃了。解决办法是换更快的Flash,或者在BootROM配置里延长超时时间(如果能改的话)。
- SPL阶段DDR初始化失败:DDR参数配置错误,或者PCB布线导致信号质量差。我建议在SPL里加一段DDR读写测试,跑完再往下走。
- U-Boot跳转内核后死机:设备树里某个外设地址写错了,或者内核启动参数不对。这时候串口打印是救命稻草。
嗯,说到串口打印,我个人的习惯是在每个阶段都加一个唯一的打印信息。比如BootROM打印"B",SPL打印"S",U-Boot打印"U"。这样看串口输出就知道卡在哪一步了。简单,但极其有效。
3.8 小结
从芯片上电到操作系统加载,这条链路说长不长,说短不短。但每一步都有它的意义。BootROM是"开荒者",在什么都没有的环境里建立起最基本的运行条件。U-Boot是"建设者",把硬件资源全部激活,为内核准备好舞台。内核是"表演者",最终在舞台上唱主角。
理解了这个全景,你再看具体的BootROM代码、U-Boot代码,就不会觉得它们是一堆杂乱无章的初始化函数了。它们是一个精心设计的接力赛,每一棒都知道自己要做什么,也知道下一棒是谁。