@@ -1,700 +0,0 @@
# STM32F4-Base 项目审查与优化建议报告
> 版本 **v3.0** 2026-10-22( v2.x 2026-09-11 起多次增量修订)
>
> **本版是全量重写**:按 2026-10-22 的仓库重新取数审计,已闭环条目、失效数字与已完成的规划正文(六维
> 评分旧表、UI 8 人日方案正文、波次交付物清单)整段删除。**刻意保留三层**:结论层(§0~§7)、**编号族
> 登记处(§9, `check_ids.py` 的属主登记处,删了就断门禁)**、变更记录与销项去处(文末,按项目纪律冻结)。
>
> ⚠ 待办**顺序的唯一权威** = [`../任务规划/README.md`](../任务规划/README.md) §2;本文只留"评审快照 +
> 证据 + 为什么这样判",不另立排期,避免两份清单漂开(本版重写就是因为旧 §0.4 与登记簿漂开了)。
## 0. 执行摘要
### 0.1 六维评分(2026-10-22 复评)
| 维度 | 评级 | 今天的实测依据 | 相比 2026-10-15 的变化 |
|---|---|---|---|
| 结构与分层 | 🟢 良好 | 单向依赖未被破坏;双端注册反向校验 PASS;326 个编译单元全部可追溯 | 无退化 |
| 文档与治理 | 🟢 良好 | 39 个提交钩子 + 16 套主机单测;工程 md 91 份;`doc-links` 全库 0 问题 | 新增 `编号门禁增量拦截` 与内存快照回填常态化 |
| 内存与 Flash | 🟡 可控但已换位置 | SRAM1 **57.4% ** (余 55,808 B)、Flash **22.1% ** 、**CCM 87.9%(余 7,904 B) ** | **红线从 SRAM1 迁到 CCM ** (堆 2026-10-11 迁 CCM + 池 40→16 KB 后 SRAM1 已宽松) |
| 可靠性与自恢复 | 🟢 良好 | 4 个 Fault 全采集现场;IWDG 五面 `required_mask` ;复位原因与 `[STACK]` 水位常开 | 台架复跑持续 PASS(今日真机 708 ms 起网、无 `[ERR]` /Fault) |
| 测试与验证 | 🟡 半办 | 16 套主机单测 + `ui-golden` /`font-charset` 入门禁;TC 双向追溯 593∶271 交集 265; * * §5.5 两条补强已全部落地**(2026-10-23:覆盖率口径 `../测试说明/主机单测覆盖率口径.md` + `malloc(` 正则 `coding-standard` CODE-RULE-20) | 覆盖率**未挂 pre-commit**(口径文档 §8 三条理由);**分支覆盖已裁定不做** (§2.1 决策记录)→ 现在真正的半办是**覆盖面**:首登照出 **125 个生产源文件从未进过任何主机单测 ** |
| UI 标准化 | 🟢 基本落地 | `ui_tokens.h` ( L0 令牌)+ `ui_page_registry.c` (描述符注册表)+ 7 个内容页 + 两枚 UI 钩子 | **U1/U3/U4 从"未做"变为已落地 ** ; U2 只有 `ui_w_records` 一个组件 |
### 0.3 主线建议(2026-10-22)
1. **最大功能缺口仍是 RS-485 采集未接线 ** (B2 按用户决策冻结);接线前必须先过 E-59 门禁——
`rs485.c` 仍有 3 处 `HAL_Delay` ( E-49 超时拉 DE、E-50 单缓冲撕裂未修)。
2. **CCM 是当下唯一预算红线 ** (余 7,904 B)。加任务 / 抬栈 / 加堆都会先在这里撞墙;`mem-budget`
92% 钩子会先报警。SRAM1 与 Flash 都已不是卡点。
3. **结构性内存优化整簇暂缓 ** ( R-2 后半 / R-3 / R-4 / R-6,用户裁定 2026-10-15),治理面常开;
重开条件见 §5.3。今天又收口一项:**F-4 判定不做、F-6 已落地、F-5 降级观察**(§5.4)。
4. **补面两条已全部落地(2026-10-23) ** :覆盖率口径(`../测试说明/主机单测覆盖率口径.md` ) +
堆禁令机器拦截(`coding-standard` **CODE-RULE-20 ** ,§5.5 第 5 条)。**剩下的量在覆盖面**:覆盖率首登
照出 **47 个 `.c`( 9,657 有效代码行)+ 78 个 `.h` 从未进过任何主机单测编译单元 ** ( `net_socket.c` 1,251 行最大)。
建桩比把在测的 19 个文件从 88% 推到 95% 值钱得多,但**不是 125 个都该做**:优先级与"刻意不做"的清单已按逐文件
画像写好 → `../测试说明/主机单测覆盖率口径.md` §9( P1 13 个 / 2,617 行、P2 5 个 / 2,226 行、P3 29 个 / 4,814 行不做)。
5. **仓库体积重心在外部资料 ** ( `docs/芯片手册` 717 文件 / 39.5 MB,占 docs 的 87%; `.git` 248 MB),
见 §3.3 D-1 与登记簿 N-29——已决策暂不处理,但**推送/对外协作前必须重评**。
## 0.4 未闭环清单(2026-10-22 复评)
> 只列**仍未闭环**项,已修项按「修复即销项」删除(去处见文末)。逐项细节以
> [`../任务规划/登记簿.md`](../任务规划/登记簿.md) 为准,本表是评审快照(图例:❌ 未办 / 🟡 半办 /
> ⏸ 暂缓 / 🟡 观察)。
| 类别 | 编号(登记簿) | 今天的现状 | 下一步 |
|---|---|---|---|
| 功能缺口 | RS-485 采集(B2) | `rs485.c` 3 处 `HAL_Delay` 未修 → E-59 挡住 `rs485_task` | 待用户排期;先修 E-49/E-50 再谈接线 |
| 硬件改版 | **N-18 ** | V0.1 把 8 路电气量分给两片转换器(无共同采样触发,跨转换器相位/功率不可用) | V0.2 全接 TPAFE; V0.1 上禁做矢量运算 |
| 内存 RAM | R-2 剩余 / R-3 / R-4 / R-6 ⏸(N-27) | 待迁体量 29,570 B,而 CCM 只剩 7,904 B → 搬迁已无空间收益 | 暂缓;重开条件 §5.3 |
| 内存 RAM | N-14 🟡(N-22) | FTL 单页写回缓存无空闲/掉电强制 flush(整页写与换页已 flush, `f_close` 经 `CTRL_SYNC` 已落盘) | 用户裁定 2026-10-22 暂不做;残余窗口只是"单条记录" |
| Flash | **F-5 🟡 观察项 ** ( N-28) | uvprojx 201 个 LVGL `.c` 里 **127 个空 TU ** ;增量构建 4~5 s、CI 不跑 `@build` | 有实测全量耗时痛点再做,用 `<IncludeInBuild>` |
| 结构 | **S-1 已止住恶化 ** ( 2026-10-22 上门禁) | 不是"命名错位",是真实的 **BSP→App 反向 include ** ,精确口径 **8 个文件 9 处 ** (上一版写"7 文件 15 处"也不准,已按 `check_layer_deps.py` 判定重算):`cfg_file.h` × 2( NETCFG)、`vol_lock.h` × 1( lftpd_io)、`sys_log.h` × 3( UI 两处 + `ui_w_records.h` );`console_log.h` / `sys_clock.h` / `sha256.h` 三个横切叶子头已**显式豁免**。**运行时的属主纪律本来就没破**(UI 只投递 `net_cfg_save_request()` ,写盘在 defaultTask),要修的只是静态方向 | **门禁已落地 ** ( `layer-deps` + `scripts/check_layer_deps.py` :存量 6 条基线只减不增、新增当场 FAIL,落地时做过故意违规自证)。剩下的才是反转:把 `cfg_file_*` 直接调用换成注入的 ops(NETCFG 已有 `net_cfg_set_save_req_cb` 先例);**目录搬迁仍暂缓**(远程 CI 不跑 `@build` ,兜底未成立) |
| ~~结构 S-2~~ ✅ **已闭环销项 2026-10-22 ** | 该修的 24 处手写跨模块裸 `extern` **全部消完 ** : 2 处与 `Inc/spi.h` /`Inc/i2c.h` 重复 → 改 include;19 处任务句柄与属性 → `App/task/task_handles.h` ; 1 处 `main.c` 函数内 `g_rcc_csr_latched` → `App/app/app_main.h` ;
`ch395f.c:177` 的 `g_spi1_dma_done` → 标志收回 GD5F 内部 `static` ,改由 `gd5f_spi1_notify_done()` / `notify_error()` 契约转调;最后一处 `net_socket.c:77` 的
`netMsgQueueHandle` → 句柄改为 net_socket 私有 `static s_net_msg_queue` ,由 `Src/freertos.c` 的 `USER CODE BEGIN RTOS_QUEUES` 区在队列创建后立刻
`net_msg_queue_adopt()` 绑定(创建点不能外移——`osKernelInitialize()` 之前的创建类 API 会杀死启动;**故意不新建第二个队列**:那会白占 CCM 里 8×`net_msg_t`
的堆空间,而 CCM 正是当前红线)。**两类从来不算债**:`stm32f4xx_it.c:241-256` 是 CubeMX「External variables」生成块;`:183/195/207/220` 是 `__asm` Handler
里的汇编标签引用(ARMCC `__asm` 取不到 C 数据符号)。 | 无(后续由 `layer-deps` 与 `cubemx-survival` 两条门禁守着不回退:新增 `net-msgq-adopt` 存活项) |
| 驱动规范 | **S-5 🟡 主体已落地 ** ( 2026-10-23:契约 + 门禁 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码) | 我原先写的"无统一错误码"**不准**,实测是 **六套并存 ** : 0/− 1( SD2506、NETCFG、nand_ftl)、自研枚举 `CH395F_STATUS_*` 、**芯片寄存器原生码** `CH395F_ERR_*` (与前一套同模块并存)、`HAL_StatusTypeDef` ( `rs485.h` 曾直接暴露)、`bool` ,外加第三方 dhara 出参 `dhara_error_t` 与 FatFs `FRESULT` 。超时侧是**同形两义**: `SD2506_I2C_TIMEOUT_MS` 带单位后缀而 `GD5F_SPI_TIMEOUT 100` 不带(**逐行核过:它也是毫秒**,我先前记成"自旋圈数"是错的)、`GD5F/TPAFE5160_BUSY_TIMEOUT = -2` 其实是**返回码**; `osWaitForever` 与 `HAL_MAX_DELAY` **同为 0xFFFFFFFFU ** (值同域不同,跨层转手不报错也不变慢)。真正值钱的不是码名,是"**能不能重试**"以前只写在个别注释里 | `Drivers/BSP/bsp_err.h` ( header-only,含 `BSP_ERR_CAN_RETRY` ) + `bsp-err` 钩子(**四条检查**: `err-type` / `timeout-name` / `return-doc` / `consume-result` ;基线 19 → 1 → **38 ** ,其中 `consume-result` 上线又暴露 37 条存量丢弃);已收编 **RS485( HAL 枚举不再外泄)/ SD2506( 11 个公共 API, I2C 超时终于归成可重试)/ TPAFE5160(含 E-44 的 FRSTDATA → `BSP_ERR_NO_DEVICE`) ** , GD5F 的 `GD5F_BUSY_TIMEOUT` 改名 `GD5F_WAIT_EXPIRED` 消掉同形两义,FTL 坍缩点按 **ECC / 预算耗尽 / 完整性 ** 三类就地分类并打可读日志(§10.4)。`net_*` BSD 层与 `tpafe5160_snapshot()` 三态按裁定**不纳入**。**已修第一处静默降级**( 2026-10-23):`gd5f2gq5ue_bbt_rebuild()` 的 ECC 关闭/恢复不再裸丢弃,并补 E-10 同款**回读
B0h 验 `ECC_EN` **——原先那两行的注释自己写着"否则后续 I/O 在无 ECC 下运行",风险却正落在它下面那行。
**待还 ** :GD5F 私有码集整体收编(169 处、含板级套件 48 处,需真机 + 套件回归)、C4 失败类快照(让卷
健康探针按类分支)、`bool ui_content_page_open()` 一条(需改签名 + 处理 3 个调用点,与还注释不是一档事)、
**37 条丢弃点的收编 ** (已入基线只减不增)、**"host 侧失败也标坏块"的策略修正**(`dhara_nand_erase` /`dhara_nand_prog`
把 host 侧 SPI 失败与 DMA 等待超时也标成坏块,而 dhara 只在 `DHARA_E_BAD_BLOCK` 才 retire;改它得先定
"host 侧失败给哪个码",见变更记录) |
| 网络 | N-09 / PHY ❌(N-25) | `NET_MAX_SOCKETS` 仍为 8(与单连接复用语义不符);PHY 强制 100M 无自协商回退;PASV 每次新建监听 | 见 §4.2;随 N-25 一并处置 |
| 存储 | N-15 🟡 **观察项 ** ( N-25) | `nand_ftl.o` 静态 6,216 B = 三块 2 KB 页缓冲 6,144 B + dhara 状态 72 B; **已论证三块不可合并**——GC 撞上脏页回写时三块同时持有活数据,合并后的失败形态是静默错数据 | **判定不做 ** ( 2026-10-23):可削减的只有 `s_copy_buf` (−1,536 / − 2,048 B),而收益落点 `RW_IRAM1` 余 55,808 B、红线在 CCM → 论证见 `../内存管理/存储栈RAM优化调研.md` §5 |
| 存储/日志 | P2-10 ❌(N-25) | 卷锁现有 5 个使用者(配置原子写 / 日志 / SOE / lftpd_io / defaultTask),容量轮转删除与 UI 读并发仍未全覆盖 | 登记簿 S-5 面(卷锁覆盖面) |
| 仓库 | D-1 ❌(N-29) | jpg 707 个 / 37.3 MB; `docs` 45.3 MB 里芯片手册占 39.5 MB; `.git` **248 MB ** | 降采样 / 拆 submodule / LFS 三选一 + 历史重写;已决策暂不做 |
| UI | U2 残余(U 簇) | 组件库只有 `ui_w_records.c/h` 一个;其余仍是页内自绘 | 按产品需求决定要不要继续抽组件 |
| ~~测试~~ ✅ **两条全部闭环 2026-10-23 ** (覆盖率口径 + 堆禁令 CODE-RULE-20) | 覆盖率:`scripts/check_test_coverage.py` + 基线 `scripts/test_coverage_baseline.json` (口径与**盲区**见 `../测试说明/主机单测覆盖率口径.md` )——首登 16 套并集 2552/2893 行(88.2%)、**未触及 125 个文件 = 47 个 `.c` ( 9,657 行)+ 78 个 `.h` (2,182 行,多为声明/宏)**,
| 待办池是那 47 个 `.c` ;分批与「刻意不做」见 `../测试说明/主机单测覆盖率口径.md` §9。堆禁令:`check_coding_standard.py` **CODE-RULE-20 ** (硬拦截、零基线;唯一豁免 = `simulator/sim/cfg_file_store_sim.c`
| 的平台分配后端宿主侧实现,见 `../门禁说明/coding-standard.md` §7) | 无(残余三项**已归口或已裁定**:覆盖率挂
| pre-commit 的前置写在口径文档 §8;分支覆盖**不做**(§2.1 决策);扩大覆盖面按 §0.3 第 4 条) |
| CI | T-7 ❌ | 远程 CI 只跑本机一台 runner,且不跑 `@build` | 保持现状即可;扩机器前重读 `docs/CI/README.md` |
## 1. 审查方法与证据基线
### 1.1 数据源(本版全部结论的出处)
- 体量:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map` ( 2026-10-22 `@build` )→ `data/ram_report_20261022.md`
- 栈:`STM32F407-Demo.htm` (静态调用图)+ 同日真机 `[STACK]` 心跳 → `data/stack_report_20261022.md`
- 仓库:`git ls-files` 逐个 `stat` (必须 `-z` ,否则中文名被控制台代码页破坏)
- 门禁:`.pre-commit-config.yaml` ( 39 个 `- id:` ) + 各钩子今日实跑输出
- 待办:`docs/任务规划/登记簿.md` (在策 18 条)与 `docs/任务规划/README.md` §2
### 1.2 复现命令(本报告所有数字可重跑)
``` powershell
# 1) 三区水位 + Top-10 RW+ZI
& '.venv\Scripts\python.exe' scripts / ram_report . py MDK-ARM / STM32F407-Demo / STM32F407-Demo . map -o docs / 内存管理 / data / ram_report_ < 日期 > . md
# 2) 每任务栈:静态最坏链 × 真机 [STACK] 水位
& '.venv\Scripts\python.exe' scripts / stack_report . py -i MDK-ARM / STM32F407-Demo / STM32F407-Demo . htm -s . dsh / skills / serial-assistant / scripts / state / serial . log -o docs / 内存管理 / data / stack_report_ < 日期 > . md
# 3) MicroLIB C 堆(应为 0x200 = 512 B)
& 'tools\grep_binaries\find.exe' -E 'Heap_Size +EQU' startup_stm32f407xx . s # 或直接 git grep Heap_Size -- Src
# 4) 仓库体量:逐文件字节(-z 防中文名破坏)
git ls-files -z | xargs -0 stat -c '%s'
# 5) 门禁全量自查(不含 @build)
& '.venv\Scripts\python.exe' scripts / ci_local . py
```
### 1.3 口径定义
| 名词 | 口径 |
|---|---|
| Flash 占用 | `Code + RO-data` = `ER_IROM1` = **232,536 B( 22.2%) ** ; `ROM 合计` 再加 `RW` 初始化表 = 233,724 B( 22.3%) |
| SRAM 占用 | `RW_IRAM1` ( DMA 可达区)与 `RW_CCM` (仅 CPU)分开计,二者不可互换(规则 R2/R3) |
| 最坏栈 | `max(armlink 静态调用图深度, 真机 [STACK] 高水位)` ;判定线 `余量 ≥ max(1024 B ISR, 配置×30%)` |
| 编译单元 | uvprojx 的 `<FilePath>` 计数(今日 **326 个 `.c` ** ,其中 LVGL 201);"自研 `.c` " 另按目录口径 114 个 |
| 工程文档 | `docs/` 的 `.md` 105 份,其中 `docs/芯片手册` 14 份为外部资料 → 工程侧 **91 份 ** |
## 2. 项目结构合理性
### 2.1 规模与分布(2026-10-22 实测)
| 顶层 | 入库文件 | 字节 | 说明 |
|---|---|---|---|
| `docs/` | 881 | 45.3 MB | **全仓最大目录 ** ,其中 `芯片手册` 717 文件 / 39.5 MB(外部资料,见 D-1) |
| `Lib/` | 1,036 | 21.3 MB | lvgl + FatFs + dhara(只读,本地补丁须文档登记) |
| `Drivers/` | 192 | 8.9 MB | 手写 BSP(含 `UI` 42 文件、`NET` 、`CH395F` 、`GD5F2GQ5UE` ) |
| `App/` | 74 | 629 KB | 应用层 L1~L3 + 任务编排 + 配置/日志/卷锁 |
| 全仓 | **2,498 ** | **80.0 MB ** | `.git` 248 MB( D-1/N-29 的关注点在这里) |
### 2.2 分层与依赖方向
单向依赖(业务 → UI/基础服务 → 中间件 → BSP → HAL/RTOS)在今天的抽查里**未被破坏**:算法落
`App/algo/` ( L1 `adc_input` /`adc_health` 、L2 `sample_ring` /`phasor` 、L2.5 `freq` 、L3 `prot_set` /`prot_elm` ),
任务侧只做消费与编排。三处"属主唯一"约束仍是硬规矩:日志唯一写者 = defaultTask、配置引擎属主 =
defaultTask、`net_*` 只在 netTask 串行执行。
### 2.3 双构建清单对齐
`registration-check` (反向校验 + UI 页面描述符接线)今日实跑 PASS:uvprojx 与 `simulator/CMakeLists.txt
` 无单侧缺项。**新增信息**: uvprojx 里 201 个 LVGL `.c` 有 **127 个在 `.map` 中零产出 ** (空 TU),
它不影响镜像只影响全量重编 → 判为观察项(§5.4 F-5),不是缺陷。
### 2.4 根目录卫生
根目录 37 项。本机产物均**未入库**且已被忽略:`sim_config.ini` 、`sim_run.log` 、`sim_selftest.log` 、
`ci-artifacts/` 、`.workbuddy-ai/` 、`tmp_save.bin` (由 `*.bin` 规则命中,属**顺带**忽略而非显式声明——
改名即失守,建议显式登记)。入库的两个 `ui_cn_chars.txt` / `ui_menu_tree.txt` 是字模提取与菜单树清单,
与生成器一一对应,可接受。**没有发现散落的临时脚本或裸备份文件。**
### 2.5 结构结论
结构本身不是当前风险源;风险集中在 §0.4 的语义层错位(S-1/S-2/S-4/S-5)与体积面(D-1)。
## 3. 文档分布合理性
### 3.1 体量画像(2026-10-22)
- `docs/` = 881 文件 / 45.3 MB;其中 `docs/芯片手册` **717 文件 / 39.5 MB( 87%) ** , `.jpg` 全库 707 张 / 37.3 MB
- 工程 md **91 ** 份(外部资料 md 14 份,最大单份 `STM32F4xx参考手册_V18英文版_1-1000.md` = 40,239 行)
- `docs/images` 23 文件 / 2.0 MB——**全部由 `tools/arch_diagram/gen_*.py` 生成**,脚本与产物一并入库
### 3.2 已制度化、应保留的做法
- 数字同源:内存快照由 `check_mem_numbers.py --write` 写受管区,人只写判读文字
- 章节同源:任务表由 `gen_task_table.py` 生成;配图由生成器出;引用图由 `tools/refgraph` 出
- 引用可校验:`doc-links --sections-strict` (今日全库 146 文档 0 问题,章节引用 2,513 处、可判定 59%)
- 编号可校验:`id-check` 增量拦截(存量 650 指纹豁免,可判定 **82.98% ** ≥ 基线 82.6%)
### 3.3 问题清单
#### D-1(P1)外部资料与工程文档同仓同树(登记簿 N-29)
今天实测:`docs/芯片手册` 717 文件 / **39.5 MB ** (占 docs 87%),全仓 80 MB 的一半;`.git` **248 MB ** 。
影响:clone / 备份 / worktree 开空间 / 对外交付的体积都被外部资料决定,且历史里删不掉。
三个选项:① 图片降采样(一次性、可保留可读性)② 拆 submodule(干净但要改所有引用路径)
③ Git LFS(要服务端配合)。任一条都要配一次 `git filter-repo` 历史重写才真正减小 `.git` 。
**当前状态:已决策暂不处理 ** (本机单 runner、内网协作,痛点未爆发);**触发重评的条件** = 对外交付 /
新机器 clone 明显变慢 / 需要双 remote。
### 3.4 文档结论
文档制度是本项目最强的一面,但**同一事实的多份副本仍会漂**——本版重写正是这件事的第三次实证
(旧 §0.4 未闭环清单与登记簿漂开)。因此本版把"待办清单"降级为快照 + 指向,**权威只留登记簿**。
同类教训同日还有两例:`项目部署环境要求.md` 的任务栈副本表(已删表改指针)、
`RAM使用分析与优化方案.md` 的阶段子项与顶层章节同号撞面(已改层级编号 `4.阶段.项` )。
## 4. 功能模块设计评审(现状复核)
> 写法变更:本节不再重述设计(设计有专属文档),只记"今天复核到的状态 + 仍在册的问题 + 证据"。
### 4.1 启动与任务框架
9 任务、就绪屏障四位(`osFlagsNoClear` )、内核前模式均已固化在 `docs/架构设计/项目启动顺序管理.md` ;
`Src/freertos.c` 584 行(含 USER CODE 区手写 ISR 节拍)。原"仓库里没有 IWDG 驱动"等立项取证已随实施
失效,取证归 `docs/子系统设计/看门狗设计说明.md` 。
### 4.2 网络子系统(CH395F + BSD 抽象层)
`net_socket.c` 3,120 行 + 拆出的 `net_pending.c` (挂起 recv,真机专属);错误码经任务通知值回传;
单连接复用 + `auto_relisten` 语义已文档化。**仍在册**(N-25):`NET_MAX_SOCKETS = 8` 与单连接语义不符、
PHY 强制 100M 无自协商回退、PASV 每次新建监听。
### 4.3 存储子系统(GD5F + dhara + FatFs)
链路:GD5F SPI NAND → dhara FTL → FatFs → `/log` 、`/config.ini` 、FTP 根。**今天澄清的一处旧口径**:
`f_close → f_sync → sync_fs → disk_ioctl(CTRL_SYNC) → ftl_flush_cache()` 已把写回缓存落盘,
所以"必须写另一个文件才能逼出脏页"不成立(旧报告与源码注释都这么写,已改)。2026-10-02 那次
"复位后整段丢失"的根因是 dhara resume 锚错 checkpoint,不是缓存未刷 → `../架构设计/STM32F4-Base存储架构说明.md` §5.4.1。
残余风险 = 单次写事务中途掉电(登记簿 N-22,用户裁定暂不做)。
### 4.4 配置引擎(`App/cfg/cfg_file.c`, 1,358 行)
defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为后续项**:9,217 B 暂态峰值(整文件驻留),
流式读写的方案与验收在 `docs/子系统设计/配置文件设计说明.md` §6.2/§8。
### 4.5 日志子系统(`App/log/sys_log_mcu.c`, 751 行)
64 B 定长记录、双文件轮转、`[ERR]` 与 Fault 现场同通道。成本模型未变:**每条 `DBG_*` 约 90 B Flash +
216 B 栈**(调用点内联行缓冲),所以"补日志"要按调用点计费(§5.2)。
### 4.6 UI 子系统
今天的真实形态:`ui_tokens.h` ( 69 行,L0 令牌层)+ `ui_page_registry.c` (描述符注册表,取代手写 `case` )
+ 7 个内容页(`ui_page_ac/dc/device_info/runlog/selftest/net_cfg/password` ) + 组件仅 `ui_w_records` 。
回归面已进门禁:`ui-golden` ( `scripts/check_ui_golden.py` ,菜单 8 状态 + 内容页黄金参考)与
`font-charset` (字符集差集)。**新增一页的实际成本**:轻页 40~71 行(ac/dc/device_info/runlog/selftest),
重页 275~370 行(password/net_cfg 含表单交互)——旧报告"新增一页 ≤30 行"的验收目标**未达**,但接线
处数已从三处降到"页面文件 + 注册表一行"。
### 4.7 外设驱动
`ch395f.c` 1,777 行(SPI 分频必须 8, PCB 约束);TPAFE 走临界区原子快照(旧 `get_buf/data_ready` 已删);
片内 ADC1 上电自校准手写;`rs485.c` 仍有 3 处 `HAL_Delay` ( E-49/E-50)→ E-59 门禁仍在挡消费者接线。
### 4.8 测试体系
- 主机侧 **16 套**单测(提交门禁实跑);板级 `test/board/` 19 文件、套件开关互斥受 `test-suite-switch` 管
- TC 双向追溯:代码侧 593 号 / 文档侧 271 号 / 交集 265,差异 13 处(当前仅告警,未升门禁)
- **两处空白已全部闭合(2026-10-23)**:① 覆盖率口径 → `scripts/check_test_coverage.py` ( mscoverage 动态
插桩 + Cobertura 并集)+ 基线 `scripts/test_coverage_baseline.json` ;口径 / 工具链实测 / **九条盲区**见
`../测试说明/主机单测覆盖率口径.md` (首登 2552/2893 行、19 文件在测、**未触及 125 文件**)。
② C 库堆禁令 → `check_coding_standard.py` **CODE-RULE-20 ** (拦 `malloc/calloc/realloc/strdup/strndup/
free/aligned_alloc(` ;硬拦截、零基线;唯一豁免 = 平台分配后端的宿主侧实现
`simulator/sim/cfg_file_store_sim.c` ,见 `../门禁说明/coding-standard.md` §7)。禁令本身的事实源仍是
AGENTS.md 平台约束与 `../内存管理/RAM使用分析与优化方案.md` §4.2.1——**为什么非得机器拦**: MicroLIB
的 C 堆只有 512 B,而**模拟器(宿主 CRT 堆)完全正常、照不出来**——配置引擎 2026-09-18 的堆分配纠偏
( 4 处 KB 级 `malloc` 落错堆)与 lftpd 的 `malloc` 隐患都是**先上线才发现**的那类
- 破坏性套件(`ENABLE_DESTRUCTIVE_TESTS` )有**持久代价**(NAND 坏块记账不可逆),默认关闭是正确设计
## 5. 内存与 Flash( 2026-10-22 实测收口)
### 5.1 实测基线
| 指标 | 2026-10-23 | 2026-10-15 | 说明 |
|---|---|---|---|
| `Code` | 213,676 B | 213,520 B | 10-23 S-5 全波 +212:契约试点 +48、C3 SD2506 迁移 +44、C1 GD5F/TPAFE 收编 +36、C2 dhara 失败类 +84( RO 另 +208 = 失败类名表);ECC 回读校验(恢复构建路径)+28(见变更记录:该函数在产品镜像里被链接器 Removing,28 B 非它带来);10-22 当日累计:F-6 −332、SPI 回调 +16、消息队列 +32、FTP 日志级别 +28( RO 另 +72)、LIST 满块续发 +172 |
| `ER_IROM1` ( Code+RO) | **232,508 B / 22.2% ** | 232,280 B / 22.2% | 余 ≈803 KB; **RAM 三项 10-23 零增量** |
| `RW_IRAM1` | **75,264 B / 57.4% ** (余 55,808) | 73,568 B / 56.1% | 增量来自 B5/B8 功能码 |
| `RW_CCM` | **57,376 B / 87.9% ** (余 7,904) | 57,376 B / 87.9% | **当前唯一红线 ** |
| FreeRTOS 堆 | 56 KB( 57,344 B) @CCM ;实测 `free=15,800` / `min_ever=8,464` | 同 | 2026-10-22 真机 |
| 任务栈 | **9/9 PASS ** (最深 defaultTask 静态 3,024/5,120,余 40%) | 8/8 PASS | `data/stack_report_20261022.md` |
| LVGL 池 | 16 KB;实测峰值 6,568 B( 40%)、菜单基线 4,784 B | 同 | 2026-10-15 由 40 KB 收缩 |
### 5.2 结构性诊断(三条,仍然成立)
1. **静态大头是"缓冲常驻" ** : `lv_mem_core_builtin.o` 16,384 + `lftpd.o` 7,724 + `ui_net_sink.o` 6,746 +
`ui_port.o` 6,408 + `nand_ftl.o` 6,200 = 43,462 B,占 `RW_IRAM1` 的 58%(今日 Top-5 实测)。
2. **动态面只有一个 ** :所有任务栈 + TCB + 队列都在 `heap_4` 里,堆在 CCM → **栈的余量问题就是 CCM 的
余量问题**,这就是红线迁到 CCM 的机制原因。
3. **栈深的主因是日志与配置写链 ** ,不是算法本体:`s_append` 每调用点 216 B 栈 + 配置原子写链
( `cfg_store_write_atomic → f_rename` )把 defaultTask 推到 3,024 B。
### 5.3 RAM 优化路线(收益 / 风险 / 优先级)
| 项 | 原收益 | 今天判定 |
|---|---|---|
| **R-9 ** LVGL 池 40→16 KB | 24,576 B | ✅ 已落地 2026-10-15( SRAM1 74.9%→56.1%),不回退 |
| R-2 后半 待迁缓冲进 CCM | ≈29.6 KB | ⏸ **暂缓 ** ——CCM 只剩 7,904 B,搬不动也无收益 |
| R-3 页缓冲合并 / R-4 UI 帧副本 / R-6 栈静态化 | 中 | ⏸ 暂缓(N-27) |
| R-5 提高 `LV_MEM_SIZE` 上限换性能 | — | ❌ 已判定不可行(单色 160×160 用不到) |
> **重开条件(满足任一条才谈搬迁/缩堆)**:① 出现"必须新增 >8 KB RAM"的功能且产品要它;② `min_ever`
> 跌破 4 KB(碎片或峰值失控,此时应先削峰值而非搬家);③ CCM 撞墙导致新任务建不起来。
> 搬迁要动 scatter、逐条过 DMA 可达性清单(违例是**静默传错**不是报错),缩堆还得上板复测——收益不抵风险。
### 5.4 Flash 优化(2026-10-22 收口:F-6 已落地、F-4 判定不做、F-5 降级观察项)
> 动手前逐条对 `.map` 与源码复核,结论:**旧报告三项里两项建议不成立、一处归因错误**(字节数全对)。
| 项 | 原估收益 | 实测复核 | 状态与结论 |
|---|---|---|---|
| **F-4 ** 关未用绘制子模块 | ≈10 KB | 五项字节数逐字节仍准(10,060 B),但**无一项有开关**:`lv_draw_sw.c` 的 `execute_drawing()` 无条件 switch 引用全部 task 类型;唯一总开关 `LV_DRAW_SW_COMPLEX` 被主界面"浅 V"3 px 粗斜腿依赖(2026-09-03 已实测失败并两端固化 =1)。原"mask/grad 是文字必经路径"证伪:字形走 blend 层 `mask_buf` | ❌ **判定不做 ** (要打补丁才谈得上,上限 ≈16 KB 换 1.6% Flash,不值) |
| **F-5 ** 清 uvprojx 空条目 | 编译时长 − 40% | 201 个 LVGL `.c` / 127 个空 TU;增量构建 4~5 s,空 TU 只在全量重建或改 `lv_conf.h` 时重编,**远程 CI 刻意不跑 `@build` **;−40% 无基线记录 | 🟡 **降级观察项 ** ;真要做用 Keil 原生 `<IncludeInBuild>0</IncludeInBuild>` (可逆),且排在任何 `lv_conf.h` 改动之后 |
| **F-6 ** 双精度软浮点 | ≈1 KB | **归因错 ** :全库 0 处 `%f` ;剩余 `dadd/dmul/ddiv/depilogue/dfixul` 1,018 B 锁在 MicroLIB `printfa.o` 的 `_printf_core → _fp_digits` ,只要用 `snprintf` 就删不掉。但**根因侧确有可修点**:`App/algo/prot_set.h` 两个 Q10 宏写成 `double` | ✅ **已落地 2026-10-22 ** :改单精度 → `@build` − 332 B、5 个 helper(300 B)消失;数值逐位等价(穷举 42 个字面量 + int32 全域 ÷1024 往返 4.49×10⁶ 例零差异);真机 `@test` PASS |
**当前可回收 Flash = 0 B ** (F-4/F-6 已定论,图片链同理不打补丁)。LVGL 仍占镜像 **37.8% **
( 87,706 / 232,536 B)——这是"用 LVGL 画单色菜单"的结构性代价,F-1/F-2/F-3 已省掉 64,576 B。
### 5.5 内存治理长效机制(常开,不受暂缓影响)
1. 每次 `@build` 后 `scripts/ram_baseline.py --update` ,再 `--write` 回填台账受管区;`--gate` 判 92%
2. `check_mem_numbers.py --scan/--write` :文档里的内存数字一律由 `.map` 反查,禁止手填
3. 改栈 / 加大局部变量前后各跑一次 `stack_report.py` (静态 × 真机 `[STACK]` )
4. 子系统配额与 CCM 台账 → `../内存管理/RAM预算台账.md` ;趋势 → `scripts/ram_baseline.tsv`
5. **两条补强已全部落地(2026-10-23) ** : `malloc(` 正则 = `check_coding_standard.py` **CODE-RULE-20 **
(硬拦截、零基线,判据与豁免见 `../门禁说明/coding-standard.md` §1/§7)。
另一条「覆盖率口径」✅ **已落地 2026-10-23 ** → `../测试说明/主机单测覆盖率口径.md` (口径 / 基线 / 盲区
一处写完,此处不复述)。⚠ 它落地的是**行与函数**覆盖,**分支覆盖本机拿不到**(无 gcc, MSVC 14.51
已无 `/coverage` )——「覆盖到哪些分支」这一半**已裁定不补**(不为它引入第二套工具链,见
`../测试说明/主机单测覆盖率口径.md` §2.1),当前补偿 = TC 双向追溯 + 用例显式列边界值
## 6. UI 界面标准化:从方案到已落地
### 6.1 现状(U1~U5 复核)
| 项 | 旧状态(2026-10-15) | 今天 | 证据 |
|---|---|---|---|
| U1 令牌层 | ❌ 未建 | ✅ 已落地 | `Drivers/BSP/UI/ui_tokens.h` ( 69 行,L0) |
| U2 组件库 | ❌ 未建 | 🟡 仅 1 个组件 | `ui_w_records.c/h` ;其余仍是页内自绘 |
| U3 页注册表 | ❌ 手写 `case` | ✅ 已落地 | `ui_page_registry.c` ( `content_page_def_t` 描述符)+ 7 页 |
| U4 回归门禁 | 🟡 未挂 pre-commit | ✅ 已挂 | `ui-golden` (菜单 8 状态 + 内容页)、`font-charset` |
| U5 页面扩展 | 27 页未落地 | 🟡 按需求推进 | 现 7 页(含网络配置与改密两重页) |
### 6.2 新增一个页面要走多少步(今天实测)
四步:① 新建 `ui_page_<name>.c` (轻页 40~71 行)② 在 `ui_page_registry.c` 加一条描述符 ③ 菜单表挂节点
④ 跑 `ui-golden` 更新黄金参考 + `@build` 。旧流程里的"改 `ui_task.c` 手写 case"已不存在。
**未达项 ** :验收目标"≤30 行/页"——最省的 `ui_page_dc.c` 也要 40 行。
### 6.3 已完成部分的记录去处
原"8 人日方案"正文(四层架构设问、逐日排期、27 页清单)已随落地删除;现行做法与约束只在两处详述:
`../UI设计/UI_设计架构.md` (分层、令牌、双端同步、陷阱表)与 `../UI设计/UI_自动化测试流程.md` (回归与
黄金参考);历史决策与验收证据见本文**变更记录**(2026-10-15「UI 扩展」三行)与 `../UI设计/菜单页面设计.md` 。
### 6.4 UI 侧的内存纪律(写进规范,勿再试探)
1. 1bpp 单色:绘制缓冲 160× 160/8 = 3,200 B/份,多缓冲每加一份都算进 `RW_IRAM1` ( DMA 必须在 SRAM)
2. `LV_MEM_SIZE` 现为 16 KB;实测峰值 6,568 B( 40%)→ **再降到 8 KB 前必须先看这个水位 **
3. 字符集:新增中文必须过 `font-charset` ,缺字 = 白框(静默视觉缺陷)
4. UI 侧缓冲**不得进 CCM**( DMA2 不可达);`ui_net_sink` 推流缓冲常驻 6.4 KB,产品构建可加开关
5. 字体与像素改动 = 视觉回归范围,必须走 `ui-verify` skill 的五步
### 6.5 实施顺序(历史,已执行完)
「UI 扩展」簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止"),顺序与理由见 §6.1 与变更记录
2026-10-15 三行;不再另存排期。
## 7. 统一改进路线图(不另立权威)
| 波次 | 本报告条目(今天状态) | 备注 |
|---|---|---|
| ~~预算与内存~~ ⏸ | R-9 ✅ / R-2 后半、R-3、R-4、R-6 暂缓 | 暂缓期间无验收动作,观测照常 |
| 结构债 | N-14 ⏸、F-5 🟡、S-1/S-2/S-4/S-5、N-25 | `--strict` 已默认;S-5 契约/门禁已落地并收编三模块,`consume-result` 已把"丢弃返回值"纳入机械检查(基线 19→1→38;剩 GD5F 码集收编、C4 快照、37 条丢弃点、标坏策略) |
| 验证常态 | ~~覆盖率~~ ✅、~~`malloc` 正则~~ ✅(均 2026-10-23)、TC 差异 13 处升门禁 | 剩 TC 一条 |
| UI 扩展 | U2 残余、U5 页量 | 随产品需求,不再单列专项 |
| 仓库决策 | D-1(N-29) | 已决策暂不处理;对外交付前重评 |
## 附录 A 关键证据索引(2026-10-22)
| 结论 | 证据位置 |
|---|---|
| 三区水位与 Top-10 静态大头 | `../内存管理/data/ram_report_20261022.md` §1/§2/§3 |
| 9 任务栈判定 | `../内存管理/data/stack_report_20261022.md` |
| F-4 关不掉 | `Lib/lvgl/src/draw/sw/lv_draw_sw.c` ( `execute_drawing()` ) + 两份 `lv_conf.h` 的 `LV_DRAW_SW_COMPLEX` 注释 + `Drivers/BSP/UI/ui_deco.c` |
| F-6 已落地与等价性 | `App/algo/prot_set.h` Q10 宏注释;`MDK-ARM/STM32F407-Demo.map` 交叉引用区(`refers to mf_w.l(d*.o)` ) |
| `snprintf` 必带 `_fp_digits` | 全库 `%f` 命中 0; `mc_w.l(printfa.o)` 交叉引用 |
| 任务栈配置唯一来源 | `Src/freertos.c` + `README.md` /`AGENTS.md` 的 `TASK_TABLE` 受管块 |
| UI 落地形态 | `Drivers/BSP/UI/ui_tokens.h` 、`ui_page_registry.c` 、`ui_w_records.c` 、`scripts/check_ui_golden.py` |
| 外部资料体积 | `git ls-files docs/芯片手册` ( 717 文件)+ `du -sm .git` ( 248 MB) |
## 附录 C 确认「无需改动」的设计(避免后续误改)
1. **MicroLIB 的 `_fp_digits` 及其双精度 helper( 1,018 B) ** ——不是"忘了清 `%f` ",是 `snprintf` 的固有
依赖;删不掉,别再去搜 `%f` 。
2. * * `LV_DRAW_SW_COMPLEX = 1` **——2026-09-03 与 2026-10-22 两次实测:关掉后"浅 V"两腿不渲染。
3. **FTL 单页写回缓存 ** ( N-14/N-22)——`f_close` 已经 `CTRL_SYNC` 落盘,为"单条记录"加空闲/掉电强制
flush 属过度设计。
4. **uvprojx 的 127 个空 LVGL TU ** (F-5)——不影响镜像与增量时长,删条目反而破坏"改 `lv_conf.h` 全量
重编"的一致性。
5. **CH395F SPI 分频 = 8 ** 、**ADC1 走 DMA2 Stream4**、**TIM3 做 1 kHz 节拍源**——都是 PCB/硬件约束,
不是可调优参数。
6. **就绪屏障位用 `osFlagsNoClear` ** ——曾被默认消费行为坑到 FTP 静默不监听。
7. * * `ui_page_registry` 描述符注册表**——已取代手写 `case` ,不要再退回分发式写法。
## 9. 编号族登记处(**冻结**, `check_ids.py` 的属主登记处)
> 本文是下列 6 个族的属主登记处(`scripts/id_registry.json`, `check_dangling=true`),其它文档以
> 「报告 N-14」「报告 F-4/F-5/F-6」「报告 D-1」「报告 R-2/R-3」式写法引用它们。**删除本节会让
> 编号门禁报"定义缺失"并拦住提交**。新增条目一律改用**语义短语**、不再发新号(`../任务规划/README.md` §0
> 的用户裁定);本节只保留存量号的最终状态。
| 号 | 事项 | 状态 | 现在详述于 |
|---|---|---|---|
| N-01 | 配置引擎整文件驻留 | ✅ 已修(流式列为后续项) | `../子系统设计/配置文件设计说明.md` §6.2/§8 |
| N-02 / N-03 / N-03a / N-03b / N-03c | 存储与日志取证类条目 | ✅ 已闭环 | 存储架构说明 §10.1、本文变更记录 |
| N-06 | 注册白名单与 `--strict` | 🟡 残余随 B2 | 登记簿 N-23 残余 |
| N-07 / N-08 | 网络自愈与卷锁 | ✅ 已闭环 | `../网络/网络自愈架构设计.md` §4.10 |
| **N-09 ** | `NET_MAX_SOCKETS` /PHY/PASV 三项网络债 | ❌ 在策 | 本文 §4.2、登记簿 N-25 |
| N-10 ~ N-13 | 主机侧 nand/dhara 移植层单测动因 | ✅ 已建 | `test/host/nand_port/` (第 16 个单测钩子) |
| **N-14 ** | FTL 写回缓存无空闲/掉电强制 flush | ⏸ 暂缓 | 本文 §4.3、登记簿 N-22 |
| **N-15 ** | `nand_ftl` 三块 2 KB 页缓冲未合并 | 🟡 **观察项(2026-10-23 判定不做) ** | `../内存管理/存储栈RAM优化调研.md` §5、登记簿 N-25 |
| N-16 / N-17 | 台架窗口项(task stall 归因等) | 🟡 待窗口 | 登记簿 N-16/N-17 |
| **N-18 ** | V0.1 双转换器无共同采样触发 | ❌ 待 V0.2 | 本文 §0.3、AGENTS 硬件引脚节 |
| N-19 ~ N-21 | 时钟/对时与已销项条目 | ✅ 已闭环 | 本文变更记录与去处表 |
| **D-1 ** | 外部资料与工程文档同仓同树 | ❌ 暂不处理 | 本文 §3.3、登记簿 N-29 |
| D-2 ~ D-7 | 文档治理条目(链接/同源/编号/BOM 等) | ✅ 多数已制度化为钩子 | 本文 §3.2、`../文档规范.md` |
| D-9 | 文档面残余观察 | 🟡 观察 | 本节即定义位 |
| **S-1 ** | 分层反向依赖(BSP include App 的头) | 🟡 已止住恶化(存量 6 条基线待还,新增被 `layer-deps` 拦) | 本文 §0.4、登记簿 C5 |
| **S-2 ** | 跨模块裸 `extern` | ✅ **已闭环 2026-10-22 ** (24 处手写全消;两类本就不算债见 §0.4) | 本文变更记录 2026-10-22 三行;契约见 `App/task/task_handles.h` 、`gd5f_spi1_notify_*` 、`net_msg_queue_adopt` |
| **S-4 ** | `app_cfg.h` 未接线占位开关 | ✅ **已闭环 2026-10-22 ** ( 4 个零消费点宏删除) | `App/app/app_cfg.h` 修改记录 v1.2;口径同步见 `../环境与部署/项目部署环境要求.md` §3.2/§3.5、`../产品需求/产品需求设计文档.md` 、`../任务规划/排期详述.md` C5 |
| **S-3 ** | `.ioc` 侧 USART1 配置 | ❌ 在策 | 登记簿 S-3 |
| **S-5 ** | 统一 `bsp_err_t` 错误码与超时命名约定 + **返回值不得裸丢弃 ** | 🟡 主体已落地(2026-10-23:契约 + `bsp-err` 四规则 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码 + `consume-result` 照出 37 条丢弃 + ECC 回读校验;基线 19 → 1 → 38) | 本文 §0.4、`pre-commit使用说明.md` §2.27、`STM32F4-Base存储架构说明.md` §10.4、`Drivers/BSP/bsp_err.h` |
| R-1 ~ R-9 | 内存优化项(R-9 已落地、R-5 判定不可行、其余随簇暂缓) | 见 §5.3 | 本文 §5.3、`../内存管理/RAM预算台账.md` |
| F-1 / F-2 / F-3 | LVGL 死代码裁剪,省 64,576 B | ✅ 已落地 2026-10-11 | `../内存管理/RAM使用分析与优化方案.md` §2.6.4 |
| **F-4 / F-5 / F-6 ** | Flash 双精度与死代码簇 | ✅/🟡/❌ 见 §5.4 | 本文 §5.4、`App/algo/prot_set.h` 注释 |
| U1 ~ U5 | UI 标准化簇 | 见 §6.1 | 本文 §6.1、`../UI设计/UI_设计架构.md` |
## 变更记录
| 日期 | 类型 | 说明 |
|------|------|------|
| 2026-09-18 | 初版 | 全量审查;基线 `HEAD = 2d0ec35` 、`.map` 链接于 2026-09-16;产出 N-01..N-18 / D-1..D-7 / S-1..S-5 / R-1..R-9 / F-1..F-6 / U1..U5 编号体系 |
| 2026-10-11 | 实施 + 销项:F-1/F-2/F-3 | 两份 `lv_conf.h` 对齐同一特性子集 → 省 64,576 B;验证链(`@build` / ui-verify / `@test` )与对象级对照见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 |
| **2026-10-15 ** | **登记簿同步销项 ** | 本报告的 N-03c / N-11 对应登记簿 **N-20 / N-26 ** 两行已按「完成即销项」从 `../任务规划/登记簿.md` §5 删除(并清 `../任务规划/README.md` §1/§2 与 `排期详述.md` §1 的引用,「结构债」链由 6 项减为 5 项、结构/健壮性计数 9→7)。删除前补齐去处记录:`../子系统设计/看门狗设计说明.md` §4.4 新增 `Error_Handler` 统一降级一节、`../内存管理/内存内容分配策略.md` §4 R2 补编译期窗口断言与启动期自检两层 + §8 落点索引两行。同批订正登记簿三处过时内容:N-23 残余去掉已删除的 `lftpd_log.c` 、N-25 去掉已删除的 `NET_RECV_TIMEOUT_MS` 、N-27 剔除不可行的 R-5 并把 CCM 余量改为实测 7,904 B、N-29 的 `.git` 由 149 MB 订正为实测 235.3 MB |
| **2026-10-15 ** | **执行本报告条目:R-9 落地并销项 ** | `LV_MEM_SIZE` 40→16 KB( `Lib/lv_conf.h` + `simulator/lv_conf.h` 同步,`lv-conf-sync` 生效值比对 PASS)。**依据**:池水位门禁实测菜单基线 5,368 B / 开页-关页往返 5,448 B / 最重页面峰值 6,568 B。**验证**: `@build` 0 错 0 警,ZI 精确 − 24,576 B → SRAM1 98,144/74.9% → **73,568/56.1%(余 57,504 B) ** ,CCM 不变;PC 侧 11 页黄金参考逐字节 10/11 一致、往返池水位 5,448 B 未变(唯一差异 `netcfg_save` 经 40 KB A/B 复测确认与本次改动无关,真因见下一行)。**连带改写**:§0.2 Top3 第 2 条、§0.3 主线一、§0.4 内存两行、§5.1 基线表(追 2026-10-15 `.map` )、§5.2 诊断 1(瓶颈由 SRAM1 换到 CCM)、§5.3( R-9 行删除、R-2 收益改 − 29,570 B)、§6.4 池容量行;同步 `../内存管理/RAM使用分析与优化方案.md` 、`../内存管理/RAM预算台账.md` §3、`../UI设计/UI_设计架构.md` 、`../UI设计/UI_界面制作流程.md` 、`../UI设计/LVGL模拟器部署与使用说明.md` 、`simulator/README.md` 、`../产品需求/产品需求设计文档.md` 。**真机复跑 ✅**: `@test` PASS( `fw build: Oct 15 2026 07:31:34` ,无 `[ERR]` /Fault,开机 622 ms 到 `net init: OK` ),`menu build done: used=3192` = 建池后 15,020 B 的 21%。台账受管区与内存快照区已 `--write` 回填 → `mem-budget` / `mem-numbers` 两钩子转绿 |
| **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` 「预算与内存」、`../内存管理/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-15 ** | **删除 §0.2「未闭环问题」小节 ** | Top 2 修复销项后该节只剩指针与暂缓说明,而三条内容都已在别处**单点详述**:未闭环清单 → §0.4;内存簇暂缓 → §0.3 主线一与 §5.3 横幅;Top 2 修复证据 → 上一条变更记录与文末「已销项条目的记录去处」。按 `../文档规范.md` 「同一事实只在一处详述」删除,不留空壳。**§0.3 / §0.4 编号保持不变、不重排**——`../任务规划/README.md` §2 前言引用本报告 §0.3,本报告内部另有 §0.4 交叉引用,重排会全断(与本表 2026-10-14 行第 ⑤ 条"报告自有编号不回改、不重排"同一口径)。文首「销项状态」行的指针由「§0.2 / §0.4」改为「§0.4」 |
| **2026-10-15 ** | **删除 §0.3 主线建议第 1 条("内存这条线到此为止") ** | 该条是同日暂缓裁定的**复述层**,同一事实已有三处单点详述:§0.4 内存两行(⏸ 暂缓)、§5.3 暂缓横幅(三条理由 + 保留常开的治理面 + 重开条件)、登记簿 N-27 与 `../内存管理/` 两份文档的横幅。按 `../文档规范.md` 「同一事实只在一处详述」删复述、留权威。⚠ 上一条变更记录里"内存簇暂缓 → §0.3 主线一"这个去处**自此失效,以本行为准**(变更记录本身不改,它是历史)。§0.3 现只剩 UI 一条主线;标题「两条主线建议」→「主线建议」,**§0.3 编号不变**(`../任务规划/README.md` §2 前言引用的是编号不是标题) |
| **2026-10-15 ** | **启动「UI 扩展」标准化簇 ** (用户裁定,覆盖 2026-10-11 的「本轮不排期」) | §0.3 删掉内存主线后只剩 UI 这一条,随即启动。已按 §6.5 的 U1~U5 拆成 5 个工作包上共享任务板,第一波 **U1 令牌层 ∥ U4 门禁补强 ** 两名成员并行(写范围严格不相交:U1 只写 `Drivers/BSP/UI/` + `App/task/ui_task.c` , U4 只写 `scripts/` + 门禁与规范文档)。**并行度的真实约束不是任务数量,而是注册文件**:新增 `.c` 必须改 `uvprojx` + `CMakeLists.txt` 两个共享文件,同工作树两人同改必互相踩,故 U3(生成器/注册表)是必须串行的一段,U2/U5 排在它后面——U5 的页面批量要等 U3 把注册自动化之后才谈得上并行。同步 §6.5 注与本表「UI 扩展」行;`../任务规划/README.md` §2.0「UI 扩展」行同步。另修正交付物清单:移植层单测拟名 `test/host/dhara/` 实际落地为 `test/host/nand_port/` ,状态 ❌ → ✅ |
| **2026-10-15 ** | * * §6 顶部加"建议栏不可照抄"总警示** | 启动「UI 扩展」后按 §6 执行 U1/U3/U4,**四次证伪**:① §6.3 要新建 `UI_SCREEN_W/H` 而 `ui_port.h:21-22` 早有同名宏;② `LV_COLOR_BLACK/WHITE` 在 LVGL 9.3 不存在**且极性反了**(墨色实为 white),照抄先编译失败、改完整屏反色;③ "27 页未落地"是**目标页数**、现存 `ui_page_*.c` 实测 7 个;④ 生成器草图漏掉**密码页非节点绑定**。另有 L0 清单 9 条取值错,明细在 §6.3 更正块。**不再逐条打补丁**,改为节顶立总口径:动手前逐条 grep 对代码核、不符一律以代码为准、差集必须回报不得静默修正;并明确**"发现"栏可信、"建议"栏不可照抄**。同一规律在 §4 的 N-13(`DHARA_E_UNSUPPORTED` 不存在)已复现,故这不是 UI 章节局部问题,而是"建议"这一体裁的通病 → 后续任务卡须显式写"报告建议值不可直接采用" |
| **2026-10-15 ** | * * 「UI 扩展」第一波落地:U1 + U3 验证通过并回填现状表** | Lead 亲自验证(成员被模型路由变更连续打断,最终停用):`@build` **0 错 0 警 ** ; golden **菜单 8 状态全像素 + 内容页 11 页逐字节一致 ** 、池水位 **5368 / 5448 B 与 U1 基线相同 ** 、参考图 sha256 **差异 0 条 ** (证明无人用 `--update` 刷基线);`registration-check` (含第 ④ 类"页面必须被表引用")与 `font-charset` **Passed ** ; `ER_IROM1` **232,052 B ** (较已提交 HEAD − 228 B)、`RW_IRAM1` **75,232 B 零增量 ** ;对 38 个在途修改文件逐个核对 **BOM / CRLF / U+FFFD 全部与 HEAD 一致 ** 。**单位成本实测**(临时页 `zzprobe` 绑到 `遥信量` 叶子 → 只跑生成器 → 带页 `@build` 0/0 → 注册表出现 `{5u, &ui_page_zzprobe_desc}` → 回滚后 6 个产出文件的 `zzprobe` 残留计数均为 0、重编回基线)得出 **新增一页 = 3 处人工(新 `.c` + 新 `.h` + 树那一行加 ` #page=<名>`)+ 1 条生成器命令,手工编辑注册文件 0 处 ** ,并确认生成器 `registered` / `pruned` **双向对账 ** (不只增、也减)。据此修正 §0.1 UI 行(B → **B+ ** )、§6.1「内容页」与「设计令牌」两行、§6.5 U3 行;并把 §6.5 U4 从我上一版**误写的 3/3 改回 2/3**( golden 两脚本至今未挂 pre-commit, "脚本已存在"≠"已进自动门禁");§6.5 U3 原口径"8 处降到 1 处"改为**真实 3 处**。**"分三笔提交"计划作废**:U1 与 U3 改同一批文件、文件内交叠,硬切会产生不可编译的中间态,改为整波一笔 |
| **2026-10-20 ** | **补上 U4 缺的第三件:golden 像素回归入提交门禁(35→36 钩子) ** | 先实测成本再定形态:改一个 UI 源文件后「增量重编模拟器 + 两条 golden」合计 **2~4 秒 ** ,支持走自动门禁(若实测要几分钟,正确做法是明确不挂、改为文档化人工门禁——一个逼人 `--no-verify` 的门禁等于没有门禁)。新增 `scripts/check_ui_golden.py` 包装器(**有意忽略 argv 并 `pass_filenames: false` **:两条 golden 按整棵菜单树 + 全量参考图工作,不接受文件参数),新钩子 `ui-golden` 的 `files:` 覆盖 UI 源 + `ui_task.{c,h}` + 模拟器 tests/sim/src + `lv_conf.h` + 包装器与本配置文件自身。
⚠ **端到端验的是钩子不是脚本 ** :干净树连跑 3 次 `rc=0` 全 Passed;注入 `ui_page_dc.c` 的 `.top_y +5` → **钩子 Failed ** 并点名 `[dc] FAIL 与参考页不一致(1882 px 差异)` ;还原后 `git diff` 干净、`simulator/tests/ref` 的 `git status` 为空(**全程未用 `--update` 刷基线**)。诚实记录一次瑕疵:首次实测在连续注入/还原的紧邻重建之间偶发失败一次,疑似 MSBuild 节点争用,已把"单次失败先复跑、连续两次才当真、不许直接绕过"写进钩子注释。
据此 §6.5 U4 由 **2/3 → 3/3 ** (这次是"真进了自动门禁",不是"脚本存在")、§6.1 golden 行转 ✅、§0.1 UI 行缺口描述更新、`AGENTS.md` 钩子数与文末索引 35→36、`pre-commit使用说明.md` 概览计数同步。**「UI 扩展」剩余**:U2 合并两页记录、U5 落地目标页(须先对齐"目标页 vs 现存 7 个 `ui_page_*.c` "差集)。
| **2026-10-22 ** | **v3.0 全量重写:删过时条目 + 按今天的仓库重新审计 ** (用户裁定"过时的都删除,重新写") | 旧正文按今天的实测重取数重判:六维评分旧表、UI「8 人日方案」正文、波次交付物清单、以及所有已闭环条目整段删除;**697 → 436 行**(−37%)。
**重写依据(全部当日实测,不再引用旧数字) ** :镜像 `ER_IROM1` 231,768 B / 22.1%( Code 213,188)、`RW_IRAM1` 75,264 B / 57.4%(余 55,808)、`RW_CCM` 57,376 B / **87.9%(余 7,904) ** 、堆 `min_ever` 8,464 B、任务栈 9/9 PASS( `data/ram_report_20261022.md` 、`data/stack_report_20261022.md` );仓库 2,498 文件 / 80.0 MB、`.git` 248 MB、`docs/芯片手册` 717 文件 / 39.5 MB 占 docs 87%; 37 个钩子、16 套主机单测、TC 593∶271 交集 265。
**改判的三条旧结论 ** :① §6 的 U1/U3/U4 原写「❌ 未建」,实际已落地(`ui_tokens.h` 69 行、`ui_page_registry.c` 描述符注册表、`ui-golden` +`font-charset` 两枚钩子);U2 只有 `ui_w_records` 一个组件;"27 页未落地"实为 7 页。② "新增一页 ≤30 行"目标**未达**(最省 40 行),如实记为未达项。③ §0.4 与登记簿已漂开 → 本版把待办清单降级为**快照 + 指向登记簿**,权威只留一处。
**刻意保留的三层 ** (删了会出事):§9 **编号族登记处 ** ——本文是 `id_registry.json` 里 `N-RPT` /`D-RPT` /`S-RPT` /`R-RPT` (游标 R-9)/`F-RPT` /`U-RPT` (游标 U5) 六族的属主登记处,且外部存在「报告 N-14」「报告 F-4/F-5/F-6」「报告 D-1」「报告 R-2/R-3」式紧邻引用与 `check_dangling` ,删定义位即触发编号门禁;§1.2/§1.3 方法与口径(数字可重跑);文末**变更记录 + 已销项去处表**原样冻结保留(52 行)。被外部点名的锚点(`§0.3` /`§3.1` /`§4.1` /`§4.2` /`§4.3` /`§5.3` × 9/`§5.4` /`§5.5` × 4/`§6.2` /`§6.3` /`§6.5` )逐个保留,**68 处入站引用零改动**。
**门禁 ** : `check_doc_links.py --all --sections-strict` → 146 文档 0 问题(章节引用 2,499、可判定 1,483);`check_ids.py --strict` 增量拦截通过、**存量债 650 → 641(−9) **、可判定比例 82.98% → **82.79% ** (仍高于基线 82.6%,但缓冲从 0.38pp 缩到 0.19pp——删掉的那批引用 99% 是可判定的,属预期副作用);`mem-numbers` / `ram_baseline --gate` / `task-table` / `cubemx-survival` 全 0。字节卫生:BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格 / LF 结尾。 |
| **2026-10-22 ** | **S-2 收尾:`netMsgQueueHandle` 收进 net_socket 内部——该族 24 处手写裸 extern 全部清零 ** | 最后一步。队列对象仍由 CubeMX 在
`MX_FREERTOS_Init()` 创建(**创建点不可外移**: `osKernelInitialize()` 之前的任何创建类 API 会杀死启动,
docs/架构设计/项目启动顺序管理.md),改为创建后立刻在 `Src/freertos.c` 的 `USER CODE BEGIN RTOS_QUEUES` 区
调用 `net_msg_queue_adopt(netMsgQueueHandle)` 把句柄交给本模块;net_socket 内改为 `static s_net_msg_queue`
(21 处引用改名),BSP 对该全局变量的裸 `extern` 消失,`netMsgQueueHandle` 现在只出现在生成文件自己的两处。 |
| | **两个刻意的设计决定 ** :① **不新建第二个队列 ** ——那会白占 CCM 堆里 8 × `net_msg_t` 的空间,而 CCM 是当下
87.9% 的红线;所以选"创建后绑定"而不是"自建队列 + 删 .ioc 里的队列"(后者还要 CubeMX 重生成,属 Ask-first)。
② `adopt()` **拒绝重复绑定并保留首次句柄 ** ——真出现两处绑定时,静默换句柄会让已入队的请求留在旧队列里永不处理,
拒掉第二个反而更容易暴露。绑定前的判空是真实防线:丢这一行的后果是"所有 net_* 请求被丢弃、网络全线静默不通",
不是崩溃。 |
| | **防回退 ** : `check_cubemx_survival.py` 新增存活项 `net-msgq-adopt` ( region `RTOS_QUEUES` , must_contain
`net_msg_queue_adopt` ),易失清单 **29 → 30 项全部到位 ** ;基线已重建。 |
| | **体量 ** : `Code` 213,204 → **213,236 B( +32) ** 、`ER_IROM1` **231,816 B( 22.1%) ** ; `RW +4 / ZI − 4` →
`RW_IRAM1` 恒 **75,264 B( 57.4%) ** 、`RW_CCM` 恒 **57,376 B( 87.9%) ** 、`RW+ZI` 总量 132,688 B **不变 ** 。 |
| | **验证 ** : `@build` 0 错 0 警告;`@test` PASS( 624 ms 到 `net init: OK` 、i104 已监听、无 `[ERR]` /Fault、
`heap free=15800 min_ever=8464` 逐项不变——证明没有多占一个队列);**另做真机 FTP 控制通道往返**:220 →
`USER admin` 331 → `PASS admin` **230 ** → `PWD` 257 → `TYPE I` 200 → `QUIT` 221,每条命令都是一次过队列的
net_recv/net_send → 绑定路径在真机走通;`.map` 里 `freertos.o(MX_FREERTOS_Init) refers to
net_socket.o(i.net_msg_queue_adopt)` 证明镜像确实含该行。 |
| | **顺带揪出一条测试工具漂移(未修,等定夺) ** : `test/host/ftp_up_dl_test.py:123/126` 仍写死
`USER anonymous` / `PASS test@test.com` ,而设备自 N-19( 2026-10-11)起按 `/config.ini [ftp]` 校验
admin/admin( SHA-256 `pwd_hash` )→ 该脚本**必然登录失败**;它是手工工具、不在任何 pre-commit 钩子里,所以
一直没人发现,而 `../测试说明/FTP_Test_Guide.md` 把它列为推荐入口。建议:加 `--user/--password`
(默认 admin/admin),并顺手把 `ftp_speed_test.py` 一起核。 |
| | 数字联动照做:两份 `data/` 快照重跑、`ram_baseline --update/--write` 、`check_mem_numbers --write` 、FLASH 与
任务栈两张配图重算重生成(逐文本复查越界 = 0)、本文 §1.3/§5.1/§5.4 与部署文档 §3.3 同步;§0.4 的 S-2 行按
「完成即销项」改写、§9 拆出 S-2 行标 ✅。 |
| **2026-10-23 ** | **执行 §5.5 第 5 条后半:C 库堆禁令变成机器拦截 ** (§4.8 空白 ②,CODE-RULE-20) | 按本文件 §8 的既有纪律**先全库普查再动手**:`coding-standard` 钩子范围内 C/H 共 **151 个文件 ** , `malloc` 族只命中
| | | `simulator/sim/cfg_file_store_sim.c` 两行(`cfg_store_alloc` /`cfg_store_free` = 平台分配后端的**宿主侧实现**,
| | | 设计依据写在 `../子系统设计/配置文件设计说明.md` 的"MCU 端 = pvPortMalloc、模拟器端 = 宿主 CRT")——**不是漏网**,
| | | 所以零基线、直接硬拦截;豁免走脚本里的 `HEAP_ALLOC_ALLOWLIST` (**文件路径 + 函数名**双条件),刻意不做目录级豁免:
| | | 下次谁要碰 C 堆就得往那张表加一行,评审看得见。判据 = 掩码后文本上匹配 `\b(malloc|calloc|realloc|strdup|strndup|
| | | free|aligned_alloc)\s*(\(` :大小写敏感 ⇒ `pvPortMalloc` /`vPortFree` 不误报;词边界 ⇒ `dhara_nand_is_free(` 、`buf_free(`
| | | 不误报;注释与字符串里写 `malloc` 不报。**自证**:临时样例三处(`malloc/calloc/free` )各报一次、五种陷阱形态全部不报;
| | | `pre-commit run coding-standard --all-files` 实测 **Passed ** ( = 全库零存量)。
| | | **顺带消掉一处自相矛盾 ** :规范 V1.0 §6.1 的示例自己写着 `malloc` /`free` (通用 C 模板遗留),已在其下方加
| | | 「本仓例外,别照抄」批注并把事实源指向 AGENTS.md 平台约束;`docs/门禁说明/mem-budget.md` 里"这条纪律只靠评审"
| | | 的旧说法同步更正。AGENTS.md 平台约束条加"已机器拦截"一句。**本轮零 `.c/.h` 改动**(只动门禁脚本与文档),
| | | 未跑 `@build` 、未占 build/bench 锁;详解归 `../门禁说明/coding-standard.md` (本处不复述判据) |
| **2026-10-23 ** | **执行 §5.5 第 5 条前半:覆盖率口径落地并销项 ** (§4.8 空白 ①) | 新增 `scripts/check_test_coverage.py` (纯标准库)+ 基线台账 `scripts/test_coverage_baseline.json` 。**工具链是实测出来的、不是查到的**:本机 PATH 上 gcc/gcov/clang/llvm-cov/lcov/gcovr **全无 ** ; `cl /coverage` 、`/fsanitize=coverage` 在 MSVC 14.51 已被砍——**只报 `warning D9002 忽略未知选项` ,然后照常编译成功**(拿它做覆盖率=静默假数据,比报错更坏);`Team Tools\Dynamic Code Coverage Tools\CodeCoverage.exe` ( 17.0)的 `collect` /`analyze` /`instrument` 一律只打印「用法: CodeCoverage <command>」。最终走 **mscoverage( `Microsoft.CodeCoverage.Console.exe` 18.x) `collect -f cobertura` ** 的**加载期动态插桩**,并实测它**原样透传子进程退出码**(子进程 `return 3` → `collect` 也返回 3)→ 于是「插桩运行下仍必须 PASS」可以当硬条件用。 |
| | | **口径 ** :只算 `App/**` 与 `Drivers/BSP/**` 的行/函数、多套单测取**并集**、生成物(文件头写「自动生成 / 请勿手改」)不进分母、分母一律来自插桩报告**不猜行数**;配置零复制——脚本 import 各运行器的 `TestBuild` 、开关取 `unit_test_harness._BASE_FLAGS` ,只追加 `/Zi /Od` 与 `/link /INCREMENTAL:NO` ,发现规则与 L0.5 同一条 `test/host/**/run_unit_test.py` , **不会漂出第二套口径**。 |
| | | **首登数字(2026-10-23) ** : 16 套全通过,并集 2552/2893 行(88.2%)、193/207 函数(93.2%)、19 个生产文件在测,**未触及 125 个生产源文件 / 约 11,839 行**( `net_socket.c` 1,251 行最大)——最后一栏才是这套口径真正照出来的东西。**门禁自证**:人为抬高基线后,行 / 函数 / 总计三条回退路径都如期报 REGRESS 且退出码 1;`--module` 子集运行不会假报总计回退(首版就会,已修)。**刻意未挂 pre-commit**:三条理由与可直接粘的 YAML 片段写在 `../测试说明/主机单测覆盖率口径.md` §8,接线者照 §8 落地即可。 |
| | | **同日另一半也落地 ** (§4.8 空白 ②):`check_coding_standard.py` 加 **CODE-RULE-20 ** 禁 C 库堆分配,
| | | 硬拦截零基线(全库普查 151 个 C/H 只有 `cfg_file_store_sim.c` 两行命中 = 平台后端的宿主侧实现,
| | | 点名进 `HEAP_ALLOC_ALLOWLIST` ,刻意不做目录级豁免);规范 §6.1 示例自己写着 `malloc/free` 的
| | | 自相矛盾也同日加了"本仓禁止"批注。**只剩分支覆盖**一条。分支覆盖**不是拿不到**:同日被用户问住后复测,本机 WSL2 Ubuntu 有 gcc/gcov 15.2.0, 16 套单测在 gcc 下**全部编过且全绿**、`gcov -b -c` 出分支数据(`nand_ftl.c` 分支至少走过一次 41.40%);但换编译器就是换分母(`sha256.c` gcc 105 行 vs MSVC 114 行)→ **用户裁定只保留 MSVC 一条通道 ** (多一套工具链=多一套分母 +多一本台账 + 多一个"这台机器有没有 WSL"的未决项),分支覆盖**记为已接受缺口**、不是待办。实测表与决策依据见 `../测试说明/主机单测覆盖率口径.md` §2.1。本轮只加 `.py` /`.json` /`.md` ,无 `.c/.h` 改动,不重跑 `@build` ;文档面动 §0.1 / §0.4 / §4.8 / §5.5 / §7 与 `docs/README.md` 索引各一处。 |
| **2026-10-23 ** | **术语迁移 ** : `bsp-err` 的四项检查从 `R1/R2/R3/R4` 改成语义标签 | 用户一句「R4
| | 到底是什么」问出来的。这四个号**是我自己造的**,而 `R1~R8` 在本仓是"专项风险"的裸形编号
| | ( `id_registry.json` 已登记那一族,保护规划 `R1~R8` 、看门狗 `R1~R7` 的风险表都在用),`R-2`
| | 还是内存优化项——**同一裸形四种意思**。最刺人的是:**编号门禁不会报这种撞法**,因为
| | `docs/编号规范.md` §4.3 已把这些裸形声明为"同号不同义"、走 `collides_with` 白名单,于是机器一路
| | 放行、人完全看不懂。
| | 同一天里我给这批缺陷起的"字母加数字"自造号被门禁当场拦下,而这套代号却一路绿灯——说明白名单
| | 挡的是"事故"、不是"可读性"。
| | 新名(同时是基线键的第二字段):`err-type` 错误通道不外泄 / `timeout-name` 超时宏不取负值 /
| | `return-doc` 公开函数必须写返回值 / `consume-result` 返回值不得裸丢弃。改名**不动任何检查行为**:
| | 脚本里规则码与基线键一起换(38 条键的字段同步替换,换完 `--emit-baseline` 复核仍是 38/38/0,
| | 中途不跑 `--prune` ,否则会把改名误读成"全部还清 + 全部新账");现状类文档(AGENTS、§2.27、
| | `bsp_err.h` 、本报告 §0.4/§9/附录 A)全部改用新名,**已提交的变更记录行里的老名按冻结历史不回改**,
| | 以 `docs/代码规范/pre-commit使用说明.md` §2.27 的对照行为准。 |
| **2026-10-23 ** | **S-5 第三波 ** :给 `bsp-err` 加 **R4(返回值不得裸丢弃) ** ,并修掉它照出来的第一处
| | ECC 静默降级 | 用户裁定"B 按你说的进行"(先上 R4,再修最小最值的那处)。R4 之所以必须自己做,是两条实测
| | 负结果:`warn_unused_result` 在 ARMCC v5 不生效;cppcheck 按本仓 `--enable` 跑同一文件,对 14 处丢弃
| | `HAL_SPI_*` **一条不报 ** (它还假定 `gd5f_read_status(&status)` 会写出参,而失败路径根本不写)。上线即照出
| | **37 条存量丢弃 ** : `gd5f2gq5ue.c` 29( 14 处 HAL + 15 处自家原语)、`net_socket.c` 8、`ui_net_sink.c` 4( `net_close` )、
| | `rs485.c` 2、`nand_ftl.c` 1——按增量拦截老规矩**入基线只减不增**(覆盖面扩大新暴露的历史债,不是新增违规)。
| | **R4 自身被修三次 ** :① 首版漏判 `foo(); /* 注释 */` 这种**行尾带注释**的裸调用(正则 `\s*$` 卡死),而那
| | 恰是最常见的"带说明的丢弃"→ 补可选注释分支并做 6 条自检;② 把 `net_set_errno` 八处误判成丢弃(它是 setter,
| | 返回的 int 是状态回声不是调用成败)→ **按真实反例收窄规则 ** (谓词 + setter 豁免),不塞基线洗掉;③ 名单
| | 只从**头文件**取,`gd5f_set_feature` 这类 **static ** 原语整个在视野外——而上面那处 ECC 丢弃恰恰是 static、靠人工才发现
| | → 名单扩到 `.c` 内 static 定义,补完立刻又照出 `gd5f_bbt_save#1` (运行时标坏后 BBT 落盘结果被丢弃 = 记账
| | 可能没持久化;已入基线记为待还,本步不动行为)。
| | **这处为什么值得修 ** : `bbt_rebuild` 注释写着"无论扫描成败都须恢复 ECC,否则后续 I/O 在无 ECC 下运行",
| | 而它下面 `gd5f_set_feature(0xB0,…)` + `gd5f_wait_busy(100)` **返回值全丢 ** :SPI 一抖 ECC 就保持关闭,且
| | `gd5f_check_ecc()` 在 ECC 关闭时恒读"无错"——整条链再无信号。补法复用 init 路径的 **E-10 ** (写完回读 B0h 验
| | `ECC_EN` ,"写了但没生效"与写失败同罪),并把"关 ECC 失败"改成**立即中止**(此时 ECC 仍开着,是安全侧;
| | 继续扫反而会在 ECC 语义下读 spare、把厂标好块误判成坏块,"重建 BBT"变成制造坏块)。
| | **一条定位纠正(差点把修复记大) ** :查链接映射发现 `Removing gd5f2gq5ue.o(i.gd5f2gq5ue_bbt_rebuild),
| | (604 bytes)` ——**该函数在产品镜像里根本没被链接**(产品构建无调用者),只由 `ENABLE_STORAGE_RECOVERY_TESTS`
| | 的**恢复构建**经 testTask 调用。所以本次修的是**恢复流程自身的安全**(跑恢复时若 ECC 没恢复,设备会带着
| | "已恢复"的结论在无保护状态下继续跑),不是现场日常路径;相应地镜像 `Code` 的 +28 B 也**不是**它带来的(未逐字节
| | 追,线索是 `gd5f_private_get_feature` 由单调用点变多调用点、内联决策可能变化)。因此验证不能只跑产品构建:
| | 临时打开 `TEST_SUITE_STORAGE` + `ENABLE_STORAGE_RECOVERY_TESTS` 编一次(**0 错 0 警告且该函数不再出现在
| | Removing 列表**),随即还原两处开关并重编产品构建核对(`test/board` 无残留差异)。
| | 验证:`@build` 0 错 0 警告,`Code` 213,648 → **213,676 ** 、RO 18,860、`ER_IROM1` **232,536 B / 22.2% ** ( ROM
| | 合计 233,724 B);`ram_report` 重生成 + `ram_baseline.py --update` + 配图常量同步(`mem-numbers` Passed);
| | `@test` **PASS ** (到 `net init: OK` 682 ms、`[ERR]` 0);R4 两处**探针实证**(临时加一条裸调用 → 立刻成为
| | 新增并 RC=1;还原 → 38/38/0);`--prune` 点名本次还掉的 2 条已删;受影响文件钩子全 Passed。
| **2026-10-23 ** | **S-5 还债:`bsp-err` 基线 19 → 1 ** ,并修掉门禁自己的一处误判口径 | 用户裁定先还 A(清存量)再调查 B。**16 条缺「返回值:」全部补齐**:`gd5f2gq5ue.h` 九个 SPI 原语
( `wait_busy` /`write_enable` /`read_status` /`page_read` /`read_from_cache` /`program_load` /`program_exec` /
`block_erase` /`check_ecc` )逐条写全 函数功能/入口参数/返回值/限定条件/函数说明,**并把每个函数
能返回哪些码写死在注释里**(哪一个是"可重试"的 `GD5F_WAIT_EXPIRED` 、哪一个是器件自认坏块的
`P_FAIL` /`E_FAIL` );`nand_ftl.h` 从零散三行补成带模块头的完整注释,并**明写"`nand_ftl_init()`
的 -1 抹掉了失败类"**——分类目前只在失败点进日志,要按类分支需要 C4 快照(现无消费者故未做),
同时警告"不得把 -1 解读成卷坏了可以重格式化"(E-31 的教训)。
`ui_menu_tab.h` 五个表查询 API 由行尾短注释升级为完整块(含"越界返回 false 是保守取只读"、
"宁可按一张错表拒绝显示也不要按它导航"这类判断依据);该文件**带 BOM,Edit 后已按原样补回**
( AGENTS 铁律:丢 BOM → ARMCC 按 CP936 解码 → 中文报 #870 -D)。 |
| | **门禁自身的误判口径也修了,而且这是本轮最该记的一条 ** :R3 原本要求注释块里出现字面"返回值",
而规范 V1.0 允许列对齐写法「**返 回 值:**」。后果是双重的——① 合规写法被判违规;② 反向更糟:
`gd5f_write_enable` /`gd5f_read_status` 两条**早就"通过"了**,原因竟是它们的**函数说明**里顺口写了
"返回值"三个字,而不是真的有一行返回值说明。**一条会因为散文里出现关键词而放行的规则,等于在
奖励运气**。修法是在块内**忽略空格**后再匹配关键词(规范允许的排版不是违规),并在注释里写明
为什么不许把"必须不空格"写进规则。基线随之从表面 15 条回到真实 16 条缺说明,再一并补齐。 |
| | **刻意没做的最后一条 ** : `bool ui_content_page_open()` 留在基线里。它和"补注释"不是一档事——
要改签名(`bsp_err_t` )、要处理 **3 个调用点 ** ( `ui_menu.c:138/192/196` , **今天三处全部丢弃返回值**,
即"按 ENTER 开页失败是静默的"这件事现在没人知道)、UI 三个文件都带 BOM、且 UI 源码**双端编译**
(模拟器同份),验证面是"重新构建模拟器 + 像素回归 + 真机进菜单逐个页"。这不是还债,是独立一波改动。
顺带查出的这一条(开页失败静默)比签名统一更值得修。 |
| | 验证:`@build` **0 错 0 警告且 `Program Size` 与上一笔逐字段相同 ** ( `Code` 213,648、RO 18,860、
RW 1,188、ZI 131,500)——证明本笔确为纯注释,故**不跑 `@test` **(无任何指令变化,跑它没有新信息);
受影响文件的 14 个钩子全 Passed(含 `ui-golden` 像素回归、`cppcheck` 、编码规范、`bsp-err` 1/1/0);
`check_doc_links --all --sections-strict` 147 篇 0 问题;字节卫生复核:UI 头 BOM 仍在、无 CR、无 U+FFFD。 |
| | **顺手更正我自己写进文档的一条错话 ** (这类"听着很顺但没核"的说法最危险):我在 S-5 各条里把
`GD5F_SPI_TIMEOUT 100` 记成"**自旋圈数**(改分频时长会变)"。这次逐行核它的 12 处用法,**全部是
HAL_* 超时参数或 `HAL_GetTick()` 差值 → 它也是毫秒**。真问题降级为"**单位没写进名字**(缺 `_MS`
后缀),读代码必须回实现查单位",而不是"换个 CPU 就悄悄变慢"。`osWaitForever` 与 `HAL_MAX_DELAY`
同为 0xFFFFFFFFU、以及"`*_BUSY_TIMEOUT = -2` 是返回码"两条**仍然成立**(前者已核头文件,后者已改)。
本行以上那些**已提交的变更记录行仍写着旧说法**——按 §8.1 冻结历史不回改,更正以本行为准;
现状描述类条目(§0.4、`bsp_err.h` 、`pre-commit使用说明.md` §2.27、脚本文件头)已全部改准。 |
| **2026-10-23 ** | **S-5 第二波:C3 SD2506 整模块收编 + C1 GD5F/TPAFE 同形两义 + C2 dhara 失败类 ** (用户指定顺序 C3 → C1 → C2) | **C3 ** : `sd2506.h` 三枚私有宏(`SD2506_OK/ERROR/I2C_ERROR` )**删除而不是留别名**(留别名 = 新代码还能继续用旧形状,等于把迁移洗掉),11 个公共 API + 6 个内部 helper 改 `bsp_err_t` ;新增 `sd2506_err_from_hal()` , **把 I2C 超时从"和没人应答同一个 −2"里拆出来归成 `BSP_ERR_TIMEOUT` (可重试)**——SD2506 是启动链第一站、I2C1 上电时序敏感,这是本波最实在的一处语义改善;
`sd2506_init` 的 charge 寄存器**回读校验不符**原返 `SD2506_ERROR` (参数类)→ 改归 `BSP_ERR_IO` 。连带迁移 `App` ( `sys_clock.c` 、`app_main.c` )、板级 RTC 套件、
主机单测(`test/host/sys_clock/` 桩 + `run_unit_test.py` 补 `Drivers/BSP` include 路径)→ **单测 100/100 ** 。 |
| | **C1 ** : GD5F 的 `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_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` (**块池见底,重格式化只是把问题推回去**)、其余 `→ BSP_ERR_IO` (完整性,保卷 +
上报,不许自愈重格式化)——与 **E-31 ** 是同一条纪律的两端。可读名用自建短表:**本仓 dhara 没有编 `error.c` , `dhara_strerror()` 会直接链接失败**。`disk_read` /`disk_write`
仍回 `RES_ERROR` (那是 FatFs 契约),要让 defaultTask 卷健康探针**按类分支**还差一个跨调用存活的快照 → **C4 待还 ** 。口径 → `存储架构说明.md` §10.4。 |
| | **又一次"凭猜测就会改错代码"的自查 ** :我一度认定 `DBG_ERROR("... err=%d", err)` 是把结构体塞进 varargs 的格式 bug,准备顺手修——**核过 `Lib/dhara/error.h` : `dhara_error_t`
是 `enum` (标量)**,那行日志本来就合法。若没核实,我会把一条正常日志"修"成访问 `err.type` 而编译失败或语义错。 |
| | **验收(这一波有真验证面,和 RS485 试点不同) ** : `@build` **0 错 0 警告 ** ( `Code` 213,648、RO 18,860、`ER_IROM1` **232,508 B / 22.2% ** 、**RAM 三项零增量**);`@test`
PASS(681 ms)且逐条读到被改模块的锚点:`SD2506 init: OK (ret=0)` + ID/时间/温度/电池四条读回正常、`ADC init: OK` 、`NAND init: OK` 、`Map resumed: 14094/97504` 、
`adc=20803→28875` 帧计数持续递增(**证明 snapshot 三态语义没被改坏**)、`[ERR]/Fault/FAIL` 0 条;主机单测 100/100; `bsp-err` 基线 **19 → 17 ** 、`--prune` 干净;
`doc-links --all --sections-strict` 147 篇 0 问题;`layer-deps` 、`id-check --strict` 、`mem-numbers` 、`mem-budget` 、`task-table` 、`ui-golden` 、`registration-check` 全过。 |
| | 口径与数字联动:`TPAFE5160/README.md` 、`GD5F2GQ5UE_Test_Guide.md` 、`GD5F2GQ5UE_Trap_Records.md` (旧名均留痕,便于对旧日志);`存储架构说明.md` **新增 §10.4 ** ;
`AGENTS.md` 与 `pre-commit使用说明.md` §2.27 的基线计数 19 → 17;配图桶按 .map 对象级增量大改(**dhara/FTL 10,931 → 11,222 = +291 全在 `nand_ftl.o` **、应用代码/UI
+36 在 `app_main.o` 、其余散量入"其他"7,557,桶和 = `Code+RO` 断言通过);`ram_baseline` /`check_mem_numbers --write` /`ram_report_20261023.md` 快照与本文
§1.3/§5.1/§5.4、部署文档 §3.3 数字全部同步。**TPAFE5160/GD5F 两个对象的 Code 增量实测为 0**(纯改名,不产生一条指令)——这也是"改名不该被当成性能风险"的实证。 |
| **2026-10-23 ** | **S-5 起步:BSP 错误码契约 + `bsp-err` 门禁 + RS485 试点 ** (钩子 38 → **39 ** ) | 先纠本报告自己:原文「无统一错误码/超时/重试约定」**不准**,实测是**六套并存**——0/− 1( SD2506、NETCFG、nand_ftl)、自研枚举 `CH395F_STATUS_*` 、芯片寄存器原生码 `CH395F_ERR_*` (与前一套同模块并存)、`HAL_StatusTypeDef` ( `rs485.h` 曾直接外泄)、`bool` ,
再加第三方 dhara 的出参 `dhara_error_t` 与 FatFs 的 `FRESULT` 。超时侧是**同形两义**: `SD2506_I2C_TIMEOUT_MS 100` /`CH395F_SPI_BYTE_TIMEOUT_MS 100U` 是毫秒,`GD5F_SPI_TIMEOUT 100` 是
**自旋圈数 ** (改 SPI 分频或换主频,时长跟着变且无人报警),`GD5F_BUSY_TIMEOUT` / `TPAFE5160_BUSY_TIMEOUT = -2` 其实是**返回码**; `osWaitForever` 与 `HAL_MAX_DELAY` **同为 0xFFFFFFFFU **
(值同域不同 → 跨层转手既不报错也不变慢)。**契约真正值钱的是把「能不能重试」提到公共层**:
以前它只写在个别注释里(`gd5f2gq5ue.c` :「GD5F_BUSY_TIMEOUT(-2) - 超时(可重试)」),
语义在、跨模块不可判定。 |
| | 新增 `Drivers/BSP/bsp_err.h` ——**header-only、无 `.c` **(不占体积、不需双端注册):`0=BSP_OK` 、
11 个负值码、`BSP_ERR_CAN_RETRY(e)` 、`BSP_WAIT_NONE/FOREVER/MS` 与边界纪律。边界纪律明确三件事:
第三方码**就地映射不改名**(改名等于伪造语义);**`net_*` BSD 兼容层不纳入**(用户裁定:调用方
要的就是「返回字节数 + 每任务 errno 槽」那个形状,模拟器 Winsock 后端同形状);**lftpd 按第三方
排除**(它的 226/425/450/550 是 FTP 协议码,不是内部错误码)。 |
| | **也纠我自己写错的一条 ** :我一度在 `bsp_err.h` 里把 `gd5f2gq5ue_read/write/erase` 当「计数或错误挤
同一返回值」的反例——**核过实现与头注释,它们是「0=成功、其它=错误码」**,本层真正的 count-or-error
只有 `net_*` (已裁定不纳入)。原文已改正,并把「数量走出参」降级为**面向新 API 的原则**。 |
| | **试点 = RS485 ** : `rs485_transmit` / `rs485_receive_start` 不再外泄 `HAL_StatusTypeDef` ,改 `bsp_err_t`
+ 模块内 `static rs485_err_from_hal()` 映射(HAL 码不出模块),顺带补 `p_handle` 判空。
`@build` **0 错 0 警告 ** ( `Code` 213,436 → **213,484, +48 B ** ;RO 不变、**RAM 一字未动**)、`@test`
PASS( 702 ms)。**诚实标注**:这两个函数当前**没有生产调用点**(B2 冻结未接线),所以 `@test`
只证明「没破坏启动」,**不证明新错误码在运行时被正确使用**——下一个试点该挑在启动链上的模块
(建议 SD2506,41 处调用点)。 |
| | **门禁 `bsp-err` ** ( `scripts/check_bsp_err.py` ,三条规则:R1 不得拿 `HAL_StatusTypeDef` /`bool` 当错误
通道、R2 `*_TIMEOUT` 宏不得取负值、R3 非 void 公开 API 必须有「返回值:」)。**规则改窄而不是
拿基线洗规则**:第一版把 7 个正当谓词(`ui_..._is_active` / `has_children` )误判成违规,遂加
`is_/has_/can_/valid` 白名单。存量 **19 条**钉基线、只减不增(两个 `*_BUSY_TIMEOUT = -2` 、一个
`bool ui_content_page_open()` 、16 个缺注释)。**当场自证**:临时埋 3 条违规 → `命中 22(在册 19 /
新增 3) ` 、**RC=1**,三条规则各自点名并给出路;删掉探针 → `19/19/新增 0` 、RC=0。 |
| | **一条实测负结果 ** (值得记):`__attribute__((warn_unused_result))` **在 ARMCC v5 上不生效 ** ——
属性挂上、调用点裸调不接返回值,构建仍 0 错 0 警告(第一次实验我误用 `(void)` ,那本就是「故意
丢弃」的写法,重做了一次)。所以「返回值必须被消费」只能靠门禁脚本 + 书写约定「故意丢弃必须
写 `(void)` 」。 |
| | **pre-commit 的坑 ** :钩子跑之前会 **stash 未暂存改动 ** ——我先把配置 `git add` 了但没加 rs485,
钩子看到的树里 rs485 还是 `HAL_StatusTypeDef` ,于是**假报警 2 条**。口径:改代码 + 同时更新基线
时,**先把改动 `git add` 再跑钩子**。已写进 `pre-commit使用说明.md` §2.27。 |
| | 口径同步:`AGENTS.md` (钩子数 + 枚举 + 索引行)、`pre-commit使用说明.md` (新增 §2.27 + 计数 +
脚本清单行)、`并行任务工作树方案.md` 、本文 §0.1/§0.4/§7 与 §9 的 S-5 行。数字联动(`ram_baseline
--update/--write` 、`check_mem_numbers --write` 、FLASH 配图常量、本文 §1.3/§5.1/§5.4 与部署文档 §3.3)
随本波提交一并做。**待还**:两个 `*_BUSY_TIMEOUT = -2` 改名(波及板级套件与 Trap 记录用词,需真机
+ 套件回归)、FTL 坍缩点拆三类码(ECC / 介质 / 映射层坏块预算,让上层能区分「重试 / 标坏块 /
别再 mkfs」)、其余模块逐个迁移。 |
| **2026-10-22 ** | **S-1 只做「防扩大」那一半:新增 `layer-deps` 分层方向门禁 ** (推荐清单第 5 步拆分执行,钩子 37 → **38 ** ) | 上一轮的结论是「反转暂缓、门禁现在就该上」,这次照做。
为什么门禁比搬迁值钱:AGENTS 与分层图早就写着「六层单向依赖」,可从来没有机械检查——于是同一件事的记载从 2026-09 的
「NETCFG 一处」漂到今天的「8 文件 9 处」,全靠人读代码才发现。边界的静默腐蚀只能靠门禁止住。 |
| | **机制(与 `id-check` 的增量拦截同构) ** : `scripts/check_layer_deps.py` 扫 `Drivers/BSP/**.{c,h}` 的 include,按 `App/**`
头索引解析反向边,分三类——ALLOW 永久豁免 3 条横切叶子头(`console_log.h` 日志后端 / `sys_clock.h` 时间源 /
`sha256.h` 纯算法,任何层都要用、不依赖上层服务);BASELINE 钉住存量 6 条(`cfg_file.h` × 2、`vol_lock.h` × 1、
`sys_log.h` ×3);**新出现的组合直接 FAIL**。基线只减不增(脚本没有 `--write` ),`--prune` 在某条还清时点名「可删」,
于是「还欠几条」成为可见进度。挂 `files: ^Drivers/BSP/` 、`pass_filenames: false` (要跨文件建索引,恒扫全库)。 |
| | **当场自证 ** :落地时临时给 `Drivers/BSP/RS485/rs485.c` 加一行 `#include "cfg_file.h"` → 钩子当场 FAIL 并点名该文件与该头
(退出码 1),删掉后转绿。**没被触发过的门禁等于没有门禁。** |
| | **反转(5a)仍暂缓 ** :没有功能在等它,且要过「改 IP 免重启生效」整条真机链,留给下次真动 NETCFG/UI 时顺带做
(N-25 网络项、U5 页面扩展都会碰到)。目录搬迁(5b)同样暂缓:uvprojx 326 条 + IncludePath + CMakeLists + 42 个 UI 文件
+ 全库引用改写,收益只是「目录名好看」,而远程 CI 仍不跑 `@build` 。 |
| | **口径同步 ** :钩子数 37 → 38 共 6 处(`AGENTS.md` 两处、`docs/代码规范/pre-commit使用说明.md` 两处含新增 §2.26、
`并行任务工作树方案.md` 、本文 §0.1/§1.1);`项目架构总览.md` §1 分层原则处补「此方向已机械检查」。§0.4 的 S-1 计数由
「7 文件 15 处」更正为脚本口径的 **8 文件 9 处 ** 。本轮只加脚本 + 配置 + 文档,无 `.c/.h` 改动,故不重跑 `@build` 。 |
| **2026-10-22 ** | **S-2 第三步:SPI1 完成标志的回调归属拆清 ** (推荐清单第 4a 步) | 问题不是风格:HAL 的 SPI 完成/错误回调
是**全局唯一符号**,全工程只能有一份实现,当前由 CH395F 代管并按 `hspi->Instance` 分发——于是 GD5F 的
`g_spi1_dma_done` 被 CH395F 跨模块裸 `extern` 直接写,**GD5F 出错时的"释放等待者"寄居在别人的 .c 里**。
谁哪天做"CH395F 清理",把那两个 `else if (SPI1)` 分支当无关代码删掉:**编译零报错**,GD5F 的 DMA 等待
在出错时只能干等 `GD5F_SPI_TIMEOUT` 兜底。
| | **改法(时序一字不动,只换所有权) ** :标志收为 GD5F 文件内 `static s_spi1_dma_done` ; GD5F 导出
`gd5f_spi1_notify_done()` / `gd5f_spi1_notify_error()` (头文件里写明"SPI1 分支必须转调这两个函数");
CH395F 删掉裸 `extern` 、include `gd5f2gq5ue.h` 、两个 SPI1 分支改为转调并就地注明"勿删"。
`HAL_SPI_AbortCpltCallback` ( `HAL_SPI_DMAStop()` 会触发)仍无人实现,**本次刻意不做**——它会改变中止
语义,属真机专项,当前超时兜底可用。
| | **体量代价 ** : `ch395f.o` − 8 B、`gd5f2gq5ue.o` +24 B → `Code` 213,188 → **213,204 B( +16 B) ** ,
RO/RW/ZI 全部不变,`ER_IROM1` 231,768 → **231,784 B( 22.1%) ** 。16 B 换一个静默失效类,划算。
数字联动一并做掉:`data/ram_report_20261022.md` 、`data/stack_report_20261022.md` 重跑;`ram_baseline
--update/--write` ; `check_mem_numbers --write` ;三张配图常量更新后重新生成(逐文本按字号与锚点复查
越界 = **0 ** );本文 §1.3/§5.1/§5.4 与 `docs/环境与部署/项目部署环境要求.md` §3.3 的 Flash 数字同步。
| | **真机验证 `@test` PASS ** :无 `[ERR]` /Fault,复位原因 software; `[STACK] heap free=15800
min_ever=8464` **与改前逐项相同**;各任务实测峰值 default 2152 / net 1116 / ftp 764 / ui 4052 /
timesync 616 / adc 952 / iec104 540 / protect 624, **静态最坏链全部未变**( 3024/1912/1456/4052/1264/
1320/1456/872)→ 9/9 PASS。⚠ 记录一条读数陷阱:测试脚本打印的"boot to net init: OK = 7,907 ms"是
采集窗口锚点造成的度量偏差,真机日志里 `boot: syslog init OK → net init: OK` 实测 ≈699 ms,与改
前一致,**不是性能回退**。 |
| **2026-10-22 ** | **S-2 第二步:19 处任务句柄裸 extern 收拢成引用契约 ** (推荐清单第 3 步) | 新建 `App/task/task_handles.h` ——**只有声明、没有 `.c` **:头文件不需要双端注册,`../App/task` 本来就在 uvprojx 的 `<IncludePath>` 里,所以这一步零构建面。`default_task.c:52-69` 的 18 行 + `adc_task.c:125` 的 1 行局部 extern 全部删除改 include(栈水位表本体仍留在 `default_task.c` );同批把 `Src/main.c` 函数内的 `extern volatile uint32_t g_rcc_csr_latched;` 移进 `App/app/app_main.h` (该文件 `main.c` 本来就 include),
USER CODE 区内只留「读 RCC_CSR → 写 RMVF 清除」两句、顺序约束的原注释保留。**价值**:原先每加一个任务都得记得往 `default_task.c` 再抄两行 extern,抄漏一行 = 读别人的栈(静默);现在只有一个改动点,且 CubeMX 若把句柄改名会在链接期报错而不是静默通过。**镜像逐项体积不变**(`Code=213,188 RO=18,580 RW=1,184 ZI=131,504` , `@build` 0 错 0 警告)证明行为中性。`check_cubemx_survival.py` 对这次有意的 USER CODE 区改动报「漂移」,已按其文档流程 `--write` 重建基线(`.cubemx_survival_baseline.json` 仅 1 行变化),`--check` 复跑通过、易失清单 29 项全部到位;顺带把 §0.4 的 S-2 口径改成精确分类:**手写该修 24 处(已消 22)+ CubeMX 生成块 16 条(不算债)+ `__asm` 汇编标签 4 条(合法例外)**——上一版「25 处」混了这三类。 |
| **2026-10-22 ** | **结构债 S-4 闭环 + S-2 第一步(用户批准「先只跑 1+2」) ** | **S-4 ** :删除 `App/app/app_cfg.h` 里 4 个**零消费点**宏(`APP_NET_ENABLE` /`APP_ADC_ENABLE` /`APP_RS485_ENABLE` /`APP_SYS_TIME_SRC` ),文件降格为「决策占位说明」——修改记录 v1.2 写明为什么删与将来怎么接回;并去掉 `app_main.c` 里只为它存在的 include。留「看着能裁剪、其实不生效」的宏比不留更糟:它不产生任何编译期效果,却让评审误信模块可裁剪;此前为了说明它,AGENTS 之外还得在 6 处文档各写一句「未接线占位,别当开关用」。同步改口径 **17 处 ** : `App/app/README.md` 、`App/README.md` 、根 `README.md` 、`项目架构总览.md` 、`项目部署环境要求.md` (§3.2 两行 + 配置表一行)、`产品需求设计文档.md` ( B0 结论 + 配置表)、`基线与待确认.md` 、`排期详述.md` C5、`登记簿.md` N-23 残余、`RAM使用分析与优化方案.md` 三处。
| | **顺带纠出另一个不存在的宏 ** : `RAM使用分析与优化方案.md` 把 `APP_CCM_ENABLE` 写成「加在 `App/app/app_cfg.h` 」,实际从来没有落地(全库仅该文档提及)——已就地标注「未实现,重开本簇时一起落地」,避免下一个读者以为 CCM 有回退开关。
| | **S-2 第一步(实测纠偏) ** :删掉 `gd5f2gq5ue.c:47` 、`sd2506.c:20` 两处与 `Inc/spi.h` /`Inc/i2c.h` 重复的 `extern` , GD5F 改 `#include "spi.h"` (沿用 `ch395f.c:22` 的既有做法)。**镜像逐字节不变**:`Code=213,188 RO=18,580 RW=1,184 ZI=131,504` 与改动前完全相同,`@build` 0 错 0 警告 → 证明这两步是行为中性的。同时把 §0.4/§9 的旧口径换成实测值:**S-2 不是 4 处而是 25 处**( `App/task/default_task.c:52-69` 一次就有 18 行;`stm32f4xx_it.c` 的 `fault_entry_hard` 属 ARMCC `__asm` 合法例外,不该「修」),**S-1 不是语义错位而是真实反向依赖**( NETCFG include `App/cfg/cfg_file.h` 10 处 + UI 3 个文件)。**这两个旧数字是我 v3.0 重写时沿用未测,一并认错。** SPI 回调归属这条隐患(CH395F 替 GD5F 释放错误路径)目前只在本文 §0.4 留了痕,**尚未进登记簿**——要不要立项等你定。 |
| **2026-10-22 ** | **执行 §5.4: F-6 落地、F-4 判定不做、F-5 降级观察项 ** (登记簿 N-28 同步改写) | 动手前逐条对 `.map` 与源码核(本报告"建议栏不可照抄"总口径的又一次复现——三处误判全部出在**建议栏**,"发现"栏字节数全对)。**F-6 ✅**:`App/algo/prot_set.h` 两个 Q10 宏去掉显式 `(double)` 、`1024.0` →`1024.0f` → `@build` 0 错 0 警、Code 213,520→**213,188(−332 B) **; `.map` 中 `dflti` 、`d2f` 、`f2d` 、`dfixi` 、`fepilogue` 共 300 B 消失,自研侧不再引用任何双精度 helper。**原报告"来自 `printf` 的 `%f` "是误判**:全库 0 处 `%f` ,剩余 `dadd` +`dmul` +`ddiv` +`depilogue` +`dfixul` = 1,018 B 由 `mc_w.l(printfa.o)` 的 `_printf_core → _fp_digits` 无条件引用,MicroLIB 层面删不掉。**数值等价用穷举证明**(宏内 42 个字面量 + `int32` 全域 ÷1024 往返 4.49×10⁶ 例零差异;×1024 方向 2×10⁵ 例零差异);回归 = `prot_set` 90/90、`prot_elm` 47/47、`iec104` 97/97 主机单测全过。**F-4 ❌ 判定不做**:五项字节数逐字节仍准(合计 10,060 B)但**无一项有开关**,根因是 `lv_draw_sw.c` 的 `execute_drawing()` 无条件 switch 引用全部 task 类型;唯一总开关 `LV_DRAW_SW_COMPLEX` 被 `ui_deco.c` 的"浅 V"3 px 粗斜腿锁死(**2026-09-03 已实测失败并两端固化 =1**);**原"mask/grad 是文字必经路径"的理由同时证伪**(字形走 blend 层 `mask_buf` ,不经 `lv_draw_sw_mask_*` )。**F-5 🟡 降级观察项**: uvprojx 实测 326 个 `.c` / LVGL 201 个 / `.map` 零产出 **127 个 ** ,但增量构建实测 4~5 s、远程 CI 刻意不跑 `@build` ,原"−40%"无基线记录。同步更正 §0.1 Flash 行、§0.4 Flash 行、注册表 uvprojx 行、§1 口径行、§5.1 快照注与要点行、§6.4 LVGL 占比分母、「结构债」波次行;台账受管区与内存快照已 `--write` 回填(`mem-budget` / `mem-numbers` 转绿)。**`@test` 台架 PASS**(固件戳 `fw build: Oct 22 2026 10:36:15 suite=NONE dstr=off` ;无 `[ERR]` /`ERROR` /`FAIL` /Fault, 708 ms 到 `net init: OK` , `protect task start` 、`i104: listening on 2404` 、`[STACK]` 三面水位齐;`adc` /`phasor` /`freq` 心跳持续) |
| **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` ) |
| **2026-10-23 ** | **N-15 由"待评估"改判"判定不做":补上页缓冲可合并性论证 ** | 报告此前把 N-15( `nand_ftl.o` 6,216 B、三块 2 KB 页缓冲)写成"与 R-3 同批评估",但**评估本身从来没写过**——`nand_ftl.c:300` 的注释直接引用了这份不存在的"报告 R-3"。本次补齐(论证落 `../内存管理/存储栈RAM优化调研.md` §5):① `.map` 实证 6,216 = RW 16 + ZI 6,200,三块缓冲 6,144 B 占 98.8%;② **不可合并 ** ——`dhara_map_write()` 先 `auto_gc()` ( `map.c:327` )再落数据,回收里的 `dhara_nand_copy()` 与"待写的 `s_cache_buf` ""攒了 16 页的 checkpoint 元数据 `s_page_buf` "三方同时存活,任两块合并都是**静默错数据而非报错**;`s_page_buf` 也不能缩(元数据跨 0..1,999 偏移,编程的是整页);③ 唯一可动的是 `s_copy_buf` :分块 512 B(−1,536 B,代价是 4 次 CPU 轮询)或内部搬页 copy-back(−2,048 B,须实证手册"奇偶属性相同"限制、并先给主机侧假芯片补 internal-data-move 语义);④ **省下来也没用 ** :三块都在 `RW_IRAM1` (余 55,808 B),红线是 `RW_CCM` (余 7,904 B),做满也不减红线,搬进 CCM 反而吃掉其 78% → 暂缓不变、重开仍按 §5.3 三条条件。顺带订正 `nand_ftl.c:405` 一句错注释(原写 `s_page_buf` "仅装 132 B 走轮询",实际按整页 2,048 B 编程、同样走 DMA,会误导 P1-5② 迁 CCM)。**仅注释 + 文档,镜像零变化**(`@build` 0 错 0 警告,Code/RO/RW/ZI 逐项不变) |
### 已销项条目的记录去处
> 删除前已逐条核对:以下去处文档/代码均含完整记录,删了不丢信息。
| 已销项条目 | 去处 |
|-----------|------|
| N-01(配置引擎 C 堆必失败) | `../子系统设计/配置文件设计说明.md` §6.2 / §8 |
| N-02( FTP 无鉴权,登记簿 N-19) | `../子系统设计/网络配置说明.md` §3.4( `[ftp] user/pwd_hash/write` 、恒定时间比较、5 败锁 60 s、出厂默认口令告警、明文嗅探边界)+ `../产品需求/产品需求设计文档.md` §FTP 鉴权 + `Drivers/BSP/NET/lftpd/lftpd_auth.c` + 主机侧 `test/host/sha256/` 、`test/host/ftp_auth/` |
| N-03a/N-03b/N-07/N-08( IWDG、`configASSERT` 、两个钩子、4 个 Fault 现场) | `../子系统设计/看门狗设计说明.md` (§4.4、§10.1/§10.2) + AGENTS.md「平台约束」 |
| N-03c( `Error_Handler` 死循环,登记簿 **N-20 ** , 2026-10-15 已销项) | `../子系统设计/看门狗设计说明.md` §4.4(统一降级:1 条自检 + `NVIC_SystemReset()` 、三条实现约束含 `__builtin_return_address` 不可用的实证、内核前日志路径安全性、重生成存活三层)+ 实施代码 `Src/main.c` 的 `USER CODE Error_Handler_Debug` 区注释 + `scripts/check_cubemx_survival.py` 审计项 |
| N-06 主体(`crc.c` /`ringbuf.c` /`lftpd_log.c` 删除、`adc_task.c` 注册)+ S-3(注册反向校验,登记簿 N-23) | `scripts/check_registration.py` ( `KNOWN_GAPS` 白名单 + `--strict` ) + `../代码规范/pre-commit使用说明.md` ( `registration-check` );`lftpd_log.c` 并入 `Drivers/BSP/NET/lftpd/lftpd_auth.c` |
| **N-11 ** ( NAND 直 DMA 调用方缓冲 / CCM 迁移硬边界,登记簿 **N-26 ** , 2026-10-15 已销项) | `../内存管理/内存内容分配策略.md` §3(全板 DMA 使用方清点)+ §4 规则 R1~R4(**R3 堆/栈禁为 DMA 源/目的**,含"缓冲自身地址无法编译期断言"这条边界)+ §8 落点索引;代码三层 = `gd5f2gq5ue.h:96-107` 编译期窗口断言 → `nand_ftl.c:241-243` 启动期自检 → `gd5f_dma_buf_ok()` 运行时强制;负路径用例 TC-GD5F-704/705 |
| N-10( UI accept 门控) | `../UI设计/RemoDispBus协议与接收设计说明.md` §3 + 看门狗设计说明 §11.4 |
| N-17(栈/堆水位观测)+ R-8(观测常开) | `../内存管理/RAM使用分析与优化方案.md` + `../测试说明/实机测试实施指南.md` ( `[STACK]` 水位 + `stack_report.py` + `ram_report.py` ) |
| N-18(本报告口径:无 CI) | `../测试说明/测试开发标准流程.md` §9 + `../CI/README.md` |
| P2-5 / P2-6( SD2506 负温度解析、WRTC 解锁顺序) | `Drivers/BSP/SD2506/sd2506.c` (符号扩展 `val \| 0xF0` ; WRTC1→WRTC2/3 顺序按手册,上电即写禁止态) |
| P2-8( `net_recv` 30 s 阻塞超时) | `Drivers/BSP/NET/net_config.h` 头注(2026-10-13 recv 语义主流化:删除 `NET_RECV_TIMEOUT_MS` ,预算由调用方显式给)+ `../网络/Net层阻塞模型架构设计.md` |
| P2-9( `mkdir` 失败不再重试) | `App/log/sys_log_mcu.c` ( `s_fs_ready` 幂等 + `mkdir /log fail (%d), will retry` + 上报自检) |
| P2-13 / P2-14 / P2-15 | AGENTS.md( `test-suite-switch` 、`keil-build` 、`stlink-flash` ) + `../测试说明/ST-LINK_CLI_复位使用说明.md` |
| D-2 / D-3 / D-4 / D-5 / D-6 / D-7 | `../文档规范.md` + `../代码规范/pre-commit使用说明.md` ;断链由 `scripts/check_doc_links.py` 门禁接管 |
| U4 主体(golden 回归) | `simulator/tests/` 四脚本 + `../UI设计/UI_自动化测试流程.md` (残余 = 挂门禁,见 §6.3) |
| R-1 / R-7 | `../子系统设计/看门狗设计说明.md` + AGENTS.md 任务表(`scripts/gen_task_table.py` 生成区) |
| §5.5 前三条(92% 门禁 / 趋势基线 / 预算台账,登记簿 N-27 前半) | `../内存管理/RAM预算台账.md` (阈值与配额的**唯一权威**) + `scripts/ram_baseline.py` ( `--gate/--update/--trend/--write` ) + `../代码规范/pre-commit使用说明.md` ( `mem-budget` ) |
| **F-4 判定不做 / F-6 已落地(2026-10-22,登记簿 N-28) ** | 实测复核与"为什么关不掉"记在本报告 §5.4;`LV_DRAW_SW_COMPLEX` 必须为 1 的约束本身在 `../UI设计/UI_设计架构.md` §1.4 第 7 条与 `../UI设计/UI_界面制作流程.md` 陷阱表;宏改动与等价性论证在 `App/algo/prot_set.h` 的 Q10 注释;MicroLIB `snprintf` 拖入 `_fp_digits` 的取证方法(在 `.map` 的 Section Cross References 区反查 `refers to mf_w.l(d*.o)` )见 §5.4 F-6 行 |
| 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 原「预算与内存」的第一步,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` 注释 |
| * * §4.8 两处空白(①无覆盖率口径 ②无 `malloc(` 正则)+ §5.5 第 5 条整条(2026-10-23 全部销项)** | `../测试说明/主机单测覆盖率口径.md` (口径 / 工具链实测 / 统计面 / 门禁姿态 / **九条限制与盲区 ** / 未挂钩子的三条理由与待粘 YAML)+ `scripts/check_test_coverage.py` ( `--list` / `--module` / `--update-baseline` / `--md` / `--json` ) + 基线台账 `scripts/test_coverage_baseline.json` (首登 2552/2893 行、未触及 125 文件)。
空白 ② → `docs/门禁说明/coding-standard.md` ( CODE-RULE-20 判据 / 唯一豁免 / 盲区 / 落地前普查数)+
`scripts/check_coding_standard.py` ;禁令本身的事实源仍是 AGENTS.md 平台约束与
`../内存管理/RAM使用分析与优化方案.md` §4.2.1;另 `../代码规范/嵌入式C语言代码规范_V1.0.md` §6.1 的示例
自己写着 `malloc/free` (通用 C 模板遗留),已同日在示例下加"本仓禁止"批注 |