接着SSD科普十一讲到这里,NAND 内部已经完成了第一次判决。

cell 的阈值电压,经过 Vread 试探;bitline 上那一点微弱的电压变化,经过 sense amp 判断;最后,一串 0 和 1 被锁存在 page buffer 里。

看起来,数据已经读出来了。

但问题是:

这串 0 和 1,不一定全是对的。

因为 sense amp 做的事情,本质上只是一次物理判决。

它判断的是:在当前这个 Vread 下,这个 cell 到底像是导通,还是不像导通。

它并不知道这个 cell 当初真正写进去的是什么。

所以,NAND 第一次读出来的结果,只能叫:

原始读出数据。

它不是最终答案。因此这一章节,简单的介绍下ECC。


1. NAND 真正可怕的,不是一个 bit 会错,而是 bit 太多了

先看一个最基本的概念:

原始误码率。

它的意思很简单:

在没有纠错之前,NAND 原始读出来的数据里,有多少 bit 是错的。

比如读出 10 亿个 bit,里面有 1000 个 bit 错了,那么原始误码率就是:

1000 / 10 亿 = 10^-6。

这个数看起来可能很小。

但 NAND 的问题是:它不是只存几个 bit。它存的是几十亿、几百亿、几千亿个 bit。

所以,只要容量足够大,哪怕单个 bit 出错概率很低,整个芯片、整个 SSD 遇到错误的概率也会被迅速放大。

单 bit 错误率只是底层概率。真正影响产品可靠性的,是这些错误落到一个页、一个纠错单元、一颗芯片之后,会不会超过纠错能力。

这句话很重要:

NAND 不是因为每个 bit 都特别不可靠,才需要 ECC。 而是因为 bit 太多了,小概率错误会被容量放大。


2. ECC 不是事后魔法,而是写入时就提前埋下“校验线索”

很多人容易把 ECC 理解成:读出来错了,后面有个算法帮忙修。这个说法不算错,但太浅了。

ECC 能修,不是因为它有魔法,而是因为写入时就提前做了准备。

SSD 写数据时,不是只把用户数据写进 NAND。它还会根据这批数据,额外生成一部分校验信息,也一起写进去。

也就是说,真正写入 NAND 的并不是单纯的:

用户数据

而是:

用户数据 + 校验信息

这些校验信息,就像给原始数据加上的“数学指纹”。

读回来的时候,ECC 会检查:这批用户数据,和当初一起写进去的校验信息,关系还能不能对上?

如果能对上,说明这串数据看起来合理。 如果对不上,说明里面一定有些 bit 已经变了。

这时 ECC 要做的事情,不是重新去看 cell 里的电荷,也不是重新观察 bitline 电压。

到了 ECC 这一层,底层模拟世界已经结束了。

ECC 面对的,是一串已经被 NAND 判好的 0 和 1。

它要判断的是:

这串 0 和 1,还是不是一个“合法结果”?


3. 什么叫“合法结果”?

这一步是理解 ECC 的关键。

可以先不看公式,用一个简单例子理解。

假设有一套非常粗糙的校验规则:

每 8 个 bit 后面,多放 1 个校验 bit。这个校验 bit 规定:前面 8 个 bit 里,1 的个数必须是偶数。

比如前 8 个 bit 是:

10110010

这里有 4 个 1,是偶数。那校验 bit 就可以写成 0,表示“规则满足”。

读回来以后,如果变成:

10110011

这时前 8 个 bit 里有 5 个 1,规则不满足了。

ECC 一看就知道:这串数据不对劲。

当然,真实 NAND 用的 ECC 比这个复杂得多,不是这么简单的奇偶校验。 但底层思想类似:

写入时加一些冗余规则,读出时检查这些规则是否还能成立;如果不成立,就说明数据被破坏了。

《Inside NAND Flash Memories》第 14 章里讲到的“综合征”,本质上就是这个检查结果。

如果综合征等于 0,说明读出来的数据看起来像一个合法结果。 如果综合征不等于 0,说明读出来的数据里面有错误。

可以把综合征理解成一个报警器:

综合征 = 0:暂时没发现矛盾; 综合征 ≠ 0:这串数据不合法,里面有 bit 可能错了。

但注意:

发现有错,不等于一定能修。

能不能修,要看错误数量有没有超过 ECC 的能力。


4. ECC 怎么“猜”出原来的数据?

这才是最重要的地方。

ECC 不是知道标准答案。它没有穿越回写入现场。

它做的是一件更接近“推断”的事情:

读出来的数据如果不合法,那就在所有可能的合法数据里,找一个最接近当前读出结果的。

也就是说,ECC 问的是:

当前这串 0 和 1,最像是从哪个合法结果被扰动过来的?

这就是为什么《Inside NAND Flash Memories》第 14 章里会讲“最大可能解码”。

它不是说 ECC 能看到真相。它是说,在所有可能的原始数据里,选一个最可能的。

前面 NAND 物理读取是在猜:

这个 cell 的阈值电压,最可能落在哪个区间?

到了 ECC 这里,又是在猜:

这串已经读出来的 0 和 1,最可能是从哪个合法数据变坏过来的?

所以 ECC 不是把“确定答案”拿回来。它是在有限错误范围内,做一次数学上的最可能修正。


5. 为什么 ECC 只能救有限个错误?

这里要讲一个非常关键的概念:

距离。

这个词听起来很数学,其实意思很简单:

两串 bit 之间,有多少个位置不一样。

比如:

101100 101000

只有一个位置不一样,距离就是 1。

再比如:

101100 001001

有三个位置不一样,距离就是 3。

ECC 为什么关心距离?

因为所有合法数据之间,必须拉开足够距离。

如果两个合法结果只差 1 个 bit,那么只要读的时候错 1 个 bit,就可能从一个合法结果变得像另一个合法结果。

这样 ECC 就分不清了。

但如果两个合法结果之间差得很远,比如相差 7 个 bit,那么少量错误不会轻易把一个合法结果伪装成另一个合法结果。

所以,ECC 的本质就是:

通过增加校验信息,让合法数据之间拉开距离。

距离越大,能发现的错误越多,能纠正的错误也越多。

ECC的纠错能力和最小距离有关。如果一个纠错码要能纠正 t 个 bit 错误,那么任意两个合法结果之间,至少要相差 2t + 1 个 bit。

这就解释了为什么 ECC 不是无限强。

如果错误太多,读出来的数据可能已经离原来的合法结果太远,甚至更像另一个合法结果。这时候 ECC 就可能失败。

于是 SSD 只能进入下一步:

重新读取,换参考电压,读重试,甚至动用更复杂的软解码。


6. BCH:早期 NAND 常用的 bit 级纠错

有很多种类的纠错码,其中 BCH 是 NAND 里非常重要的一类。

可以先这样理解 BCH:它是按 bit 来纠错的。

也就是说,它关心的是:

这一串 bit 里面,哪些具体位置被翻错了?

BCH 的解码过程可以概括成三步:

第一步,计算综合征。 先判断读出来的数据有没有矛盾。


举个简单例子:ECC 怎么知道是哪个 bit 错了?

为了理解“综合征”到底是什么,我们先不看 BCH 里复杂的多项式和有限域运算,而是看一个极简的单 bit 纠错例子。

假设现在有 7 个 bit:

  1. 第1位第2位第3位第4位第5位第6位第7位

写入时,ECC 会给这 7 个 bit 设计几组校验规则。可以先把它想象成有 3 个检查员:

  1. 检查员A:检查第1、3、5、7位
  2. 检查员B:检查第2、3、6、7位
  3. 检查员C:检查第4、5、6、7位

每个检查员只负责一件事:看自己负责的那几位,是否还满足写入时设定好的校验规则。

为了方便理解,先假设规则是:

每一组里面,1 的个数必须是偶数。

如果数据没有出错,三个检查员重新检查一遍,都会觉得正常。

也就是:

  1. A 不报警
  2. B 不报警
  3. C 不报警

这就说明:

暂时没有发现矛盾。

这个“报警结果”,就可以理解成综合征。


现在假设 NAND 读取时,第 5 位被读反了。

原来它是 0,结果读出来变成了 1;或者原来它是 1,结果读出来变成了 0。

那会发生什么?

我们看第 5 位被哪些检查员检查。

  1. 第5位在 A 这一组里;
  2. 第5位不在 B 这一组里;
  3. 第5位在 C 这一组里。

所以第 5 位一旦翻转,受到影响的是 A 和 C。

结果就是:

  1. A 报警
  2. B 不报警
  3. C 报警

也就是说,报警组合是:

  1. A、C 同时报警,B 没报警

ECC 看到这个组合,就可以反推出:

最可能出错的是第 5 位。

为什么?

因为第 5 位刚好只会影响 A 和 C,不会影响 B。

于是,ECC 只要把第 5 位翻回来,数据就被修正了。


这个例子想说明的不是“真实 BCH 就是这么简单”。真实 NAND 里的 BCH 要复杂得多。因为实际情况里,一个纠错单元里可能不只错 1 个 bit,而是同时错很多 bit。

比如:

  1. 第5位错了
  2. 第19位错了
  3. 第103位也错了

这时候,报警结果就不会像刚才那样,直接指向一个位置。多个错误会混在一起,留下更复杂的错误痕迹。

所以 BCH 解码才需要三步:

第一步,计算综合征。 也就是先看读出来的数据违反了哪些校验规则。

第二步,根据综合征推导错误位置多项式。 也就是把这些复杂的错误痕迹,整理成一个数学表达式。

第三步,搜索错误位置。 也就是找出到底哪些 bit 最可能被 NAND 读错了。

最后,再把这些位置上的 bit 翻回来。


所以,综合征可以先理解成一组“报警灯”。没有错误时,报警灯全灭。有错误时,某些报警灯会亮。亮灯的组合,就留下了错误的痕迹。

对于最简单的 1bit 错误,这个痕迹可以直接指向出错位置。对于真实 BCH 这种能纠多个 bit 的纠错码,就要通过更复杂的数学方法,把这些痕迹还原成具体错误位置。

这就是 ECC 能“救数据”的原因。

它不是凭空知道原始答案。它是通过写入时留下的校验规则,观察哪些规则被破坏了,再反推出哪些 bit 最可能被读错。

BCH 解码比编码复杂得多。编码相对简单,解码要计算综合征、求错误定位多项式、再找根。

翻成人话就是:

写的时候,加校验信息不算太难;读的时候,要从一堆错误痕迹里反推出错的位置,这才麻烦。

这也符合 SSD 的现实:

读路径比写路径更依赖纠错能力。 越到寿命后期,读数据越像在救火。


7. 为什么后来又需要 LDPC?

如果 BCH 能纠错,那为什么现代 TLC、QLC SSD 又大量使用 LDPC?

原因在于:NAND 越来越不适合只靠“硬判决”。

所谓硬判决,就是 NAND 读完以后,只告诉你:这是 0或者这是 1。没有更多信息。

但在 NAND 里,两个同样被判成 0 的 cell,可靠程度可能完全不同:

一个 cell 的阈值电压离边界很远。它被判成 0,很稳。 另一个 cell 的阈值电压几乎贴着边界。它也被判成 0,但这个 0 很危险。 稍微换一点 Vread,或者温度、电荷、噪声再变一点,它可能就会被判成 1。

硬判决的问题就在这里:它把“很稳的 0”和“很悬的 0”,都压成同一个 0。这等于丢掉了大量有价值的信息。

所以会区分硬判决和软判决。硬判决只使用 0/1;软判决还会使用可靠度信息,例如这个判断有多大把握。

这就是 LDPC 的价值。LDPC 不只是看:是 0 还是 1。

它还希望知道:

这个 0 有多可靠? 这个 1 离边界有多近? 哪些 bit 最可疑? 哪些 bit 基本不用怀疑?

有了这些信息,纠错算法就能把精力集中在最可疑的位置上。

这也是为什么 TLC、QLC 时代,软信息越来越重要。

因为一个 cell 里塞的状态越来越多:TLC 要分 8 个状态;QLC 要分 16 个状态。

状态越多,相邻状态之间的距离越近。 距离越近,越容易出现边界附近的危险 cell。

这时候,只靠一次硬判决,很容易信息不够。 需要多次读取、不同参考电压、更多可靠度信息,才能把数据救回来。

用 LDPC 和 BCH 的比较也说明了这个趋势:LDPC 可以利用软信息,在高密度 NAND 场景下获得更强的纠错表现。


8. 所以,ECC 也是“猜测”的一部分

回到第十一章文章的标题:

NAND 读取的本质:不是读取,而是猜测

现在这个“猜测”就不只是物理层面的了。

它至少有两层。

第一层: 是 NAND 内部的物理猜测。

它用 Vread 去试探 cell 的阈值电压;用 bitline 上那点微弱的变化判断导通与否;最后由 sense amp 把结果拍成 0 或 1。

第二层: 是 ECC 的数学猜测。

它拿到这串 0 和 1 之后,检查它和校验信息之间是否自洽;如果不自洽,就反推哪些 bit 最可能被判错;如果错误数量还在能力范围内,就把它们修回来。

所以,主机最后看到的确定数据,不是 NAND 天然吐出来的确定答案。它是经过两次处理之后得到的结果:

先由物理电路从模拟信号里判出来;再由纠错算法从错误痕迹里救回来。这才是 NAND 读取真正复杂的地方。

你以为 SSD 读数据,是把一串 0 和 1 从 NAND 里拿出来。

但真实情况更像是:

NAND 先给出一个可能有错的初稿; ECC 再拿着校验信息检查这份初稿; 发现哪里不合理,就尝试修正; 修正成功,才交给主机。

所以,SSD 读出来的数据之所以看起来可靠,不是因为 NAND 底层从来不犯错。

恰恰相反:

NAND 底层一直可能犯错。只是 ECC 在背后不断把这些错误压下去。

最终你看到的是一个确定的文件。但这个确定结果背后,是一整套和不确定性搏斗的机制。

不是 NAND 天然清楚。而是 ECC 把它救清楚了。