Files
STM32F4-Base/Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h
T
wandering1 d1e6bb4ba7 W5波+N-19收口:FTP鉴权落地、Trap25三连定案修复(N-30)、N-20/N-23/N-26
N-19 FTP 鉴权:
- lftpd_auth(USER/PASS 状态机 + 连续5次失败锁60s)+ SHA-256
  (user||pwd) 口令哈希;config.ini [ftp] 段 user/pwd_hash/write,
  出厂 admin/admin 在用有启动告警;lftpd_log.c 删除并入 lftpd_auth
- ftp_cfg 配置读写;台架 ftp_auth_bench.py 13/13

N-30 / Trap 25(CH395F_Trap_Records.md 四次定案全记录):
- 修复1 closed-with-data accept:客户端发完正常 FIN 后芯片按
  FUN_PARA(0x09) §5.52② 保留 Socket+RX 缓冲,旧代码把 CLOSED 一律
  relisten 自毁缓冲 → STOR 必挂 425;改为接受"已关但可读"连接排空
- 修复2 丢 CONNECT 中断探针 net_lost_connect_probe + last_peer_port
  幽灵甄别(拆链窗口握手完成但中断丢失,双信源去抖补 accept)
- 修复3 send_list/send_nlst 批量单发:背靠背 ~60B 小段毒化芯片 TX
  (芯片报 SEND_OK 但对端全丢),整份目录一次性写入
- 证伪存档:固定沉降/Socket 轮换冷却/listen 门控(台架 v7 矩阵)
- 台架工具 test/board/ch395f_reuse_bench.{c,h} + host 脚本存档
  (不注册编译;实验分支 n30-chip-bench 保留)

N-20:Error_Handler 死循环降级(记录+复位路径)
N-23:scripts/check_registration.py 双端注册反向校验(pre-commit)
N-26:GD5F NAND 直 DMA 调用方缓冲窗口断言

验证:BUILD_OK 0/0;ftp_regression 连跑 4 轮 11/11(修复前
LIST#4/#7 100% 挂、STOR#2 曾 100% 425);ftp_auth_bench 13/13;
@test 启动标记 PASS;ci_local L0+L0.5 全过;doc-links 通过。
登记簿 N-30 已销项。
2026-10-11 18:50:24 +08:00

332 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 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 可达内存窗口(N-26,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
/* 编译期守卫(N-26):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_BUSY_TIMEOUT -2
#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 契约(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。
*/
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-STO-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 共享) ==================== */
int32_t gd5f_wait_busy(uint32_t timeout_ms);
int32_t gd5f_write_enable(void);
int32_t gd5f_read_status(uint8_t *p_status);
int32_t gd5f_page_read(uint32_t page_addr);
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, uint32_t size);
int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, uint32_t size);
int32_t gd5f_program_exec(uint32_t page_addr);
int32_t gd5f_block_erase(uint32_t block_addr);
int32_t gd5f_check_ecc(void);
/*
* 函数功能:判定缓冲整段是否位于 DMA 可达内存(GD5F_DMA_RAM_* 白名单窗口)
* 入口参数:p_buf - 传输缓冲首地址 const void*
* size - 传输字节数 size_t
* 返回值:1 - 整段 DMA 可达;0 - 不可达(含 size=0 / 地址回绕 / 越窗)
* 限定条件:无
* 函数说明:N-26(2026-10-12)由 gd5f2gq5ue.c 私有守卫升级为公开 API——
* 驱动内部每次 >阈值 传输前用它(不可达即回落 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);
#ifdef __cplusplus
}
#endif
#endif /* GD5F2GQ5UE_H */