3. NAND Flash原理:SLC/MLC/TLC/QLC差异、Page/Block/Plane/Die层级、读写擦除特性、延迟分布
各位同学,咱们今天聊聊NAND Flash。这东西,说白了就是嵌入式系统的“硬盘”,但脾气比硬盘怪多了。我刚开始做存储驱动时,被它折腾得够呛——明明写进去了,读出来却是错的;明明擦除了,下次写又报错。后来我才明白,不理解它的“性格”,你就没法跟它好好相处。
3.1 SLC/MLC/TLC/QLC:容量与寿命的博弈
NAND Flash存储数据,靠的是浮栅晶体管里存了多少电子。电子存得越多,阈值电压就越高。读数据时,我们给一个参考电压,看管子通不通,就能判断是“0”还是“1”。
SLC(Single-Level Cell):一个Cell只存1个bit。只有两种状态:0和1。阈值电压窗口大,干扰小,寿命长。擦写次数能到10万次。我早期做工业级产品,首选就是SLC。虽然贵,但稳。
MLC(Multi-Level Cell):一个Cell存2个bit。四种电压状态:00、01、10、11。容量翻倍,但电压窗口被切成了四份。干扰变大,寿命降到1万次左右。嗯,这里要注意:MLC的“M”是“Multi”,但业内习惯把2bit叫MLC,3bit叫TLC,4bit叫QLC。
TLC(Triple-Level Cell):一个Cell存3个bit。八种电压状态。容量更大,但电压窗口更窄。寿命只有3000-5000次。消费级SSD的主力。我做过一个项目,客户非要用TLC做车载记录仪,结果一年后坏块率飙升。后来我建议他改用MLC,问题才解决。
QLC(Quad-Level Cell):一个Cell存4个bit。十六种电压状态。容量最大,但寿命只有1000次左右。读取速度也慢。说白了,就是拿寿命换容量。适合冷数据存储,比如监控录像、备份盘。
| 类型 | 每Cell比特数 | 电压状态数 | 典型擦写次数 | 读取延迟 | 写入延迟 |
|---|---|---|---|---|---|
| SLC | 1 | 2 | 100,000 | ~25μs | ~200μs |
| MLC | 2 | 4 | 10,000 | ~50μs | ~600μs |
| TLC | 3 | 8 | 3,000 | ~75μs | ~900μs |
| QLC | 4 | 16 | 1,000 | ~120μs | ~1500μs |
核心结论:容量越大,寿命越短,速度越慢。选型时,先问自己:这个产品要写多少次?数据多重要?别为了省几毛钱,后面赔几万块售后。
3.2 Page/Block/Plane/Die:NAND的层级结构
NAND Flash不是一坨铁板,它有清晰的层级。理解这个层级,你才能知道为什么读写擦除这么“麻烦”。
Die(晶粒):一个独立的芯片颗粒。每个Die有自己的控制逻辑、寄存器、I/O引脚。多个Die可以封装在一个芯片里,通过CE(Chip Enable)片选信号来区分。我做过一个项目,用了4个Die交错访问,吞吐量直接翻了3倍多。
Plane(平面):一个Die内部又分成多个Plane。每个Plane有自己的缓存寄存器(Page Register)。为什么要有Plane?因为可以并行操作。比如,同时从两个Plane读数据,速度翻倍。但要注意:不同Plane的擦除操作不能同时进行,会打架。
Block(块):一个Plane包含多个Block。Block是擦除的最小单位。你想想看,擦除一次,整个Block的数据全没了。所以,更新数据时,不能直接覆盖,得先读到缓存,修改,再写到另一个空闲Block,最后把旧Block标记为“待擦除”。这就是“写放大”的根源。
Page(页):一个Block包含多个Page。Page是读写的最小单位。典型大小是4KB、8KB、16KB。读一个Page,大概几十微秒。写一个Page,几百微秒到几毫秒。注意:写之前,Page必须是“空”的(已擦除状态)。
我的习惯:设计文件系统时,我会把文件系统的“块大小”对齐到NAND的Page大小。比如Page是8KB,我就把文件系统块设为8KB。这样能避免跨Page读写,减少性能损耗。
3.3 读写擦除特性:为什么NAND这么“矫情”
NAND Flash有三个基本操作:读、写(编程)、擦除。每个操作都有自己的脾气。
读操作:最快。给一个地址,等几十微秒,数据就出来了。但要注意:读太多会干扰相邻Cell的数据。这叫“读干扰”。我曾经遇到一个bug,连续读同一个Block几百次后,相邻Page的数据就出错了。解决方案是:读计数达到阈值后,把数据搬走,擦除重写。
写操作(编程):比读慢一个数量级。写一个Page,需要几百微秒到几毫秒。写的过程是:先把数据加载到Page Register,然后施加高压脉冲,把电子注入浮栅。电压越高,注入越快,但磨损也越大。所以,控制器会动态调整写电压——刚开始用高电压快速写,快写完时用低电压精调。
擦除操作:最慢。擦除一个Block,需要几毫秒到十几毫秒。擦除是把Block里所有Cell的电子都拉出来,让它们回到“1”状态。注意:擦除只能按Block来,不能只擦一个Page。所以,如果你只想改一个Page的数据,得把整个Block读出来,改掉那个Page,再写到另一个空闲Block。这就是“垃圾回收”的由来。
避坑指南:我曾经在项目里直接调用NAND的擦除命令,没检查Block是不是“忙”。结果擦除到一半,又发了个读命令,导致数据全乱。记住:每次操作前,一定要检查状态寄存器,确认设备空闲了再发下一条命令。
3.4 延迟分布:别被“平均延迟”骗了
很多芯片手册只给一个“典型延迟”。但实际项目中,延迟是分布式的,不是固定值。你想想看,如果按典型值设计超时,遇到慢的Page,系统就卡死了。
读延迟分布:大部分Page读得很快,但有些Page因为干扰、磨损,需要多次尝试才能读对。比如TLC,第一次读可能失败,需要调整参考电压再读一次。这叫“重读”。重读次数多了,延迟就上去了。
写延迟分布:新Block写得快,旧Block写得慢。因为磨损后,电子注入效率下降,需要更长的编程时间。我做过测试:一个新Block写一个Page只要300μs,同一个Block擦写5000次后,写一个Page要800μs。
擦除延迟分布:同样,新Block擦除快,旧Block擦除慢。而且,擦除时间受温度影响很大。低温下,电子更难拉出来,擦除时间可能翻倍。
实战建议:设计超时时间时,别用典型值。用最大值,再加20%余量。比如手册说写一个Page最大900μs,你就设1.2ms超时。别问我怎么知道的——我吃过亏。
好了,这一章就到这里。NAND Flash的原理,说白了就是“电子管理艺术”。理解了它的层级和特性,你才能写出靠谱的驱动和文件系统。下一章,咱们聊聊FTL(Flash Translation Layer),看看怎么把NAND的“坏脾气”藏起来,让上层应用觉得它就是个普通硬盘。