存储测试通过

This commit is contained in:
2026-08-27 18:22:44 +08:00
parent 88160a5134
commit cf33171c8f
19 changed files with 2952 additions and 336 deletions

384
test/storage_test_task.c Normal file
View File

@@ -0,0 +1,384 @@
/******************************************************************************
* 模块名称Storage Test Task
* 模块功能FTL + FatFS 存储测试,从 defaultTask 迁入,统一由 ch395fTestTask
* 调度运行。正常产品构建下由 defaultTask 调用,行为与原内联逻辑一致。
* 作者:王建锋
* 创建日期2026-08-26
******************************************************************************/
#include <string.h>
#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