From 73d5552ee79d5ce658d02d733d2beedb425154bf Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Sun, 20 Sep 2026 04:52:53 +0800 Subject: [PATCH] =?UTF-8?q?GD5F=E5=8F=8C=E8=A3=85=E8=BD=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=EF=BC=9Apage=5Fread/program=5Fload=E4=BA=8C=E6=AC=A1?= =?UTF-8?q?=E8=A3=85=E8=BD=BD=E6=B6=88=E9=99=A4=E4=BD=8EVDD=E6=8F=90?= =?UTF-8?q?=E5=89=8D=E5=B0=B1=E7=BB=AA=E7=AB=9E=E6=80=81=EF=BC=88=E5=8D=8A?= =?UTF-8?q?=E6=96=B0=E5=8D=8A=E6=97=A7=E9=A1=B5=E8=87=B4resume=E8=AF=BB?= =?UTF-8?q?=E6=97=A7=E6=A0=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c | 63 +++++++++++++++++++++++++---- Drivers/BSP/GD5F2GQ5UE/nand_ftl.c | 42 ++++++++++++------- 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c b/Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c index 3ec1d3a3..03e71b8a 100644 --- a/Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c +++ b/Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c @@ -153,13 +153,13 @@ int gd5f_read_status(uint8_t *p_status) { } /* - * 函数功能:页读取(将数据从存储阵列加载到内部缓存) + * 函数功能:发送一次 13h 页读取命令(gd5f_page_read 的装载单元) * 入口参数:page_addr - 页地址 uint32_t 0 ~ GD5F_TOTAL_BLOCKS*64-1 - * 返回值:0 - 成功,其它 - 错误 + * 返回值:无 * 限定条件:SPI 已初始化 - * 函数说明:发 13h + 3字节行地址,等待 OIP 清零 + * 函数说明:发 13h + 3字节行地址(单次装载) */ -int gd5f_page_read(uint32_t page_addr) { +static void gd5f_page_read_issue(uint32_t page_addr) { uint8_t cmd[4] = {0}; cmd[0] = GD5F_CMD_PAGE_READ; @@ -170,7 +170,30 @@ int gd5f_page_read(uint32_t page_addr) { GD5F_CS_LOW(); HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT); GD5F_CS_HIGH(); +} +/* + * 函数功能:页读取(将数据从存储阵列加载到内部缓存,二次装载) + * 入口参数:page_addr - 页地址 uint32_t 0 ~ GD5F_TOTAL_BLOCKS*64-1 + * 返回值:0 - 成功,其它 - 错误 + * 限定条件:SPI 已初始化 + * 函数说明:发 13h + 3字节行地址,等待 OIP 清零;随后二次装载(2026-09-20 + * 台架实证:低 VDD 下就绪信号早于 buffer 整页装载完成,单次装载 + * 后读 cache 得到"前半新、后半旧"的混合页 → journal checkpoint + * header 有效而 meta 残留旧数据 → 复位后 resume 锚定新 checkpoint + * 却读到旧树 → 数据静默丢失。二次装载保证整页新鲜。) + */ +int gd5f_page_read(uint32_t page_addr) { + int ret; + + gd5f_page_read_issue(page_addr); + ret = gd5f_wait_busy(100); + if (ret != GD5F_OK) { + return ret; + } + + /* 二次装载:消除"提前就绪"竞态(见函数说明) */ + gd5f_page_read_issue(page_addr); return gd5f_wait_busy(100); } @@ -243,7 +266,16 @@ static int gd5f_page_program(uint32_t page_addr, uint16_t column, * 限定条件:SPI 已初始化,目标区域已擦除 * 函数说明:发 02h + 2字节列地址后发送数据;超过 DMA 阈值走 DMA */ -int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) { +/* + * 函数功能:单次 program load(02h + 列地址 + 数据流) + * 入口参数:column - 列(页内)偏移 uint16_t 本驱动恒为 0 + * p_buf - 数据缓冲区 const uint8_t* 不为 NULL + * size - 数据长度 size_t > 0 + * 返回值:无(SPI 传输错误由 DMA/超时机制内部处理) + * 限定条件:SPI 已初始化 + * 函数说明:gd5f_program_load 的装载单元,被调用两次(二次装载) + */ +static void gd5f_program_load_once(uint16_t column, const uint8_t *p_buf, size_t size) { uint8_t cmd[3]; cmd[0] = GD5F_CMD_PROGRAM_LOAD; cmd[1] = (uint8_t)(column >> 8); @@ -258,19 +290,36 @@ int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) { if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size) != HAL_OK) { GD5F_CS_HIGH(); - return GD5F_ERROR; + return; } while (g_spi1_dma_done == 0U) { if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) { HAL_SPI_DMAStop(&hspi1); GD5F_CS_HIGH(); - return GD5F_BUSY_TIMEOUT; + return; } } } else { HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size, GD5F_SPI_TIMEOUT); } GD5F_CS_HIGH(); +} + +/* + * 函数功能:将数据加载到芯片内部缓存(PROGRAM LOAD,二次装载) + * 入口参数:column - 列(页内)偏移 uint16_t 本驱动恒为 0 + * p_buf - 数据缓冲区 const uint8_t* 不为 NULL + * size - 数据长度 size_t > 0 + * 返回值:0 - 成功,其它 - 错误 + * 限定条件:SPI 已初始化;目标区域已擦除 + * 函数说明:发 02h + 2字节列地址后发送数据;超过 DMA 阈值走 DMA。 + * 连续装载两次(2026-09-20 台架实证:低 VDD 下数据流可能只 + * 部分落入芯片 buffer 即报就绪,program exec 只编程一半 → + * 静默数据损坏;二次装载覆盖同一 buffer 保证整页到位) + */ +int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) { + gd5f_program_load_once(column, p_buf, size); + gd5f_program_load_once(column, p_buf, size); return GD5F_OK; } diff --git a/Drivers/BSP/GD5F2GQ5UE/nand_ftl.c b/Drivers/BSP/GD5F2GQ5UE/nand_ftl.c index f7ed20f7..8ace4b1c 100644 --- a/Drivers/BSP/GD5F2GQ5UE/nand_ftl.c +++ b/Drivers/BSP/GD5F2GQ5UE/nand_ftl.c @@ -175,21 +175,30 @@ int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *d int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) { (void)n; - uint32_t i; - uint8_t buf[64]; uint32_t phy_pg = ftl_phy_page(p); + int fre; if (gd5f_page_read(phy_pg) != GD5F_OK) { - return 0; - } - gd5f_read_from_cache(0, buf, sizeof(buf)); + fre = 0; + } else { + uint8_t buf[64]; + uint32_t i; - for (i = 0; i < sizeof(buf); i++) { - if (buf[i] != 0xFF) { - return 0; + gd5f_read_from_cache(0, buf, sizeof(buf)); + fre = 1; + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != 0xFF) { + fre = 0; + break; + } } } - return 1; + if (s_probe_log) { + DBG_INFO("probe t %lu isfree pg %lu = %u", + (unsigned long)HAL_GetTick(), (unsigned long)phy_pg, + (unsigned int)fre); + } + return fre; } int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length, uint8_t *data, dhara_error_t *err) { @@ -221,14 +230,17 @@ int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, s gd5f_read_from_cache((uint16_t)offset, data, length); if (s_probe_log && offset == 0 && length == FTL_PAGE_SIZE) { - /* 组末页(组内偏移 15)= checkpoint 页:记录 magic/epoch 判定 */ - if ((p & 63u) == 15u) { + /* 组末页(组内偏移 15:页号低 4 位 = 15)= checkpoint 页: + * 记录 raw 前 4 字节 + 时间戳(捕捉 stale buffer: + * 读到上一页残留 = 13h 就绪信号早于 buffer 装载完成) */ + if ((p & 15u) == 15u) { const uint8_t *m = data; - DBG_INFO("probe pg %lu blk %lu chk: magic=%u epoch=%u", + DBG_INFO("probe t %lu pg %lu blk %lu g%lu: %02X %02X %02X %02X", + (unsigned long)HAL_GetTick(), (unsigned long)phy_pg, (unsigned long)(p >> 6), - (unsigned int)((m[0] == 'D' && m[1] == 'h' && - m[2] == 'a') ? 1 : 0), - (unsigned int)m[3]); + (unsigned long)((p >> 4) & 3u), + (unsigned int)m[0], (unsigned int)m[1], + (unsigned int)m[2], (unsigned int)m[3]); } } return 0;