GD5F双装载修复:page_read/program_load二次装载消除低VDD提前就绪竞态(半新半旧页致resume读旧树)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user