3、Flash存储器基础:NOR Flash与NAND Flash的区别、Flash的读写特性、坏块管理与ECC

做Bootloader,说白了就是跟Flash打交道。你想想看,代码存在Flash里,启动时从Flash读出来,升级时往Flash里写——整个流程都绕不开它。我最早接触Flash时也犯过迷糊,觉得不就是个存储芯片嘛,后来踩了坑才明白,NOR和NAND完全是两码事。

3.1 NOR Flash与NAND Flash的核心区别

这两种Flash,名字听着像,脾气可差远了。我习惯用一个比喻来理解:NOR像内存,NAND像硬盘。

NOR Flash支持随机访问,CPU可以直接在上面执行代码,不需要先拷贝到RAM里。很多低成本的嵌入式系统就是这么干的——代码直接在NOR上跑。但NOR的容量做不大,成本也高,写入速度还慢。

NAND Flash就不一样了。它按页读写、按块擦除,不能随机寻址。CPU没法直接在NAND上执行代码,得先把代码读到RAM里再跑。但NAND容量大、价格低、写入快,现在主流的手机、平板、路由器,清一色用NAND。

我整理了一张对比表,方便你对照着看:

特性 NOR Flash NAND Flash
访问方式 随机访问,类似SRAM 页访问,类似块设备
是否支持XIP 支持(片上执行) 不支持
容量密度 低(通常≤512Mb) 高(可达数Tb)
读速度 快(随机读) 快(连续读)
写速度 慢(按字节/字编程) 快(按页编程)
擦除速度 慢(块擦除) 快(块擦除)
坏块管理 出厂无坏块 出厂可能有坏块
位翻转率 极低 较高,需ECC
典型应用 BIOS、固件存储 文件系统、大容量数据

关键结论:Bootloader如果放在NOR上,可以直接运行;如果放在NAND上,必须由一级引导(SPL/ROM code)先把Bootloader主体加载到RAM里。

3.2 Flash的读写特性——你必须知道的底层细节

Flash的读写,跟普通RAM完全不是一个套路。我刚开始做驱动时,按SRAM的方式写Flash,结果数据死活不对。后来才明白,Flash有三大铁律:

  1. 写之前必须先擦除——Flash只能把1写成0,不能把0写成1。想改数据?先把整块擦成全1,再写。
  2. 擦除以块为单位——NOR的块大小通常是64KB~128KB,NAND的块大小通常是128KB~2MB。你哪怕只改一个字节,也得擦掉整个块。
  3. 写入有次数限制——每个块大约能擦写10万次(NOR)到1万~10万次(NAND)。用完了就废了。

这里我放一张流程图,帮你理清Flash的读写流程:

Flash读写操作流程 开始操作 读还是写? 直接读取目标地址 检查块是否已擦除 已擦除? 执行块擦除操作 执行页编程/字节编程 操作完成

实战经验:我在做U-Boot移植时,遇到过NAND写入后读出来全是0xFF的情况。排查了半天,发现是擦除操作没等完成就发了编程命令。Flash操作一定要等状态寄存器返回就绪,别偷懒。

3.3 坏块管理——NAND绕不过去的坎

NOR Flash出厂时基本没有坏块,但NAND不一样。NAND出厂时就可能有坏块,而且随着使用还会产生新的坏块。这不是质量问题,是NAND的物理特性决定的——它的存储单元密度太高,工艺上做不到100%完美。

我当年第一次用NAND时,天真地以为所有块都能用。结果写文件系统时,某个块怎么擦都擦不干净,读出来的数据全是乱的。后来才学会,NAND驱动必须做坏块管理。

坏块管理通常分两步:

  1. 出厂坏块标记——每个块的第1页(或最后1页)的某个字节(通常是第0字节或第2048字节)被写成了非0xFF的值。驱动初始化时扫描这些标记,建立坏块表。
  2. 运行时坏块检测——每次擦除或编程后,检查状态寄存器。如果返回失败,就把这个块标记为坏块,以后不再使用。

这里有个常见的做法:

/* NAND坏块检查示例(伪代码) */
int nand_is_bad_block(struct nand_chip *chip, int block) {
    uint8_t bad_mark;
    /* 读取块内第一个页的OOB区域第0字节 */
    nand_read_oob(chip, block * chip->pages_per_block, &bad_mark, 1);
    /* 如果标记不为0xFF,说明是坏块 */
    return (bad_mark != 0xFF);
}

注意:不同厂商的NAND,坏块标记位置可能不同。有的放在页内数据区,有的放在OOB区。一定要看数据手册,别想当然。

3.4 ECC纠错——让数据更可靠

NAND还有个让人头疼的问题——位翻转。就是某个bit本来存的是1,读出来变成了0,或者反过来。SLC NAND的位翻转率大约每10^9次读出现1次,MLC和TLC就更严重了。

为什么会这样?因为NAND的存储单元之间会互相干扰,读的次数多了,电荷会慢慢流失。这不是芯片坏了,是正常现象。

ECC(Error Correction Code)就是用来解决这个问题的。常见的ECC算法有:

ECC算法 纠错能力 开销 适用场景
汉明码(Hamming) 1bit纠错,2bit检错 低 SLC NAND,老设备
BCH码 可配置(4~24bit) 中等 MLC NAND,主流方案
LDPC码 强(数十bit) 高 TLC/QLC NAND,大容量
Reed-Solomon 强(突发错误) 高 特殊场景

在Bootloader里,ECC通常由硬件模块自动完成。你只需要配置好ECC参数,读写时硬件会自动计算和校验。但有些老芯片需要软件实现ECC,那就得自己写算法了。

我的建议:选型时优先选带硬件ECC的NAND控制器。软件ECC虽然也能用,但会拖慢启动速度。我曾经在一个项目里用软件BCH纠8bit错误,结果每读一页要花几毫秒算ECC,Bootloader加载时间直接翻倍。

3.5 本章小结

NOR和NAND,一个像内存,一个像硬盘。做Bootloader时,你得根据硬件选型决定怎么用它们。NOR简单直接,适合存启动代码;NAND容量大、成本低,但需要处理坏块和ECC。嗯,这些基础概念搞清楚了,后面讲分区加载和镜像管理时,你才不会觉得吃力。