2.2 NOR与NAND——存储世界的两条路
你面前有两条路,你必须选一条
想象你是一个存储架构师,坐在会议室的白板前。白板上写着你的设计约束:512KB的NOR Flash,128个4KB块,Cortex-R5 MCU,FreeRTOS,AUTOSAR。你要在这上面跑一个文件系统。
你先不考虑文件系统——你先看看你手里的这块NOR Flash和隔壁部门用的那块NAND Flash有什么区别。
你同事小李做存储卡的,他用的是64GB的TLC NAND。你问他:读写速度多少?他说:顺序读500MB/s,顺序写200MB/s。你又问:你查过坏块表没有?他说:出厂就有2%的坏块,跑着跑着还会新增,反正靠ECC纠错。
你沉默了。你的Flash上,一个坏块就意味着512KB里面少了4KB——0.8%的容量没了,而且你的文件系统在设计时根本没考虑坏块管理这件事。
这就是NOR和NAND的分岔路口。一个走可靠性路线,一个走成本路线。
1970年代末,Intel发明了EPROM。1984年东芝的舛冈富士雄在EPROM基础上发明了Flash——同时提出了NOR和NAND两种可能的连接方式。起初NOR Flash占据了嵌入式市场,因为它跟CPU的接口像SRAM一样简单。1990年代末,三星和东芝大幅推动了NAND Flash的商业化,因为它更适合SSD和存储卡这种大容量场景。
从那时起,两条路越走越远,越走越深。它们的差异,比高速公路和铁路的差异还大。
NOR Flash:每个单元都有自己的门牌号
NOR Flash的架构名称来自布尔逻辑中的“NOR“门。但别被名字吓到——理解它的架构只需要一个直觉:每个浮栅晶体管都直接挂在位线上。
NOR Flash单元阵列——每个单元独立可寻址
============================================
Bit Line 0 Bit Line 1 Bit Line 2
│ │ │
Word Line 0 ──────────────┼──────┬───────┼──────┬───────┼──────
│ ┌─┴─┐ │ ┌─┴─┐ │
└────┤FG ├─────┴────┤FG ├─────┘
│ │ │ │
┌───┤ ├────┬─────┤ ├───┐
Word Line 1 ──────────────┼───┤ │ │ │ │ │
│ └─┬─┘ │ └─┬─┘ │
│ ┌─┴─┐ │ ┌─┴─┐ │
└───┤FG ├────┴─────┤FG ├───┘
│ │ │ │
┌───┤ ├────┬─────┤ ├───┐
Word Line 2 ──────────────┼───┤ │ │ │ │ │
│ └───┘ │ └───┘ │
│ │ │ │ │
Source Lines (按扇区块共用)
选中一个单元:WL0 + BL1 → 交点处那个FG直接导通/截止 → 读出放大
这就是随机访问。不需要读邻居,不需要翻页,直接定位。
关键特征:
-
每个单元的漏极独立接到一条位线上。 你想读哪个单元,字线选中那一行,位线选中那一列,交点那个单元就直接输出信号——不要经过任何其他单元。
-
每个字节都有一个地址。 NOR Flash和CPU之间的接口是地址总线和数据总线。CPU把地址放到总线上,几十纳秒后数据就回来了。这和SRAM的接口是一模一样的。
-
以字节为最小访问单位。 你不需要“翻一页“。你要读第456个字节?地址总线输出0x1C8,70ns后数据回来。就这么简单。
这就是为什么NOR Flash可以做XIP(eXecute In Place)——CPU直接从Flash取指执行,不需要先把代码复制到RAM。在嵌入式系统里,这意味着你可以省掉宝贵的SRAM(Cortex-R5通常只有几百KB到几MB的SRAM)。
NOR Flash与CPU的连接(简化)
=============================
CPU NOR Flash
┌──────────┐ ┌───────────┐
│ │─── Address[18:0]──→│地址解码器 │→ 选字线/位线
│ │ │ │
│ │←── Data[15:0] ────│读出放大器 │← 选中单元输出
│ │ │ │
│ │─── /CE (片选) ────→│ │
│ │─── /OE (输出使能) ─→│ │
│ │─── /WE (写使能) ──→│ │
└──────────┘ └───────────┘
接口像SRAM,不像磁盘。
CPU发地址 → Flash返回数据。不需要"读页"命令。
这不是磁盘的访问模式。这是内存的访问模式。NOR Flash的随机读取延迟是70纳秒级别——和SRAM一个数量级。CPU把地址放到总线上,几十纳秒后指令就回来了,直接送入流水线执行。
这就是为什么NOR Flash可以做 XIP(eXecute In Place,原地执行)——CPU直接从Flash取指执行,不需要先把代码复制到SRAM。在嵌入式系统里,这意味着你可以省掉宝贵的SRAM(Cortex-R5通常只有几百KB到几MB),固件镜像可以直接跑在Flash上。你的汽车ECU、WiFi路由器、智能手表——它们的代码都这样跑着。
也正因为每个字节独立可寻址,早期单片机程序员可以直接在Flash上修改一个字节然后跳过去执行——这在NAND上是完全不可想象的。
NAND Flash:一串糖葫芦上的山楂
NAND Flash的架构完全不同。它不叫“NAND“是因为逻辑门,而是因为它的连接方式让你想到串联——每一列的单元是串联在一起的,像一串糖葫芦。
NAND Flash单元阵列——一串串的串联结构
=========================================
Bit Line 0 Bit Line 1 Bit Line 2
│ │ │
String Select ────┼──────┬──────────┼──────┬──────────┼──────
(SST) │ ┌─┴─┐ │ ┌─┴─┐ │
└────┤FG ├────────┴────┤FG ├────────┘
│ │ │ │
Word Line 0 ────────────┤FG ├─────────────┤FG ├────────────
│ │ │ │
Word Line 1 ────────────┤FG ├─────────────┤FG ├────────────
│ │ │ │
Word Line 2 ────────────┤FG ├─────────────┤FG ├────────────
│ │ │ │
... (一连串32-128个单元串联)
│ │ │ │
Word Line 31 ───────────┤FG ├─────────────┤FG ├────────────
│ │ │ │
Ground Select ─────┼───┤ ├────┼────────┤ ├───┼────
(GST) │ └───┘ │ └───┘ │
│ │ │ │ │
└─────┘ └──────────┘ └─────
Source Line (共地)
一"串"里有32-128个单元。要读其中一个,必须让其他所有单元强制导通,
然后读出放大器感应这个选中的单元是导通还是截止。
NAND Flash的存储单元不是一个个独立的。一列32到128个浮栅晶体管被串联成一个“串(String)“——这大大节省了位线接触孔的数量,让每个单元的面积从NOR的~10F²缩小到~4F²(F是特征尺寸)。这就是NAND比NOR便宜的物理根源。
但便宜有便宜的代价。
要读一串中某一个单元的数值,你必须把其他所有单元强制导通——在它们的控制栅上加一个足够高的电压(叫Vpass,通常是6-8V),让不管它们浮栅里有没有电子,沟道都全部导通。然后,只对被选中的那个单元的控制栅加Vread,看它导通还是不导通。
NAND读取操作
==============
选中单元:该单元CG加Vread (~3V)
其他单元:CG加Vpass (~7V) → 不管它们浮栅状态如何,全部强制导通
读出放大器:敏感总电流 → 判断选中单元是导通(1)还是截止(0)
不是"读一个",是"读一整串"然后只关心其中一个。
所以你不可能在NAND上做随机字节访问。 你必须读一整页(通常是4KB到32KB),然后把你需要的那一个字节从这个页里挑出来。NAND的寻址不是以字节为单位的——是以页(Page) 为单位的。
这就是为什么NAND不能XIP。你要在NAND上执行代码?先把整页数据都读到一块SRAM buffer里,然后CPU从buffer拿指令。这就不是“直接执行“了,这是“先加载后执行“。
NOR vs NAND:一张决定命运的对比表
| 特性 | NOR Flash | NAND Flash |
|---|---|---|
| 读取延迟 | ~70ns(字节随机访问) | ~25µs(页读取,第一字节延迟) |
| 读取粒度 | 1字节 | 1页(4KB-32KB) |
| 最大读取带宽 | ~100-200MB/s(突发模式) | ~500MB/s-4GB/s(顺序读) |
| 写入粒度 | 1字节(需先擦除整扇区) | 1页(需先擦除整块) |
| 编程时间(单个单位) | ~10µs/字节 | ~200-500µs/页 |
| 擦除粒度 | 扇区(4KB-64KB) | 块(128KB-16MB) |
| 擦除时间 | ~100ms/扇区 | ~2ms/块 |
| 擦除速度(折算) | ~0.04MB/s | ~64MB/s |
| 擦写寿命 | ~10万次(SLC) | SLC:10万次, MLC:3000-1万, TLC:500-3000 |
| 坏块率(出厂) | 接近0% | 2%-5%(出厂时就可能有) |
| 坏块管理 | 不需要(嵌入式场景) | 必须(FTL/ECC/Bad Block Table) |
| 最大容量 | ≤2GB | ≤1TB+(单片),堆叠可达16TB+ |
| 每位成本 | 高(~10F²/bit) | 低(~4F²/bit,3D可达更小) |
| 耐温范围 | -40°C ~ +125°C | 0°C ~ +70°C(消费级) |
| 抗辐射 | 好 | 差 |
| ECC要求 | 低(1-bit ECC即可,甚至不需要) | 必须(4-120 bit ECC/每页) |
| XIP支持 | ✓ 支持 | ✗ 不支持(必须加载到RAM) |
| 典型应用 | 汽车ECU、MCU固件、BIOS/UEFI | SSD、U盘、手机存储、SD卡 |
这张表里藏着一个残酷的经济学真相:NOR Flash的单位面积成本是NAND的2.5倍——不是你买这颗芯片贵,而是“做同样的容量,NOR需要2.5倍的硅面积“。
因为NAND的串联结构省掉了大量的接触孔和隔离区。一个NAND单元在硅片上占的面积是~4F²(F是特征尺寸)。而一个NOR单元需要~10F²——因为每个单元的漏极都需要独立的接触孔,每个位线都需要隔离。
面积对比(简化示意图)
NOR单元:大约10F² NAND单元:大约4F²
┌──────────────────┐ ┌──────────────┐
│ ┌────┐ ┌────┐ │ │ ┌──┐ ┌──┐ ┌──┐│
│ │ FG │ │ FG │ │ ← 独立接触孔 │ │FG│ │FG│ │FG││
│ │ │ │ │ │ 隔离区 │ └──┘ └──┘ └──┘│
│ └────┘ └────┘ │ │ ← 串联,省接触孔 │
│ ↑接触 ↑接触 │ └──────────────┘
│ ↓孔 ↓孔 │ 面积 = ~4F²
└──────────────────┘
面积 = ~10F²
F²在这里不是开玩笑的数量级。对于一个16nm工艺(F=16nm),10F²=2560 nm²,4F²=1024 nm²。1平方毫米可以塞入约39万个NOR单元,或者约97万个NAND单元。
这意味着同样一片300mm晶圆,NAND的容量是NOR的2.5倍。而这个倍数,在3D NAND的世界里变成了几十倍甚至几百倍——因为你可以往上堆叠。
NOR的垂直困境 vs NAND的3D腾飞
在很长一段时间里,NOR和NAND都在2D平面工艺上前进——把尺寸做小、把单元塞得更密。但到了16nm左右,NOR Flash遇到了一个物理瓶颈:
浮栅晶体管必须保持那个8nm的隧道氧化层。 这个厚度不能再薄了——更薄意味着更大的泄漏、更短的保持时间、更差的可靠性。而且NOR需要独立接触孔和隔离区,这些结构的尺寸不能无限缩小——缩小了接触电阻变大、隔离失效。
所以NOR Flash在16nm节点后就基本停止了微缩。目前主流嵌入式NOR Flash是45nm-55nm工艺——而且这个工艺对NOR来说已经是“成熟“到不能再成熟了。
NAND这边呢?
NAND不需要每个单元独立可寻址——所以它可以在Z轴方向堆叠。这就是3D NAND:在同一个硅片上垂直堆叠多层存储单元。
3D NAND结构(概念示意)
==========================
Z轴 ↑ 堆叠层数:2013年24层 → 2025年232层+
│
┌────┴────┐ ← 第N层 (最高层)
│ FG FG │
│ FG FG │ ← 第N-1层
│ FG FG │
│ FG FG │ ← 第N-2层
│ FG FG │
│ ... │ ← ... 232层堆叠
│ FG FG │
│ FG FG │ ← 第2层
│ FG FG │
└────┬────┘ ← 第1层 (靠近衬底)
│
外围控制电路 (在衬底上,或放在存储阵列下方——CMOS Under Array)
每一层都是一整面的存储单元阵列。这些层像千层饼一样堆叠在一起,通过垂直通道(Vertical Channel)——一个从顶部贯穿到底部的多晶硅柱——把所有层串起来。一个Vertical Channel穿过232层后,这个“串“就有232个单元串联在一起。
这带来了几个惊人的结果:
-
容量密度的爆炸:232层 × 每层面积不变 = 容量是2D时代的232倍。一块300mm晶圆可以产出1TB以上的NAND Flash。
-
工艺倒退式的进步:3D NAND不需要最先进的EUV光刻。因为每一层的特征尺寸可以放得比较大(20-30nm,甚至更大),靠层数来弥补密度。这反而降低了光刻成本。
-
NOR彻底跟不上:因为NOR需要每个单元独立可寻址的位线——如果也做232层,垂直方向上的位线怎么走?在3D结构里,每一层都需要独立的位线接触吗?物理上几乎不可能。所以NOR Flash至今几乎没有3D版本。
为什么汽车电子选择了NOR
你现在知道为什么你的汽车ECU里使用的是NOR Flash而不是NAND了。
第一,可靠性。 汽车的Flash存储了发动机控制、刹车系统、安全气囊的控制代码。如果存储的数据出错——哪怕只是一个比特——后果可能是灾难性的。NOR Flash出厂几乎零坏块,10万次擦写寿命,不需要复杂的ECC纠错。NAND出厂就有2%-5%坏块,需要强大的ECC(BCH/LDPC)来纠错,擦写寿命在MLC和TLC上只有几千次。
第二,XIP(原地执行)。 汽车ECU的SRAM非常宝贵。Cortex-R5可能只有256KB到1MB的SRAM,而固件镜像可能有2MB到16MB。如果使用NAND,CPU不能直接从NAND取指——你必须先把代码加载到SRAM再执行。这意味着你至少需要和固件一样大的SRAM。而用NOR Flash,CPU直接从Flash取指,SRAM只用来跑栈和堆,省掉一大块成本。
第三,抗辐射和宽温。 汽车引擎舱的工作温度范围从-40°C到+125°C,还伴随着电磁干扰和高能粒子辐射。NOR Flash在这种环境下的数据保持能力远好于NAND——因为它的单元结构更简单、电荷量更大(SLC vs MLC/TLC)。NAND在极端温度和辐射下,电荷泄漏速度会急剧加快。
第四,确定性。 NOR Flash的擦除是扇区级的——你知道擦一个4KB扇区需要大约100ms。NAND的擦除是块级的——你擦一个4MB的块可能需要3ms,但加上坏块管理、磨损均衡、垃圾回收、写放大,实际的延迟抖动很大。对于汽车这种实时系统来说,确定性比平均性能更重要。
汽车ECU使用NOR Flash的典型场景
==================================
ECU上电 → CPU从NOR Flash取Reset Vector → 开始执行固件
(不需要从Flash复制到RAM,直接取指执行)
固件存储区(NOR Flash)
┌─────────────────────────┐
│ Bootloader (64KB) │ ← 一级引导
├─────────────────────────┤
│ Application (2MB) │ ← 主应用程序 (XIP)
├─────────────────────────┤
│ Calibration (256KB) │ ← 校准参数 (读写)
├─────────────────────────┤
│ Log/Data (512KB) │ ← 运行时数据记录
└─────────────────────────┘
全部在NOR Flash上。无须外部RAM加载。
为什么手机和U盘选择了NAND
反过来看,你的手机、你的U盘、你的SSD——它们选NAND的理由同样充分。
第一,便宜。 1GB NOR Flash的成本可能是1GB NAND的2-3倍。一个128GB的手机用NOR?成本翻3倍,手机卖不出去。
第二,密度。 3D NAND可以堆叠200+层,在同样的芯片面积里做出几百倍的容量。NOR做不到。
第三,写入吞吐量。 NAND按页写入(16KB一页),一页只需200微秒——虽慢,但一页数据量大。总体写入带宽远高于NOR的逐字节编程。
第四,应用场景不需要随机字节访问。 手机里的照片、U盘里的文件——从来不需要“随机读第35467字节“。它们总是按块读写。NAND的页访问模型完美匹配这类场景:该读多少读多少,不够再加一页。
第五,软件层掩盖了硬件缺陷。 NAND的坏块、磨损、电荷泄漏——这些“缺陷“都被FTL(Flash Translation Layer) 掩盖了。FTL是一层软件/固件,它维护逻辑页到物理页的映射表、跟踪坏块、做磨损均衡、做垃圾回收。上层的应用和文件系统看到的是一块“完美“的块设备——而不知道下面是一块“千疮百孔“的NAND。
NAND 软硬件堆栈
=================
应用层: 照片、视频、文件
文件系统层: FAT32/exFAT/ext4/f2fs
FTL层: 逻辑块↔物理块映射 ← 掩盖坏块、磨损均衡、GC
闪存控制器层: ECC纠错、坏块管理、命令调度
物理层: TLC NAND Die × N通道 × M芯片
FTL是NAND的“遮羞布“。也是它的“翻译官“。没有FTL,NAND就是一块到处坏块的破布。有了FTL,它就是一块完美的块设备。而FTL本身的实现复杂度,比本文讨论的整个KnotFS文件系统还要大好几倍。
这本书只讲NOR
你面前的这本书,最终只讲了一条路:NOR Flash上的嵌入式文件系统。
不是因为我们觉得NAND不重要。而是因为我们关心的场景——汽车MCU、实时嵌入式系统、资源极度有限的Cortex-R5——和NAND毫无交集。
一个NAND系统的FTL需要几十KB甚至几百KB的SRAM来维护映射表。但我们的MCU总共只有512KB SRAM,FTL的开销就占了10%甚至更多。
一个NAND系统的ECC引擎需要硬件支持(BCH编码器和解码器)。但我们的MCU没有硬件ECC,纯软件CRC32校验已经是我们在计算资源上的极限了。
一个NAND系统的擦除块是128KB到16MB。而我们的Flash总共才512KB——一个擦除块就占25%到3000%。没有磨损均衡的空间,没有垃圾回收的余地。
所以NOR是唯一的路。也正因为是唯一的路,才有我们后面要讲的KnotFS——一个专门为 NOR Flash 设计的嵌入式文件系统,从 64KB 的教学规模开始,理解它如何在真实的车载环境中扩展到完整的 Flash 管理。
你手里那个16MB的NOR Flash,按照它的工艺节点、容量和成本,在消费电子世界里已经算是“博物馆文物“了。但在嵌入式实时系统里,它是标配。你的汽车、你的路由器、你的智能电表、你的无人机飞控——都靠它存储代码和数据。
这是一条大多数人不会走的路。但走这条路的人,造你家汽车的刹车、你头顶上那架飞机的飞控、你手腕上那块手表的固件更新系统。
这是一条窄路。但它的每一条指令、每一个比特,都在保护你的安全。
下集预告
NOR 选定,赛道确定。但你手里的这颗 NOR Flash 到底能存多少个比特?同样是浮栅晶体管,为什么有的芯片标着“SLC“能擦写十万次,有的芯片标着“TLC“几千次就废了?
决定 Flash 寿命和可靠性的,不是 NOR 还是 NAND——而是每个存储单元里塞了几个比特。
下一节,我们走进 SLC/MLC/TLC/QLC 的世界,看看“越多越不靠谱“的物理根源。
悬念留给:一个晶体管存 4 个比特是什么概念?你需要把 3.3V 的电压窗口切成 16 份——每份只有 0.2V。温度高一点,电子漏几个,0 就变成了 1。