feat(S-5): 收编 SD2506/TPAFE 并给 dhara 失败分类,门禁基线 19→17

前一笔立契约与门禁(`Drivers/BSP/bsp_err.h` + `bsp-err` 钩子 + RS485 试点);这一笔按用户
指定顺序 C3 → C1 → C2 把三个模块真正收编,并让存储失败第一次变得可读、可分类。

**C3 · SD2506 整模块(11 个公共 API + 6 个内部 helper)**:三枚私有宏 `SD2506_OK`/
`SD2506_ERROR`/`SD2506_I2C_ERROR` **删除而不是留别名**——留别名等于新代码还能用旧形状,
把迁移洗掉;编译器替我们找全所有调用点。新增 `sd2506_err_from_hal()`,**把 I2C 超时从
"和没人应答共用一个 −2"里拆出来归成 `BSP_ERR_TIMEOUT`(可重试)**——SD2506 是启动链第一站、
I2C1 上电时序敏感,这是本波唯一真正的语义改善,不是改名。`sd2506_init` 的 charge 寄存器
**回读校验不符**原返参数类错误,改归 `BSP_ERR_IO`(那不是调用方的错)。

**C1 · 同形两义**:`GD5F_BUSY_TIMEOUT` → `GD5F_WAIT_EXPIRED`(**值 −2 不变**,纯消"名字叫
TIMEOUT 其实是返回码")。**GD5F 私有码集整体收编刻意没做**:169 处引用里 48 处在板级套件、
还要动 Trap 记录用词,属另一波带真机+套件回归的工作,不夹带。TPAFE5160 收编 23 处:
`BUSY_TIMEOUT → BSP_ERR_TIMEOUT`、`ERROR → BSP_ERR_PARAM`(**同值 −1,行为零变化**)、
`FRSTDATA_ERR → BSP_ERR_NO_DEVICE`(E-44 的本义就是"芯片没驱动 DB 总线",归到器件无响应比
私有 −3 有用,且避开与 `BSP_ERR_TIMEOUT` 撞值)。实测 `tpafe5160.o`/`gd5f2gq5ue.o` 的 Code
增量为 **0**——改名不产生一条指令。

⚠ **`tpafe5160_snapshot()` 刻意不套成败码**:它是 1=有新帧 / 0=无新帧 / −1=参数错 的三态
**状态**返回,0 不是错误而是"这一拍没有新数据",强行收编会把慢消费者的正常路径变成失败。
顺带订正我上一版在 `bsp_err.h` 的断言——"本层只有 `net_*` 把数量与错误挤同一返回值"是错的,
`snapshot`/`adc_input_poll` 就是第二族;两族现都已写明并各注保留理由。

**C2 · FTL 坍缩点分类**:`ftl_flush_cache`/`ftl_read_page` 不再把 dhara 失败压成
  `GD5F_ERROR`,
按**处置差异**分三类,并在最早同时知道 LPN 与原始码的位置就地打印:`ECC`/`TOO_BAD` →
`BSP_ERR_CRC`(页级,重试或换页,不代表卷坏);`JOURNAL_FULL`/`MAP_FULL` →
`BSP_ERR_NO_SPACE`(介质健康但块池/日志见底,此时重格式化只是把问题推回去);
`BAD_BLOCK`/`RECOVER`/`CORRUPT_MAP`/越界 → `BSP_ERR_IO`(完整性故障,保卷 + 上报,不许自愈
重格式化)。这与 **E-31** 是同一条纪律的两端。可读名用自建短表:**本仓 dhara 没有编
`error.c`,用 `dhara_strerror()` 会直接链接失败**。`disk_read`/`disk_write` 仍回
  `RES_ERROR`
(FatFs 契约);要让 defaultTask 卷健康探针按类分支还差跨调用存活的快照 → C4 待还。

**两次靠核实拦住的自我错误**:① 我一度认定 `DBG_ERROR("... err=%d", err)` 是把结构体塞进
varargs 的格式 bug——核 `Lib/dhara/error.h` 后确认 `dhara_error_t` 是 `enum`(标量),那行日志
本来合法,改了反而是引入错误;② `bsp-err` 一次 `replace_all` 越界把 8 处 **GD5F 码空间**的
判据改成 `!= BSP_OK`(数值同为 0、无行为变化),但那正是"把两个码空间混起来",已逐一还原。
`cppcheck` 亦抓到 `cls` 声明作用域过宽,已收进失败分支。

**验证**:`@build` 0 错 0 警告(`Code` 213,648、RO 18,860、`ER_IROM1` 232,508 B /
  22.2%、
`ROM 合计` 233,696;**RAM 三项零增量**);`@test` PASS(692 ms,烧入戳 `Oct 23 01:35:32`)
且逐条读到被改模块的锚点:`SD2506 init: OK (ret=0)` 与 ID/时间/温度/电池四条读回正常、
`ADC init: OK`、`NAND init: OK`、`Map resumed: 14094/97504`、`adc=` 帧计数持续递增(证明
snapshot 三态未被改坏)、`[ERR]`/Fault/FAIL 0 条。主机单测:系统时钟 100/100、dhara NAND
移植层 149/149(两者都因本次改动需补 `Drivers/BSP` include 路径,补后在钩子内实跑 Passed)。
`pre-commit run` 全量 RC=0(20 个实跑钩子 Passed);`bsp-err` 基线 **19 → 17**(还掉两个
`*_BUSY_TIMEOUT = -2`)、`--prune` 干净;`doc-links --all --sections-strict` 147 篇
  0 问题;
`id-check --strict`、`layer-deps`、`mem-numbers`、`mem-budget`、`ui-golden` 全过;33
  个文件
字节卫生 0 异常(无 CR / 无 U+FFFD / 无行尾空格 / 均有尾换行)。

**口径与数字联动**:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.4(三类码与处置
理由);`pre-commit使用说明.md` §2.27 与 `AGENTS.md` 基线计数 19 → 17;TPAFE README、
GD5F 测试指南与 Trap 记录均**保留旧名留痕**以便对旧日志;配图桶按 .map 对象级增量重算
(`dhara/FTL` 10,931 → 11,222,+291 全部落在 `nand_ftl.o`;应用代码/UI +36 落在
`app_main.o`;其余散量入"其他"7,557;桶和 = Code+RO 断言通过、文本溢出 0);
`ram_baseline --update/--write`、`ram_report_20261023.md` 快照、报告 §1.3/§5.1/§5.4
  与
部署文档 §3.3 数字全部同步;报告 §0.4/§9 的 S-5 状态改为 🟡 主体已落地。
This commit is contained in:
2026-10-23 07:42:02 +08:00
parent 8d3349c587
commit 78248a1cdf
33 changed files with 1183 additions and 345 deletions
+1
View File
@@ -50,6 +50,7 @@ BUILD = TestBuild(
src=os.path.join(HERE, "nand_port_test.c"),
include_dirs=[
os.path.join(HERE, "stub"),
os.path.join(ROOT, "Drivers", "BSP"), # bsp_err.h(nand_ftl.c 依赖,2026-10-23 S-5)
os.path.join(ROOT, "Drivers", "BSP", "GD5F2GQ5UE"),
os.path.join(ROOT, "Lib", "dhara"),
os.path.join(ROOT, "Lib", "FatFs"),
+2 -1
View File
@@ -39,13 +39,14 @@ from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
# 顺序敏感:stub 目录必须**先于**生产目录,才能覆盖 main.h / FreeRTOS.h / task.h /
# dbg_log.h(使 sys_clock.c 可在 PC 上编译)。sd2506.h 用真实头(其内部 #include
# "main.h" 命中 stub,故无需桩),保留原汁的 sd2506_time_t 与 SD2506_OK。
# "main.h" 命中 stub,故无需桩),保留原汁的 sd2506_time_t 与 BSP_OK(sd2506.h 现直接 include "bsp_err.h")。
BUILD = TestBuild(
name="System Clock Tests",
src=os.path.join(HERE, "sys_clock_test.c"),
include_dirs=[
os.path.join(HERE, "stub"),
os.path.join(ROOT, "App", "util"),
os.path.join(ROOT, "Drivers", "BSP"), # bsp_err.h(sd2506.h 依赖,2026-10-23 S-5)
os.path.join(ROOT, "Drivers", "BSP", "SD2506"),
HERE,
],
+29 -29
View File
@@ -61,7 +61,7 @@
#include "dbg_log.h"
/*
* sd2506_time_t 与 SD2506_OK 必须先可见(桩函数签名 / 阴影寄存器用它)。
* sd2506_time_t 与 BSP_OK 必须先可见(桩函数签名 / 阴影寄存器用它)。
* 真实 sd2506.h 内部 #include "main.h" 命中本目录 stub/main.h,故可安全使用;
* sys_clock.h 提供被测 API 原型(与 sys_clock.c 内部 include 走同一 guard)。
*/
@@ -85,27 +85,27 @@ uint32_t HAL_GetTick(void) { return s_tick; }
* - s_rtc_set_calls :写次数(观测 init/set 是否写回)
*/
static sd2506_time_t s_rtc;
static int s_rtc_read_rc = SD2506_OK;
static int s_rtc_write_rc = SD2506_OK;
static bsp_err_t s_rtc_read_rc = BSP_OK;
static bsp_err_t s_rtc_write_rc = BSP_OK;
static int s_rtc_get_calls = 0;
static int s_rtc_set_calls = 0;
int sd2506_get_time(sd2506_time_t *p_time) {
s_rtc_get_calls++;
if (s_rtc_read_rc != SD2506_OK) {
if (s_rtc_read_rc != BSP_OK) {
return s_rtc_read_rc; /* 故障注入:不动 p_time */
}
*p_time = s_rtc;
return SD2506_OK;
return BSP_OK;
}
int sd2506_set_time(const sd2506_time_t *p_time) {
s_rtc_set_calls++;
if (s_rtc_write_rc != SD2506_OK) {
if (s_rtc_write_rc != BSP_OK) {
return s_rtc_write_rc;
}
s_rtc = *p_time; /* 成功则阴影寄存器被更新(复读可见) */
return SD2506_OK;
return BSP_OK;
}
/* DBG_ERROR 计数 */
@@ -203,8 +203,8 @@ static void tc_001_init_ok(void) {
int rc;
printf("TC-SYSCLK-001 init 正常(RTC 合法)\n");
rtc_set(2026, 8, 24, 0, 0, 0);
s_rtc_read_rc = SD2506_OK;
s_rtc_write_rc = SD2506_OK;
s_rtc_read_rc = BSP_OK;
s_rtc_write_rc = BSP_OK;
clear_obs();
s_tick = 12345u;
@@ -219,7 +219,7 @@ static void tc_002_init_leap(void) {
printf("TC-SYSCLK-002 init 闰年换算\n");
/* 2024 是闰年:2/29 存在 */
rtc_set(2024, 2, 29, 12, 0, 0);
s_rtc_read_rc = SD2506_OK;
s_rtc_read_rc = BSP_OK;
CHECK(sys_clock_init() == 0, "2024-02-29 合法");
CHECK_EQ_U32(sys_clock_get(), ref_unix(2024, 2, 29, 12, 0, 0),
"2024-02-29 12:00:00 换算正确");
@@ -241,15 +241,15 @@ static void tc_002_init_leap(void) {
static void tc_003_init_month_end(void) {
printf("TC-SYSCLK-003 init 月末边界\n");
rtc_set(2026, 1, 31, 0, 0, 0); s_rtc_read_rc = SD2506_OK;
rtc_set(2026, 1, 31, 0, 0, 0); s_rtc_read_rc = BSP_OK;
CHECK(sys_clock_init() == 0, "1/31 合法");
CHECK_EQ_U32(sys_clock_get(), ref_unix(2026, 1, 31, 0, 0, 0), "1/31 换算");
rtc_set(2026, 2, 28, 23, 59, 59); s_rtc_read_rc = SD2506_OK;
rtc_set(2026, 2, 28, 23, 59, 59); s_rtc_read_rc = BSP_OK;
CHECK(sys_clock_init() == 0, "2/28 合法(平年)");
CHECK_EQ_U32(sys_clock_get(), ref_unix(2026, 2, 28, 23, 59, 59), "2/28 末端换算");
rtc_set(2026, 4, 30, 0, 0, 0); s_rtc_read_rc = SD2506_OK;
rtc_set(2026, 4, 30, 0, 0, 0); s_rtc_read_rc = BSP_OK;
CHECK(sys_clock_init() == 0, "4/30 合法(30 天月)");
CHECK_EQ_U32(sys_clock_get(), ref_unix(2026, 4, 30, 0, 0, 0), "4/30 换算");
@@ -269,12 +269,12 @@ static void tc_004_init_epoch_bounds(void) {
* Unix 0 经由 set 路径可达,见 tc_017(base_at(0) 等价)。
* 这里验证 init 可接受的最早年月,以及 uint32 上限。
*/
rtc_set(2000, 1, 1, 0, 0, 0); s_rtc_read_rc = SD2506_OK;
rtc_set(2000, 1, 1, 0, 0, 0); s_rtc_read_rc = BSP_OK;
CHECK(sys_clock_init() == 0, "2000-01-01 合法(可接受最早年)");
CHECK_EQ_U32(sys_clock_get(), ref_unix(2000, 1, 1, 0, 0, 0), "2000-01-01 换算");
/* 2038 上限:2038-01-19 03:14:07 = 0x7FFFFFFF */
rtc_set(2038, 1, 19, 3, 14, 7); s_rtc_read_rc = SD2506_OK;
rtc_set(2038, 1, 19, 3, 14, 7); s_rtc_read_rc = BSP_OK;
CHECK(sys_clock_init() == 0, "2038-01-19 合法(uint32 上限内)");
CHECK_EQ_U32(sys_clock_get(), 0x7FFFFFFFu, "2038-01-19 03:14:07 → 0x7FFFFFFF");
@@ -292,8 +292,8 @@ static void tc_004_init_epoch_bounds(void) {
static void tc_005_init_rtc_read_fail(void) {
printf("TC-SYSCLK-005 init RTC 读失败 → 降级默认时间并返回 -1\n");
s_rtc_read_rc = SD2506_ERROR; /* 注入读故障 */
s_rtc_write_rc = SD2506_OK;
s_rtc_read_rc = BSP_ERR_IO; /* 注入读故障 */
s_rtc_write_rc = BSP_OK;
clear_obs();
s_tick = 777u;
@@ -305,7 +305,7 @@ static void tc_005_init_rtc_read_fail(void) {
RTC_DEFAULT_MINUTE, RTC_DEFAULT_SECOND),
"降级基准 = 编译期默认时间(墙钟仍可用)");
CHECK(g_sclk_err_count >= 1, "打了 DBG_ERROR(RTC 异常)");
s_rtc_read_rc = SD2506_OK; /* 复位注入 */
s_rtc_read_rc = BSP_OK; /* 复位注入 */
}
static void tc_006_init_out_of_range_selfheal(void) {
@@ -316,8 +316,8 @@ static void tc_006_init_out_of_range_selfheal(void) {
* (只有写回/复读仍失败才走降级 -1,见 tc_005 与 tc_020。)
*/
rtc_set(1999, 5, 5, 0, 0, 0); /* year < 2000 → 越界 */
s_rtc_read_rc = SD2506_OK;
s_rtc_write_rc = SD2506_OK;
s_rtc_read_rc = BSP_OK;
s_rtc_write_rc = BSP_OK;
clear_obs();
CHECK(sys_clock_init() == 0, "year=1999 → 自愈写回成功 → 返回 0");
CHECK(s_rtc_set_calls == 1, "越界触发一次写回");
@@ -341,8 +341,8 @@ static void tc_020_init_selfheal_fail_degrades(void) {
* rtc_ok 清 0 → 走降级分支(启用默认值时建基准 + 返回 -1)。
*/
rtc_set(1999, 5, 5, 0, 0, 0);
s_rtc_read_rc = SD2506_OK;
s_rtc_write_rc = SD2506_ERROR; /* 写回失败 */
s_rtc_read_rc = BSP_OK;
s_rtc_write_rc = BSP_ERR_IO; /* 写回失败 */
clear_obs();
s_tick = 42u;
int rc = sys_clock_init();
@@ -352,14 +352,14 @@ static void tc_020_init_selfheal_fail_degrades(void) {
CHECK_EQ_U32(sys_clock_get(), ref_unix(RTC_DEFAULT_YEAR, RTC_DEFAULT_MONTH,
RTC_DEFAULT_DAY, 0, 0, 0),
"降级基准 = 编译期默认时间(墙钟仍可用)");
s_rtc_write_rc = SD2506_OK;
s_rtc_write_rc = BSP_OK;
}
static void tc_007_init_corrects_rtc(void) {
printf("TC-SYSCLK-007 init 越界 → 自动写回默认时间并复验\n");
rtc_set(1999, 1, 1, 0, 0, 0); /* 越界,触发校时写回 */
s_rtc_read_rc = SD2506_OK;
s_rtc_write_rc = SD2506_OK;
s_rtc_read_rc = BSP_OK;
s_rtc_write_rc = BSP_OK;
clear_obs();
int rc = sys_clock_init();
CHECK(s_rtc_set_calls == 1, "越界 → 写回 RTC 一次");
@@ -477,7 +477,7 @@ static void tc_016_set_writes_rtc(void) {
sd.year = 2027; sd.month = 3; sd.day = 15;
sd.hour = 6; sd.minute = 7; sd.second = 8; sd.week = 1;
s_rtc_write_rc = SD2506_OK;
s_rtc_write_rc = BSP_OK;
clear_obs();
s_tick = 100u;
CHECK(sys_clock_set(7000000u, &sd) == 0, "set 返回 0");
@@ -489,11 +489,11 @@ static void tc_016_set_writes_rtc(void) {
/* 写失败:基准仍更新,但打错误日志 */
clear_obs();
s_rtc_write_rc = SD2506_ERROR;
s_rtc_write_rc = BSP_ERR_IO;
sys_clock_set(7000001u, &sd);
CHECK_EQ_U32(sys_clock_get(), 7000001u, "RTC 写失败仍更新基准");
CHECK(g_sclk_err_count >= 1, "RTC 写失败打 DBG_ERROR");
s_rtc_write_rc = SD2506_OK;
s_rtc_write_rc = BSP_OK;
}
static void tc_017_set_no_rtc(void) {
@@ -509,7 +509,7 @@ static void tc_018_set_unix_fields(void) {
sd2506_time_t expect_week;
uint32_t u = ref_unix(2026, 10, 2, 15, 30, 45);
clear_obs();
s_rtc_write_rc = SD2506_OK;
s_rtc_write_rc = BSP_OK;
s_tick = 0u;
CHECK(sys_clock_set_unix(u) == 0, "set_unix 返回 0");
CHECK(s_rtc_set_calls == 1, "写回 RTC 一次");