台架执行《实机测试实施指南》步骤 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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 可用块数(总块数减去保留块池)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user