第4章 存储介质与启动设备:NOR Flash、NAND Flash、eMMC、SD卡、SPI Flash的启动特性
做Bootloader移植,第一关就是搞清楚你的代码到底从哪儿启动。不同存储介质,启动流程天差地别。我见过不少工程师,拿着NOR Flash的启动代码往NAND上硬套,结果折腾一周都起不来。说白了,每种介质都有自己的脾气,你得顺着它来。
4.1 存储介质的核心差异
先看一张总览表,把五种常见介质的启动特性说清楚。这是我做项目时经常翻的对照表,建议你收藏。
| 特性 | NOR Flash | NAND Flash | eMMC | SD卡 | SPI Flash |
|---|---|---|---|---|---|
| 接口类型 | 并行/CFI | 并行/NAND协议 | MMC协议 | SPI/SDIO | SPI |
| 读取方式 | XIP(片内执行) | 需拷贝到RAM | 需拷贝到RAM | 需拷贝到RAM | 部分支持XIP |
| 坏块管理 | 无 | 必须软件管理 | 内置控制器 | 内置控制器 | 无 |
| 启动速度 | 快(直接执行) | 慢(需搬运) | 中等 | 慢 | 快(XIP时) |
| 典型容量 | 1MB~64MB | 128MB~8GB | 4GB~128GB | 1GB~64GB | 512KB~32MB |
| 启动场景 | 早期Bootloader | 大容量嵌入式 | 手机/平板 | 开发板/单板机 | 低成本IoT |
4.2 NOR Flash:XIP启动的王者
NOR Flash最大的特点就是支持XIP(eXecute In Place)。什么意思?CPU可以直接在Flash上取指令执行,不需要先把代码搬到RAM里。这特性在Bootloader启动阶段太关键了——上电瞬间RAM还没初始化,代码就能跑起来。
关键点:NOR Flash的读时序和SRAM很像,地址线直接映射到CPU的地址空间。比如某款ARM芯片,NOR Flash挂在CS0上,起始地址就是0x00000000。上电后PC指针直接指向这里,开始执行第一条指令。
我在一个工业控制项目里用过NOR Flash启动。当时选的是8MB的并行NOR,映射到0x08000000。Bootloader只有64KB,直接在Flash里跑,速度飞快。但要注意,NOR Flash写操作很慢,擦除一个扇区要几百毫秒。所以运行时数据不能写在NOR里,得放到RAM。
个人习惯:NOR Flash启动时,我会把中断向量表放在Flash开头,然后立即初始化RAM,再把关键数据段搬到RAM里。这样既利用了XIP的快速启动,又保证了运行时的性能。
4.3 NAND Flash:大容量但麻烦多
NAND Flash容量大、价格低,但启动起来特别麻烦。它不支持XIP,CPU不能直接在NAND上执行代码。为什么?因为NAND的读操作是页模式的,一次读一页(通常2KB或4KB),而且有坏块问题。你想想看,CPU取指令时随机访问,NAND根本应付不来。
所以NAND启动的套路是:芯片内部ROM里有一段固化代码(叫ROM Code或BootROM),上电后它先把NAND前几页的数据读到内部SRAM里。这段数据就是你的Bootloader第一阶段(SPL)。SPL负责初始化DRAM,然后把完整的Bootloader从NAND搬到DRAM里执行。
我曾经踩过的坑:NAND Flash出厂时可能有坏块,而且坏块标记通常在每页的 spare area 里。ROM Code读前几页时如果遇到坏块,启动就失败了。后来我学乖了,在烧录时就把Bootloader放在好块区域,并在前几页里加入坏块跳过表。
NAND启动的典型流程:
- ROM Code读取NAND第0页(通常是4KB或8KB)到内部SRAM
- 执行SPL代码,初始化时钟和DRAM控制器
- SPL从NAND读取完整的U-Boot到DRAM
- 跳转到U-Boot入口,继续启动
4.4 eMMC:内置控制器的省心方案
eMMC本质上就是NAND Flash加了一个控制器芯片,封装在一起。这个控制器帮你处理了坏块管理、ECC校验、磨损均衡。对Bootloader来说,eMMC就像一个黑盒子,你通过MMC协议读写就行了,不用操心底层细节。
eMMC启动有两种模式:
- Boot分区模式:eMMC内部有专用的Boot分区,上电后芯片自动从Boot分区读取数据。这分区是只读的,非常安全。
- 用户分区模式:从用户数据分区的特定扇区启动。这种方式灵活,但容易被误擦除。
我建议:量产产品一定要用Boot分区模式。把Bootloader烧到eMMC的Boot分区里,然后把这个分区写保护。这样即使系统崩溃,Bootloader也不会丢。我在一个行车记录仪项目里就这么做的,返修率降了不少。
4.5 SD卡:开发板的标配
SD卡启动在开发板上最常见。它的协议和eMMC很像,都是基于MMC协议。但SD卡有个特点——它支持SPI模式。很多低成本的MCU没有SDIO控制器,只能用SPI接口读写SD卡。
SD卡启动的典型场景:
- 树莓派、BeagleBone等开发板从SD卡启动
- FPGA配置从SD卡加载
- MCU通过SPI接口读取SD卡中的Bootloader
要注意,SD卡的启动顺序是有讲究的。通常芯片会先检测SD卡是否存在,然后读取MBR(主引导记录)或分区表,找到启动分区。如果分区表损坏,启动就失败了。
调试技巧:SD卡启动失败时,先用读卡器在PC上检查分区表。用fdisk -l看看第一个分区是不是FAT32,启动标志有没有设置。我遇到过好几次,问题就是分区表格式不对。
4.6 SPI Flash:小身材大用途
SPI Flash是成本最低的启动方案。它只有4~8个引脚,占PCB面积小,适合IoT设备。SPI Flash也支持XIP吗?部分支持。有些MCU内部有SPI映射模块,可以把SPI Flash映射到地址空间,实现XIP。但大多数情况下,SPI Flash还是需要把代码搬到RAM里执行。
SPI Flash的启动速度受限于SPI时钟频率。一般SPI时钟在20MHz~50MHz,比并行NOR慢不少。所以SPI Flash启动时,Bootloader要尽量小,通常不超过64KB。如果Bootloader太大,启动时间会很长。
| SPI Flash型号 | 容量 | 最大SPI时钟 | 典型启动时间(64KB) |
|---|---|---|---|
| W25Q16 | 2MB | 50MHz | 约10ms |
| W25Q64 | 8MB | 50MHz | 约10ms |
| W25Q128 | 16MB | 50MHz | 约10ms |
注意:SPI Flash的写操作需要先擦除,擦除单位是扇区(通常4KB)。如果你在Bootloader里需要保存配置参数,一定要规划好存储布局。我曾经把配置参数和Bootloader放在同一个扇区,结果每次更新配置都把Bootloader擦掉了……
4.7 启动介质选择决策树
下面这张SVG图帮你快速决策:你的项目该用哪种介质启动?
4.8 启动故障定位思路
启动不起来怎么办?我总结了一套排查思路:
- 先确认硬件连接:用示波器看片选信号、时钟信号、数据线有没有波形。很多时候是虚焊或电平不匹配。
- 检查启动配置:芯片的启动引脚(BOOT0、BOOT1等)有没有设置正确?我见过有人把eMMC启动引脚接成了SD卡模式。
- 验证存储介质:用编程器读出存储介质前几页的数据,看看Bootloader有没有烧录正确。CRC校验一下。
- 跟踪ROM Code:如果芯片有调试接口,单步跟踪ROM Code的执行流程,看它卡在哪个步骤。
核心原则:启动故障定位,从物理层开始,逐层向上排查。不要一上来就怀疑Bootloader代码有问题,先确认硬件和配置是对的。