片内 ECC 与主控 ECC 的责任切分
SPI NAND 内置 ECC、裸 NAND 主控 ECC、eMMC 黑盒 ECC 三种架构下,纠错责任归属、错误可见性与失效归因路径的工程对照 | 编制日期 2026-10-14 | 版本号 Rev 1.0
结论:纠错能力从来不是「有没有」的问题,而是「谁负责、能看到多少、错了怎么算」的问题。 同一片 NAND 阵列,放进 SPI NAND、裸 NAND 加主控、eMMC 三种形态里,纠错责任的归属截然不同: 内置 ECC 把纠错变成一项黑盒服务,主控侧只看到状态位; 主控 ECC 把全部责任连同全部可见性一起交给控制器, spare 区、读阈值、坏块表都在它手里; eMMC 的黑盒 ECC 则连错误计数都一并藏起来,主机只剩寿命字段与超时可看。 选型的第一判据不是接口速率,而是你愿意把多少责任、多少可见性交给器件内部。
报告总结
工程现场大量关于 ECC 的争论,争的其实不是算法,而是责任归属:内置 ECC 的器件出了不可纠正错误,是器件质量问题还是使用方式问题?裸 NAND 方案里纠不过来了,是码强不够还是备用区布局不对?eMMC 报了预 EOL,主机侧还能做什么、该做什么?这些问题没有算法答案,只有边界答案——每一类架构把纠错责任切在哪里、错误信息暴露到哪一层、失效之后沿哪条路径归因。
这份文档把三种架构的边界逐项画出来:介质侧、控制器侧、主机侧各管到什么位置,哪些信息可见、哪些不可见,失效之后先查什么、能查到什么程度。 全文与系列中的《ECC 与 LDPC 纠错引擎》分工明确:那份讲纠错算法本身(码的构造、译码流程、软判决接口),本文只讲责任归属与边界,算法结论只在解释「为什么某件事只有主控能做」时被引用。 凡涉及具体数值(误码率量级、纠错位数、温度点、字段编码),本文只给机制、顺序与条件量级,一律以正式 datasheet 与受控规范为准。
读者路径
前言
NAND 闪存的原始误码率从来不为零,且随磨损、温度与驻留时间持续变化,纠错因此不是可选功能而是数据完整性的前提。但「有纠错」和「纠错责任清晰」是两件事:同一颗器件,纠错引擎放在片内还是放在片外,主机能看到的状态、能做的动作、失效后能查到的线索,完全不同。本文的任务是把这些差异从「感觉」变成「可以逐项核对的清单」。
全文按一条主线展开:责任怎么切(第 1 章)→ 误码率曲线长什么样(第 2 章)→ 三种架构各自的边界(第 3~5 章)→ 横向对照与归因(第 6 章)→ 定量关系与工程实现(第 7~8 章)→ 系统级配合(第 9 章)→ 选型与测试(第 10~11 章)→ 结论与清单(第 12 章)。 文中以串行接口器件(SPI NAND、SD NAND)、并行 NAND(ONFI / Toggle 家族)与 eMMC 为主要对象;涉及接口命令时只写标准命令名与配置顺序,不写具体命令号、寄存器地址、位域位置与选项字节默认值,这些一律以正式规范与 datasheet 为准。文中不涉及任何商业数据。
1. 纠错责任的三个决策层
同一片 NAND 阵列,纠错引擎放在片内还是片外,责任的归属就完全不同。这一章先把「谁纠错、纠什么、纠不动怎么办」三个决策问题立成框架,再算清责任切分不清的代价,最后给出全文的对照地图。
1.1 谁纠错:介质侧、控制器侧、主机侧
把纠错责任按引擎所在的位置分成三层,是理解三种架构最快的入口。介质侧指器件内部的纠错引擎——SPI NAND 的内置 ECC、eMMC 内部的控制器与 ECC 都属于这一层;控制器侧指裸 NAND 方案里由主控 SoC 或独立控制器承担的 ECC;主机侧指驱动、文件系统与应用层的端到端校验,它不碰介质细节,只保证「交出去的数据对」。
三层的区别不只是位置,更是信息权:纠错引擎放在哪里,原始页数据、备用区内容、读阈值就在谁手里。内置 ECC 的器件把这些全部藏在片内,主控只能读到纠正后的数据和几个状态位;裸 NAND 方案里主控握着全部原始比特,可以调阈值、可以重读、可以自己定义 spare 区布局。责任与可见性始终绑定在一起——这是后面所有对照的出发点。
| 层级 | 纠错引擎位置 | 直接可见的信息 | 典型动作 | 做不到的事 |
|---|---|---|---|---|
| 介质侧 | 器件内部(SPI NAND / eMMC) | 纠错状态位、坏块标记、寿命字段(eMMC) | 按固定策略纠正页内错误 | 看不到错误数量级与位置分布 |
| 控制器侧 | 主控 SoC 或独立控制器(配裸 NAND) | 原始页、备用区全部比特、读阈值 | 重读、阈值扫描、搬迁、标记、刷新 | 不负责端到端业务一致性 |
| 主机侧 | 驱动 / 文件系统 / 应用 | 读回的数据与返回码 | 重试、告警、降级、切换通路 | 看不到介质内部任何状态 |
三层之间用「可见性」结算:介质侧向控制器侧承诺「按固定强度纠正页内错误并给出状态位」,控制器侧向主机侧承诺「交出的数据已按约定强度纠正,失效时给出错误码」。任何一层悄悄扩大或缩小自己的承诺——比如主控绕开内置 ECC 自己再套一层校验,或者主机把内部超时当成普通读失败重试——故障就会以另一层的症状出现,第 1.4 节与第 10.2 节给出这类事故的典型形态。
1.2 纠什么:读误码、写干扰、保持失效
「误码」在工程现场是一个被混用的词。按发生时机与主导机制,至少有三类完全不同的错误,它们的责任层和处置手段都不一样:
- 读误码:读出瞬间发生的错误。来源包括读出噪声、电压偏移、感测放大器失调、亚稳态与诱发射电。特征是随机、可重读、由持有 ECC 引擎的一层负责纠正。
- 写干扰:编程或擦除当时埋下的错误。编程电压拖尾、邻近单元扰动、擦除残留都会让写入时就接近阈值的单元在后续读出时出错。责任在写入口——内置 ECC 器件在片内,裸 NAND 在主控。
- 保持失效:写入后任意时刻因电荷泄漏与陷阱释放导致的错误。与时间、温度强相关,出厂时完全正常,数月后才暴露,是最难在测试阶段拦截的一类。
| 错误类型 | 发生时机 | 主导机制 | 首要责任层 | 首选处置 |
|---|---|---|---|---|
| 读误码 | 读出瞬间 | 读出噪声、电压偏移、软错误 | 持有 ECC 引擎的一层 | ECC 纠正,超出则重读或调阈值 |
| 写干扰 | 编程 / 擦除当时及之后 | 邻近单元扰动、编程拖尾、擦除残留 | 写入口(片内或主控) | 写验证、坏块隔离、重写 |
| 保持失效 | 写入后任意时刻 | 电荷泄漏、陷阱释放、温度加速 | 介质与系统策略共同承担 | 刷新、搬迁、缩短保持预期 |
保持失效的单元读一次错一次,重读无效——错误不是瞬态的,而是存储状态已经漂移。现场若按「读误码」处理,表现就是不断重读、不断报错、延迟劣化,而数据始终救不回来。正确路径是先判类型(第 11.3 节的数据收集方法),保持失效只能靠刷新、搬迁或提前退役。注意量级判断:保持失效往往伴随整页同时错,这与随机读误码的分散形态完全不同。
1.3 纠不动怎么办:上报、重读、搬迁、标记
纠错能力总有边界。错误数超过引擎能力时,系统有且只有四种处置动作,而每种动作能否执行,取决于责任切在哪一层:
- 上报:把「我纠不动了」告诉上一层。内置 ECC 器件靠状态位,主控 ECC 靠错误码与计数,主机侧只能看到最终读失败。上报是成本最低的动作,但前提是上层有人接、有人记。
- 重读:对以软错误为主的场景有效,代价是延迟成倍增加。主控 ECC 还能在重读时调整读阈值(第 8.2 节);内置 ECC 器件通常没有这个手段。
- 搬迁:把数据从问题块搬到健康块,之后老块退役。需要映射能力与空闲块,裸 NAND 主控天然具备;内置 ECC 器件需要主控在块粒度自己做。
- 标记:把坏块登记在案,后续不再分配。三种架构都需要,但标记位的归属与初始坏块表的提供方不同(第 3.3 节、第 8.5 节)。
| 处置 | 前提条件 | 代价 | 适用架构与责任层 |
|---|---|---|---|
| 上报 | 状态字段或错误计数可读 | 几乎为零,但要求上层有人接、有人记 | 三种架构都应有;内置 ECC 只到状态位粒度 |
| 重读 | 错误以软错误为主,且有重试时间预算 | 延迟成倍增加,总线占用上升 | 主控 ECC 最有效(可调阈值);内置 ECC 只能原样重读 |
| 搬迁 | 有空闲块与逻辑到物理的映射能力 | 写放大、延迟尖峰、掉电窗口 | 裸 NAND 主控负责;内置 ECC 器件由主控在块级做 |
| 标记 | 有坏块标记位与登记表 | 容量损失、表维护成本 | 三种架构都需要,标记位归属各不相同 |
四种动作里,重读和搬迁是「强处置」——它们需要对介质的直接控制权。一个架构如果把这两件事藏在器件内部,主机侧就只剩「上报」可用,现场问题会全部堆积成「读失败 but 不知道为什么」。这就是第 6.2 节可见性分级要定量回答的问题。
1.4 责任切分不清的代价
责任切分不清很少以「设计缺陷」的形式出现,它通常以重复劳动、互相覆盖、归因困难的形式潜伏在系统里,直到现场失效才被结算。下面五类是现场最高发的形态:
- 双 ECC 叠加:器件内置 ECC 已经纠过一轮,主控再按裸 NAND 的格式套一层自己的校验。结果是冗余双倍付出,而两层的能力都不清楚,任何一层失败都会把另一层拖进误判。
- 坏块管理重叠:器件内部按自己的策略标记坏块,主控又维护一份坏块表,两份表对同一块的结论不一致时,数据落在「双方都认为对方管」的缝里。
- 寿命字段误读:把 eMMC 的预 EOL 提示当成「即将全坏」直接整片停用,或者反过来当成普通告警完全忽略——两种极端都源于不知道这个字段承诺了什么。
- 开关时机错误:内置 ECC 的开关在器件生命周期中间被切换,用旧设置写入的数据按新设置读出,页内布局与纠正行为对不上(第 3.1 节)。
- 原始访问冲突:主控为做检测或修复去读原始页,踩到内置 ECC 的输出格式,把「纠正后的数据加状态位」当成裸数据解析。
某方案用 SPI NAND 存储文件系统,为「提升可靠性」让主控对每次回读再做一层校验与重试。初期一切正常;进入磨损中期后,出现小概率整页读失败且重试无效。归因时主控日志显示「校验不通过」,器件状态显示「ECC 失败」,两边都认为自己尽责了。根因是:内置 ECC 强度固定,磨损中期误码率越过其能力线后,主控那一层校验只能发现错误、不能纠正错误——双 ECC 并没有把能力叠起来,只是把「谁负责」搞混了。正确做法见第 10.4 节的双 ECC 争议分析。
1.5 本文件的对照框架
全文用一个三维框架组织:架构维度(SPI NAND 内置 ECC / 裸 NAND 主控 ECC / eMMC 黑盒 ECC)、责任维度(介质侧 / 控制器侧 / 主机侧)、时间维度(设计期定边界、量产期验边界、现场期用边界)。每个章节回答框架里的一个格子,附录则把结论抽成可核对的表。
| 想回答的问题 | 直接读哪里 |
|---|---|
| 三种架构各管到哪、边界画在哪 | 第 3、4、5 章 + 附录 B 速查 |
| 错误能看到多少、归因能查多深 | 6.2 可见性分级 + 6.3 归因路径 + 图 7 |
| ECC 强度要多大、余量留多少 | 第 2 章 + 第 7 章 + 附录 C 估算方法 |
| 主控侧机制怎么落地 | 第 4 章 + 第 8 章 |
| 内置 ECC 器件怎么配合 | 第 3 章 + 第 9 章 |
| 选型怎么定、事故怎么防 | 第 10 章 + 图 13 决策树 |
| 测试怎么设计、数据怎么收 | 第 11 章 + 附录 C |
阅读时建议始终带着一个问题:「这一节说的这件事,在另外两种架构下是谁做?」对照着读,比单读一种架构更能看出责任切分的本质——很多机制不是技术问题,只是「它得有个主人」。
2. NAND 失效率的物理曲线
责任切分的所有讨论都落在一条约束上:介质会以多快的速度变坏。这一章不讲纠错算法,只讲误码率的物理来源与三条主曲线——它们是第 7 章定量关系的输入,也是判断「内置 ECC 够不够」的唯一依据。
2.1 原始误码率的来源
原始误码率(BER)描述纠错之前信道直接输出的错误比特占比。它不是一个数,而是一组随条件变化的曲线。按成因可分三类:
- 制造残留:薄膜沉积与刻蚀的工艺波动留下固定坏点、阈值分布拖尾。这部分在出厂时已存在,表现为初始坏块与「读十次错在同一处」的硬错误。
- 运行扰动:读写操作本身引入的扰动——读干扰、写干扰、衬底噪声、感测放大器失调、电源弹导致的分组错误。这部分随使用强度增长。
- 时间退化:电荷泄漏与陷阱释放让存储状态随时间漂移,温度越高越快。这部分与用不用无关,只与写了多久、存放在多热的环境有关。
BER 不是常数(随磨损、温度、读次数变化);不是单一值(随机错误与突发错误的成分随阶段变化);不是可直接测量的量(只能通过大量读出的统计估计,且估计值依赖样本量与条件)。规格书里给出的是特定条件下的典型值或上界,设计必须按寿命末期的最坏情况取,具体数值与测试条件一律以正式 datasheet 为准。
这条物理曲线决定了责任切分的一个硬约束:错误率随时间单向右移。任何把纠错能力固定死的设计(内置 ECC 的强度、eMMC 内部的码),都在与这条曲线对赌——赌器件在承诺的寿命与保持期内,曲线不会越过能力线。
2.2 擦写次数与 BER 的关系
每次编程 / 擦除循环都会在隧穿氧化层里留下少量陷阱电荷,并让界面态缓慢增加。宏观表现是单元阈值分布整体变宽、拖尾变长:分布越宽,同样噪声下越过判决阈值的比特越多;拖尾越长,出现「坏得离谱」的单元的密度越高。BER 因此随擦写次数单调上升,且在末期常出现膝点——之前平缓、之后急剧恶化。
| 阶段 | 分布特征 | BER 量级(相对描述) | 设计含义 |
|---|---|---|---|
| 初期 | 窄、远离阈值 | 最低,通常远低于任何 ECC 能力线 | 规格书典型值的测试条件 |
| 中期 | 开始变宽,拖尾出现 | 中等,接近弱档 ECC 的能力线 | 软判决与重试开始体现价值 |
| 后期 | 膝点之后急剧变宽 | 高,逼近内置 ECC 的能力线 | 必须按此阶段设计,不用初期值 |
| 末期 | 拖尾压过阈值 | 最高,可能超过固定强度的 ECC | 靠预留块、刷新、提前退役兜底 |
注意两个常见混淆:耐久规格(可承受的 P/E 次数)与 BER 规格是两回事——前者说「器件还能用多久」,后者说「每个阶段错多少」。一个器件可以在远未达到耐久上限时,BER 就已经越过了内置 ECC 的能力线;反之亦然。两者都要看,且都要看测试条件(温度、页、块位置、写入模式),量级比较务必以 datasheet 与实测为准。
2.3 保持时间与 BER 的关系
写入之后,存储电荷会缓慢泄漏,陷阱电荷会重新分布。宏观表现是阈值分布向擦除态方向收缩:原本相距很远的两个分布逐渐靠近,判决裕量被侵蚀,BER 随时间上升。温度是这条曲线最强的加速因子——电荷泄漏与陷阱释放都是热激活过程,温度每升高一档,等效劣化速度成倍加快(工程上按阿伦尼乌斯模型折算加速因子,具体以规范与实测为准)。
与磨损不同,保持失效与使用强度无关——一块只写过一次、几乎没读过的数据,在高温环境存放数月后同样会出错。这意味着它无法用「量产时的全检」拦截,只能在设计期用加速老化评估(第 11.2 节),在运行期用刷新与搬迁缓解(第 9.4 节)。eMMC 的预 EOL 与 SPI NAND 的状态位都不承诺覆盖保持失效,这是第 5 章黑盒可见性讨论的关键背景。
保持曲线的工程形态有三点值得记住:初期快、后期慢(写入后头几个月劣化最快);高温存放远比常温存放凶险(车规与工业场景必须按最高工作温度加存放温度评估);与磨损可叠加——磨损末期的块,保持余量也被消耗殆尽,两条曲线同向而行(第 7.2 节)。
2.4 读干扰的叠加效应
读操作并不是完全无害的。对 NAND 单元的每次读取都会在字线或位线上引入微弱的扰动,单次影响可以忽略,但对同一块的反复读取会累积,形成读干扰(read disturb)。它的工程特征是:错误集中在被反复读取的块上,尤其是只读不写的数据块——固件若没有读干扰预算,热点块会在没有任何写操作的情况下慢慢变坏。
读干扰的特殊性在于它是三种错误源里唯一「因防护而产生」的:为了发现保持失效而做的定期巡检读、为了纠错而做的重读、为了校验而做的回读,都在消耗这个预算。这也是第 9.4 节要专门讨论「巡检节奏」的原因——防过度的保护和保护不足同样危险。
三条曲线不会商量着来,它们各自独立地把 BER 往上推。工程近似上,总误码率可按各来源贡献之和估算(同页内错误则更接近泊松叠加),最坏情况是「磨损末期 + 高温存放 + 热点块反复读」同时发生。第 7.2 节给出可操作的叠加模型,第 7.3 节给出余量准则。
2.5 从 BER 曲线到 ECC 强度需求
把物理曲线翻译成设计输入,需要五步。核心原则只有一条:用末期最坏值,不用典型值。用初期误码率做设计、在末期才发现纠不过来,是这条线上最常见的事故(第 10.2 节有复盘)。
| 步骤 | 输入 | 输出 | 注意 |
|---|---|---|---|
| 1 定最坏 BER | 末期 + 最高温 + 最大读次数叠加 | BER_worst | 不要用典型值;条件要写清 |
| 2 定码字划分 | 页大小与每码字字节数 | 每码字比特数 N | 划分越细,突发错误越难纠 |
| 3 算期望错误数 | BER_worst 乘 N | 每码字错误数均值 | 按泊松近似估尾部概率 |
| 4 加工程余量 | 寿命分散、样本外情形 | 所需纠正能力 t | 余量准则见 7.3 |
| 5 核对冗余预算 | t 与校验位开销的对应 | 校验位占比 | 内置 ECC 无法改,只能换器件 |
第 5 步是责任切分的分水岭:裸 NAND 方案的 t 由主控设计者自己定,不够就加码强、加重读、上 LDPC;内置 ECC 器件的 t 在芯片设计时已固定,系统侧只剩「换器件、降使用强度、提前退役」三个选项。第 3.4 节与第 7.1 节分别从边界和定量两个角度回到这一点。
三条曲线(磨损、保持、读干扰)是第 7 章定量关系的输入:7.1 用它们推所需纠错能力,7.2 给出叠加模型,7.3 定余量准则。附录 C 把 BER 测量与强度估算整理成可执行的步骤,第 11 章给出对应的验证实验设计。
3. SPI NAND 的内置 ECC:黑盒边界
串行接口的 NAND 器件把纠错引擎做进了芯片里,对主控而言这是一个「只给结果、不给过程」的黑盒。这一章把这条边界逐项画出来:开关在哪里切、主控能看见什么、坏块标记怎么与 ECC 失败叠加、固定强度意味着什么,以及内置 ECC 在哪些场景里反而成了负担。
3.1 内置 ECC 的开启与关闭
内置 ECC 不是一个外加模块,而是器件的一种工作模式。它在芯片内部把页数据切分成若干码字,为每个码字生成校验位并写入页内的保留区域;读出时按同样的划分做译码,把结果通过状态位告知主控。开启与关闭通过器件内部的配置位完成——具体是哪一个配置寄存器、哪一位、出厂默认值是什么,一律以正式 datasheet 为准,本文只讲机制与切换时机。
理解开关的关键在于:开启内置 ECC 会改变页内的字节布局。页内原本全部可用于存放用户数据的区域,在开启后要让出一部分给校验位;关闭后这部分区域又变回普通数据区。这意味着「用开启状态写入的数据」与「用关闭状态读出的数据」在字节语义上互不兼容。因此开关的切换只能发生在生命周期的边界上——上电初始化的最早阶段、整片格式化或全片擦除之后——而不能在已有数据的中途切换。
| 维度 | 内置 ECC 开启 | 内置 ECC 关闭 |
|---|---|---|
| 页内布局 | 一部分字节划作校验区,用户可用字节数相应减少 | 整页全部按用户数据解读 |
| 纠错责任 | 页内错误由器件纠正,主控只读结果 | 全部责任回到主控,需要自建校验机制 |
| 状态输出 | 状态位给出纠正 / 未纠正的判定结果 | 通常只反映操作本身是否完成 |
| 主控负担 | 低,按状态位做重试与坏块处理即可 | 高,需要完全按裸 NAND 的方式管理 |
| 典型用途 | 量产产品的正常工作模式 | 原始特性测试、出厂扫描、故障取证 |
| 主要风险 | 强度固定,磨损后期可能越过能力线 | 主控若无配套机制,数据完整性无保障 |
典型形态是:引导阶段用关闭模式做了坏块扫描并写入了初始表,进入正常工作后又切回开启模式,或者反过来。这类切换在功能测试时往往不暴露——因为测试用的是刚擦写过的、误码极低的块;等运行一段时间、误码率上升之后,读出来的数据与写入时的解读方式对不上,表现是「莫名其妙的校验失败」。规范做法是:把开关状态作为器件初始化序列的一步固定下来,并在整个生命周期内不再改动;确需切换,必须先做全片擦除。
另一个容易忽略的点是关闭状态并不等于「没有 ECC 能力」,而是「纠错责任交回主控」。此时页内的校验区变成普通数据,主控若不做任何校验就把数据写进去,等于把可靠性完全押在介质的原始误码率上。因此关闭模式只应出现在测试、取证、以及主控自带完整 ECC 与 spare 管理机制的特定设计中。
3.2 主控能看到什么、看不到什么
内置 ECC 器件与主控之间的全部信息交换,只有四类:命令、地址、数据、状态位。这条通道的宽度决定了主控视野的宽度。把它拆开看,可见性分成三档。
- 完全可见:纠正后的页数据、ECC 结果状态位(通常区分「无错」「已纠正」「不可纠正」三档)、坏块标记、器件标识与配置状态。这些是主控一切策略的输入。
- 粗粒度可见:「这一页纠正过没有」「这一页失败没有」。部分器件会给出纠错量级的粗略提示(是否超过某个档位),是否提供、分几档、如何编码,以正式 datasheet 为准。
- 完全不可见:原始页比特、错误的确切数量与位置分布、内部使用的读阈值、内部采用的码型与译码策略、内部的替换与重映射记录。
| 信息项 | 可见性 | 对主控的价值 | 缺失后的后果 |
|---|---|---|---|
| 纠正后的页数据 | 完全可见 | 业务数据的唯一来源 | —— |
| ECC 是否纠正过 | 可见(状态位) | 判断块是否进入磨损中期 | 错过提前搬迁的窗口 |
| ECC 是否失败 | 可见(状态位) | 触发重读、搬迁、标记 | —— |
| 错误的具体数量 | 不可见(或仅档位) | 量化健康度、做趋势预测 | 只能用「有无」代替「多少」 |
| 错误的位置分布 | 不可见 | 区分随机错误与局部缺陷 | 无法判断是磨损还是缺陷 |
| 内部读阈值 | 不可见 | 自主调整读电压 | 重读只能原样重复 |
| 内部码型与强度 | 不可见 | 核算能力余量 | 只能靠实测反推 |
| 内部替换记录 | 不可见(部分器件) | 区分真实块与替换块 | 容量与寿命估算失真 |
如果状态位只给「成功 / 失败」两档,主控的动作集就只有「重试」和「标记坏块」;如果给了「无错 / 已纠正 / 不可纠正」三档,主控就能把「已纠正」当作早期预警,在真正失败之前做数据搬迁。这个差别不是精度问题,而是策略空间的差别——第 9.5 节的健康自查手段,完全建立在这一档信息之上。
把图 3 与第 1 章的三层框架对照可以看出:内置 ECC 器件把介质侧与控制器侧合并进了同一颗芯片,主控拿到的其实是「主机侧」的视角——只有数据与状态位,没有过程信息。这正是后面所有「内置 ECC 器件上策略做不深」的根源。
3.3 坏块标记与 ECC 失败的叠加
坏块与 ECC 是两条独立的判定线,但在实际使用中它们会叠加出现:坏块标记是「这一块被登记为不可用」的静态结论,ECC 状态是「这一页这次读得怎么样」的动态结果。一个块被标记,通常是因为它反复出现 ECC 失败;但 ECC 失败一次并不必然要立刻标记坏块。两者之间的判定顺序需要主控自己定义。
出厂坏块与使用中出现的新增坏块也要分开处理:出厂坏块由厂商在指定位置做标记,主控在首次上电时扫描并建立坏块表;新增坏块则由主控在运行期判定并追加标记。两者共用同一套标记位,但来源不同、可信度不同、处置节奏也不同——出厂标记通常不可撤销,运行期标记则可以设定确认阈值。
| 坏块标记状态 | 本次 ECC 结果 | 判定 | 推荐处置 |
|---|---|---|---|
| 未标记 | 无错 | 健康 | 正常读写,无需动作 |
| 未标记 | 已纠正 | 磨损信号 | 记录次数,达到阈值后做搬迁 |
| 未标记 | 不可纠正 | 疑似新增坏块 | 换阈值重读;仍失败则搬迁数据并标记 |
| 未标记 | 连续多页不可纠正 | 块级失效 | 整块标记,数据迁入备用块 |
| 已标记 | (不再访问) | 已退役 | 保持标记,不参与分配 |
| 已标记但被误擦 | 任意 | 标记丢失 | 靠坏块表与出厂信息双源核对 |
状态位说「已纠正」,主控就认为数据没问题——这在单次读的意义上是对的,在系统意义上却漏掉了最重要的信息:这一页的错误数已经逼近了固定强度的上限。再磨损一点、再放久一点、温度再高一点,下一次读就是不可纠正。而内置 ECC 器件通常不给错误数量,主控只能靠「纠正发生的频次」间接推断。工程做法是把「已纠正」当成计数事件:同一块在滑动窗口内触发次数超阈值就搬迁,而不去等真正的失败出现(第 9.5 节给出具体的自查节奏)。
还有一类叠加容易被忽视:擦除操作本身可能破坏坏块标记。标记通常写在块内指定页的保留区域,一旦整块被擦除而主控的坏块表没有同步更新,这个块就会以「健康」身份重新进入分配池。规范做法是坏块信息双源保存——既依赖器件内的标记位,也在主控侧维护一份持久化的坏块表,两边不一致时以主控表为准并重新写回标记。
3.4 内置 ECC 的强度边界
内置 ECC 的强度在芯片设计阶段就已经固定,系统侧没有任何调节手段。规格书通常以「每若干字节可纠正若干位」的形式给出,并配套说明该强度对应的耐久次数、数据保持期与工作温度范围的组合条件,具体数值一律以正式 datasheet 为准。理解这条固定的能力线,有三条推论值得记住。
- 能力线水平,误码率曲线单向上行,二者必然相交。内置 ECC 不是「会不会失效」的问题,而是「什么时候失效」的问题。设计的全部工作,是把这个交点推到产品寿命之外。
- 系统侧只能通过降低使用强度来推迟交点:减少不必要的擦写、控制工作温度、避免热点块反复读、按保持期做刷新。这些都改变曲线的斜率或起点,但不改变能力线的高度。
- 余量只能在选型阶段预留,不能在运行期补足。裸 NAND 方案里纠不过来可以加码强、上 LDPC、调阈值;内置 ECC 器件里纠不过来,系统侧只剩「换器件、降使用强度、提前退役」三个选项。
| 推论 | 工程含义 | 系统侧可做的事 | 系统侧做不到的事 |
|---|---|---|---|
| 能力线固定 | 失效是时间问题,不是有无问题 | 把交点推到寿命之外 | 提高能力线本身 |
| 曲线单向上行 | 末期的误码率一定高于初期 | 按末期最坏值设计 | 让误码率回落 |
| 余量只能预留 | 选型时就要定强度档 | 降 P/E、控温、刷新、搬迁 | 运行期动态加码 |
| 使用强度可调节 | 曲线斜率由使用方式决定 | 磨损均衡、读写整形 | 改变固定强度的码 |
完整的说法必须是:在某某擦写次数、某某工作温度、某某保持期、某某读强度的组合条件下,该器件的内置 ECC 强度留有足够余量。脱离条件谈「够用」没有意义——同样的器件,放在常温下记录日志与放在高温环境里存固件镜像,结论可以完全相反。第 7 章给出把条件翻译成强度需求的方法,附录 C 给出 BER 测量与强度估算的可执行步骤。
判断余量是否足够,工程上有一条实用准则:用寿命末期、最高工作温度、最大读次数叠加后的最坏误码率,去核对规格书给出的能力线,并要求两者之间留有明确的工程余量。余量取多大取决于数据的重要程度与可恢复性——可重新下载的内容与不可再生的现场数据,不该用同一档余量。第 7.3 节给出余量准则的定量表述。
3.5 什么时候内置 ECC 反成负担
内置 ECC 在大多数场景里是净收益——它用极小的主控代价换来了可用的数据完整性。但也有若干场景,它会从「帮忙」变成「负担」。识别这些场景,是选型阶段就要做的事。
| 负担类型 | 表现 | 根因 | 规避方式 |
|---|---|---|---|
| 容量代价 | 用户可用字节数小于标称页容量 | 校验位占用页内区域 | 按可用容量而非标称容量做容量规划 |
| 重复付出 | 主控又套一层自己的校验与重试 | 两层责任未分清 | 明确单层负责;端到端校验只对关键数据做 |
| 误判风险 | 把「已纠正」当成「没问题」 | 状态位粒度不足 | 把纠正次数当计数事件,设阈值搬迁 |
| 管理冲突 | 器件内部与主控的坏块表结论不一致 | 两套坏块管理叠加 | 约定单一权威来源,另一方只读 |
| 能力不可调 | 场景变化后无法补强 | 强度在芯片设计期固定 | 选型时按最坏场景留余量 |
| 取证困难 | 现场失效说不清是器件还是用法问题 | 过程信息不可见 | 提前约定状态位记录与日志字段 |
最适合:主控资源紧张(没有硬件 ECC 引擎、算力与内存有限)、数据可恢复或可再生、工作温度与保持期要求温和、产品生命周期内擦写强度可控、团队没有 spare 区与坏块管理的积累。最不适合:高温或长保持期要求严苛、数据不可再生且责任重大、需要在运行期按健康度动态调节策略、主控侧已经有成熟 ECC 与闪存管理能力却还要再加一层。这几类场景的选型判据在第 10.1 节展开,决策树见图 13。
值得强调的一点是:负担大多不是内置 ECC 本身带来的,而是「边界没说清」带来的。同样的器件,在系统里明确了「器件只负责页内纠错并给出状态位,主控负责块级策略与端到端校验」之后,大部分所谓的负担都会消失。这也是第 6.5 节要把责任边界写进规格确认清单的原因。
4. 裸 NAND 的主控 ECC:全责模式
并行接口裸 NAND 与主控 SoC 的组合,把纠错责任连同全部可见性一起交给了控制器:原始页、备用区、读阈值、坏块表都在它手里。这一章讲这套机制怎么组织——备用区怎么划分、BCH 与 LDPC 怎么选、读改写与阈值扫描怎么走、错误怎么上报,以及全责模式究竟要付出哪些设计负担。
4.1 OOB 与 spare 区的布局与责任
裸 NAND 的每个页除了主数据区,还带一段备用区(并行接口家族常称 OOB / spare,串行家族常称 spare)。这段区域不由器件解释,完全由主控定义——这是全责模式与内置 ECC 最本质的区别:内置 ECC 器件里校验区归器件管,裸 NAND 里备用区归主控管。
一段典型的备用区布局会同时承载四类内容:ECC 校验位(按码字切分,每个码字一段)、坏块标记(通常在块内指定页的指定位置)、映射与状态元数据(逻辑块号、页序号、写序号、状态标记)、保留与对齐区域。各段的先后次序、长度与校验范围,都是主控固件的设计决策,一旦量产就不能随意更改——改布局等价于让老数据无法解读。
| 备用区内容 | 由谁写入 | 由谁读取 | 出错后的后果 | 设计注意 |
|---|---|---|---|---|
| ECC 校验位 | 主控 ECC 引擎(写入时) | 主控 ECC 引擎(读出时) | 该码字无法纠正 | 校验范围要覆盖元数据 |
| 坏块标记 | 主控固件 / 厂商(出厂) | 上电扫描与分配逻辑 | 坏块被当健康块使用 | 出厂标记与运行标记分开记 |
| 逻辑块号与页序号 | 写入路径 | 重建映射、掉电恢复 | 掉电后映射错乱 | 必须与数据原子更新 |
| 写序号 / 版本号 | 写入路径 | 挑选最新版本 | 旧版本被误当成最新 | 序号回绕要处理 |
| 状态标记 | 状态迁移逻辑 | 读路径与回收逻辑 | 页状态判定错误 | 状态机要覆盖掉电中断 |
| 保留区域 | —— | —— | —— | 预留少量余量便于后续扩展 |
很多设计只把 ECC 校验范围限定在主数据区,元数据裸放在备用区里。这样做的代价是:元数据出错时没有任何保护,而元数据(逻辑块号、写序号)出错的后果往往比一页数据出错更严重——它会导致映射错乱,让一批数据一起失联。把元数据纳入 ECC 校验范围,只增加很少的校验位开销,却能显著改善掉电恢复的健壮性。
图 4 的数据流里有一个容易忽略的细节:备用区与数据区是同一条通道上的两段。主控在一次页编程里把两者一起写给器件,在一次页读里把两者一起读回。这意味着备用区的可靠性与主数据区处于同一水平——它并没有因为是「元数据」就更可靠,反而因为要承载掉电恢复的关键信息,需要额外的保护手段(第 8.3 节)。
4.2 BCH 与 LDPC 的硬件化选择
裸 NAND 方案的纠错码主要有两个家族:BCH 与 LDPC。二者都能硬件化实现,但能力、代价与适用场景差别明显。算法本身的构造与译码流程属于《ECC 与 LDPC 纠错引擎》的范围,本节只从责任归属与工程取舍的角度做对照。
- BCH:代数译码,硬判决为主,译码延迟确定、电路规模可控、纠正能力在设计时按档位固定。适合误码率中等、对延迟确定性要求高的场景。它的责任边界很清晰——能纠多少位就是多少位,超了就是超了。
- LDPC:迭代译码,可用硬判决也可引入软判决,在同样的冗余开销下纠错能力更强,尤其适合磨损末期与长保持期的高误码场景。代价是译码延迟不确定(迭代次数随错误数变化)、电路与功耗更大、软判决需要器件提供多档读电压的支持。
| 对照维度 | BCH(硬判决) | LDPC(硬 / 软判决) |
|---|---|---|
| 纠错能力 | 按设计档位固定,提升靠增加校验位 | 同冗余下更强,尤其在高误码区 |
| 译码延迟 | 确定,与错误数基本无关 | 随迭代次数变化,最坏值需单独评估 |
| 电路与功耗 | 规模小、功耗低 | 规模与功耗显著更高 |
| 对器件的依赖 | 只要求标准读操作 | 软判决需要器件支持多档读电压 |
| 能力可调节性 | 档位在设计时选定,运行期可切换有限 | 码率与迭代次数运行期可调 |
| 失效特征 | 越过能力线后直接不可纠正 | 先出现迭代不收敛,再退化为不可纠正 |
| 适配的器件阶段 | 初期到中期的误码水平 | 磨损末期、高温、长保持期场景 |
软判决 LDPC 的能力优势来自「每个比特的可信度信息」,而这些信息要靠在同一页上用多档读电压分别读出、比较结果才能得到。如果器件不支持多档读电压(或支持的档位太少、切换开销过大),软判决就退化成硬判决,多出来的电路与功耗换不来对应的能力。因此选型的正确顺序是:先确认器件的读电压扫描能力,再决定码型,而不是反过来。
还有一个常被低估的取舍维度:延迟的确定性。BCH 的译码延迟基本恒定,系统可以按固定预算做时序设计;LDPC 的迭代次数随错误数上升,最坏情况的延迟可能是典型值的数倍。对有硬实时约束的读路径(比如代码就地执行、启动时加载固件),必须按最坏值评估,或者为这类路径单独设定「迭代上限 + 失败即走备用路径」的策略。
4.3 读改写流程与读电压扫描
当一次读的误码数超过硬判决能力时,全责模式的主控还有一件内置 ECC 器件做不到的事:调整读阈值再读一次。阈值扫描的基本思路是——单元阈值分布整体漂移时,原本落在判决门限错误一侧的比特,换个门限就可能落回正确一侧;通过多档电压分别读出并比对,既能直接得到一组更干净的数据,也能为软判决译码提供可信度信息。
阈值扫描不是免费的,它的代价体现在三个地方:
- 时间代价:每多扫一档,就要多一次完整的页读出与传输。扫描档位数与总延迟近似成正比,必须有上限,否则最坏延迟不可控。
- 干扰代价:每次读都在消耗读干扰预算。扫描档位越多,对邻近单元的扰动越大——为了纠错而做的动作,本身在制造新的错误。
- 策略复杂度:要不要扫、扫几档、什么时候停、扫出来怎么与上一次的结果合并,都需要一套明确的策略与收敛判据。
NAND 的擦除单位是块、编程单位是页,因此「改写一页里的几个字节」在读回、修改、重新编程的过程中,会带来三个责任问题:① 读回时如果这一页本身就接近能力上限,改写后的数据可靠性更差;② 重新编程前必须先擦除,实际上要搬到新页并更新映射;③ 改写过程中掉电,需要靠写序号与状态标记判断哪一版有效。这三件事全归主控管,任何一个处理不当都会表现为「数据悄悄变旧」或「映射错乱」。
把阈值扫描与读改写放在一起看,可以得出全责模式的一条重要结论:纠错的手段越多,需要主控管理的状态也越多。内置 ECC 器件把这一切压缩成一个状态位,主控 ECC 则要把扫描档位、迭代次数、重试次数、历史阈值全部记下来,才能做出有依据的判断。这就是第 4.5 节要展开的「设计负担」。
4.4 主控 ECC 的错误上报链
全责模式下,错误信息从介质一路传到应用,中间每一层都会做一次「翻译」。设计上报链的原则是:每一层只增加上下文,不丢失信息。现场最让人头疼的失效,往往就是因为某一层把「错误数超了」笼统地翻译成「读失败」,把关键的归因线索丢掉了。
| 层级 | 产生的原始信息 | 向上传递时应该带什么 | 常见的丢失方式 |
|---|---|---|---|
| ECC 引擎 | 纠正是否成功、错误数量、是否收敛 | 错误数量与所在码字位置 | 只报成功 / 失败,丢掉数量 |
| 闪存驱动 | 重试次数、使用的阈值档位、扫描过程 | 最终采用的档位与重试次数 | 只报最终结果,丢掉过程 |
| FTL 层 | 块的历史纠正次数、擦写次数、上次搬迁时间 | 块的健康指标与趋势 | 只报本次结果,丢掉历史 |
| 块管理 | 是否搬迁、是否标记坏块、备用块消耗 | 搬迁动作与剩余备用块数 | 只报完成,丢掉资源余量 |
| 文件系统 / 应用 | 读失败返回码 | 是否可重试、是否需降级 | 把介质错误当成普通 IO 错误 |
如果最上层只拿到一个「读失败」的返回码,它就只能选择重试或报错;而如果它拿到的是「这一页错误数已达到能力上限的若干成,所在块擦写次数已进入末期,建议搬迁」,它就能做出完全不同的决策——把数据搬到健康块、把这一块提前退役、向上报告寿命预警。同样的硬件、同样的误码率,上报链设计的差别直接决定了系统是「能用」还是「好用」。
上报链还有一条工程约束:错误日志本身也要可靠。失效分析依赖日志,而日志要写在同一片闪存上——如果日志区自己先坏掉,取证就无从谈起。常见做法是给日志区单独配置更保守的刷新策略与更高的纠错余量,并让日志写入尽量小、尽量少、尽量顺序。第 11.3 节讨论误码归因的数据收集设计。
4.5 全责模式下的设计负担
全责模式换来的是完整的可见性与完整的处置权,代价是一整套必须自己实现的机制。把这套机制列成清单,是评估「要不要上裸 NAND 方案」最实际的依据。
| 必须实现的机制 | 不做会怎样 | 复杂度 | 责任归属 |
|---|---|---|---|
| 备用区布局与版本管理 | 老数据无法解读,固件升级即变砖 | 中 | 主控固件 |
| ECC 引擎与码字切分 | 数据完整性无保障 | 高(若自研) | 主控固件 / IP |
| 坏块表与出厂扫描 | 出厂坏块被当健康块使用 | 中 | 主控固件 |
| 逻辑到物理映射 | 无法实现磨损均衡与搬迁 | 高 | FTL |
| 磨损均衡 | 热点块提前失效 | 中 | FTL |
| 掉电一致性 | 掉电后映射错乱、数据变旧 | 高 | 主控固件 + 硬件 |
| 读阈值扫描与重试策略 | 误码率上升后无法挽回 | 中 | 主控固件 + 器件 |
| 垃圾回收与预留空间 | 写放大失控,性能骤降 | 高 | FTL |
| 健康度统计与预警 | 失效无先兆,只能事后归因 | 中 | 主控固件 |
第一笔是掉电一致性:它不是「加个电容」就能解决的,而是要在映射更新、状态迁移、垃圾回收的每一步都设计可恢复点,并且要用真实掉电测试去验证(第 8.3 节)。第二笔是垃圾回收的写放大:预留空间不足时,写放大会显著恶化,既吃掉寿命也吃掉性能。第三笔是长期维护:裸 NAND 的器件供货周期长、不同批次特性各异,ECC 策略与阈值参数需要持续适配,这是内置 ECC 器件不需要付出的成本。
把图 4 与图 5 对照来看,可以更清楚地看到三种架构的取舍:裸 NAND 把边界画在「器件之外」,主控什么都能看到、什么都要自己管;接下来第 5 章要展开的 eMMC 则把边界再往外推一层,连物理块与错误计数都藏进了器件内部——它的黑盒边界如图 5 所示,主机连 spare 区的概念都不存在了。