Files
STM32F4-Base/docs/问题记录/GD5F2GQ5UE_Trap_Records.md
2026-08-28 22:02:24 +08:00

5.8 KiB
Raw Blame History

GD5F2GQ5UE 陷阱记录

Trap 01 — 块擦除地址错误(有效)

发现时间2026-07-21

现象 gd5f_block_erase() 使用 byte_addr = block × 128KB(字节地址)作为 D8h 命令的入参,但 GD5F2GQ5UE 要求的是页地址block × 64)。

只有 block 0 的 byte_addr=0 与 page_addr=0 巧合一致,其余块全部指向了错误的物理块。导致:

  • 验证流程从未擦除过大部分块,厂标坏块每次重启都恢复
  • 基于错误擦除的实验结论全部无效

根因

  • gd5f_block_erase(block_addr)block_addr × GD5F_BLOCK_SIZE 计算地址
  • 芯片 D8h 命令将 24-bit 地址作为页地址(行地址)解析:RA<16:6> 选块,RA<5:0> 选页内页
  • 对于 block=1发送 {0xD8, 0x00, 0x02, 0x00} → 芯片视为 page=512 → 擦除 block=8
  • 导致 block 17、915、17~23…等大量块从未被擦除

修复:改用 page_addr = block_addr × GD5F_PAGES_PER_BLOCK(块首页地址)

验证结果(修复后):

  • 全部 2048 块正确擦除,厂标全部清除,重启后 BBT scan 为 0
  • ECC 开启时 BBT rescan 正常工作
  • ECC 校验码正确生成ECCS=0
  • Block 0 无特殊行为

经验教训

  • D8h 和 13h/10h 都使用 24-bit 行地址,格式一致
  • 块擦除不使用 block × block_size(字节偏移),而用块首页地址
  • 芯片手册的 memory mapping 必须严格遵守

Trap 02 — 测试缓冲越界导致跨页比对误判(有效)

发现时间2026-08-26

现象 gd5f_test_task.cs_wbuf / s_rbuf 按单页大小定义:

static uint8_t s_wbuf[GD5F_PAGE_SIZE];   /* 2048 字节 */
static uint8_t s_rbuf[GD5F_PAGE_SIZE];   /* 2048 字节 */

TC-GD5F-302 跨页写读用 gd5f2gq5ue_write(base + 1000, s_wbuf, 3000) / read(..., s_rbuf, 3000)。驱动实际把 3000 字节正确写入了 NAND但测试缓冲只有 2048 字节,发生数组越界

  • 前 2048 字节写入合法区page0 全 + page1 前 1000 字节)→ 比对正确;
  • page1 剩余 952 字节被写到 s_rbuf[2048..2999](越界),落入相邻内存,丢失
  • 比对时读 s_rbuf[2048..2999] 为越界垃圾 → 误判 cross-page MISMATCH

根因 测试缓冲未覆盖实际最大读写长度(跨页场景超过单页 2048 字节)。

修复 将缓冲放大到 ≥ 最大读写长度(如 GD5F_PAGE_SIZE * 2,即 4096

static uint8_t s_wbuf[GD5F_PAGE_SIZE * 2];
static uint8_t s_rbuf[GD5F_PAGE_SIZE * 2];

验证结果 缓冲放大后 TC-GD5F-302 一次通过,不再误判。

经验教训

  • 任何“跨页 / 大于单页”的读写测试,缓冲必须按实际长度上限而非单页大小分配
  • 越界写会污染相邻全局变量,可能引起其它用例莫名失败,定位时优先怀疑缓冲尺寸

Trap 03 — 驱动读写擦缺越界检查(有效 / 健壮性缺口)

发现时间2026-08-26

现象 TC-GD5F-1001 用 offset = GD5F_TOTAL_SIZE(恰好超出末尾 1 字节)做 read/write 期望驱动拒绝(返回错误),但 gd5f2gq5ue_read / write / erase 原实现无任何边界校验 直接计算 page_addr = offset / PAGE_SIZE 后继续操作,返回 GD5F_OK → 测试 FAIL。 同时这也意味着正常调用若传入越界参数,会静默访问到回绕后的非法页。

根因 gd5f2gq5ue_read / write / erase 入口未校验 offset / size 是否在 [0, GD5F_TOTAL_SIZE] 容量范围内。

修复 在三个函数入口统一加边界检查FTL/diskio 均在容量内访问,不受影响):

if (offset < 0 || (unsigned long)offset + size > (unsigned long)GD5F_TOTAL_SIZE) {
    return GD5F_ERROR;
}

erase 在原有的块对齐检查之后、循环之前加同一判断即可。

验证结果

  • TC-GD5F-1001 read/write 越界均被拒绝ret=-1测试 PASS
  • TC-GD5F-1002 size=0 仍返回 GD5F_OKoffset 合法且 size=0 不越界),符合预期
  • 该检查还顺带逮到 Trap 04 的测试越界擦除(见下)

经验教训

  • 面向字节偏移的裸 NAND 接口必须做容量边界检查,调用方传错参数时尽早失败
  • 无越界检查的驱动容易“静默回绕”,问题极难定位

Trap 04 — gd5f_find_run 返回语义与多块擦除窗口错位(测试 bug

发现时间2026-08-26

现象 TC-GD5F-402 多块擦除(gd5f_find_run(3) 后擦 3 块)返回 ret=-1。 经 Trap 03 的边界检查定位:实际要擦的块超出了设备末尾。

根因 gd5f_find_run(n) 原实现返回的是连续好块的末尾块号 top (校验 top, top-1, ..., top-(n-1) 共 n 块),但调用方把返回值当作起始块号使用:

base3 = (long)blk3 * GD5F_BLOCK_SIZE;
gd5f2gq5ue_erase(base3, 3 * GD5F_BLOCK_SIZE);  /* 实际擦 blk3, blk3+1, blk3+2 */

gd5f_find_run(3) 返回的末尾块贴到设备末尾(例如 2047即块 2045/2046/2047 为好块) 时,调用方实际要擦 2047, 2048, 2049,块 2048/2049 越界。 n=1 时首尾块号相同所以不暴露,仅 n>1 暴露。

修复 统一让 gd5f_find_run 返回起始块号,与 n=1 的用法一致,并仍避开块 0

if (ok) {
    return top - (uint32_t)(n - 1);  /* 返回连续好块的起始块号 */
}

循环条件保持 top >= (uint32_t)(n - 1) + 1,保证起始块 ≥ 1。

验证结果 修复后 TC-GD5F-402 多块擦除ret=0及“擦后全 0xFF”均 PASS 全套用例最终 60/60 PASSED。

经验教训

  • 返回“连续区间”的辅助函数,其返回值是起点还是终点必须在注释/命名中明确, 调用方与实现必须一致
  • 这类错位 bug 往往只在“区间贴到设备边界”时才触发,正常情况能过,建议测试覆盖边界块