/****************************************************************************** * 模块名称: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 #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 */