4. NAND Flash 时序分析:读、写、擦除与时序优化

时序这东西,说白了就是芯片内部各个操作在时间轴上的“对齐”问题。你驱动写得再花哨,时序对不上,数据就是错的。我早年调一个 ONFI 接口的 NAND,就因为 tREA 差了那么几纳秒,读出来的数据每隔几页就跳一个 bit 的错误,查了整整两天。

这一章我们就把 NAND Flash 最核心的几组时序参数掰开揉碎。读、写、擦除,每个操作都有它的关键路径。理解了这些,你写驱动的时候才知道哪些地方能压,哪些地方必须等。

4.1 读时序参数:tR、tRC、tREA

读操作是 NAND 最频繁的操作。三个参数决定了读性能的上限。

参数 全称 典型值(TLC) 含义
tR Read Time 45~100 μs 从发出读命令到数据准备好,送到页缓存的时间
tRC Read Cycle Time 20~50 ns 连续读取时,相邻两个数据输出之间的最小间隔
tREA Read Enable Access Time 15~35 ns RE# 信号有效到数据在 DQ 上稳定的时间

tR 是读延迟的大头。你发一个 00h-Addr-30h 的读序列,然后就要等 tR。这期间 NAND 内部在做电荷感测和数据搬移。我见过有人在这个等待期里用轮询 R/B# 引脚,其实更高效的做法是发完读命令后立刻切到下一个 die 去操作——这就是后面要讲的 多平面交错 的基础。

tRC 决定了读带宽。假设 tRC = 25 ns,数据位宽 8 bit,那理论峰值就是 1 / 25 ns × 8 = 320 MB/s。但实际跑不到,因为还有命令开销和 tREA 的抖动。

tREA 是个容易踩坑的参数。它定义了 RE# 下降沿到数据在 DQ 上有效的时间。控制器采样数据的时候,必须等 tREA 过了再采样。我见过一个项目,控制器采样点设得太靠前,高温下 tREA 变大了,数据就采错了。后来我们在驱动里加了 采样窗口调整 的逻辑,根据温度动态偏移采样点。

避坑指南: 我曾经在一个 3D NAND 项目里,发现读同一个 LUN 的不同 plane,tR 居然有 ±15% 的差异。后来查手册才知道,plane 离页缓存的距离不同,内部走线延迟不一样。所以驱动里不要对 tR 做死假设,最好用 R/B# 中断或者状态寄存器来确认操作完成。

4.2 写时序参数:tPROG、tWC、tADL

写操作比读复杂得多。因为要注入电荷,所以时间也长得多。

参数 全称 典型值(TLC) 含义
tPROG Program Time 500~2000 μs 从数据写入页缓存到电荷注入完成的时间
tWC Write Cycle Time 20~50 ns 连续写入时,相邻两个数据输入之间的最小间隔
tADL Address to Data Load Time 70~200 ns 地址锁存后,到可以开始加载数据的最小等待时间

tPROG 是写操作的瓶颈。一个 page 的编程要 500 μs 到 2 ms。你想想看,如果一次只写一个 page,那带宽就被锁死了。所以现代 NAND 都支持 多页编程(Multi-Plane Program),一次 tPROG 可以写 2 个或 4 个 page。

tWC 决定了写带宽。和 tRC 类似,tWC 越小,数据灌入越快。但要注意,tWC 是控制器侧的限制,不是 NAND 内部的速度。NAND 内部写缓存的速度其实比 tWC 快,瓶颈在 I/O 接口上。

tADL 是个容易被忽略的参数。它规定了地址锁存(ALE 有效)之后,必须等 tADL 才能开始写数据。我见过有人把地址发完立刻就开始发数据,结果数据没写进正确的页缓存。嗯,这里要注意:tADL 不是 NAND 的“处理时间”,而是地址解码和页缓存选通需要的建立时间。

个人经验: 写驱动的时候,我习惯把 tADL 和 tWC 合并成一个约束:地址发完后,至少等 tADL 再开始发数据;数据发完后,至少等 tWC 再发下一个数据。这样既保证时序,又不会过度浪费等待时间。

4.3 擦除时序参数:tBERS

擦除操作是 NAND 里最慢的操作,没有之一。

参数 全称 典型值(TLC) 含义
tBERS Block Erase Time 3~15 ms 擦除一个 block 所需的时间

tBERS 为什么这么慢?因为擦除是对整个 block 的所有 wordline 施加高压脉冲,把浮栅里的电子拉出来。这个过程需要精确控制电压和时长,快了擦不干净,慢了会过度擦除导致阈值电压漂移。

我遇到过一个问题:某个批次 NAND 的 tBERS 在低温下会翻倍。从 5 ms 变成 10 ms。驱动里如果用了固定超时,低温下就会频繁超时。后来我们在驱动里加了 自适应超时,根据温度查表调整 tBERS 的等待时间。

警告: 擦除操作不可中断。一旦发出擦除命令,必须等 tBERS 完成或者用 R/B# 检测到结束。强行复位或断电可能导致 block 损坏。我曾经在调试时手贱按了复位,结果那个 block 再也没法用了。

4.4 时序优化策略

知道了参数,怎么优化?我总结了几条实战经验。

4.4.1 流水线化:隐藏延迟

tR 和 tPROG 是“等待时间”,tRC 和 tWC 是“传输时间”。优化的核心思路是:让等待时间和传输时间重叠。

  • Cache Read / Cache Program:利用 NAND 内部的页缓存,在读下一个 page 的同时,把上一个 page 的数据从缓存搬出去。
  • 多 Die 交错:一个 die 在 tPROG 的时候,另一个 die 可以接收数据。我做过一个 4-die 交错的方案,写带宽提升了 3.2 倍。
  • 多 Plane 操作:同一个 die 的多个 plane 可以并行编程,tPROG 只花一次。

4.4.2 采样点调优:tREA 的补偿

tREA 会随 PVT(工艺、电压、温度)变化。驱动里可以加一个 训练序列:在初始化时发一段已知数据,然后调整采样点,找到最佳位置。我习惯在驱动初始化时做一次 DQS 训练,把采样点锁定在眼图中央。

4.4.3 命令合并:减少开销

每次发命令都有地址和命令周期开销。如果能合并,就能省下不少时间。

  • 多页读:一次发多个 page 的读命令,然后连续读数据。
  • 多页写:一次发多个 page 的写命令,然后连续写数据。
  • Copy-Back:在 NAND 内部直接搬移数据,不经过控制器。适合 GC(垃圾回收)场景。

4.4.4 时序参数表

下面这张图是我自己整理的时序优化决策树,帮你快速定位瓶颈。

NAND Flash 时序优化决策树 时序优化入口 读操作优化 Cache Read 多Die交错 写操作优化 多Plane编程 Copy-Back 擦除操作优化 自适应超时 后台擦除 优化原则 让等待时间(tR/tPROG/tBERS)与传输时间(tRC/tWC)重叠 采样点调优 + 命令合并 + 流水线化 = 最大化带宽

4.4.5 实际案例:读带宽从 180 MB/s 提升到 280 MB/s

我之前优化过一个 UFS 3.1 的读性能。原始驱动是单 die 顺序读,tR 期间 CPU 空等。我做了三件事:

  1. 启用 Cache Read:让 NAND 在输出数据的同时预读下一个 page。
  2. 多 Die 交错:4 个 die 轮流读,tR 被完全隐藏。
  3. DQS 训练:把采样点从默认的 50% 位置调到 65%,消除了高温下的采样错误。

结果读带宽从 180 MB/s 直接跳到 280 MB/s。嗯,这就是时序优化的价值。

小技巧: 调时序的时候,别只看 datasheet 上的典型值。要关注 min 和 max。驱动里用典型值做性能估算,用 max 值做超时保护。这样既快又稳。