Flash 存储器基础:从 NOR 到 NAND,从 SLC 到 QLC
好,咱们正式开始啃 Flash 这块硬骨头。做 NVM 开发,Flash 是绕不过去的基础设施。我个人习惯把 Flash 理解成「电子纸」—— 能写、能擦、能读,但每次擦写都有寿命。这一章咱们把 NOR 和 NAND 的架构差异、SLC/MLC/TLC/QLC 的原理、Page 和 Block 的概念彻底讲透。
NOR Flash vs NAND Flash:架构决定命运
先看一张对比图,我当年第一次看到这个结构时,心里想的是:「这不就是内存和硬盘的区别吗?」 嗯,还真有点那个意思。
说白了,NOR 像一本字典 —— 你可以随便翻到哪一页就读,但写起来很慢。NAND 像一卷卫生纸 —— 你得从头撕到尾,但撕起来很快。我在做嵌入式 Bootloader 时,就吃过 NOR 写入慢的亏:烧一次固件要等 30 秒,后来换成 NAND 的 SPI Flash,3 秒搞定。
SLC / MLC / TLC / QLC:一个 Cell 能存多少比特?
你想想看,Flash 存储单元本质上就是一个浮栅晶体管。通过控制浮栅里的电子数量,来改变阈值电压。能区分几个电压等级,就能存几个比特。
| 类型 | 每 Cell 比特数 | 电压等级数 | 擦写寿命(次) | 读取速度 | 写入速度 |
|---|---|---|---|---|---|
| SLC | 1 | 2 | 10 万 | 最快 | 最快 |
| MLC | 2 | 4 | 1 万 | 快 | 中等 |
| TLC | 3 | 8 | 3 千 ~ 5 千 | 中等 | 慢 |
| QLC | 4 | 16 | 1 千 ~ 3 千 | 慢 | 最慢 |
关键认知: 每 Cell 比特数越高,容量越大,但寿命越短、速度越慢。这不是技术缺陷,是物理规律 —— 电压窗口被切得越细,干扰越敏感。
我曾经在一个工业级项目里,客户要求 SSD 每天写满一次,连续用 5 年。算下来需要 1825 次 P/E 循环。TLC 勉强够,但我建议用 MLC,留 5 倍余量。结果客户选了 TLC,第 3 年就开始出坏块。嗯,这就是现实。
Page 与 Block:Flash 的读写基本单位
做 NVM 开发,必须把 Page 和 Block 刻在脑子里。我见过太多新手把 Flash 当硬盘用,直接按字节写,结果数据全乱套。
- Page(页):读写的最小单位。NAND 的 Page 通常是 4KB、8KB、16KB。NOR 的 Page 一般是 256B 或 512B。
- Block(块):擦除的最小单位。NAND 的一个 Block 包含 64 或 128 个 Page。NOR 的 Block 通常是 64KB 或 128KB。
为什么不能按字节擦除?因为 Flash 的擦除操作需要施加高电压脉冲,这个脉冲的物理范围就是 Block 大小。你想想看,如果允许按字节擦,那控制电路得复杂到什么程度?成本根本扛不住。
避坑指南: 我曾经在写 FTL(Flash Translation Layer)时,没注意 Block 和 Page 的对齐关系。结果每次写 512 字节,都触发了一次读-改-写操作,性能直接腰斩。后来改成按 Page 对齐写入,吞吐量翻了一倍。
Flash 操作的三板斧:读、写、擦
Flash 的操作就三种,但每种都有严格的约束。我习惯用一张表来记:
| 操作 | 单位 | 约束 | 典型耗时(NAND) |
|---|---|---|---|
| 读(Read) | Page | 无特殊约束 | 25~50 μs |
| 写(Program) | Page | 只能将 1 写成 0(即只能清 0) | 200~500 μs |
| 擦(Erase) | Block | 将整个 Block 恢复为全 1 | 1~3 ms |
这里有个容易搞混的点:写操作只能把 1 变成 0,不能把 0 变成 1。要把 0 变回 1,必须擦除整个 Block。所以 Flash 的写入策略是「先擦后写」—— 写之前,目标 Block 必须是擦除状态(全 1)。
⚠️ 重要警告: 不要试图在同一个 Page 上连续写两次!第一次写完后,第二次写会破坏已有数据。我见过有人用 Flash 模拟 EEPROM,没做磨损均衡,结果同一个 Page 被写了 3 次,数据全乱码了。
Flash 的物理特性:为什么需要坏块管理?
Flash 出厂时就有坏块,而且随着使用,坏块会越来越多。这不是质量问题,是物理特性。NAND 的工艺密度太高,不可能保证每个 Cell 都完美。
- 出厂坏块:厂商会在出厂时标记,通常 Block 0 是好的,其他 Block 可能有坏块。
- 使用坏块:随着 P/E 循环增加,氧化层磨损,Cell 失效。
- 读干扰:频繁读取某个 Page,会导致相邻 Page 的电子被干扰,数据出错。
- 写干扰:写某个 Page 时,高电压会影响同一 Block 内的其他 Page。
我记得有一次调试 SSD 固件,发现某个 Block 的读取错误率突然飙升。排查了半天,发现是读干扰导致的 —— 那个 Block 被频繁读取了 100 万次,相邻 Page 的阈值电压偏移了。解决方案是加读干扰刷新(Read Disturb Refresh),每读一定次数就搬移数据。
小结:这一章你该记住什么?
- NOR 适合代码存储,NAND 适合数据存储。
- SLC 寿命最长、速度最快,QLC 容量最大、寿命最短。
- Page 是读写单位,Block 是擦除单位。
- 写操作只能 1→0,擦除操作才能 0→1。
- 坏块管理是 Flash 开发的必修课,别想着绕过去。