0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
420 lines
21 KiB
C
420 lines
21 KiB
C
#ifndef GD5F2GQ5UE_H
|
||
#define GD5F2GQ5UE_H
|
||
|
||
/*
|
||
* 模块名称:GD5F2GQ5UE SPI NAND Flash 驱动
|
||
* 模块功能:提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
|
||
* 适用平台:STM32F407ZGT6 + SPI1 硬件 SPI
|
||
* 作者:王建锋
|
||
* 创建日期:2026-07-16
|
||
* 修改记录:
|
||
* 2026-07-16 王建锋 创建初始版本
|
||
* 2026-07-17 王建锋 切换为硬件 SPI,修正擦除地址,参考 NuttX 驱动
|
||
*/
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#include "main.h"
|
||
#include <stdint.h>
|
||
|
||
/* ======================== 宏定义 ======================== */
|
||
|
||
/* SPI 命令码 */
|
||
#define GD5F_CMD_WRITE_ENABLE 0x06
|
||
#define GD5F_CMD_WRITE_DISABLE 0x04
|
||
#define GD5F_CMD_GET_FEATURE 0x0F
|
||
#define GD5F_CMD_SET_FEATURE 0x1F
|
||
#define GD5F_CMD_READ_ID 0x9F
|
||
#define GD5F_CMD_PAGE_READ 0x13
|
||
#define GD5F_CMD_READ_FROM_CACHE 0x0B
|
||
#define GD5F_CMD_PROGRAM_LOAD 0x02
|
||
#define GD5F_CMD_PROGRAM_EXEC 0x10
|
||
#define GD5F_CMD_BLOCK_ERASE 0xD8
|
||
#define GD5F_CMD_RESET 0xFF
|
||
|
||
/* 寄存器地址 */
|
||
#define GD5F_REG_PROTECT 0xA0
|
||
#define GD5F_REG_FEATURE 0xB0
|
||
#define GD5F_REG_STATUS 0xC0
|
||
#define GD5F_REG_DRIVER 0xD0
|
||
|
||
/* 状态位定义 */
|
||
#define GD5F_STATUS_OIP (1 << 0)
|
||
#define GD5F_STATUS_WEL (1 << 1)
|
||
#define GD5F_STATUS_E_FAIL (1 << 2)
|
||
#define GD5F_STATUS_P_FAIL (1 << 3)
|
||
#define GD5F_STATUS_ECCS0 (1 << 4)
|
||
#define GD5F_STATUS_ECCS1 (1 << 5)
|
||
|
||
/* Feature 位定义 */
|
||
#define GD5F_FEATURE_ECC_EN (1 << 4)
|
||
#define GD5F_FEATURE_QE (1 << 0)
|
||
|
||
/* 芯片参数 */
|
||
#define GD5F_PAGE_SIZE 2048
|
||
#define GD5F_SPARE_SIZE 64
|
||
#define GD5F_TOTAL_PAGE_SIZE 2112
|
||
#define GD5F_PAGES_PER_BLOCK 64
|
||
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE)
|
||
|
||
/*
|
||
* 页读取就绪判定 = WIP 轮询(与 Linux 主线 / GigaDevice 厂商驱动完全一致,
|
||
* 单发令形状)。2026-09-20 沉降 / 13h 二次发令史实统一记于
|
||
* GD5F2GQ5UE_Trap_Records.md TRAP-GD5F-05。
|
||
*/
|
||
#define GD5F_TOTAL_BLOCKS 2048
|
||
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
|
||
|
||
/*
|
||
* SPI DMA 传输阈值:超过该字节数走 DMA,否则走 CPU 轮询。
|
||
* 取值约束:① 必须 >= 存储路径上可能出现在任务栈的最大缓冲(dhara meta 132B、
|
||
* dhara_nand_is_free 64B)——任务栈随 FreeRTOS 堆驻留 CCM(0x10000000),
|
||
* 而 DMA1/DMA2 物理不可达 CCM,栈缓冲一旦进 DMA 即静默失败(2026-09-20 实证);
|
||
* ② 必须 < 页大小(2048),页级传输保留 DMA。
|
||
* 公开此宏供测试用例对齐阈值上下界,避免测试取值与驱动漂移。
|
||
*/
|
||
#define GD5F_DMA_THRESHOLD 1024
|
||
|
||
/* ==================== DMA 可达内存窗口(DMA 可达性三层防线,2026-10-12) ====================
|
||
* DMA1/DMA2 物理不可达 CCM(0x10000000~0x1000FFFF):以 CCM 缓冲做 DMA 源/目的
|
||
* 会静默不落地(2026-09-20 首轮 CCM 扩容实验实证根因;口径见
|
||
* docs/内存管理/内存内容分配策略.md §2 与 §4 规则 R2/R3)。
|
||
* 白名单窗口必须与分散加载 RW_IRAM1(0x20000000,128KB,SRAM1+SRAM2)一致
|
||
* (MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct);将来新增其它 DMA 可达内存
|
||
* (如外部 SDRAM)须同步扩展本窗口。
|
||
* ⚠ 缓冲**自身地址**无法编译期断言:.bss 地址链接期才确定,armcc 的常量表达式
|
||
* 取不到(__builtin 系在 MicroLIB 下亦不可用,见 Src/main.c Error_Handler 注释)。
|
||
* 静态缓冲的落位由 scatter(RW_CCM 只列 heap_4.o)+ nand_ftl.c 的 init 期自检
|
||
* (gd5f_dma_buf_ok)双层保证;动态传入缓冲由每次传输的守卫强制。 */
|
||
#define GD5F_DMA_RAM_BASE 0x20000000UL
|
||
#define GD5F_DMA_RAM_END 0x20020000UL
|
||
#define GD5F_CCM_RAM_BASE 0x10000000UL
|
||
#define GD5F_CCM_RAM_END 0x10010000UL
|
||
|
||
/* 编译期守卫(DMA 可达性三层防线一环):AC5 无 _Static_assert(C11 不支持),用项目惯用的
|
||
* 负数组 typedef(同 App/cfg/cfg_file.c、Drivers/BSP/UI/ui_menu_tab.h)——
|
||
* 条件不成立则数组尺寸 -1 → 编译错。
|
||
* ① DMA 白名单窗口与 CCM 窗口**永不得重叠**(有人改上面常量时立即爆炸);
|
||
* ② 阈值契约(见 GD5F_DMA_THRESHOLD 处注释):≥ 存储路径最大栈缓冲
|
||
* (dhara meta 132 B = DHARA_META_SIZE 上限)且 < 页大小(页级传输保留 DMA)。 */
|
||
typedef char gd5f_dma_window_no_ccm_check_t[
|
||
((GD5F_DMA_RAM_END <= GD5F_CCM_RAM_BASE) || (GD5F_DMA_RAM_BASE >= GD5F_CCM_RAM_END))
|
||
? 1 : -1];
|
||
typedef char gd5f_dma_threshold_contract_check_t[
|
||
((GD5F_DMA_THRESHOLD >= 132U) && (GD5F_DMA_THRESHOLD < GD5F_PAGE_SIZE))
|
||
? 1 : -1];
|
||
|
||
/* 坏块表持久化:保留块池数量(从 NAND 顶部向下挑选出厂好块存储 BBT) */
|
||
#define GD5F_BBT_POOL_COUNT 4
|
||
|
||
/* 制造商 ID 和设备 ID */
|
||
#define GD5F_MANUFACTURER_ID 0xC8
|
||
#define GD5F_DEVICE_ID 0x52
|
||
|
||
/* 返回值定义 */
|
||
#define GD5F_OK 0
|
||
#define GD5F_ERROR -1
|
||
#define GD5F_WAIT_EXPIRED -2 /* 旧名 GD5F_BUSY_TIMEOUT;timeout-name 检查:**返回码不许叫 *_TIMEOUT** */
|
||
#define GD5F_ECC_ERROR -3
|
||
#define GD5F_PROGRAM_FAIL -4
|
||
#define GD5F_ERASE_FAIL -5
|
||
#define GD5F_ID_MISMATCH -6
|
||
|
||
/* 控制引脚宏 */
|
||
#define GD5F_CS_LOW() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_RESET)
|
||
#define GD5F_CS_HIGH() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_SET)
|
||
#define GD5F_WP_HIGH() HAL_GPIO_WritePin(GD_WP_GPIO_Port, GD_WP_Pin, GPIO_PIN_SET)
|
||
#define GD5F_HOLD_HIGH() HAL_GPIO_WritePin(GD_HOLD_GPIO_Port, GD_HOLD_Pin, GPIO_PIN_SET)
|
||
|
||
/* ======================== 函数声明 ======================== */
|
||
|
||
/*
|
||
* 函数功能:初始化 GD5F2GQ5UE(读 ID + 使能 ECC + 解除块保护)
|
||
* 入口参数:无
|
||
* 返回值:0 - 成功,其他 - 错误码
|
||
* 限定条件:SPI1 和相关 GPIO 已由 CubeMX 初始化完成
|
||
* 函数说明:1. 发送复位命令并等待完成
|
||
* 2. 读取芯片 ID 并校验
|
||
* 3. 使能内部 ECC (B0h bit4)
|
||
* 4. 解除所有块保护 (A0h = 0x00)
|
||
*/
|
||
int32_t gd5f2gq5ue_init(void);
|
||
|
||
/*
|
||
* 函数功能:读取芯片 ID(MID + DID)
|
||
* 入口参数:mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
|
||
* did - 设备 ID 输出指针 uint8_t* 不为 NULL
|
||
* 返回值:0 - 成功
|
||
* 限定条件:SPI 已初始化
|
||
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
|
||
*/
|
||
int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did);
|
||
|
||
/*
|
||
* 函数功能:从 NAND 读取数据(支持跨页)
|
||
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
|
||
* buf - 数据缓冲区 uint8_t* 不为 NULL
|
||
* size - 读取字节数 size_t > 0
|
||
* 返回值:GD5F_OK(0) - SPI 传输完成;其它 - 错误码
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间。
|
||
* ⚠ **raw 契约(2026-10-03 显式文档化)**:返回 0 只表示 **SPI 传输完成**,
|
||
* **不代表数据无位翻转**——ECC 不可纠正页(ECCS 非零)在 SPI 层属正常完成,
|
||
* 本函数仍返回 0。数据正确性由 **ECC 层**保证:生产主路径经 dhara glue
|
||
* (`nand_ftl.c`)自有 ECC 检查保护;**直接使用本 raw API** 的调用方
|
||
* (测试/诊断路径)须自行处理位翻转。详见
|
||
* docs/问题记录/GD5F2GQ5UE_Trap_Records.md。
|
||
*/
|
||
int32_t gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
|
||
|
||
/*
|
||
* 函数功能:向 NAND 写入数据(支持跨页)
|
||
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
|
||
* buf - 数据缓冲区 uint8_t* 不为 NULL
|
||
* size - 写入字节数 size_t > 0
|
||
* 返回值:GD5F_OK(0) - 成功;其它 - 错误码
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
|
||
* 函数说明:自动处理跨页写入,每次写入不超过当前页剩余空间
|
||
*/
|
||
int32_t gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
|
||
|
||
/*
|
||
* 函数功能:擦除块(按块擦除,最小单位 128KB)
|
||
* 入口参数:offset - 起始字节偏移 long 必须 GD5F_BLOCK_SIZE 对齐
|
||
* size - 擦除字节数 size_t 必须 GD5F_BLOCK_SIZE 整数倍
|
||
* 返回值:GD5F_OK(0) - 成功;其它 - 错误码
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:擦除操作以块为单位,offset 和 size 必须块对齐
|
||
*/
|
||
int32_t gd5f2gq5ue_erase(long offset, size_t size);
|
||
|
||
/*
|
||
* 函数功能:复位芯片
|
||
* 入口参数:无
|
||
* 返回值:0 - 成功
|
||
* 限定条件:SPI 已初始化
|
||
* 函数说明:发送 FFh 复位命令后等待 5ms
|
||
*/
|
||
int32_t gd5f2gq5ue_reset(void);
|
||
|
||
/*
|
||
* 函数功能:查询块是否坏块
|
||
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||
* 返回值:0 - 好块,1 - 坏块
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:通过 BBT(Bad Block Table)查询,BBT 在 init 阶段 ECC 使能前扫描构建
|
||
*/
|
||
int32_t gd5f2gq5ue_is_block_bad(uint32_t block);
|
||
|
||
/*
|
||
* 函数功能:标记块为坏块
|
||
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||
* 返回值:无
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:更新 RAM 中的 BBT,并持久化到保留块池(带版本号 + CRC,
|
||
* 轮转写入,掉电安全);仅在管理区内的块才纳入持久化 BBT
|
||
*/
|
||
void gd5f2gq5ue_mark_block_bad(uint32_t block);
|
||
void gd5f2gq5ue_bbt_clear(void);
|
||
void gd5f2gq5ue_print_bbt(void);
|
||
|
||
/*
|
||
* 函数功能:重建 BBT(重新扫描出厂坏块并持久化,覆盖运行时注入的坏块)
|
||
* 入口参数:无
|
||
* 返回值:GD5F_OK - 成功,其他 - 错误码
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:用于测试(如 TC-STORAGE-03 注入坏块后)或出厂重置后恢复。
|
||
* 临时关闭 ECC 重新扫描每块 page0 spare[0] 的出厂坏块标记,覆盖 RAM BBT,
|
||
* 再写回持久化 BBT 池(版本 +1,掉电安全),从而清除运行时 mark 的坏块。
|
||
* 调用后建议再执行 nand_ftl_format() 清空 dhara map。
|
||
*/
|
||
int32_t gd5f2gq5ue_bbt_rebuild(void);
|
||
|
||
/*
|
||
* 函数功能:获取 dhara 可用块数(总块数减去保留块池)
|
||
* 入口参数:无
|
||
* 返回值:dhara 可见的块数量(= 保留块池起始块号)
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:保留块池用于存储持久化 BBT,不交给 dhara 管理
|
||
*/
|
||
uint32_t gd5f_get_usable_blocks(void);
|
||
|
||
/*
|
||
* 函数功能:从闪存读取当前生效的 BBT 位图(测试/调试用)
|
||
* 入口参数:p_bbt - 位图输出缓冲 uint8_t* 不为 NULL,长度 >= GD5F_BBT_SIZE
|
||
* p_version - 版本号输出 uint32_t* 不为 NULL
|
||
* 返回值:0 - 成功,其他 - 错误码
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:返回闪存中版本号最高的有效 BBT 拷贝(供测试验证持久化)
|
||
*/
|
||
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version);
|
||
|
||
/*
|
||
* 函数功能:擦除保留块池(撤销持久化 BBT,回到仅工厂扫描态)
|
||
* 入口参数:无
|
||
* 返回值:0 - 成功
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:仅供测试/出厂重置使用;擦除后下次启动 load 找不到副本,
|
||
* 仅保留 RAM 中工厂扫描结果(调用方需自行还原 RAM BBT)。
|
||
*/
|
||
int32_t gd5f_bbt_wipe_pool(void);
|
||
|
||
/*
|
||
* 函数功能:从保留块池重新加载持久化 BBT 到 RAM(测试/调试用)
|
||
* 入口参数:无
|
||
* 返回值:0 - 成功
|
||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||
* 函数说明:将闪存中版本号最高的有效副本 OR 进 RAM BBT
|
||
*/
|
||
int32_t gd5f_bbt_reload(void);
|
||
|
||
/* ==================== SPI 原语(FTL 共享) ==================== */
|
||
|
||
/*
|
||
* 函数功能:等待芯片空闲(轮询状态位 BUSY/OIP)
|
||
* 入口参数:timeout_ms - 等待上限 uint32_t 毫秒
|
||
* 返 回 值:GD5F_OK - 已空闲;GD5F_WAIT_EXPIRED - 时限内未空闲(**可重试**,旧名 BUSY_TIMEOUT);GD5F_ERROR - SPI 读取失败
|
||
* 限定条件:SPI 已初始化;不得在中断里调用
|
||
* 函数说明:把"超时"与"读状态本身失败"分开返回,两者处置不同(前者再等、后者查硬件)
|
||
*/
|
||
int32_t gd5f_wait_busy(uint32_t timeout_ms);
|
||
|
||
/*
|
||
* 函数功能:发送 06h WRITE ENABLE(编程/擦除前必须成功执行一次)
|
||
* 入口参数:无
|
||
* 返 回 值:GD5F_OK - 成功;其它 - SPI 传输或等待失败错误码
|
||
* 限定条件:SPI 已初始化
|
||
* 函数说明:本函数返回值必须被检查并上报(错误通道统一纪律),忽略它会让后续 program/erase 静默无效
|
||
*/
|
||
int32_t gd5f_write_enable(void);
|
||
|
||
/*
|
||
* 函数功能:读取状态寄存器(0Fh / READ STATUS)
|
||
* 入口参数:p_status - 状态字节输出 uint8_t* 不得为 NULL
|
||
* 返 回 值:GD5F_OK - 成功(*p_status 有效);GD5F_ERROR - SPI 失败(*p_status 不可信,调用方不得读用)
|
||
* 限定条件:SPI 已初始化
|
||
* 函数说明:同族纪律——失败时出参内容不可信,故判成败看返回值而不是看 *p_status 是否像样
|
||
*/
|
||
int32_t gd5f_read_status(uint8_t *p_status);
|
||
|
||
/*
|
||
* 函数功能:页读取(13h:把数据从存储阵列装进内部缓存)
|
||
* 入口参数:page_addr - 物理页地址 uint32_t 0 - GD5F_TOTAL_PAGES-1
|
||
* 返 回 值:GD5F_OK - 命令已发出且等待完成;其它 - 透传底层 SPI/等待错误码
|
||
* 限定条件:SPI 已初始化;**本函数成功不代表数据无位翻转**,正确性由随后的 gd5f_check_ecc() 判定
|
||
* 函数说明:装载完成后须用 gd5f_read_from_cache() 取数据
|
||
*/
|
||
int32_t gd5f_page_read(uint32_t page_addr);
|
||
|
||
/*
|
||
* 函数功能:从内部缓存读出数据(0Bh READ FROM CACHE)
|
||
* 入口参数:column - 缓存内起始列 uint16_t 0 - GD5F_PAGE_SIZE-1
|
||
* p_buf - 数据输出缓冲 uint8_t* 不得为 NULL
|
||
* size - 读取字节数 uint32_t 1 - GD5F_PAGE_SIZE-column
|
||
* 返 回 值:GD5F_OK - 成功;GD5F_ERROR - 参数非法或 SPI 失败(**此时 p_buf 内容不可信**)
|
||
* 限定条件:必须先成功调用 gd5f_page_read() 完成阵列→缓存装载
|
||
* 函数说明:同族——调用方不得在失败分支继续使用 p_buf(FTL 侧据此把读失败保守判为"非空页")
|
||
*/
|
||
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, uint32_t size);
|
||
|
||
/*
|
||
* 函数功能:把数据装进内部缓存(02h PROGRAM LOAD)
|
||
* 入口参数:column - 缓存内起始列 uint16_t 0 - GD5F_PAGE_SIZE-1
|
||
* p_buf - 待写数据 const uint8_t* 不得为 NULL
|
||
* size - 字节数 uint32_t 1 - GD5F_PAGE_SIZE-column
|
||
* 返 回 值:GD5F_OK - 装载完成;GD5F_WAIT_EXPIRED - 装载后等待 OIP 超时(**可重试**);GD5F_ERROR - 参数/SPI 失败
|
||
* 限定条件:先成功执行 gd5f_write_enable();缓冲须在 DMA 可达窗口内(GD5F_DMA_RAM_* 白名单)
|
||
* 函数说明:装载本身不写阵列,真正的编程由 gd5f_program_exec() 触发
|
||
*/
|
||
int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, uint32_t size);
|
||
|
||
/*
|
||
* 函数功能:触发页编程(10h PROGRAM EXEC)并检查 P_FAIL
|
||
* 入口参数:page_addr - 目标物理页地址 uint32_t 合法页号
|
||
* 返 回 值:GD5F_OK - 编程完成且无 P_FAIL;**GD5F_PROGRAM_FAIL - 芯片置 P_FAIL(块退化的信号)**;其它 - 透传底层/SPI 错误
|
||
* 限定条件:本调用前须已 gd5f_program_load() 成功
|
||
* 函数说明:P_FAIL 是**器件自己认定这一页写不下去**的位,与 SPI 层错误性质不同(FTL 据此标坏块)
|
||
*/
|
||
int32_t gd5f_program_exec(uint32_t page_addr);
|
||
|
||
/*
|
||
* 函数功能:擦除一个块(D8h 擦除序列 + 等待 + 检查 E_FAIL)
|
||
* 入口参数:block_addr - 物理块地址 uint32_t 0 - GD5F_TOTAL_BLOCKS-1
|
||
* 返 回 值:GD5F_OK - 擦净;**GD5F_ERASE_FAIL - 芯片置 E_FAIL(坏块信号)**;其它 - 透传底层/SPI 错误
|
||
* 限定条件:先成功执行 gd5f_write_enable()
|
||
* 函数说明:E_FAIL 与 SPI 错误的区别同 program_exec;擦除超时归 GD5F_WAIT_EXPIRED(可重试)
|
||
*/
|
||
int32_t gd5f_block_erase(uint32_t block_addr);
|
||
|
||
/*
|
||
* 函数功能:检查上一次页读取的 ECC 结果(读 00h/BCh 特征寄存器的 ECC 状态)
|
||
* 入口参数:无
|
||
* 返 回 值:GD5F_OK - 无不可纠错误(可能含已纠正的位翻转);GD5F_ECC_ERROR - **存在不可纠正的 ECC 错误**
|
||
* 限定条件:SPI 已初始化且 ECC 已使能(init 期扫描在使能前调用本函数无意义)
|
||
* 函数说明:这是数据正确性的唯一判定点——SPI 层读到"内容"不等于"内容对"(见 §8.1 与本头"同族纪律"注释)
|
||
*/
|
||
int32_t gd5f_check_ecc(void);
|
||
|
||
/*
|
||
* 函数功能:判定缓冲整段是否位于 DMA 可达内存(GD5F_DMA_RAM_* 白名单窗口)
|
||
* 入口参数:p_buf - 传输缓冲首地址 const void*
|
||
* size - 传输字节数 size_t
|
||
* 返回值:1 - 整段 DMA 可达;0 - 不可达(含 size=0 / 地址回绕 / 越窗)
|
||
* 限定条件:无
|
||
* 函数说明:2026-10-12 由 gd5f2gq5ue.c 私有守卫升级为公开 API(DMA 可达性三层防线一环)——
|
||
* 驱动内部每次 >阈值 传输前用它(不可达即回落 CPU 轮询),
|
||
* FTL 层(nand_ftl.c)在 init 期用它自检静态页缓冲的落位。
|
||
*/
|
||
int gd5f_dma_buf_ok(const void *p_buf, size_t size);
|
||
|
||
/*
|
||
* 函数功能:NAND 长耗时操作看门狗心跳弱钩子
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:任意上下文可调(内核启动前调用为空操作)
|
||
* 函数说明:gd5f_wait_busy 的长轮询(块擦除等待最长 5 s、GC 页拷贝、
|
||
* 日志写)都运行在调用者任务上下文(default/ftp/ui/timesync 等);
|
||
* 本钩子默认空实现,App/util/task_watchdog.c 提供强符号,按调用者
|
||
* 任务句柄路由到该任务的心跳位,防止长耗时窗口心跳缺席导致 IWDG
|
||
* 误复位(2026-10-02 台架:0 字节 STOR 截断 → dhara GC > 800 ms
|
||
* 无心跳 → 停喂 → IWDG 误复位)。
|
||
*/
|
||
void nand_ftl_wd_beat(void);
|
||
|
||
/*
|
||
* 函数功能:NAND 长耗时操作看门狗直接喂狗弱钩子
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:任意上下文可调(内核启动前 / IWDG 未启动时为空操作)
|
||
* 函数说明:心跳钩子(nand_ftl_wd_beat)只能维持"心跳位",喂狗动作本身在
|
||
* defaultTask 主循环(task_wd_poll)——若长耗时 FTL 操作(GC/整卷
|
||
* 格式化)发生在 defaultTask 上下文,主循环被阻塞在 FTL 调用里,
|
||
* 喂狗点停摆 → 心跳位再新也无人喂 → IWDG 误复位(2026-10-02 台架
|
||
* 实证:console tx fault 条目落盘触发 GC,poller 饥饿 ~2 s → IWDG
|
||
* 复位、无任何 stall 日志)。本钩子让 FTL 长路径在操作进行中直接
|
||
* 喂狗,覆盖"喂狗点自身被长 I/O 阻塞"的形态。强符号由
|
||
* App/util/task_watchdog.c 提供(仅 IWDG 已启动时生效)。
|
||
*/
|
||
void nand_ftl_wd_feed(void);
|
||
|
||
/*
|
||
* 函数功能:SPI1 DMA 完成 / 错误通知(由实际注册 HAL SPI 回调的模块转调)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:中断上下文(HAL SPI 回调内),两者只写一个完成标志
|
||
* 函数说明:HAL 的 SPI 完成/错误回调是全局唯一符号,本模块只实现 TxCplt/RxCplt
|
||
* 两个(供自己的收发用),Error 回调的所有权留给回调的实际注册方
|
||
* (当前 = Drivers/BSP/CH395F/ch395f.c,按 hspi->Instance 分发)。**SPI1
|
||
* 分支必须转调这两个函数**,否则本模块的 DMA 等待在出错时只能靠超时
|
||
* 兜底。2026-10-22:取代原先被 CH395F 跨模块裸 extern 直接写的全局变量。
|
||
*/
|
||
void gd5f_spi1_notify_done(void);
|
||
void gd5f_spi1_notify_error(void);
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* GD5F2GQ5UE_H */
|