GD5F页读取固定沉降延时:13h后等5ms再0Bh,消除低VDD就绪信号提前导致的半新半旧页(resume读旧树根治)
This commit is contained in:
@@ -153,14 +153,20 @@ int gd5f_read_status(uint8_t *p_status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:发送一次 13h 页读取命令(gd5f_page_read 的装载单元)
|
* 函数功能:页读取(将数据从存储阵列加载到内部缓存)
|
||||||
* 入口参数:page_addr - 页地址 uint32_t 0 ~ GD5F_TOTAL_BLOCKS*64-1
|
* 入口参数:page_addr - 页地址 uint32_t 0 ~ GD5F_TOTAL_BLOCKS*64-1
|
||||||
* 返回值:无
|
* 返回值:0 - 成功,其它 - 错误
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:发 13h + 3字节行地址(单次装载)
|
* 函数说明:发 13h + 3字节行地址,等待 OIP 清零 + 固定沉降延时
|
||||||
|
* GD5F_PAGE_READ_SETTLE_MS(2026-09-20 台架实证:低 VDD 下
|
||||||
|
* 就绪信号早于 array→buffer 整页装载完成,0Bh 会读到"前段新、
|
||||||
|
* 后段旧"的混合页 → journal resume 锚定新 checkpoint 却读到
|
||||||
|
* 旧树 → 数据静默丢失。固定沉降保证整页新鲜,双装载无法
|
||||||
|
* 消除该确定性竞态,已弃用。)
|
||||||
*/
|
*/
|
||||||
static void gd5f_page_read_issue(uint32_t page_addr) {
|
int gd5f_page_read(uint32_t page_addr) {
|
||||||
uint8_t cmd[4] = {0};
|
uint8_t cmd[4] = {0};
|
||||||
|
int ret;
|
||||||
|
|
||||||
cmd[0] = GD5F_CMD_PAGE_READ;
|
cmd[0] = GD5F_CMD_PAGE_READ;
|
||||||
cmd[1] = (page_addr >> 16) & 0xFF;
|
cmd[1] = (page_addr >> 16) & 0xFF;
|
||||||
@@ -170,31 +176,16 @@ static void gd5f_page_read_issue(uint32_t page_addr) {
|
|||||||
GD5F_CS_LOW();
|
GD5F_CS_LOW();
|
||||||
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
|
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
|
||||||
GD5F_CS_HIGH();
|
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);
|
ret = gd5f_wait_busy(100);
|
||||||
if (ret != GD5F_OK) {
|
if (ret != GD5F_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 二次装载:消除"提前就绪"竞态(见函数说明) */
|
/* 固定沉降:等待 array→buffer 整页装载完成(见函数说明) */
|
||||||
gd5f_page_read_issue(page_addr);
|
HAL_Delay(GD5F_PAGE_READ_SETTLE_MS);
|
||||||
return gd5f_wait_busy(100);
|
|
||||||
|
return GD5F_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -267,15 +258,17 @@ static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
|||||||
* 函数说明:发 02h + 2字节列地址后发送数据;超过 DMA 阈值走 DMA
|
* 函数说明:发 02h + 2字节列地址后发送数据;超过 DMA 阈值走 DMA
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* 函数功能:单次 program load(02h + 列地址 + 数据流)
|
* 函数功能:将数据加载到芯片内部缓存(PROGRAM LOAD)
|
||||||
* 入口参数:column - 列(页内)偏移 uint16_t 本驱动恒为 0
|
* 入口参数:column - 列(页内)偏移 uint16_t 本驱动恒为 0
|
||||||
* p_buf - 数据缓冲区 const uint8_t* 不为 NULL
|
* p_buf - 数据缓冲区 const uint8_t* 不为 NULL
|
||||||
* size - 数据长度 size_t > 0
|
* size - 数据长度 size_t > 0
|
||||||
* 返回值:无(SPI 传输错误由 DMA/超时机制内部处理)
|
* 返回值:0 - 成功,其它 - 错误
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化;目标区域已擦除
|
||||||
* 函数说明:gd5f_program_load 的装载单元,被调用两次(二次装载)
|
* 函数说明:发 02h + 2字节列地址后发送数据;超过 DMA 阈值走 DMA。
|
||||||
|
* (buffer 由 SPI 数据流直接填充,无 array→buffer 竞态,
|
||||||
|
* 无需二次装载;program exec 的落盘校验由 FTL 回读完成)
|
||||||
*/
|
*/
|
||||||
static void gd5f_program_load_once(uint16_t column, const uint8_t *p_buf, size_t size) {
|
int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
|
||||||
uint8_t cmd[3];
|
uint8_t cmd[3];
|
||||||
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
|
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
|
||||||
cmd[1] = (uint8_t)(column >> 8);
|
cmd[1] = (uint8_t)(column >> 8);
|
||||||
@@ -290,36 +283,19 @@ static void gd5f_program_load_once(uint16_t column, const uint8_t *p_buf, size_t
|
|||||||
if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size)
|
if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size)
|
||||||
!= HAL_OK) {
|
!= HAL_OK) {
|
||||||
GD5F_CS_HIGH();
|
GD5F_CS_HIGH();
|
||||||
return;
|
return GD5F_ERROR;
|
||||||
}
|
}
|
||||||
while (g_spi1_dma_done == 0U) {
|
while (g_spi1_dma_done == 0U) {
|
||||||
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
||||||
HAL_SPI_DMAStop(&hspi1);
|
HAL_SPI_DMAStop(&hspi1);
|
||||||
GD5F_CS_HIGH();
|
GD5F_CS_HIGH();
|
||||||
return;
|
return GD5F_BUSY_TIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size, GD5F_SPI_TIMEOUT);
|
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size, GD5F_SPI_TIMEOUT);
|
||||||
}
|
}
|
||||||
GD5F_CS_HIGH();
|
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;
|
return GD5F_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,16 @@ extern "C" {
|
|||||||
#define GD5F_TOTAL_PAGE_SIZE 2112
|
#define GD5F_TOTAL_PAGE_SIZE 2112
|
||||||
#define GD5F_PAGES_PER_BLOCK 64
|
#define GD5F_PAGES_PER_BLOCK 64
|
||||||
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE)
|
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 页读取固定沉降延时(2026-09-20 台架实证):13h 发出后 array→buffer
|
||||||
|
* 的整页装载完成前,就绪信号可能提前清零(低 VDD 下内部传输速率下降,
|
||||||
|
* 确定性早于 0Bh 读完成)→ 0Bh 流式读到"前段新、后段旧"的混合页
|
||||||
|
* (journal checkpoint header 有效而 meta 残留旧数据 → 复位后 resume
|
||||||
|
* 锚定新 checkpoint 却读到旧树 → 数据静默丢失)。13h 之后、0Bh 之前
|
||||||
|
* 强制等待该时长,保证整页新鲜。取值须覆盖低 VDD 下 t_RR 最慢工况。
|
||||||
|
*/
|
||||||
|
#define GD5F_PAGE_READ_SETTLE_MS 5
|
||||||
#define GD5F_TOTAL_BLOCKS 2048
|
#define GD5F_TOTAL_BLOCKS 2048
|
||||||
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
|
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user