只读分享解决方案文档调试&解决方案📅 2026-09-21🔖 Rev 1.0⏳ 29天有效(至 2026-10-31)
🔧解决方案&应用市场分析 -> 调试&解决方案 -> 存储_样机问题测试 -> 主控驱动不匹配_参数页对齐_样机排查

主控驱动不匹配_参数页对齐_样机排查

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

主控驱动不匹配 / 参数页对齐 · 样机排查手册

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

XTX 芯天下 · FAE 现场技术文档 | 换料 / 换批次后驱动不认或行为异常的分层定位与参数页对齐 | 2026-09 版

技术事实核查纪律(动手前必读)

驱动表结构体名、宏名、字段偏移、设备树属性名全部以所用内核 / SDK 源码与器件 datasheet 为准;本文只回答「要查哪一类、按什么顺序改」。

  • 本文讨论的是器件与主控驱动之间的「认识问题」:硬件连得上、波形正常,但驱动不认, 或者认下来了却用错参数。连 ID 都读不回来的硬件层问题,请先看《上电不识别_ID读不到_样机排查》。
  • SFDP 的字段名与偏移以 JESD216 系列规范为准、ONFI 参数页以 ONFI 规范为准、 EXT_CSD 以 JEDEC JESD84 为准。本文不给出未经核实的具体偏移与位定义, 只给出「要核对哪些字段」,数值一律回查规范与 datasheet。
  • SPI NAND 读参数页的命令码(ONFI 常见写法为 0xEC)与地址字节数在不同器件上并不统一, 动手前必须逐字节核对 datasheet 的指令表。
  • 驱动表条目里的 擦除 opcode、编程 opcode、容量字段 写错会直接破坏数据,属高危操作, 见第 3 章的危险操作提示。
3 层
识别架构
硬件 ID → 参数页 → 驱动表,缺一关都不成立
参数页
对齐核心
SFDP / ONFI / EXT_CSD 三者字段各不相同
换料号
最高频触发
ID 变了没同步改驱动表
16 MB
4 字节地址边界
超过不切地址模式就回绕覆盖
tuning
eMMC 高速前提
HS200 / HS400 必须做采样点调校

这份文档解决什么

解决的是换料、换批次、换主控平台之后,驱动层不认新器件,或者认下来了却用错几何参数、容量、时序与 ECC 策略 这一类问题的定位与根治方法。覆盖 SPI NOR(SFDP)、SPI NAND(ONFI)、eMMC(EXT_CSD)、SD / SD NAND(CSD / SCR) 四种介质的参数页对齐。

全文按「机制 → 决策树 → 分成因排查 → 速查表 → 核查清单」组织:第 2 章把问题收敛到 A / B / C 三类, 第 3~5 章给出各介质可直接粘贴的驱动表条目、设备树片段与回读命令,第 6 章速查,第 7 章核查与结案。

一句话结论:现代驱动不再靠「认 ID 查表」一件事搞定,而是三层串行—— 硬件层把 ID 读回来、参数页层把器件自描述表解析开、驱动表层命中一个条目。 换料失败时,绝大多数人只改了第三层(补一条驱动表),却忽略了第二层: 早期或特殊器件的 SFDP / ONFI 参数页字段可能缺失、填 0 或与默认约定不一致,驱动 fallback 到一套错误假设, 于是容量减半、地址回绕、ECC 冲突、高速读失败这些「玄学」现象就来了。 正确的动作顺序是:先回读参数页原始 hex,逐字段与 datasheet 比对,再决定是改驱动表还是改设备树; 临时兼容条目只用来证明「参数对了就能跑」,绝不能锁进量产参数卡。

1. 机制:驱动是怎么认识一颗芯片的

早期驱动靠一张静态表认芯片,现代驱动多了一层——先读器件的自描述参数页,再决定用什么参数。 理解这三层,换料失败就不再神秘:要么 ID 没读回,要么参数页解得不对,要么表里根本没这一项。

1.1 三层识别架构

第一层是硬件识别:控制器按各介质的约定发读 ID 命令,器件回厂商 ID 与器件 ID。 这一层不通后面全免谈,但它通常不是换料失败的堵点——换料前后硬件连接没变。

第二层是参数页,也是本文重点。器件内部存了一张自描述表,驱动读回它就能知道容量、擦除粒度、 读命令与 dummy cycle、Quad 能力、ECC 要求等。问题在于:不是所有器件的参数页都完整、都符合驱动期望的那个版本。 早期器件可能没有参数页,或关键字段填 0 / 填了驱动不认的值,此时驱动会 fallback—— 而 fallback 用的那套默认假设,往往与新器件并不相符。

第三层是驱动表。表里没有这一项,就退回默认参数或直接报错。 不同系统的表位置完全不同:linux 的 spi-nor 器件表、U-Boot 的 flash 表、RT-Thread SFUD 的表、 Zephyr 与 ESP-IDF 各自的 ID 表,文件名、结构体名、宏名都要按 SDK 源码核对,不能跨系统照抄。

驱动识别是一串串联的三关:ID 读得回、参数页解得开、驱动表命中,缺一关都不成立L1 硬件识别层控制器发读 ID 命令,器件回厂商 ID / 器件 ID(或 CID)SPI NOR:读 JEDEC IDSPI NAND:读 ID + ONFIeMMC:CMD2 读 CIDL2 参数页层(器件自描述)驱动按标准读回器件自描述表,从中取出几何、时序与能力SPI NOR:SFDP 参数表SPI NAND:ONFI 参数页eMMC:EXT_CSD(512B)L3 驱动表层(静态表)驱动表里没有这一项,就退回默认参数或直接报错退出linux:spi-nor 器件表U-Boot / SFUD:flash 表mmc:驱动按 CID/EXT_CSD 匹配每层的典型失败表现L1 失败:ID 读不回或值不对串口:no flash / unknown ID先查硬件:电源、CS#、pinmux、线序不要一上来就改驱动表L2 失败:ID 对但几何 / 时序错容量减半、读写越界、ECC 报错回读参数页原始 hex,与 datasheet 逐字段比对补 4 字节地址、Quad、dummy 标志L3 失败:表里有但条目不匹配换料号 / 换批次后 ID 变了,没同步改表补驱动表条目或改设备树节点临时兼容必须标记,禁止锁进量产注:linux / U-Boot / RT-Thread / Zephyr 的驱动表文件名、结构体名与宏名各不相同,以所用 SDK 源码为准。
图 1 · 驱动识别的三层架构(左:串行三关;右:每层失败表现)

1.2 四种介质的参数页对照

四种介质的自描述表名字不同、读法不同、包含的信息也不同。 排查前先确认你面对的是哪一种,用错入口会得到完全错误的结论。

介质自描述表常见读取入口需要对齐的关键信息缺失时的典型后果
SPI NORSFDP(JESD216 系列)驱动探测时自动读取容量、擦除粒度、读命令与 dummy cycle、4 字节地址、Quad 能力fallback 到默认参数,容量或读命令不匹配
SPI NANDONFI 参数页Read Parameter Page 命令(常见 0xEC)page / spare / block 几何、ECC 要求、时序参数OOB 布局与 ECC 策略按错误假设配置
eMMCEXT_CSDCMD8(SEND_EXT_CSD)容量扇区数、速率能力位图、缓存与后台操作、分区配置容量不符、速率上不去、缓存行为异常
SD / SD NANDCSD / SCRCMD9(SEND_CSD)容量、速率等级、总线宽度能力容量识别错误、宽度切换失败
容易混淆:eMMC 的 CMD8 与 SD 的 CMD8 不是一回事

eMMC 的 CMD8 是 SEND_EXT_CSD(读扩展寄存器), SD 的 CMD8 是 SEND_IF_COND(电压与版本探测), 命令号相同、语义完全不同。跨介质套用命令码是排查时最常见的低级错误。

2. 决策树:不认 / 认错怎么收敛

拿到「换料后不认」的样机,先按 ID 读不读得回、读回来对不对、认下来行为正不正常 把问题收敛到 A / B / C 三类,再进对应章节。不要一上来就补驱动表条目—— 八成的坑在第二层参数页,不在第三层。

换料 / 换批次后驱动不认,或认下来但行为异常A 类 · ID 读不回来判定:日志报 no flash / unknown IDB 类 · ID 对但表里没有判定:ID 值正确却报不支持该型号C 类 · 认下来但行为异常判定:能挂载却读写错 / 容量不对1. 硬件连通问题电源、CS#、pinmux、焊接与线序2. 读 ID 命令不支持老器件无该命令或时序不匹配3. 器件处于异常状态深度掉电、busy、读保护未解1. 驱动表缺条目补 name/vendor/size/erase/page op2. 匹配字段写错ID 掩码或长度不匹配导致漏命中3. 设备树未绑定compatible 与驱动匹配不上1. 参数页字段缺失驱动 fallback 到默认参数2. 4 字节地址未启用超过 16 MB 后地址回绕覆盖3. ECC / OOB 假设不一致on-die ECC 与主控 ECC 冲突对齐动作:读回原始 hex → 与 datasheet 逐字段比对 → 改驱动表或设备树 → 复测(大块读写 + 回读校验)。纪律:临时兼容只用于证明「参数对了就能跑」,正式补丁回来后必须撤掉并回归。
图 2 · 驱动不匹配问题的收敛决策树(按 ID 读回情况分三类)

怎么用这棵树

  1. 先把 ID 读回来:串口日志、驱动调试打印、逻辑分析仪解码任选。 拿到 ID 之前不要改任何代码。
  2. ID 对不对,用 datasheet 核,不要用「上一颗料的 ID」核。 换料后 ID 变了是正常的,变了不认才是问题。
  3. ID 对但不认 → B 类:查驱动表的匹配字段(ID 序列、匹配长度、掩码)与设备树绑定。
  4. 认下来但行为异常 → C 类:回读参数页原始 hex 与 datasheet 逐字段比对,而不是继续猜。

2.1 对齐方法四步

无论哪种介质,参数页对齐的动作序列都是固定的这四步。跳过第二步直接改代码,是返工的根源。

  1. 回读原始 hex:用内核调试日志、U-Boot 命令或用户态 spidev 脚本, 把参数页原始字节读回来存档。只要原始字节,不要只看驱动解析后的结果—— 解析结果已经被驱动的假设污染过了。
  2. 与 datasheet 逐字段比对:签名、版本、几何字段、能力位、时序字段一项一项对, 对不上的记下来,就是要改的地方。
  3. 改驱动表或设备树:几何与能力类的差异改驱动表条目; 速率、总线宽度、匹配串、分区这类差异改设备树节点。
  4. 复测:大块读写 + 回读校验 + 冷启动一致性 + 温压拉偏。 只跑通 boot 不算复测通过。
临时兼容与正式补丁的边界

为快速证明「参数对了就能跑」,可以克隆相近型号的条目做成临时兼容条目,但必须满足三条: 在名称或注释里标明「临时 / 非正式」、未核实的字段保持待确认而不是照抄、 正式补丁回来后立刻撤掉并回归。 把临时条目直接锁进量产参数卡是这类问题里后果最严重的做法——它会在几个月后以「批次间不一致」的形式重新暴露, 且那时已追溯不回原因。

3. 成因一:SPI NOR 的 SFDP 与驱动表

SPI NOR 侧最常见的情况是:驱动优先读 SFDP,读不到或字段不认就 fallback, fallback 的默认参数与新器件不符;或者干脆是驱动表里没有这个 ID。

3.1 原理:SFDP 优先,驱动表兜底

支持 SFDP 的驱动,探测流程大致是:读 JEDEC ID → 读 SFDP 参数表 → 用参数表里的信息填充几何与时序 → 如果 SFDP 缺失或解析失败,才退回驱动表里该条目的静态参数。 这意味着两件事:

  • SFDP 存在但字段有问题时,驱动会用错误的自描述参数去工作,此时改驱动表条目可能完全不起作用, 因为驱动根本没走到查表那一步。
  • SFDP 缺失时,驱动表条目是唯一的参数来源,此时条目里的每一个字段都必须来自 datasheet, 照抄相近型号必然出错。

几个高频的具体坑:

  • 4 字节地址:容量超过 16 MB 必须用 4 字节地址(或地址模式切换机制)。 标志没开,访问高位地址时会回绕到低位、静默覆盖已有数据——最危险的一类,因为它不报错。
  • Quad 能力:使能位与读命令需要配套。位没开但驱动按 Quad 读,读出来全错; 位开了但主控没按 Quad 时序发,同样错。
  • dummy cycle:读命令与 dummy cycle 数必须成对。 配错表现为「低速读正常、高速读错」或反过来。
SPI NOR:SFDP 回读与关键字段比对字段回读值期望 / 判据判定签名 'SFDP'53 46 44 5053 46 44 50✓第 1 参数头 ID0000 = 基础参数表✓参数表指针xx xx xx落在器件地址范围内核容量(密度字段)xx xx xx xx= 标称容量核4 KB 擦除支持1 / 0按 datasheet核1-1-4 / 1-4-4 读1 / 0与驱动实际用法一致核dummy cycle 数xx与读命令配套核4 字节地址标志1 / 0>16MB 必须为 1核注:字段名与偏移以 JESD216(SFDP)规范与器件 datasheet 为准;表中数值为示意。比对动作与高频误判回读方式linux:开 spi-nor 调试日志,打印 SFDP 字段U-Boot / RT-Thread:加临时打印回读 hex比对要点签名必须对;参数表指针必须落在器件内密度字段换算后必须等于标称容量高频误判SFDP 缺失就当「不支持」,实际器件支持dummy cycle 与读命令不配套,高速读错
图 3 · SPI NOR 的 SFDP 回读与关键字段比对(右:比对动作与高频误判)

3.2 实操:回读比对与补条目

第一步:回读 SFDP 原始 hex

# 1) 打开 SPI NOR 子系统的动态调试(路径随内核版本不同,按实际调整)
echo 'file drivers/mtd/spi-nor/* +p' > /sys/kernel/debug/dynamic_debug/control

# 2) 重新触发一次探测(重新绑定驱动或重启),再抓日志
dmesg -w | grep -iE 'spi-nor|sfdp|nor0|unrecognized|unknown|jedec'

# 3) 若内核导出了 sysfs 节点,可直接看识别结果(路径随内核版本不同,不存在就回到 dmesg)
cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id     # ← 总线号/CS 号按实际改
cat /sys/bus/spi/devices/spi0.0/spi-nor/partname

# 4) 看容量是否与标称一致(mtd 大小不等于文件系统可用大小,要看 mtd 原始大小)
cat /proc/mtd

第二步:补驱动表条目(可直接粘贴,注释处按实际改)

/* 文件示例:drivers/mtd/spi-nor/<厂商>.c,或 SDK 自带的 spi-nor 器件表
 * 具体文件名、结构体名、宏名以所用内核 / SDK 源码为准,不要照抄路径。
 *
 * 填写纪律:
 *   1) 每个字段都要在 datasheet 上找到出处,没出处的先留 0 并标记为「待确认」;
 *   2) erase / program 的 opcode 必须与 datasheet 的指令表逐字节核对;
 *   3) 能力标志只开「已经实测验证过」的,不要一次全开。
 */
{
    /* 名称:会打印在日志里,建议直接用完整料号,便于日后追溯到批次 */
    .name = "xt25wxxxx",              /* ← 按实际料号改 */

    /* JEDEC ID:填读 ID 命令实际回读的厂商 ID + 器件 ID 序列
     * 顺序与长度按驱动定义的匹配规则填,不要凭印象写 */
    .id     = { 0x00, 0x00, 0x00 },   /* ← 按实际回读值改 */
    .id_len = 3,                      /* ← 按驱动的匹配长度定义改 */

    /* 容量:单位字节,按 datasheet 标称容量填,不是「读出来的最大值」 */
    .size = (1 << 24),                /* ← 按标称容量改,例如 2 MB = 1<<21 */

    /* 写粒度:页大小,必须来自 datasheet */
    .page_size = 256,                 /* ← 按 datasheet 改 */

    /* 擦除:opcode 与粒度来自 datasheet 的指令表,两者必须配套 */
    .erase_opcode = 0x20,             /* ← 常见为 4 KB 扇区擦除,务必核对 datasheet */
    .sector_size  = (1 << 12),        /* ← 与该 opcode 对应的擦除粒度 */

    /* 编程:opcode 来自 datasheet 的指令表 */
    .program_opcode = 0x02,           /* ← 常见为 Page Program,务必核对 datasheet */

    /* 能力标志:宏名随内核版本变化(4 字节地址、Quad 使能、读命令各不相同)
     * 未核实的标志一律留 0,确认一项开一项 */
    .flags = 0,                       /* ← 逐项核实后再按位或上去,例如
                                       *   .flags = SPI_NOR_4B_OPCODES 之类,
                                       *   宏名以所用内核源码为准 */
},

第三步:改设备树节点(可直接粘贴,注释处按实际改)

/* 文件:<板级>.dts —— 路径与节点名按实际平台改 */
&spi0 {
    status = "okay";

    /* 引脚复用必须与实际原理图一致;漏配会表现为「命令发出去没有回读」 */
    pinctrl-names = "default";
    pinctrl-0 = <&spi0_pins>;            /* ← 按实际 pinctrl 节点名改 */

    flash@0 {
        /* 匹配串:不同内核版本写法不同,常见为 "jedec,spi-nor",
         * 也有厂商自定义匹配串,以所用内核的驱动源码为准 */
        compatible = "jedec,spi-nor";    /* ← 按内核支持的匹配串填 */
        reg = <0>;                       /* ← CS# 编号,按原理图改 */

        /* 频率:先填保守值,跑通后按实测波形逐步收敛,不要一上来就写器件标称上限 */
        spi-max-frequency = <50000000>;  /* ← 按实际改,调试阶段可先降到 10000000 */

        /* 总线宽度:调试阶段先设 1,确认单线能跑通再改成 4 */
        spi-rx-bus-width = <1>;          /* ← 1 / 2 / 4 */
        spi-tx-bus-width = <1>;          /* ← 1 / 2 / 4 */

        #address-cells = <1>;
        #size-cells = <1>;

        partitions {
            compatible = "fixed-partitions";
            #address-cells = <1>;
            #size-cells = <1>;

            partition@0 {
                label = "spl";           /* ← 分区名按实际启动布局改 */
                reg = <0x0 0x100000>;    /* ← 偏移与长度按实际改 */
                read-only;
            };

            partition@100000 {
                label = "uboot";
                reg = <0x100000 0x200000>;   /* ← 按实际改 */
            };

            /* 其余分区按实际布局继续补;
             * 分区偏移必须与烧录器配置、量产脚本三者完全一致,否则会出现
             * 「烧进去的地址与启动读取的地址不是一个」这类错位问题 */
        };
    };
};
危险操作:驱动表里这几个字段写错会直接毁数据
  • 擦除 opcode 写错:驱动用错误命令去擦,可能擦掉不该擦的区域,严重时擦成空白且不可恢复。
  • 编程 opcode 写错:写入内容与校验结果不符,还可能触发意外的编程操作。
  • 容量字段填大:驱动会访问超出物理范围的地址,最坏情况是回绕覆盖已写入的数据。
  • 4 字节地址标志乱开 / 该开不开:前者在 3 字节器件上发出非法命令序列, 后者在超过 16 MB 的地址上静默回绕覆盖。
  • 用 flash_eraseall / flashcp 前不确认目标 mtd 编号: 选错编号会擦掉另一个分区,执行前先 cat /proc/mtd 核对。

3.3 验证:条目填对了的判据

  • 容量等于标称容量:看块设备或 mtd 的原始大小,不是「看起来能跑」。
  • 跨 16 MB 边界读写不回绕:边界前后各写不同数据再回读,确认没互相覆盖。 超过 16 MB 的器件这一项必做。
  • 擦除粒度正确:按 datasheet 的最小擦除单位擦写,能成功且不影响相邻区域。
  • Quad 读(若开了)与单线读结果一致:两种模式各读一遍同一区域比对。
  • 全片回读校验通过:整片写入已知图案再回读比对,不是只测几个页。
  • 连续 20 次冷启动识别结果一致,且打印出的器件名就是新料号。

4. 成因二:SPI NAND 的 ONFI 与 ECC

SPI NAND 比 SPI NOR 多两件必须谈拢的事:OOB(spare)区怎么布局、ECC 由谁来做。 这两件事谈不拢的典型表现是「能烧、能启动,但文件系统挂载就报错」。

ONFI 参数页读取与关键字段发出 Read Parameter Page 命令命令码常见为 0xEC,地址 0x00(以 datasheet 为准)等待 tR 后连续读回字节流长度至少覆盖参数页前段,够取几何字段校验签名 'ONFI'签名不对 → 命令 / 时序 / 线宽有误,或器件非 ONFI取 page / spare / block 几何字段spare 区大小决定 OOB 布局,不能想当然取 ECC 要求字段器件要求的最低纠错能力,需不超过主控 ECC 能力ECC 与 OOB 的三种典型错配错配一:on-die ECC 与主控 ECC 同时开现象:写入正常,回读出现不可纠正错原因:on-die ECC 已占用 spare,主控又写一份处理:二选一,全链路统一策略错配二:OOB 布局假设不一致现象:能烧能启动,文件系统挂载就报错原因:驱动按旧料的 spare 布局算 ECC 位置处理:按参数页 spare 大小重算布局错配三:page size 假设不一致现象:小数据 OK,跨页写入出错原因:驱动假设 2 KB,实际 4 KB(或反之)处理:以参数页字段为准,禁止硬编码
图 4 · SPI NAND 的 ONFI 参数页读取(左)与 ECC / OOB 三种典型错配(右)

4.1 原理:几何字段与 ECC 要求都在参数页里

ONFI 参数页里放着驱动需要的全部几何信息:page 大小、spare(OOB)大小、block 大小, 以及器件要求的最低纠错能力。驱动读回参数页后按这些信息配置布局与 ECC 策略。问题出在两处:

  • 驱动没真正读参数页,而是按「上一个料号」的值硬编码。 换料后 page 从 2 KB 变 4 KB、spare 从 64 B 变 128 B,驱动还按旧几何算 ECC 位置, 于是数据被 ECC 校验字节挤掉,或 ECC 根本没覆盖到数据区。
  • ECC 责任方没统一。SPI NAND 普遍带 on-die ECC(片内纠错), 主控侧也有硬件 ECC,两者不能同时承担同一份数据的纠错, 同时开会导致 spare 区被重复占用、回读出现不可纠正错。 正确做法是二选一并在全链路统一。

4.2 实操:回读参数页与核对 ECC 策略

回读 ONFI 参数页(示意脚本,需 root 与 spidev 权限)

# 通过 /dev/spidevX.Y 回读 ONFI 参数页 —— 示意脚本,需 root 与 spidev 权限
# 依赖:pip install spidev(部分平台需先在设备树里启用 spidev 节点)

import time
import spidev

spi = spidev.SpiDev()
spi.open(0, 0)                 # ← 按实际 SPI 总线号 / CS 号改
spi.max_speed_hz = 1_000_000   # 读参数页先用低速,保证稳定
spi.mode = 0                   # ← 按 datasheet 的 CPOL / CPHA 改

# 1) 发 Read Parameter Page:命令码 + 地址 + 等待 tR
#    命令码与地址字节数以 datasheet 的指令表为准(ONFI 常见写法为 0xEC + 地址 0x00)
cmd = [0xEC, 0x00, 0x00]       # ← 逐字节核对 datasheet
spi.xfer2(cmd)

# 2) 等 tR:器件把参数页搬到内部缓存的时长,量级见 datasheet
time.sleep(0.001)              # ← 按 datasheet 的 tR 改

# 3) 读回前 128 字节,先看签名
buf = bytes(spi.readbytes(128))
print("signature :", buf[0:4])            # 期望为 b'ONFI'
print("raw[0:32] :", buf[0:32].hex(" "))

# 4) 按参数页字段偏移解析 page / spare / block 与 ECC 要求
#    偏移以 ONFI 规范与 datasheet 为准,禁止沿用「上一个料号的值」
spi.close()

在目标板上交叉确认

# 在目标板上交叉确认内核识别出的几何与 ECC 信息
cat /proc/mtd
mtdinfo /dev/mtd0                      # 需 mtd-utils:看擦除块 / 最小写入单元 / OOB 大小
dmesg | grep -iE 'spi-nand|onfi|ecc|oob|bitflip|bad block'

# 若使用 UBI / UBIFS,看挂载时打出的几何信息
dmesg | grep -iE 'ubi[0-9]|physical eraseblock|logical eraseblock|sub-page'
# 三者必须一致:参数页回读值 = datasheet = mtdinfo 输出

ECC 与 OOB 的核对顺序

  1. 先确认 on-die ECC 是开还是关。查 datasheet 里相关配置位(或 feature 寄存器)的默认值, 再确认驱动 / 烧录工具有没有主动改过。状态要与量产脚本一致。
  2. 再确认主控侧 ECC 能力:主控硬件 ECC 能纠多少位,是否覆盖器件要求的最低纠错能力。 器件要求的能力必须不超过主控能力。
  3. 核对 OOB 布局:spare 区里 ECC 字节放哪、坏块标记放哪,必须与文件系统层的期望一致。
  4. 核对 page / block 几何:驱动解析出的值必须与参数页回读值一致,不能来自硬编码。
  5. 全链路统一:BootROM、SPL、U-Boot、内核、量产烧录工具五处策略必须一致。 只改内核不改烧录工具,是最常见的漏项。

4.3 验证:布局对不对,跑一遍就知道

  • 参数页回读的几何值与 datasheet 一致,且与驱动打印出的值一致。
  • 跨页写入验证:写超过一个 page 长度的数据再回读。 只写几十字节测不出 page 大小假设错误。
  • 文件系统完整流程:挂载、写入、卸载、重新挂载、回读校验。
  • ECC 纠错能力实测:在可控环境人为制造少量位翻转,确认能被纠正并上报正确位数。
  • 坏块表一致性:驱动扫描出的坏块与量产工具扫描出的一致。
「能烧进去」不代表布局对了

烧录器能烧、能启动,只证明烧录器与 BootROM 这一对谈拢了。 内核与文件系统层用的是另一套布局假设,不匹配时会在挂载阶段才暴露。 所以验收必须走到文件系统挂载 + 回读校验,停在「能启动」不够。