/****************************************************************************** * 模块名称:Storage Test Task * 模块功能:FTL + FatFS 存储测试,从 defaultTask 迁入,统一由 ch395fTestTask * 调度运行。正常产品构建下由 defaultTask 调用,行为与原内联逻辑一致。 * 作者:王建锋 * 创建日期:2026-08-26 ******************************************************************************/ #include #include "main.h" #include "ff.h" #define DBG_TAG "[STORAGE-TEST]" #include "dbg_log.h" #include "storage_test.h" #include "nand_ftl.h" #include "diskio.h" #include "gd5f2gq5ue.h" storage_test_stats_t g_storage_stats; static uint8_t s_perf_buf[4096]; /* TC-STO-01: FTL 掉电恢复(journal 持久化) * 写文件并同步落盘,随后模拟掉电(卸载 + 复位 FTL + 重新 resume),再挂载校验内容一致。 */ static void storage_test_powerloss(void) { static FATFS fs; static FIL fil; const uint32_t len = 4096; uint8_t wbuf[512]; uint8_t rbuf[512]; UINT bw, br; uint32_t i; int ok = 0; DBG_INFO("=== Storage Test: TC-STO-01 Power-loss recovery ==="); if (f_mount(&fs, "", 1) != FR_OK) { STORAGE_TEST_CHECK(0, "TC-STO-01 mount"); return; } /* Phase 1: 写已知内容文件并同步 */ for (i = 0; i < sizeof(wbuf); i++) { wbuf[i] = (uint8_t)((i * 31 + 7) & 0xFF); } if (f_open(&fil, "pl.dat", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) { for (i = 0; i < len; i += sizeof(wbuf)) { f_write(&fil, wbuf, sizeof(wbuf), &bw); } f_sync(&fil); f_close(&fil); } else { STORAGE_TEST_CHECK(0, "TC-STO-01 f_open(write)"); return; } /* 模拟掉电:卸载 + 复位 FTL + 重新 resume */ f_mount(&fs, "", 0); nand_ftl_deinit(); nand_ftl_init(); if (f_mount(&fs, "", 1) != FR_OK) { STORAGE_TEST_CHECK(0, "TC-STO-01 remount after resume"); return; } /* Phase 2: 重新打开校验 */ if (f_open(&fil, "pl.dat", FA_READ) == FR_OK) { ok = 1; for (i = 0; i < len; i += sizeof(rbuf)) { f_read(&fil, rbuf, sizeof(rbuf), &br); for (uint32_t j = 0; j < br; j++) { if (rbuf[j] != (uint8_t)(((i + j) * 31 + 7) & 0xFF)) { ok = 0; break; } } if (!ok) break; } f_close(&fil); } else { ok = 0; } STORAGE_TEST_CHECK(ok, "TC-STO-01 content survived power-loss (resume)"); f_unlink("pl.dat"); } /* TC-STO-02: 大文件 / 多扇区 / 随机 seek * 写 256KB 位置相关模式文件,顺序读回比对 + 随机偏移 seek 读比对。 */ static void storage_test_largefile(void) { static FATFS fs; static FIL fil; const uint32_t total = 256 * 1024; const uint32_t chunk = 4096; uint8_t buf[4096]; UINT bw, br; uint32_t off, i; int ok = 1; DBG_INFO("=== Storage Test: TC-STO-02 Large file / random seek ==="); if (f_mount(&fs, "", 1) != FR_OK) { STORAGE_TEST_CHECK(0, "TC-STO-02 mount"); return; } /* 写:位置相关模式 */ if (f_open(&fil, "big.dat", FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) { STORAGE_TEST_CHECK(0, "TC-STO-02 f_open(write)"); return; } for (off = 0; off < total; off += chunk) { for (i = 0; i < chunk; i++) { buf[i] = (uint8_t)(((off + i) * 31 + 7) & 0xFF); } f_write(&fil, buf, chunk, &bw); } f_close(&fil); /* 顺序读回 */ if (f_open(&fil, "big.dat", FA_READ) != FR_OK) { STORAGE_TEST_CHECK(0, "TC-STO-02 f_open(read)"); return; } for (off = 0; off < total; off += chunk) { f_read(&fil, buf, chunk, &br); if (br != chunk) { ok = 0; break; } for (i = 0; i < chunk; i++) { if (buf[i] != (uint8_t)(((off + i) * 31 + 7) & 0xFF)) { ok = 0; break; } } if (!ok) break; } STORAGE_TEST_CHECK(ok, "TC-STO-02 sequential 256KB read consistent"); /* 随机 seek 读 */ if (ok) { uint32_t seek_offs[] = { 0, 33333, 131072, 200000, total - 200 }; int seek_ok = 1; for (int s = 0; s < 5; s++) { uint32_t so = seek_offs[s]; uint32_t sc = 200; if (f_lseek(&fil, so) != FR_OK) { seek_ok = 0; break; } f_read(&fil, buf, sc, &br); for (i = 0; i < sc; i++) { if (buf[i] != (uint8_t)(((so + i) * 31 + 7) & 0xFF)) { seek_ok = 0; break; } } if (!seek_ok) break; } STORAGE_TEST_CHECK(seek_ok, "TC-STO-02 random seek read consistent"); } f_close(&fil); f_unlink("big.dat"); } /* TC-STO-03: 坏块注入下存储写入 * 清空旧 map → 注入若干好块为坏(持久化) → 重建文件系统 → 正常写读 → 校验坏块仍被报告。 */ static void storage_test_badblock(void) { static FATFS fs; static FIL fil; uint8_t wbuf[512], rbuf[512]; UINT bw, br; uint32_t bad[3]; int nbad = 0; int ok = 1; DBG_INFO("=== Storage Test: TC-STO-03 Bad-block injection during storage ==="); /* 清空旧 dhara map,避免后续注入的坏块被旧映射引用 */ nand_ftl_format(); /* 注入:挑出厂好块标记为坏(更新并持久化 BBT) */ for (uint32_t b = 100; b < gd5f_get_usable_blocks() && nbad < 3; b++) { if (gd5f2gq5ue_is_block_bad(b) == 0) { gd5f2gq5ue_mark_block_bad(b); bad[nbad++] = b; } } if (nbad < 3) { STORAGE_TEST_CHECK(0, "TC-STO-03 could not find 3 good blocks (got %d)", nbad); return; } STORAGE_TEST_CHECK(nbad == 3, "TC-STO-03 injected %d bad blocks", nbad); /* 复位 FTL 使 dhara 经 is_block_bad 看到新坏块 */ f_mount(&fs, "", 0); nand_ftl_deinit(); nand_ftl_init(); /* 重建文件系统(dhara 仍会跳过坏块) */ { MKFS_PARM opts = { FM_FAT32, 0, 0, 0, 0 }; uint8_t work[FF_MAX_SS]; if (f_mkfs("", &opts, work, sizeof(work)) != FR_OK) { STORAGE_TEST_CHECK(0, "TC-STO-03 f_mkfs"); return; } } if (f_mount(&fs, "", 1) != FR_OK) { STORAGE_TEST_CHECK(0, "TC-STO-03 mount"); return; } /* 正常文件写读 */ for (uint32_t i = 0; i < sizeof(wbuf); i++) { wbuf[i] = (uint8_t)((i * 17 + 3) & 0xFF); } if (f_open(&fil, "bbt.dat", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) { f_write(&fil, wbuf, sizeof(wbuf), &bw); f_close(&fil); } else { STORAGE_TEST_CHECK(0, "TC-STO-03 f_open(write)"); return; } if (f_open(&fil, "bbt.dat", FA_READ) == FR_OK) { f_read(&fil, rbuf, sizeof(rbuf), &br); f_close(&fil); if (br == sizeof(wbuf)) { for (uint32_t i = 0; i < sizeof(rbuf); i++) { if (rbuf[i] != wbuf[i]) { ok = 0; break; } } } else { ok = 0; } } else { ok = 0; } STORAGE_TEST_CHECK(ok, "TC-STO-03 file intact with injected bad blocks"); /* 校验注入坏块仍被报告为坏(未被 dhara 占用) */ int badok = 1; for (int k = 0; k < nbad; k++) { if (gd5f2gq5ue_is_block_bad(bad[k]) != 1) { badok = 0; } } STORAGE_TEST_CHECK(badok, "TC-STO-03 injected blocks still reported bad"); f_unlink("bbt.dat"); /* 注意:注入的坏块已持久化,测试设备专用;如需恢复需重建 BBT / 重新格式化 */ } #ifdef ENABLE_STORAGE_RECOVERY_TESTS static void storage_test_recover(void); #endif void storage_test_run(void) { static FATFS fs; static FIL fil; static uint8_t work[FF_MAX_SS]; char read_buf[64]; const char *test_str = "Hello FatFS + dhara FTL!"; memset(&g_storage_stats, 0, sizeof(g_storage_stats)); /* ==================== FTL + FatFS 文件系统测试 ==================== */ FRESULT 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)); STORAGE_TEST_CHECK(res == FR_OK, "f_mkfs"); res = f_mount(&fs, "", 1); } STORAGE_TEST_CHECK(res == FR_OK, "f_mount"); if (res == FR_OK) { UINT bw, br; res = f_open(&fil, "test.txt", FA_CREATE_ALWAYS | FA_WRITE); STORAGE_TEST_CHECK(res == FR_OK, "f_open(write)"); if (res == FR_OK) { f_write(&fil, test_str, (UINT)strlen(test_str), &bw); STORAGE_TEST_CHECK(bw == (UINT)strlen(test_str), "f_write (%u bytes)", bw); f_close(&fil); res = f_open(&fil, "test.txt", FA_READ); STORAGE_TEST_CHECK(res == FR_OK, "f_open(read)"); if (res == FR_OK) { f_read(&fil, read_buf, (UINT)(sizeof(read_buf) - 1), &br); f_close(&fil); read_buf[br] = '\0'; STORAGE_TEST_CHECK(br == (UINT)strlen(test_str) && memcmp(test_str, read_buf, br) == 0, "data verify \"%s\"", read_buf); } } f_unlink("test.txt"); } /* ==================== FTL + FatFS 性能测试 ==================== */ DBG_INFO("=== Storage Test: Performance ==="); { FIL pfile; UINT pw, pr; uint32_t t0, tw, tr; uint32_t i; uint32_t total_kb = 128; uint32_t chunk = sizeof(s_perf_buf); uint32_t count = (total_kb * 1024) / chunk; memset(s_perf_buf, 0xA5, sizeof(s_perf_buf)); /* 写入 */ t0 = HAL_GetTick(); if (f_open(&pfile, "perf.dat", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) { for (i = 0; i < count; i++) { f_write(&pfile, s_perf_buf, chunk, &pw); } f_close(&pfile); } tw = HAL_GetTick() - t0; /* 读取 */ t0 = HAL_GetTick(); if (f_open(&pfile, "perf.dat", FA_READ) == FR_OK) { for (i = 0; i < count; i++) { f_read(&pfile, s_perf_buf, chunk, &pr); } f_close(&pfile); } tr = HAL_GetTick() - t0; f_unlink("perf.dat"); if (tw > 0) { DBG_INFO(" Write: %lu KB in %lu ms = %lu KB/s", total_kb, tw, (total_kb * 1000) / tw); } if (tr > 0) { DBG_INFO(" Read: %lu KB in %lu ms = %lu KB/s", total_kb, tr, (total_kb * 1000) / tr); } } /* ==================== 存储集成测试(TC-STO-01~03) ==================== */ storage_test_powerloss(); storage_test_largefile(); storage_test_badblock(); #ifdef ENABLE_STORAGE_RECOVERY_TESTS storage_test_recover(); #endif /* ==================== 汇总报告 ==================== */ STORAGE_TEST_REPORT("Storage Tests"); } #ifdef ENABLE_STORAGE_RECOVERY_TESTS /* TC-STO-04: 重建 BBT + 重新格式化(恢复 TC-STO-03 注入的坏块) * 仅在启用 ENABLE_STORAGE_RECOVERY_TESTS 时编译运行;运行一次即可把设备 * 恢复到干净出厂态(清除运行时注入坏块并清空 dhara map)。 */ static void storage_test_recover(void) { DBG_INFO("=== Storage Test: TC-STO-04 BBT rebuild + format (recovery) ==="); int r = gd5f2gq5ue_bbt_rebuild(); STORAGE_TEST_CHECK(r == GD5F_OK, "TC-STO-04 bbt rebuild"); nand_ftl_format(); STORAGE_TEST_CHECK(1, "TC-STO-04 nand_ftl_format done"); uint8_t fb[GD5F_TOTAL_BLOCKS / 8]; uint32_t ver = 0; if (gd5f_bbt_dump_flash(fb, &ver) == GD5F_OK) { STORAGE_TEST_CHECK(ver >= 1, "TC-STO-04 persisted BBT version=%lu", ver); } else { STORAGE_TEST_CHECK(0, "TC-STO-04 dump_flash failed"); } DBG_INFO("TC-STO-04 recovery complete; recommend power-cycle to re-init cleanly"); } #endif