Files
wandering1 4a4ad6bc5f chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名,
不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体
run_boot_test.py 的真实调用。

- AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改
  「编译(/keil-build)」「串口回归(/serial-boot-test)」
- keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree
  方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围)
- 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入
  等 41 行历史证据保留旧名
- gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑
- 顺手补并行方案同行漏网计数 51→52 钩子

验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all
rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52
钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
2026-10-30 13:03:12 +08:00

821 lines
38 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GD5F2GQ5UE NAND Flash 测试规范
> 本规范参照 `docs/测试说明/板级测试/CH395F_Test_Guide.md` 结构编写,覆盖 `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c`
> 全部公开/私有函数,并向上延伸到 FTL(`nand_ftl.c`)与 FatFS diskio。
> 详细芯片行为见 `docs/芯片手册/GD5F2GQ5UExxG.md`,已知陷阱见 `docs/问题记录/GD5F2GQ5UE_Trap_Records.md`。
>
> **2026-10-03 错误通道硬化**(见 `问题记录/GD5F2GQ5UE_Trap_Records.md` TRAP-GD5F-06):
> `wait_busy` 区分"超时"(`GD5F_WAIT_EXPIRED`,旧名 `GD5F_BUSY_TIMEOUT`)与"真故障"(`GD5F_ERROR`)、`bbt_scan` 失败**放弃持久化**、
> init 全链路检查返回值 + **回读 B0h 校验 ECC 使能**、`read` 的 raw 契约文档化、公开 API 统一 `int32_t`。
> 套件回归 `=== GD5F2GQ5UE Tests: 16/16 PASSED (failed=0) ===`。
> ⚠ **编排器注意**:`TC-GD5F-704` 会**预期**打出 `[ERR][NAND] RX DMA buffer not DMA-reachable`(回落轮询,用例 PASS)——
> 用 `run_all_suites.py` 跑本套件时 `--forbidden` 须去掉 `[ERR]`(脚本已按套件覆盖,见其 `SUITES` 注释)。
## 1. 概述
### 设计原则
- **同步 SPI 访问**:GD5F 驱动全部为**同步阻塞** SPI 事务(CS 拉低 → 命令/地址/数据 → CS 拉高),
由调用任务直接执行,**不经过消息队列**(与 CH395F 的 netTask 串行化模型不同)。
因此测试代码可在任意任务上下文调用,但要注意调用任务会被 SPI 传输阻塞数毫秒(擦除最久)。
- **先擦后写**:NAND 只能将 1 写为 0,**写操作前目标块必须已擦除**,否则数据不可预期(见边界条件 TC-GD5F-1003)。
- **ECC 默认开启**:`gd5f2gq5ue_init()` 会置 `Feature(B0h).ECC_EN=1`,读/写均走内部 ECC。
- **坏块以 BBT 管理**:出厂坏块标记在每块的第一页(page 0)spare byte 0(列地址 0x800,见 `docs/芯片手册/GD5F2GQ5UExxG.md` §12.4 / Table 12-6 / Note);`init` 阶段扫描构建 RAM 中的 BBT。
### 芯片参数(关键参数)
| 参数 | 值 | 说明 |
|------|-----|------|
| 制造商/设备 ID | `0xC8` / `0x52` | `gd5f2gq5ue_read_id()` 校验依据 |
| 页数据大小 | 2048 B | `GD5F_PAGE_SIZE` |
| Spare 大小 | 64 B | `GD5F_SPARE_SIZE`(ECC 开启时有效) |
| 单页总长 | 2112 B | `GD5F_TOTAL_PAGE_SIZE`(ECC 开启) |
| 每块页数 | 64 | `GD5F_PAGES_PER_BLOCK` |
| 块大小 | 128 KB | `GD5F_BLOCK_SIZE = 64×2048` |
| 总块数 | 2048 | `GD5F_TOTAL_BLOCKS` |
| 总容量 | 256 MB | `GD5F_TOTAL_SIZE` |
| 最小擦除单位 | 1 块(128 KB) | 不可按页/字节擦除 |
| DMA 阈值 | 1024 B | `GD5F_DMA_THRESHOLD`,>1024B 走 `HAL_SPI_*_DMA`;非 SRAM 缓冲由运行时守卫回落轮询(见阶段 7) |
### 寄存器与状态位
| 寄存器 | 地址 | 关键位 |
|--------|------|--------|
| Status | `0xC0` | OIP(b0) 忙标志、WEL(b1) 写使能、E_FAIL(b2)、P_FAIL(b3)、ECCS1/0(b5/b4) ECC 状态 |
| Feature | `0xB0` | ECC_EN(b4)、QE(b0) |
| Protect | `0xA0` | BP2/1/0 块保护;init 后写为 `0x00` 解除保护 |
ECC 状态(`ECCS1:ECCS0`):`00`=无错;`01/10/11`=纠正 1/2/3 bit;`10`(ECCS1=1,ECCS0=0)= 超出纠正能力(>4bit,不可纠正)。
驱动 `gd5f_check_ecc()` 在 `(status>>4)&0x03 == 2` 时返回 `GD5F_ECC_ERROR`。
### 测试环境
- **硬件**:STM32F407ZGTx + GD5F2GQ5UE(SPI NAND)
- **SPI 接口**:SPI1(CubeMX 初始化),引脚:
- CS=`PE0`,SCK=`PB3`,MISO=`PB4`,MOSI=`PB5`,WP=`PB8`,HOLD=`PE1`
- **调试输出**:USART1(115200bps),`[NAND]` 标签(`dbg_log.h`)
- **擦除/编程耗时**(参考驱动超时):PAGE_READ 等待 100ms、PROGRAM_EXEC 等待 1000ms、BLOCK_ERASE 等待 5000ms
### 统一测试调度(ch395fTestTask)
所有测试套件(CH395F / 存储 / GD5F)统一收归 `ch395fTestTask` 线程(`test/board/ch395f_test_task.c`
的 `StartCh395fTestTask`),通过 `test/board/test_config.h` 中的 `TEST_SUITE_*` 宏在**编译期**选择要跑的套件。
选中 `TEST_SUITE_GD5F` 时,`test_config.h` 会自动 `#define ENABLE_GD5F_TESTS` 及**只读阶段**宏
(PHASE1/2/10),随后由 `gd5f_test_run()`(`test/board/gd5f_test_task.c`)按阶段串行执行。
> **破坏性用例开关(2026-09-21 新增,默认关闭)**:`test_config.h` 的 `ENABLE_DESTRUCTIVE_TESTS`
> 控制所有**会改变设备持久状态**的用例——写/擦 NAND、`nand_ftl_format`、FatFS 建文件写文件、
> 向 BBT 持久化坏块标记(永久减少可用容量)。关闭时为**只读模式**,可随时在在用设备上跑;
> 打开后才编译并运行 PHASE3~9,以及 PHASE2 的 TC-GD5F-206/207、PHASE10 的 TC-GD5F-1003/1004。
> 依据:2026-09-21 实测一轮破坏性 GD5F 运行即让 dhara 多记一个坏块(容量 95248 → 95200;
> dhara 把该计数持久化在 journal checkpoint 头里,普通恢复模式清不掉——见下文容量恢复说明),
> 故破坏性用例只在**产品首次上电/产线首启**或明确需要时开启。
`test/board/test_config.h` 配置示例(三选一,互斥,多选触发 `#error`):
```c
// #define TEST_SUITE_CH395F
// #define TEST_SUITE_STORAGE
#define TEST_SUITE_GD5F /* 选中后自带 gd5f 只读阶段宏 */
// #define ENABLE_DESTRUCTIVE_TESTS /* 需写/擦/格式化/落盘坏块时才打开(默认关闭) */
```
执行顺序(由 `gd5f_test_run()` 按阶段串行,与函数依赖一致):
- **只读(默认)**:`init/read_id/reset` → `BBT(读取与 RAM 内标记)` → `DMA 守卫读方向(TC-GD5F-704)` → `边界条件(越界 / size=0)`
- **破坏性(开关打开时插入其间)**:`页读写` → `块擦除` → `ECC` → `SPI 原语` → `DMA 边界(701~703、705)` → `FTL(含 format)` → `FatFS`
开机横幅会明确打印本次模式:`mode: READ-ONLY (...)` 或 `mode: DESTRUCTIVE (...)`。
> `defaultTask` **不再承担任何测试**(仅空闲);所有测试都只在 `ch395fTestTask` 中运行。
### 启用测试
1. 在 `test/board/test_config.h` 取消注释所需的 `TEST_SUITE_*` 宏(如 `TEST_SUITE_GD5F`),
需要写/擦类用例时再打开 `ENABLE_DESTRUCTIVE_TESTS`;
2. 编译(`/keil-build`)确认 0 错误 0 警告;
3. 烧录后串口观察 `mode: ...` 横幅、`[NAND-TEST]` 日志与各 TC 的 `PASS/FAIL`,结尾打印
`=== GD5F2GQ5UE Tests: X/Y PASSED ===`;
4. 不选任何 `TEST_SUITE_*` 时为正常产品构建(`ch395fTestTask` 空闲,不跑测试)。
---
## 2. 阶段 1:初始化与 ID 识别
### TC-GD5F-101: gd5f2gq5ue_init 全流程
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-101 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | 初始化成功(复位→ID 校验→BBT 扫描→ECC 使能→解除块保护) |
| **前置条件** | SPI1 与 GPIO 已由 CubeMX 初始化 |
| **测试步骤** | 1. 调用 `gd5f2gq5ue_init()`<br>2. 观察日志:Resetting / Reading NAND ID / Scanning bad blocks / Enabling ECC / Unlocking block protection / NAND init OK |
| **通过标准** | 返回 `GD5F_OK`;日志无 `ID mismatch` / `Reset failed`;BBT 扫描打印 bad count |
| **覆盖原则** | 初始化主路径 |
### TC-GD5F-102: gd5f2gq5ue_read_id 返回正确 ID
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-102 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | 读取 MID=0xC8、DID=0x52 |
| **前置条件** | `gd5f2gq5ue_init()` 已成功(或至少 SPI 已初始化) |
| **测试步骤** | 调用 `gd5f2gq5ue_read_id(&mid, &did)` 并比较 |
| **通过标准** | `mid==0xC8 && did==0x52` |
| **覆盖原则** | `gd5f2gq5ue_read_id` |
### TC-GD5F-103: gd5f2gq5ue_reset 成功
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-103 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | 软复位后状态寄存器 OIP 清零 |
| **前置条件** | SPI 已初始化 |
| **测试步骤** | 调用 `gd5f2gq5ue_reset()`;随后 `gd5f_read_status(&s)` 检查 `OIP==0` |
| **通过标准** | `reset` 返回 `GD5F_OK` 且 `s & GD5F_STATUS_OIP == 0` |
| **覆盖原则** | `gd5f2gq5ue_reset` / `gd5f_read_status` |
---
## 3. 阶段 2:坏块管理(BBT)
> 注:`gd5f2gq5ue_mark_block_bad()` / `gd5f2gq5ue_bbt_clear()` **仅修改 RAM 中的 BBT 位图**,
> 不会写回 Flash 物理标记(与头注释"尝试物理标记"不符,见陷阱节)。重启后由 `init` 重新扫描出厂标记。
### TC-GD5F-201: 出厂坏块扫描
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-201 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | init 后 BBT 扫描可完成并打印坏块数 |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | 观察 init 日志 `BBT scan: N bad blocks found`;调用 `gd5f2gq5ue_print_bbt()` |
| **通过标准** | 扫描完成(N 为合理值,通常 0~数十);`print_bbt` 能列出坏块偏移 |
| **覆盖原则** | BBT 扫描(`gd5f_private_bbt_scan`) |
### TC-GD5F-202: gd5f2gq5ue_is_block_bad 查询
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-202 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | 好块返回 0,坏块返回 1 |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | 1. 对已知好块(如 block 0,除非其为坏块)`is_block_bad` 应返回 0<br>2. 对 `print_bbt` 列出的坏块 `is_block_bad` 应返回 1 |
| **通过标准** | 好块返回 0,坏块返回 1;越界 block 返回 1 |
| **覆盖原则** | `gd5f2gq5ue_is_block_bad` |
### TC-GD5F-203: mark_block_bad 与 is_block_bad 一致性
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-203 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | 标记后查询一致(RAM BBT) |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | 取一个好块 B;`gd5f2gq5ue_mark_block_bad(B)` 后 `is_block_bad(B)==1`;再 `gd5f2gq5ue_bbt_clear()` 后 `is_block_bad(B)==0` |
| **通过标准** | mark 后查得 1,clear 后查得 0(RAM 行为符合预期) |
| **覆盖原则** | `gd5f2gq5ue_mark_block_bad` / `gd5f2gq5ue_bbt_clear` |
### TC-GD5F-204: 初始化 BBT 稳定读取(无需 rescan)
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-204 |
| **优先级** | P2 |
| **类型** | 一致性测试 |
| **标题** | 初始化 BBT 可直接读取且稳定(无需 rescan) |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | 记录 `print_bbt` 坏块数;再次 `print_bbt` 比较坏块数(验证 init BBT 可直接读取且稳定) |
| **通过标准** | 两次坏块数一致(出厂标记未变) |
| **覆盖原则** | `gd5f2gq5ue_is_block_bad` / `gd5f2gq5ue_bbt_clear` / `gd5f2gq5ue_mark_block_bad`(init BBT 读取) |
---
## 4. 阶段 3:页读写(跨页)
> 重要:写操作前目标区域必须已擦除(见阶段 4 / 边界条件 TC-GD5F-1003)。
### TC-GD5F-301: 整页写读
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-301 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | offset=0, size=2048 写入递增模式并读回比对 |
| **前置条件** | 目标块已擦除 |
| **测试步骤** | 1. 填 `buf[i]=i&0xFF`(或 `(offset+i)&0xFF`)<br>2. `gd5f2gq5ue_write(0, buf, 2048)`<br>3. `gd5f2gq5ue_read(0, rbuf, 2048)` 逐字节比对 |
| **通过标准** | 读回与写入完全一致 |
| **覆盖原则** | `gd5f2gq5ue_write` / `gd5f2gq5ue_read`(单页路径) |
### TC-GD5F-302: 跨页写读
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-302 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | offset=1000, size=3000 跨越页边界 |
| **前置条件** | 涉及块已擦除 |
| **测试步骤** | 写入 3000 字节(offset=1000 起),读回同窗口比对 |
| **通过标准** | 跨页数据连续正确(`gd5f2gq5ue_read/write` 自动分页) |
| **覆盖原则** | 跨页拆分逻辑 |
### TC-GD5F-303: 非对齐 offset 写读
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-303 |
| **优先级** | P1 |
| **类型** | 边界测试 |
| **标题** | offset=1, size=2047 |
| **前置条件** | 目标块已擦除 |
| **测试步骤** | 同 TC-GD5F-301,但 offset=1 |
| **通过标准** | 读回与写入一致(列地址 = offset%2048 正确) |
| **覆盖原则** | 列地址计算 |
### TC-GD5F-304: 多页顺序写读
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-304 |
| **优先级** | P1 |
| **类型** | 压力测试 |
| **标题** | 连续 10 页(offset=0, size=20480) |
| **前置条件** | 前 10 页所在块已擦除 |
| **测试步骤** | 写入 20480 字节递增模式,读回全量比对 |
| **通过标准** | 全部一致 |
| **覆盖原则** | 多页循环 |
### TC-GD5F-305: 随机单字节访问
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-305 |
| **优先级** | P2 |
| **类型** | 功能测试 |
| **标题** | 任意 offset,size=1 写入/读回 |
| **前置条件** | 目标块已擦除 |
| **测试步骤** | 在若干随机 offset 写单字节并读回 |
| **通过标准** | 单字节值正确,且不影响同页其余字节(部分页编程约束内) |
| **覆盖原则** | 单字节 `program_load` 列偏移 |
---
## 5. 阶段 4:块擦除
### TC-GD5F-401: 单块擦除后读全 0xFF
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-401 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | 擦除 block 0(先确认非坏块),读出应全 0xFF |
| **前置条件** | block 0 非坏块 |
| **测试步骤** | `gd5f2gq5ue_erase(0, 128*1024)`;读整块比对全 0xFF |
| **通过标准** | 返回 `GD5F_OK` 且整块读回全 0xFF |
| **覆盖原则** | `gd5f2gq5ue_erase` / `gd5f_block_erase` |
### TC-GD5F-402: 多块擦除
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-402 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | size=3 块(384KB)连续擦除 |
| **前置条件** | 对应块非坏块 |
| **测试步骤** | `gd5f2gq5ue_erase(0, 3*128*1024)`;读回比对全 0xFF |
| **通过标准** | 返回 `GD5F_OK`,范围全 0xFF |
| **覆盖原则** | 多块循环擦除 |
### TC-GD5F-403: 非块对齐应返回错误
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-403 |
| **优先级** | P0 |
| **类型** | 负向测试 |
| **标题** | offset 或 size 非 128KB 整数倍 |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | `gd5f2gq5ue_erase(100, 128*1024)`(offset 不对齐);`gd5f2gq5ue_erase(0, 100)`(size 不对齐) |
| **通过标准** | 两者均返回 `GD5F_ERROR` |
| **覆盖原则** | 对齐校验 |
### TC-GD5F-404: 擦除-写-读 完整循环
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-404 |
| **优先级** | P0 |
| **类型** | 集成测试 |
| **标题** | erase → write → read 闭环比对 |
| **前置条件** | 目标块非坏块 |
| **测试步骤** | 1. `erase` 目标块<br>2. `write` 已知模式(如 0xAA 填充/递增)<br>3. `read` 比对 |
| **通过标准** | 读回 == 写入(验证"先擦后写"链路) |
| **覆盖原则** | 完整 NAND 写流程 |
---
## 6. 阶段 5:ECC 验证
### TC-GD5F-501: ECC 开启下写读正确
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-501 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | ECC 使能时数据正确,ECC status 无错 |
| **前置条件** | `gd5f2gq5ue_init()`(已使能 ECC);目标块已擦除 |
| **测试步骤** | 写随机/递增模式;`gd5f_page_read(page)` + `gd5f_read_from_cache` 读回;`gd5f_check_ecc()` 检查 |
| **通过标准** | 数据一致且 `gd5f_check_ecc()` 返回 `GD5F_OK`(ECCS=00 无错) |
| **覆盖原则** | ECC 数据路径 |
### TC-GD5F-502: gd5f_check_ecc 干净读
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-502 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | 干净页 `gd5f_check_ecc()` 返回 OK |
| **前置条件** | 已 `gd5f_page_read` 加载页 |
| **测试步骤** | 读后调 `gd5f_check_ecc()` |
| **通过标准** | 返回 `GD5F_OK`(非 `GD5F_ECC_ERROR`) |
| **覆盖原则** | `gd5f_check_ecc` |
### TC-GD5F-503: ECC 纠错能力(可选)
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-503 |
| **优先级** | P3 |
| **类型** | 可选/深入测试 |
| **标题** | 观察 ECCS 位在纠正场景下的变化 |
| **前置条件** | 已知好块,ECC 开启 |
| **测试步骤** | 写入后读回,检查 `ECCS` 位(00 无错 / 01~11 已纠正 bit);人为制造位翻转较困难,建议仅读状态位验证逻辑 |
| **通过标准** | `gd5f_check_ecc()` 在不可纠正时返回 `GD5F_ECC_ERROR`,否则 `GD5F_OK` |
| **覆盖原则** | ECC 状态位语义 |
---
## 7. 阶段 6:SPI 原语(FTL 共享)
> 下列函数为 `gd5f2gq5ue.c` 的私有/半公开原语,FTL 直接复用;通过组合调用验证。
### TC-GD5F-601: page_read + read_from_cache 组合
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-601 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | 单页加载到 cache 并读出 |
| **前置条件** | 目标页有已编程数据 |
| **测试步骤** | `gd5f_page_read(page)` → `gd5f_read_from_cache(0, buf, 2048)` |
| **通过标准** | 数据正确,返回 `GD5F_OK` |
| **覆盖原则** | `gd5f_page_read` / `gd5f_read_from_cache` |
### TC-GD5F-602: program_load + program_exec 组合
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-602 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | 单页编程(load→exec) |
| **前置条件** | 目标块已擦除 |
| **测试步骤** | `gd5f_write_enable()` → `gd5f_program_load(0, buf, 2048)` → `gd5f_program_exec(page)`;读回比对 |
| **通过标准** | 编程后数据一致,无 `P_FAIL` |
| **覆盖原则** | `gd5f_program_load` / `gd5f_program_exec` |
### TC-GD5F-603: gd5f_block_erase 单块
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-603 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | 原语级块擦除(地址=块首页地址) |
| **前置条件** | SPI 已初始化 |
| **测试步骤** | `gd5f_block_erase(block)`(注意传**块编号**,内部转块首页地址,见TRAP-GD5F-01) |
| **通过标准** | 返回 `GD5F_OK`,无 `E_FAIL` |
| **覆盖原则** | `gd5f_block_erase`(TRAP-GD5F-01 修复点) |
### TC-GD5F-604: write_enable / read_status(WEL 位)
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-604 |
| **优先级** | P2 |
| **类型** | 功能测试 |
| **标题** | 写使能后 WEL 位置位 |
| **前置条件** | SPI 已初始化 |
| **测试步骤** | `gd5f_write_enable()` → `gd5f_read_status(&s)` 检查 `s & GD5F_STATUS_WEL` |
| **通过标准** | WEL 位为 1 |
| **覆盖原则** | `gd5f_write_enable` / `gd5f_read_status` |
### TC-GD5F-605: gd5f_wait_busy 正常返回
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-605 |
| **优先级** | P2 |
| **类型** | 功能测试 |
| **标题** | 操作完成后 wait_busy 在超时内返回 0 |
| **前置条件** | 执行一次读/写操作后 |
| **测试步骤** | 调 `gd5f_wait_busy(1000)`,检查返回值 |
| **通过标准** | 返回 `GD5F_OK`(OIP 已清零) |
| **覆盖原则** | `gd5f_wait_busy` |
---
## 8. 阶段 7:DMA 边界与 DMA 可达性守卫(阈值 1024 字节)
> 驱动 `GD5F_DMA_THRESHOLD=1024`:`size>1024` 走 `HAL_SPI_*_DMA`,否则轮询。两路径结果必须一致。
> **前置约束**:DMA 源/目的缓冲必须落在 SRAM(`RW_IRAM1`,0x20000000~0x2001FFFF)。CCM(0x10000000)
> 为 DMA1/DMA2 物理不可达区,误用会静默不落地;驱动已加运行时守卫 `gd5f_dma_buf_ok()`,
> 越界缓冲自动回落轮询并打印 `[ERR][NAND] … not DMA-reachable … fallback poll`。
### TC-GD5F-701: 阈值下界(轮询路径)
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-701 |
| **优先级** | P1 |
| **类型** | 边界测试 |
| **标题** | size=1024(= `GD5F_DMA_THRESHOLD`)走非 DMA 路径,读写正确 |
| **前置条件** | 目标块已擦除 |
| **测试步骤** | 写/读 1024 字节并比对 |
| **通过标准** | 数据一致 |
| **覆盖原则** | `GD5F_DMA_THRESHOLD` 下界 |
### TC-GD5F-702: 阈值上界(DMA 路径)
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-702 |
| **优先级** | P1 |
| **类型** | 边界测试 |
| **标题** | size=1025(= `GD5F_DMA_THRESHOLD`+1)走 DMA 路径,读写正确 |
| **前置条件** | 目标块已擦除 |
| **测试步骤** | 写/读 1025 字节并比对 |
| **通过标准** | 数据一致 |
| **覆盖原则** | `GD5F_DMA_THRESHOLD` 上界 |
### TC-GD5F-703: 大块 DMA 一致性
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-703 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | size=2048 整页(DMA 路径)读写正确 |
| **前置条件** | 目标块已擦除 |
| **测试步骤** | 同 TC-GD5F-301,确认 DMA 路径全页正确 |
| **通过标准** | 数据一致 |
| **覆盖原则** | DMA 大数据路径 |
### TC-GD5F-704: DMA 可达性守卫负路径(读方向,只读,两种模式都跑)
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-704 |
| **优先级** | P0 |
| **类型** | 边界/健壮性测试(**只读**,不受 `ENABLE_DESTRUCTIVE_TESTS` 门控) |
| **标题** | 目的缓冲位于 CCM(不可达区)时守卫应拒绝 DMA 并回落轮询,数据仍正确 |
| **前置条件** | FreeRTOS 堆驻留 CCM(scatter `RW_CCM`);探针缓冲由 `pvPortMalloc(2048)` 取得(堆不在 CCM 时用例跳过) |
| **测试步骤** | 1. 先用 SRAM 静态缓冲参考读同页<br>2. 探针缓冲预填参考值的反码<br>3. `gd5f2gq5ue_read()` 整页读入 CCM 探针缓冲<br>4. 与参考缓冲逐字节比对 |
| **通过标准** | 数据与参考完全一致,且串口出现 `[ERR][NAND] RX DMA buffer not DMA-reachable (p=0x1000xxxx size=2048), fallback poll` |
| **判定依据** | 本板实证 DMA 对 CCM 静默不落地:若误走 DMA,探针缓冲保持预填反码必失配——故"数据一致"即证明回落真实发生,而非仅"未报错" |
| **覆盖原则** | DMA 可达性守卫(读方向);堆缓冲禁入 DMA 的运行时强制(内存分配策略 R3) |
### TC-GD5F-705: DMA 可达性守卫负路径(写方向)
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-705 |
| **优先级** | P0 |
| **类型** | 边界/健壮性测试 |
| **标题** | 源缓冲位于 CCM(不可达区)时守卫应拒绝 DMA 并回落轮询,数据仍正确落盘 |
| **前置条件** | 同 TC-GD5F-704;目标块已擦除 |
| **测试步骤** | 1. 探针缓冲填入 `i^0x5A` 图案<br>2. `gd5f2gq5ue_write()` 整页写入<br>3. 读回 SRAM 静态缓冲逐字节比对 |
| **通过标准** | 写读一致,且串口出现 `[ERR][NAND] TX DMA buffer not DMA-reachable … fallback poll` |
| **判定依据** | 同 TC-GD5F-704(写方向 DMA 读 CCM 同样不落地) |
| **覆盖原则** | DMA 可达性守卫(写方向);堆缓冲禁入 DMA 的运行时强制(内存分配策略 R3) |
> ⚠ **卷破坏警告(仅 `ENABLE_DESTRUCTIVE_TESTS` 打开时)**:阶段 8 会调用 `nand_ftl_format()`
> 清空 dhara map,阶段 9 会写入文件——整轮运行后卷数据即失效;大写入还可能使 dhara 多记一个
> 坏块(2026-09-21 实测:一次整轮运行容量 95248 → 95200,日志见
> `[ERR][NAND_FTL] program verify: data mismatch, blk 305`,该块被 dhara 隔离,测试仍 68/68 通过)。
>
> **容量记账与恢复(2026-09-21 读码 + 台架实证)**:
> - 健康满容量 = **96064 pages(187 MB)**(`No valid map found, creating fresh` 分支下的满值)。
> - 容量随 dhara 计到的坏块数递减,且该计数**持久化在 journal checkpoint 头里**
> (`journal.c` `hdr_set_bb_current/bb_last`);`dhara_journal_clear()` 只做
> `bb_last = bb_current`(**不归零**)——所以 `nand_ftl_format()`(TC-STORAGE-04 普通恢复)
> **清不掉被扣掉的容量**,断电重扫也一样。
> - 恢复满容量:启用 `ENABLE_STORAGE_RECOVERY_TESTS` **并叠加** `STORAGE_RECOVERY_FULL_WIPE`
> 跑一次 STORAGE 套件(擦净 dhara 可见区 2044 块 + 重建 BBT),随后断电重启——
> `dhara_map_resume` 失败 → 走 `dhara_map_clear` 全新初始化(bb_last = bb_current = 0)→ 容量回满。
> 实测 2026-09-21:95200 → **96064** ✓(代价:卷内容全失,启动时 FatFs 自动重建)。
## 9. 阶段 8:FTL 层(nand_ftl)
### TC-GD5F-801: nand_ftl_init 成功
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-801 |
| **优先级** | P0 |
| **类型** | 功能测试 |
| **标题** | FTL 初始化(在 driver init 之上建立映射) |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | 调用 `nand_ftl_init()` |
| **通过标准** | 返回 0;日志无异常 |
| **覆盖原则** | `nand_ftl_init` |
### TC-GD5F-802: nand_ftl_format 成功
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-802 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | 格式化后可正常使用 |
| **前置条件** | `nand_ftl_init()` 已成功 |
| **测试步骤** | 调用 `nand_ftl_format()` |
| **通过标准** | 返回 0 |
| **覆盖原则** | `nand_ftl_format` |
---
## 10. 阶段 9:FatFS diskio(可选,待 FTL 完成)
### TC-GD5F-901: 挂载 + 文件写读
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-901 |
| **优先级** | P2 |
| **类型** | 集成测试 |
| **标题** | FatFS 挂载 `/`,写文件后读回比对 |
| **前置条件** | FTL 已 init/format;FatFS diskio 已对接 |
| **测试步骤** | `f_mount` → `f_open` 写若干 KB → `f_close` → `f_open` 读回比对 |
| **通过标准** | 文件内容一致 |
| **覆盖原则** | FatFS diskio 链路 |
---
## 11. 边界条件
### TC-GD5F-1001: offset 越界
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-1001 |
| **优先级** | P1 |
| **类型** | 负向测试 |
| **标题** | offset >= TOTAL_SIZE 应返回错误 |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | `gd5f2gq5ue_read(GD5F_TOTAL_SIZE, buf, 1)` / `write` 同 |
| **通过标准** | 返回错误码(非 GD5F_OK) |
| **覆盖原则** | 边界保护 |
### TC-GD5F-1002: size=0
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-1002 |
| **优先级** | P2 |
| **类型** | 负向测试 |
| **标题** | 零长度读写不崩溃 |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | `gd5f2gq5ue_read(0, buf, 0)` / `write(0, buf, 0)` |
| **通过标准** | 返回 GD5F_OK 或合理错误,无 HardFault |
| **覆盖原则** | 零长度处理 |
### TC-GD5F-1003: 未擦除区域写入
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-1003 |
| **优先级** | P0 |
| **类型** | 注意事项/负向 |
| **标题** | 写前未擦除 → 数据不可预期 |
| **前置条件** | 目标块含旧数据(未擦除) |
| **测试步骤** | 直接 `write` 新数据并 `read` 比对 |
| **通过标准** | **不通过比对**(验证 NAND "先擦后写" 特性;测试本身用于确认驱动不会误报成功) |
| **覆盖原则** | NAND 写约束 |
### TC-GD5F-1004: 写保护解除验证
| 字段 | 值 |
|------|-----|
| **ID** | TC-GD5F-1004 |
| **优先级** | P1 |
| **类型** | 功能测试 |
| **标题** | init 后 Protect 寄存器为 0x00,可写 |
| **前置条件** | `gd5f2gq5ue_init()` 已成功 |
| **测试步骤** | `gd5f_private_set_feature` 读 Protect(0xA0);或间接由"可正常 program"推断 |
| **通过标准** | 块保护已解除,编程不返回 `GD5F_PROGRAM_FAIL` |
| **覆盖原则** | 块保护解除 |
---
---
## 12. 存储套件测试(TEST_SUITE_STORAGE / storage_test_run)
> 本套件独立于 GD5F 驱动套件,由 `test_config.h` 的 `TEST_SUITE_STORAGE` 选择,
> 入口 `storage_test_run()`(`test/board/storage_test_task.c`),统一在 `ch395fTestTask` 中调度。
> 当前 `storage_test_run()` 已覆盖 mkfs/mount/文件写读/性能(与阶段 9 FatFs 同类路径),
> 并新增下列三项**集成测试**(TC-STORAGE-01~03),覆盖 FTL journal 掉电恢复、大文件/随机访问、坏块注入下的存储可用性。
> 三项均已于 STORAGE 套件实测通过(`=== Storage Tests: 11/11 PASSED (failed=0) ===`)。
>
> **已知陷阱(已修复)**:FTL 页缓存用 `s_cached_lpn = 0` 表示"缓存未加载",但 LPN 0 是合法页(FAT 引导扇区所在页)。
> resume/重新初始化后 `s_cached_lpn` 被复位为 0,导致首次 `f_mount` 读 LBA0 时误判缓存命中、返回 `memset` 的全 0 缓存,
> 引导扇区签名变成 `0000`,FatFs 报 `FR_NO_FILESYSTEM`。正常启动因 `f_mkfs` 兜底分支掩盖了此问题(第二次挂载才真正读盘)。
> 修复:将"缓存空"哨兵改为非法值 `(dhara_sector_t)-1`(0xFFFFFFFF,远超实际容量),涉及 `disk_initialize` / `nand_ftl_deinit` / `nand_ftl_format`。
### TC-STORAGE-01: FTL 掉电恢复(journal 持久化)
| 字段 | 值 |
|------|-----|
| **ID** | TC-STORAGE-01 |
| **优先级** | P1 |
| **类型** | 集成/可靠性测试 |
| **标题** | 写入文件并同步后,可经 `dhara_map_resume` 恢复映射,文件不丢 |
| **前置条件** | `gd5f2gq5ue_init()` + `nand_ftl_init()` 已成功;FatFS 可挂载 |
| **测试步骤** | 1. `f_mount` → 写已知内容文件 `pl.dat`(4KB 位置相关模式)→ `f_sync`/`f_close` 落盘<br>2. 模拟掉电:`f_mount("",0)` 卸载 → `nand_ftl_deinit()` 复位 FTL → `nand_ftl_init()` 重新走 `dhara_map_resume`<br>3. `f_mount("",1)` 重新挂载 → 打开 `pl.dat` 读回比对 |
| **通过标准** | 掉电(模拟)前后文件内容逐字节一致;resume 后映射有效(无异常) |
| **覆盖原则** | `disk_initialize` / `dhara_map_resume` / FatFS 掉电安全 |
| **依赖** | `nand_ftl_deinit()`(清 `s_initialized`/`s_cached_lpn`,使下次 init 真正重新 resume) |
| **实测** | PASS(STORAGE 套件:resume 后 `pl.dat` 内容逐字节一致;修复 `s_cached_lpn` 哨兵 bug 后通过) |
### TC-STORAGE-02: 大文件 / 多扇区 / 随机 seek
| 字段 | 值 |
|------|-----|
| **ID** | TC-STORAGE-02 |
| **优先级** | P1 |
| **类型** | 功能/压力测试 |
| **标题** | 256KB 文件顺序写读 + 随机偏移 seek 读一致性 |
| **前置条件** | FTL 已初始化、FatFS 已挂载 |
| **测试步骤** | 1. 写 256KB 文件 `big.dat`,每字节按位置相关模式填充(便于任意偏移校验)<br>2. 顺序读回全量比对<br>3. `f_lseek` 到若干随机偏移(如 0/33KB/128KB/200KB/末段)读小块比对 |
| **通过标准** | 顺序与全部随机偏移读回内容均一致(验证 FTL 大范围映射 + GC + FatFS 随机访问) |
| **覆盖原则** | FTL 大范围 LBA 映射 / 垃圾回收 / FatFs `f_lseek` + `f_read` |
| **实测** | PASS(STORAGE 套件:256KB 顺序 + 随机 seek 读回一致) |
### TC-STORAGE-03: 坏块注入下存储写入
| 字段 | 值 |
|------|-----|
| **ID** | TC-STORAGE-03 |
| **优先级** | P1 |
| **类型** | 负向/健壮性测试 |
| **标题** | 运行时注入坏块后,FatFS 仍可正常写读且坏块未被占用 |
| **前置条件** | `gd5f2gq5ue_init()` 成功;FTL 已复位 |
| **测试步骤** | 1. `nand_ftl_format()` 清空旧 map → 选若干出厂好块 `gd5f2gq5ue_mark_block_bad()` 注入(更新并持久化 BBT)<br>2. `nand_ftl_deinit()` + `nand_ftl_init()` 使 dhara 经 `is_block_bad` 看到新坏块<br>3. `f_mkfs` 重建文件系统 → 正常文件写读 `bbt.dat` 并校验<br>4. 校验注入坏块仍 `gd5f2gq5ue_is_block_bad()==1` |
| **通过标准** | 文件内容完整;注入坏块未被 dhara 分配使用(仍报告坏),存储可用 |
| **注意事项** | 本例会**永久**将若干好块标记为坏并持久化(测试设备专用);如需恢复,运行 TC-STORAGE-04(启用 `ENABLE_STORAGE_RECOVERY_TESTS` 编译 STORAGE 套件一次)即可重建 BBT + 重新格式化 |
| **实测** | PASS(STORAGE 套件:注入 3 坏块后文件完整且坏块仍报告坏) |
### TC-STORAGE-04: 重建 BBT + 重新格式化(恢复)
| 字段 | 值 |
|------|-----|
| **ID** | TC-STORAGE-04 |
| **优先级** | P1 |
| **类型** | 恢复/清理测试 |
| **标题** | 重建 BBT(重新扫描出厂坏块并持久化)并清空 dhara map,恢复设备干净态 |
| **前置条件** | `gd5f2gq5ue_init()` 成功;需启用 `ENABLE_STORAGE_RECOVERY_TESTS` 编译 |
| **测试步骤** | 1. `gd5f2gq5ue_bbt_rebuild()`(关 ECC → 重新扫描出厂坏块 → 开 ECC → `gd5f_bbt_save` 持久化,覆盖注入坏块)<br>2. `nand_ftl_format()` 清空 dhara map<br>3. `gd5f_bbt_dump_flash` 校验持久化 BBT 版本 ≥1 |
| **通过标准** | 重建返回 OK 且持久化 BBT 版本 ≥1;设备坏块回到出厂集合,注入坏块被清除 |
| **注意事项** | 运行一次即恢复;恢复后建议断电重启让 init 重新加载干净 BBT。本 TC 默认关闭,避免常规测试每次重置设备 |
| **依赖** | `gd5f2gq5ue_bbt_rebuild`(详见 §3 坏块管理与 BBT 持久化) |
---
## 附录 B:通过/失败汇总
| TC-ID | 标题 | 优先级 | 实测 |
|-------|------|--------|------|
| TC-GD5F-101 | init 全流程 | P0 | PASS |
| TC-GD5F-102 | read_id 正确 ID | P0 | PASS |
| TC-GD5F-103 | reset 成功 | P1 | PASS |
| TC-GD5F-201 | 出厂坏块扫描 | P0 | PASS |
| TC-GD5F-202 | is_block_bad 查询 | P0 | PASS |
| TC-GD5F-203 | mark/clear 一致性 | P1 | PASS |
| TC-GD5F-204 | init BBT 稳定(无需 rescan) | P2 | PASS |
| TC-GD5F-205 | 可用块数 < 总块数(保留池生效) | P1 | PASS |
| TC-GD5F-206 | RAM 标记→闪存持久化(bit+version) | P1 | PASS |
| TC-GD5F-207 | 清 RAM→reload 恢复坏块 | P1 | PASS |
| TC-GD5F-301 | 整页写读 | P0 | PASS |
| TC-GD5F-302 | 跨页写读 | P0 | PASS |
| TC-GD5F-303 | 非对齐 offset | P1 | PASS |
| TC-GD5F-304 | 多页顺序写读 | P1 | PASS |
| TC-GD5F-305 | 随机单字节访问 | P2 | PASS |
| TC-GD5F-401 | 单块擦除读 0xFF | P0 | PASS |
| TC-GD5F-402 | 多块擦除 | P1 | PASS |
| TC-GD5F-403 | 非块对齐返回错误 | P0 | PASS |
| TC-GD5F-404 | 擦-写-读 闭环 | P0 | PASS |
| TC-GD5F-501 | ECC 开启写读 | P0 | PASS |
| TC-GD5F-502 | check_ecc 干净读 | P1 | PASS |
| TC-GD5F-503 | ECC 纠错(可选) | P3 | 未实现(可选) |
| TC-GD5F-601 | page_read+cache 组合 | P1 | PASS |
| TC-GD5F-602 | program_load+exec 组合 | P1 | PASS |
| TC-GD5F-603 | block_erase 原语 | P1 | PASS |
| TC-GD5F-604 | write_enable WEL 位 | P2 | PASS |
| TC-GD5F-605 | wait_busy 正常返回 | P2 | PASS |
| TC-GD5F-701 | DMA 阈值下界(1024) | P1 | PASS |
| TC-GD5F-702 | DMA 阈值上界(1025) | P1 | PASS |
| TC-GD5F-703 | 大块 DMA 一致性 | P1 | PASS |
| TC-GD5F-704 | DMA 守卫负路径(读,CCM) | P0 | PASS |
| TC-GD5F-705 | DMA 守卫负路径(写,CCM) | P0 | PASS |
| TC-GD5F-801 | nand_ftl_init | P0 | PASS |
| TC-GD5F-802 | nand_ftl_format | P1 | PASS |
| TC-GD5F-901 | FatFS 挂载写读 | P2 | PASS |
| TC-GD5F-1001 | offset 越界 | P1 | PASS |
| TC-GD5F-1002 | size=0 | P2 | PASS |
| TC-GD5F-1003 | 未擦除写入 | P0 | PASS |
| TC-GD5F-1004 | 写保护解除 | P1 | PASS |
| TC-STORAGE-01 | FTL 掉电恢复(journal) | P1 | PASS |
| TC-STORAGE-02 | 大文件/随机 seek | P1 | PASS |
| TC-STORAGE-03 | 坏块注入下存储写入 | P1 | PASS |
| TC-STORAGE-04 | BBT 重建 + 重新格式化(恢复) | P1 | 受 `ENABLE_STORAGE_RECOVERY_TESTS` 控制(默认关) |
> 当前实测(2026-09-21,`ENABLE_DESTRUCTIVE_TESTS` 关闭 / 打开两种配置):
> 只读模式 `=== GD5F2GQ5UE Tests: 16/16 PASSED (failed=0) ===`(PHASE1/2/7 那批只读阶段用例,含 704 那条只读守卫项;无任何写操作,
> 卷内容与容量均不变);破坏性模式 `=== GD5F2GQ5UE Tests: 68/68 PASSED (failed=0) ===`(全部阶段)。
> `TEST_SUITE_STORAGE` 只读模式实测 `=== Storage Tests: 3/3 PASSED (failed=0) ===`(mount + TC-STORAGE-05,
> 卷内容与容量不变);破坏性模式为全量集成用例(2026-09-20 实测 **20/20 PASSED**,含 TC-STORAGE-01/02/03/05/06)。
> 各套件在两种模式下均 0 错误 0 警告通过 MDK 编译。
>
> **破坏性用例清单**(仅在 `ENABLE_DESTRUCTIVE_TESTS` 打开时编译运行):
> GD5F:TC-GD5F-206/207、301~305、401~404、501~502、601~605、701~703、705、801~802、901、1003~1004
> (TC-GD5F-704 为**只读**,两次运行都跑);存储:TC-STORAGE-01、02、03、06 与内建文件系统写读/
> 性能压测(TC-STORAGE-05 为只读,始终运行);RTC:TC-RTC-002(写时间,跑完复原)、014(写 SRAM)、015(写报警)。
---
## 附录 C:驱动函数覆盖
| 函数 | 覆盖 TC |
|------|---------|
| `gd5f2gq5ue_init` | TC-GD5F-101 |
| `gd5f2gq5ue_read_id` | TC-GD5F-102 |
| `gd5f2gq5ue_reset` | TC-GD5F-103 |
| `gd5f2gq5ue_read` | TC-GD5F-301/302/303/304/305 |
| `gd5f2gq5ue_write` | TC-GD5F-301/302/303/304/305 |
| `gd5f2gq5ue_erase` | TC-GD5F-401/402/403/404 |
| `gd5f2gq5ue_is_block_bad` | TC-GD5F-202 |
| `gd5f2gq5ue_mark_block_bad` | TC-GD5F-203 |
| `gd5f2gq5ue_bbt_clear` | TC-GD5F-203 |
| `gd5f2gq5ue_is_block_bad` / 初始化 BBT | TC-GD5F-204 |
| `gd5f2gq5ue_print_bbt` | TC-GD5F-201 |
| `gd5f_wait_busy` | TC-GD5F-605 |
| `gd5f_write_enable` | TC-GD5F-604 |
| `gd5f_read_status` | TC-GD5F-103/604 |
| `gd5f_page_read` | TC-GD5F-601 |
| `gd5f_read_from_cache` | TC-GD5F-601 |
| `gd5f_program_load` | TC-GD5F-602 |
| `gd5f_program_exec` | TC-GD5F-602 |
| `gd5f_block_erase` | TC-GD5F-603 |
| `gd5f_check_ecc` | TC-GD5F-501/502 |
| `nand_ftl_init` | TC-GD5F-801 |
| `nand_ftl_format` | TC-GD5F-802 |
| `nand_ftl_deinit` | TC-STORAGE-01 / TC-STORAGE-03 |
| `gd5f2gq5ue_bbt_rebuild` | TC-STORAGE-04 |
| `storage_test_run`(存储套件) | TC-STORAGE-01/02/03/04 |
---
## 附录 D:参数速查
| 项 | 值 |
|----|-----|
| 页数据/Spare/总长 | 2048 / 64 / 2112 B |
| 每块页数 / 块大小 | 64 / 128 KB |
| 总块数 / 容量 | 2048 / 256 MB |
| ID | MID=0xC8, DID=0x52 |
| 擦除最小单位 | 1 块(128 KB,须对齐) |
| DMA 阈值 | 1024 B(>1024 走 DMA;非 SRAM 缓冲由守卫回落轮询) |
| 状态寄存器 | 0xC0;OIP=0, WEL=1, E_FAIL=2, P_FAIL=3, ECCS=4/5 |
| Feature 寄存器 | 0xB0;ECC_EN=4, QE=0 |
| Protect 寄存器 | 0xA0;init 后写 0x00 解除保护 |
| SPI 引脚 | CS=PE0, SCK=PB3, MISO=PB4, MOSI=PB5, WP=PB8, HOLD=PE1 |