Files
STM32F4-Base/test/host/nand_port/stub/gd5f2gq5ue.h
T
wandering1 720f841494 fix(storage): dhara 移植层 is_free 整页精度 + HAL 越界守卫;日志读写批量化
登记簿 N-21(报告 N-12/N-13)与 N-24(报告 N-16)两项未闭环问题修复并销项。

N-12:dhara_nand_is_free() 旧实现只读页首 64 B 判全 0xFF,会把"页首
0xFF、后段有数据"的已编程页误判空闲,dhara 遂往已编程页再编程 = 位碰撞、
journal/map 指向垃圾且不报任何错。改整页扫描(栈上 64 B 分块、页首命中即
早退,故"已编程且页首有数据"的绝大多数页成本与旧实现同价)。

N-13:报告原建议经核对不成立——本仓裁剪版 dhara 既无 DHARA_NO_PAGE(哨兵
实为 DHARA_PAGE_NONE,journal.h:46)也无 DHARA_E_UNSUPPORTED(error.h 枚举
里没有,照抄直接编译失败),且 map.c 三处 dhara_journal_copy 调用点各自已
挡掉哨兵。真风险是移植层零上界校验:越界页号被芯片地址位截断成某个真实页
→ 静默读写错页。改为六个 HAL 入口统一几何上界守卫 + DHARA_E_CORRUPT_MAP。
错误码选择是刻意的:BAD_BLOCK 会让 journal.c:904 标坏无辜好块,RECOVER 会
让 map.c:275 启动回收;无错误通道的 is_free/is_bad/mark_bad 一律取保守值。

N-16 写侧:一次排空合并成"每文件一次 open/write/close"(≤8 条 = 512 B
静态缓冲)。掉电语义不变——批次仍在同一次 drain 调用内 close,而 close
回写目录项这个不同 LPN 会把 FTL 单页写回缓存逼出到 NAND;失败尾部回投
队列,条目不丢(旧实现 -2 卷忙会直接丢掉那一条)。容量轮转仍只在按条慢
路径做,批量路径发现装不下一批就返回 -3 退回。
N-16 读侧:s_read_raw() 加 8 条滑动读窗口 + 写者代数失效,API 与 UI 零改动。

验证:@build 0 错 0 警(Code 213,540 / ER_IROM1 232,280 B = 22.2%,
RW_IRAM1 75,232 B = 57.4%,ZI +1,640 B 全为静态缓冲、不吃任务栈);
@test PASS,且台架 journal 恢复点与修复前同轨——连续四次启动 sectors used
恒 14,094、Map head 每轮 +128 单调推进、epoch 稳定 3 → 整页 is_free 未
改变恢复语义、无双编程、无回退;stack_report 9 个任务全 PASS(defaultTask
静态最坏链 3,024 B / 余 40%,实测峰值 2,164 B / 余 57%)。

新增主机侧移植层单测 test/host/nand_port/(第 16 个 -unit-test 钩子):
RAM 假芯片 + 真实 dhara map/journal 源,149 断言,含"旧算法只看页首 64 B"
对照用例(将来有人把 is_free 改回去会立刻红)与 dhara 级 resume 双编程计数
为 0 的场景。L0.5 由 15 runner / 955 断言 → 16 runner / 1,104 断言。

文档:存储架构说明 §5.3 加移植层两条硬约束;运行与自检记录设计说明
§4.4/§4.5 写批量化与读窗口;GD5F Trap Records 新增 Trap 07(含三条遗留
代价与一条待办编译期断言);登记簿 N-21/N-24 删行销项并同步 README
§1/§2.0/§2.1、排期详述 C3、报告 §0.1/§0.2/§0.4/§4.x/§7 与去处表;
钩子计数 33→34、主机侧单测 15→16 全库同步。
2026-10-15 12:34:39 +08:00

85 lines
3.4 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.
/*
* 主机侧单测桩:gd5f2gq5ue.h(GD5F2GQ5UE SPI NAND 驱动 API)
* 模块功能:为 Drivers/BSP/GD5F2GQ5UE/nand_ftl.c 提供**可在 PC 上编译链接**的
* 芯片几何常量与 SPI 原语声明;实现体(RAM 假芯片)在
* nand_port_test.c 里给出。
*
* ⚠ 仅供 test/host/nand_port/ 使用,不参与 MCU 构建。
* 真实头 Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h 会 `#include "main.h"` 拖入整套
* ST HAL / CMSIS(GPIO、SPI、HAL_GetTick),主机侧不引入。
*
* 保真点(与真实头逐项对齐,只有容量是刻意缩小的):
* - 几何常量 GD5F_PAGE_SIZE / GD5F_PAGES_PER_BLOCK / GD5F_BLOCK_SIZE /
* GD5F_SPARE_SIZE / GD5F_BBT_POOL_COUNT **同名同值**;
* - GD5F_TOTAL_BLOCKS 真实 2048(256 MB)→ 本桩 **32**(4 MB),
* 够跑 dhara map/journal 与全部越界用例;
* - 返回值 GD5F_OK / GD5F_ERROR / GD5F_PROGRAM_FAIL / GD5F_ERASE_FAIL 同值;
* - SPI 原语**签名逐字照抄**真实头(gd5f_page_read/gd5f_read_from_cache/
* gd5f_program_load/gd5f_program_exec/gd5f_block_erase/gd5f_check_ecc/
* gd5f_write_enable/gd5f_dma_buf_ok/gd5f_get_usable_blocks/
* gd5f2gq5ue_is_block_bad/gd5f2gq5ue_mark_block_bad),
* 桩与真实 API 一旦漂移,编译期即报错。
*
* 刻意**不搬**的部分:SPI 命令码 / 状态位 / 控制引脚宏 / DMA 窗口常量与
* 编译期守卫(nand_ftl.c 不使用它们;DMA 可达性在 PC 上没有物理意义,
* 由假芯片的 gd5f_dma_buf_ok 恒返 1 代替)。
*
* 创建日期:2026-10-15
*/
#ifndef GD5F2GQ5UE_H
#define GD5F2GQ5UE_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ======================== 芯片几何(与真实头同名同值,除总块数) ======================== */
#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)
/* 真实 2048 块(256 MB)→ 假芯片 32 块(4 MB),见文件头说明 */
#define GD5F_TOTAL_BLOCKS 32
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
/* 坏块表保留块池(真实头同值):dhara 可见块数 = 总块数 - 本值 */
#define GD5F_BBT_POOL_COUNT 4
/* ======================== 返回值(与真实头同值) ======================== */
#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
/* ======================== SPI 原语(签名逐字照抄真实头) ======================== */
int32_t gd5f_write_enable(void);
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);
int32_t gd5f2gq5ue_is_block_bad(uint32_t block);
void gd5f2gq5ue_mark_block_bad(uint32_t block);
uint32_t gd5f_get_usable_blocks(void);
int gd5f_dma_buf_ok(const void *p_buf, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* GD5F2GQ5UE_H */