/******************************************************************************
 * 模块名称：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 */
