diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index 327a1cd2..06e694af 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -5,9 +5,13 @@ > `data/ram_report_20261022.md` / `data/stack_report_20261022.md` 两份新快照。 > 变更:2026-10-23 补 `data/ram_report_20261023.md` 快照(S-5 试点:RS485 改报 `bsp_err_t`, > `Code` 213,436 → **213,648**(S-5 全波 +212:契约试点 +48、C3 SD2506 +44、C1 GD5F/TPAFE +36、 -C2 dhara 失败类 +84;RO 另 +208,全是 C2 的失败类名表)→ `ER_IROM1` **232,536 B / 22.2%**; +C2 dhara 失败类 +84;RO 另 +208,全是 C2 的失败类名表)→ `ER_IROM1` 当日 **227.1 KB / 22.2%**(字节值见 `data/ram_report_20261023.md`,正文不留会过期的裸 B 值); > **RAM 全部字段零增量**;受管表已由 > `check_mem_numbers.py --write` 同步,配图常量同步)。 +> 变更:2026-10-24 合入 `task/cov-host-line-coverage`(覆盖率口径 §9 的 P1 全批)——RAM **+8 B** +> (`net_cfg.c` 为 WAIT 契约修复新增 `s_save_busy` 标志)、Flash **−24 B**(`lftpd_string` 改名并删掉 +> 永不执行的尾循环、`net_select` 去掉冗余的 `timeout_ms > 0` 守卫),CCM 零增量;受管表与本文现状数字 +> 已按合并后的 `.map` 同步(RW_IRAM1 75,272 / ER_IROM1 232,512 / RW_CCM 57,376 / Flash 233,704)。 > 变更:v1.2 数据现状全量同步——§2.1/§2.2/§5/§6/§8.6 等"现状"数据全部更新为 2026-09-21 lftpd 静态化后的最新 map 与实测值;清除 2026-09-11 历史口径残留(历史快照保留于 `data/`) > 适用工程:STM32F4-Base(STM32F407ZGTx @168MHz,FreeRTOS V10.3.1 + CMSIS-RTOS2) > 数据来源:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(`@build` 重建)+ `scripts/ram_report.py`(静态台账)+ `scripts/stack_report.py`(静态调用图 × 运行时栈水位)+ 源码静态分析 + 网络调研 @@ -25,7 +29,7 @@ C2 dhara 失败类 +84;RO 另 +208,全是 C2 的失败类名表)→ `ER_IR - F407 另有 **64KB CCM RAM**:**已于 2026-09-21 启用**并把 FreeRTOS 堆迁入(§4.1.2) - 起步时没有任何运行时测量手段:不知道每个任务实际用了多少栈,改栈全靠拍脑袋 -当前状态(**以 §2.1 受管快照为准**,由 `.map` 自动生成;2026-10-22 `@build`):RW_IRAM1 **75,264 / 131,072 B(57.4%)**、RW_CCM **57,376 / 65,280 B(87.9%)**。✅ **任务栈:`stack_report.py` 9/9 PASS**(2026-10-22 静态最坏链 + 同日真机 `[STACK]` 水位,`defaultTask` 最坏 3024B/5120(余 40%)、`protectTask` 872B/2048(余 57%),见 `data/stack_report_20261022.md`);**卡点已从 SRAM1 移到 CCM**(余 7,904 B)。历史:`defaultTask` 曾于 2026-10-04 达 3664B/4096 ≈10%,登记为 S-1,2026-10-10 削链后销项(详见"2026-10-10 复测"小节)。 +当前状态(**以 §2.1 受管快照为准**,由 `.map` 自动生成;2026-10-24 `@build`):RW_IRAM1 **75,272 / 131,072 B(57.4%)**、RW_CCM **57,376 / 65,280 B(87.9%)**。✅ **任务栈:`stack_report.py` 9/9 PASS**(2026-10-22 静态最坏链 + 同日真机 `[STACK]` 水位,`defaultTask` 最坏 3024B/5120(余 40%)、`protectTask` 872B/2048(余 57%),见 `data/stack_report_20261022.md`);**卡点已从 SRAM1 移到 CCM**(余 7,904 B)。历史:`defaultTask` 曾于 2026-10-04 达 3664B/4096 ≈10%,登记为 S-1,2026-10-10 削链后销项(详见"2026-10-10 复测"小节)。 本方案三个目标: @@ -43,10 +47,10 @@ C2 dhara 失败类 +84;RO 另 +208,全是 C2 的失败类名表)→ `ER_IR | 区域 | 用量 | 容量 | 占比 | |---|---|---|---| -| ER_IROM1 | 232,536 B (227.09 KB) | 1,048,576 B (1024 KB) | 22.2% | -| RW_IRAM1 | 75,264 B (73.50 KB) | 131,072 B (128 KB) | 57.4% | +| ER_IROM1 | 232,512 B (227.06 KB) | 1,048,576 B (1024 KB) | 22.2% | +| RW_IRAM1 | 75,272 B (73.51 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 合计)** | **233,724 B (228.2 KB)** | 1,048,576 B (1024 KB) | **22.3%** | +| **Flash (ROM 合计)** | **233,704 B (228.2 KB)** | 1,048,576 B (1024 KB) | **22.3%** | 各区的容量与常量来源: diff --git a/docs/内存管理/RAM预算台账.md b/docs/内存管理/RAM预算台账.md index 8a7fa378..7e59bd4c 100644 --- a/docs/内存管理/RAM预算台账.md +++ b/docs/内存管理/RAM预算台账.md @@ -15,14 +15,14 @@ | 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) | |---|---|---|---|---| -| ER_IROM1 (Flash) | 232,536 B | 1,048,576 B | 22.2% | 参考 | +| ER_IROM1 (Flash) | 232,512 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 | 75,264 B | 131,072 B | 57.4% | PASS | +| RW_IRAM1 | 75,272 B | 131,072 B | 57.4% | PASS | | 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 | |---|---|---|---| -| 网络 | 13,694 B | 12 KB | ⚠ 超 1.4 KB | +| 网络 | 13,698 B | 12 KB | ⚠ 超 1.4 KB | | 存储 | 7,056 B | 12 KB | OK | | UI | 30,422 B | 16 KB | ⚠ 超 13.7 KB | | RTOS 堆 | 57,376 B | 56 KB | 堆=配额 | diff --git a/docs/测试说明/主机单测覆盖率口径.md b/docs/测试说明/主机单测覆盖率口径.md index 1677e8c7..4fb83229 100644 --- a/docs/测试说明/主机单测覆盖率口径.md +++ b/docs/测试说明/主机单测覆盖率口径.md @@ -316,11 +316,11 @@ 两处"现状桩"翻成正向契约,另新增 `TC-NETSEL-012c`;全量 22 套件仍然全绿。 ### 假 CH395F 芯片:实施规格(2026-10-23 立项,交给下一会话执行) -**为什么要它**:@@NET@@ 域剩下的三块(@@net_pending.c@@ 287、@@net_events.c@@ 263、@@net_listener.c@@ 191 = 741 行) +**为什么要它**:`NET` 域剩下的三块(`net_pending.c` 287、`net_events.c` 263、`net_listener.c` 191 = 741 行) 全都把芯片当真理读,真机上这些语义只在总线上一闪而过、台架不可复现。做完它,P2 的 -@@net_socket.c@@(1,251 行,全仓最大逻辑块)也一起解锁。 +`net_socket.c`(1,251 行,全仓最大逻辑块)也一起解锁。 -**真实原型(@@Drivers/BSP/CH395F/ch395f.h@@ 逐条核出,假件必须一字不改地照抄签名)**: +**真实原型(`Drivers/BSP/CH395F/ch395f.h` 逐条核出,假件必须一字不改地照抄签名)**: ```cpp uint8_t ch395f_sock_state_cached(uint8_t sn); /* 返回 ch395f_sock_state_t 枚举值 */ @@ -341,43 +341,43 @@ uint16_t ch395f_link_change_count(void); ``` **假芯片必须复刻的两条芯片怪癖**(不复刻就等于把被测代码的坑假件掉): -1. @@get_sock_int_status@@ 是**整体读即清**、芯片没有按位清除手段。假件要按"每次调用 +1. `get_sock_int_status` 是**整体读即清**、芯片没有按位清除手段。假件要按"每次调用 返回该 socket 的挂起位图并**同时清空**"实现,并允许逐 socket 独立预置——否则 照不出 AGENTS「模块约定」里 B3 那条实证坑:*"顺手全量清一遍"等于把别的在用 Socket 的门焊死*。 -2. 状态是**缓存值**而非现场值(@@sock_state_cached@@/@@link_up_cached@@):假件要把 +2. 状态是**缓存值**而非现场值(`sock_state_cached`/`link_up_cached`):假件要把 "缓存更新发生在哪一次调用之后"做成可脚本化,才能测出对账循环读到陈旧状态的路径。 -**脚本化模型**(沿用本仓既有假件风格:@@nand_port@@ 的真头+假 diskio、@@lftpd_io@@ 的可注入假堆): +**脚本化模型**(沿用本仓既有假件风格:`nand_port` 的真头+假 diskio、`lftpd_io` 的可注入假堆): 每个读函数一条**按调用序号取的返回队列**(耗尽后回落到最后一值),每个写函数一条 -**调用记录**(次数 + 最近一次入参),另给 @@g_chip_call_count[函数名]@@ 让"这一轮不该碰芯片" -的契约可断言;@@reset@@ 一次全清。所有假件写在测试单元 @@.c@@ 里,**不建共享 @@.so@@/库**。 +**调用记录**(次数 + 最近一次入参),另给 `g_chip_call_count[函数名]` 让"这一轮不该碰芯片" +的契约可断言;`reset` 一次全清。所有假件写在测试单元 `.c` 里,**不建共享 `.so`/库**。 -**桩放哪里(一个需要拍板的点,先给推荐)**:三个模块都要用同一份假芯片,@@test/host/*/stub/@@ +**桩放哪里(一个需要拍板的点,先给推荐)**:三个模块都要用同一份假芯片,`test/host/*/stub/` 每模块一份会漂移(三处签名各改一半 = 假件与被测代码同时错还不报错)。推荐 -@@test/host/common/ch395f_fake.h + .c@@(与 @@unit_test_harness.py@@ 同级,三模块的 -@@include_dirs@@ 各加这一条),代价是它不进 MCU 构建、要靠 @@test-cppcheck@@ 覆盖。 -若沿用"每模块自带桩"的现有惯例,则必须让三份桩都 @@#include@@ 同一份头以锁死签名。 +`test/host/common/ch395f_fake.h` 与同名 `test/host/common/ch395f_fake.c`(与 `unit_test_harness.py` 同级,三模块的 +`include_dirs` 各加这一条),代价是它不进 MCU 构建、要靠 `test-cppcheck` 覆盖。 +若沿用"每模块自带桩"的现有惯例,则必须让三份桩都 `#include` 同一份头以锁死签名。 **三个文件的真实依赖面(逐个核出,别按猜的排工作量)**: -- @@net_listener.c@@(191 行)—— **最便宜,先做它**:@@net_drain_chip_rx@@/@@net_relisten_locked@@/ - @@net_relisten_done@@ 全是**本文件内的 static**(2026-10-23 实测确认),所以它只需要 - 芯片假件 + @@net_get_sock@@ + @@net_pending_active@@ + @@HAL_GetTick@@ + @@dbg_log@@, - 外加 @@test_config.h@@/@@net_latency_test.h@@ 两个可空壳的头。**不需要假造 net_socket.c 内部函数** +- `net_listener.c`(191 行)—— **最便宜,先做它**:`net_drain_chip_rx`/`net_relisten_locked`/ + `net_relisten_done` 全是**本文件内的 static**(2026-10-23 实测确认),所以它只需要 + 芯片假件 + `net_get_sock` + `net_pending_active` + `HAL_GetTick` + `dbg_log`, + 外加 `test_config.h`/`net_latency_test.h` 两个可空壳的头。**不需要假造 net_socket.c 内部函数** (我上一轮把它当成前置成本,是错的,特此更正)。判据现成:单连接 socket 复用、 - 按网络代次重建监听、@@net_listener_armed@@ 门控(@@docs/网络/网络自愈架构设计.md@@ §4.10)。 -- @@net_pending.c@@(287 行)—— 要 @@net_listener_request@@(可用一个只记录调用的假件), - 契约是挂起 recv 的注册/超时/取消与"写失败回置"那族;@@tick@@ 收 @@net_sock_t *@@ 表参数 - (@@net_get_sock@@ 对未用槽位返 NULL,tick 需读 @@in_use == 0@@ 兜底 —— AGENTS 已记)。 -- @@net_events.c@@(263 行)—— 依赖最多(@@net_sock_raw@@/@@net_listener_settle@@/ - @@net_pending_cancel@@/@@net_send_pending_cancel@@/@@xTaskGetTickCount@@),**放最后**。 - 注意 @@net_sock_raw@@ 是刻意不给应用层用的现场指针(头注释写明"仅供 net_events.c 的 + 按网络代次重建监听、`net_listener_armed` 门控(`docs/网络/网络自愈架构设计.md` §4.10)。 +- `net_pending.c`(287 行)—— 要 `net_listener_request`(可用一个只记录调用的假件), + 契约是挂起 recv 的注册/超时/取消与"写失败回置"那族;`tick` 收 `net_sock_t *` 表参数 + (`net_get_sock` 对未用槽位返 NULL,tick 需读 `in_use == 0` 兜底 —— AGENTS 已记)。 +- `net_events.c`(263 行)—— 依赖最多(`net_sock_raw`/`net_listener_settle`/ + `net_pending_cancel`/`net_send_pending_cancel`/`xTaskGetTickCount`),**放最后**。 + 注意 `net_sock_raw` 是刻意不给应用层用的现场指针(头注释写明"仅供 net_events.c 的 对账循环使用"),假件要保留"对未在用槽位也能拿到项"的行为,否则对账兜底路径测不到。 -**验收顺序**:@@net_listener@@ 绿 → 立刻登记基线(哪怕只有它一个)→ @@net_pending@@ → @@net_events@@; -每步都要 ```build 0/0 + 21+ 套件全绿 + --update-baseline + 补建 @@TC-HOST-*@@ 族@@(族数现在 16, -三块做完应为 19),并且**先补族再往文档里引这些号**,否则 @@id-check@@ 会按台账外新账卡提交 -(本会话已被 @@TC-NETCFG-028@@ 实测拦过一次)。 +**验收顺序**:`net_listener` 绿 → 立刻登记基线(哪怕只有它一个)→ `net_pending` → `net_events`; +每步都要 @build 0/0 + 全部套件绿 + --update-baseline + 补建 `TC-HOST-*` 族(本规格落地时族数 +16 → 22、套件 16 → 28,实测见上方落地进度),并且**先补族再往文档里引这些号**,否则 `id-check` 会按台账外新账卡提交 +(本会话已被 `TC-NETCFG-028` 实测拦过一次)。 ### P2|值得但工程量大(5 个 `.c` = 2,226 行) diff --git a/scripts/ram_baseline.tsv b/scripts/ram_baseline.tsv index 7d55ad18..f4af8360 100644 --- a/scripts/ram_baseline.tsv +++ b/scripts/ram_baseline.tsv @@ -6,3 +6,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi 2026-10-20 213356 18580 1184 131480 231936 22.1 75240 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736 2026-10-22 213436 18652 1188 131500 232088 22.1 75264 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736 2026-10-23 213676 18860 1188 131500 232536 22.2 75264 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736 +2026-10-24 213652 18860 1192 131504 232512 22.2 75272 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736 diff --git a/test/host/ftp_cfg/run_unit_test.py b/test/host/ftp_cfg/run_unit_test.py index 9a70a642..0764852f 100644 --- a/test/host/ftp_cfg/run_unit_test.py +++ b/test/host/ftp_cfg/run_unit_test.py @@ -49,4 +49,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/iec104_serv/run_unit_test.py b/test/host/iec104_serv/run_unit_test.py index afc448d3..9a55713f 100644 --- a/test/host/iec104_serv/run_unit_test.py +++ b/test/host/iec104_serv/run_unit_test.py @@ -74,4 +74,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/lftpd/run_unit_test.py b/test/host/lftpd/run_unit_test.py index 5672f3cd..96f57134 100644 --- a/test/host/lftpd/run_unit_test.py +++ b/test/host/lftpd/run_unit_test.py @@ -59,4 +59,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/lftpd_inet/run_unit_test.py b/test/host/lftpd_inet/run_unit_test.py index e182379d..130541e2 100644 --- a/test/host/lftpd_inet/run_unit_test.py +++ b/test/host/lftpd_inet/run_unit_test.py @@ -53,4 +53,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/lftpd_io/run_unit_test.py b/test/host/lftpd_io/run_unit_test.py index 7ef05171..e8f6096e 100644 --- a/test/host/lftpd_io/run_unit_test.py +++ b/test/host/lftpd_io/run_unit_test.py @@ -60,4 +60,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/net_cfg/run_unit_test.py b/test/host/net_cfg/run_unit_test.py index b464d53c..b608cc4f 100644 --- a/test/host/net_cfg/run_unit_test.py +++ b/test/host/net_cfg/run_unit_test.py @@ -57,4 +57,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/net_events/run_unit_test.py b/test/host/net_events/run_unit_test.py index beba0e8c..ddb685fb 100644 --- a/test/host/net_events/run_unit_test.py +++ b/test/host/net_events/run_unit_test.py @@ -57,4 +57,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/net_listener/run_unit_test.py b/test/host/net_listener/run_unit_test.py index 2a1c936e..d6ca12e5 100644 --- a/test/host/net_listener/run_unit_test.py +++ b/test/host/net_listener/run_unit_test.py @@ -59,4 +59,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/net_pending/run_unit_test.py b/test/host/net_pending/run_unit_test.py index 9e212c54..51642c2b 100644 --- a/test/host/net_pending/run_unit_test.py +++ b/test/host/net_pending/run_unit_test.py @@ -56,4 +56,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/net_select/run_unit_test.py b/test/host/net_select/run_unit_test.py index 74522790..67fd0d6b 100644 --- a/test/host/net_select/run_unit_test.py +++ b/test/host/net_select/run_unit_test.py @@ -52,4 +52,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/prot_set_store/run_unit_test.py b/test/host/prot_set_store/run_unit_test.py index 0b304a3c..a2d7a715 100644 --- a/test/host/prot_set_store/run_unit_test.py +++ b/test/host/prot_set_store/run_unit_test.py @@ -49,4 +49,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) - diff --git a/test/host/task_prof/run_unit_test.py b/test/host/task_prof/run_unit_test.py index 9c7ebca6..d0856e84 100644 --- a/test/host/task_prof/run_unit_test.py +++ b/test/host/task_prof/run_unit_test.py @@ -51,4 +51,3 @@ BUILD = TestBuild( if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD)) -