台架执行《实机测试实施指南》步骤 3~7 并闭环

步骤 3/4(IWDG V2/V4/V5 + 堆低水位 S11):
- V2 任务饿死 stall 后 ≤2.5s IWDG 复位;V4 关中断死循环 ~4.9s 稳定复位;
  V5 30min 120×FTP 全 PASS、零 IWDG 复位。堆告警全程未触发(min_ever=4728B)。
- task_watchdog.c:stall 证据写盘会经 nand_ftl_wd_feed 喂狗 → 增 s_stall_evidenced
  每次 stall 仅取一次证据,避免"取证据"本身阻止复位。
- default_task.c:堆低水位告警(HEAP_LOW_WATER_B=3072)。

步骤 5(GD5F 错误通道 S9):gd5f2gq5ue.c/.h + nand_ftl.c 错误码统一为 int32_t 并
全量检查返回值;套件回归 16/16 PASSED。
- test/run_all_suites.py:--forbidden 改为按套件覆盖(GD5F 用 "HardFault;assert")——
  TC-GD5F-704 故意触发 [ERR] 回落轮询,统一默认值会误判 FAIL。

步骤 6(网络稳定性):7.1 正常路径 ✅(V5 证据);7.2 N-2 丢弃可观测性代码已具备、
49s 静默在 V5 未复现(降级低风险观察项);7.3 N-3 待 PC peer。

步骤 7(工程卫生):
- S5:新增 test-cppcheck 钩子(仅 warning,排除模拟器桩)。
- S4:新增 scripts/check_test_traceability.py(默认仅告警)。
- 待核实项 TEST_SUITE_SELECTED 实机核实 = 死代码(无使用点);RTC 套件实际正常执行,
  真缺口 = 缺套件级汇总行 → sd2506_test_task.c 补 === RTC Tests: N/M PASSED === 并
  修正 Phase 级汇总累计问题;test_config.h 补齐 RTC 并注释"当前无引用"。

验证:产品 @build 0/0;pre-commit --all-files 17/17 Passed;
run_all_suites.py --suites RTC → PASS。
文档:实机测试实施指南 / SD2506_Test_Guide / 待处理内容 / 测试与架构改进建议 同步。
This commit is contained in:
2026-10-04 00:35:06 +08:00
parent aef1d92b3f
commit 029437be89
22 changed files with 631 additions and 151 deletions
+160 -39
View File
@@ -91,6 +91,7 @@ void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
const uint8_t *p_buf, size_t size);
static int gd5f_set_feature(uint8_t addr, uint8_t data);
static int gd5f_private_get_feature(uint8_t addr, uint8_t *p_data);
/* BBT 持久化内部函数 */
static int gd5f_private_bbt_locate_pool(void);
@@ -140,9 +141,17 @@ static int gd5f_dma_buf_ok(const void *p_buf, size_t size) {
/*
* 函数功能:等待芯片操作完成(轮询 OIP 位)
* 入口参数:timeout_ms - 超时时间 uint32_t > 0
* 返回值:0 - 操作完成,-2 - 超时
* 返回值:GD5F_OK(0) - 操作完成;GD5F_BUSY_TIMEOUT(-2) - 超时(可重试);
* GD5F_ERROR(-1) - SPI 传输真故障(不可重试,须上报自检)
* 限定条件:SPI 已初始化
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时。
* E-53(2026-10-03,GD5F 错误通道统一):三处 HAL 调用的返回值**必须**检查
* ——原实现全部丢弃,失败时 `status` 保持初值 0 → `(status & OIP) == 0`
* 立即成立 → **把"操作完成"伪造出来**,后续命令在未就绪的芯片上执行,
* 最终表现为"静默报成功但数据没落盘"。现区分两类失败:
* ① SPI 层失败(HAL≠OK)→ GD5F_ERROR(真故障);
* ② OIP 长时间不落 → GD5F_BUSY_TIMEOUT(可重试)。
* 调用点据此分级:超时重试 / 真故障上报自检并降级(见审计 §5 第二档 #1)。
*/
/* 看门狗心跳弱钩子:默认空实现(无看门狗/模拟器构建);强符号由
* App/util/task_watchdog.c 提供,路由到调用者任务的心跳位 */
@@ -155,7 +164,7 @@ __weak void nand_ftl_wd_beat(void) {
__weak void nand_ftl_wd_feed(void) {
}
int gd5f_wait_busy(uint32_t timeout_ms) {
int32_t gd5f_wait_busy(uint32_t timeout_ms) {
uint8_t cmd = GD5F_CMD_GET_FEATURE;
uint8_t addr = GD5F_REG_STATUS;
uint8_t status = 0;
@@ -166,12 +175,23 @@ int gd5f_wait_busy(uint32_t timeout_ms) {
nand_ftl_wd_feed(); /* 看门狗:入口直接喂狗(覆盖 defaultTask 长 I/O 阻塞主循环喂狗点的形态) */
while (1) {
HAL_StatusTypeDef hs;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, &status, 1, GD5F_SPI_TIMEOUT);
hs = HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
if (hs == HAL_OK) {
hs = HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
}
if (hs == HAL_OK) {
hs = HAL_SPI_Receive(&hspi1, &status, 1, GD5F_SPI_TIMEOUT);
}
GD5F_CS_HIGH();
if (hs != HAL_OK) {
/* E-53:SPI 真故障——绝不伪造成"操作完成"(详见函数说明) */
return GD5F_ERROR;
}
if ((status & GD5F_STATUS_OIP) == 0) {
return GD5F_OK;
}
@@ -192,7 +212,7 @@ int gd5f_wait_busy(uint32_t timeout_ms) {
* 返回值:0 - 成功
* 限定条件:SPI 已初始化
* 函数说明:CS# 拉低后发 06h 命令再拉高 */
int gd5f_write_enable(void) {
int32_t gd5f_write_enable(void) {
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
GD5F_CS_LOW();
@@ -208,7 +228,7 @@ int gd5f_write_enable(void) {
* 返回值:0 - 成功
* 限定条件:SPI 已初始化
* 函数说明:发 0Fh + C0h 地址后读 1 字节状态 */
int gd5f_read_status(uint8_t *p_status) {
int32_t gd5f_read_status(uint8_t *p_status) {
uint8_t cmd = GD5F_CMD_GET_FEATURE;
uint8_t addr = GD5F_REG_STATUS;
@@ -252,7 +272,7 @@ static void gd5f_page_read_issue(uint32_t page_addr) {
* 统一记于 docs/问题记录/GD5F2GQ5UE_Trap_Records.md Trap 05,
* 复现手段见测试 Phase 11 用例。
*/
int gd5f_page_read(uint32_t page_addr) {
int32_t gd5f_page_read(uint32_t page_addr) {
int ret;
gd5f_page_read_issue(page_addr);
@@ -272,7 +292,7 @@ int gd5f_page_read(uint32_t page_addr) {
* 返回值:0 - 成功
* 限定条件:必须先调用 gd5f_page_read 完成数据加载
* 函数说明:发 0Bh + 2字节列地址 +1字节 dummy 后读取数据 */
int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
size_t size) {
uint8_t cmd[4] = {0};
uint8_t use_dma = 0U;
@@ -355,7 +375,7 @@ static int gd5f_page_program(uint32_t page_addr, uint16_t column,
* (buffer 由 SPI 数据流直接填充,无 array→buffer 竞态,
* 无需二次装载;program exec 的落盘校验由 FTL 回读完成)
*/
int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
uint8_t cmd[3];
uint8_t use_dma = 0U;
@@ -406,7 +426,7 @@ int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
* 限定条件:已先调用 gd5f_program_load 加载数据
* 函数说明:发 10h + 3字节页地址触发编程,等待完成后检查 P_FAIL 位
*/
int gd5f_program_exec(uint32_t page_addr) {
int32_t gd5f_program_exec(uint32_t page_addr) {
int ret;
uint8_t cmd[4];
uint8_t status;
@@ -436,7 +456,7 @@ int gd5f_program_exec(uint32_t page_addr) {
* 限定条件:SPI 已初始化,ECC 已使能
* 函数说明:读取状态寄存器 ECCS[5:4] 位,0x02 表示不可纠正错误
*/
int gd5f_check_ecc(void) {
int32_t gd5f_check_ecc(void) {
uint8_t status;
gd5f_read_status(&status);
uint8_t ecc = (status >> 4) & 0x03;
@@ -448,11 +468,15 @@ int gd5f_check_ecc(void) {
* 函数功能:设置 Feature 寄存器
* 入口参数:addr - 寄存器地址 uint8_t
* data - 写入数据 uint8_t
* 返回值:0 - 成功
* 返回值:GD5F_OK - 成功;其它 - SPI 传输/等待失败错误码
* 限定条件:SPI 已初始化
* 函数说明:先写使能,发 1Fh + 地址 + 数据,等待操作完成 */
* 函数说明:先写使能,发 1Fh + 地址 + 数据,等待操作完成。
* E-53(2026-10-03):HAL 传输与 wait_busy 的返回值必须检查并上报——
* 原实现恒返回 GD5F_OK,"配置未生效"对上层不可见(E-10 同族问题)。
*/
static int gd5f_set_feature(uint8_t addr, uint8_t data) {
uint8_t cmd[3];
int ret;
gd5f_write_enable();
@@ -461,14 +485,48 @@ static int gd5f_set_feature(uint8_t addr, uint8_t data) {
cmd[2] = data;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
if (HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT) != HAL_OK) {
GD5F_CS_HIGH();
return GD5F_ERROR;
}
GD5F_CS_HIGH();
gd5f_wait_busy(100);
ret = gd5f_wait_busy(100);
if (ret != GD5F_OK) {
return ret;
}
return GD5F_OK;
}
/*
* 函数功能:读取任意 Feature 寄存器(0Fh + 地址 + 读 1 字节)
* 入口参数:addr - feature 寄存器地址 uint8_t
* 出口参数:p_data - 读到的字节输出指针 uint8_t* 不为 NULL
* 返回值:GD5F_OK - 成功;GD5F_ERROR - SPI 传输失败
* 限定条件:SPI 已初始化
* 函数说明:E-10(2026-10-03):init 需**回读** B0h 校验 ECC 使能位真正生效
* (原实现只写不读,"init 返回 OK 但 ECC 未打开"→ 存储栈在无 ECC
* 保护下静默降级)。与 gd5f_read_status 同构(后者固定 C0h),
* 此处参数化地址;同样检查 HAL 返回值(E-53)。
*/
static int gd5f_private_get_feature(uint8_t addr, uint8_t *p_data) {
uint8_t cmd = GD5F_CMD_GET_FEATURE;
HAL_StatusTypeDef hs;
GD5F_CS_LOW();
hs = HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
if (hs == HAL_OK) {
hs = HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
}
if (hs == HAL_OK) {
hs = HAL_SPI_Receive(&hspi1, p_data, 1, GD5F_SPI_TIMEOUT);
}
GD5F_CS_HIGH();
return (hs == HAL_OK) ? GD5F_OK : GD5F_ERROR;
}
/*
* 函数功能:块擦除(擦除128KB块)
* 入口参数:block_addr - 块编号 uint32_t 0 - 2047
@@ -476,7 +534,7 @@ static int gd5f_set_feature(uint8_t addr, uint8_t data) {
* 限定条件:gd5f2gq5ue_init() 已调用
* 函数说明:1. 写使能 - D8h + 3字节页地址 - 等待完成
* 2. 擦除完成后检查 E_FAIL */
int gd5f_block_erase(uint32_t block_addr) {
int32_t gd5f_block_erase(uint32_t block_addr) {
int ret = GD5F_OK;
uint8_t cmd[4];
uint8_t status = 0;
@@ -509,10 +567,16 @@ int gd5f_block_erase(uint32_t block_addr) {
/*
* 函数功能:扫描所有块构建 BBT(在 ECC 使能前调用)
* 入口参数:无
* 返回值:0 - 成功
* 返回值:GD5F_OK - 扫描完成;其它 - 读失败错误码(扫描已中止,s_bbt 为部分结果)
* 限定条件:SPI 已初始化,ECC 尚未使能
* 函数说明:读取每个块首页(page 0)的 spare byte 0,
* 若不为 0xFF 则为出厂坏块(符合 datasheet §12.4)
* 若不为 0xFF 则为出厂坏块(符合 datasheet §12.4)。
* E-42(2026-10-03,GD5F 错误通道统一):原实现丢弃 page_read /
* read_from_cache 的返回值且对外恒返回 GD5F_OK——SPI 读失败时
* `spare[8]` 保持上一轮的**陈旧内容**,且"整片扫描全失败也返回成功",
* 上层以为坏块表可用(rebuild 路径还会把错表持久化覆盖好副本)。
* 现:① 每轮先清零 spare(防陈旧内容误判);② 任一读失败即**中止**
* 并返回错误码,由调用方决定"放弃持久化 / 让 init 失败"。
*/
static int gd5f_bbt_scan(void) {
uint32_t block;
@@ -523,9 +587,21 @@ static int gd5f_bbt_scan(void) {
for (block = 0; block < GD5F_TOTAL_BLOCKS; block++) {
uint32_t page = block << 6;
int ret;
gd5f_page_read(page);
gd5f_read_from_cache(GD5F_PAGE_SIZE, spare, 1);
memset(spare, 0, sizeof(spare)); /* 每轮清零:防读失败时用陈旧内容误判 */
ret = gd5f_page_read(page);
if (ret != GD5F_OK) {
DBG_ERROR("BBT scan: page_read fail at block %lu (ret=%d)",
(unsigned long)block, ret);
return ret; /* 扫描失败即中止,不得对外报"扫描完成" */
}
ret = gd5f_read_from_cache(GD5F_PAGE_SIZE, spare, 1);
if (ret != GD5F_OK) {
DBG_ERROR("BBT scan: read_from_cache fail at block %lu (ret=%d)",
(unsigned long)block, ret);
return ret;
}
if (spare[0] != 0xFF) {
s_bbt[block >> 3] |= (1 << (block & 7));
@@ -557,7 +633,9 @@ void gd5f2gq5ue_bbt_clear(void) {
* 再写回持久化 BBT 池(版本 +1,掉电安全),从而清除 TC-STO-03 等运行时
* 标记的坏块。调用后建议再执行 nand_ftl_format() 清空 dhara map。
*/
int gd5f2gq5ue_bbt_rebuild(void) {
int32_t gd5f2gq5ue_bbt_rebuild(void) {
int ret;
DBG_INFO("Rebuilding BBT from factory scan...");
/* 1. 关闭 ECC 以读取原始出厂坏块标记(与 gd5f2gq5ue_init 的扫描前提一致) */
@@ -565,12 +643,21 @@ int gd5f2gq5ue_bbt_rebuild(void) {
gd5f_wait_busy(100);
/* 2. 重新扫描出厂坏块(覆盖 RAM BBT,剔除运行时注入的坏块) */
gd5f_bbt_scan();
ret = gd5f_bbt_scan();
/* 3. 重新使能 ECC */
/* 3. 重新使能 ECC(无论扫描成败都须恢复,否则后续 I/O 在无 ECC 下运行) */
gd5f_set_feature(0xB0, 0x10);
gd5f_wait_busy(100);
/* E-42(2026-10-03,数据安全优先):扫描失败 → **放弃持久化**(保留持久区
* 既有好副本),并从盘上回读 BBT 恢复 RAM 视图。原实现无论扫描成败都照常
* save(),会把"读失败拼出来的错表"覆盖掉正确副本。 */
if (ret != GD5F_OK) {
DBG_ERROR("BBT rebuild: scan FAIL (ret=%d), persist abandoned", ret);
(void)gd5f_bbt_load();
return ret;
}
/* 4. 持久化重建后的 BBT(覆盖持久化区中的脏副本) */
if (gd5f_bbt_save() != GD5F_OK) {
DBG_ERROR("BBT rebuild: persist failed");
@@ -620,7 +707,7 @@ void gd5f2gq5ue_print_bbt(void) {
* 5. 解除所有块保护 (A0h = 0x00)
* 6. 定位保留块池并加载持久化 BBT(ECC 使能后)
*/
int gd5f2gq5ue_init(void) {
int32_t gd5f2gq5ue_init(void) {
int ret = GD5F_OK;
uint8_t mid = 0;
uint8_t did = 0;
@@ -656,21 +743,55 @@ int gd5f2gq5ue_init(void) {
}
DBG_INFO("Scanning bad blocks...");
gd5f_bbt_scan();
ret = gd5f_bbt_scan();
if (ret != GD5F_OK) {
/* E-10:扫坏块失败 → init 失败,不静默继续(上层据此上报自检并降级) */
DBG_ERROR("BBT scan failed: %d", ret);
return ret;
}
DBG_INFO("Enabling ECC...");
gd5f_set_feature(0xB0, 0x10);
gd5f_wait_busy(100);
ret = gd5f_set_feature(0xB0, 0x10);
if (ret != GD5F_OK) {
DBG_ERROR("Enable ECC failed: %d", ret);
return ret;
}
/* E-10:回读 B0h 校验 ECC 使能位(bit4)**真正生效**——原实现只写不读,
* "init 返回 OK 但 ECC 未真正打开"会使存储栈在无 ECC 保护下静默降级。 */
{
uint8_t feat = 0;
ret = gd5f_private_get_feature(GD5F_REG_FEATURE, &feat);
if (ret != GD5F_OK) {
DBG_ERROR("Read B0h failed: %d", ret);
return ret;
}
if ((feat & GD5F_FEATURE_ECC_EN) == 0u) {
DBG_ERROR("ECC not enabled (B0h=0x%02X)", feat);
return GD5F_ERROR;
}
}
DBG_INFO("Unlocking block protection...");
gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
gd5f_wait_busy(100);
ret = gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
if (ret != GD5F_OK) {
DBG_ERROR("Unlock block protection failed: %d", ret);
return ret;
}
DBG_INFO("Locating BBT pool...");
gd5f_private_bbt_locate_pool();
ret = gd5f_private_bbt_locate_pool();
if (ret != GD5F_OK) {
DBG_ERROR("Locate BBT pool failed: %d", ret);
return ret;
}
DBG_INFO("Loading persisted BBT...");
gd5f_bbt_load();
ret = gd5f_bbt_load();
if (ret != GD5F_OK) {
/* BBT 持久副本缺失/损坏不致命(RAM BBT 已由扫描建立),仅告警 */
DBG_ERROR("Load persisted BBT failed: %d (use scanned BBT)", ret);
}
DBG_INFO("NAND init OK");
return GD5F_OK;
@@ -684,7 +805,7 @@ int gd5f2gq5ue_init(void) {
* 限定条件:SPI 已初始化
* 函数说明:发 9Fh 命令后接 1 字节 dummy + MID + DID
*/
int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
uint8_t cmd = GD5F_CMD_READ_ID;
uint8_t id_buf[3] = {0};
@@ -708,7 +829,7 @@ int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:自动处理跨页读取;offset+size 越界返回错误
*/
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
int32_t gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
return GD5F_ERROR;
@@ -751,7 +872,7 @@ int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
* 函数说明:自动处理跨页写入;offset+size 越界返回错误
*/
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
int32_t gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
return GD5F_ERROR;
@@ -788,7 +909,7 @@ int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:循环擦除 [offset, offset+size) 覆盖的每个块,偏移/长度不对齐返回错误
*/
int gd5f2gq5ue_erase(long offset, size_t size) {
int32_t gd5f2gq5ue_erase(long offset, size_t size) {
if (offset % GD5F_BLOCK_SIZE != 0) {
return GD5F_ERROR;
@@ -819,7 +940,7 @@ int gd5f2gq5ue_erase(long offset, size_t size) {
* 限定条件:SPI 已初始化
* 函数说明:发 FFh 复位命令后等待 5ms
*/
int gd5f2gq5ue_reset(void) {
int32_t gd5f2gq5ue_reset(void) {
uint8_t cmd = GD5F_CMD_RESET;
GD5F_CS_LOW();
@@ -838,7 +959,7 @@ int gd5f2gq5ue_reset(void) {
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:通过 BBT 查询
*/
int gd5f2gq5ue_is_block_bad(uint32_t block) {
int32_t gd5f2gq5ue_is_block_bad(uint32_t block) {
if (block >= GD5F_TOTAL_BLOCKS) {
return 1;
}
+20 -14
View File
@@ -125,35 +125,41 @@ int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did);
/*
* 函数功能:从 NAND 读取数据(支持跨页)
* 入口参数:offset - 起始字节偏移 int32_t 0 - GD5F_TOTAL_SIZE-1
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 读取字节数 uint32_t > 0
* 返回值:0 - 成功,其他 - 错误码
* size - 读取字节数 size_t > 0
* 返回值:GD5F_OK(0) - SPI 传输完成;其它 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间。
* ⚠ **raw 契约(E-56,2026-10-03 显式文档化)**:返回 0 只表示 **SPI 传输完成**,
* **不代表数据无位翻转**——ECC 不可纠正页(ECCS 非零)在 SPI 层属正常完成,
* 本函数仍返回 0。数据正确性由 **ECC 层**保证:生产主路径经 dhara glue
* (`nand_ftl.c`)自有 ECC 检查保护;**直接使用本 raw API** 的调用方
* (测试/诊断路径)须自行处理位翻转。详见
* docs/问题记录/GD5F2GQ5UE_Trap_Records.md。
*/
int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
int32_t gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
/*
* 函数功能:向 NAND 写入数据(支持跨页)
* 入口参数:offset - 起始字节偏移 int32_t 0 - GD5F_TOTAL_SIZE-1
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 写入字节数 uint32_t > 0
* 返回值:0 - 成功,其他 - 错误码
* size - 写入字节数 size_t > 0
* 返回值:GD5F_OK(0) - 成功;其它 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
* 函数说明:自动处理跨页写入,每次写入不超过当前页剩余空间
*/
int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
int32_t gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
/*
* 函数功能:擦除块(按块擦除,最小单位 128KB)
* 入口参数:offset - 起始字节偏移 int32_t 必须 GD5F_BLOCK_SIZE 对齐
* size - 擦除字节数 uint32_t 必须 GD5F_BLOCK_SIZE 整数倍
* 返回值:0 - 成功,其他 - 错误码
* 入口参数:offset - 起始字节偏移 long 必须 GD5F_BLOCK_SIZE 对齐
* size - 擦除字节数 size_t 必须 GD5F_BLOCK_SIZE 整数倍
* 返回值:GD5F_OK(0) - 成功;其它 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:擦除操作以块为单位,offset 和 size 必须块对齐
*/
int gd5f2gq5ue_erase(long offset, size_t size);
int32_t gd5f2gq5ue_erase(long offset, size_t size);
/*
* 函数功能:复位芯片
@@ -195,7 +201,7 @@ void gd5f2gq5ue_print_bbt(void);
* 再写回持久化 BBT 池(版本 +1,掉电安全),从而清除运行时 mark 的坏块。
* 调用后建议再执行 nand_ftl_format() 清空 dhara map。
*/
int gd5f2gq5ue_bbt_rebuild(void);
int32_t gd5f2gq5ue_bbt_rebuild(void);
/*
* 函数功能:获取 dhara 可用块数(总块数减去保留块池)
+19 -8
View File
@@ -153,14 +153,20 @@ int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) {
fre = 0;
} else {
uint8_t buf[64];
uint32_t i;
gd5f_read_from_cache(0, buf, sizeof(buf));
fre = 1;
for (i = 0; i < sizeof(buf); i++) {
if (buf[i] != 0xFF) {
fre = 0;
break;
if (gd5f_read_from_cache(0, buf, sizeof(buf)) != GD5F_OK) {
/* E-42 同族(2026-10-03):读失败 → buf 可能为陈旧/垃圾内容,
* 保守判为非空(块不可复用),避免把"没读到内容的块"判成空闲。 */
fre = 0;
} else {
uint32_t i;
fre = 1;
for (i = 0; i < sizeof(buf); i++) {
if (buf[i] != 0xFF) {
fre = 0;
break;
}
}
}
}
@@ -181,7 +187,12 @@ int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, s
return -1;
}
gd5f_read_from_cache((uint16_t)offset, data, length);
/* E-42 同族(2026-10-03):read_from_cache 返回值必须检查——失败时 data 内容
* 不可信,原实现仍返回 0(成功),会把未读到的数据当有效数据交给上层。 */
if (gd5f_read_from_cache((uint16_t)offset, data, length) != GD5F_OK) {
dhara_set_error(err, DHARA_E_ECC);
return -1;
}
return 0;
}