3、启动流程基础:CPU复位行为、启动ROM(Boot ROM)设计、多级启动链(Bootloader分级)、典型RISC-V芯片启动时序。
好,咱们今天聊聊芯片上电之后,到底发生了什么。
很多刚入行的朋友,拿到一块RISC-V开发板,第一件事就是点灯。点完灯就觉得自己会了。但说实话,如果不懂启动流程,你连芯片是怎么“活过来”的都不知道。出了问题,你连从哪下手查都不知道。
我个人习惯,拿到一个新芯片,第一件事不是看外设驱动,而是先看它的启动流程文档。这就像你开一辆新车,得先知道钥匙插哪、怎么点火,对吧?
3.1 CPU复位行为:芯片的“第一口呼吸”
芯片上电或复位信号拉低的那一刻,CPU内部会发生什么?
说白了,就是CPU被强制“归零”,然后去一个固定的地址取第一条指令。这个地址,就是复位向量(Reset Vector)。
核心要点:复位向量是CPU生命的起点。对于RISC-V架构,复位向量通常由硬件设计决定,常见的有0x00000000或0x80000000等。
我记得有一次调试一块国产RISC-V芯片,死活跑不起来。查了半天,发现是复位向量地址和Boot ROM的映射地址没对上。嗯,这种坑,踩过一次就记住了。
复位时,CPU内部寄存器会进入默认状态:
- PC(程序计数器):被设置为复位向量地址
- 通用寄存器:大部分清零,部分可能有默认值
- CSR(控制状态寄存器):进入M模式(机器模式),中断关闭
- 缓存/MMU:默认关闭,需要软件初始化
注意:不同厂商的RISC-V核,复位行为可能有细微差别。比如SiFive的核和Syntacore的核,复位后某些CSR的默认值就不一样。一定要看数据手册,别想当然。
3.2 启动ROM(Boot ROM)设计:芯片出厂自带的“小系统”
CPU复位后,去取第一条指令。但这时候内存还没初始化,Flash里也没代码。那指令从哪来?
答案就是Boot ROM。它是芯片内部的一块只读存储器,出厂时就已经固化好了代码。你改不了它,但它能帮你启动。
Boot ROM通常做这几件事:
- 基本硬件初始化:配置时钟、PLL,让CPU先跑起来
- 检测启动模式:通过管脚电平或efuse,判断从哪启动(比如SPI Flash、SD卡、UART)
- 加载下一级Bootloader:从启动介质中读取代码,放到SRAM或DDR中,然后跳转过去
一个小技巧:我曾经在调试一个量产项目时,发现某些芯片Boot ROM读取SPI Flash总是超时。后来发现是SPI Flash的初始化时序和Boot ROM预期的不一致。解决办法?换了一款兼容性更好的Flash。这种坑,设计阶段就要考虑进去。
Boot ROM的设计原则很简单:越小越好,越稳越好。因为它占的是芯片面积,面积就是成本。所以Boot ROM通常只有几KB到几十KB,功能极其精简。
3.3 多级启动链:为什么需要“分级”启动?
你想想看,Boot ROM那么小,能干的事有限。它不可能认识文件系统,也不可能解析FAT32或者ext4。那怎么办?
答案是分级。就像接力赛,一棒传一棒。
典型的RISC-V芯片启动链是这样的:
| 级别 | 名称 | 存储位置 | 主要任务 |
|---|---|---|---|
| 第0级 | Boot ROM | 芯片内部ROM | 初始化时钟、检测启动介质、加载第1级 |
| 第1级 | SPL / MBR | SPI Flash / SD卡 | 初始化DDR、加载第2级 |
| 第2级 | U-Boot / OpenSBI | DDR中运行 | 加载操作系统内核、设备树 |
| 第3级 | 操作系统内核 | DDR中运行 | 启动Linux/RTOS |
为什么要分这么多级?
- Boot ROM太小,放不下复杂逻辑
- DDR需要初始化,而初始化DDR的代码本身就需要运行空间
- 安全考虑:每一级都可以校验下一级的签名,防止被篡改
避坑指南:我曾经遇到一个项目,SPL代码太大,超过了Boot ROM能加载的大小限制。结果芯片一直复位循环。后来把SPL拆成了两部分,一部分做最基础的DDR初始化,另一部分等DDR起来后再加载。这就是分级的灵活性所在。
3.4 典型RISC-V芯片启动时序:从复位到操作系统
好,咱们把上面这些串起来,看看一个典型的RISC-V芯片从复位到跑起Linux,到底经历了什么。
下面这张图,是我自己画的启动时序图,你可以对照着看:
这张图里,每个阶段都有明确的分工。你想想看,如果没有分级,Boot ROM直接去加载Linux内核,那它得认识文件系统、得初始化DDR、还得解析设备树。这代码量,Boot ROM那几KB根本放不下。
实际项目中,启动时序的调试往往是最头疼的。我遇到过一个问题:芯片在低温环境下,DDR初始化总是失败。查了很久,发现是SPL里DDR的时序参数在低温下需要调整。后来在SPL里加了一个温度检测逻辑,根据温度选择不同的时序参数,问题才解决。
经验之谈:调试启动流程时,最常用的工具就是GPIO点灯和串口打印。在Boot ROM和SPL里,加几个GPIO翻转的代码,用示波器一看,就知道代码跑到哪一步了。这招虽然土,但真的管用。
好了,这一章的内容就到这里。启动流程是固件开发的地基,地基不稳,上面盖什么都白搭。下一章咱们会深入Boot ROM的代码实现,看看那些“出厂即固化”的代码到底是怎么写的。
本章小结:
- CPU复位后从复位向量取指,进入M模式
- Boot ROM是芯片内部固化的启动代码,负责最基础的初始化
- 多级启动链解决了存储空间和功能复杂度的矛盾
- 典型启动时序:复位 → Boot ROM → SPL → U-Boot → 内核