存储测试通过
This commit is contained in:
750
test/gd5f_test_task.c
Normal file
750
test/gd5f_test_task.c
Normal file
@@ -0,0 +1,750 @@
|
||||
/******************************************************************************
|
||||
* 模块名称: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 */
|
||||
Reference in New Issue
Block a user