720f841494
登记簿 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 全库同步。
63 lines
3.2 KiB
Python
63 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""dhara NAND 移植层(nand_ftl)的主机侧单元测试运行器(L0.5,纯标准库)。
|
||
|
||
做什么:
|
||
用本机 MSVC 编译 test/host/nand_port/nand_port_test.c(它以 #include 纳入
|
||
**生产源文件** Drivers/BSP/GD5F2GQ5UE/nand_ftl.c),在 PC 上离线跑 dhara NAND
|
||
HAL 六个入口(is_bad / mark_bad / erase / prog / is_free / read / copy)的
|
||
几何守卫与整页判空,失败即非 0 退出。零硬件、零烧录、毫秒级。
|
||
|
||
工具链探测 / INCLUDE-LIB 拼接 / 编译运行 / 退出码判定全部委托给通用 harness
|
||
`test/host/common/unit_test_harness.py`(本文件只做声明)。
|
||
|
||
为什么要主机侧单测(而不是只用板端套件):
|
||
移植层是**纯地址算术 + 边界判定**,芯片侧只经 11 个 SPI 原语打交道——把它换成
|
||
一块 RAM 假芯片,整层就完全可在 PC 上跑。而它要防的两类缺陷恰恰是真机极难复现的:
|
||
- N-12:is_free 只看页首 64 B → "页首 0xFF、后段有数据"的页被误判空闲 →
|
||
dhara 往已编程页再编程 = 静默数据丢失(真机要恰好撞上那种页形态才发作);
|
||
- N-13:越界页/块号(含 DHARA_PAGE_NONE 哨兵)被地址位截断成真实页 →
|
||
静默读写/搬走无辜数据(真机上没有任何路径会主动喂进越界号)。
|
||
假芯片把"双编程"做成可数事件、并记录全部访问次数,于是"越界不产生任何芯片
|
||
访问""越界标坏块次数为 0"这类判据可机械断言——这是板端永远拿不到的观测面。
|
||
|
||
用法(仓库根目录):
|
||
python test/host/nand_port/run_unit_test.py
|
||
或统一用 .venv: & '.venv\\Scripts\\python.exe' test/host/nand_port/run_unit_test.py
|
||
|
||
退出码:0=PASS / 1=测试失败 / 2=环境错误(找不到编译器 / 编译失败 / 无汇总行)。
|
||
|
||
设计依据:docs/问题记录/项目审查与优化建议报告.md N-12/N-13(报告 R-3 建议的
|
||
`test/host/dhara/` 移植层单测,落位为 test/host/nand_port/);
|
||
创建日期:2026-10-15
|
||
"""
|
||
import os
|
||
import sys
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(HERE)))
|
||
# harness 位于 test/host/common/
|
||
sys.path.insert(0, os.path.join(ROOT, "test", "host", "common"))
|
||
|
||
from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
|
||
|
||
# 顺序敏感:stub 目录必须**先于**生产目录,才能覆盖 gd5f2gq5ue.h(真实头会拖入
|
||
# main.h + 整套 ST HAL/CMSIS,PC 上不可编译)与 dbg_log.h(真实头依赖 sys_clock)。
|
||
# nand.h / journal.h / map.h / error.h(Lib/dhara)与 ff.h / diskio.h / ffconf.h
|
||
# (Lib/FatFs)用**真实头**——接口契约原汁,桩与真实 API 一旦漂移编译期即报错。
|
||
BUILD = TestBuild(
|
||
name="NAND Port Tests",
|
||
src=os.path.join(HERE, "nand_port_test.c"),
|
||
include_dirs=[
|
||
os.path.join(HERE, "stub"),
|
||
os.path.join(ROOT, "Drivers", "BSP", "GD5F2GQ5UE"),
|
||
os.path.join(ROOT, "Lib", "dhara"),
|
||
os.path.join(ROOT, "Lib", "FatFs"),
|
||
HERE,
|
||
],
|
||
summary_marker="=== NAND Port Tests:",
|
||
)
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(run_host_unit_test(BUILD))
|