Files
STM32F4-Base/test/gd5f_test_task.c
2026-08-27 18:22:44 +08:00

751 lines
28 KiB
C
Raw 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 Driver Test Task
* 模块功能GD5F2GQ5UE 驱动全阶段测试入口,统一在 defaultTask 上下文(同步阻塞)
* 中运行。各阶段由 gd5f_test.h 中的 ENABLE_GD5F_PHASE*_TESTS 宏独立开关,
* 覆盖 init/read_id/reset、BBT、页读写、块擦除、ECC、SPI 原语、DMA 边界、
* FTL 与 FatFS。所有结果通过 GD5F_TEST_CHECK / GD5F_TEST_REPORT 输出串口。
* 作者:王建锋
* 创建日期2026-08-26
*
* 注意NAND 不可原地覆盖,且同一页在一次擦除内只能编程一次。因此每个会写入的子
* 测试开始前都先擦除目标块,避免非法"部分页重编程"导致数据异常或失败。
******************************************************************************/
#include <string.h>
#include "test_config.h"
#include "gd5f_test.h"
#include "nand_ftl.h"
#define DBG_TAG "[NAND-TEST]"
#include "dbg_log.h"
#include "ff.h"
/* 仅在启用总开关时编译全部测试体(避免未启用时产生未使用函数告警) */
#ifdef ENABLE_GD5F_TESTS
gd5f_test_stats_t g_gd5f_test_stats;
/* 静态缓冲避免占用任务栈defaultTask 栈仅 2048 字节)。
* 不使用大块连续缓冲:多页测试改为逐页读写,降低 RAM 占用。 */
static uint8_t s_wbuf[GD5F_PAGE_SIZE * 2];
static uint8_t s_rbuf[GD5F_PAGE_SIZE * 2];
/* -------------------------------------------------------------------
* 辅助:从最高块向下寻找连续 n 个好块,返回其中最高块号;找不到返回 GD5F_TOTAL_BLOCKS
* ------------------------------------------------------------------- */
static uint32_t gd5f_find_run(int32_t n)
{
uint32_t usable = gd5f_get_usable_blocks();
uint32_t top;
int32_t k;
if (n < 1) {
n = 1;
}
if (usable < (uint32_t)n) {
return usable; /* 管理区不足 */
}
for (top = usable - 1; top >= (uint32_t)(n - 1) + 1; top--) {
int32_t ok = 1;
for (k = 0; k < n; k++) {
if (gd5f2gq5ue_is_block_bad(top - (uint32_t)k)) {
ok = 0;
break;
}
}
if (ok) {
return top - (uint32_t)(n - 1); /* 返回连续好块的起始块号 */
}
}
return usable; /* 未找到足够连续的块 */
}
/* 辅助:统计当前 BBT 中的坏块数量 */
static uint32_t gd5f_count_bad(void)
{
uint32_t b;
uint32_t cnt = 0;
for (b = 0; b < GD5F_TOTAL_BLOCKS; b++) {
if (gd5f2gq5ue_is_block_bad(b)) {
cnt++;
}
}
return cnt;
}
/* -------------------------------------------------------------------
* Phase 1 — 初始化与 ID 识别
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE1_TESTS
static void gd5f_phase1(void)
{
uint8_t mid = 0, did = 0;
uint8_t s = 0;
int32_t ret;
DBG_INFO("=== GD5F Phase 1: init / read_id / reset ===");
/* TC-GD5F-101: 再次 init幂等应成功 */
ret = gd5f2gq5ue_init();
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-101 init ret=%d", ret);
/* TC-GD5F-102: read_id 返回 MID=0xC8 DID=0x52 */
ret = gd5f2gq5ue_read_id(&mid, &did);
GD5F_TEST_CHECK(ret == GD5F_OK && mid == GD5F_MANUFACTURER_ID &&
did == GD5F_DEVICE_ID,
"TC-GD5F-102 read_id MID=0x%02X DID=0x%02X", mid, did);
/* TC-GD5F-103: reset 后 OIP 清零 */
ret = gd5f2gq5ue_reset();
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-103 reset ret=%d", ret);
gd5f_read_status(&s);
GD5F_TEST_CHECK((s & GD5F_STATUS_OIP) == 0,
"TC-GD5F-103 OIP cleared (status=0x%02X)", s);
}
#endif /* ENABLE_GD5F_PHASE1_TESTS */
/* -------------------------------------------------------------------
* Phase 2 — 坏块管理BBT
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE2_TESTS
static void gd5f_phase2(void)
{
uint32_t blk;
uint32_t cnt1, cnt2;
int32_t good, m, c;
/* 快照init 阶段已扫描构建的出厂 BBT测试后据此还原不再重扫 */
uint32_t orig_bad[GD5F_TOTAL_BLOCKS / 32];
DBG_INFO("=== GD5F Phase 2: BBT ===");
/* 快照初始化时的 BBT由 gd5f2gq5ue_init -> gd5f_private_bbt_scan 构建) */
memset(orig_bad, 0, sizeof(orig_bad));
for (blk = 0; blk < GD5F_TOTAL_BLOCKS; blk++) {
if (gd5f2gq5ue_is_block_bad(blk) != 0) {
orig_bad[blk >> 5] |= (1U << (blk & 31));
}
}
/* TC-GD5F-201: 出厂坏块扫描结果可打印(直接读取 init BBT */
gd5f2gq5ue_print_bbt();
GD5F_TEST_CHECK(1, "TC-GD5F-201 bbt scan/print executed");
/* TC-GD5F-202: is_block_bad 好块=0越界=1 */
blk = gd5f_find_run(1);
good = gd5f2gq5ue_is_block_bad(blk);
GD5F_TEST_CHECK(good == 0, "TC-GD5F-202 is_block_bad(good=%lu)=%d", blk, good);
GD5F_TEST_CHECK(gd5f2gq5ue_is_block_bad(GD5F_TOTAL_BLOCKS + 1) == 1,
"TC-GD5F-202 is_block_bad(OOB)=1");
/* TC-GD5F-203: mark_block_bad / bbt_clear 仅改 RAM BBT一致性 */
gd5f2gq5ue_mark_block_bad(blk);
m = gd5f2gq5ue_is_block_bad(blk);
gd5f2gq5ue_bbt_clear();
c = gd5f2gq5ue_is_block_bad(blk);
GD5F_TEST_CHECK(m == 1 && c == 0,
"TC-GD5F-203 mark=%d clear=%d (RAM only)", m, c);
/* 恢复初始化时的 BBT按快照还原不调用已删除的 bbt_rescan */
gd5f2gq5ue_bbt_clear();
for (blk = 0; blk < GD5F_TOTAL_BLOCKS; blk++) {
if ((orig_bad[blk >> 5] & (1U << (blk & 31))) != 0) {
gd5f2gq5ue_mark_block_bad(blk);
}
}
/* TC-GD5F-204: 初始化 BBT 可直接读取且稳定(无需 rescan */
cnt1 = gd5f_count_bad();
gd5f2gq5ue_print_bbt();
cnt2 = gd5f_count_bad();
GD5F_TEST_CHECK(cnt1 == cnt2,
"TC-GD5F-204 init BBT stable %lu == %lu", cnt1, cnt2);
/* ---------- BBT 持久化测试(保留块池,掉电安全) ---------- */
{
uint32_t usable = gd5f_get_usable_blocks();
uint32_t X = gd5f_find_run(1); /* 管理区内的一个好块 */
uint8_t flash_bbt[GD5F_TOTAL_BLOCKS / 8];
uint32_t ver = 0;
uint32_t cnt_a, cnt_b;
int32_t marked;
/* TC-GD5F-205: 保留块池已预留dhara 可用块数 < 总块数;
* 且测试用块 X 落在管理区内(不会擦到 BBT 存储区) */
GD5F_TEST_CHECK(usable < (uint32_t)GD5F_TOTAL_BLOCKS,
"TC-GD5F-205 usable=%lu < total=%lu", usable, (uint32_t)GD5F_TOTAL_BLOCKS);
GD5F_TEST_CHECK(X < usable,
"TC-GD5F-205 test block X=%lu < usable=%lu", X, usable);
/* TC-GD5F-206: 标记坏块后 RAM 生效,且已落盘(闪存副本含该位、版本>=1 */
gd5f2gq5ue_mark_block_bad(X);
marked = gd5f2gq5ue_is_block_bad(X);
GD5F_TEST_CHECK(marked == 1, "TC-GD5F-206 RAM mark X=%lu -> %d", X, marked);
if (GD5F_OK == gd5f_bbt_dump_flash(flash_bbt, &ver)) {
int32_t flash_bit = (flash_bbt[X >> 3] >> (X & 7)) & 1;
GD5F_TEST_CHECK(flash_bit == 1 && ver >= 1,
"TC-GD5F-206 flash persist X bit=%d ver=%lu", flash_bit, ver);
} else {
GD5F_TEST_CHECK(0, "TC-GD5F-206 dump_flash failed");
}
/* TC-GD5F-207: 清空 RAM 后从闪存重载,坏块记录被恢复(验证 load 链路) */
cnt_a = gd5f_count_bad();
gd5f2gq5ue_bbt_clear();
gd5f_bbt_reload();
marked = gd5f2gq5ue_is_block_bad(X);
cnt_b = gd5f_count_bad();
GD5F_TEST_CHECK(marked == 1 && cnt_b >= cnt_a,
"TC-GD5F-207 reload X=%lu restored (cnt %lu->%lu)", X, cnt_a, cnt_b);
/* 清理还原出厂快照RAM并擦除保留池闪存设备回到干净态
* 保证测试可重复且不在 NAND 上留下伪坏块 */
gd5f2gq5ue_bbt_clear();
for (blk = 0; blk < GD5F_TOTAL_BLOCKS; blk++) {
if ((orig_bad[blk >> 5] & (1U << (blk & 31))) != 0) {
gd5f2gq5ue_mark_block_bad(blk);
}
}
gd5f_bbt_wipe_pool();
}
}
#endif /* ENABLE_GD5F_PHASE2_TESTS */
/* -------------------------------------------------------------------
* Phase 3 — 页读写(跨页 / 非对齐 / 多页 / 单字节)
* 每个子测试前先擦除 scratch 块,遵守"一次擦除内每页仅编程一次"。
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE3_TESTS
static void gd5f_phase3(void)
{
uint32_t blk;
int32_t base;
int32_t ret, ok, i;
DBG_INFO("=== GD5F Phase 3: page read/write ===");
blk = gd5f_find_run(1);
base = (int32_t)blk * GD5F_BLOCK_SIZE;
/* TC-GD5F-301: 整页写读offset=0, size=2048 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
s_wbuf[i] = (uint8_t)(i & 0xFF);
}
ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-301 write ret=%d", ret);
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
ret = gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE);
ok = 1;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != (uint8_t)(i & 0xFF)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ret == GD5F_OK && ok, "TC-GD5F-301 readback %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-302: 跨页写读offset=1000, size=3000 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < 3000; i++) {
s_wbuf[i] = (uint8_t)((1000 + i) & 0xFF);
}
ret = gd5f2gq5ue_write(base + 1000, s_wbuf, 3000);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-302 write ret=%d", ret);
memset(s_rbuf, 0, 3000);
ret = gd5f2gq5ue_read(base + 1000, s_rbuf, 3000);
ok = 1;
for (i = 0; i < 3000; i++) {
if (s_rbuf[i] != (uint8_t)((1000 + i) & 0xFF)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ret == GD5F_OK && ok, "TC-GD5F-302 cross-page %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-303: 非对齐 offset=1, size=2047 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < 2047; i++) {
s_wbuf[i] = (uint8_t)((1 + i) & 0xFF);
}
ret = gd5f2gq5ue_write(base + 1, s_wbuf, 2047);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-303 write ret=%d", ret);
memset(s_rbuf, 0, 2047);
ret = gd5f2gq5ue_read(base + 1, s_rbuf, 2047);
ok = 1;
for (i = 0; i < 2047; i++) {
if (s_rbuf[i] != (uint8_t)((1 + i) & 0xFF)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ret == GD5F_OK && ok, "TC-GD5F-303 unaligned %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-304: 多页顺序写读10 页,逐页校验,避免大缓冲占用 RAM */
{
int32_t p;
int32_t off;
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (p = 0; p < 10; p++) {
off = p * GD5F_PAGE_SIZE;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
s_wbuf[i] = (uint8_t)((off + i) & 0xFF);
}
ret = gd5f2gq5ue_write(base + off, s_wbuf, GD5F_PAGE_SIZE);
if (ret != GD5F_OK) break;
}
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-304 write ret=%d", ret);
ok = 1;
for (p = 0; p < 10 && ok; p++) {
off = p * GD5F_PAGE_SIZE;
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
gd5f2gq5ue_read(base + off, s_rbuf, GD5F_PAGE_SIZE);
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != (uint8_t)((off + i) & 0xFF)) { ok = 0; break; }
}
}
GD5F_TEST_CHECK(ok, "TC-GD5F-304 multi-page %s", ok ? "OK" : "MISMATCH");
}
/* TC-GD5F-305: 随机单字节访问(独立擦除后写单字节,余字节应保持 0xFF */
{
uint32_t seeds[7];
int32_t k, allok = 1;
uint8_t val, r = 0;
seeds[0] = 0;
seeds[1] = 1;
seeds[2] = GD5F_PAGE_SIZE - 1;
seeds[3] = GD5F_PAGE_SIZE;
seeds[4] = GD5F_PAGE_SIZE + 100;
seeds[5] = 3000;
seeds[6] = 20479;
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (k = 0; k < 7; k++) {
val = (uint8_t)((seeds[k] * 7 + 3) & 0xFF);
ret = gd5f2gq5ue_write(base + (int32_t)seeds[k], &val, 1);
if (ret != GD5F_OK) { allok = 0; break; }
r = 0;
ret = gd5f2gq5ue_read(base + (int32_t)seeds[k], &r, 1);
if (ret != GD5F_OK || r != val) { allok = 0; break; }
}
GD5F_TEST_CHECK(allok, "TC-GD5F-305 random single-byte");
}
}
#endif /* ENABLE_GD5F_PHASE3_TESTS */
/* -------------------------------------------------------------------
* Phase 4 — 块擦除
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE4_TESTS
static void gd5f_phase4(void)
{
uint32_t blk, blk3;
int32_t base, base3;
int32_t ret, ok, i;
int32_t off;
DBG_INFO("=== GD5F Phase 4: block erase ===");
blk = gd5f_find_run(1);
base = (int32_t)blk * GD5F_BLOCK_SIZE;
/* TC-GD5F-401: 单块擦除后读全 0xFF */
ret = gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-401 erase ret=%d", ret);
ok = 1;
for (off = 0; off < (int32_t)GD5F_BLOCK_SIZE; off += GD5F_PAGE_SIZE) {
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
gd5f2gq5ue_read(base + off, s_rbuf, GD5F_PAGE_SIZE);
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != 0xFF) { ok = 0; break; }
}
if (!ok) break;
}
GD5F_TEST_CHECK(ok, "TC-GD5F-401 erased block all 0xFF");
/* TC-GD5F-402: 多块擦除3 块连续) */
blk3 = gd5f_find_run(3);
if (blk3 == GD5F_TOTAL_BLOCKS) {
GD5F_TEST_CHECK(0, "TC-GD5F-402 no 3 consecutive good blocks");
} else {
base3 = (int32_t)blk3 * GD5F_BLOCK_SIZE;
ret = gd5f2gq5ue_erase(base3, 3 * GD5F_BLOCK_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-402 multi-erase(3) ret=%d", ret);
ok = 1;
for (off = 0; off < (int32_t)(3 * GD5F_BLOCK_SIZE); off += GD5F_PAGE_SIZE) {
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
gd5f2gq5ue_read(base3 + off, s_rbuf, GD5F_PAGE_SIZE);
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != 0xFF) { ok = 0; break; }
}
if (!ok) break;
}
GD5F_TEST_CHECK(ok, "TC-GD5F-402 multi-erase all 0xFF");
}
/* TC-GD5F-403: 非块对齐应返回错误 */
ret = gd5f2gq5ue_erase(base + 100, GD5F_BLOCK_SIZE);
GD5F_TEST_CHECK(ret != GD5F_OK,
"TC-GD5F-403 offset unaligned rejected (ret=%d)", ret);
ret = gd5f2gq5ue_erase(base, 100);
GD5F_TEST_CHECK(ret != GD5F_OK,
"TC-GD5F-403 size unaligned rejected (ret=%d)", ret);
/* TC-GD5F-404: 擦除-写-读 完整闭环 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
memset(s_wbuf, 0xA5, GD5F_PAGE_SIZE);
ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-404 write ret=%d", ret);
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE);
ok = 1;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != 0xA5) { ok = 0; break; }
}
GD5F_TEST_CHECK(ok, "TC-GD5F-404 erase-write-read loop %s", ok ? "OK" : "MISMATCH");
}
#endif /* ENABLE_GD5F_PHASE4_TESTS */
/* -------------------------------------------------------------------
* Phase 5 — ECC 验证
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE5_TESTS
static void gd5f_phase5(void)
{
uint32_t blk;
uint32_t page;
int32_t base;
int32_t ret, ecc, ok, i;
DBG_INFO("=== GD5F Phase 5: ECC ===");
blk = gd5f_find_run(1);
base = (int32_t)blk * GD5F_BLOCK_SIZE;
page = blk * GD5F_PAGES_PER_BLOCK;
/* TC-GD5F-501: ECC 开启下写读正确check_ecc 无错 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
s_wbuf[i] = (uint8_t)(i & 0x55);
}
ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-501 write ret=%d", ret);
ret = gd5f_page_read(page);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-501 page_read ret=%d", ret);
ecc = gd5f_check_ecc();
GD5F_TEST_CHECK(ecc == GD5F_OK, "TC-GD5F-501 check_ecc=%d", ecc);
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
gd5f_read_from_cache(0, s_rbuf, GD5F_PAGE_SIZE);
ok = 1;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != (uint8_t)(i & 0x55)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ok, "TC-GD5F-501 data correct %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-502: 干净页 check_ecc 返回 OK */
ret = gd5f_page_read(page);
ecc = gd5f_check_ecc();
GD5F_TEST_CHECK(ret == GD5F_OK && ecc == GD5F_OK,
"TC-GD5F-502 clean read ecc=%d", ecc);
}
#endif /* ENABLE_GD5F_PHASE5_TESTS */
/* -------------------------------------------------------------------
* Phase 6 — SPI 原语FTL 共享)
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE6_TESTS
static void gd5f_phase6(void)
{
uint32_t blk;
uint32_t page0;
int32_t base;
int32_t ret, ok, i;
uint8_t s = 0;
DBG_INFO("=== GD5F Phase 6: SPI primitives ===");
blk = gd5f_find_run(1);
base = (int32_t)blk * GD5F_BLOCK_SIZE;
page0 = blk * GD5F_PAGES_PER_BLOCK;
/* TC-GD5F-603: gd5f_block_erase 原语(传块号) */
ret = gd5f_block_erase(blk);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-603 block_erase ret=%d", ret);
ok = 1;
for (i = 0; i < GD5F_PAGES_PER_BLOCK; i++) {
gd5f_page_read(page0 + (uint32_t)i);
gd5f_read_from_cache(0, s_rbuf, GD5F_PAGE_SIZE);
for (int32_t j = 0; j < GD5F_PAGE_SIZE; j++) {
if (s_rbuf[j] != 0xFF) { ok = 0; break; }
}
if (!ok) break;
}
GD5F_TEST_CHECK(ok, "TC-GD5F-603 erased block all 0xFF");
/* TC-GD5F-602: program_load + program_exec 组合(先写使能) */
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
s_wbuf[i] = (uint8_t)(i & 0xAA);
}
gd5f_write_enable();
ret = gd5f_program_load(0, s_wbuf, GD5F_PAGE_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-602 program_load ret=%d", ret);
ret = gd5f_program_exec(page0);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-602 program_exec ret=%d", ret);
/* 用高层读验证 */
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
ret = gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE);
ok = 1;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != (uint8_t)(i & 0xAA)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ret == GD5F_OK && ok,
"TC-GD5F-602 verify (high-level read) %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-601: page_read + read_from_cache 组合 */
ret = gd5f_page_read(page0);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-601 page_read ret=%d", ret);
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
ret = gd5f_read_from_cache(0, s_rbuf, GD5F_PAGE_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-601 read_from_cache ret=%d", ret);
ok = 1;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != (uint8_t)(i & 0xAA)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ok, "TC-GD5F-601 read via primitives %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-604: write_enable 后置 WEL 位 */
gd5f_write_enable();
gd5f_read_status(&s);
GD5F_TEST_CHECK((s & GD5F_STATUS_WEL) != 0,
"TC-GD5F-604 WEL set (status=0x%02X)", s);
/* TC-GD5F-605: wait_busy 正常返回 */
ret = gd5f_wait_busy(1000);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-605 wait_busy ret=%d", ret);
}
#endif /* ENABLE_GD5F_PHASE6_TESTS */
/* -------------------------------------------------------------------
* Phase 7 — DMA 边界(阈值 32 字节)
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE7_TESTS
static void gd5f_phase7(void)
{
uint32_t blk;
int32_t base;
int32_t ret, ok, i;
DBG_INFO("=== GD5F Phase 7: DMA boundary (threshold 32) ===");
blk = gd5f_find_run(1);
base = (int32_t)blk * GD5F_BLOCK_SIZE;
/* TC-GD5F-701: size=32 轮询路径 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < 32; i++) {
s_wbuf[i] = (uint8_t)(i + 0x10);
}
ret = gd5f2gq5ue_write(base, s_wbuf, 32);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-701 write(32) ret=%d", ret);
memset(s_rbuf, 0, 32);
ret = gd5f2gq5ue_read(base, s_rbuf, 32);
ok = 1;
for (i = 0; i < 32; i++) {
if (s_rbuf[i] != (uint8_t)(i + 0x10)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ok, "TC-GD5F-701 polling path %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-702: size=33 DMA 路径 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < 33; i++) {
s_wbuf[i] = (uint8_t)(i + 0x20);
}
ret = gd5f2gq5ue_write(base, s_wbuf, 33);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-702 write(33) ret=%d", ret);
memset(s_rbuf, 0, 33);
ret = gd5f2gq5ue_read(base, s_rbuf, 33);
ok = 1;
for (i = 0; i < 33; i++) {
if (s_rbuf[i] != (uint8_t)(i + 0x20)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ok, "TC-GD5F-702 DMA path %s", ok ? "OK" : "MISMATCH");
/* TC-GD5F-703: size=2048 整页 DMA 路径 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
s_wbuf[i] = (uint8_t)(i & 0xFF);
}
ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-703 write(2048) ret=%d", ret);
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
ret = gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE);
ok = 1;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != (uint8_t)(i & 0xFF)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ok, "TC-GD5F-703 DMA whole page %s", ok ? "OK" : "MISMATCH");
}
#endif /* ENABLE_GD5F_PHASE7_TESTS */
/* -------------------------------------------------------------------
* Phase 8 — FTL 层
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE8_TESTS
static void gd5f_phase8(void)
{
int32_t ret;
DBG_INFO("=== GD5F Phase 8: FTL ===");
/* TC-GD5F-801: nand_ftl_init */
ret = nand_ftl_init();
GD5F_TEST_CHECK(ret == 0, "TC-GD5F-801 nand_ftl_init ret=%d", ret);
/* TC-GD5F-802: nand_ftl_format */
ret = nand_ftl_format();
GD5F_TEST_CHECK(ret == 0, "TC-GD5F-802 nand_ftl_format ret=%d", ret);
}
#endif /* ENABLE_GD5F_PHASE8_TESTS */
/* -------------------------------------------------------------------
* Phase 9 — FatFS diskio可选
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE9_TESTS
static void gd5f_phase9(void)
{
FATFS fs;
FIL fil;
UINT bw = 0, br = 0;
FRESULT res;
const char *ts = "GD5F-FatFS-OK";
char rb[32];
static uint8_t work[FF_MAX_SS];
DBG_INFO("=== GD5F Phase 9: FatFS diskio ===");
nand_ftl_init();
res = f_mount(&fs, "", 1);
if (res == FR_NO_FILESYSTEM) {
MKFS_PARM opts = { FM_FAT32, 0, 0, 0, 0 };
res = f_mkfs("", &opts, work, sizeof(work));
res = f_mount(&fs, "", 1);
}
GD5F_TEST_CHECK(res == FR_OK, "TC-GD5F-901 f_mount res=%d", res);
if (res == FR_OK) {
res = f_open(&fil, "gd5f_t.txt", FA_CREATE_ALWAYS | FA_WRITE);
GD5F_TEST_CHECK(res == FR_OK, "TC-GD5F-901 f_open(w) res=%d", res);
if (res == FR_OK) {
f_write(&fil, ts, (UINT)strlen(ts), &bw);
GD5F_TEST_CHECK(bw == (UINT)strlen(ts), "TC-GD5F-901 f_write %u", bw);
f_close(&fil);
res = f_open(&fil, "gd5f_t.txt", FA_READ);
GD5F_TEST_CHECK(res == FR_OK, "TC-GD5F-901 f_open(r) res=%d", res);
if (res == FR_OK) {
memset(rb, 0, sizeof(rb));
f_read(&fil, rb, (UINT)strlen(ts), &br);
f_close(&fil);
GD5F_TEST_CHECK(br == (UINT)strlen(ts) &&
memcmp(ts, rb, br) == 0,
"TC-GD5F-901 verify \"%.*s\"", (int)br, rb);
}
}
f_unlink("gd5f_t.txt");
}
}
#endif /* ENABLE_GD5F_PHASE9_TESTS */
/* -------------------------------------------------------------------
* Phase 10 — 边界条件
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE10_TESTS
static void gd5f_phase10(void)
{
uint32_t blk;
int32_t base;
int32_t ret, i, ne, ok;
DBG_INFO("=== GD5F Phase 10: boundary conditions ===");
blk = gd5f_find_run(1);
base = (int32_t)blk * GD5F_BLOCK_SIZE;
/* TC-GD5F-1001: offset 越界应返回错误 */
ret = gd5f2gq5ue_read((int32_t)GD5F_TOTAL_SIZE, s_rbuf, 1);
GD5F_TEST_CHECK(ret != GD5F_OK,
"TC-GD5F-1001 read OOB rejected (ret=%d)", ret);
ret = gd5f2gq5ue_write((int32_t)GD5F_TOTAL_SIZE, s_wbuf, 1);
GD5F_TEST_CHECK(ret != GD5F_OK,
"TC-GD5F-1001 write OOB rejected (ret=%d)", ret);
/* TC-GD5F-1002: size=0 不崩溃,返回 GD5F_OK空操作 */
ret = gd5f2gq5ue_read(base, s_rbuf, 0);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-1002 read size=0 ret=%d", ret);
ret = gd5f2gq5ue_write(base, s_wbuf, 0);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-1002 write size=0 ret=%d", ret);
/* TC-GD5F-1003: 写前未擦除 → 数据不可预期NAND 不可原地覆盖)
* 先擦除写入全 0x00再不清擦写入全 0xFF需要 0→1物理不可行
* 读回应仍 != 0xFF。 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
memset(s_wbuf, 0x00, GD5F_PAGE_SIZE);
gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE);
memset(s_wbuf, 0xFF, GD5F_PAGE_SIZE);
gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE); /* 未擦除0→1 不可能 */
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE);
ne = 0;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != 0xFF) ne++;
}
GD5F_TEST_CHECK(ne > 0,
"TC-GD5F-1003 write-without-erase not applied (bytes!=0xFF: %d)", ne);
/* TC-GD5F-1004: 写保护解除验证init 后 Protect=0x00可正常编程 */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
memset(s_wbuf, 0x5A, GD5F_PAGE_SIZE);
ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE);
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE);
ok = 1;
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
if (s_rbuf[i] != 0x5A) { ok = 0; break; }
}
GD5F_TEST_CHECK(ret == GD5F_OK && ok,
"TC-GD5F-1004 write unprotected (ret=%d) %s", ret, ok ? "OK" : "FAIL");
}
#endif /* ENABLE_GD5F_PHASE10_TESTS */
/* -------------------------------------------------------------------
* 入口:按启用的阶段顺序执行并汇总
* ------------------------------------------------------------------- */
void gd5f_test_run(void)
{
memset(&g_gd5f_test_stats, 0, sizeof(g_gd5f_test_stats));
DBG_INFO("########## GD5F2GQ5UE Test Start ##########");
#ifdef ENABLE_GD5F_PHASE1_TESTS
gd5f_phase1();
#endif
#ifdef ENABLE_GD5F_PHASE2_TESTS
gd5f_phase2();
#endif
#ifdef ENABLE_GD5F_PHASE3_TESTS
gd5f_phase3();
#endif
#ifdef ENABLE_GD5F_PHASE4_TESTS
gd5f_phase4();
#endif
#ifdef ENABLE_GD5F_PHASE5_TESTS
gd5f_phase5();
#endif
#ifdef ENABLE_GD5F_PHASE6_TESTS
gd5f_phase6();
#endif
#ifdef ENABLE_GD5F_PHASE7_TESTS
gd5f_phase7();
#endif
#ifdef ENABLE_GD5F_PHASE8_TESTS
gd5f_phase8();
#endif
#ifdef ENABLE_GD5F_PHASE9_TESTS
gd5f_phase9();
#endif
#ifdef ENABLE_GD5F_PHASE10_TESTS
gd5f_phase10();
#endif
GD5F_TEST_REPORT("GD5F2GQ5UE Tests");
DBG_INFO("########## GD5F2GQ5UE Test Done ##########");
}
#endif /* ENABLE_GD5F_TESTS */