385 lines
12 KiB
C
385 lines
12 KiB
C
/******************************************************************************
|
||
* 模块名称: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
|