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 全库同步。
This commit is contained in:
@@ -418,6 +418,17 @@ cppcheck 是开源 C/C++ 静态分析工具,**不运行代码**,通过语法
|
||||
| **为什么需要** | 记录格式是**磁盘上的字节级契约**——编解码错了要 FTP 下载 + 解码才看得见,板端日志照不出来;与 `/log/run.log` 同款定长 64 B 语义 |
|
||||
| **运行器** | `test/host/soe/run_unit_test.py`(MSVC `cl.exe`,L0.5) |
|
||||
|
||||
### 2.23 nand-port-unit-test — dhara NAND 移植层主机侧单测(2026-10-15 加,N-21)
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发条件** | `Drivers/BSP/GD5F2GQ5UE/nand_ftl.(c\|h)`、`gd5f2gq5ue.h`、`Lib/dhara/(map\|journal\|nand\|error\|bytes).(c\|h)`、`test/host/nand_port/**` 或 `test/host/common/unit_test_harness.py` 变动 |
|
||||
| **处理方式** | **阻止提交**(单测失败即拦截) |
|
||||
| **被测对象** | `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` 的 dhara NAND HAL 六个入口(`is_bad` / `mark_bad` / `erase` / `prog` / `is_free` / `read` / `copy`)——纯地址算术 + 边界判定;测试单元 `#include` 真实 `nand_ftl.c` + 真实 `Lib/dhara/journal.c` / `map.c`,芯片侧换成 **RAM 假芯片**(32 块 × 64 页 × 2048 B,两段式读缓存、NOR 叠加编程、双编程违规计数、访问次数记账) |
|
||||
| **用例** | 149 断言:整页扫描(页首 64 B 全 0xFF + 第 1000 字节有数据 / 页尾单字节 / 第 64 字节)+ **旧算法对照**(内联"只看页首 64 B"的旧实现,同一页答"空闲"而新实现答"非空")+ 六个入口的越界守卫(`DHARA_PAGE_NONE` / `total_pages` / `num_blocks`,断言 `-1` + `DHARA_E_CORRUPT_MAP` + **零芯片访问 / 零标坏块**)+ 正常往返 + 故障注入(ECC / read_from_cache / program_load / program_exec / erase 失败)+ dhara map 级"写入 → 重新 resume"场景(用 64 B 列读指纹证明 resume 真走了 is_free,再断言全程**双编程计数为 0**) |
|
||||
| **为什么需要** | N-12 / N-13 两类缺陷真机**极难复现**:is_free 误判要恰好撞上"页首 0xFF、后段有数据"的页形态,越界地址要哨兵从别处漏进来;两者发作后都只表现为"某次复位后丢数据",无从定位。移植层是纯逻辑 + 假芯片,能钉死真机极难复现的越界/误判路径,且"越界不产生任何芯片访问"这类判据在板端**根本拿不到观测面** |
|
||||
| **运行器** | `test/host/nand_port/run_unit_test.py`(MSVC `cl.exe`,L0.5);套件说明见 `test/host/README.md` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 日常工作流
|
||||
@@ -545,7 +556,7 @@ $env:PATH = [Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [Env
|
||||
|
||||
| 文件 | 位置 | 说明 |
|
||||
|---|---|---|
|
||||
| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**33 个钩子**,2026-10-12 清点;本文 §2 详解非单测类——N-19 新增的两个单测钩子见 §2.16/§2.17,N-23 双端注册反向校验见 §2.18,N-27 RAM 预算门禁见 §2.19,B4 IEC104 协议栈单测见 §2.20,B8 频率测量单测见 §2.21,B9 SOE 动作记录单测见 §2.22,其余主机侧单测见 AGENTS.md 门禁条;主机侧单测共 15 个) |
|
||||
| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**34 个钩子**,2026-10-15 清点;本文 §2 详解非单测类——N-19 新增的两个单测钩子见 §2.16/§2.17,N-23 双端注册反向校验见 §2.18,N-27 RAM 预算门禁见 §2.19,B4 IEC104 协议栈单测见 §2.20,B8 频率测量单测见 §2.21,B9 SOE 动作记录单测见 §2.22,N-21 dhara NAND 移植层单测见 §2.23,其余主机侧单测见 AGENTS.md 门禁条;主机侧单测共 16 个) |
|
||||
| `.gitlint` | 仓库根 | gitlint 规则(宽松,兼容中文) |
|
||||
| `.cubemx_survival_baseline.json` | 仓库根 | CubeMX 易失定制基线(哈希;`--write` 重建) |
|
||||
| `scripts/check_mojibake.py` | scripts/ | U+FFFD 检测脚本 |
|
||||
|
||||
+9
-11
@@ -66,8 +66,7 @@
|
||||
| P1 收尾 | 1 | P1-10 测试固件构建实测(`测试与架构专项.md` §1) |
|
||||
| 网络 | 4 | N-2(49s 日志静默,**低风险观察项**)· N-3(L5 基线重验)· N-16(A2/A4 负向台架)· N-17(探针窗口心跳缺口) |
|
||||
| 存储 / 日志 | 3 | **S-3(USART1 TX-only 根治)** · S-4(dhara 重试自旋)· S-5(可选全序列卷锁) |
|
||||
| 数据正确性 | 1 | **N-21(dhara `is_free`/`copy`,全簿最高风险)** |
|
||||
| 结构 / 健壮性 | 6 | N-22(写回 flush)· N-23(注册校验残余白名单)· N-24(日志逐条开关)· N-25(性能/语义簇)· N-28(Flash 死代码簇)· **N-33(栈余量收口,W1)** |
|
||||
| 结构 / 健壮性 | 5 | N-22(写回 flush)· N-23(注册校验残余白名单)· N-25(性能/语义簇)· N-28(Flash 死代码簇)· **N-33(栈余量收口,W1)** |
|
||||
| 仓库 | 1 | N-29(`.git` **235 MB**,**已决策暂不处理、登记观察**) |
|
||||
| 硬件 | 1 | N-18(V0.2 改版项) |
|
||||
| 测试 / 基础设施 | 6 | T-7(CI runner + 推送闭环 + 第二机交叉验证)· S12(断言宏)· S1-c(可选单测)· A-4(采集链路测试约束,RS485 侧随 B2 暂缓)· A-5 残余(CI 体积门禁盲区)· D7(串口助手 A6/A7 待 CH340) |
|
||||
@@ -78,14 +77,14 @@
|
||||
> `docs/编号规范.md` §4.3):E-25 / E-27 / E-28 挂 C1(net 层跨上下文收口,见 `排期详述.md` §1);
|
||||
> E-47 待 CH395F 驱动侧定案;E-12 为 `FF_FS_LOCK` / 单写者纪律记录项;E-31 后续与 E-62
|
||||
> 为回归监督项(纪律已固化于 `AGENTS.md`,无需改动)。
|
||||
> UI 标准化簇与覆盖率缺口(lftpd/dhara 移植层无主机测试)为整簇排期项,见 §2 W6 / N-21。
|
||||
> UI 标准化簇与 lftpd 主机测试缺口为整簇排期项,见 §2 W6(dhara 移植层主机单测已随登记簿
|
||||
> N-21 销项落地 `test/host/nand_port/`,2026-10-15)。
|
||||
|
||||
### 1.1 结构性判断(排序依据)
|
||||
|
||||
1. **用户决定(2026-10-12):先把 ADC 采集、保护等相关功能完成**——"转发 + 保护"两环代码已落地(B4/B5,2026-10-12),但**尚未台架闭环、功能面不完整**(定值不落盘、无频率保护、无动作记录/SOE、栈安全线两处 FAIL)。新 W1 = 采集→相量→保护→上送链路的**收尾与闭环**(B6~B10 + N-33,见 `W1-采集保护收尾.md`),其余波次顺延。**RS-485 对下采集(B2)按用户决定暂缓(2026-10-12,`暂缓-RS485.md`),主线不再等它**。
|
||||
2. **数据正确性最高风险 = N-21**(dhara `is_free` 只查页首 64 B / `copy` 不处理哨兵):低概率、后果重(数据丢失),且至今只能靠人工读代码发现——修复须配主机侧移植层测试(一并补报告"覆盖率"缺口)。
|
||||
3. **内存预算红线已机械化(2026-10-12,W1-0)**:`mem-budget` 钩子拦 RW_IRAM1/RW_CCM >92%,子系统配额 / CCM 台账 / 趋势基线 → `docs/内存管理/RAM预算台账.md`。实际优化只做了 **R-9(2026-10-15 落地,SRAM1 74.9% → 56.1%)**;**R-2 后半与 R-3 / R-4 / R-6 已按用户裁定 2026-10-15 整簇暂缓**(当前 CCM 只剩 **7,904 B**,重开条件见 `../问题记录/项目审查与优化建议报告.md` §5.3)——**门禁与台账继续常开,它们正是"何时该重开"的传感器**。
|
||||
4. **验证债仅剩窗口性事项**(D2/D5/D6/D7 + P1-10 收尾),不单独排期、逢窗口顺手做。
|
||||
2. **内存预算红线已机械化(2026-10-12,W1-0)**:`mem-budget` 钩子拦 RW_IRAM1/RW_CCM >92%,子系统配额 / CCM 台账 / 趋势基线 → `docs/内存管理/RAM预算台账.md`。实际优化只做了 **R-9(2026-10-15 落地,SRAM1 74.9% → 56.1%)**;**R-2 后半与 R-3 / R-4 / R-6 已按用户裁定 2026-10-15 整簇暂缓**(当前 CCM 只剩 **7,904 B**,重开条件见 `../问题记录/项目审查与优化建议报告.md` §5.3)——**门禁与台账继续常开,它们正是"何时该重开"的传感器**。
|
||||
3. **验证债仅剩窗口性事项**(D2/D5/D6/D7 + P1-10 收尾),不单独排期、逢窗口顺手做。
|
||||
|
||||
---
|
||||
|
||||
@@ -101,26 +100,25 @@
|
||||
| 波次 | 内容 | 为什么在这 |
|
||||
|---|---|---|
|
||||
| **W1 采集+保护收尾** | **里程碑制 + 并行道**:M1 台架闭环 ✅ 达成(B6 ✅→N-33 ✅)∥ M2 功能面(代码道 B7 ✅→{B8∥B9})→ M3 送检(B10,Fixed Date)——任务卡/判据/写范围见 `W1-采集保护收尾.md` | **用户决定(2026-10-12):先把 ADC 采集、保护等相关功能完成**。B4/B5 代码已落地但未台架闭环、功能面不完整(定值不落盘 / 无频率保护 / 无动作记录 / 栈两处 FAIL)——本波收口到"可用 + 可验收";真约束只有**台架**与**送检窗口**两个稀缺资源,其余任务按写范围切分并行 |
|
||||
| **W2 数据正确性** | **N-21** dhara `is_free`/`copy`(报告 N-12/13) | 低概率、后果重(数据丢失);修复须配主机侧移植层测试,一并补报告"覆盖率"缺口 |
|
||||
| **W3 预算与内存** | ⏸ **整簇暂缓**(用户裁定 2026-10-15):~~缩堆 56→32 KB → R-2 后半(29.5 KB→CCM)→ R-3 / R-4 / R-6 逐项评估~~。本簇已落地的只有 **R-9 ✅(2026-10-15)**;R-5 已判定不可行 | **不再排期**:没有功能在等这块预算,而搬迁要动 scatter、要逐条过 DMA 可达性清单、缩堆还要上板复测。**W3 槽位保留、不重编号**(避免全库改 W4~W7 引用)。重开条件 → `../问题记录/项目审查与优化建议报告.md` §5.3 |
|
||||
| **W4 结构债** | **C6(S-3)→ C3(N-2+N-24)→ C1 net 层跨上下文收口 → C5(含 N-23 残余)→ N-22/N-25/N-28**(详表 `排期详述.md` §1) | 无紧急,按性价比排:C6 灭 IWDG 误复位机制根源;C1 消化 E-25/E-27/E-28(原 ACCEPT+SEND pending 化已收口),体量大、验证重,放后(N-23 钩子已落地 2026-10-12,残余并入 C5) |
|
||||
| **W4 结构债** | **C6(S-3)→ C3(N-2)→ C1 net 层跨上下文收口 → C5(含 N-23 残余)→ N-22/N-25/N-28**(详表 `排期详述.md` §1) | 无紧急,按性价比排:C6 灭 IWDG 误复位机制根源;C1 消化 E-25/E-27/E-28(原 ACCEPT+SEND pending 化已收口),体量大、验证重,放后(N-23 钩子已落地 2026-10-12,残余并入 C5)。⚠ 原挂 C3 的 **N-24(日志逐条开关)已于 2026-10-15 单独修复销项**,C3 只剩 N-2 观察项 |
|
||||
| **W5 验证常态** | P1-10 收尾 · T-7 CI · D2 N-3 · D5 N-16 · D6 N-17 · D7 串口助手 · S12 · S1-c · A-5(详表 `排期详述.md` §2) | 窗口性,逢机顺带,不单独排期 |
|
||||
| **W6 UI 扩展** | U4 残余(golden + 字符集差集挂门禁)→ U1 令牌层 → U2/U3 → **27 页**落地 | 产品驱动、体量大,需单独立项(顺序按 `docs/问题记录/项目审查与优化建议报告.md` §6.5);**本轮不排期(2026-10-11 已确认)** |
|
||||
| **W7 仓库决策** | **N-29** `.git` **235 MB** + 图片 36.3 MB | **已决策(2026-10-11):暂不处理、登记观察**——推送/协作前重评(`git filter-repo` 历史重写影响未推送提交与协作者) |
|
||||
| **暂缓** | **B2 RS-485 整簇**(`暂缓-RS485.md`) | **用户决定(2026-10-12):当前不处理 RS-485** |
|
||||
| **硬件改版** | N-18 8 路电气量全接 TPAFE(V0.2) | 下一版硬件;固件侧已用语义映射隔离,改版只动映射表 |
|
||||
|
||||
> ✅ **W2 数据正确性已清空销项(2026-10-15)**:登记簿 N-21(dhara `dhara_nand_is_free()` 只查页首 64 B / HAL 越界无校验)已修复并配主机侧移植层单测 `test/host/nand_port/` → 实施记录 `../架构设计/STM32F4-Base存储架构说明.md` §5.3、验证链 `../问题记录/项目审查与优化建议报告.md` 变更记录。**W2 槽位与 W3 同理保留、不复用也不重编号 W3~W7**(避免全库改引用)。
|
||||
|
||||
### 2.1 顺序一图
|
||||
|
||||
```
|
||||
W1 收尾 台架道: B6✅→N-33✅(M1 ✅ 2026-10-12) ∥ 代码道: B7✅→B8✅→B9✅(M2 ✅ 2026-10-12) → B10 送检(M3, Fixed Date) 详表 W1-采集保护收尾.md
|
||||
(B4 IEC104 ✅ / B5 protectTask ✅ / B6 台架复测 ✅ / B7 定值持久化+软压板 ✅ / N-33 栈收口 ✅ / B8 频率保护 ✅ / B9 SOE 动作记录 ✅ 2026-10-12 均已销项;台账三件套 ✅)
|
||||
↓
|
||||
W2 数据 N-21 dhara is_free/copy(配主机侧移植层测试)
|
||||
↓
|
||||
W3 预算 ⏸ 整簇暂缓(2026-10-15 用户裁定);R-9 池瘦身 ✅ 已落地不回退
|
||||
↓
|
||||
W4 结构 C6(S-3) → C3(N-2/N-24) → C1 net 层跨上下文收口 → C5(含 N-23 残余) → N-22/25/28
|
||||
W4 结构 C6(S-3) → C3(N-2) → C1 net 层跨上下文收口 → C5(含 N-23 残余) → N-22/25/28
|
||||
↓
|
||||
W5 验证 P1-10 · T-7 CI · D2/D5/D6/D7 · S12 · S1-c · A-5
|
||||
↓
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
| 序 | 任务 | 说明 | 出处 |
|
||||
|---|---|---|---|
|
||||
| **C6** | **USART1 TX-only 结构根治(S-3)** | 灭 ORE 僵尸机制根源(2026-10-02 台架 IWDG 误复位的机制根源):改 `.ioc` 令 USART1 不使能 RX / PA10 不复用为 `USART1_RX` → CubeMX 重生成 → 跑 `check_cubemx_survival.py`。结构性改动,需单开一轮 + 复跑台架闭环 | S-3 |
|
||||
| **C3** | **存储侧日志管线(N-2 + N-24)** | 丢弃可观测性**代码已具备**(`console_log.c` `s_con_drop`+`[ERR] CONSOLE dropped`、`sys_log_mcu.c` 队列满 `DBG_ERROR`、TX 僵尸通道自愈);V5 30 min 日志**未复现 49s 静默**(运行期 >10s 静默全为 ~28s、均在 `e02_probe` 窗口内=N-17)。剩余:与 S8(非阻塞日志)合并评估 + 日志逐条 `f_open/f_write/f_close` 改造评估(N-24) | N-2 / N-24 |
|
||||
| **C3** | **存储侧日志管线(N-2)** | 丢弃可观测性**代码已具备**(`console_log.c` `s_con_drop`+`[ERR] CONSOLE dropped`、`sys_log_mcu.c` 队列满 `DBG_ERROR`、TX 僵尸通道自愈);V5 30 min 日志**未复现 49s 静默**(运行期 >10s 静默全为 ~28s、均在 `e02_probe` 窗口内=N-17)。剩余:与 S8(非阻塞日志)合并评估。⚠ 原挂本卡的 **N-24**(日志逐条 `f_open/f_write/f_close`)已于 2026-10-15 修复销项(写侧一次排空合并成每文件一次开关、读侧 8 条滑动窗口)→ 实施记录 `../子系统设计/运行与自检记录设计说明.md` §4.4 / §4.5 | N-2 |
|
||||
| **C1** | **net 层跨上下文收口** | 消化 E-25(`net_select` 语义)、E-27/E-28(裸指针收口为状态查询 API)。原 ACCEPT + SEND pending 化已收口:SEND 2026-10-14 落地、ACCEPT 决定不实施(2026-10-15) | E-25 / E-27 / E-28 |
|
||||
| **C5** | 一档剩余 | **N-23 残余**(`KNOWN_GAPS` 白名单清理 + `app_cfg.h` 未接线开关,见 `登记簿.md`)。**RS-485 DE 收尾已移入 `暂缓-RS485.md`** | 一档/二档 |
|
||||
| **N-22/25/28** | 评估簇 | 写回 flush / 性能与语义一致性 / Flash 死代码——逐项见 `登记簿.md` 对应行,随窗口评估 | 报告各条 |
|
||||
|
||||
@@ -41,10 +41,8 @@
|
||||
|
||||
| # | 事项 | 影响 / 现状 | 处置方向 | 波次 | 出处 |
|
||||
|---|---|---|---|---|---|
|
||||
| N-21 | **dhara 两处**:`dhara_nand_is_free()` 只查页首 64 B;`dhara_nand_copy()` 不处理 `DHARA_NO_PAGE` 哨兵 | 映射恢复误判 → **数据丢失风险**(低概率、后果重);哨兵页号被当真实页 → 未定义行为;至今只能靠人工读代码发现 | 整页(或首/中/尾)扫描 + 返回 `DHARA_E_UNSUPPORTED`;**必须配主机侧移植层单测**(一并补报告"覆盖率"缺口) | **W2** | 报告 N-12/N-13 + §7 |
|
||||
| N-22 | 写回缓存无空闲/掉电强制 flush(整页写与换页已 flush) | 写回窗口内掉电丢最近日志/配置写 | defaultTask 空闲 N ms flush | W4 | 报告 N-14 |
|
||||
| N-23 残余 | **钩子已落地**(2026-10-12 `registration-check` 挂 pre-commit,存量缺口走 `KNOWN_GAPS` 白名单只 WARN);**残余** = 白名单只剩 `rs485_task.c` 一条**刻意**缺口(E-59 门禁下随 B2 暂缓,见 `暂缓-RS485.md`);`app_cfg.h` 未接线开关(C5)。⚠ `lftpd_log.c` 已随 N-19 删除并入 `lftpd_auth.c`(2026-10-11),不再是缺口 | 白名单不清 = "写了不编译"盲区仍在 | 随 B2 一并出白名单(`--strict` 供审计复跑);开关随 C5 处置 | W4(C5) | 报告 N-06 残余 + 报告 S-3 |
|
||||
| N-24 | 日志每条 `f_open+f_write+f_close`(`sys_log_mcu.c:123-140`) | 每条日志 2–4 次 NAND 页读写;UI 翻记录逐条 open/seek/close | 与 C3 日志管线合并评估(现仅 `FIL` static 化省 528 B 栈) | W4 | 报告 N-16(**≠ 本登记簿 N-16**) |
|
||||
| N-25 | 性能/资源建议簇:PASV 每次新建监听;PHY 强制 100M 无自协商回退;`NET_MAX_SOCKETS 8` 与单连接语义不符;`HAL_Delay(1)` 阻塞 DE(**DE 项随 B2 暂缓,见 `暂缓-RS485.md`**)。⚠ 原列的 `NET_RECV_TIMEOUT_MS 30000` 已随 2026-10-13「recv 语义主流化」删除(挂起 recv 模型),不再是待办 | 次级性能/语义一致性 | 逐项见 `../问题记录/项目审查与优化建议报告.md` §4.2/§4.7,随窗口评估 | W4 | 报告 N-09/PHY/P3-x/rs485 |
|
||||
| N-27 | **内存优化簇**:**R-9 ✅ 已落地(2026-10-15)**——`LV_MEM_SIZE` 40→16 KB → SRAM1 由 74.9% 降到 **56.1%(余 57,504 B)**;待迁 CCM 体量随之由 54,146 B 降到 **29,570 B**,而 CCM 只剩 **7,904 B**(`RW_CCM` 上限 65,280 − 57,376) | ⏸ **整簇暂缓(用户裁定 2026-10-15)**:R-2 后半(缩堆 56→32 KB → 迁 CCM)与 R-3 / R-4 / R-6 **不再排期**——没有功能在等这块预算,而搬迁要动 scatter、要逐条过 DMA 可达性清单、缩堆还要上板复测,收益不抵风险与工时。⚠ 代价落在"以后加任务 / 抬栈 / 加堆"那一刻:CCM 只剩 7,904 B、FreeRTOS 堆实测 `min_ever` 8,464 B,届时 `mem-budget` 会先报警 → 按报告 §5.3 重开条件评估 | **三件套 ✅ 2026-10-12 落地**(`ram_baseline.py` 四模式 + `ram_baseline.tsv` 趋势 + `docs/内存管理/RAM预算台账.md` + `mem-budget` 钩子 92% 门禁)→ **R-9 ✅ 2026-10-15**(实测峰值 6,568 B = 池的 40%,水位表见 `../内存管理/RAM使用分析与优化方案.md` §2.6.2)→ **⏸ 其余整簇暂缓**(2026-10-15 裁定;理由与重开条件 → `../问题记录/项目审查与优化建议报告.md` §5.3)。⚠ **R-5 已判定不可行、从本簇剔除**:CMSIS-RTOS2 `freertos_os2.h` 有 `#if (configMAX_PRIORITIES != 56) #error`,改小直接编译失败(为 480 B 改第三方包装层不划算) | **暂缓** | 报告 R-2 剩余/R-3/R-4/R-6/§5.5 |
|
||||
| N-28 | Flash 死代码评估簇:剩余 ≈10 KB(`box_shadow` 3,246 / `draw_sw_img` 2,960 / `bin_decoder` 1,972 / `image_decoder` 952 / `triangle` 930)+ 双精度 `dadd` 等 ≈1 KB;uvprojx 仍挂 201 个空 LVGL TU(F-1/F-2/F-3 已闭环,省的是编译时长) | 编译时长 + 文档噪音 | 逐项实测再关;⚠ `mask`/`mask_rect`/`grad` 是文字与矩形绘制必经路径,**不可盲关** | W4 | 报告 F-4/F-5/F-6 |
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 |
|
||||
|---|---|---|---|
|
||||
| ER_IROM1 | 231,084 B (225.67 KB) | 1,048,576 B (1024 KB) | 22.0% |
|
||||
| RW_IRAM1 | 73,568 B (71.84 KB) | 131,072 B (128 KB) | 56.1% |
|
||||
| ER_IROM1 | 232,280 B (226.84 KB) | 1,048,576 B (1024 KB) | 22.2% |
|
||||
| RW_IRAM1 | 75,232 B (73.47 KB) | 131,072 B (128 KB) | 57.4% |
|
||||
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
|
||||
| **Flash (ROM 合计)** | **232,196 B (226.8 KB)** | 1,048,576 B (1024 KB) | **22.1%** |
|
||||
| **Flash (ROM 合计)** | **233,416 B (227.9 KB)** | 1,048,576 B (1024 KB) | **22.3%** |
|
||||
<!-- MEM_SNAPSHOT:END -->
|
||||
|
||||
各区的容量与常量来源:
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|
||||
|---|---|---|---|---|
|
||||
| ER_IROM1 (Flash) | 231,084 B | 1,048,576 B | 22.0% | 参考 |
|
||||
| ER_IROM1 (Flash) | 232,280 B | 1,048,576 B | 22.2% | 参考 |
|
||||
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
|
||||
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
|
||||
| RW_IRAM1 | 73,568 B | 131,072 B | 56.1% | PASS |
|
||||
| RW_IRAM1 | 75,232 B | 131,072 B | 57.4% | PASS |
|
||||
|
||||
| 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 |
|
||||
|---|---|---|---|
|
||||
@@ -26,7 +26,7 @@
|
||||
| 存储 | 7,056 B | 12 KB | OK |
|
||||
| UI | 30,412 B | 16 KB | ⚠ 超 13.7 KB |
|
||||
| RTOS 堆 | 57,376 B | 56 KB | 堆=配额 |
|
||||
| 应用 | 13,247 B | 8 KB | ⚠ 超 4.9 KB |
|
||||
| 应用 | 14,911 B | 8 KB | ⚠ 超 6.6 KB |
|
||||
| 其它 | 9,184 B | — | 参考 |
|
||||
<!-- MEM_BUDGET:END -->
|
||||
|
||||
|
||||
@@ -123,12 +123,18 @@
|
||||
|
||||
defaultTask(唯一写者,挂载成功后):
|
||||
1) f_mkdir("/log")(幂等)
|
||||
2) 排空启动前数组 + 队列 → 逐条落盘(f_open FA_OPEN_APPEND → f_write(64B) → f_close)
|
||||
2) 排空启动前数组 + 队列 → 按文件合并落盘(一批最多 8 条:一次
|
||||
f_open FA_OPEN_APPEND → f_write(n×64B) → f_close,见下方 N-24 说明)
|
||||
3) 写"上电/重启"分类记录(§5.3,基于 run.log 末条 epoch)
|
||||
4) 主循环:osMessageQueueGet(阻塞, 10s 超时) → 落盘;超时空转继续 LED 闪烁
|
||||
```
|
||||
|
||||
- 读路径与写路径并发安全:UI 页在 uiTask 直接 `f_open(FA_READ)` 按偏移读记录(只读既有内容,写者只在 EOF 追加);FatFS 系统锁保护单次调用。
|
||||
- **N-24(2026-10-15,报告 N-16 写侧):一次排空合并成"每文件一次 open/write/close"**。旧实现每条记录一轮,而 `f_close` 要回写目录项、`f_write` 要对目标扇区做读-改-写 → **每条日志 2~4 次 NAND 页读写**。现在同一文件的一批(≤8 条 = 512 B 静态缓冲 `s_batch`)合并成一次追加写:
|
||||
- **掉电语义不变**:批次仍在**同一次 `syslog_drain()` 调用内**写完并 `f_close`;close 回写目录项(另一个 LPN)会把 FTL 的单页写回缓存逼出到 NAND——该"写到不同 LPN 才刷前一脏页"的语义见 `sys_log_evidence()` 的 flush 诱导注释。落盘时机与旧实现每条 close 等价,**批次不跨调用驻留 RAM**。
|
||||
- **条目不丢**:启动前数组按下标"看而不删",整批成功才删;队列条目在落盘失败(-2 卷忙 / -1 写失败 / -3 容量不足)时**回投队列**——旧实现 -2 会直接丢掉那一条。
|
||||
- **容量轮转仍只在按条慢路径做**:批量路径发现剩余容量装不下一批时返回 -3,退回逐条 `s_append()`("满了 unlink 重建"只保留一处实现,避免一批跨多轮重建)。
|
||||
- 自愈重试(L1 丢弃重建 + 重试一次)与错误去重(L2 只报第一次)判据与单条路径一致。
|
||||
- 读路径与写路径并发安全:UI 页在 uiTask 经 `syslog_get_record()` 读记录(内部走 `s_read_raw()`,见 §4.5);读写两侧都取卷锁 try 语义(P2-10),FatFS 系统锁保护单次调用。
|
||||
- **存储未就绪/挂载失败**:待发条目滞留 RAM(下次上电丢失),串口打印告警——存储故障时本就无法持久化,属预期行为。
|
||||
|
||||
### 4.5 读路径(UI / 模拟器)
|
||||
@@ -139,7 +145,9 @@ int syslog_get_record(syslog_id_t id, uint32_t idx, char *ts, size_t ts_len
|
||||
char *desc, size_t desc_len); /* 越界返回 -1 */
|
||||
```
|
||||
|
||||
- MCU:`f_open(FA_READ) → f_lseek(idx*64) → f_read(64B) → f_close`,内联拆分函数切 ts/desc。
|
||||
- MCU:**8 条滑动读窗口**(N-24,报告 N-16 读侧)——`s_read_raw()` 先查窗口(同 id + 代数未变 + 落在区间内 → **零 I/O** 直接从 512 B 静态窗口返回);未命中才 `f_open(FA_READ) → f_lseek(窗口首条×64) → f_read(≤8×64B) → f_close` **一次取回连续 8 条**,再从中切出请求的那条。旧实现每读一条都要 open/lseek/read/close 一轮,UI 翻 20 条记录 = 20 次目录项/FAT 往返。
|
||||
- 失效机制:写者每次落盘后把 `s_rd_gen[id]` 自增,读者比对填充时记录的代数,不匹配就重填。对齐 32 位在 ARM 上原子,跨任务(uiTask 填、defaultTask 增)无需临界区;最坏情况多读一轮,不会读错。
|
||||
- **API 与调用方一律不变**(`syslog_get_record()` 签名照旧,`ui_page_runlog` / `ui_page_selftest` 零改动)。
|
||||
- 模拟器:宿主文件同语义(UI 页双端零差异)。
|
||||
- UI 页每帧仅读当前窗口 ≤8 条记录(每次重绘 8×64B SPI 读,用户翻页触发,频率极低)。
|
||||
|
||||
|
||||
@@ -331,11 +331,16 @@ dhara 通过 7 个回调函数访问物理 NAND:
|
||||
| `dhara_nand_erase` | 擦除块 | `nand_block_erase(block + 1024)` |
|
||||
| `dhara_nand_prog` | 写页 | `nand_program_load + nand_program_exec` |
|
||||
| `dhara_nand_read` | 读页 | `nand_page_read_to_cache + nand_read_from_cache` |
|
||||
| `dhara_nand_is_free` | 检查页空闲 | 读前 64 字节判断全为 0xFF |
|
||||
| `dhara_nand_is_free` | 检查页空闲 | **整页扫描 2048 B 判全 0xFF**(栈上 64 B 缓冲分块读;页首命中即早退,见下方 ⚠) |
|
||||
| `dhara_nand_copy` | 页拷贝 (GC 用) | read + prog 组合 |
|
||||
|
||||
所有回调自动将 dhara 逻辑块/页加 `FTL_START_BLOCK` 偏移转换为物理地址。
|
||||
|
||||
⚠ **两条移植层硬约束(2026-10-15,登记簿 N-21 / 报告 N-12、N-13)**:
|
||||
|
||||
1. **`is_free` 必须整页精确**。dhara 的 journal 用它判断"检查点组是否未编程"(`journal.c:416 cp_free`、`journal.c:520` 找下一空闲页),其正确性前提是"答空闲的页必须真的全 0xFF"(`journal.c:405-414` 的注释论证)。旧实现只看页首 64 B → 一个"页首 0xFF、后段有数据"的已编程页会被误判空闲,dhara 遂**往已编程页再编程一次** = 位碰撞、journal/map 指向垃圾(数据丢失,且不报任何错)。本卷跑的是 FatFS 数据页,这种页型很常见(刚格式化的卷、稀疏文件块、日志尾部)。成本上做了快路径:页首 64 B 有数据就当场返回非空,与旧实现同价,只有"目前读到的全是 0xFF"的页才继续扫后段。
|
||||
2. **六个 HAL 入口一律做几何上界校验**(上界 = `n->num_blocks << n->log2_ppb`)。越界页/块号(含 dhara 哨兵 `DHARA_PAGE_NONE` = 0xffffffff)旧实现直接加偏移送进 SPI 命令,会被芯片地址位**截断成某个真实页** → 静默读写/搬走无辜数据。现越界时 prog/read/copy/erase 返回 -1 且 `*err == DHARA_E_CORRUPT_MAP`,`is_free` 返回 0(保守判非空)、`is_bad` 返回 1(保守判坏)、`mark_bad` 直接返回(绝不标无辜好块)。**错误码选择是刻意的**:不能用 `DHARA_E_BAD_BLOCK`(`journal.c:904 recover_from` 会据此标坏块并跳块),也不能用 `DHARA_E_RECOVER`(`map.c:275 try_recover` 会据此启动回收);其余错误一律原样上抛,不会静默清 map(E-31 教训)。
|
||||
|
||||
### 5.4 初始化流程 (disk_initialize)
|
||||
|
||||
```
|
||||
|
||||
@@ -273,6 +273,7 @@ static void phaseN_run(void) {
|
||||
| **`test/host/watchdog/run_unit_test.py`** | `test/host/watchdog/` | **L0.5 主机侧单测**:声明 `TestBuild` 后调 harness,穷举看门狗判定状态机(零硬件、秒级)。**需本机装有 VS(`cl.exe`)**,见 §4.2 环境要求 |
|
||||
| **`test/host/sys_clock/run_unit_test.py`** | `test/host/sys_clock/` | **L0.5 主机侧单测**:穷举 `sys_clock` 时间换算 / 星期 / 惰性再基准化 / RTC 故障降级(100 断言,S1-a) |
|
||||
| **`test/host/cfg_file/run_unit_test.py`** | `test/host/cfg_file/` | **L0.5 主机侧单测**:INI 解析 / 值编解码 / 段注册 / load-save-poll 契约,内存虚拟 FS 驱动(139 断言,S1-b) |
|
||||
| **`test/host/nand_port/run_unit_test.py`** | `test/host/nand_port/` | **L0.5 主机侧单测**:离线编译真实 `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` + 真实 dhara,以 **RAM 假芯片**驱动,149 断言钉死 is_free 整页扫描(N-12)与六个入口的越界守卫(N-13) |
|
||||
| **`scripts/ci_local.py`** | `scripts/` | **本地跑与 CI 等价的门禁**(L0 + L0.5):自动选用装了 pre-commit 的解释器;`.map` 自适应跳过 `mem-numbers`;退出码 0/1/2 与 CI 对齐。见 §9.3 |
|
||||
| `.serialtest/config.json` | 仓库 | **串口端口单一配置源**(当前 COM6 / 115200);换端口只改这个文件 |
|
||||
| `test/host/ftp_*.py`(4 个)/ `ch395f_socket_test.py` / `time_sync_server.py` | `test/host/` | PC 端交互测试工具(FTP 客户端 / socket 回显 / 对时推送) |
|
||||
@@ -329,9 +330,10 @@ static void phaseN_run(void) {
|
||||
| `App/util/time_sync.c` | — | ⬜ 候选 | 协议帧编解码 | 报文组装/解析纯逻辑;网络收发不测 |
|
||||
| `Drivers/BSP/NETCFG/net_cfg_legacy.c` | — | ⛔ 已删除(2026-10-10) | — | 旧档 NCFG 迁移整体移除(旧档 `/cfg/netcfg.bin` 不读、不删、不迁移),测试候选随之撤销 |
|
||||
| `App/protocol/iec104/*`(104 协议栈) | — | ✅ 已覆盖(82 断言,2026-10-12 B4) | 数据源 vtable + tick 注入 + 发送回调注入 | APCI 重组/总召分片/COV/k-w-t1~t3/两段式全离线穷举;`iec104-unit-test` 钩子 |
|
||||
| `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c`(dhara 移植层) | 573 行 | ✅ 已覆盖(149 断言,2026-10-15 N-21) | 11 个 SPI 原语(`gd5f2gq5ue.h`)→ **RAM 假芯片** | 移植层是纯地址算术 + 边界判定,换掉芯片即可离线跑;钉死 N-12(is_free 整页扫描)与 N-13(六入口越界守卫),并内联旧算法做对照证明断言有牙 |
|
||||
| `App/util/rs485.c` | — | ⏸ 待功能实现 | — | 上游 B2;当前为空桩 |
|
||||
| `Drivers/BSP/TPAFE5160/*.c` | — | ❌ 不适格 | — | 寄存器序列为主,需真机时序 |
|
||||
| 其余驱动(CH395F/GD5F2GQ5/SD2506) | — | ❌ 不适格 | — | 需硬件时序 / SPI / I2C 总线 |
|
||||
| 其余驱动(CH395F/GD5F2GQ5UE 芯片层/SD2506) | — | ❌ 不适格 | — | 需硬件时序 / SPI / I2C 总线。⚠ **例外**:GD5F 之上的 `nand_ftl.c` 移植层**适格**(见上一行)——"驱动不适格"不等于"驱动之上的胶水层不适格" |
|
||||
| `App/util/crc.c` · `ringbuf.c` | 120/154 行 | ⛔ 已删除(2026-10-02 S8) | — | 孤儿文件,零引用,已 `git rm` |
|
||||
|
||||
**使用方式**:新增/改动上述模块前,按表判断是否需补 L0.5;`⬜ 候选` 表示"适格但未做",
|
||||
@@ -623,6 +625,7 @@ CI 就静默停摆(2026-10-11 实测 run 16~21 共 12 个 job 零日志)。
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-15 | **新增 `test/host/nand_port/`(dhara NAND 移植层 L0.5,N-21 / 报告 N-12+N-13)**:`#include` 真实 `nand_ftl.c` + 真实 `Lib/dhara/{journal,map}.c`,芯片侧换成 **RAM 假芯片**(32 块 × 64 页 × 2048 B,两段式读缓存 + NOR 叠加编程 + 双编程违规计数 + 访问次数记账),149 断言覆盖 is_free 整页扫描、六入口越界守卫(`-1` + `DHARA_E_CORRUPT_MAP` + 零芯片访问 / 零标坏块)、故障注入与 dhara map"写入 → 重新 resume"场景;**内联一份"只看页首 64 B"的旧算法做对照**,证明断言能咬人。挂 pre-commit 钩子 `nand-port-unit-test`(第 16 个主机侧单测,钩子总数 33→34)。§4.1 登记运行器、§4.2.1 台账新增该模块并给"其余驱动不适格"补例外说明(驱动不适格 ≠ 驱动之上的胶水层不适格) |
|
||||
| 2026-10-12 | **§9 瘦身:机器侧内容迁出**(远程 CI 文档独立成 `docs/CI/`)。§9.2 两个 job 的**步骤级细节**并入 `docs/CI/远程CI完整指南.md` §5.5、§9.4 部署前置表并入其 §0.2;本文 §9 只保留**测试分层视角**的内容(CI 覆盖哪些层、两个 job 各守哪层、触发与 paths-ignore 语义、本地等价 `ci_local.py`、"CI 不替代本地门禁")。§9.1 两个 🔴 块压缩为结论 + 指针(机制根因与排障口径归 `docs/CI/`)。相关文档表两行 CI 条目合并为一行指向 `docs/CI/README.md` |
|
||||
| 2026-10-10 | §4.2.1 台账:`Drivers/BSP/NETCFG/net_cfg_legacy.c` 由 `⬜ 候选` 改为 **`⛔ 已删除`**——旧档 NCFG 迁移整体移除(`net_cfg_legacy.{c,h}` 删除、`cfg_store_legacy_path()` 撤销;旧档 `/cfg/netcfg.bin` 不读、不删、不迁移,配置回落工厂默认),该测试候选随之撤销 |
|
||||
| 2026-10-02 | **S6**:`test/run_all_suites.py` 新增 **`--check-build`**(只编译不烧录,纯离机,不需要 MCU/串口/ST-LINK);新增 **§3.6.1** 说明其解决的问题(测试构建无独立门禁,2026-08~09 CH395F 套件实例)、判定语义与用法。**实测 4 套件全部 `BUILD_OK: 0 errors, 0 warnings`**(即 T-4 提到的"CH395F 不可编译"已修复) |
|
||||
|
||||
@@ -397,7 +397,7 @@ docs\内存管理\RAM使用分析与优化方案.md:585 [路径引用] docs/内
|
||||
|
||||
| 主题 | 去处 |
|
||||
|---|---|
|
||||
| 提交门禁 30 个钩子 | `docs/代码规范/pre-commit使用说明.md` |
|
||||
| 提交门禁 34 个钩子 | `docs/代码规范/pre-commit使用说明.md` |
|
||||
| 自动化测试与台架流程 | `docs/测试说明/自动化测试流程.md` |
|
||||
| 文档写作规范(编码/链接/登记) | `docs/文档规范.md` |
|
||||
| 工具路径环境变量(换机) | `docs/环境与部署/本地工具路径环境变量方案.md` |
|
||||
|
||||
@@ -231,3 +231,70 @@ if (ok) {
|
||||
- 坏块表这类**持久化状态**,扫描失败时必须"放弃持久化"而非"照常保存"——
|
||||
保护既有好副本比刷新更重要;
|
||||
- 使能类配置(ECC)必须**回读校验**,不能只信写命令的返回。
|
||||
|
||||
## Trap 07 — dhara 移植层:`is_free` 只看页首 + HAL 无几何校验(已修复,2026-10-15)
|
||||
|
||||
**发现时间**:2026-10-15(`项目审查与优化建议报告.md` N-12 / N-13 复核,登记簿原 N-21)
|
||||
|
||||
**现象 / 机制**(两个独立缺陷,同一层):
|
||||
|
||||
1. `dhara_nand_is_free()` 只读页首 64 B 判全 0xFF。dhara journal 用它论证"检查点组尚未编程"
|
||||
(`journal.c:405-414` 的前提是**组内最后一页一旦编程必然含非 0xFF 字节**),而
|
||||
`cp_free`(`journal.c:422`)与"找下一空闲页"(`journal.c:520`)都直接信这个返回值。
|
||||
一个"页首恰好全 0xFF、后段有数据"的已编程页(刚格式化的卷、稀疏文件块、日志尾部都很常见)
|
||||
会被**误判空闲** → dhara 往已编程页**再编程一次** = 位碰撞,journal / map 指向垃圾,
|
||||
**且不报任何错**。
|
||||
2. 六个 HAL 入口(`is_bad` / `mark_bad` / `erase` / `prog` / `read` / `copy`)对页号、块号
|
||||
**零上界校验**。越界值(含 dhara 哨兵 `DHARA_PAGE_NONE` = 0xffffffff,`journal.h:46`)
|
||||
加完 `FTL_START_BLOCK` 偏移送进 SPI 命令后,被芯片地址位**截断成某个真实页**
|
||||
→ 静默读写、甚至把无辜页的数据 GC 搬走。
|
||||
|
||||
**与 Trap 03 的关系(本条的核心教训)**:Trap 03 已为**裸驱动字节偏移 API**
|
||||
(`gd5f2gq5ue_read/write/erase`)补过容量检查,但**dhara 移植层是另一个入口**,同一教训在那儿
|
||||
原样重演。边界校验必须**逐层**做——"下层查过了"推不出"上层调用也安全",何况本例下层查的是
|
||||
字节偏移、上层传的是页号,两者根本不是同一个量。
|
||||
|
||||
**修复(2026-10-15)**:
|
||||
|
||||
- `is_free` 改**整页扫描**(栈上 64 B 缓冲分块读满 2048 B;**页首命中即早退**,所以"已编程且
|
||||
页首有数据"的绝大多数页成本与旧实现同价,只有前缀全 0xFF 的页才多读几块)。
|
||||
- 新增 `ftl_page_in_range()` / `ftl_block_in_range()`(上界 = `n->num_blocks << n->log2_ppb`),
|
||||
六个入口统一守卫。
|
||||
- **错误码选择是刻意的**:越界返回 `DHARA_E_CORRUPT_MAP`。**不能**用 `DHARA_E_BAD_BLOCK`
|
||||
——`journal.c:904 recover_from` 只对该码执行"标坏块 + 跳块",会把无辜好块标掉;
|
||||
**不能**用 `DHARA_E_RECOVER`——`map.c:275 try_recover` 只对该码启动回收。其余错误一律
|
||||
原样上抛,不会静默清 map(E-31 教训)。
|
||||
- 无错误通道的两个函数取**保守值**:`is_free` 越界返回 0(判非空,最多少用一页)、
|
||||
`is_bad` 越界返回 1(判坏,绝不读写)、`mark_bad` 越界直接返回(绝不标无辜好块)。
|
||||
- `is_free` 内 `gd5f_page_read` / `gd5f_read_from_cache` 任一失败 → 返回 0
|
||||
(Trap 06 同族:读失败**不得**伪装成"空闲")。
|
||||
|
||||
**验证**:
|
||||
|
||||
- `@build` 0 错 0 警;`@test` PASS,且**台架 journal 恢复点与修复前同轨**——连续四次启动
|
||||
`sectors used` 恒 14,094、`Map head` 每轮 +128 单调推进(37,472 → 37,600 → 37,728 → 37,856)、
|
||||
`epoch` 稳定 3 → 整页 `is_free` 未改变恢复语义、无双编程、无回退。
|
||||
- 主机侧移植层单测 `test/host/nand_port/`(RAM 假芯片 + "旧算法只看页首 64 B"**对照用例**,
|
||||
将来有人把 `is_free` 改回去会立刻红)。
|
||||
|
||||
**经验教训**:
|
||||
|
||||
- **移植层的"廉价近似"会被上游当成精确契约**:dhara 已在注释里论证 `is_free` 必须精确,端口却
|
||||
按"页首大概会有数据"实现。契约类回调不许采样式实现——要嘛精确,要嘛按保守值失败。
|
||||
- 擦除态检测(判"空闲")的正确做法是扫全页;性能用**早退快路径**去拿,而不是靠缩小扫描范围赌。
|
||||
- 哨兵值(`DHARA_PAGE_NONE`)在 C 里就是个普通整数,**下层不校验就一定会被当地址用**。
|
||||
|
||||
**遗留与代价(2026-10-15 主机侧单测落地时复核提出,未修)**:
|
||||
|
||||
1. **`is_free` 读失败返 0 的方向性代价**:比"误判空闲 → 再编程"安全(dhara 会去读 checkpage,
|
||||
被 magic / CRC 门挡住),但一次**瞬态 SPI 毛刺**就可能让整次 mount 失败——旧实现反而会被
|
||||
"页首恰好读到 0xFF"的快路径掩盖过去。这是**刻意选择**:宁可挂载失败,不可误判空闲
|
||||
(E-31 纪律:绝不拿销毁数据换可用性)。排障时记住"挂载失败 + 有 SPI 错误"先看这里。
|
||||
2. **空白卷首启成本**:dhara resume 扫到的每个检查点页都是全 0xFF → 每页要 32 次列读
|
||||
(旧实现 1 次)。量级仍小(O(log N) × ppc),但**首次挂载(新格式化卷)会明显变慢**。
|
||||
台架若观察到该现象,根因在此,**不是回归**。
|
||||
3. **守卫上界依赖硬编码 `log2_ppb = 6`**:`ftl_page_in_range()` 的上界 =
|
||||
`n->num_blocks << n->log2_ppb`,而这两个 `log2_*` 在 `disk_initialize` 里是字面量。
|
||||
将来换 128 页/块的芯片,守卫会**静默算错**(偏大 = 越界守卫失效,正是它要防的那类)。
|
||||
→ 待办:加编译期断言 `GD5F_PAGES_PER_BLOCK == (1u << 6)`(本仓 `gd5f2gq5ue.h` 已有
|
||||
"负数组 typedef"式契约断言先例,照抄即可)。
|
||||
|
||||
+30
-30
@@ -25,27 +25,29 @@
|
||||
|------|------|-----------|-----------------|
|
||||
| 项目结构 | **A−** | 六层分层真实可执行、命名一致、门禁到位;反向依赖控制在 4 处 | `NETCFG`/`UI` 语义层错位(S-1)、4 处裸 `extern`(S-2)、`rs485_task.c` 仍在注册白名单里等 B2 |
|
||||
| 文档分布 | **A−** | 工程文档 **87 篇 / 30,781 行**(2026-10-15 随 `错误码传播审计.md` 删除而 −1 篇 / −635 行),`docs/README.md` 索引 + `docs/文档规范.md` + 三档一致性门禁全部落地,`check_doc_links --all` = 0 失效 | **D-1**:`docs/` 占仓库 **71.8%**、手册图片 **36.3 MB**、`.git` 已 **235 MB**(已决策暂不处理,登记观察) |
|
||||
| 模块设计 | **A−** | 看门狗(IWDG + 心跳位图 + 4 个 Fault 现场 + `.noinit`)、就绪屏障、卷锁、FTP 鉴权、`Error_Handler` 自恢复、ADC 全链均已落地 | **N-12/N-13** dhara 移植两处缺陷(数据丢失风险)、**N-16** 日志逐条开关、驱动层无统一错误码约定(S-5)、RS485 无协议栈(随 B2 暂缓) |
|
||||
| 内存使用 | **B+** | 堆迁 CCM + 栈/堆三面观测常开 + `mem-budget` 92% 门禁 + 预算台账与趋势基线;**R-9 已落地(2026-10-15)→ SRAM1 由 74.9% 降到 56.1%(余 57,504 B),不再是卡点** | **内存优化簇已整簇暂缓**(用户裁定 2026-10-15):R-2 后半(待迁 29,570 B → CCM,只差缩堆 56→32 KB)与 R-3 / R-4 / R-6 不再排期,理由与重开条件见 §5.3;**当前真正的约束是 CCM 只剩 7,904 B**——加任务 / 抬栈 / 加堆会先在这里撞墙,届时按 §5.3 重开;MicroLIB 512 B C 堆仍无机械检查(§5.5) |
|
||||
| Flash 使用 | **A−** | F-1/F-2/F-3 落地省 64,576 B → 占用 **22.0%(231,084 B)** | F-4/F-5/F-6 ≈10 KB + 编译时长(§5.4) |
|
||||
| 模块设计 | **A−** | 看门狗(IWDG + 心跳位图 + 4 个 Fault 现场 + `.noinit`)、就绪屏障、卷锁、FTP 鉴权、`Error_Handler` 自恢复、ADC 全链均已落地;**原列的 dhara 移植层两处缺陷(N-12/N-13)与日志逐条开关(N-16)已于 2026-10-15 修复** → `GD5F2GQ5UE_Trap_Records.md` Trap 07、`../架构设计/STM32F4-Base存储架构说明.md` §5.3、`../子系统设计/运行与自检记录设计说明.md` §4.4/§4.5 | 驱动层无统一错误码约定(S-5)、RS485 无协议栈(随 B2 暂缓) |
|
||||
| 内存使用 | **B+** | 堆迁 CCM + 栈/堆三面观测常开 + `mem-budget` 92% 门禁 + 预算台账与趋势基线;**R-9 已落地(2026-10-15)→ SRAM1 由 74.9% 降到 56.1%;同日移植层与日志修复 +1.6 KB → 现 **57.4%(余 55,840 B)**,不再是卡点** | **内存优化簇已整簇暂缓**(用户裁定 2026-10-15):R-2 后半(待迁 29,570 B → CCM,只差缩堆 56→32 KB)与 R-3 / R-4 / R-6 不再排期,理由与重开条件见 §5.3;**当前真正的约束是 CCM 只剩 7,904 B**——加任务 / 抬栈 / 加堆会先在这里撞墙,届时按 §5.3 重开;MicroLIB 512 B C 堆仍无机械检查(§5.5) |
|
||||
| Flash 使用 | **A−** | F-1/F-2/F-3 落地省 64,576 B → 占用 **22.2%(232,280 B)** | F-4/F-5/F-6 ≈10 KB + 编译时长(§5.4) |
|
||||
| UI 标准化 | **B** | golden 回归已建(`simulator/tests/` 四脚本,含池水位门禁) | 无令牌层 / 无组件库 / 无页注册表(手写 `case` 仍在)、**27 页**未落地、golden 与字符集差集未挂门禁(§6) |
|
||||
|
||||
### 0.2 未闭环问题(Top 2,2026-10-15)
|
||||
### 0.2 未闭环问题(2026-10-15 复核)
|
||||
|
||||
| # | 编号 | 问题 | 影响 | 现状 |
|
||||
|---|------|------|------|------|
|
||||
| 1 | **N-12 / N-13**(登记簿 **N-21**,W2) | `dhara_nand_is_free()` 只查页首 64 B(`nand_ftl.c:152-179`);`dhara_nand_copy()` 不处理 `DHARA_NO_PAGE` 哨兵(`:204-216`) | 映射恢复误判 → **数据丢失风险**(低概率、后果重);哨兵页号被当真实页 → 未定义行为 | ❌ 两处原样;至今只能靠人工读代码发现(主机侧无 dhara 移植层测试) |
|
||||
| 2 | **N-16**(登记簿 **N-24**,W4) | 日志每条记录一次 `f_open + f_write + f_close`(`sys_log_mcu.c:124-141`) | 每条日志 2–4 次 NAND 页读写;UI 翻记录逐条 open/seek/close | ❌ 未办(仅 `FIL` static 化省 528 B 栈) |
|
||||
> ✅ **原 Top 2 已全部修复并销项(2026-10-15)**:
|
||||
> ① **N-12 / N-13**(登记簿 N-21,W2)——`dhara_nand_is_free()` 改整页精确扫描、HAL 六个入口加几何上界守卫;
|
||||
> ② **N-16**(登记簿 N-24,W4)——日志写侧一次排空合并成"每文件一次 open/write/close"、读侧加 8 条滑动窗口。
|
||||
> 实施记录与硬约束 → `../架构设计/STM32F4-Base存储架构说明.md` §5.3(移植层两条硬约束)、`../子系统设计/运行与自检记录设计说明.md` §4.4 / §4.5(写批量化 + 读窗口);验证链见文末变更记录(含台架 journal 恢复点同轨对照)。
|
||||
|
||||
剩余未闭环项**均非紧急级**(W4 结构债 / 评估簇 / 已裁定暂缓的内存优化簇),逐条见 §0.4;推进顺序的唯一权威仍是 `../任务规划/README.md` §2。
|
||||
|
||||
⚠ 另有两条**不在上表但需知晓**:`.git` 已膨胀到 **235 MB**(D-1,§3.1,2026-10-11 决策"暂不处理、登记观察");`rs485_task.c` 仍不在两端构建清单里——**这是 E-59 门禁下的刻意缺口**(随 B2 暂缓),已登记在 `check_registration.py` 的 `KNOWN_GAPS`,不是漏注册。
|
||||
|
||||
⏸ **内存优化簇已整簇暂缓**(用户裁定 2026-10-15,原 Top 2 的 R-2 剩余已移出):R-2 后半(把 LVGL 池 + UI 两份缓冲 ≈29.6 KB 搬进 CCM)与 R-3 / R-4 / R-6 不再排期。**已落地的 R-9 与治理面保留常开**(`mem-budget` 92% 门禁、预算台账、趋势基线、栈/堆观测)。理由与重开条件 → §5.3;登记簿 N-27 已标暂缓。
|
||||
⏸ **内存优化簇已整簇暂缓**(用户裁定 2026-10-15,未闭环清单里的 R-2 剩余已移出):R-2 后半(把 LVGL 池 + UI 两份缓冲 ≈29.6 KB 搬进 CCM)与 R-3 / R-4 / R-6 不再排期。**已落地的 R-9 与治理面保留常开**(`mem-budget` 92% 门禁、预算台账、趋势基线、栈/堆观测)。理由与重开条件 → §5.3;登记簿 N-27 已标暂缓。
|
||||
|
||||
### 0.3 两条主线建议
|
||||
|
||||
> **推进顺序的唯一权威是 [`../任务规划/README.md`](../任务规划/README.md) §2**;本节只是体系评审视角的两条结构性建议,不另立顺序。
|
||||
|
||||
1. **内存这条线到此为止**(用户裁定 2026-10-15,**覆盖本报告的原建议**):R-9 已把 LVGL 池 40→16 KB、SRAM1 降到 **56.1%(余 57,504 B)**,够用了。原建议的"缩堆腾 CCM → 把 LVGL 池与 UI 帧缓冲(现 ≈29.6 KB)搬进 CCM 作为后续功能预算来源"**整簇暂缓,不再作为主线**——没有功能在等这块预算,而搬迁要动 scatter、要逐条过 DMA 可达性清单、缩堆还得上板复测,收益不抵风险与工时。**保留的是治理面**(92% 门禁 + 台账 + 趋势 + 栈/堆观测):它会在真正吃紧时自动报警,届时再按 §5.3 的重开条件评估,而不是现在预先腾预算。
|
||||
1. **内存这条线到此为止**(用户裁定 2026-10-15,**覆盖本报告的原建议**):R-9 已把 LVGL 池 40→16 KB、SRAM1 降到 **57.4%(余 55,840 B)**,够用了。原建议的"缩堆腾 CCM → 把 LVGL 池与 UI 帧缓冲(现 ≈29.6 KB)搬进 CCM 作为后续功能预算来源"**整簇暂缓,不再作为主线**——没有功能在等这块预算,而搬迁要动 scatter、要逐条过 DMA 可达性清单、缩堆还得上板复测,收益不抵风险与工时。**保留的是治理面**(92% 门禁 + 台账 + 趋势 + 栈/堆观测):它会在真正吃紧时自动报警,届时再按 §5.3 的重开条件评估,而不是现在预先腾预算。
|
||||
2. **把 UI 从"每页手写"变"声明 + 原型库"**:令牌层 → 原型库 → 生成器补全注册 → golden 入门禁,使新增一页的成本从 8 处手工改动降到 1 处声明(§6)。
|
||||
|
||||
---
|
||||
@@ -57,7 +59,6 @@
|
||||
|
||||
| 类别 | 编号 | 未闭环内容 | 下一步 |
|
||||
|------|------|-----------|--------|
|
||||
| **数据正确性** | **N-12 / N-13** ❌(N-21,W2) | `is_free` 只查页首 64 B;`copy` 不处理 `DHARA_NO_PAGE` | 整页(或首/中/尾)扫描 + 返回 `DHARA_E_UNSUPPORTED` + **必须配主机侧移植层单测** |
|
||||
| 内存 RAM | **R-2 剩余** ⏸ **暂缓**(N-27,原 W3) | 待迁体量已由 54,146 B 降到 **29,570 B**(R-9 落地后);CCM 只剩 7,904 B | **用户裁定 2026-10-15:暂不做**——没有功能在等这块预算;重开条件 → §5.3 |
|
||||
| 内存 RAM | R-3 / R-4 / R-6 ⏸ **暂缓**(N-27,原 W3) | 页缓冲未合并 / UI 帧 4 副本 / 栈仍动态 | **同上裁定,暂不做**。本簇已落地的只有 R-9(2026-10-15,SRAM1 74.9%→56.1%,见 §5.3);**R-5 已判定不可行**(§5.3) |
|
||||
| 内存 RAM | N-14 🟡(N-22,W4) | 写回缓存无空闲/掉电强制 flush(整页写与换页已 flush) | 空闲 N ms 由 defaultTask flush |
|
||||
@@ -108,8 +109,8 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
| 术语 | 定义 |
|
||||
|------|------|
|
||||
| 自研代码 | `App/`、`Drivers/BSP/`(不含 `Lib/`、`Middlewares/`、HAL/CMSIS)、`test/`;不含 CubeMX 生成的 `Src/`、`Inc/` |
|
||||
| Flash 占用 | `Code + RO-data` = `ER_IROM1` **231,084 B(22.0% of 1 MB)**;`ROM` 再加 `RW-data` 初始化表 = 232,196 B |
|
||||
| RAM 占用 | `RW-data + ZI-data`,**跨两个区域** = SRAM1 `RW_IRAM1` **73,568 / 131,072(56.1%)** + CCM `RW_CCM` **57,376 / 65,280(87.9%)** + `UNINIT_CCM` 48 B(`.noinit` 崩溃现场保留区)。⚠ 原报告"CCM 不在 scatter 内计 0"的口径已不成立 |
|
||||
| Flash 占用 | `Code + RO-data` = `ER_IROM1` **232,280 B(22.2% of 1 MB)**;`ROM` 再加 `RW-data` 初始化表 = 233,416 B |
|
||||
| RAM 占用 | `RW-data + ZI-data`,**跨两个区域** = SRAM1 `RW_IRAM1` **75,232 / 131,072(57.4%)** + CCM `RW_CCM` **57,376 / 65,280(87.9%)** + `UNINIT_CCM` 48 B(`.noinit` 崩溃现场保留区)。⚠ 原报告"CCM 不在 scatter 内计 0"的口径已不成立 |
|
||||
| "死代码" | 该对象在镜像中有码,但其功能被工程配置(如 `LV_COLOR_DEPTH 1`)或调用点扫描(零调用者)证明不可达 |
|
||||
|
||||
> **内存数字的权威源**:区域水位与子系统配额由 `../内存管理/RAM预算台账.md`(受管区,`ram_baseline.py --write` 生成)持有,趋势在 `scripts/ram_baseline.tsv`。本报告只在支撑结论处引用,不与之竞争事实源。
|
||||
@@ -272,8 +273,6 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
|
||||
| 编号 | 问题 | 证据 | 影响/建议 |
|
||||
|------|------|------|-----------|
|
||||
| **N-12** | `dhara_nand_is_free()` 只检查页首 64 B | `nand_ftl.c:152-179`(仍 `uint8_t buf[64]`) | dhara 依赖该判定做映射恢复;页首 64 B 全 FF 但后半页有数据时会误判为空闲 → 数据丢失风险(低概率但后果重)。建议整页扫描或至少首/中/尾三处采样 |
|
||||
| **N-13** | `dhara_nand_copy()` 未处理 `DHARA_NO_PAGE` 语义(dst 为哨兵值时仍执行"读+编程") | `nand_ftl.c:204-216`(read→prog 直传) | dhara map 在回收/擦除路径会用 `DHARA_NO_PAGE` 试探;当前实现会把它当作真实页号 → 未定义行为。建议:`if (dst == DHARA_NO_PAGE) { dhara_set_error(err, DHARA_E_UNSUPPORTED); return -1; }` |
|
||||
| N-14 | 写回缓存 `s_cache_buf/s_cache_dirty` 无掉电保护策略:`CTRL_SYNC` 已实现但**没有空闲时强制 flush** | `nand_ftl.c:418` | 掉电窗口 = 最后一次未跨页的写。已改善:整页写立即 flush、换页时 flush、`CTRL_SYNC` 走 flush + `dhara_map_sync`;仍无空闲定时 flush → 建议 defaultTask 空闲 N ms flush |
|
||||
| N-15 | 页缓冲重复:`nand_ftl` 3×2,048(`s_page_buf/s_cache_buf/s_copy_buf`) | 对象 ZI:nand_ftl 6,216 | 与 `s_copy_buf` 复用可省 2 KB(§5.3 R-3)。🟡 `gd5f` 的 `s_bbt_page_buf` 已由 2,048 B 收紧为 272 B |
|
||||
| — | `disk_ioctl GET_BLOCK_SIZE = 1` | `nand_ftl.c:442-444` | FatFs 的擦除块提示失效(FAT32 对齐优化拿不到信息);建议返回 64 页(=1 物理块) |
|
||||
@@ -296,7 +295,6 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
|
||||
| 编号 | 问题 | 证据 | 建议 |
|
||||
|------|------|------|------|
|
||||
| **N-16** | **每条记录一次 `f_open + f_write + f_close`**,读侧同样每条 `f_open/f_lseek/f_close` | `sys_log_mcu.c:124-141`(失败重建重试一次)、`wdev.log` 同模式 `:486-489` | 写侧:常驻 `FIL`(仅 defaultTask 持有)+ 批量 flush;读侧:打开一次 + 缓存目录偏移(UI 已有 `s_cache_valid/s_last_rec` 雏形,可升级为页缓存)。🟡 唯一改善 = `FIL` 已 static 化(省 528 B 栈) |
|
||||
| P2-10 | 容量轮转 `f_unlink` 与 UI 读并发 | | 轮转只在写者、读者持版本号;卷锁覆盖面评估 → 登记簿 S-5 |
|
||||
|
||||
### 4.6 UI 子系统
|
||||
@@ -316,7 +314,7 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
|
||||
### 4.8 测试体系
|
||||
|
||||
**设计良好**:`test/board/test_config.h` 的 `TEST_SUITE_*` 互斥(`#error` 保护)、CH395F 硬件层/NET 层 Phase 互斥、`@test` 一键闭环(烧录→wait-idle→标记校验)、模拟器 `--cfgtest/--logtest`、主机侧单测 15 个钩子(含 `sha256` / `ftp_auth` / `vol_lock` / 采集算法族)。
|
||||
**设计良好**:`test/board/test_config.h` 的 `TEST_SUITE_*` 互斥(`#error` 保护)、CH395F 硬件层/NET 层 Phase 互斥、`@test` 一键闭环(烧录→wait-idle→标记校验)、模拟器 `--cfgtest/--logtest`、主机侧单测 16 个钩子(含 `sha256` / `ftp_auth` / `vol_lock` / 采集算法族 / **`nand_port` dhara 移植层**)。
|
||||
|
||||
> ⚠ 仍缺:**覆盖率概念**、**主机侧对 `dhara` 移植层的测试**(N-12/N-13 这类缺陷至今只能靠人工读代码发现)。
|
||||
|
||||
@@ -334,13 +332,13 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
|
||||
| 指标 | 数值 | 上限 | 占用 |
|
||||
|------|------|------|------|
|
||||
| `Code + RO-data`(`ER_IROM1`) | 231,084 B | 1 MB Flash | **22.0%** |
|
||||
| `ROM`(`Code + RO + RW` 初始化表) | 232,196 B | 1 MB | 22.1% |
|
||||
| **SRAM1(0x20000000,128 KB)** `RW_IRAM1` | **73,568 B** | 131,072 B | **56.1%(余 57,504 B)** |
|
||||
| `Code + RO-data`(`ER_IROM1`) | 232,280 B | 1 MB Flash | **22.2%** |
|
||||
| `ROM`(`Code + RO + RW` 初始化表) | 233,416 B | 1 MB | 22.3% |
|
||||
| **SRAM1(0x20000000,128 KB)** `RW_IRAM1` | **75,232 B** | 131,072 B | **57.4%(余 55,840 B)** |
|
||||
| **CCM(0x10000000,64 KB)** `RW_CCM` | **57,376 B** | 65,280 B | **87.9%(余 7,904 B)** |
|
||||
| `UNINIT_CCM`(`.noinit` 崩溃现场) | 48 B | 256 B | 18.8% |
|
||||
|
||||
> **R-9 已落地(2026-10-15)**:`LV_MEM_SIZE` 40→16 KB → SRAM1 由 98,144 B / **74.9%** 降到 **73,568 B / 56.1%**(−24,576 B,与池缩减量逐字节吻合)。SRAM1 首次从"最紧的一块"变成**余 57.5 KB**;**当前瓶颈换到 CCM(87.9%,只剩 7,904 B)**。⏸ 该簇后续动作(缩堆 → 迁 CCM)已按用户裁定 2026-10-15 **整簇暂缓**,见 §5.3。
|
||||
> **R-9 已落地(2026-10-15)**:`LV_MEM_SIZE` 40→16 KB → SRAM1 由 98,144 B / **74.9%** 降到 **73,568 B / 56.1%**(−24,576 B,与池缩减量逐字节吻合)。SRAM1 首次从"最紧的一块"变成**余 57.5 KB**(现 75,232 B / 57.4%、余 55,840 B,增量来自同日移植层守卫与日志缓冲 +1,664 B);**当前瓶颈换到 CCM(87.9%,只剩 7,904 B)**。⏸ 该簇后续动作(缩堆 → 迁 CCM)已按用户裁定 2026-10-15 **整簇暂缓**,见 §5.3。
|
||||
|
||||
> **权威源**:区域水位与子系统配额 → `../内存管理/RAM预算台账.md`(受管区);趋势 → `scripts/ram_baseline.tsv`(`ram_baseline.py --trend` 高亮 RW+ZI 增长 >1 KB 的对象)。本节只列支撑结论所需的数。
|
||||
|
||||
@@ -375,13 +373,13 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
| 其余(`App/` 应用与驱动自研、startup、C 库、填充) | ≈45,497 | ≈19.7% |
|
||||
| **合计** | **231,084** | 100% |
|
||||
|
||||
> 归类按 `.map` 组件表对象名前缀,合计与区域总量可差几十字节(链接器填充)。前 7 行逐字节与 2026-10-14 那次构建相同;并行未提交改动(`cfg_file` / `sd2506`)的 +48 B 落在"其余"。
|
||||
> 归类按 `.map` 组件表对象名前缀,合计与区域总量可差几十字节(链接器填充)。前 7 行逐字节与 2026-10-14 那次构建相同;并行未提交改动(`cfg_file` / `sd2506`)的 +48 B 落在"其余"。⚠ **本表是那次构建的构成快照**(合计 231,084 B);当前镜像 **232,280 B / 22.2%**,增量 +1,192 B `Code` 来自同日 dhara 移植层守卫与日志批量化(登记簿原 N-21 / N-24),**不改变"LVGL 是最大单一子系统"的结论**。
|
||||
|
||||
**要点**:LVGL 的绝对量自 F-1/F-2/F-3 裁剪后**未再增长**(87,706 B),但仍是镜像最大单一子系统 → §5.4 的剩余裁剪(≈10 KB)与 F-5 的编译时长收益依旧成立。
|
||||
|
||||
### 5.2 结构性诊断(三条)
|
||||
|
||||
1. **瓶颈已从 SRAM1 换到 CCM**:R-9 落地后 SRAM1 只剩 56.1%(**余 57,504 B**),而 **CCM 被堆占掉 87.9%(只剩 7,904 B)**,待迁的 LVGL 池 + UI 两份缓冲 = **29,570 B**。
|
||||
1. **瓶颈已从 SRAM1 换到 CCM**:R-9 落地后 SRAM1 只剩 56.1%(**现 57.4%、余 55,840 B**),而 **CCM 被堆占掉 87.9%(只剩 7,904 B)**,待迁的 LVGL 池 + UI 两份缓冲 = **29,570 B**。
|
||||
→ R-2 的剩余部分**只差"缩堆"这一步**(56→32 KB,`../内存管理/RAM使用分析与优化方案.md` §2.2;⚠ 2026-10-12 B5 是反向扩容,缩回前必须重跑 `stack_report.py` + 上板 `min_ever` 复测);顺序不可颠倒(`../内存管理/RAM预算台账.md` §3)。
|
||||
⏸ **本簇已按用户裁定 2026-10-15 整簇暂缓**(§5.3)——这条诊断保留作将来重开时的现状基线,**不代表现在要动**。
|
||||
2. **任务栈全部动态取自 FreeRTOS 堆**:`osThreadAttr_t` 只设 `stack_size` 不设 `stack_mem`(`Src/freertos.c`)→ 8 条产品任务栈合计 **9,792 words = 39,168 B**(另 testTask 2,048 words 仅测试构建)连同 idle/timer 栈、TCB、IPC 全部从堆里扣,**留给动态分配的实际余量很紧**(堆 56 KB 是 2026-10-12 B5 扩容后的值)。
|
||||
@@ -392,7 +390,7 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
### 5.3 RAM 优化路线(收益/风险/优先级)
|
||||
|
||||
> ⏸ **本表除已落地的 R-9 外整簇暂缓(用户裁定 2026-10-15)**,理由三条:
|
||||
> ① **没有功能在等这块预算**——SRAM1 已降到 56.1%(余 57,504 B)、Flash 22.0%,离 `mem-budget` 的 92% 红线很远;
|
||||
> ① **没有功能在等这块预算**——SRAM1 已降到 57.4%(余 55,840 B)、Flash 22.2%,离 `mem-budget` 的 92% 红线很远;
|
||||
> ② **搬迁本身有风险和工时**——R-2 要动 scatter 并逐条过 DMA 可达性清单(规则 R2/R3,违例是**静默传错**不是报错),R-4 要重排 sink 与 LVGL 刷新的生命周期,R-6 静态化后栈溢出更难发现,缩堆还得上板复测 `min_ever`;
|
||||
> ③ **收益与代价不对称**——腾出的 CCM 是给"以后可能有的功能",代价是现在动存储与显示两条主链。
|
||||
>
|
||||
@@ -419,7 +417,7 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
|
||||
|
||||
### 5.4 Flash 优化(未闭环:F-4 / F-5 / F-6)
|
||||
|
||||
> **F-1 / F-2 / F-3 已落地并销项(2026-10-11)**:两份 `lv_conf.h` 对齐为同一特性子集 → **实测省 64,576 B**,Flash 26.3% → 20.1%(现 22.0%,增量来自 B4/B5 功能代码)。完整记录见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 与 `../UI设计/UI_设计架构.md` §1.4 第 10 条。
|
||||
> **F-1 / F-2 / F-3 已落地并销项(2026-10-11)**:两份 `lv_conf.h` 对齐为同一特性子集 → **实测省 64,576 B**,Flash 26.3% → 20.1%(现 22.2%,增量来自 B4/B5 功能代码)。完整记录见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 与 `../UI设计/UI_设计架构.md` §1.4 第 10 条。
|
||||
|
||||
| 编号 | 措施 | 可省 Flash | 依据 | 状态 |
|
||||
|------|------|-----------|------|------|
|
||||
@@ -550,7 +548,7 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea
|
||||
|------|------|
|
||||
| 页面**不得**新增静态大缓冲;文本缓冲统一用 `UI_BUF_STRLEN` | 现有 `s_ts/s_desc` 已是范式 |
|
||||
| 每页**只用** `lv_style_const_prop_t`,禁止运行期 `lv_style_init` | 已验证动态样式每次泄漏 88 B |
|
||||
| 优先自绘(`lv_draw_*` + `lv_text_get_width`),新控件需评审(会破坏 §5.4 F-2 **已落地**的裁剪,且须两端同步) | LVGL 现占镜像 **38.0%**(87,706 / 231,084 B) |
|
||||
| 优先自绘(`lv_draw_*` + `lv_text_get_width`),新控件需评审(会破坏 §5.4 F-2 **已落地**的裁剪,且须两端同步) | LVGL 现占镜像 **37.8%**(87,706 / 232,280 B) |
|
||||
| 帧缓冲任何改动必须遵守 R-4(副本数 ≤ 2) | 当前 4 副本 12.8 KB |
|
||||
| `LVGL 池`用量必须经 `lv_mem_monitor` / 池水位门禁实测后才允许扩大 | **16 KB**(R-9 于 2026-10-15 由 40 KB 降下;实测峰值 6,568 B = 池的 40%,余 ≈9.8 KB) |
|
||||
|
||||
@@ -570,13 +568,12 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea
|
||||
|
||||
## 7. 统一改进路线图
|
||||
|
||||
> **顺序的唯一权威 = [`../任务规划/README.md`](../任务规划/README.md) §2**(W1 采集保护收尾 → W2 数据正确性 → ~~W3 预算与内存~~ **⏸ 已整簇暂缓** → W4 结构债 → W5 验证常态 → W6 UI 扩展 → W7 仓库决策;B2 RS-485 暂缓)。本节只保留"本报告条目 → 波次"的映射,不另立顺序。
|
||||
> **顺序的唯一权威 = [`../任务规划/README.md`](../任务规划/README.md) §2**(W1 采集保护收尾 → ~~W2 数据正确性~~ **✅ 已清空销项 2026-10-15** → ~~W3 预算与内存~~ **⏸ 已整簇暂缓** → W4 结构债 → W5 验证常态 → W6 UI 扩展 → W7 仓库决策;B2 RS-485 暂缓)。本节只保留"本报告条目 → 波次"的映射,不另立顺序。
|
||||
|
||||
| 波次 | 本报告条目 | 验收要点 |
|
||||
|------|-----------|---------|
|
||||
| **W2 数据正确性** | N-12 / N-13(登记簿 N-21) | 整页扫描 + `DHARA_E_UNSUPPORTED` + **主机侧 dhara 移植层单测**(一并补覆盖率缺口) |
|
||||
| ~~**W3 预算与内存**~~ ⏸ | **整簇暂缓(用户裁定 2026-10-15)**:R-9 ✅ 已落地不回退;R-2 后半 / R-3 / R-4 / R-6 不再排期 | 暂缓期间**无验收动作**;`mem-budget` 92% 门禁与台账、趋势、`stack_report.py` / `ram_report.py` 观测**照常跑**(它们是判断何时重开的输入)→ 重开条件见 §5.3 |
|
||||
| **W4 结构债** | N-16、N-14、F-4/F-5/F-6、S-1/S-2/S-4/S-5;`rs485_task.c` 随 B2 出注册白名单 | `--strict` 默认化;`bsp_err_t` 进规范 |
|
||||
| **W4 结构债** | N-14、F-4/F-5/F-6、S-1/S-2/S-4/S-5;`rs485_task.c` 随 B2 出注册白名单 | `--strict` 默认化;`bsp_err_t` 进规范 |
|
||||
| **W5 验证常态** | P1-10 收尾(构建实测链接 + `@test` 闭环) | 逢窗口顺手做 |
|
||||
| **W6 UI 扩展** | U4 残余 → U1 → U2/U3 → U5(27 页) | 新增一页 ≤30 行;**本轮不排期** |
|
||||
| **W7 仓库决策** | D-1(登记簿 N-29) | **已决策暂不处理**;推送/协作前重评 |
|
||||
@@ -598,7 +595,7 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea
|
||||
|
||||
| 结论 | 证据(2026-10-15 `.map`) |
|
||||
|------|---------------------|
|
||||
| SRAM1 56.1%、余 57,504 B;CCM 87.9%、余 7,904 B | map `Execution Region RW_IRAM1 Size: 0x00011f60, Max: 0x00020000` / `RW_CCM Size: 0x0000e020, Max: 0x0000ff00`;`ram_baseline.tsv` 末行 |
|
||||
| SRAM1 57.4%、余 55,840 B;CCM 87.9%、余 7,904 B | map `Execution Region RW_IRAM1 Size: 0x00012600, Max: 0x00020000` / `RW_CCM Size: 0x0000e020, Max: 0x0000ff00`;`ram_baseline.tsv` 末行 |
|
||||
| MicroLIB C 堆 = 512 B | `MDK-ARM/startup_stm32f407xx.s` `Heap_Size EQU 0x200`;map `Removing startup_stm32f407xx.o(HEAP), (512 bytes)`(未被引用 → 整段移除) |
|
||||
| 任务栈全部动态分配 | `Src/freertos.c` 的 `osThreadAttr_t` 无 `.stack_mem`;8 条产品任务栈合计 9,792 words(R-6 未办) |
|
||||
| DMA 可达性三层防线(原 N-11 的处置) | 编译期窗口断言 `gd5f2gq5ue.h:96-107`(负数组 typedef)→ 启动期自检 `nand_ftl.c:241-243` → 运行时守卫 `gd5f_dma_buf_ok()`(`gd5f2gq5ue.c:121-139`);规则族 R1~R4 → `../内存管理/内存内容分配策略.md` §4;负路径用例 TC-GD5F-704/705 |
|
||||
@@ -644,6 +641,7 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea
|
||||
| **2026-10-15** | **修 `netcfg_save` 假失败:回归改为自洽运行** | 上一条那个"唯一差异"查到真因,**不是代码回归**:`--cfgtest` 的 E-01 场景 2 伪造一份 4000 B 纯注释配置档并断言"拒写后原文件不动",**连通过路径也不删除它**;留在工作目录后每次运行都被加载 → 外科手术保存要在 4000 B 注释后补写受管段 → 输出必超 `CFG_FILE_MAX_LEN` → 引擎**按设计拒写** → 保存行显示 `ERR`。修三处:`simulator/main.c` 给 `--cfgtest` 加外壳(无论成败一律删除伪造档)、`check_menu_rows.py` 与 `check_content_pages.py` 改在**空临时目录**里跑(`cwd=tmp`)、脚本里已作废的 `sim_netcfg.bin` 兼容清理改为只提示不删除。修后 **11/11 页 + 往返池水位 5,448 B 全过,未刷任何黄金参考**(参考本来就是对的)。排查口径 → `../UI设计/UI_自动化测试流程.md` §五 |
|
||||
| **2026-10-15** | **补漏:§0.1 六维评分与口径/附录数字追平 R-9 后状态** | 前面那条只改了 §0.2/§0.3/§0.4/§5.x,**漏掉 §0.1、§1 术语表、附录 A 证据表**(用户指出):① 内存使用行仍写"54,146 B 仍在 SRAM1 / SRAM1 74.9% / 须先 R-9"→ 改为 SRAM1 **56.1%(余 57,504 B)**、瓶颈收敛到 CCM 单点、只差"缩堆"一步,评级 **B → B+**(理由:SRAM1 由卡点变富裕,治理三件套与观测齐备;扣分项换成 CCM 单点 + C 堆无机械检查)② Flash `231,036` → **`231,084`**(§0.1 / §1 术语表 / §6.4 三处),并修掉术语表里 `ROM = 231,324 B` 这个**算术错**(`Code + RO + RW` 实为 **232,196 B**)③ RAM 占用定义 `98,144 / 74.9%` → **`73,568 / 56.1%`** ④ 附录 A 证据行改 `RW_IRAM1 Size: 0x00011f60`、余量 57,504 B ⑤ 文档分布 **88 篇 / 31,416 行 → 87 篇 / 30,781 行**、§3.1 体量表受控文件 **2,466 / 79.3 MB → 2,465 / 79.2 MB**(均随 `错误码传播审计.md` 删除,−1 篇 / −635 行;同表 `docs/` **56.9 MB**、手册图片 **703 张 / 36.3 MB** 复核未变)⑥ §0.1/§0.2/§0.4 与附录的复核日期统一标 **2026-10-15**,最近复核行的 `HEAD` 改指 `a7f5265` 并注明"后续文档与模拟器侧提交不影响固件",避免每提交一次就过期 |
|
||||
| **2026-10-15** | **裁定:内存优化簇整簇暂缓** | 用户判定"把 LVGL 池与 UI 帧缓冲(现 ≈29.6 KB)搬进 CCM 作为后续功能预算来源"这个决定不合适,内存优化的事先放一放。据此:§0.2 **Top 3 → Top 2**(R-2 剩余移出名列,表下加一条 ⏸ 说明);§0.3 主线一由"把内存从卡死变富裕"改写为"**这条线到此为止**";§0.4 两条内存条目 🟡/❌ → **⏸ 暂缓**;§5.1 结论段与 §5.2 诊断 1 补"不代表现在要动";**§5.3 加暂缓横幅**(三条理由 + 保留常开的治理面 + 三条重开条件),R-2 / R-3 / R-4 / R-6 优先级列一律 ~~P1/P2~~ → **⏸ 暂缓**。**已落地的 R-9 不回退**(−24,576 B 是净收益且已台架验收);`mem-budget` 92% 门禁、预算台账、趋势基线、栈/堆观测**全部保留常开**——它们是传感器,不是优化动作。同步 `../任务规划/登记簿.md` N-27、`../任务规划/README.md` W3、`../内存管理/RAM预算台账.md` §3、`../内存管理/RAM使用分析与优化方案.md` 阶段 1.2 / 2 |
|
||||
| **2026-10-15** | **执行本报告条目:N-12 / N-13 / N-16 落地并销项** | **N-12**:`dhara_nand_is_free()` 改整页扫描(栈上 64 B 分块、页首命中即早退,成本与旧实现同价)。**N-13:本报告原建议经核对不成立,已按实际契约改修**——本仓裁剪版 dhara 既没有 `DHARA_NO_PAGE`(哨兵实为 `DHARA_PAGE_NONE`,`journal.h:46`)也没有 `DHARA_E_UNSUPPORTED`(`error.h:20-31` 枚举里没有,照抄直接编译失败),且 `map.c` 三处 `dhara_journal_copy` 调用点各自已挡掉哨兵(`map.c:260`、`:434`+`:440`、`:242`);真风险是**移植层零上界校验**(越界页号被地址位截断成真实页 → 静默读写错页),故改为六个 HAL 入口统一几何上界守卫 + `DHARA_E_CORRUPT_MAP`(不能用 `BAD_BLOCK`:`journal.c:904` 会据此标坏块;不能用 `RECOVER`:`map.c:275` 会据此启动回收)。**N-16**:写侧一次排空合并成"每文件一次 open/write/close"(≤8 条 = 512 B 静态缓冲;**掉电语义不变**——批次仍在同一次 drain 内 close,close 回写目录项这个不同 LPN 会逼出 FTL 单页写回缓存;失败尾部回投队列,条目不丢);读侧 8 条滑动窗口 + 写者代数失效(API 与 UI 零改动)。**刻意不批量**:`sys_log_evidence()` 的 `wdev.log` 仍逐条落盘——取证路径要求立刻进 NAND 并靠 flush 诱导逼出脏页。**验证**:`@build` 0 错 0 警(Code 212,344→**213,536**、ZI 129,880→**131,520**,+1,640 B 全为静态缓冲,不吃栈);`@test` PASS 且**台架 journal 恢复点与修复前同轨**(`sectors used` 恒 14,094、`Map head` 每轮 +128 单调推进、`epoch` 稳定 3 → 整页 `is_free` 未改变恢复语义、无双编程);`stack_report.py` 9 个任务全 PASS(defaultTask 静态最坏链 3,024 B / 余 40%,实测峰值 2,164 B / 余 57%);新增主机侧移植层单测 `test/host/nand_port/`(假芯片 + 旧算法对照用例)。实施记录 → `../架构设计/STM32F4-Base存储架构说明.md` §5.3、`../子系统设计/运行与自检记录设计说明.md` §4.4/§4.5 |
|
||||
| **2026-10-14** | **清理:删除已完成与过时内容** | ① **已修复项按销项删除**(去处见下表):N-02(FTP 鉴权)、N-03c(`Error_Handler` 降级)、**N-11(DMA 可达性三层防线 + 规则族 R1~R4)**、S-3(注册反向校验)、P2-5/P2-6(SD2506 负温度与 WRTC)、P2-8(`NET_RECV_TIMEOUT_MS` 已删)、P2-9(`mkdir` 幂等重试)、§5.5 前三条(内存治理三件套)、交付物清单已建项。② **删除过时层**:2026-09-16 / 2026-10-11 双列基线表与"复核期间未提交改动"口径说明、旧"组合结果预估"表、逐条"2026-10-11 复核"批注、历史变更记录行——内存数字改由 `../内存管理/RAM预算台账.md` 与 `scripts/ram_baseline.tsv` 持有。③ **按 2026-10-14 `.map` 与代码重测**全部保留结论的数字(含 `lftpd_log.c` 已删除并入 `lftpd_auth.c`,N-06 残余只剩 `rs485_task.c` 一条刻意缺口)。④ **修正三处错误**:R-5(`configMAX_PRIORITIES` 56→32)**判定不可行**(CMSIS-RTOS2 `#error`);原 §7 把报告 S-5(驱动统一错误码)与登记簿 S-5(卷锁覆盖面)混为一谈;原 §5.5 第 4 条建议的 `_Static_assert` 形态在 AC5 上取不到链接期地址,已被"窗口断言 + 启动自检 + 运行时守卫"三层取代。⑤ 报告自有编号**不回改、不重排**(`../编号规范.md`) |
|
||||
|
||||
### 已销项条目的记录去处
|
||||
@@ -671,3 +669,5 @@ const ui_page_desc_t ui_page_deadzone = UI_FORM_PAGE("遥测死区", s_items_dea
|
||||
| §5.5 前三条(92% 门禁 / 趋势基线 / 预算台账,登记簿 N-27 前半) | `../内存管理/RAM预算台账.md`(阈值与配额的**唯一权威**)+ `scripts/ram_baseline.py`(`--gate/--update/--trend/--write`)+ `../代码规范/pre-commit使用说明.md`(`mem-budget`) |
|
||||
| F-1 / F-2 / F-3(LVGL 死代码裁剪) | `../内存管理/RAM使用分析与优化方案.md` §2.6.4;`../UI设计/UI_设计架构.md` §1.4 第 10 条(**两份 `lv_conf.h` 必须同一特性子集**);`../UI设计/UI_自动化测试流程.md` 变更记录(池水位基线) |
|
||||
| **R-9(`LV_MEM_SIZE` 40→16 KB,登记簿 N-27 原 W3 的第一步,2026-10-15 已执行;该簇其余部分同日整簇暂缓)** | 取值依据与再降 8 KB 的前置写在 `Lib/lv_conf.h` 与 `simulator/lv_conf.h` 的池注释;实测水位 → `../内存管理/RAM使用分析与优化方案.md` §2.6.2 与 §阶段 1.1(已执行);解锁链第 ① 步 ✅(后续步骤暂缓)→ `../内存管理/RAM预算台账.md` §3;池容量口径 → `../UI设计/UI_设计架构.md` §1.2、`../UI设计/UI_界面制作流程.md` C4 |
|
||||
| **N-12 / N-13**(dhara 移植层 `is_free` 精度 + HAL 越界守卫,登记簿 **N-21**,2026-10-15 已销项) | `../架构设计/STM32F4-Base存储架构说明.md` §5.3(移植层两条硬约束:整页精确的原因链 + 错误码取舍与"为什么不会静默清卷")+ `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` 的 `ftl_page_in_range` / `ftl_block_in_range` / `dhara_nand_is_free` 注释 + 主机侧单测 `test/host/nand_port/`(含"旧算法只看页首 64 B"的对照用例,防止将来被改回去) |
|
||||
| **N-16**(日志读写放大,登记簿 **N-24**,2026-10-15 已销项) | `../子系统设计/运行与自检记录设计说明.md` §4.4(写侧批量合并 + **掉电语义不变**的论证 + 失败回投不丢条目 + 容量轮转只在慢路径)与 §4.5(读侧 8 条窗口 + 写者代数失效 + API 不变)+ `App/log/sys_log_mcu.c` 的 `s_format_rec` / `s_append_rec` / `s_append_many` / `s_read_raw` 注释 |
|
||||
|
||||
Reference in New Issue
Block a user