# 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 1~7、9~15、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.c` 中 `s_wbuf` / `s_rbuf` 按单页大小定义：
```c
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）：
```c
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 均在容量内访问，不受影响）：
```c
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_OK`（offset 合法且 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 块），但调用方把返回值当作**起始块号**使用：
```c
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：
```c
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 往往只在“区间贴到设备边界”时才触发，正常情况能过，建议测试覆盖边界块

