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。
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,见手册 §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 阈值 |
32 B |
GD5F_DMA_THRESHOLD,>32B 走 HAL_SPI_*_DMA |
寄存器与状态位
| 寄存器 |
地址 |
关键位 |
| 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/ch395f_test_task.c
的 StartCh395fTestTask),通过 test/test_config.h 中的 TEST_SUITE_* 宏在编译期选择要跑的套件。
选中 TEST_SUITE_GD5F 时,test_config.h 会自动 #define ENABLE_GD5F_TESTS 及全部
ENABLE_GD5F_PHASE*_TESTS,随后由 gd5f_test_run()(test/gd5f_test_task.c)按阶段串行执行。
defaultTask 不再承担任何测试(仅空闲);所有测试都只在 ch395fTestTask 中运行。
test/test_config.h 配置示例(三选一,互斥,多选触发 #error):
执行顺序(由 gd5f_test_run() 按阶段串行,与函数依赖一致):
init/read_id/reset → BBT → 页读写 → 块擦除 → ECC → SPI 原语 → DMA 边界 → FTL → FatFS → 边界条件。
启用测试
- 在
test/test_config.h 取消注释所需的 TEST_SUITE_* 宏(如 TEST_SUITE_GD5F);
- 编译(
@build)确认 0 错误 0 警告;
- 烧录后串口观察
[NAND-TEST] 日志与各 TC 的 PASS/FAIL,结尾打印
=== GD5F2GQ5UE Tests: X/Y PASSED ===;
- 不选任何
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() 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 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) 2. gd5f2gq5ue_write(0, buf, 2048) 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 目标块 2. write 已知模式(如 0xAA 填充/递增) 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)(注意传块编号,内部转块首页地址,见陷阱 01) |
| 通过标准 |
返回 GD5F_OK,无 E_FAIL |
| 覆盖原则 |
gd5f_block_erase(Trap 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 边界(阈值 32 字节)
驱动 GD5F_DMA_THRESHOLD=32:size>32 走 HAL_SPI_*_DMA,否则轮询。两路径结果必须一致。
TC-GD5F-701: 阈值下界(轮询路径)
| 字段 |
值 |
| ID |
TC-GD5F-701 |
| 优先级 |
P1 |
| 类型 |
边界测试 |
| 标题 |
size=32 走非 DMA 路径,读写正确 |
| 前置条件 |
目标块已擦除 |
| 测试步骤 |
写/读 32 字节并比对 |
| 通过标准 |
数据一致 |
| 覆盖原则 |
GD5F_DMA_THRESHOLD 下界 |
TC-GD5F-702: 阈值上界(DMA 路径)
| 字段 |
值 |
| ID |
TC-GD5F-702 |
| 优先级 |
P1 |
| 类型 |
边界测试 |
| 标题 |
size=33 走 DMA 路径,读写正确 |
| 前置条件 |
目标块已擦除 |
| 测试步骤 |
写/读 33 字节并比对 |
| 通过标准 |
数据一致 |
| 覆盖原则 |
GD5F_DMA_THRESHOLD 上界 |
TC-GD5F-703: 大块 DMA 一致性
| 字段 |
值 |
| ID |
TC-GD5F-703 |
| 优先级 |
P1 |
| 类型 |
功能测试 |
| 标题 |
size=2048 整页(DMA 路径)读写正确 |
| 前置条件 |
目标块已擦除 |
| 测试步骤 |
同 TC-GD5F-301,确认 DMA 路径全页正确 |
| 通过标准 |
数据一致 |
| 覆盖原则 |
DMA 大数据路径 |
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/storage_test_task.c),统一在 ch395fTestTask 中调度。
当前 storage_test_run() 已覆盖 mkfs/mount/文件写读/性能(与阶段 9 FatFs 同类路径),
并新增下列三项集成测试(TC-STO-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-STO-01: FTL 掉电恢复(journal 持久化)
| 字段 |
值 |
| ID |
TC-STO-01 |
| 优先级 |
P1 |
| 类型 |
集成/可靠性测试 |
| 标题 |
写入文件并同步后,可经 dhara_map_resume 恢复映射,文件不丢 |
| 前置条件 |
gd5f2gq5ue_init() + nand_ftl_init() 已成功;FatFS 可挂载 |
| 测试步骤 |
1. f_mount → 写已知内容文件 pl.dat(4KB 位置相关模式)→ f_sync/f_close 落盘 2. 模拟掉电:f_mount("",0) 卸载 → nand_ftl_deinit() 复位 FTL → nand_ftl_init() 重新走 dhara_map_resume 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-STO-02: 大文件 / 多扇区 / 随机 seek
| 字段 |
值 |
| ID |
TC-STO-02 |
| 优先级 |
P1 |
| 类型 |
功能/压力测试 |
| 标题 |
256KB 文件顺序写读 + 随机偏移 seek 读一致性 |
| 前置条件 |
FTL 已初始化、FatFS 已挂载 |
| 测试步骤 |
1. 写 256KB 文件 big.dat,每字节按位置相关模式填充(便于任意偏移校验) 2. 顺序读回全量比对 3. f_lseek 到若干随机偏移(如 0/33KB/128KB/200KB/末段)读小块比对 |
| 通过标准 |
顺序与全部随机偏移读回内容均一致(验证 FTL 大范围映射 + GC + FatFS 随机访问) |
| 覆盖原则 |
FTL 大范围 LBA 映射 / 垃圾回收 / FatFs f_lseek + f_read |
| 实测 |
PASS(STORAGE 套件:256KB 顺序 + 随机 seek 读回一致) |
TC-STO-03: 坏块注入下存储写入
| 字段 |
值 |
| ID |
TC-STO-03 |
| 优先级 |
P1 |
| 类型 |
负向/健壮性测试 |
| 标题 |
运行时注入坏块后,FatFS 仍可正常写读且坏块未被占用 |
| 前置条件 |
gd5f2gq5ue_init() 成功;FTL 已复位 |
| 测试步骤 |
1. nand_ftl_format() 清空旧 map → 选若干出厂好块 gd5f2gq5ue_mark_block_bad() 注入(更新并持久化 BBT) 2. nand_ftl_deinit() + nand_ftl_init() 使 dhara 经 is_block_bad 看到新坏块 3. f_mkfs 重建文件系统 → 正常文件写读 bbt.dat 并校验 4. 校验注入坏块仍 gd5f2gq5ue_is_block_bad()==1 |
| 通过标准 |
文件内容完整;注入坏块未被 dhara 分配使用(仍报告坏),存储可用 |
| 注意事项 |
本例会永久将若干好块标记为坏并持久化(测试设备专用);如需恢复,运行 TC-STO-04(启用 ENABLE_STORAGE_RECOVERY_TESTS 编译 STORAGE 套件一次)即可重建 BBT + 重新格式化 |
| 实测 |
PASS(STORAGE 套件:注入 3 坏块后文件完整且坏块仍报告坏) |
TC-STO-04: 重建 BBT + 重新格式化(恢复)
| 字段 |
值 |
| ID |
TC-STO-04 |
| 优先级 |
P1 |
| 类型 |
恢复/清理测试 |
| 标题 |
重建 BBT(重新扫描出厂坏块并持久化)并清空 dhara map,恢复设备干净态 |
| 前置条件 |
gd5f2gq5ue_init() 成功;需启用 ENABLE_STORAGE_RECOVERY_TESTS 编译 |
| 测试步骤 |
1. gd5f2gq5ue_bbt_rebuild()(关 ECC → 重新扫描出厂坏块 → 开 ECC → gd5f_bbt_save 持久化,覆盖注入坏块) 2. nand_ftl_format() 清空 dhara map 3. gd5f_bbt_dump_flash 校验持久化 BBT 版本 ≥1 |
| 通过标准 |
重建返回 OK 且持久化 BBT 版本 ≥1;设备坏块回到出厂集合,注入坏块被清除 |
| 注意事项 |
运行一次即恢复;恢复后建议断电重启让 init 重新加载干净 BBT。本 TC 默认关闭,避免常规测试每次重置设备 |
| 依赖 |
gd5f2gq5ue_bbt_rebuild(详见 §4.8 持久化设计) |
附录 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 阈值下界(32) |
P1 |
PASS |
| TC-GD5F-702 |
DMA 阈值上界(33) |
P1 |
PASS |
| TC-GD5F-703 |
大块 DMA 一致性 |
P1 |
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-STO-01 |
FTL 掉电恢复(journal) |
P1 |
PASS |
| TC-STO-02 |
大文件/随机 seek |
P1 |
PASS |
| TC-STO-03 |
坏块注入下存储写入 |
P1 |
PASS |
| TC-STO-04 |
BBT 重建 + 重新格式化(恢复) |
P1 |
受 ENABLE_STORAGE_RECOVERY_TESTS 控制(默认关) |
当前实测:TEST_SUITE_GD5F 构建一次运行全部阶段,结尾输出 === GD5F2GQ5UE Tests: 65/65 PASSED (failed=0) ===(65 为各 TC 内部断言总数,含 TC-GD5F-205~207 BBT 持久化用例;TC 覆盖见上表)。TEST_SUITE_STORAGE 套件结尾输出 === Storage Tests: 11/11 PASSED (failed=0) ===(TC-STO-01/02/03 各含若干内部断言,且 TC-STO-01 依赖的 s_cached_lpn 哨兵 bug 已修复)。两套件均 0 错误 0 警告通过 MDK 编译。
附录 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-STO-01 / TC-STO-03 |
gd5f2gq5ue_bbt_rebuild |
TC-STO-04 |
storage_test_run(存储套件) |
TC-STO-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 阈值 |
32 B(>32 走 DMA) |
| 状态寄存器 |
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 |