接着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位第2位第3位第4位第5位第6位第7位
写入时,ECC 会给这 7 个 bit 设计几组校验规则。可以先把它想象成有 3 个检查员:
检查员A:检查第1、3、5、7位检查员B:检查第2、3、6、7位检查员C:检查第4、5、6、7位
每个检查员只负责一件事:看自己负责的那几位,是否还满足写入时设定好的校验规则。
为了方便理解,先假设规则是:
每一组里面,1 的个数必须是偶数。
如果数据没有出错,三个检查员重新检查一遍,都会觉得正常。
也就是:
A 不报警B 不报警C 不报警
这就说明:
暂时没有发现矛盾。
这个“报警结果”,就可以理解成综合征。
现在假设 NAND 读取时,第 5 位被读反了。
原来它是 0,结果读出来变成了 1;或者原来它是 1,结果读出来变成了 0。
那会发生什么?
我们看第 5 位被哪些检查员检查。
第5位在 A 这一组里;第5位不在 B 这一组里;第5位在 C 这一组里。
所以第 5 位一旦翻转,受到影响的是 A 和 C。
结果就是:
A 报警B 不报警C 报警
也就是说,报警组合是:
A、C 同时报警,B 没报警
ECC 看到这个组合,就可以反推出:
最可能出错的是第 5 位。
为什么?
因为第 5 位刚好只会影响 A 和 C,不会影响 B。
于是,ECC 只要把第 5 位翻回来,数据就被修正了。
这个例子想说明的不是“真实 BCH 就是这么简单”。真实 NAND 里的 BCH 要复杂得多。因为实际情况里,一个纠错单元里可能不只错 1 个 bit,而是同时错很多 bit。
比如:
第5位错了第19位错了第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 把它救清楚了。
Alipay
Wechat