Files
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号)
一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、
采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。

门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏
undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8
reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF;
id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。

test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字,
是本批唯一改变构建产物的文件集合。

为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged
部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、
不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。

实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01%
(基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 /
告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。

基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处
引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使
识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。

⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二
根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有),
今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。

固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0;
App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889
断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿;
check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带
BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。

.cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法:
Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。

同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%,
RW_CCM 57,376 B / 87.9%,阈值 92%。
2026-10-26 12:28:14 +08:00

986 lines
38 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* 模块名称:GD5F2GQ5UE Driver Test Task
* 模块功能:GD5F2GQ5UE 驱动全阶段测试入口,统一在 defaultTask 上下文(同步阻塞)
* 中运行。各阶段由 gd5f_test.h 中的 ENABLE_GD5F_PHASE*_TESTS 宏独立开关,
* 覆盖 init/read_id/reset、BBT、页读写、块擦除、ECC、SPI 原语、DMA 边界与
* DMA 可达性守卫负路径、FTL 与 FatFS。所有结果通过 GD5F_TEST_CHECK /
* GD5F_TEST_REPORT 输出串口。
* 作者:王建锋
* 创建日期:2026-08-26
*
* 注意:NAND 不可原地覆盖,且同一页在一次擦除内只能编程一次。因此每个会写入的子
* 测试开始前都先擦除目标块,避免非法"部分页重编程"导致数据异常或失败。
******************************************************************************/
#include <string.h>
#include "FreeRTOS.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];
/* CCM(SRAM3)区间:DMA1/DMA2 物理不可达,用于守卫负路径的前提判定 */
#define GD5F_TEST_CCM_BASE 0x10000000UL
#define GD5F_TEST_CCM_END 0x10010000UL
/* -------------------------------------------------------------------
* 辅助:从最高块向下寻找连续 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); /* 管理区内的一个好块 */
/* 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);
#ifdef ENABLE_DESTRUCTIVE_TESTS
{
uint8_t flash_bbt[GD5F_TOTAL_BLOCKS / 8];
uint32_t ver = 0;
uint32_t cnt_a, cnt_b;
int32_t marked;
/* 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();
}
#else
DBG_INFO("TC-GD5F-206/207 skipped: destructive tests disabled "
"(BBT flash persist + pool wipe)");
#endif
}
}
#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 边界与 DMA 可达性守卫(阈值见 GD5F_DMA_THRESHOLD)
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE7_TESTS
static void gd5f_phase7(void)
{
uint32_t blk;
int32_t base;
int32_t ret, ok, i;
uint8_t *p_heap; /* 堆缓冲探针(堆驻留 CCM → DMA 不可达) */
uintptr_t p_addr;
DBG_INFO("=== GD5F Phase 7: DMA boundary (threshold %d) ===",
(int)GD5F_DMA_THRESHOLD);
blk = gd5f_find_run(1);
base = (int32_t)blk * GD5F_BLOCK_SIZE;
#ifdef ENABLE_DESTRUCTIVE_TESTS
/* TC-GD5F-701: 阈值下界(= 阈值,轮询路径) */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < (int32_t)GD5F_DMA_THRESHOLD; i++) {
s_wbuf[i] = (uint8_t)(i + 0x10);
}
ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_DMA_THRESHOLD);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-701 write(%d) ret=%d",
(int)GD5F_DMA_THRESHOLD, ret);
memset(s_rbuf, 0, GD5F_DMA_THRESHOLD);
ret = gd5f2gq5ue_read(base, s_rbuf, GD5F_DMA_THRESHOLD);
ok = 1;
for (i = 0; i < (int32_t)GD5F_DMA_THRESHOLD; 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: 阈值上界(= 阈值+1,DMA 路径,缓冲在 SRAM 静态区) */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < (int32_t)GD5F_DMA_THRESHOLD + 1; i++) {
s_wbuf[i] = (uint8_t)(i + 0x20);
}
ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_DMA_THRESHOLD + 1U);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-702 write(%d) ret=%d",
(int)GD5F_DMA_THRESHOLD + 1, ret);
memset(s_rbuf, 0, GD5F_DMA_THRESHOLD + 1U);
ret = gd5f2gq5ue_read(base, s_rbuf, GD5F_DMA_THRESHOLD + 1U);
ok = 1;
for (i = 0; i < (int32_t)GD5F_DMA_THRESHOLD + 1; 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");
#else
DBG_INFO("TC-GD5F-701/702/703 skipped: destructive tests disabled (write/erase)");
#endif
/* ================= DMA 可达性守卫(负路径) =================
* 探针缓冲取自 FreeRTOS 堆:堆驻留 CCM(0x10000000),DMA1/DMA2 不可达。
* 期望:驱动守卫拒绝该缓冲 → 回落 CPU 轮询 → 数据仍正确落位(轮询可写 CCM)。
* 判定依据(本板实证):若误走 DMA,CCM 内容不会改变(静默不落地),
* 故"数据与参考一致"即证明回落确实发生——非仅未报错。
* 堆不在 CCM 时本用例前提不成立(改为验证正路径无意义),跳过并记录。 */
p_heap = (uint8_t *)pvPortMalloc(GD5F_PAGE_SIZE);
if (p_heap == NULL) {
GD5F_TEST_CHECK(0, "TC-GD5F-704 pvPortMalloc(2048) failed");
} else {
p_addr = (uintptr_t)p_heap;
if ((p_addr < GD5F_TEST_CCM_BASE) || (p_addr >= GD5F_TEST_CCM_END)) {
DBG_INFO("TC-GD5F-704/705 SKIP: heap buf not in CCM (p=0x%08lX)",
(unsigned long)p_addr);
} else {
/* TC-GD5F-704: 读方向 —— 目的缓冲在 CCM,应回落轮询(只读,日常回归亦运行) */
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
(void)gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE); /* 参考读(SRAM 静态) */
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
p_heap[i] = (uint8_t)~s_rbuf[i]; /* 预填参考值反码:未落数必失配 */
}
ret = gd5f2gq5ue_read(base, p_heap, GD5F_PAGE_SIZE);
ok = (ret == GD5F_OK) &&
(memcmp(p_heap, s_rbuf, GD5F_PAGE_SIZE) == 0);
GD5F_TEST_CHECK(ok, "TC-GD5F-704 CCM(heap) rx poll fallback %s",
ok ? "OK" : "MISMATCH");
#ifdef ENABLE_DESTRUCTIVE_TESTS
/* TC-GD5F-705: 写方向 —— 源缓冲在 CCM,应回落轮询(需擦写) */
gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE);
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
p_heap[i] = (uint8_t)(i ^ 0x5A);
}
ret = gd5f2gq5ue_write(base, p_heap, GD5F_PAGE_SIZE);
GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-705 write(CCM src) 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 ^ 0x5A)) { ok = 0; break; }
}
GD5F_TEST_CHECK(ok, "TC-GD5F-705 CCM(heap) tx poll fallback %s",
ok ? "OK" : "MISMATCH");
#else
DBG_INFO("TC-GD5F-705 skipped: destructive tests disabled (write/erase)");
#endif
}
vPortFree(p_heap);
}
}
#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;
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);
#ifdef ENABLE_DESTRUCTIVE_TESTS
{
int32_t i, ne, ok;
/* 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");
}
#else
DBG_INFO("TC-GD5F-1003/1004 skipped: destructive tests disabled (write/erase)");
#endif
}
#endif /* ENABLE_GD5F_PHASE10_TESTS */
/* -------------------------------------------------------------------
* Phase 11 — 13h 锁存 / WIP 提前清零对照复现(2026-09-21 台架实验)
*
* 复现对象 = 09-20 两项读异常(归因/证伪史实见 GD5F Trap_Records
* TRAP-GD5F-05,本用例只承担复现职能):
* H1: 编程/擦除风暴后 13h 命令未被锁存(空操作)→ 0Bh 读残留缓存
* H2: WIP 早于 array→cache 整页装载完成而清零 → 0Bh 读混合页
* 设计:
* 每轮:擦块 → 64 页背靠背编程(风暴,每页唯一图案)→ 立即(零间隔,
* 最坏时序)13h 读 3 个非相邻页并逐字节比对。
* 读目标刻意避开最后编程的页(页 63):若 13h 为空操作,残留缓存即
* 最后一次编程的数据,读页 63 会巧合一致而掩盖故障;读其他页时,
* 空操作(整页陈旧 = 页 63 图案)与提前清零(前段新后段旧)都会
* 表现为不一致。
* 对照组:延迟 50ms 后重读同 3 页——立即读失败而延迟读成功 = 时间窗型故障。
* 首次失败时记录页头/尾 4B,用于区分 H1 / H2 签名。
* 影响面:仅擦写最高一块(gd5f_find_run(1) 取最高好块,远离 FTL 活跃区);
* 不写 BBT、不格式化卷。
* ------------------------------------------------------------------- */
#ifdef ENABLE_GD5F_PHASE11_TESTS
#define GD5F_P11_ROUNDS 16
static const uint32_t s_p11_probe[3] = {0, 21, 43};
static void gd5f_phase11(void)
{
uint32_t blk;
uint32_t page0;
int32_t round, pi, p, i;
int32_t ret;
int32_t storm_fail = 0;
int32_t imm_fail = 0, ctl_fail = 0;
int32_t imm_bad = 0, ctl_bad = 0;
int32_t first_bad = -1;
blk = gd5f_find_run(1);
if (blk >= GD5F_TOTAL_BLOCKS) {
GD5F_TEST_CHECK(0, "no usable block for repro");
GD5F_TEST_REPORT("GD5F Phase 11");
return;
}
page0 = blk * GD5F_PAGES_PER_BLOCK;
DBG_INFO("=== GD5F Phase 11: 13h latch / WIP repro (%d rounds, block %u) ===",
GD5F_P11_ROUNDS, (unsigned)blk);
for (round = 1; round <= GD5F_P11_ROUNDS; round++) {
int32_t round_bad = 0;
/* 风暴:擦除 + 64 页背靠背编程(图案 = i ^ p*0x11 ^ round*0x37) */
if (gd5f_block_erase(blk) != GD5F_OK) {
storm_fail++;
}
for (p = 0; p < GD5F_PAGES_PER_BLOCK; p++) {
for (i = 0; i < GD5F_PAGE_SIZE; i++) {
s_wbuf[i] = (uint8_t)(i ^ (p * 0x11) ^ (round * 0x37));
}
gd5f_write_enable();
if (gd5f_program_load(0, s_wbuf, GD5F_PAGE_SIZE) != GD5F_OK ||
gd5f_program_exec(page0 + (uint32_t)p) != GD5F_OK) {
storm_fail++;
}
}
/* 立即读(零间隔、最坏时序):3 个非相邻页逐字节比对 */
for (pi = 0; pi < 3; pi++) {
int32_t ok = 1;
uint8_t want;
ret = gd5f_page_read(page0 + s_p11_probe[pi]);
if (ret != GD5F_OK) {
ok = 0;
}
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
if (gd5f_read_from_cache(0, s_rbuf, GD5F_PAGE_SIZE) != GD5F_OK) {
ok = 0;
}
for (i = 0; ok && i < GD5F_PAGE_SIZE; i++) {
want = (uint8_t)(i ^ (s_p11_probe[pi] * 0x11) ^ (round * 0x37));
if (s_rbuf[i] != want) {
ok = 0;
}
}
if (!ok) {
imm_fail++;
round_bad = 1;
if (first_bad < 0) {
first_bad = round;
DBG_INFO("r=%d p=%u IMM mismatch head=%02X %02X %02X %02X "
"tail=%02X %02X %02X %02X",
round, (unsigned)s_p11_probe[pi],
s_rbuf[0], s_rbuf[1], s_rbuf[2], s_rbuf[3],
s_rbuf[GD5F_PAGE_SIZE - 4], s_rbuf[GD5F_PAGE_SIZE - 3],
s_rbuf[GD5F_PAGE_SIZE - 2], s_rbuf[GD5F_PAGE_SIZE - 1]);
}
}
}
/* 对照组:延迟 50ms 后重读同 3 页(区分时间窗型故障) */
HAL_Delay(50);
for (pi = 0; pi < 3; pi++) {
int32_t ok = 1;
uint8_t want;
ret = gd5f_page_read(page0 + s_p11_probe[pi]);
if (ret != GD5F_OK) {
ok = 0;
}
memset(s_rbuf, 0, GD5F_PAGE_SIZE);
if (gd5f_read_from_cache(0, s_rbuf, GD5F_PAGE_SIZE) != GD5F_OK) {
ok = 0;
}
for (i = 0; ok && i < GD5F_PAGE_SIZE; i++) {
want = (uint8_t)(i ^ (s_p11_probe[pi] * 0x11) ^ (round * 0x37));
if (s_rbuf[i] != want) {
ok = 0;
}
}
if (!ok) {
ctl_fail++;
ctl_bad = 1;
}
}
if (round_bad) {
imm_bad++;
}
if (round == 1 || (round & 3) == 0) {
DBG_INFO("progress r=%d/%d storm_fail=%d imm_fail=%d ctl_fail=%d",
round, GD5F_P11_ROUNDS, storm_fail, imm_fail, ctl_fail);
}
}
GD5F_TEST_CHECK(storm_fail == 0,
"storm ops: erase/program all OK (fail=%d)", storm_fail);
GD5F_TEST_CHECK(imm_bad == 0,
"immediate read after storm: %d/%d bad rounds (pages=%d first=%d)",
imm_bad, GD5F_P11_ROUNDS, imm_fail, first_bad);
GD5F_TEST_CHECK(ctl_fail == 0,
"delayed(50ms) read: %d/%d bad rounds (pages=%d)",
ctl_bad, GD5F_P11_ROUNDS, ctl_fail);
GD5F_TEST_REPORT("GD5F Phase 11");
}
#endif /* ENABLE_GD5F_PHASE11_TESTS */
/* -------------------------------------------------------------------
* 入口:按启用的阶段顺序执行并汇总
* ------------------------------------------------------------------- */
void gd5f_test_run(void)
{
memset(&g_gd5f_test_stats, 0, sizeof(g_gd5f_test_stats));
DBG_INFO("########## GD5F2GQ5UE Test Start ##########");
#ifdef ENABLE_DESTRUCTIVE_TESTS
DBG_INFO("mode: DESTRUCTIVE (write/erase/format/BBT-persist enabled)");
#else
DBG_INFO("mode: READ-ONLY (destructive cases skipped; set "
"ENABLE_DESTRUCTIVE_TESTS in test_config.h to run them)");
#endif
#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
#ifdef ENABLE_GD5F_PHASE11_TESTS
gd5f_phase11();
#endif
GD5F_TEST_REPORT("GD5F2GQ5UE Tests");
DBG_INFO("########## GD5F2GQ5UE Test Done ##########");
}
#endif /* ENABLE_GD5F_TESTS */