1. Boot ROM 的诞生:芯片上电第一行代码,为什么是 Mask ROM 而不是 Flash?

芯片上电后,第一行代码从哪里来?

这个问题,我刚入行时也想过。当时我天真地以为,Flash 里存着代码,上电直接读就行了。直到第一次参与流片,被架构师问了一句:“Flash 还没初始化,CPU 怎么读它?”

嗯,这一问,直接把我问住了。

1.1 上电瞬间,CPU 面临的核心矛盾

CPU 上电后,第一件事是取指令。但此时,整个芯片的状态是这样的:

  • Flash 控制器还没初始化——无法访问外部 Flash
  • DRAM 控制器还没初始化——内存不可用
  • 时钟/PLL 还没稳定——频率可能不对
  • 电源域还没完全建立——部分模块可能还在复位

说白了,CPU 就像一个刚出生的婴儿,什么都做不了。但它必须立刻执行代码,否则芯片就“死”在那了。

核心结论:上电第一段代码必须存放在一个不需要初始化、上电即就绪的存储介质中。这个介质,就是 Mask ROM。

1.2 Mask ROM vs Flash:本质差异

我见过不少工程师混淆这两个概念。其实它们的区别,从物理层面就决定了。

特性 Mask ROM Flash
制造方式 光刻掩模版直接定义,出厂即固定 通过浮栅晶体管编程,可多次擦写
上电可用性 上电即读,无需初始化 需要初始化控制器、配置时序参数
读取延迟 固定、极低(通常 1-2 个周期) 较高,依赖控制器和时钟
可靠性 极高,不受电压波动影响 受擦写次数、保持时间影响
修改能力 不可修改 可在线升级
成本(大规模) 极低(分摊后) 较高(需要额外工艺步骤)

你想想看,Flash 需要先配置时序、使能时钟、设置读命令——这些操作本身就需要代码。这不就成死循环了吗?

我的经验:有一次调试一块新板子,Flash 初始化代码里有个时序参数配错了,结果芯片反复复位,就是跑不起来。最后用逻辑分析仪抓 Mask ROM 的读信号,才发现是 Flash 控制器配置早了。从那以后,我养成了一个习惯:先确认 Mask ROM 能正常读,再碰 Flash。

1.3 Mask ROM 里到底放了什么?

Mask ROM 的容量通常很小——几 KB 到几十 KB。空间极其宝贵,每一行代码都要精打细算。

一般来说,Boot ROM 包含这几部分:

  1. 硬件初始化代码——设置栈指针、配置基本时钟、使能关键模块
  2. Flash 控制器驱动——初始化外部 Flash 所需的时序和命令序列
  3. 安全验证逻辑——校验下一级镜像的签名或哈希
  4. 启动模式选择——根据管脚电平或 OTP 配置,决定从哪个介质加载
  5. 回退/恢复机制——主镜像损坏时,进入 DFU 或串口下载模式

我个人习惯把 Boot ROM 比作“芯片的 BIOS”。它不负责干活,只负责把干活的人(操作系统)请进来。

1.4 为什么不用 OTP 或 eFuse?

有人会问:OTP(一次性可编程)或 eFuse 也能上电就读,为什么不用?

原因很简单:成本与容量。

  • OTP/eFuse 的位单元面积比 Mask ROM 大得多。同样 64KB,Mask ROM 可能只占 0.5mm²,eFuse 要 5mm² 以上。
  • OTP 的读取需要额外的感测放大器,功耗和延迟都更高。
  • eFuse 的编程需要高压,工艺复杂度增加。

所以业界的主流做法是:Mask ROM 存主代码,OTP/eFuse 存密钥、配置参数、芯片唯一 ID。各司其职,不越界。

注意:有些低成本芯片会直接用 OTP 替代 Mask ROM,但那是牺牲了容量和良率换来的。但凡对安全性和启动可靠性有要求的产品,Boot ROM 必须是 Mask ROM。我在一个 IoT 项目里见过用 OTP 做启动的芯片,结果量产时一批芯片的 OTP 读取不稳定,整批报废——教训深刻。

1.5 Boot ROM 的执行流程

下面这张图,是我在培训新同事时必讲的。它展示了从复位向量到跳转主镜像的完整路径。

Boot ROM 执行流程 复位向量入口 硬件最小初始化 读取启动模式管脚 Flash 启动 DFU/串口下载 初始化 Flash 控制器 加载并跳转 SPL/BL1 DFU 模式说明 等待主机通过 USB/串口 下载固件到 RAM 并执行

流程其实不复杂。关键就几步:复位 → 初始化 → 选路 → 加载。但每一步都可能踩坑。

1.6 一个真实的踩坑案例

我曾经参与过一个车规级芯片项目。Boot ROM 已经流片回来了,功能测试都过了。结果在低温测试(-40°C)时,发现部分芯片无法正常启动。

查了整整两周,最后定位到问题:Mask ROM 里的 Flash 初始化代码,对时钟分频器的等待时间写死了。低温下 PLL 锁定时间变长,代码还没等 PLL 稳定就去读 Flash,读回来的全是乱码。

修复方案?改 Mask ROM 是不可能的——已经刻死了。最后只能改 PCB,外加一个 RC 延时电路,硬生生拖慢复位释放时间。

从那以后,我写 Boot ROM 代码时,所有时序相关的等待都改用硬件定时器轮询,绝不写死循环计数。温度变了、电压变了,代码依然健壮。

经验总结:Boot ROM 是芯片的“第一口气”。它必须足够简单、足够鲁棒、足够保守。任何花哨的优化,都不应该出现在这里。

1.7 小结

Mask ROM 不是技术倒退,而是工程智慧的体现。它用“不可修改”换来了“绝对可靠”,用“容量受限”换来了“零延迟可用”。

理解了这个选择,你就能理解为什么安全启动的根,必须扎在 Mask ROM 里。下一层——Flash 里的镜像,将由 Boot ROM 来验证和加载。但那是后面章节的事了。

嗯,先把这第一行代码的位置搞清楚,后面的路才好走。