2、启动模式与介质:NOR Flash启动、NAND Flash启动、eMMC启动、SD卡启动、USB启动、UART启动

好,咱们进入第二章。这一章聊的是BootROM的“口粮”从哪来。

说白了,芯片上电后,BootROM要做的第一件事就是——找个地方把代码读进来。这个“地方”就是启动介质。不同的介质,脾气秉性完全不同。我这些年调过的板子,十有八九的启动问题都出在对介质特性的理解不够深上。

你想想看,BootROM是固化在芯片内部的只读存储器,它自己不会跑飞,但它要去“喂”自己第一口饭。这口饭从哪来?怎么嚼?嚼碎了往哪咽?这就是启动模式要解决的问题。

核心观点:启动介质的选择,决定了BootROM的初始化策略、加载速度、安全校验方式,以及整个系统的启动可靠性。选错了介质,或者没处理好介质的坑,轻则启动慢,重则变砖。

2.1 NOR Flash启动

NOR Flash,老大哥了。它的特点是支持XIP(eXecute In Place),也就是芯片可以直接在Flash上执行代码,不用先拷到RAM里。嗯,这里要注意,这个特性在BootROM场景下非常关键。

为什么?因为BootROM本身很小,它只需要把第二阶段的bootloader(比如U-Boot的SPL)从NOR Flash里读出来,然后跳过去执行。由于NOR Flash支持随机访问,BootROM可以直接通过地址总线映射到NOR Flash的存储空间,然后像读内存一样读指令。

我在项目中遇到过一件事:某款工业控制器,用的就是NOR Flash启动。当时发现系统上电后偶尔起不来,查了好久。后来发现是NOR Flash的复位时间比芯片慢了一拍。BootROM去读的时候,Flash还没准备好,读回来的全是0xFF。解决方案很简单——在BootROM里加一个超时重试机制。

个人经验:NOR Flash的读时序相对简单,但要注意它的“读操作”和“写/擦除操作”是完全两套逻辑。BootROM只负责读,所以不用管写。但如果你在调试阶段用编程器烧录NOR Flash,记得检查一下“复位后默认输出”是不是高阻态。我曾经被这个坑过,编程器烧完好好的,焊到板子上就读不出来。

2.2 NAND Flash启动

NAND Flash就不一样了。它便宜、容量大,但有个致命问题——不能XIP。而且NAND Flash天生带坏块,出厂就有,用着用着还会产生新的坏块。

BootROM要从NAND Flash启动,必须先把代码从NAND里读到芯片内部的SRAM或者DDR里,然后再跳转执行。这个“读”的过程,BootROM得自己实现NAND的控制器驱动。

你想想看,BootROM才多大?通常就几十KB。它要在这几十KB里塞下NAND控制器的初始化、时序配置、ECC校验、坏块管理……这活儿不轻松。

我记得有一次调试一个NAND启动的板子,BootROM总是加载到一半就卡死。后来用逻辑分析仪抓了NAND的引脚波形,发现是BootROM里配置的NAND时序参数跟实际颗粒不匹配。那个颗粒的tR(读操作时间)比标准值大了那么一点点,BootROM按标准值去等,结果数据还没准备好就读了。

避坑指南:我曾经因为NAND Flash的“随机读”和“页读”搞混,导致BootROM加载速度慢得离谱。NAND Flash读一个字节和读一页的开销差不了太多,所以BootROM应该按页读取,而不是一个字节一个字节地读。这个优化能让启动时间缩短一个数量级。

2.3 eMMC启动

eMMC,说白了就是把NAND Flash和一个控制器封装在一起,对外暴露一个标准接口。BootROM跟它打交道,用的是MMC/SDIO协议。

eMMC的好处是:坏块管理、ECC校验、磨损均衡,这些脏活累活eMMC自己内部搞定了。BootROM只需要发命令、读数据就行。但代价是——协议复杂。

eMMC启动有专门的“Boot Partition”模式。芯片上电后,eMMC会进入Boot模式,直接把Boot Partition里的数据通过数据线吐出来。BootROM只需要在数据线上等着收就行。这个模式不需要发命令,纯硬件流。

我个人习惯是:如果项目对启动速度要求高,优先考虑eMMC的Boot Partition模式。它比从User Partition读数据快得多,因为eMMC内部会以最高速度把数据推出来。

关键点:eMMC的Boot Partition大小通常是128KB或256KB,要确保你的bootloader能塞进去。塞不进去?那就只能用User Partition启动,但那样BootROM就得先初始化完整的MMC协议栈,代码量会大不少。

2.4 SD卡启动

SD卡启动,在开发板和单板计算机上很常见。它的协议跟eMMC同源,都是MMC协议衍生出来的。但SD卡有个特点——它是可插拔的。

这个“可插拔”带来了一个问题:BootROM怎么知道SD卡插没插?

通常芯片会有一个GPIO引脚,连接到SD卡座的卡检测开关。BootROM先读这个GPIO,如果检测到卡,再初始化SD卡控制器,发命令,读数据。如果没检测到,就跳到下一个启动介质。

我记得有一次,客户反馈说他们的设备用SD卡启动,有时候能起来有时候起不来。排查后发现是卡检测引脚的滤波电容太大,导致BootROM读GPIO的时候电平还没稳定。嗯,硬件设计上的小细节,往往就是这种问题。

建议:SD卡启动的BootROM代码里,一定要加超时处理。有些劣质SD卡初始化时间很长,甚至超过500ms。如果BootROM等不及就放弃了,那系统就启动失败了。我一般会在代码里留一个“长等待”模式,给慢卡一个机会。

2.5 USB启动

USB启动,这个就有点意思了。它不是为了量产用的,而是为了开发和调试。

BootROM通过USB跟PC上的工具通信,PC把bootloader镜像通过USB发过来,BootROM收到后写到RAM里,然后跳转执行。这样就不用反复烧录Flash了,开发效率高很多。

USB启动的难点在于:BootROM要实现USB Device协议栈。虽然不用实现完整的USB协议,但至少要实现枚举、控制传输、批量传输这几个基本功能。而且USB的时序要求很严格,BootROM得在中断或者轮询模式下及时响应USB总线事件。

我做过一个项目,BootROM的USB启动总是枚举失败。后来发现是芯片的USB PHY(物理层)初始化顺序不对。先复位PHY再配置时钟,和先配置时钟再复位PHY,结果完全不同。这个顺序在芯片手册里写得很隐晦,我是翻了应用笔记才找到的。

注意:USB启动通常需要PC端安装特定的工具软件,比如Rockchip的RKDevTool、Allwinner的PhoenixSuit。这些工具跟BootROM之间的通信协议是私有的,没有公开文档。如果你想自己写一个开源工具,得逆向分析协议,工作量不小。

2.6 UART启动

UART启动,最原始,也最可靠。它只需要三根线:TX、RX、GND。任何带UART的芯片都能用。

但UART的速度慢啊。通常115200bps,算下来每秒也就传11KB左右。一个256KB的bootloader,要传20多秒。所以UART启动只适合小镜像,或者作为最后的“救砖”手段。

BootROM的UART启动流程一般是:上电后先等一会儿,看UART RX引脚上有没有数据。如果有,就按约定的协议(比如XMODEM、YMODEM,或者自定义协议)接收数据,写到RAM里,然后跳转。

我曾经用UART启动救过一块变砖的板子。NAND Flash里的数据全乱了,NOR Flash也烧坏了,但BootROM还在。我通过UART把一个新的bootloader传进去,然后bootloader再去初始化eMMC,从eMMC里把完整的系统拉起来。嗯,那种感觉,就像给一个昏迷的人做心肺复苏。

实用技巧:UART启动的BootROM里,我建议加一个“超时自动跳过”的逻辑。如果几秒钟内没收到数据,就自动跳到下一个启动介质。这样量产的时候,不需要人工干预,板子自己会从正常的介质启动。只有需要调试的时候,才在PC端发数据触发UART启动。

2.7 启动介质对比总结

说了这么多,咱们用一张表来总结一下。这张表是我自己整理的,每次选型的时候都会拿出来看一眼。

启动介质 速度 可靠性 成本 适用场景 BootROM复杂度
NOR Flash 中 高 高 小容量、快速启动、XIP需求 低
NAND Flash 高 中(需ECC) 低 大容量、成本敏感 高
eMMC 高 高 中 主流嵌入式、Android设备 中
SD卡 中 中 低 开发板、可插拔存储 中
USB 高 高 无额外成本 开发调试、量产烧录 高
UART 低 高 无额外成本 调试、救砖 低

2.8 启动介质选择的核心逻辑

最后,我用一张流程图来展示BootROM选择启动介质的核心逻辑。这张图是我自己画的,你可以把它当作一个决策树。

BootROM启动介质选择决策流程 芯片上电复位 读取启动模式配置引脚(BOOT_MODE) 启动介质 类型判断 NOR XIP直接执行 NAND 初始化NAND控制器 eMMC/SD 初始化MMC协议 加载Bootloader到SRAM 安全校验(签名/哈希)

这张图展示的是最典型的流程。实际项目中,BootROM通常会按照一个优先级列表去尝试多个介质。比如先试eMMC,不行再试SD卡,再不行就进USB/UART等待模式。这个优先级是在芯片出厂时固化的,或者通过外部引脚配置的。

我个人习惯是:在产品设计阶段,就把所有可能用到的启动介质都列出来,然后跟芯片原厂确认BootROM支持的介质列表和优先级。有些芯片的BootROM不支持NAND启动,你非得用NAND,那就得在外部加一个小的NOR Flash或者SPI Flash来放一个“二级BootROM”,由它再去初始化NAND。嗯,这种方案虽然绕,但确实能解决问题。

好了,这一章的内容就到这里。启动介质是BootROM的“粮仓”,选对了粮仓,后面的安全回滚保护才能有基础。下一章我们会深入BootROM的启动流程,看看代码是怎么一步步跑起来的。

可可精品资料库