只读分享解决方案文档调试&解决方案📅 2026-09-21🔖 Rev 1.0⏳ 29天有效(至 2026-10-31)
🔧解决方案&应用市场分析 -> 调试&解决方案 -> 存储_量产问题排查 -> 烧录成功但不开机_SPI_NAND_量产排查

烧录成功但不开机_SPI_NAND_量产排查

生成时间 2026-09-28 13:34:54 有效期至 2026-10-31 23:59:59(29天有效(至 2026-10-31))
同系列 · 调试&解决方案

烧录成功但不开机 · SPI NAND 量产排查手册

编制日期 2026-09-21 | 版本号 Rev 1.0

XTX 芯天下 · FAE 现场技术文档 | 覆盖 SD NAND / Serial NAND / ONFI 兼容 SPI NAND | 2026-09 版

技术事实核查纪律(执行前必读)

文中的命令、寄存器位、ECC 强度、坏块标记位置一律以器件 datasheet 与目标平台驱动代码为准,不得凭印象编写。

  • SPI NAND 没有统一的强制标准:坏块标记位置、spare 区划分、ECC 保护范围、配置位地址与默认值都由各厂自定,同名位在不同厂器件上含义可能不同。
  • 命令码与特征地址(如 SET/GET FEATURES、读状态、页读)需逐颗核对 datasheet;本文给出的均为常见写法示例,不是通用常量。
  • 目标平台的 OOB 布局(ECC 位置、ECC 字节数、ECC 块大小)以驱动源码与设备树为准,不以烧录器默认值或经验为准。
  • 凡本文标注「以 datasheet 为准」的条目,现场必须打开对应料号的正式 datasheet 复核后再动手。
6 类
不开机根因归类
坏块 / ECC / 偏移 / 加载链 / 配置位 / 版本批次
3 层
错位可能发生的层
烧录器侧 · 镜像侧 · 目标驱动侧
1 次
整片擦除即抹掉出厂坏块标记
标记不可可靠恢复,是最贵的一次操作
默认开
多数 SPI NAND 片上 ECC 上电默认
以 datasheet 为准;部分料号为 ECC OFF 版本
装板实跑
唯一有效的验收动作
读回校验 / 离线比对都不能替代

一句话结论:烧录器的 Verify PASS 只证明 「写进去的字节和源镜像一致」,不证明「目标 SoC 读得懂这些字节」。 NAND 的校验是「数据搬运正确」,不是「系统语义正确」—— 坏块策略、ECC 生成方、OOB 布局、分区偏移、控制位这五件事只要有一件与板上驱动不一致, 校验就会 100% 通过而系统 0% 起来。

这份文档解决什么

解决的是裸片烧录成功、贴板却不启动这一类问题的定位与根治方法, 适用于使用 SPI NAND(含 SD NAND、Serial NAND、ONFI 兼容 SPI NAND)作为启动介质或数据存储介质的量产场景。 全文按「现象 → 机制 → 类目 → 立即动作 → 根治动作」组织,第 9 章是可直接翻查的速查表。

第 1 类
坏块处理不一致
出厂坏块标记的位置、烧录器的跳过策略、目标驱动的 BBT 重建方式三者错位。全片擦除会把标记一起抹掉。
高发动作:整片擦除后再烧
第 2 类
ECC 与 spare 区不一致
多数 SPI NAND 片上 ECC 上电默认为「开」,器件会自算校验字节写进 spare;烧录器若再算一遍就是双重 ECC。
高发动作:空白页照烧 / 双写 ECC
第 3 类
分区与镜像偏移错位
镜像里的偏移是逻辑偏移,一旦烧录器跳过坏块,物理偏移整体后移,按硬编码偏移取数的一方就落空。
高发动作:按固定块号找 boot
第 4 类
启动加载链断点
BootROM → SPL/TPL → ATF → U-Boot → kernel → rootfs,每一段对分块、对齐、放置块都有自己的要求。
高发动作:整包镜像直接铺进去
第 5 类
控制位与读模式未生效
ECC 使能位、读模式位、Quad 使能、写保护位等多为易失位,断电即回上电默认,烧录时设的不会带到目标板。
高发动作:只改烧录器不改驱动
第 6 类
固件版本与料号批次不匹配
同一料号不同批次、不同 ECC 开关版本混线,表现与前面五类完全一样。本文只做指路,详见第 8 章的相互引用。
高发动作:换料号不重跑首件

怎么用这份文档

  1. 先做最小判别:把「不开机」按第 1 章的四种画像归类,确定是哪一段起不来,不要一上来就换芯片。
  2. 按第 2 章的决策树定层:先判断错位发生在烧录器侧、镜像侧还是目标驱动侧,再进对应章节。
  3. 拿「已知好板」做对照:找一块能正常启动的板子,把它的镜像、烧录配置、器件读回数据各存一份基线。
  4. 改一个变量就验一次:坏块策略、ECC 开关、偏移配置每次只改一处,改完全片重烧再上电。
  5. 用装板实跑闭环:任何配置变更,最终判定以「装板跑通完整启动 + 关键功能 + 一次断电重启」为准。

1. 问题本质:烧录通过 ≠ 系统能跑

烧录器报 Verify PASS、CRC 完全一致,芯片贴板后却黑屏、卡 logo 或文件系统挂不上—— 这通常不是芯片坏了,而是「写进去的字节」与「读它时使用的规则」之间错位了。本章先把现象还原成机制。

1.1 四种数据画像

现场接到「烧好了但不开机」时,先按串口输出把现象归到下面四类之一。 四类现象对应的是同一机制在不同启动阶段的暴露,不是四种独立故障。

完全黑屏 · 串口无输出上电电流正常,串口一行都不出BootROM 未识别到有效启动头或启动头所在块被判为坏块高概率:类目 3 / 4 / 5卡 logo · 启动到一半死SPL / U-Boot 起来,进 kernel 时挂串口出现 ECC error / 文件系统报错或卡在等待根设备那一步高概率:类目 1 / 2文件系统挂不上kernel 正常,mount 失败或变只读报坏 PEB / 卷头错位分区起始地址与镜像对不上高概率:类目 1 / 3应用起来但功能异常能进系统,但 MAC / 校准参数乱码工厂参数区读出全 FF 或全 00参数区被烧录器覆盖或 ECC 冲突高概率:类目 2 / 5四种现象不是四类故障,而是同一机制在不同阶段的暴露:写进去的字节是对的,但读它的一方解释不了。排查起点不是「芯片有没有坏」,而是「谁在读、按什么规则读、规则由谁定的」。
图 1 · 烧录通过但贴板不开机的四种现象画像

1.2 校验通过,到底证明了什么

这是全文最关键的一张表。烧录器的校验(Verify)本质是把写进去的内容读回来与源镜像逐字节比对, 它检验的是「数据搬运」这一件事;而系统能否跑起来,取决于目标 SoC 侧的「数据解释」规则。

校验通过能证明校验通过不能证明由谁决定
烧录器缓冲区里的数据与源镜像逐字节一致目标 SoC 能读懂这些字节目标驱动 / BootROM
每个被写的页都编程成功(编程失败标志为 0)这些页落在正确的物理块上烧录器的坏块策略
spare 区写入的字节与烧录配置一致spare 区布局与驱动的 OOB 布局一致驱动 ECC 配置
在当前配置位状态下读写正常断电后配置位仍保持烧录时设的值器件上电默认值
校验时器件处于可编程状态出厂坏块标记仍然存在且可识别烧录前是否整片擦除
这一颗芯片能跑这一批、这个料号、这个批次都能跑首件确认 + 装板抽检

1.3 一句话机制:三层规则必须对齐

一颗 SPI NAND 从被烧录到被系统读出来,中间要经过三方规则的叠加:

  • 烧录器侧规则:坏块怎么处置、ECC 由谁算、写入粒度是整页还是分段、空白页怎么处理、配置位设成什么。
  • 镜像侧规则:分区表怎么划、各段按页对齐还是按块对齐、spare 区里带不带 ECC 字节。
  • 目标驱动侧规则:BootROM 认哪几个块、驱动用片上 ECC 还是 SoC 硬件 ECC、OOB 布局硬编码成什么样。

这三层里任意一层与另外两层不一致,校验都会通过,系统都不会起来。 烧录器的校验逻辑完全不参与这三层规则的判定,所以它永远不会因此报错。

贯穿全文的一条纪律

「烧录通过 ≠ 贴板能跑」。只要现象是「校验 100%% 通过但系统不起来」,就不要把时间花在怀疑芯片质量上——先查三层规则是否对齐。芯片本身不良的概率远低于规则错位的概率。

1.4 与 SPI NOR 相比,NAND 多了什么

很多 FAE 的直觉来自 SPI NOR:NOR 没有坏块、没有 spare 区、字节可寻址、烧什么读什么。 换成 NAND 之后,这条直觉链全部失效:

对比项SPI NORSPI NAND(含 SD NAND / Serial NAND)
坏块出厂无坏块,无需坏块管理出厂允许存在坏块,需坏块表管理(以 datasheet 给出的最小有效块数为准)
最小写入单位可按字节 / 页编程按页编程,且页内部分编程次数有限(常见为每页最多 4 次,以 datasheet 为准)
擦除单位扇区 / 块块(一个块含数十页,以 datasheet 为准)
spare 区无每页带 spare 区(常见 64B / 128B),存放坏块标记、元数据与 ECC 校验字节
ECC不需要必须有;可由器件片上 ECC 生成,也可由 SoC 硬件 ECC 或软件 ECC 生成
读取流程发地址直接读先把整页从阵列搬到缓存,再从缓存读出(页读 + 读缓存两段式)
地址语义字节地址页地址 + 列地址;物理块号会因跳过坏块而漂移
最容易踩的一条直觉错误

不要假设 SPI NAND 出厂坏块策略就是「跳过」——各厂不同,同一厂不同料号也可能不同。有的器件在出厂时把坏块标记写进坏块第一页的 spare 区首字节,有的厂商在坏块的前两页都写标记;有的器件内建坏块替换查找表(LUT),有的完全交给主机软件管理。现场必须打开实际料号的 datasheet 确认「坏块标记写在哪、怎么识别」。

2. 三层错位总览与排查决策树

所有的「烧录通过但不开机」都可以归结为三层规则(烧录器侧 / 镜像侧 / 目标驱动侧) 没有对齐。本章给出总览图与决策树,用来在动手之前先定层——定错层,后面所有动作都是白做。

2.1 三层错位总览

下面的三层各有自己的一套规则。烧录器的校验逻辑完全不参与这三层规则的判定, 所以三层之间错位时,校验永远通过。定位问题的第一步,是判断错位发生在哪一层。

三层规则必须两两对齐,系统才起得来;任意一层与另外两层错位,烧录校验都不会报错。① 烧录器侧(裸片烧录台)坏块策略跳过坏块 / 保留标记 / 整片擦后写ECC 生成方片上 ECC 自算 / 烧录器算 / 不算写入粒度整页写 / 仅写主区 / 分段多次写空白页处理跳过空白页 / 照烧配置位上电默认 / 烧录时改写 / 易失② 镜像侧(打包产出)分区表起始块、大小、对齐单位分段布局boot / ATF / U-Boot / 环境 / kernel对齐规则按页对齐 / 按块对齐spare 区内容是否预置 ECC 校验字节版本标识料号、批次、ECC 开关版本③ 目标驱动侧(板上 SoC)坏块表 BBT全片扫描重建 / 复用已有表ECC 引擎片上 ECC / SoC 硬件 ECC / 软件OOB 布局校验字节位置与长度硬编码读方式兼容读命令 / 页读加读缓存BootROM只认固定块、固定分块大小任一层与另外两层错位 → 校验 100% 通过、贴板 0% 启动。校验只证明「写进去的和源文件一致」,不证明「系统读得懂」。
图 2 · 烧录器侧 / 镜像侧 / 目标驱动侧三层错位总览

2.2 三层各自的职责与典型错位点

层它决定什么典型错位点由谁确认
① 烧录器侧坏块如何处置、ECC 由谁生成、写入粒度、空白页如何处理、配置位设成什么跳过坏块导致物理偏移整体漂移;空白页照烧触发重复编程(over program)烧录工程师 / 编程器原厂
② 镜像侧分区表、各段起始偏移与对齐单位、spare 区是否预置 ECC 字节镜像按逻辑偏移打包,未预留坏块跳过的容量;分段未按要求对齐固件 / BSP 负责人
③ 目标驱动侧BootROM 认哪几个块、用哪种 ECC 引擎、OOB 布局硬编码成什么样驱动的 OOB 布局与烧录侧 spare 布局不一致;BootROM 要求特殊分块而镜像未分块驱动 / BSP 负责人

2.3 排查决策树

拿到一块「烧好了但不开机」的板子,按下面的顺序走。每一步都有明确的判定输出, 不要在没拿到串口日志之前就开始改配置。

烧录校验 100% 通过贴板不开机 / 启动异常完全黑屏串口零输出换已知好片 + 同镜像能起?类目 4 · 加载链+ 3 偏移卡 logo启动中断串口有 ECC 报错或坏块提示?类目 2 · ECC+ 1 坏块文件系统挂不上报坏 PEB 或卷头错位?类目 3 · 偏移+ 1 坏块功能异常参数乱码参数区读出全 FF 或全 00?类目 5 · 配置位+ 2 ECC先按串口输出归类,再按决策树定层,最后进对应类目章节。每一层只改一个变量,改完必须整片重烧并装板实跑验证。
图 3 · 按串口现象定位类目的排查决策树

2.4 判定顺序:从最便宜的动作开始

  1. 收集:串口完整日志(从第一条开始,不要只看后半段)、烧录器工程文件与配置截图、 镜像分区表、器件丝印料号与批次号。
  2. 取基线:找一块能正常启动的板子,用同一套工具读回它的全片内容(含 spare 区)存为基线。
  3. 定层:按图 3 决策树确定现象归属,进入第 3~7 章中对应的类目章节。
  4. 单点改动:一次只改一个变量(坏块策略 / ECC 开关 / 偏移配置 / 分块方式),改完整片重烧。
  5. 实跑验证:装板跑通完整启动 + 关键功能 + 一次断电重启,才算这个变量验完。
  6. 归档:把结论写回 SOP 与烧录工程文件,并标注料号 + 批次 + 固件版本三要素。
为什么一定要先取「已知好板」的基线

三层错位本质上是两份配置的差异。没有基线,就只能靠猜;有了基线,把好板与坏板的读回数据(含 spare 区)、烧录器工程配置、串口日志两两对比,差异点基本就是根因。

基线要包含 spare 区——主区数据完全一致但系统一好一坏的情况,根因几乎都在 spare 区(坏块标记 / ECC 校验字节 / 元数据)。

别在没拿到串口日志之前改配置

串口日志是唯一能区分「完全黑屏」「卡 logo」「文件系统挂不上」的依据,而这三种现象指向的类目完全不同。先接串口、先存日志,再谈改配置。

3. 第 1 类:坏块处理不一致

NAND 出厂就允许带坏块,坏块靠「标记」来识别。烧录器、镜像、目标驱动三方 对「标记写在哪、要不要跳过、跳过后偏移怎么算」的约定一旦不一致,就会出现 校验全过、贴板不启动。这是 SPI NAND 量产最高发的一类问题。

3.1 先把这颗料的坏块规则查清楚

动手之前,必须打开实际料号的 datasheet,把下面四个问题查明白。 这四件事各厂不同,且同一厂不同料号也可能不同。

块(Block)结构:一个块含若干页Page 0Main 2048B00hPage 1Main 2048B00hPage 2Main 2048BsparePage 3Main 2048BsparePage 4Main 2048BsparePage 5Main 2048BsparePage 63Main 2048Bspare(中间页省略,一个块的页数以 datasheet 为准)页(Page)内字节布局:以 2048B + 128B 为例0x000~0x7FF用户数据 2048BMain 主区 2048B0x800~0x803坏块标记 非 FFhspare 首字节:出厂坏块标记0x804~0x83F用户元数据spare 元数据区(保护性各厂不同)0x840~0x87FECC 校验字节ECC 校验区(片上 ECC 自写)注:spare 区常见为 64B 或 128B,页内划分与 ECC 保护范围各厂不同。坏块标记通常落在 spare 首字节(即 byte 2048 处),须查证实际料号。要点一:出厂坏块标记是「非 FFh」(多数厂写 00h),贴板后驱动靠扫描它来建立坏块表(BBT)。要点二:标记一旦被擦除就无法可靠恢复——擦掉标记的坏块仍是坏块,只是没人认识了。
图 4 · 块与页结构以及出厂坏块标记的位置
要查清的问题为什么重要不查清会怎样
坏块标记写在哪常见为每块第 0 页 spare 区首字节(即 byte 2048 处);部分厂商在第 0、1 两页都写标记按错位置扫描 → 坏块被当成好块使用,运行期随机出错
标记的判定值是什么常见为「非 FFh」(多数厂写 00h);也有厂用其它约定判定阈值写错 → 好块被误判为坏块,可用容量骤减
出厂最少有效块数(NVB)datasheet 会给出保证值(例如 1Gb 器件常见的 1004 / 1024,以 datasheet 为准)扫出的坏块数一旦超过上限,说明料已被擦过标记,应整批隔离
器件是否内建坏块替换表部分器件内建查找表(LUT),可自动把坏块重映射到好块,条目数有限误以为主机软件全权管理 → 与器件内建机制重复处理,偏移算法错乱

3.2 三种坏块处置策略及其后果

烧录器对坏块的处理方式,决定了贴板后驱动看到的「世界」长什么样。 三种策略里,只有策略 B 是天然贴近驱动预期的,A 依赖驱动能力,C 是明确禁止的。

策略 A · 跳过坏块(逻辑重映射)烧录器做了什么扫描出厂坏块标记遇坏块则跳过,后续数据整体后移逻辑块号与物理块号不再一一对应贴板后驱动看到物理块 N 的内容 ≠ 镜像里第 N 块按固定块号取 boot 会落空按标记自建 BBT 则可自愈结果 / 风险风险取决于驱动是否自建 BBTBootROM 只认 block 0 时直接黑屏策略 B · 保留标记按物理块写烧录器做了什么不扫描、不跳过,按物理块顺序写坏块页写不进,编程失败标志置位烧录器可能在此报校验失败贴板后驱动看到坏块标记保持原样(非 FFh)驱动扫描能识别出坏块块号与镜像块号一一对应结果 / 风险最贴近驱动预期,通常可自愈前提:boot 段没落在坏块上策略 C · 整片擦除后再写入烧录器做了什么先整片或逐块擦除,再烧镜像出厂坏块标记被一并抹掉贴板后驱动看到全片读起来都是「好块」坏块里写入的数据不可靠驱动没法重建正确的坏块表结果 / 风险校验可过,运行期随机崩标记不可可靠恢复纪律:出厂坏块标记擦掉后无法可靠恢复,坏块依然是坏块,只是没人认识了。整片擦除是最容易造成「批量良率莫名下降」的一次操作,产线原则上禁止对 NAND 整片擦除。
图 5 · 三种坏块处置策略的后果对比

3.3 现场排查动作

  1. 先扫原始状态:拿到未烧录的空白片(同批次),读回整片 raw 数据, 用下面的脚本统计坏块数量与块号,作为出厂基线。
  2. 再扫烧录后状态:同一颗片子烧完再读回,再统计一次。 坏块数从 N 变成 0,就是标记被擦过,直接查烧录器的擦除配置。
  3. 核对烧录器配置:在工程文件里确认「擦除方式」是 按块 / 按需擦除还是整片擦除,以及「坏块处理」选的是哪一项。
  4. 核对驱动侧:在驱动源码里确认 BBT 的建立方式—— 是上电全片扫描重建,还是从固定位置读取已有表,还是两者都有。
  5. 对齐后重烧验证:改完配置后整片重烧(不是增量烧),装板实跑。

3.4 读回与比对

所有结论都要落回「读回来的字节」上,不要停在配置界面里的选项名。 下面给出读回与比对的常用命令与一段扫描脚本。

读回与比对的常用命令

# ① 在目标板上读回含 spare 区的原始页(命令随平台而变,以实际 BSP 为准)
#    U-Boot 下:
nand dump 0x0                 # 查看第 0 页主区
nand dump.oob 0x0             # 查看第 0 页 OOB / spare 区

#    Linux 下按 MTD 分区读回(含 OOB):
nanddump -o -f /tmp/mtd0_oob.bin /dev/mtd0
hexdump -C /tmp/mtd0_oob.bin | head -n 16

# ② 与烧录器读回的文件在同一台机器上逐字节比对
cmp -l /tmp/from_programmer.bin /tmp/from_board.bin | head -n 20

# ③ 重点看三处:spare 首字节(坏块标记)、ECC 校验区、分区起始块的内容
#    主区全对、spare 不对  -> 第 4 章(ECC / spare 区不一致)
#    spare 全对、块号不对  -> 第 5 章(分区偏移错位)

坏块扫描脚本(参数必须按 datasheet 改)

# badblock_scan.py:从整片读回的 raw bin 中统计疑似出厂坏块
# 下面四个参数必须按实际器件 datasheet 修改,不要照抄
PAGE          = 2048 + 128   # 页大小(主区 + spare 区)
PAGES_PER_BLK = 64           # 每块页数
BB_OFF        = 2048         # 坏块标记在页内偏移(spare 首字节)
CHECK_PAGES   = (0, 1)       # 多数厂只标记第 0 页,部分厂标记前两页

data = open("dump_raw.bin", "rb").read()
nblk = len(data) // (PAGE * PAGES_PER_BLK)
bad = []
for b in range(nblk):
    base = b * PAGE * PAGES_PER_BLK
    for p in CHECK_PAGES:
        off = base + p * PAGE + BB_OFF
        if off < len(data) and data[off] != 0xFF:
            bad.append((b, p, data[off]))
            break
print("块总数 %d,疑似坏块 %d 个(%.2f%%)" % (nblk, len(bad), 100.0 * len(bad) / max(1, nblk)))
for b, p, m in bad[:40]:
    print("  block %5d  page %d  mark = 0x%02X" % (b, p, m))

# 判读:
#   - 烧录前扫描到 N 个坏块,烧录后扫描到 0 个   -> 标记被擦除过,必须查烧录器配置
#   - 烧录前后坏块数一致,但块号变了             -> 烧录器做了重映射,见第 5 章
#   - 坏块比例明显高于 datasheet 给的上限        -> 拿到的是被擦过标记的料,整批隔离

3.5 坏块相关的状态位

SPI NAND 的操作结果通过状态寄存器 / 特征寄存器回读。下面列出的是 ONFI 兼容器件中较为常见的一组定义,不同厂家的位分配与编码并不统一, 必须按实际料号 datasheet 逐位核对。

标志位(示例名)含义典型处置备注
OIP / BUSY器件正在执行内部操作轮询等待清零各厂均有,位置以 datasheet 为准
P_FAIL编程(写页)失败标记该块为坏块,换块重写出现即说明该块不可靠
E_FAIL擦除失败标记该块为坏块,不再使用出现即说明该块不可靠
ECCS2/1/0读操作的 ECC 状态见下表的编码说明ONFI 兼容器件常见命名
WEL写使能锁存写/擦前必须先置位未置位时写命令会被忽略
ECCS2/1/0含义(示例编码)处置建议
000无错误正常
001发现 1~3 bit 错误并已纠正记录即可,可考虑刷新该页
011发现 4~6 bit 错误并已纠正建议数据刷新(搬走重写)
101发现 7~8 bit 错误并已纠正必须刷新,保留余量已不多
010错误位数超出纠正能力,不可纠按坏块处理,触发换块流程
危险操作:整片擦除 / 跨块回绕擦除

对 NAND 执行整片擦除(Chip Erase)或不受控的跨块回绕擦除,会把出厂坏块标记一起抹掉,且标记无法可靠恢复。坏块的物理缺陷不会因为擦除而消失,只是失去了被识别的手段——结果是坏块被当成好块编程写入数据,现场表现为「校验 100% 通过,运行期随机崩、批量良率莫名下降」。

产线纪律:原则上禁止对 NAND 做整片擦除;确需擦除时,必须先逐块读出并保存坏块标记,擦除后按原标记重写回去,并在 MES 中记录该操作。

不要假设出厂坏块策略就是「跳过」

各厂不同,同一厂不同料号也可能不同。有的器件在坏块第 0 页 spare 区首字节写标记,有的在第 0、1 两页都写;有的内建坏块替换查找表(条目数有限,用满后由标志位指示),有的完全交给主机软件管理。

如果器件内建替换表,而烧录器又做了一次逻辑跳过,两套重映射会叠加,偏移算法直接错乱。遇到内建 LUT 的料,先决定「用器件内建的」还是「关掉它用软件的」,两者不能同时生效。

4. 第 2 类:ECC 与 spare 区不一致

SPI NAND 必须有 ECC,但「谁来算 ECC、算完放哪、保护多大范围」这三件事, 在烧录器、器件片上引擎、目标驱动三方之间必须完全一致。 只要有一方多算或少算一层,主区数据可以完全正确,系统却照样起不来。

4.1 第一件事:确认器件片上 ECC 的上电默认状态

这是整个第 2 类的起点,也是现场最常被跳过的一步。 多数 SPI NAND 的片内 ECC 在上电后默认为使能, 且该位多为易失位——器件复位不会把它清零,但断电后会回到默认值。 具体默认值、所在寄存器与位号一律以实际料号 datasheet 为准。

关键纪律:不要假设 ECC 必须由烧录器生成

很多 SoC 用硬件 ECC,也有很多器件片内已经算了 ECC。烧录器再算一遍 ECC,反而会与它们冲突。

ECC 生成方只能有一个:器件片内引擎、SoC 硬件 ECC 引擎、软件 ECC,三选一。两套并存时,主区数据通常还是对的(所以校验通过),但 spare 里的校验字节被后写的一方覆盖,读回来校验失败,现场表现为「能读到数据但报不可纠错误」。

4.2 四种组合,只有一种是对的

把「器件片上 ECC 开关」与「烧录器是否算 ECC 并写 spare」两两组合, 得到下面四种情形。先把当前产线落在哪一格确定下来,再谈怎么改。

A · 片上 ECC 开 + 烧录器只写主区器件自动对主区算 ECC,把校验字节写入 spare 校验区若驱动同时启用 SoC 硬件 ECC,同一份数据被两套 ECC 解释spare 校验区已被器件占用,驱动再往里写会被忽略或失败症状:字节读出来是对的,但 ECC 状态位报不可纠B · 片上 ECC 开 + 烧录器写主区 + spare烧录器算好的 ECC 字节会被器件重算结果覆盖空白页照烧 → spare 被写入 → 该页不再是「已擦除」态页内重复编程会破坏已写入的 ECC 校验数据症状:二次写入后数据错,文件系统报校验错误C · 片上 ECC 关 + 烧录器算 ECC 写 spare必须三方对齐:算法、纠错强度、校验字节位置与长度与驱动硬编码的 OOB 布局差一个字节就全崩烧录器侧与驱动侧要用同一套布局定义,不能各写各的症状:能起 kernel,挂载时满屏 ECC 报错D · 片上 ECC 关 + 烧录器不写 sparespare 区保持 FFh,交给驱动 / 文件系统首次写入前提是烧录器不会顺手写 spare,也不会写空白页最干净的一种,但要求器件片内 ECC 确实可以关掉症状:起不来时先确认 ECC 是否真的关掉了判定要点:先看器件片上 ECC 的上电默认状态(多数厂默认为「开」,部分料号为 ECC OFF 版本),再决定烧录器算不算 ECC。顺序反了 —— 烧录器算得再准,器件也会把它覆盖掉,烧多少片都是白烧。
图 6 · 片上 ECC 与烧录器 ECC 的四种组合及后果

4.3 ECC 布局的三方对齐检查表

如果确定走「烧录器算 ECC」这条路(图 6 的 C 格),下面每一项都必须 在烧录器配置与驱动源码 / 设备树两侧取到同一个值。

对齐项烧录器侧取值驱动侧取值不一致的后果
纠错算法BCH / Hamming(按驱动要求选)驱动或 SoC 寄存器里配置的算法校验字节无法被正确译码
纠错强度如 4 / 8 / 24 bit(以 datasheet 与驱动为准)驱动实际配置的强度强度低于需求则不可纠,高于需求则布局对不上
ECC 块大小每个校验分组覆盖的字节数驱动的 step size分组边界错位,全部校验失败
校验字节位置spare 区内的起始偏移(ECCPOS)驱动硬编码的 OOB 布局差一个字节就全崩
校验字节长度每个分组对应的字节数(ECCBYTES)驱动的 ecc bytes越界覆盖坏块标记或元数据
受保护范围仅主区 / 主区 + 部分元数据驱动声明的保护范围元数据被当作无保护数据处理

4.4 spare 区布局示例与差异警告

下表是一种常见的 2K + 128B 页结构的划分示例, 用于说明「spare 区不是一整块空地,而是被切成若干用途不同的小段」这一事实。 不同厂家的划分差别很大,必须按实际料号 datasheet 复核。

页内地址(示例)区域受片内 ECC 保护说明
0x000 ~ 0x7FFMain 0~3 用户数据(4 × 512B)是用户数据主体
0x800 ~ 0x803Spare 0 首字节否出厂坏块标记;须在片内 ECC 关闭时读取才可靠
0x804 ~ 0x83FSpare 元数据区各厂不同部分厂按 4B + 12B 再分组,受保护性不同
0x840 ~ 0x87FECC 校验区(4 × 16B)是片内 ECC 开启时由器件自写,主机写入被忽略
spare 区划分各厂不同,这张表只是示例

同样是 2K + 128B 的页,不同厂家的 spare 区划分并不一致:有的把未受保护区域划为 4 字节,有的划到 32 字节;有的把 ECC 校验区放在 spare 区末尾,有的放在中间。尤其要注意「未受保护区域的起始地址与长度」——它直接决定坏块标记会不会被 ECC 覆盖。

现场做法:以实际料号 datasheet 的 spare / ECC 布局表为准,并把该表截图贴进烧录 SOP,避免二次查错。

4.5 空白页与页内重复编程(over program)陷阱

这是片内 ECC 开启时特有的坑,也是「校验通过但运行期出错」的高频根因:

  • 片内 ECC 引擎一次对整页(或整个 ECC 分组)算出校验字节并写进 spare 区。
  • 如果烧录器把一页「全 FFh 的空白页」也照烧一遍,器件照样会为它算出校验字节并写入 spare—— 于是这一页在物理上就不再是「已擦除」状态。
  • 文件系统按「全 FFh = 已擦除」的约定,会直接往这一页写数据而不先擦除, 造成页内重复编程,写入的校验字节与数据不再匹配。
  • 由于存储单元只能从 1 写成 0、不能从 0 写回 1,第二次写入的校验字节无法覆盖第一次的, 结果是数据错乱。
空白页处理的两条对策

对策一:烧录前判断整页是否全为 FFh,是则跳过该页不编程。
对策二:确认片内 ECC 可关闭时,先把 ECC 使能位关掉再烧,烧完再恢复。

两条都不做的话,「空白页照烧」几乎必然在运行期暴露为文件系统校验错误。

4.6 现场核对动作

  1. 查器件默认值:打开料号 datasheet,确认片内 ECC 使能位的上电默认值、寄存器地址、位号、是否易失。
  2. 查驱动实际值:在目标板上用下面的命令读回驱动真正在用的 ECC 参数, 不要相信配置项的名字,要看运行时值。
  3. 查烧录器配置:确认烧录器是否勾选了「计算并写入 ECC」、 「跳过空白页」、「包含 spare 区」等选项。
  4. 三方对齐后再烧:只保留一个 ECC 生成方。若器件片内 ECC 已开, 就把烧录器的 ECC 关掉;反之亦然。
  5. 装板实跑:跑通启动 + 读写文件 + 断电重启,确认文件系统可正常挂载与写入。

核对驱动侧 ECC 参数的常用命令

# 在目标板上确认驱动实际使用的 ECC 参数(路径随内核版本而变,以实际平台为准)
cat /proc/mtd
cat /sys/class/mtd/mtd0/ecc_strength        # 纠错强度(bit)
cat /sys/class/mtd/mtd0/ecc_step_size       # 每个 ECC 块的大小(Byte)
cat /sys/class/mtd/mtd0/oobsize             # spare / OOB 区大小(Byte)
cat /sys/class/mtd/mtd0/bad_blocks          # 驱动认定的坏块数

# U-Boot 下查看识别到的器件与分区
nand info
mtd list
mtdparts

# 内核启动日志里抓 ECC / 坏块相关行(这是最快的一步)
dmesg | grep -iE "nand|ecc|spinand|ubi|bad"
第 2 类的快速判定

如果主区数据逐字节全对,但系统报 ECC 错误,那么 90%% 以上落在第 2 类:把 spare 区单独 dump 出来,对照「坏块标记位置 / 元数据区 / ECC 校验区」三段逐一比对,差异点即为根因。