2. NAND Flash 物理特性:从存储单元到坏块管理

做存储驱动这些年,我最大的感触就是:不懂 NAND 物理特性,写出来的驱动就是空中楼阁。你想想看,连存储单元怎么存数据、Page 和 Block 什么关系、擦除为什么那么慢都不清楚,怎么去优化读写性能?

这一章,我们就从最底层开始,把 NAND Flash 的物理特性彻底讲透。

2.1 存储单元结构:SLC / MLC / TLC / QLC

NAND Flash 的存储单元,本质上是一个浮栅晶体管(Floating Gate Transistor)。通过控制浮栅中电子的数量,来代表不同的数据状态。

根据每个单元能存储的比特数,分为四种类型:

类型 每单元比特数 电压阈值状态数 典型寿命(P/E Cycles) 典型应用
SLC 1 bit 2 种 50,000 ~ 100,000 企业级 SSD、工业级存储
MLC 2 bits 4 种 3,000 ~ 10,000 消费级 SSD、高端 U 盘
TLC 3 bits 8 种 1,000 ~ 3,000 主流消费级 SSD
QLC 4 bits 16 种 500 ~ 1,000 大容量数据盘、归档存储

这里有个关键点:电压阈值状态越多,读取时判断越困难,出错概率也越高。我在项目中遇到过,某款 QLC 颗粒刚出厂时 Raw Bit Error Rate(RBER)就高达 10-3,这意味着每 1000 个 bit 就有 1 个可能出错。如果不配合强大的 ECC 引擎,根本没法用。

核心结论:从 SLC 到 QLC,容量翻倍,但寿命、性能和可靠性都在下降。驱动开发时,必须根据颗粒类型调整 ECC 强度、读写时序和磨损均衡策略。

2.2 Page 与 Block:NAND 的基本操作单位

NAND Flash 的物理结构是分层的:Die → Plane → Block → Page。我们最常打交道的是 Page 和 Block。

  • Page:读写操作的最小单位。典型大小是 16KB + 2KB(主数据区 + OOB/Spare 区)。
  • Block:擦除操作的最小单位。一个 Block 包含 128 ~ 1024 个 Page,典型大小是 2MB ~ 16MB。

为什么读写和擦除的单位不一样?这跟 NAND 的物理结构有关。擦除需要给整个 Block 的衬底加高压,把电子从浮栅中“吸”出来。这个过程没法只针对单个 Page 做。

我个人的习惯:在驱动初始化时,一定要从 ONFI 或 JEDEC 参数表中读取 Page 和 Block 的大小,不要硬编码。不同厂商、不同制程的颗粒,这些参数可能完全不同。

2.3 读写擦除的基本操作

NAND Flash 的三种基本操作,各有各的脾气。

2.3.1 读操作(Read)

读操作相对最快,典型延迟在 50 ~ 100 μs。流程如下:

  1. 发送读命令(00h)和地址(5 个周期)
  2. 等待 tR(读忙时间)
  3. 读取数据(Page 大小)

这里有个坑:读操作也会干扰相邻单元,这叫 Read Disturb。频繁读同一个 Block 的某些 Page,可能导致其他 Page 的数据出错。我曾经遇到过,一个日志系统频繁读同一个 Block,结果一个月后那个 Block 的 RBER 飙升了 10 倍。

2.3.2 写操作(Program)

写操作比读慢得多,典型延迟在 200 ~ 500 μs。流程:

  1. 发送写命令(80h)和地址
  2. 写入数据(Page 大小)
  3. 发送确认命令(10h)
  4. 等待 tPROG(写忙时间)

写操作有个硬性要求:只能从擦除状态(全 1)往已编程状态(0)写。也就是说,要改写一个 Page,必须先擦除整个 Block。这就是为什么 NAND 需要 FTL(Flash Translation Layer)来做垃圾回收和磨损均衡。

2.3.3 擦除操作(Erase)

擦除是最慢的操作,典型延迟在 2 ~ 10 ms。流程:

  1. 发送擦除命令(60h)和 Block 地址
  2. 发送确认命令(D0h)
  3. 等待 tBERS(擦除忙时间)

注意:擦除操作会磨损 Block。每个 Block 都有擦除次数上限(P/E Cycles)。超过上限后,Block 的绝缘层会损坏,导致无法正常存储数据。这就是 Bad Block 产生的主要原因。

2.4 Bad Block 管理

Bad Block 是 NAND Flash 的“原罪”。出厂时就有坏块,使用过程中还会不断产生新的坏块。

2.4.1 坏块的分类

  • 出厂坏块(Initial Bad Block):芯片出厂时就存在的坏块。厂商会在 Block 的第一个 Page 的 OOB 区标记为 0x00(非 0xFF)。
  • 使用中坏块(Grown Bad Block):在使用过程中,由于擦写磨损、读干扰、数据保持等问题产生的坏块。

2.4.2 坏块管理策略

我做过几个项目,总结下来,坏块管理有几种常见策略:

策略 描述 优点 缺点
跳过坏块 在逻辑地址映射时,直接跳过坏块 实现简单 地址映射表复杂
保留块替换 预留一部分好块,坏块出现时替换 地址映射稳定 需要预留空间
动态重映射 运行时检测坏块,动态调整映射 灵活性高 实现复杂,有性能开销

避坑指南:我曾经在某个项目中,为了省事,直接用跳过坏块策略。结果坏块分布不均匀,导致某些逻辑地址范围性能极差。后来改成保留块替换 + 动态重映射的混合策略,才解决问题。

2.5 本章知识结构图

下面这张图,把 NAND Flash 的物理特性、操作流程和坏块管理串起来了:

NAND Flash 物理特性知识结构图 NAND Flash 物理特性知识结构 存储单元结构 Page / Block 读写擦操作 Bad Block 管理 SLC MLC TLC QLC Page:读写最小单位 Block:擦除最小单位 Read:50~100 μs Program:200~500 μs Erase:2~10 ms 出厂坏块 使用中坏块 电压阈值状态数 SLC:2 / MLC:4 / TLC:8 / QLC:16 OOB / Spare 区 ECC、坏块标记 Read Disturb 读干扰问题 坏块管理策略 跳过 / 替换 / 重映射 驱动设计核心:根据颗粒类型调整 ECC、磨损均衡、坏块管理策略 💡 说明: • 存储单元类型决定了容量、寿命和性能的取舍 • Page/Block 结构决定了读写擦的操作粒度,驱动必须遵循

2.6 小结

这一章我们聊了 NAND Flash 的物理特性,从存储单元到坏块管理。说白了,这些东西是驱动开发的“地基”。

你想想看:

  • 知道颗粒是 TLC 还是 QLC,你才能决定用多强的 ECC
  • 知道 Page 和 Block 的大小,你才能设计合理的缓存和垃圾回收策略
  • 知道坏块怎么产生、怎么管理,你才能保证数据可靠性

嗯,这些知识点,后面每一章都会用到。尤其是 FTL 设计、磨损均衡和垃圾回收,全都建立在这个基础上。