存储测试通过

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

View File

@@ -54,7 +54,7 @@ extern "C" {
/* ---- NET 层(阶段 6~11---- */
//#define ENABLE_PHASE6_TESTS /* [网] TCP Echo单连接基础收发 */
//#define ENABLE_PHASE7_TESTS /* [网] TCP Client 主动连接(回显收发 + 关闭重连 + 连接超时) */
#define ENABLE_PHASE8_TESTS /* [网] 大文件传输100KB 文件收发) */
//#define ENABLE_PHASE8_TESTS /* [网] 大文件传输100KB 文件收发) */
//#define ENABLE_PHASE9_TESTS /* [网] Select/Poll I/O 多路复用 */
//#define ENABLE_PHASE10_TESTS /* [网] 边界条件测试 */
//#define ENABLE_PHASE11_TESTS /* [网] KeepAlive 超时断开测试 */

View File

@@ -17,6 +17,9 @@
#include "net_socket.h"
#include "ch395f_test.h"
#include "ch395f.h"
#include "test_config.h"
#include "storage_test.h"
#include "gd5f_test.h"
#define DBG_TAG "[TEST_TASK]"
#include "dbg_log.h"
@@ -48,7 +51,7 @@ test_stats_t g_test_stats;
static void test_parse_ip(const char *str, uint8_t *ip_arr)
{
uint32_t val;
int i;
int32_t i;
for (i = 0; i < 4; i++) {
val = 0;
@@ -121,7 +124,7 @@ static void phase1_run(void) {
ch395f_set_ip_addr(set_ip);
memset(ip_info, 0, sizeof(ip_info));
ch395f_get_ip_inf(ip_info);
int match = (ip_info[0] == 192 && ip_info[1] == 168 &&
int32_t match = (ip_info[0] == 192 && ip_info[1] == 168 &&
ip_info[2] == 1 && ip_info[3] == 100);
DBG_INFO(" [P1-03] ch395f_set_ip_addr(192.168.1.100)");
DBG_INFO(" expect: IP[0..3] = 192.168.1.100");
@@ -141,7 +144,7 @@ static void phase1_run(void) {
ch395f_set_gwip_addr(set_gw);
memset(ip_info, 0, sizeof(ip_info));
ch395f_get_ip_inf(ip_info);
int match = (ip_info[4] == 192 && ip_info[5] == 168 &&
int32_t match = (ip_info[4] == 192 && ip_info[5] == 168 &&
ip_info[6] == 1 && ip_info[7] == 1);
DBG_INFO(" [P1-04] ch395f_set_gwip_addr(192.168.1.1)");
DBG_INFO(" expect: GW[0..3] = 192.168.1.1");
@@ -161,7 +164,7 @@ static void phase1_run(void) {
ch395f_set_mask_addr(set_mask);
memset(ip_info, 0, sizeof(ip_info));
ch395f_get_ip_inf(ip_info);
int match = (ip_info[8] == 255 && ip_info[9] == 255 &&
int32_t match = (ip_info[8] == 255 && ip_info[9] == 255 &&
ip_info[10] == 255 && ip_info[11] == 0);
DBG_INFO(" [P1-05] ch395f_set_mask_addr(255.255.255.0)");
DBG_INFO(" expect: MASK[0..3] = 255.255.255.0");
@@ -503,8 +506,8 @@ static void phase2_tc202_once(uint8_t sock, uint16_t local_port)
uint16_t n;
uint16_t rl;
uint32_t tick;
int k;
int match;
int32_t k;
int32_t match;
for (k = 0; k < 64; k++) {
tx[k] = (uint8_t)k;
@@ -596,7 +599,7 @@ static void phase2_tc202(void)
{
uint8_t sock = 0;
uint8_t ip_arr[4];
int i;
int32_t i;
test_parse_ip(PHASE2_REMOTE_IP, ip_arr);
@@ -804,7 +807,7 @@ static void phase2_run(void)
static void phase3_run(void) {
uint8_t sock = 0;
uint8_t rx_buf[300] = {0};
int counted;
int32_t counted;
DBG_INFO("=== CH395F Phase 3 Tests (UDP) ===");
@@ -845,8 +848,8 @@ static void phase3_run(void) {
counted = 0;
while (counted < PHASE3_TOTAL_ROUNDS) {
uint32_t tick = HAL_GetTick();
int echo_ok = 0;
int is_probe = 0;
int32_t echo_ok = 0;
int32_t is_probe = 0;
while (HAL_GetTick() - tick < 30000) {
uint16_t rlen = ch395f_get_recv_len(sock);
if (rlen > 8) {
@@ -925,7 +928,7 @@ static void phase3_run(void) {
static void phase4_run(void) {
uint8_t ip_info[20] = {0};
int same = 0;
int32_t same = 0;
DBG_INFO("=== CH395F Phase 4 Tests (DHCP) ===");
@@ -1015,7 +1018,7 @@ static void phase4_run(void) {
ch395f_set_des_ip(anno_sock, bcast);
ch395f_set_des_port(anno_sock, 0);
int hello_ok = 0;
int32_t hello_ok = 0;
uint32_t wait = HAL_GetTick();
while (HAL_GetTick() - wait < PHASE4_HELLO_WAIT_MS) {
ch395f_get_glob_int_status_all();
@@ -1112,13 +1115,13 @@ static void phase5_run(void) {
static uint8_t exp[PHASE5_CHUNK_SIZE];
uint8_t sock = 0;
uint8_t ip_arr[4];
int chunks;
int bad = 0;
int conn_ok = 0;
int blk_tx = 0;
int blk_rx = 0;
int can_write = 1;
int progress_64k = -1;
int32_t chunks;
int32_t bad = 0;
int32_t conn_ok = 0;
int32_t blk_tx = 0;
int32_t blk_rx = 0;
int32_t can_write = 1;
int32_t progress_64k = -1;
uint32_t send_bytes;
uint32_t sum_tx = 0;
uint32_t sum_rx = 0;
@@ -1345,8 +1348,9 @@ void StartCh395fTestTask(void *argument)
{
(void)argument;
DBG_INFO("Test task started");
DBG_INFO("Test task started, suite=%s", TEST_SUITE_NAME);
#if defined(TEST_SUITE_CH395F)
#if defined(ENABLE_HW_LAYER_TESTS)
/* 硬件层模式(阶段 1~5netTask 未创建,由本任务完成一次性协议栈
* 初始化并独占 CH395F —— 无 net_poll() 并发读取中断,无 SPI 并发事务 */
@@ -1384,8 +1388,13 @@ void StartCh395fTestTask(void *argument)
#if defined(ENABLE_PHASE7_TESTS)
net_layer_client_test_main();
#else
net_layer_test_main();
net_layer_test_main();
#endif
#endif /* TEST_SUITE_CH395F */
#elif defined(TEST_SUITE_STORAGE)
storage_test_run();
#elif defined(TEST_SUITE_GD5F)
gd5f_test_run();
#endif
for (;;) {

89
test/gd5f_test.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* 模块名称GD5F2GQ5UE Driver Test Suite
* 模块功能GD5F2GQ5UE SPI NAND Flash 驱动测试入口,按阶段组织测试用例,
* 覆盖 gd5f2gq5ue.c 全部公开/私有函数,并延伸到 FTLnand_ftl
* FatFS diskio。详见 docs/GD5F2GQ5UE_Test_Guide.md。
* 适用平台STM32F4 系列GD5F2GQ5UE SPI NAND
* 作者:王建锋
* 创建日期2026-08-26
*
* === 测试阶段一览(与指南 TC 一一对应)===
* Phase | 描述
* ---------|-----------------------------------------------
* Phase 1 | 初始化与 ID 识别init / read_id / reset
* Phase 2 | 坏块管理BBTscan / is_block_bad / mark+clear / rescan
* Phase 3 | 页读写(跨页 / 非对齐 / 多页 / 单字节)
* Phase 4 | 块擦除(单块 / 多块 / 非对齐负向 / 擦写读闭环)
* Phase 5 | ECC 验证(开启下写读 / check_ecc
* Phase 6 | SPI 原语page_read / program_load+exec / block_erase / write_enable / wait_busy
* Phase 7 | DMA 边界(阈值 32/33/整页)
* Phase 8 | FTL 层nand_ftl_init / nand_ftl_format
* Phase 9 | FatFS diskio可选
* Phase 10 | 边界条件offset 越界 / size=0 / 未擦除写入 / 写保护解除)
*/
#ifndef __GD5F_TEST_H
#define __GD5F_TEST_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "gd5f2gq5ue.h"
/* ======================== 阶段使能开关 ======================== */
/* 取消注释对应阶段即可启用;总开关 ENABLE_GD5F_TESTS 控制是否编译并运行测试。
* 启用后defaultTask 中的常规 FTL/FatFS 存储测试会被跳过(独占 NAND
* 注意:测试会擦除/写入若干块,且 Phase 8/9 会格式化 FTL/FatFS仅用于测试构建。 */
//#define ENABLE_GD5F_TESTS /* 总开关(由 test_config.h 的 TEST_SUITE_GD5F 自动开启) */
//#define ENABLE_GD5F_PHASE1_TESTS
//#define ENABLE_GD5F_PHASE2_TESTS
//#define ENABLE_GD5F_PHASE3_TESTS
//#define ENABLE_GD5F_PHASE4_TESTS
//#define ENABLE_GD5F_PHASE5_TESTS
//#define ENABLE_GD5F_PHASE6_TESTS
//#define ENABLE_GD5F_PHASE7_TESTS
//#define ENABLE_GD5F_PHASE8_TESTS
//#define ENABLE_GD5F_PHASE9_TESTS
//#define ENABLE_GD5F_PHASE10_TESTS
/* ======================== 测试统计与断言 ======================== */
typedef struct {
uint16_t total;
uint16_t passed;
uint16_t failed;
} gd5f_test_stats_t;
extern gd5f_test_stats_t g_gd5f_test_stats;
#define GD5F_TEST_CHECK(cond, fmt, ...) do { \
g_gd5f_test_stats.total++; \
if (cond) { \
g_gd5f_test_stats.passed++; \
DBG_INFO("[PASS] " fmt, ##__VA_ARGS__); \
} else { \
g_gd5f_test_stats.failed++; \
DBG_ERROR("[FAIL] " fmt, ##__VA_ARGS__); \
} \
} while (0)
#define GD5F_TEST_REPORT(name) do { \
DBG_INFO("=== %s: %d/%d PASSED (failed=%d) ===", \
name, g_gd5f_test_stats.passed, g_gd5f_test_stats.total, \
g_gd5f_test_stats.failed); \
} while (0)
#if defined(ENABLE_GD5F_TESTS)
void gd5f_test_run(void);
#else
#define gd5f_test_run() ((void)0)
#endif
#ifdef __cplusplus
}
#endif
#endif /* __GD5F_TEST_H */

750
test/gd5f_test_task.c Normal file
View 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 */

View File

@@ -51,15 +51,15 @@
* =================================================================== */
#ifdef ENABLE_PHASE6_TESTS
static void phase6_run(int sockfd) {
static void phase6_run(int32_t sockfd) {
/*
* 单连接 TCP EchoNET_MSG_DONTWAIT 非阻塞收,原样回显。
* 连接断开由 net 层自动重监听auto_relisten应用层不维护生命周期
* 字节完整性由 PC 侧校验data == test_data固件不做每连接自检。
*/
int n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, NET_MSG_DONTWAIT);
int32_t n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, NET_MSG_DONTWAIT);
if (n > 0) {
int sent = net_send(sockfd, s_rx_buf, n, 0);
int32_t sent = net_send(sockfd, s_rx_buf, n, 0);
if (sent != n) {
DBG_ERROR("echo: send %d != recv %d", sent, n);
}
@@ -119,9 +119,9 @@ static uint32_t phase7_cksum(const uint8_t *p, uint32_t n) {
/* 单次 TCP Client 会话:对应 TC 701 主体 / TC 702 单轮。
* 每轮使用不同本地端口 local_port 规避 TIME_WAIT。
* 返回 1 表示全流程通过0 表示中途失败(已记录 TEST_CHECK。 */
static int phase7_session(uint16_t local_port)
static int32_t phase7_session(uint16_t local_port)
{
int sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0);
int32_t sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0);
if (sockfd < 0) {
TEST_CHECK(0, "TC701 socket create");
return 0;
@@ -149,14 +149,14 @@ static int phase7_session(uint16_t local_port)
DBG_INFO("TC701 connecting %s:%d (local port %d)",
PHASE7_REMOTE_IP, PHASE7_REMOTE_PORT, local_port);
int rc = net_connect(sockfd, (struct net_sockaddr *)&raddr, sizeof(raddr));
int32_t rc = net_connect(sockfd, (struct net_sockaddr *)&raddr, sizeof(raddr));
TEST_CHECK(rc == 0, "TC701 connect issued (ret=%d)", rc);
if (rc != 0) { net_close(sockfd); return 0; }
/* 等待连接建立:轮询 socket 状态ESTABLISHED
* 用 net_select 触发 net_poll 并作为 100ms 节拍,状态以 net_get_sock 为准。 */
{
int connected = 0;
int32_t connected = 0;
uint32_t tick = HAL_GetTick();
while (HAL_GetTick() - tick < PHASE7_CONN_TIMEOUT_MS) {
net_sock_t *sk = net_get_sock(sockfd);
@@ -175,16 +175,16 @@ static int phase7_session(uint16_t local_port)
/* 数据交换64B 递增模式 */
uint8_t tx[PHASE7_EXCH_SIZE];
uint8_t rx[PHASE7_EXCH_SIZE];
for (int i = 0; i < PHASE7_EXCH_SIZE; i++) tx[i] = (uint8_t)i;
for (int32_t i = 0; i < PHASE7_EXCH_SIZE; i++) tx[i] = (uint8_t)i;
int sent = net_send(sockfd, tx, PHASE7_EXCH_SIZE, 0);
int32_t sent = net_send(sockfd, tx, PHASE7_EXCH_SIZE, 0);
TEST_CHECK(sent == PHASE7_EXCH_SIZE, "TC701 send %dB (sent=%d)", PHASE7_EXCH_SIZE, sent);
if (sent != PHASE7_EXCH_SIZE) { net_close(sockfd); return 0; }
int recvd = 0;
int32_t recvd = 0;
uint32_t tick = HAL_GetTick();
while (recvd < PHASE7_EXCH_SIZE) {
int n = net_recv(sockfd, rx + recvd, PHASE7_EXCH_SIZE - recvd, NET_MSG_DONTWAIT);
int32_t n = net_recv(sockfd, rx + recvd, PHASE7_EXCH_SIZE - recvd, NET_MSG_DONTWAIT);
if (n > 0) {
recvd += n;
} else if (n == 0) {
@@ -199,10 +199,10 @@ static int phase7_session(uint16_t local_port)
if (HAL_GetTick() - tick > PHASE7_CONN_TIMEOUT_MS) break;
}
int ok = (recvd == PHASE7_EXCH_SIZE);
int32_t ok = (recvd == PHASE7_EXCH_SIZE);
if (ok) {
int bad = 0;
for (int i = 0; i < PHASE7_EXCH_SIZE; i++) {
int32_t bad = 0;
for (int32_t i = 0; i < PHASE7_EXCH_SIZE; i++) {
if (rx[i] != (uint8_t)i) { bad = 1; break; }
}
TEST_CHECK(!bad, "TC701 byte-compare %dB", PHASE7_EXCH_SIZE);
@@ -220,7 +220,7 @@ static int phase7_session(uint16_t local_port)
* 701/702 要求服务端在线703 要求服务端离线,故 703 应在独立的一轮中运行)。 */
static void phase7_test_timeout(void)
{
int sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0);
int32_t sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0);
if (sockfd < 0) { TEST_CHECK(0, "TC703 socket create"); return; }
struct net_sockaddr_in raddr;
@@ -231,11 +231,11 @@ static void phase7_test_timeout(void)
DBG_INFO("TC703 connecting %s:%d (expect TIMEOUT, PC server must be OFF)",
PHASE7_REMOTE_IP, PHASE7_REMOTE_PORT);
int rc = net_connect(sockfd, (struct net_sockaddr *)&raddr, sizeof(raddr));
int32_t rc = net_connect(sockfd, (struct net_sockaddr *)&raddr, sizeof(raddr));
TEST_CHECK(rc == 0, "TC703 connect issued (ret=%d)", rc);
if (rc != 0) { net_close(sockfd); return; }
int established = 0;
int32_t established = 0;
uint32_t tick = HAL_GetTick();
while (HAL_GetTick() - tick < PHASE7_TIMEOUT_TEST_MS) {
net_sock_t *sk = net_get_sock(sockfd);
@@ -262,19 +262,19 @@ static void phase7_test_timeout(void)
#define PHASE7_MULTI_BASE_PORT 51000
#endif
static int phase7_session_multi(void)
static int32_t phase7_session_multi(void)
{
int sockfd[PHASE7_MULTI_COUNT];
int32_t sockfd[PHASE7_MULTI_COUNT];
uint8_t tx[PHASE7_EXCH_SIZE];
uint8_t rx[PHASE7_EXCH_SIZE];
for (int i = 0; i < PHASE7_EXCH_SIZE; i++) tx[i] = (uint8_t)i;
for (int32_t i = 0; i < PHASE7_EXCH_SIZE; i++) tx[i] = (uint8_t)i;
/* 1) 打开并连接所有 Socket各自绑定不同本地端口规避 TIME_WAIT */
for (int i = 0; i < PHASE7_MULTI_COUNT; i++) {
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
sockfd[i] = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0);
if (sockfd[i] < 0) {
TEST_CHECK(0, "TC704 socket create #%d", i);
for (int j = 0; j < i; j++) net_close(sockfd[j]);
for (int32_t j = 0; j < i; j++) net_close(sockfd[j]);
return 0;
}
struct net_sockaddr_in laddr;
@@ -284,7 +284,7 @@ static int phase7_session_multi(void)
if (net_bind(sockfd[i], (struct net_sockaddr *)&laddr, sizeof(laddr)) < 0) {
TEST_CHECK(0, "TC704 bind #%d port %d", i, PHASE7_MULTI_BASE_PORT + i);
net_close(sockfd[i]);
for (int j = 0; j < i; j++) net_close(sockfd[j]);
for (int32_t j = 0; j < i; j++) net_close(sockfd[j]);
return 0;
}
struct net_sockaddr_in raddr;
@@ -292,11 +292,11 @@ static int phase7_session_multi(void)
raddr.sin_family = NET_AF_INET;
raddr.sin_port = net_htons((uint16_t)PHASE7_REMOTE_PORT);
raddr.sin_addr.s_addr = net_inet_addr(PHASE7_REMOTE_IP);
int rc = net_connect(sockfd[i], (struct net_sockaddr *)&raddr, sizeof(raddr));
int32_t rc = net_connect(sockfd[i], (struct net_sockaddr *)&raddr, sizeof(raddr));
TEST_CHECK(rc == 0, "TC704 connect #%d issued (ret=%d)", i, rc);
if (rc != 0) {
net_close(sockfd[i]);
for (int j = 0; j < i; j++) net_close(sockfd[j]);
for (int32_t j = 0; j < i; j++) net_close(sockfd[j]);
return 0;
}
}
@@ -304,10 +304,10 @@ static int phase7_session_multi(void)
/* 2) 等待所有连接建立(或超时) */
{
uint32_t tick = HAL_GetTick();
int all_up = 0;
int32_t all_up = 0;
while (HAL_GetTick() - tick < PHASE7_CONN_TIMEOUT_MS) {
all_up = 1;
for (int i = 0; i < PHASE7_MULTI_COUNT; i++) {
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
net_sock_t *sk = net_get_sock(sockfd[i]);
if (!sk || sk->state != NET_SOCK_STATE_ESTABLISHED) { all_up = 0; break; }
}
@@ -316,7 +316,7 @@ static int phase7_session_multi(void)
}
TEST_CHECK(all_up, "TC704 all %d sockets established within %dms", PHASE7_MULTI_COUNT, PHASE7_CONN_TIMEOUT_MS);
if (!all_up) {
for (int i = 0; i < PHASE7_MULTI_COUNT; i++) {
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
net_sock_t *sk = net_get_sock(sockfd[i]);
if (sk) DBG_ERROR("TC704 socket #%d state=%d", i, sk->state);
net_close(sockfd[i]);
@@ -326,18 +326,18 @@ static int phase7_session_multi(void)
}
/* 3) 每路独立收发回显 */
int ok = 1;
for (int i = 0; i < PHASE7_MULTI_COUNT; i++) {
int sent = net_send(sockfd[i], tx, PHASE7_EXCH_SIZE, 0);
int32_t ok = 1;
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) {
int32_t sent = net_send(sockfd[i], tx, PHASE7_EXCH_SIZE, 0);
if (sent != PHASE7_EXCH_SIZE) {
TEST_CHECK(0, "TC704 socket #%d send %dB (sent=%d)", i, PHASE7_EXCH_SIZE, sent);
ok = 0;
continue;
}
int recvd = 0;
int32_t recvd = 0;
uint32_t tick = HAL_GetTick();
while (recvd < PHASE7_EXCH_SIZE) {
int n = net_recv(sockfd[i], rx + recvd, PHASE7_EXCH_SIZE - recvd, NET_MSG_DONTWAIT);
int32_t n = net_recv(sockfd[i], rx + recvd, PHASE7_EXCH_SIZE - recvd, NET_MSG_DONTWAIT);
if (n > 0) recvd += n;
else if (n == 0) break;
else if (n < 0 && net_get_errno() == NET_ERR_WOULDBLOCK) {
@@ -347,10 +347,10 @@ static int phase7_session_multi(void)
} else break;
if (HAL_GetTick() - tick > PHASE7_CONN_TIMEOUT_MS) break;
}
int sock_ok = (recvd == PHASE7_EXCH_SIZE);
int32_t sock_ok = (recvd == PHASE7_EXCH_SIZE);
if (sock_ok) {
int bad = 0;
for (int k = 0; k < PHASE7_EXCH_SIZE; k++) if (rx[k] != (uint8_t)k) { bad = 1; break; }
int32_t bad = 0;
for (int32_t k = 0; k < PHASE7_EXCH_SIZE; k++) if (rx[k] != (uint8_t)k) { bad = 1; break; }
TEST_CHECK(!bad, "TC704 socket #%d echo %dB OK", i, PHASE7_EXCH_SIZE);
} else {
TEST_CHECK(0, "TC704 socket #%d recv %dB (expect %d)", i, recvd, PHASE7_EXCH_SIZE);
@@ -359,7 +359,7 @@ static int phase7_session_multi(void)
}
/* 4) 关闭所有 */
for (int i = 0; i < PHASE7_MULTI_COUNT; i++) net_close(sockfd[i]);
for (int32_t i = 0; i < PHASE7_MULTI_COUNT; i++) net_close(sockfd[i]);
return ok;
}
@@ -373,8 +373,8 @@ void net_layer_client_test_main(void)
DBG_INFO("--- TC 702: close/reconnect x%d ---", PHASE7_LOOP_COUNT);
{
int all_ok = 1;
for (int i = 0; i < PHASE7_LOOP_COUNT; i++) {
int32_t all_ok = 1;
for (int32_t i = 0; i < PHASE7_LOOP_COUNT; i++) {
if (!phase7_session((uint16_t)(PHASE7_LOCAL_PORT + 1 + i))) all_ok = 0;
}
TEST_CHECK(all_ok, "TC702 reconnect x%d all passed", PHASE7_LOOP_COUNT);
@@ -445,7 +445,7 @@ static uint8_t s_tx_buf[TEST_BUF_SIZE];
static uint32_t phase8_crc32_step(uint32_t crc, const uint8_t *data, uint32_t len) {
for (uint32_t i = 0; i < len; i++) {
crc ^= data[i];
for (int k = 0; k < 8; k++) {
for (int32_t k = 0; k < 8; k++) {
crc = (crc & 1u) ? (crc >> 1) ^ 0xEDB88320u : (crc >> 1);
}
}
@@ -464,7 +464,7 @@ static void phase8_fill(uint8_t *buf, uint32_t size, uint32_t offset) {
/* 仅 ECHO/RECV 方向需要:逐字节校验接收数据 */
#if PHASE8_DIRECTION != PHASE8_DIR_SEND
static int phase8_verify(uint8_t *buf, uint32_t size, uint32_t offset) {
static int32_t phase8_verify(uint8_t *buf, uint32_t size, uint32_t offset) {
for (uint32_t i = 0; i < size; i++) {
if (buf[i] != (uint8_t)((offset + i) & 0xFF)) {
DBG_ERROR("Phase8: mismatch at %lu (expect 0x%02X, got 0x%02X)",
@@ -476,7 +476,7 @@ static int phase8_verify(uint8_t *buf, uint32_t size, uint32_t offset) {
}
#endif
static void phase8_run(int sockfd) {
static void phase8_run(int32_t sockfd) {
#if PHASE8_DIRECTION == PHASE8_DIR_RECV
/* PC 发送文件(尾附 4B CRC32MCU 接收并逐字节 + CRC32 校验 */
DBG_INFO("Phase8[RECV]: receiving %d-byte file", PHASE8_FILE_SIZE);
@@ -484,14 +484,14 @@ static void phase8_run(int sockfd) {
uint32_t recvd = 0;
uint32_t crc = 0xFFFFFFFFu;
while (recvd < PHASE8_FILE_SIZE) {
int to_read = (PHASE8_FILE_SIZE - recvd) > (TEST_BUF_SIZE - 4)
int32_t to_read = (PHASE8_FILE_SIZE - recvd) > (TEST_BUF_SIZE - 4)
? (TEST_BUF_SIZE - 4) : (int)(PHASE8_FILE_SIZE - recvd);
int n = net_recv(sockfd, s_rx_buf, to_read, 0);
int32_t n = net_recv(sockfd, s_rx_buf, to_read, 0);
if (n <= 0) {
DBG_ERROR("Phase8[RECV]: recv fail at %lu (ret=%d)", recvd, n);
return;
}
int ok = phase8_verify(s_rx_buf, (uint32_t)n, recvd);
int32_t ok = phase8_verify(s_rx_buf, (uint32_t)n, recvd);
TEST_CHECK(ok, "Phase8[RECV]: verify at %lu (%d bytes)", recvd, n);
if (!ok) return;
crc = phase8_crc32_step(crc, s_rx_buf, (uint32_t)n);
@@ -502,7 +502,7 @@ static void phase8_run(int sockfd) {
uint8_t foot[4];
uint32_t got = 0;
while (got < 4) {
int n = net_recv(sockfd, foot + got, (int)(4 - got), 0);
int32_t n = net_recv(sockfd, foot + got, (int)(4 - got), 0);
if (n <= 0) {
DBG_ERROR("Phase8[RECV]: footer recv fail (ret=%d)", n);
return;
@@ -527,7 +527,7 @@ static void phase8_run(int sockfd) {
uint32_t chunk = (PHASE8_FILE_SIZE - sent) > (uint32_t)TEST_BUF_SIZE
? (uint32_t)TEST_BUF_SIZE : (PHASE8_FILE_SIZE - sent);
phase8_fill(s_tx_buf, chunk, sent);
int n = net_send(sockfd, s_tx_buf, (int)chunk, 0);
int32_t n = net_send(sockfd, s_tx_buf, (int)chunk, 0);
if (n <= 0) {
DBG_ERROR("Phase8[SEND]: send fail at %lu (ret=%d)", sent, n);
return;
@@ -547,7 +547,7 @@ static void phase8_run(int sockfd) {
uint32_t chunk = (PHASE8_FILE_SIZE - sent) > (uint32_t)TEST_BUF_SIZE
? (uint32_t)TEST_BUF_SIZE : (PHASE8_FILE_SIZE - sent);
phase8_fill(s_tx_buf, chunk, sent);
int n = net_send(sockfd, s_tx_buf, (int)chunk, 0);
int32_t n = net_send(sockfd, s_tx_buf, (int)chunk, 0);
if (n <= 0) {
DBG_ERROR("Phase8[ECHO]: send fail at %lu (ret=%d)", sent, n);
return;
@@ -557,12 +557,12 @@ static void phase8_run(int sockfd) {
uint32_t recvd = 0;
while (recvd < PHASE8_FILE_SIZE) {
int n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, 0);
int32_t n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, 0);
if (n <= 0) {
DBG_ERROR("Phase8[ECHO]: recv fail at %lu (ret=%d)", recvd, n);
return;
}
int ok = phase8_verify(s_rx_buf, (uint32_t)n, recvd);
int32_t ok = phase8_verify(s_rx_buf, (uint32_t)n, recvd);
TEST_CHECK(ok, "Phase8[ECHO]: verify at %lu (%d bytes)", recvd, n);
if (!ok) return;
recvd += (uint32_t)n;
@@ -595,7 +595,7 @@ static void phase8_run(int sockfd) {
#include "net_select.h"
static uint32_t s_phase9_count = 0;
static void phase9_run(int sockfd) {
static void phase9_run(int32_t sockfd) {
net_fd_set readfds;
net_timeval tv;
@@ -609,14 +609,14 @@ static void phase9_run(int sockfd) {
tv.tv_sec = 0;
tv.tv_usec = 50000;
int nfds = net_select(sockfd + 1, &readfds, NULL, NULL, &tv);
int32_t nfds = net_select(sockfd + 1, &readfds, NULL, NULL, &tv);
if (nfds <= 0) return;
/* Socket 0 可读:非阻塞接收并回显 */
if (NET_FD_ISSET(sockfd, &readfds)) {
int n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, NET_MSG_DONTWAIT);
int32_t n = net_recv(sockfd, s_rx_buf, TEST_BUF_SIZE, NET_MSG_DONTWAIT);
if (n > 0) {
int sent = net_send(sockfd, s_rx_buf, n, 0);
int32_t sent = net_send(sockfd, s_rx_buf, n, 0);
if (sent == n) {
s_phase9_count++;
TEST_CHECK(1, "Phase9: select echo #%lu (%d bytes)", s_phase9_count, n);
@@ -645,7 +645,7 @@ static void phase9_run(int sockfd) {
* =================================================================== */
#ifdef ENABLE_PHASE10_TESTS
static int s_phase10_stage = 0;
static int32_t s_phase10_stage = 0;
static uint32_t s_phase10_recv = 0;
static void phase10_fill(uint8_t *buf, uint32_t size, uint8_t seed) {
@@ -654,19 +654,19 @@ static void phase10_fill(uint8_t *buf, uint32_t size, uint8_t seed) {
}
}
static int phase10_verify(uint8_t *buf, uint32_t size, uint8_t seed) {
static int32_t phase10_verify(uint8_t *buf, uint32_t size, uint8_t seed) {
for (uint32_t i = 0; i < size; i++) {
if (buf[i] != (uint8_t)(seed + i)) return 0;
}
return 1;
}
static void phase10_run(int sockfd) {
static void phase10_run(int32_t sockfd) {
/* 阶段 0 → 1发送 4095B */
if (s_phase10_stage == 0) {
DBG_INFO("Phase10: 4095B (DMA-1)");
phase10_fill(s_tx_buf, 4095, 0x10);
int sent = net_send(sockfd, s_tx_buf, 4095, 0);
int32_t sent = net_send(sockfd, s_tx_buf, 4095, 0);
TEST_CHECK(sent == 4095, "Phase10: send 4095B (sent=%d)", sent);
s_phase10_recv = 0;
s_phase10_stage = 1;
@@ -674,7 +674,7 @@ static void phase10_run(int sockfd) {
}
/* 阶段 1接收并验证 4095B 回显 */
if (s_phase10_stage == 1) {
int n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4095 - s_phase10_recv), 0);
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4095 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 4095) {
@@ -688,7 +688,7 @@ static void phase10_run(int sockfd) {
if (s_phase10_stage == 2) {
DBG_INFO("Phase10: 4096B (DMA exact)");
phase10_fill(s_tx_buf, 4096, 0x20);
int sent = net_send(sockfd, s_tx_buf, 4096, 0);
int32_t sent = net_send(sockfd, s_tx_buf, 4096, 0);
TEST_CHECK(sent == 4096, "Phase10: send 4096B (sent=%d)", sent);
s_phase10_recv = 0;
s_phase10_stage = 3;
@@ -696,7 +696,7 @@ static void phase10_run(int sockfd) {
}
/* 阶段 3接收并验证 4096B 回显 */
if (s_phase10_stage == 3) {
int n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4096 - s_phase10_recv), 0);
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4096 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 4096) {
@@ -710,7 +710,7 @@ static void phase10_run(int sockfd) {
if (s_phase10_stage == 4) {
DBG_INFO("Phase10: 4097B (DMA+1, 2 SPI xacts)");
phase10_fill(s_tx_buf, 4097, 0x30);
int sent = net_send(sockfd, s_tx_buf, 4097, 0);
int32_t sent = net_send(sockfd, s_tx_buf, 4097, 0);
TEST_CHECK(sent == 4097, "Phase10: send 4097B (sent=%d)", sent);
s_phase10_recv = 0;
s_phase10_stage = 5;
@@ -718,7 +718,7 @@ static void phase10_run(int sockfd) {
}
/* 阶段 5接收并验证 4097B 回显 */
if (s_phase10_stage == 5) {
int n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4097 - s_phase10_recv), 0);
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(4097 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 4097) {
@@ -731,7 +731,7 @@ static void phase10_run(int sockfd) {
/* 阶段 6零长度发送 —— 期望 net_send 返回 -1NET_ERR_INVAL */
if (s_phase10_stage == 6) {
DBG_INFO("Phase10: zero-length send");
int sent = net_send(sockfd, s_tx_buf, 0, 0);
int32_t sent = net_send(sockfd, s_tx_buf, 0, 0);
TEST_CHECK(sent < 0, "Phase10: send 0B returns %d (expect <0)", sent);
s_phase10_stage = 7;
return;
@@ -740,8 +740,8 @@ static void phase10_run(int sockfd) {
if (s_phase10_stage == 7) {
DBG_INFO("Phase10: 20x rapid sends (100B each)");
phase10_fill(s_tx_buf, 100, 0x40);
int all_ok = 1;
for (int i = 0; i < 20; i++) {
int32_t all_ok = 1;
for (int32_t i = 0; i < 20; i++) {
if (net_send(sockfd, s_tx_buf, 100, 0) != 100) {
TEST_CHECK(0, "Phase10: rapid #%d fail", i + 1);
all_ok = 0; break;
@@ -759,7 +759,7 @@ static void phase10_run(int sockfd) {
}
/* 阶段 8接收 2000B 回显总和并验证 */
if (s_phase10_stage == 8) {
int n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(2000 - s_phase10_recv), 0);
int32_t n = net_recv(sockfd, s_rx_buf + s_phase10_recv, (int)(2000 - s_phase10_recv), 0);
if (n > 0) {
s_phase10_recv += (uint32_t)n;
if (s_phase10_recv >= 2000) {
@@ -787,10 +787,10 @@ static void phase10_run(int sockfd) {
* =================================================================== */
#ifdef ENABLE_PHASE11_TESTS
static int s_phase11_armed = 0;
static int32_t s_phase11_armed = 0;
static uint32_t s_phase11_start = 0;
static void phase11_run(int sockfd) {
static void phase11_run(int32_t sockfd) {
/*
* 第一次调用:记录开始时间并 arm。
* 后续调用:非阻塞 recv若返回 0 表示连接已断开KeepAlive 触发)。
@@ -803,7 +803,7 @@ static void phase11_run(int sockfd) {
return;
}
int n = net_recv(sockfd, s_rx_buf, 64, NET_MSG_DONTWAIT);
int32_t n = net_recv(sockfd, s_rx_buf, 64, NET_MSG_DONTWAIT);
if (n == 0) {
uint32_t elapsed = HAL_GetTick() - s_phase11_start;
TEST_CHECK(elapsed >= 60000,
@@ -816,11 +816,11 @@ static void phase11_run(int sockfd) {
void net_layer_test_main(void)
{
int sockfd = -1;
int phase_active = 0;
int32_t sockfd = -1;
int32_t phase_active = 0;
uint32_t accept_retry = 0;
struct net_sockaddr_in client_addr;
int addrlen = sizeof(client_addr);
int32_t addrlen = sizeof(client_addr);
sockfd = net_socket(NET_AF_INET, NET_SOCK_STREAM, 0);
if (sockfd < 0) {
@@ -855,7 +855,7 @@ void net_layer_test_main(void)
*/
for (;;) {
if (!phase_active) {
int acc = net_accept(sockfd, (struct net_sockaddr *)&client_addr, &addrlen);
int32_t acc = net_accept(sockfd, (struct net_sockaddr *)&client_addr, &addrlen);
if (acc < 0) {
if (accept_retry % 100 == 0) {
DBG_INFO("Test task: waiting for connection...");

57
test/storage_test.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* 模块名称Storage Test Suite
* 模块功能FTL + FatFS 存储测试的声明与断言宏。实现见 storage_test_task.c
* 统一由 ch395fTestTask 调度;正常产品构建下由 defaultTask 调用,
* 行为与原 defaultTask 内联逻辑一致。
* 适用平台STM32F4 系列
* 作者:王建锋
* 创建日期2026-08-26
*/
#ifndef __STORAGE_TEST_H
#define __STORAGE_TEST_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct {
uint16_t total;
uint16_t passed;
uint16_t failed;
} storage_test_stats_t;
extern storage_test_stats_t g_storage_stats;
#define STORAGE_TEST_CHECK(cond, fmt, ...) do { \
g_storage_stats.total++; \
if (cond) { \
g_storage_stats.passed++; \
DBG_INFO(" [PASS] " fmt, ##__VA_ARGS__); \
} else { \
g_storage_stats.failed++; \
DBG_ERROR(" [FAIL] " fmt, ##__VA_ARGS__); \
} \
} while (0)
#define STORAGE_TEST_REPORT(name) do { \
DBG_INFO("=== %s: %d/%d PASSED (failed=%d) ===", \
name, g_storage_stats.passed, g_storage_stats.total, \
g_storage_stats.failed); \
} while (0)
/* 存储测试入口FTL + FatFS 文件系统与性能测试 */
void storage_test_run(void);
/* 存储套件恢复测试(默认关闭):取消注释并编译 STORAGE 套件运行一次,
* 即可重建 BBT清除 TC-STO-03 等注入的坏块)并重新格式化,恢复设备干净态。
* 恢复完成后建议重新注释并编译正常构建。 */
//#define ENABLE_STORAGE_RECOVERY_TESTS
#ifdef __cplusplus
}
#endif
#endif /* __STORAGE_TEST_H */

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

73
test/test_config.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* 模块名称:统一测试套件选择
* 模块功能:集中选择当前构建要运行的测试套件。所有测试统一由
* ch395fTestTaskStartCh395fTestTask调度defaultTask 在测试
* 构建下空闲,正常产品构建下仅运行存储挂载/测试。
* 适用平台STM32F4 系列
* 作者:王建锋
* 创建日期2026-08-26
*
* 一次构建仅启用一个 TEST_SUITE_*(或都不启用 = 正常产品构建):
* TEST_SUITE_CH395F : CH395F 测试(沿用 ch395f_test.h 阶段宏HW 1~5 / NET 6~11
* TEST_SUITE_STORAGE : FTL + FatFS 存储测试(从 defaultTask 迁入)
* TEST_SUITE_GD5F : GD5F2GQ5UE NAND 驱动测试
*/
#ifndef __TEST_CONFIG_H
#define __TEST_CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* === 选择当前构建的测试套件(三选一,或都注释 = 正常产品构建)=== */
//#define TEST_SUITE_CH395F
//#define TEST_SUITE_STORAGE
//#define TEST_SUITE_GD5F
#if defined(TEST_SUITE_CH395F) + defined(TEST_SUITE_STORAGE) + defined(TEST_SUITE_GD5F) > 1
#error "测试套件互斥:一次构建只能启用一个 TEST_SUITE_*"
#endif
#if defined(TEST_SUITE_CH395F) || defined(TEST_SUITE_STORAGE) || defined(TEST_SUITE_GD5F)
#define TEST_SUITE_SELECTED
#endif
#ifdef TEST_SUITE_CH395F
#include "ch395f_test.h"
#define TEST_SUITE_NAME "CH395F"
#endif
#ifdef TEST_SUITE_STORAGE
#include "storage_test.h"
#define TEST_SUITE_NAME "STORAGE"
#endif
#ifdef TEST_SUITE_GD5F
/* 选中 GD5F 套件时,自动开启 gd5f 测试总开关与全部阶段 */
#define ENABLE_GD5F_TESTS
#define ENABLE_GD5F_PHASE1_TESTS
#define ENABLE_GD5F_PHASE2_TESTS
#define ENABLE_GD5F_PHASE3_TESTS
#define ENABLE_GD5F_PHASE4_TESTS
#define ENABLE_GD5F_PHASE5_TESTS
#define ENABLE_GD5F_PHASE6_TESTS
#define ENABLE_GD5F_PHASE7_TESTS
#define ENABLE_GD5F_PHASE8_TESTS
#define ENABLE_GD5F_PHASE9_TESTS
#define ENABLE_GD5F_PHASE10_TESTS
#include "gd5f_test.h"
#define TEST_SUITE_NAME "GD5F"
#endif
#ifndef TEST_SUITE_NAME
#define TEST_SUITE_NAME "NONE"
#endif
#ifdef __cplusplus
}
#endif
#endif /* __TEST_CONFIG_H */