docs(内存): 阶段子项改层级编号 4.阶段.项,消除与顶层章节同号撞面
CI / L0 静态门禁 (pre-commit) (push) Failing after 2m1s
CI / L0.5 主机侧单测 (MSVC) (push) Successful in 21s

根因:`RAM使用分析与优化方案.md` 顶层有 §1 背景 / §2 现状测量(2.1~2.6),而 §4
「方案」下"阶段 N"的子项也用裸号 0.x / 1.x / 2.x——阶段 2 的 2.1/2.2/2.3 与顶层
2.x **完全同号**,阶段 1 的 1.x 又长得像"第 1 章第 x 节"。`doc-links` 对本文自
引用按设计直接放过,于是门禁和人一起看错:AGENTS.md 把"栈余量台账"写成 §1.4,
而 §1.4 其实是阶段 1 的 lftpd 静态化项(上一笔提交的信息里我把它误判成断链,
已 amend 更正)。

改法:阶段子项一律改层级编号,阶段号不变、只加 `4.` 前缀 —— 0.x→4.0.x、
1.x→4.1.x、2.x→4.2.x。共改 14 个标题;文内自引用与实施序列表(S4~S9 那套)、
暂缓清单、§5 预期效果列里的裸号一并补成 §4.x.y。

外部引用同步 15 处:AGENTS.md、lftpd/README ×2、菜单页面设计、RAM预算台账 ×2、
项目架构总览 ×3、FTP_Test_Guide ×3、boot_log_data/README、部署环境要求、审查
报告、ram_baseline.py ×3。最有说服力的两处歧义:审查报告与 RAM预算台账都写
"缩堆见 …§2.2"——既可读成顶层 §2.2(静态大项排行)也可读成阶段 2 第 2 项,
现均为 §4.2.2(阶段 2 第 2 项"缩堆")。

另修 4 处把 §1.4 当"栈台账"的错指:采样有效性校验设计说明 :441/:593/:650 改
指 §2.3(任务栈配置与实测峰值)与 §4.0.6(静态最坏链与判定);id_aliases.json
 的 note 改指 §4.0.6 并标注"原 §1.4,2026-10-22 层级编号"。

冻结与示例:其它文档**变更记录行**里的旧编号按项目纪律不改写(项目架构总览
:367、RAM预算台账 :98),改在被引文档 §4 开头留一张换算表 + 约定说明;把旧号
当语法示例写的 7 处一并换掉(文档规范 ×3、check_doc_links ×3、rename_ref ×1),
否则示例本身指向已改名的锚点。主文档版本行 bump 到 v1.3 / 2026-10-22。

门禁:check_doc_links.py --all --sections-strict → 146 个文档 0 问题(章节引用
2513 处、机器可判定 1491);check_ids.py --strict 增量拦截通过;mem-numbers、
ram_baseline --gate(SRAM1 57.4% / CCM 87.9%)、task-table 全绿;本轮未改任何
.c/.h。16 个文件字节卫生自查 BOM/CR/U+FFFD/行尾空格全清。已提交本地,未推送。
This commit is contained in:
2026-10-22 15:38:14 +08:00
parent f4b9bd57d5
commit 504db26698
16 changed files with 84 additions and 76 deletions
+1 -1
View File
@@ -63,7 +63,7 @@
| protectTask | 512 | Normal |
<!-- TASK_TABLE:END -->
(实际值以 `Src/freertos.c` 为准,本表由 `scripts/gen_task_table.py` 生成、`task-table` 钩子校验;testTask 仅在启用测试套件时创建。FreeRTOS 堆已迁 CCM,任务栈不占 SRAM;**每次抬栈的实测最坏链与余量台账** → `docs/内存管理/RAM使用分析与优化方案.md` §2.3(任务栈配置与实测峰值)与 §0.6(静态最坏链与判定)、`docs/内存管理/内存内容分配策略.md` §6(容量台账))
(实际值以 `Src/freertos.c` 为准,本表由 `scripts/gen_task_table.py` 生成、`task-table` 钩子校验;testTask 仅在启用测试套件时创建。FreeRTOS 堆已迁 CCM,任务栈不占 SRAM;**每次抬栈的实测最坏链与余量台账** → `docs/内存管理/RAM使用分析与优化方案.md` §2.3(任务栈配置与实测峰值)与 §4.0.6(静态最坏链与判定)、`docs/内存管理/内存内容分配策略.md` §6(容量台账))
## 铁律:源码编辑与编码
+2 -2
View File
@@ -29,7 +29,7 @@ lftpd(本目录)
- **ftpTask 就绪屏障**:`osEventFlagsWait` 等两位就绪必须加 `osFlagsNoClear`,
否则位被先到的消费者清掉 → FTP 21 端口静默不监听(无日志无报错)
- **lftpd 大局部缓冲**:已按 `docs/内存管理/RAM使用分析与优化方案.md` §1.4 静态化 9 处
- **lftpd 大局部缓冲**:已按 `docs/内存管理/RAM使用分析与优化方案.md` §4.1.4 静态化 9 处
大局部缓冲(BSS +4.6KB),使 ftpTask 栈峰值从 3480B 降到 1184B——**勿改回局部变量**
- **上传中止截断**:FTP STOR 中途断开后首笔复连被截断(TRAP-NET-18)已在 `net_socket.c` 侧修复
- 本目录受 pre-commit 的**部分排除**(第三方代码)
@@ -38,4 +38,4 @@ lftpd(本目录)
- 测试指南:`docs/测试说明/FTP_Test_Guide.md`
- 台架探针:`test/host/ftp_abort_probe.py`、`MDK-ARM/ftp_freeze_probe.py`
- 内存优化:`docs/内存管理/RAM使用分析与优化方案.md` §1.4
- 内存优化:`docs/内存管理/RAM使用分析与优化方案.md` §4.1.4
+1 -1
View File
@@ -772,7 +772,7 @@ ESC/LEFT : d>0 → menu_back() (根框 d=0 不响
| **小计** | | **~0.35 KB**(逻辑层 17 B + 显示层 ~0.33 KB) |
**LVGL 池(动态,`LV_MEM` 16 KB 预算内)**:行自绘无对象,池只占**屏上框体**(2~3 个 `lv_obj`)+ 根框装饰/状态行,
典型 ≈ **1~2 KB**,仍远小于 16 KB(池已于 2026-10-15 由 40 KB 缩到 16 KB,见 `docs/内存管理/RAM使用分析与优化方案.md` §1.1;真机开机实测 `menu build done: used=3192`)。
典型 ≈ **1~2 KB**,仍远小于 16 KB(池已于 2026-10-15 由 40 KB 缩到 16 KB,见 `docs/内存管理/RAM使用分析与优化方案.md` §4.1.1;真机开机实测 `menu build done: used=3192`)。
---
@@ -1,6 +1,8 @@
# STM32F4-Base RAM 使用分析与优化方案(内存管理主文档)
> 版本:v1.2 日期:2026-09-21
> 版本:v1.3 日期:2026-10-22(v1.2 2026-09-21)
> 变更:v1.3 §4 的阶段子项改为**层级编号**(见下"编号约定");同步 2026-10-22 现状数字与
> `data/ram_report_20261022.md` / `data/stack_report_20261022.md` 两份新快照。
> 变更: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`(静态调用图 × 运行时栈水位)+ 源码静态分析 + 网络调研
@@ -15,7 +17,7 @@
- 主 SRAM 128KB 中静态区曾占 **122.7KB**,余量仅 5.3KB
- FreeRTOS 堆 48KB 里装下 ~33.5KB 任务栈,可分配余量只有 ~12KB
- F407 另有 **64KB CCM RAM**:**已于 2026-09-21 启用**并把 FreeRTOS 堆迁入(§1.2)
- 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 复测"小节)。
@@ -47,7 +49,7 @@
| 项 | 说明 | 来源 |
|---|---|---|
| 主 SRAM (0x20000000) | 128 KB(RW_IRAM1 Max `0x20000`) | scatter `STM32F407-Demo.sct:22` |
| CCM SRAM (0x10000000) | 64 KB;FreeRTOS 堆于 2026-09-21 迁入(§1.2) | scatter `STM32F407-Demo.sct:25`;RM0090 §3.1.1 |
| CCM SRAM (0x10000000) | 64 KB;FreeRTOS 堆于 2026-09-21 迁入(§4.1.2) | scatter `STM32F407-Demo.sct:25`;RM0090 §3.1.1 |
| FreeRTOS 堆 (heap_4) | **56 KB(57,344 B)**,**位于 RW_CCM**;2026-10-12(B5)由 48 KB 扩容(protectTask/iec104Task 栈入堆,CCM 水位 87.9% < 92% 门禁);旧 48KB 池实测 `free=8,784 / min_ever=4,848` B(2026-10-03),新池 min_ever 待上板复测 | `Inc/FreeRTOSConfig.h:71` `configTOTAL_HEAP_SIZE`(L1:`.ioc` `FREERTOS.configTOTAL_HEAP_SIZE`) |
| C 库堆/栈 (startup) | 512 B / 1 KB,启动期 main() 用 | map `startup_stm32f407xx.o` HEAP/STACK 节 |
@@ -61,7 +63,7 @@
| 2 | 6,746 | `ui_net_sink.o` | 远程显示收发缓冲 `g_tx[3209]` + `g_lcdmem_data[3204]` + `g_rx[64]` |
| 3 | 6,408 | `ui_port.o` | `g_ui_fbuf[3200]` + `g_ui_draw_buf[3200]`(160×160 1bpp 帧缓冲) |
| 4 | 6,200 | `nand_ftl.o` | `s_page_buf`/`s_cache_buf`/`s_copy_buf` 各 2,048 B + dhara 结构(**DMA**) |
| 5 | 4,616 | `lftpd.o` | FTP 会话静态缓冲(2026-09-21 由栈迁入,§1.4) |
| 5 | 4,616 | `lftpd.o` | FTP 会话静态缓冲(2026-09-21 由栈迁入,§4.1.4) |
| 6 | 4,104 | `ch395f.o` | `s_spi2_dma_tx_buf[2052]` + `s_spi2_dma_rx_buf[2052]`(**DMA**) |
| 7 | 2,576 | `gd5f2gq5ue.o` | `s_bbt[256]` + `s_bbt_best[256]` + `s_bbt_page_buf[2048]`(**DMA**) |
| 8 | 1,720 | `cmsis_os2.o` | CMSIS-RTOS2 封装静态区 |
@@ -77,7 +79,7 @@
> (卷探针 `FIL` 528 + `default_fatfs`/`fs_work`)、`cfg_file_store_mcu.o` 1,104 B(2 处 `FIL` = 1,056)。
> 同期 Flash **−600 B**(删旧档迁移)。台账:`data/ram_report_20261010.md`。
**RW_CCM(CCM)**:`heap_4.o` 49,184 B(FreeRTOS 堆 49,152 B + RW 32 B)——2026-09-21 由主 SRAM 迁入(§1.2),不再计入主 SRAM。
**RW_CCM(CCM)**:`heap_4.o` 49,184 B(FreeRTOS 堆 49,152 B + RW 32 B)——2026-09-21 由主 SRAM 迁入(§4.1.2),不再计入主 SRAM。
**静态合计(以 §2.1 受管快照为准)**:RW_IRAM1 **87,040 B(85.00 KB / 128 KB,66.4%)** + RW_CCM **49,184 B(48.03 KB / 64 KB,75.0%)** = **136,224 B(RW+ZI)**。
> 历史(2026-09-21):RW_IRAM1 82,048 B(62.6%)+ RW_CCM 49,184 B = 131,232 B。
@@ -121,12 +123,12 @@
| idle(内核) | `configMINIMAL_STACK_SIZE=128` | 0.5KB | 空闲 | 未观测(未取句柄) |
| timer(内核) | `configTIMER_TASK_STACK_DEPTH=256` | 1KB | 软定时器 | 未观测 |
> 任务栈全部从 FreeRTOS 堆(56KB = `configTOTAL_HEAP_SIZE` 57,344 B;2026-09-21 迁 CCM、2026-10-12 扩容)分配。**"配置栈"列以 `Src/freertos.c` 为唯一来源;实测峰值/余量判定以 §0.6 的 `stack_report` 基线为准**(本表数值同步自该基线)。
> 任务栈全部从 FreeRTOS 堆(56KB = `configTOTAL_HEAP_SIZE` 57,344 B;2026-09-21 迁 CCM、2026-10-12 扩容)分配。**"配置栈"列以 `Src/freertos.c` 为唯一来源;实测峰值/余量判定以 §4.0.6 的 `stack_report` 基线为准**(本表数值同步自该基线)。
### 2.4 动态分配点与隐患
1. **lftpd 的 C 库 `malloc` 隐患(✅ 2026-09-10 已修,见阶段 2.1)**:`lftpd_io.c` 的 3 处 `malloc`/`free`(路径规范化/`DIR`)与 `lftpd.c` 的 7 处 `free` 已全部改用 `pvPortMalloc`/`vPortFree`(C 库堆仅 startup 的 512B,原写法长路径必炸);当前源码无裸 `malloc` 残留。
2. **lftpd 大栈局部变量(✅ 2026-09-21 已整改,见 §1.4)**:`lftpd.c:67-68`(message[256]+response[512])、`:109,134`(name[256]×2)、`:156,185`(**buffer[1024]×2**)、`:551`(read_buffer[512])、`:583`(arg_buf[512])以及 `lftpd_log.c` 的 `buffer[256]` 已全部改为 `static`;实测 ftpTask 峰值 3480 → 1184 B,不再需要 4KB+ 栈。
1. **lftpd 的 C 库 `malloc` 隐患(✅ 2026-09-10 已修,见 §4.2.1)**:`lftpd_io.c` 的 3 处 `malloc`/`free`(路径规范化/`DIR`)与 `lftpd.c` 的 7 处 `free` 已全部改用 `pvPortMalloc`/`vPortFree`(C 库堆仅 startup 的 512B,原写法长路径必炸);当前源码无裸 `malloc` 残留。
2. **lftpd 大栈局部变量(✅ 2026-09-21 已整改,见 §4.1.4)**:`lftpd.c:67-68`(message[256]+response[512])、`:109,134`(name[256]×2)、`:156,185`(**buffer[1024]×2**)、`:551`(read_buffer[512])、`:583`(arg_buf[512])以及 `lftpd_log.c` 的 `buffer[256]` 已全部改为 `static`;实测 ftpTask 峰值 3480 → 1184 B,不再需要 4KB+ 栈。
3. **DMA 缓冲位置**:CH395F(SPI2 DMA)、GD5F(SPI1 DMA)、UART DMA 缓冲必须由 DMA 访问 → **只能放主 SRAM,不能放 CCM**(CCM 仅 CPU 可达,见 4.3)。
### 2.5 结论:紧张的三个根因(含 2026-09-11 审计量化)
@@ -134,7 +136,7 @@
| 根因 | 占用 | 可回收性 |
|---|---|---|
| ① LVGL 内存池 40KB 对 1bpp 小屏菜单过大 | 40KB → **16KB ✅ 2026-10-15** | 高(目标 8~16KB;**开机实测 UI 仅用 ~3KB**,见 §2.6.2) |
| ② 任务栈"往大了给",无实测依据 | 38,912 B(38 KB 现值,§2.6.1) | 高(§0.6 已有实测:uiTask 3.8/16KB、netTask 0.6/4KB → 阶段 1.3 缩减) |
| ② 任务栈"往大了给",无实测依据 | 38,912 B(38 KB 现值,§2.6.1) | 高(§4.0.6 已有实测:uiTask 3.8/16KB、netTask 0.6/4KB → §4.1.3 缩减) |
| ③ 64KB CCM 曾完全未利用,缓冲挤在主 SRAM | — | ✅ 2026-09-21 已启用(堆迁入 CCM);2026-10-22 CCM 余量 7,904 B(**全局卡点**)|
量化补充(来源:2026-09-11 审计快照,口径与方法见 §8.4):
@@ -142,9 +144,9 @@
- 两池(FreeRTOS 48KB + LVGL 40KB)曾占主 SRAM 的 **68%**,是当时"SRAM 几乎耗尽(95.4%,余 5.8KB)"的直接原因;**堆迁 CCM 后该结构性压力已解除**(2026-10-22 RW_IRAM1 57.4%,见 §2.1)
- heap_4 **不合并相邻空闲块** → 长期运行需盯碎片,`min_ever`(2026-10-22 实测 8,464 B,56KB 池)是硬指标而非 `free`
- 未映射资源:SRAM3 64KB(scatter 未映射;clone 芯片可能无此区);CCM 已用
- 曾经的三条中风险项:新任务无处安置(已缓解)、堆碎片(仍存)、栈溢出检测缺失 → **已于阶段 0 开启 `configCHECK_FOR_STACK_OVERFLOW=2` 并实测抓到过溢出**(§0.1)
- 曾经的三条中风险项:新任务无处安置(已缓解)、堆碎片(仍存)、栈溢出检测缺失 → **已于阶段 0 开启 `configCHECK_FOR_STACK_OVERFLOW=2` 并实测抓到过溢出**(§4.0.1)
小项:FF_FS_TINY=0(每打开文件 512B 扇区窗,阶段 1.5 可改 1)、`ui_net_sink` PC 远程显示缓冲常驻 6.4KB(产品构建可加开关关闭,阶段 1.5);lftpd 栈局部变量 ✅ 2026-09-21 已静态化(§1.4)。
小项:FF_FS_TINY=0(每打开文件 512B 扇区窗,§4.1.5 可改 1)、`ui_net_sink` PC 远程显示缓冲常驻 6.4KB(产品构建可加开关关闭,§4.1.5);lftpd 栈局部变量 ✅ 2026-09-21 已静态化(§4.1.4)。
### 2.6 堆 / LVGL 池 / C 库 / Flash 明细
@@ -189,7 +191,7 @@
| 组件 | 大小 | 说明 |
|---|---|---|
| 启动栈 | 1,024 B | `startup_stm32f407xx.o`,仅 `HAL_Init`/`app_main_init` 期间 |
| C 库堆 | 512 B | `Heap_Size EQU 0x200` → **自研代码禁用 C 库 `malloc`**(AGENTS.md 铁律;lftpd 的 `malloc` 隐患见 §2.4-1 与阶段 2.1) |
| C 库堆 | 512 B | `Heap_Size EQU 0x200` → **自研代码禁用 C 库 `malloc`**(AGENTS.md 铁律;lftpd 的 `malloc` 隐患见 §2.4-1 与 §4.2.1) |
| C 库代码(Flash) | ~4.2 KB | printf / 浮点 / 字符串 |
#### 2.6.4 Flash 1 MB 分布
@@ -207,7 +209,7 @@
> **2026-10-11 裁剪 LVGL 死代码(原审查报告 F-1/F-2/F-3,已销项)**:`Lib/lv_conf.h` 显式关掉 10 个 `LV_DRAW_SW_SUPPORT_*`(只留 I1)、5 个未用控件类(label / button / buttonmatrix / bar / textarea)、`LV_USE_THEME_SIMPLE`、`LV_USE_FLEX` / `LV_USE_GRID` → **`ER_IROM1` 274,724 → 210,148 B(省 64,576 B = 旧镜像的 23.5%)**,Flash 占比 26.3% → **20.1%**。
> 对象级对照(`.map` 组件表,Code+RO+RW):`blend_to_*` 40,582 → 1,338(其中 `blend_to_i1` 自身 5,272 → 1,338——它按**源**格式的分支也一并消失);控件类 13,397 → 0;`lv_theme_simple` 866 → 0;`lv_draw_sw_transform` 4,340 → 598;`lv_theme_mono` 1,400 → 1,036;镜像内对象数 227 → 212。实测比原报告估算(54,630 B)多省约 10 KB,差额正是这些"锚点消失后连带消失的分支"。
> ⚠ **配套本地补丁**:`lv_draw_sw_blend_to_i1.c` 的 `lv_color16_from_u16` 上游 9.3.0 漏了定义处守卫(唯一使用点全在 `#if LV_DRAW_SW_SUPPORT_RGB565_SWAPPED` 内)→ 关掉该格式后 ARMCC 报 `#177-D declared but never referenced`(破坏"0 警告"门禁)。已按使用点补同一守卫,升级 LVGL 时若上游已修可去掉。
> 验证:ui-verify 五步全过——**11 页内容黄金参考在裁剪前生成、裁剪后逐字节一致**,菜单 8 状态像素一致,LVGL 池水位 5,440 → 5,368 B(−72 B,样式表更小,一次性残留仍 80 B);`@build` 0 错 0 警;`@test` 真机 PASS。详见 `../UI设计/UI_设计架构.md` §1.4 第 10 条。
> 验证:ui-verify 五步全过——**11 页内容黄金参考在裁剪前生成、裁剪后逐字节一致**,菜单 8 状态像素一致,LVGL 池水位 5,440 → 5,368 B(−72 B,样式表更小,一次性残留仍 80 B);`@build` 0 错 0 警;`@test` 真机 PASS。详见 `../UI设计/UI_设计架构.md` §4.1.4 第 10 条。
> **长效防复发**:已建门禁 `scripts/check_lv_conf_sync.py`(pre-commit 钩子 `lv-conf-sync`)——比对两份 `lv_conf.h` 的**生效值**(未定义则取 `lv_conf_internal.h` 兜底默认),并抓无效宏名与重复定义;首次跑揪出两端共 26 个 9.3 不存在的选项、3 处重复定义、10 项靠默认值隐式生效的开关,已清理(本次清理对镜像体积中性)。详解见 `../代码规范/pre-commit使用说明.md` §2.15。
![Flash 布局](../images/内存布局_FLASH.svg)
@@ -231,11 +233,17 @@
## 4. 方案
> **编号约定(2026-10-22 起)**:本节的子项按层级写成 **`4.<阶段>.<项>`**(如 `§4.1.4`),
> 不再用裸 `0.x / 1.x / 2.x`——旧写法与本文顶层章节 `§1 背景`、`§2 现状测量` **同号撞面**,
> 人和 `doc-links`(自引用直接放过)都会读错;实测最坑的是"缩堆"那条既像 §2.2(静态大项排行)
> 又像阶段 2 第 2 项。对照:`0.x→4.0.x`、`1.x→4.1.x`、`2.x→4.2.x`(阶段号不变、只加 `4.` 前缀)。
> **其它文档的变更记录行里的旧编号按项目纪律冻结不改写**,需要追溯时按本表换算。
> ⏸ **实施状态(用户裁定 2026-10-15)**——本节的"阶段/天数"是当初的排期设想,现在按下面口径执行:
>
> - **保持常开**:阶段 0 的测量能力 + 阶段 3 的长效机制(`mem-budget` 92% 门禁、`RAM预算台账.md` 受管区、`ram_baseline.tsv` 趋势、`[STACK]` 水位与 `stack_report.py` / `ram_report.py`)。它们已落地、零维护成本,且正是"何时该重开优化"的传感器。
> - **已落地的优化不回退**:1.1 LVGL 池 40→16 KB(✅ 2026-10-15,SRAM1 74.9% → **56.1%**)、1.4 lftpd 大栈局部变量改静态(✅ 2026-09-21)。
> - **整簇暂缓、不再排期**:1.2 启用 CCM 承载 UI 侧缓冲、1.3 任务栈按实测缩减、2.2 缩减 `configTOTAL_HEAP_SIZE`、2.3 任务/队列全静态化,以及把池进一步试到 8 KB。
> - **已落地的优化不回退**:§4.1.1 LVGL 池 40→16 KB(✅ 2026-10-15,SRAM1 74.9% → **56.1%**)、§4.1.4 lftpd 大栈局部变量改静态(✅ 2026-09-21)。
> - **整簇暂缓、不再排期**:§4.1.2 启用 CCM 承载 UI 侧缓冲、§4.1.3 任务栈按实测缩减、§4.2.2 缩减 `configTOTAL_HEAP_SIZE`、§4.2.3 任务/队列全静态化,以及把池进一步试到 8 KB。
>
> **暂缓理由与重开条件** → `../问题记录/项目审查与优化建议报告.md` §5.3。一句话:**当前没有功能在等这块预算**,而搬迁要动 scatter、要逐条过 DMA 可达性清单(违例是静默传错不是报错)、缩堆还得上板复测 `min_ever`——收益不抵风险与工时。下面各节**保留作重开时的实施依据,不代表现在要做**。
@@ -243,7 +251,7 @@
> 原则:没有数据不动刀。本阶段全部是"只增不减"的改动,零功能风险。
#### 0.1 栈溢出检测(防身)
#### 4.0.1 栈溢出检测(防身)
`Inc/FreeRTOSConfig.h` 增加:
@@ -270,7 +278,7 @@ void vApplicationMallocFailedHook(void)
成本:每次上下文切换几十周期,不占 RAM。
#### 0.2 运行时栈水位(已落地:`stack_probe_poll`,2026-09-21)
#### 4.0.2 运行时栈水位(已落地:`stack_probe_poll`,2026-09-21)
实现位置:`App/task/default_task.c` 的 `stack_probe_poll`(2026-09-21 落地于 `Src/freertos.c` USER CODE 区,2026-09-25 随 defaultTask 任务体整体迁出至 `App/task/` 并注册 Keil 工程 App 组)。
数据源是本版 CMSIS/FreeRTOS **已有**的 API:`osThreadGetStackSpace()`(剩余字节,内部走
@@ -291,9 +299,9 @@ void vApplicationMallocFailedHook(void)
- `uxTaskGetStackHighWaterMark` 返回**任务创建以来**的最小剩余栈(单位 word=4B);`used = 配置 − 剩余` 即该任务历史峰值栈占用。
- **测量必须覆盖全功能路径**(高水位是历史最小值,只反映跑过的路径):全菜单遍历、FTP 上传/下载大文件、CH395F 断链重连、NAND 格式化、时间同步。建议 `@test` 全回归 + 1 小时浸泡。
- 未覆盖 idle/timer(内核静态小栈、纯循环,未取句柄);构建期静态侧见 §0.6。
- 未覆盖 idle/timer(内核静态小栈、纯循环,未取句柄);构建期静态侧见 §4.0.6。
#### 0.3 map 静态占用报告脚本
#### 4.0.3 map 静态占用报告脚本
脚本 `scripts/ram_report.py`(Python,已随仓库提供):
@@ -304,15 +312,15 @@ void vApplicationMallocFailedHook(void)
- ✅ **2026-10-12 落地**:`scripts/ram_baseline.py --trend` 与基线 TSV
(`scripts/ram_baseline.tsv`)做 diff,任何对象 RW+ZI 增量 >1KB 即高亮
#### 0.4 (可选)运行时间统计
#### 4.0.4 (可选)运行时间统计
`configGENERATE_RUN_TIME_STATS=1` + `portCONFIGURE_TIMER_FOR_RUN_TIME_STATS` 用 TIM2(HAL 时基已占 TIM7,TIM2 当前空闲,需核对 `.ioc`)。`vTaskGetRunTimeStats()` 给出每任务 CPU 占比——用于发现"CPU 大户",与内存问题正交,优先级低于 0.1/0.2。
#### 0.5 (可选)main() 启动期栈峰值
#### 4.0.5 (可选)main() 启动期栈峰值
启动期跑在 startup 的 1KB C 栈上(CubeMX 初始化 + 各驱动 init,含 BBT 全扫描)。ARMCC 可用 `__current_stack_usage()` 在 `app_main_init` 末尾打点,确认 1KB 够用;不够则调 scatter 的 `STACK` 节(与任务栈无关,单独预算)。
#### 0.6 静态栈分析 + 汇总工具(构建期,零目标依赖;2026-09-21 落地)
#### 4.0.6 静态栈分析 + 汇总工具(构建期,零目标依赖;2026-09-21 落地)
**UV4 每次构建已自动生成静态调用图** `MDK-ARM/STM32F407-Demo/STM32F407-Demo.htm`
(标题 "Static Call Graph",随 `@build` 刷新,**无需**给 armlink 加 `--callgraph`)。每个函数
@@ -340,7 +348,7 @@ void vApplicationMallocFailedHook(void)
| `--min-free-pct` | `30` | 要求的最小余量百分比 |
- **判定规则**:`余量 = 配置 − max(静态最坏深度, 实测峰值)`,须 `≥ max(1024 B, 配置×30%)`;任一任务不达标 → 脚本退出码 **1**(可挂门禁),全达标 → 0,HTM 缺失 → 2。
- **何时跑**:① 改栈或加大局部变量前后;② 改 FatFs/lftpd/LVGL 等第三方配置后;③ 任务职责变化时。串口日志覆盖的路径越全越准(路径覆盖要求见 §0.2)。
- **何时跑**:① 改栈或加大局部变量前后;② 改 FatFs/lftpd/LVGL 等第三方配置后;③ 任务职责变化时。串口日志覆盖的路径越全越准(路径覆盖要求见 §4.0.2)。
- **报告内容**:每任务预算表(配置/静态/实测/最坏/余量/判定)、各任务最坏调用链、帧宽 Top15(找"宽度大户")、堆 free/min_ever。
首份基线(2026-09-21,产品构建 + FTP 会话实测;产物 `docs/内存管理/data/stack_report_20260921.md`):
@@ -349,7 +357,7 @@ void vApplicationMallocFailedHook(void)
|---|---|---|---|---|---|---|
| defaultTask | 4096 | **2776** | 2136 | 2776 | 1320 (32%) | PASS |
| **ftpTask**(优化前) | 4096 | 2528 | **3480** | **3480** | **616 (15%)** | **FAIL** |
| **ftpTask**(§1.4 静态化后) | 4096 | **472** | **1184** | **1184** | **2912 (71%)** | **PASS** |
| **ftpTask**(§4.1.4 静态化后) | 4096 | **472** | **1184** | **1184** | **2912 (71%)** | **PASS** |
| netTask | 4096 | 440 | 588 | 588 | 3508 (85%) | PASS |
| uiTask | 16384 | 912 | 3824 | 3824 | 12560 (76%) | PASS |
| timeSyncTask | 2048 | 336 | 420 | 420 | 1628 (79%) | PASS |
@@ -357,12 +365,12 @@ void vApplicationMallocFailedHook(void)
实测堆:`free=8784 B / min_ever=4848 B`(48KB 池)。
**处置结果(2026-09-21 当日闭环,走 ②→①)**:先做 §1.4(lftpd 大局部变量静态化)——复测 ftpTask 峰值 **3480 → 1184 B**(余量 616 → **2912 B / 71%**),已满足 `≥ max(1024 B, 配置×30%)`,**①(涨栈到 1600 words)不再需要**。代价:静态 BSS +4,616 B(RW_IRAM1 77.4 → 82.0 KB / 128 KB,余量仍 ~48 KB)。
**处置结果(2026-09-21 当日闭环,走 ②→①)**:先做 §4.1.4(lftpd 大局部变量静态化)——复测 ftpTask 峰值 **3480 → 1184 B**(余量 616 → **2912 B / 71%**),已满足 `≥ max(1024 B, 配置×30%)`,**①(涨栈到 1600 words)不再需要**。代价:静态 BSS +4,616 B(RW_IRAM1 77.4 → 82.0 KB / 128 KB,余量仍 ~48 KB)。
**静态与实测是互补的两套数据(必须都留)**:
- defaultTask:静态 2776 > 实测 2136——静态是**上界**(最深分支未必走到),实测是**已走路径**。**判栈够不够看两者更坏者,做定栈优化看实测。**
- netTask / uiTask / testTask / timeSyncTask / **ftpTask**:实测 > 静态——静态是**下界**(互递归、间接调用、ISR 上下文保存 ~100B 未计入)。ftpTask 最典型:优化前静态 2528 B(控制通道握手链)**低估了 950 B**,真实峰值 3480 B 只在 128KB STOR 传输时暴露 → **不跑真实业务就定栈 = 拍脑袋。** §1.4 静态化后静态 472 B / 实测 1184 B,两者重新收敛(差距回到 ISR/上下文量级)。
- netTask / uiTask / testTask / timeSyncTask / **ftpTask**:实测 > 静态——静态是**下界**(互递归、间接调用、ISR 上下文保存 ~100B 未计入)。ftpTask 最典型:优化前静态 2528 B(控制通道握手链)**低估了 950 B**,真实峰值 3480 B 只在 128KB STOR 传输时暴露 → **不跑真实业务就定栈 = 拍脑袋。** §4.1.4 静态化后静态 472 B / 实测 1184 B,两者重新收敛(差距回到 ISR/上下文量级)。
- 本次可观测性的第一个产出不是数字而是**缺陷**:ftpTask 因就绪位被其它等待者消费而**从未启动**(21 端口一直无监听),修好后才有了上面这组数据——详见 `docs/测试说明/FTP_Test_Guide.md` §8.8。
要点与边界:
@@ -370,7 +378,7 @@ void vApplicationMallocFailedHook(void)
- defaultTask 的静态最坏链是**配置保存/旧档迁移**路径,不是首启探针路径(**2026-09-21 当时的链;旧档迁移已于 2026-10-10 删除、日志行缓冲已按模块收紧,现链见下方"2026-10-10 复测"**):
`net_cfg_load → net_cfg_legacy_migrate → net_cfg_save → cfg_file_save → s_write_template → s_write_doc → cfg_store_write_atomic → f_rename → follow_path → dir_next → create_chain → put_fat → move_window → sync_window → disk_write → ftl_flush_cache → dhara_map_write → prepare_write → auto_gc → dhara_map_gc → ... → trace_path → dhara_journal_read_meta → dhara_nand_read → gd5f_read_from_cache → HAL_SPI_Receive → ... → SPI_WaitFlagStateUntilTimeout`。
**2776 B > 2048 B** → 512 words 栈必然溢出,与 2026-09-21 实测完全一致。
- 帧宽榜直接点名宽度大户(自身帧):`s_append` 704 B(FIL 512 + 记录 64 + 时间戳)、`StartDefaultTask` 560 B(探针 `FIL __chk`)、`cfg_store_read`/`cfg_store_write_atomic` 576 B、LVGL 绘制 624/560/400 B——与 §1.4 的整改项对得上。**lftpd 三项已于 2026-09-21 静态化(§1.4),已退出帧宽榜前 15**(原 `handle_control_channel` 1104 B、`receive_file`/`send_file` 1088 B、`send_response` 800 B)。
- 帧宽榜直接点名宽度大户(自身帧):`s_append` 704 B(FIL 512 + 记录 64 + 时间戳)、`StartDefaultTask` 560 B(探针 `FIL __chk`)、`cfg_store_read`/`cfg_store_write_atomic` 576 B、LVGL 绘制 624/560/400 B——与 §4.1.4 的整改项对得上。**lftpd 三项已于 2026-09-21 静态化(§4.1.4),已退出帧宽榜前 15**(原 `handle_control_channel` 1104 B、`receive_file`/`send_file` 1088 B、`send_response` 800 B)。
- **静态分析的天然盲区**:递归/互递归(LVGL widget 树、`layout_update_core`、`apply_theme` 等)与间接调用只能按 "Cycle" 近似,armlink 明确标注 `+ Unknown(Cycles, Untraceable Function Pointers)`。因此 uiTask 的 912 B 是**下限**(实测 3824 B),必须靠运行时水位。
- testTask 在产品构建里只是 `osDelay` 空循环(168 B);2048 words 是给**存储套件构建**(`TEST_SUITE_STORAGE`)预留的,测套件时应重跑本脚本看该构建的静态值。
- **测 ftpTask 的复现配方**:设备就绪后跑 `& '.venv\Scripts\python.exe' test/host/ftp_speed_test.py --size 128`(STOR 128KB + RETR + 逐字节比对),再按上面命令叠加串口日志出报告。**不要用裸 TCP 工具探测 21 端口**——CH395F 单客户端复用,探测会占掉唯一会话(见 `docs/测试说明/FTP_Test_Guide.md` §8.9)。
@@ -433,14 +441,14 @@ FreeRTOS 堆不变(`free=17248 / min_ever=9312`)。验证链:`@build` 0/0
### 阶段 1:静态大项削减(收益最大,3~5 天)
#### 1.1 LVGL 内存池 40KB → 16KB(✅ 已执行 2026-10-15,省 24,576 B)→ 8KB(待评估)
#### 4.1.1 LVGL 内存池 40KB → 16KB(✅ 已执行 2026-10-15,省 24,576 B)→ 8KB(待评估)
- 依据:屏幕仅 160×160×1bpp,菜单为轻量控件树;LVGL 内存池只装对象/样式/字符串缓冲,与像素缓冲无关。40KB 来自模板默认值,无适配依据。
- **已落地**:`Lib/lv_conf.h` 与 `simulator/lv_conf.h` 同步改 `(16 * 1024)`——两端必须同值,`lv-conf-sync` 门禁按**生效值**比对。实测水位见 §2.6.2(最重页面峰值 6,568 B = 池的 40%);`@build` 0 错 0 警,ZI 精确 −24,576 B → SRAM1 由 74.9% 降到 **56.1%**(余 57,504 B)。
- 验证口径(本次即用它):`simulator/tests/check_menu_rows.py`(菜单 8 状态像素 + 池基线 5,368 B)+ `simulator/tests/check_content_pages.py`(11 页黄金参考逐字节 + 往返池水位 5,448 B)+ 真机 `menu build done: used=` 日志。
- **再降 8KB 的前置**:全量 UI 回归(PC 模拟器 + 真机)无 `LV_ASSERT_MALLOC`/分配失败;若 8KB 不够就停在 16KB,不勉强。
#### 1.2 启用 CCM RAM(+64KB 预算空间)
#### 4.1.2 启用 CCM RAM(+64KB 预算空间)
依据 RM0090 §3.1.1:F407 的 64KB CCM @0x10000000 仅 CPU 可访问(DMA 不可达、无 cache)。
@@ -469,7 +477,7 @@ FreeRTOS 堆不变(`free=17248 / min_ever=9312`)。验证链:`@build` 0/0
验证:map 中出现 `RW_CCM` 且 `*(CCM_DATA)` 各对象地址落在 0x10000000~0x100FFFF;`ram_report.py` 同时输出两个 Region 水位。
#### 1.3 任务栈按实测数据缩减(省 ~17KB;目标值已按 2026-09-21 实测修正)
#### 4.1.3 任务栈按实测数据缩减(省 ~17KB;目标值已按 2026-09-21 实测修正)
定栈规则(写入台账,长期执行):
@@ -485,7 +493,7 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/
|---|---|---|---|
| uiTask | 16KB | **8KB** | 实测 3824B → 5736+1024=6760B → 8KB(LVGL 渲染走静态缓冲) |
| netTask | 4KB | **2KB** | 实测 588B → 1024+1024=2048B |
| ftpTask | 4KB | **3KB** | 实测 1184B(§1.4 静态化后)→ 1776+1024=2800B → 3KB;再降须放宽 ISR 余量并复测 |
| ftpTask | 4KB | **3KB** | 实测 1184B(§4.1.4 静态化后)→ 1776+1024=2800B → 3KB;再降须放宽 ISR 余量并复测 |
| testTask | 8KB | **2KB** | 产品构建空转(实测 348B)→ 1024+1024;存储套件构建按该构建静态值另放大 |
| defaultTask | 4KB | **保持 4KB** | 2026-10-10 削链后静态最坏 2776B / 实测 2152B(余 32%);曾达 5312B 溢出判据,见"2026-10-10 复测" |
| timeSyncTask | 2KB | **保持 2KB** | 实测 420B → 1024+1024=2048B,无缩减空间 |
@@ -494,7 +502,7 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/
每改一批 → `@build` + 全功能回归 + 复测高水位(`free_min` 应 ≥ 新栈×30%),不满足则回调。
#### 1.4 lftpd 大栈局部变量 → 静态缓冲(✅ 2026-09-21 完成;ftpTask 峰值 3480 → 1184 B)
#### 4.1.4 lftpd 大栈局部变量 → 静态缓冲(✅ 2026-09-21 完成;ftpTask 峰值 3480 → 1184 B)
已把 `lftpd.c` / `lftpd_log.c` 中 **9 个大局部缓冲改为函数级 `static`**(lftpd 单客户端,全部调用与日志都在 ftpTask 内串行执行,静态化安全;各函数**各自独立缓冲**,无嵌套/别名冲突):
@@ -514,25 +522,25 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/
> lftpd 属第三方代码(pre-commit 排除范围),只做最小侵入改动(每处仅加 `static` + 单行注释),改动点在本台账登记。
#### 1.5 其他小项
#### 4.1.5 其他小项
- `Lib/FatFs/ffconf.h:32` `FF_FS_TINY` 0→1(每打开文件省 512B 扇区窗;FAT32 适用;`FF_FS_REENTRANT=0` 现状下安全)
- `ui_net_sink`(PC 远程显示,6.4KB):加 `APP_UI_NET_SINK` 编译开关,产品固件可关;保留时缓冲随 1.2 迁 CCM(⏸ **1.2 已暂缓,实际仍在 SRAM1**)
- `ui_net_sink`(PC 远程显示,6.4KB):加 `APP_UI_NET_SINK` 编译开关,产品固件可关;保留时缓冲随 §4.1.2 迁 CCM(⏸ **§4.1.2 已暂缓,实际仍在 SRAM1**)
- `CH395F_TCP_MSS=1024` 已是压缩值,DMA 缓冲 `MSS×2+4` 为协议决定,不动
### 阶段 2:堆与动态分配治理(1~2 天)
#### 2.1 修复 lftpd 的 malloc 隐患(必修)
#### 4.2.1 修复 lftpd 的 malloc 隐患(必修)
`lftpd_io.c:44,49,54` 的 `malloc` / 对应 `free` 与 `:189` 的 `malloc(sizeof(DIR))` 改为 `pvPortMalloc` / `vPortFree`(加 `#include "FreeRTOS.h"`)。消除"512B C 堆"地雷,FTP 长路径不再随机失败。
> ✅ **2026-09-10 已落地**:`lftpd_io.c`(3 处 `malloc` + 3 处 `free`)与 `lftpd.c`(7 处 `free`,释放 `lftpd_io_canonicalize_path` 返回的堆指针)全部改用 `pvPortMalloc`/`vPortFree`;两文件加 `#include "FreeRTOS.h"`。lftpd 不参与模拟器构建,仅 MCU 端。Keil 重建 0 错 0 警。
#### 2.2 缩减 configTOTAL_HEAP_SIZE
#### 4.2.2 缩减 configTOTAL_HEAP_SIZE
阶段 1 完成后复测:堆内需容纳 ≈ 栈(15KB) + TCB(2.3KB) + 队列/事件组(0.5KB) + lftpd 临时分配(1KB) + 余量。`configTOTAL_HEAP_SIZE` 49152 → **32768**,要求全功能负载下 `heap free ≥ 8KB` 持续成立(浸泡 1 小时验证)。
#### 2.3 (可选,进阶)任务/队列全静态化
#### 4.2.3 (可选,进阶)任务/队列全静态化
`configSUPPORT_STATIC_ALLOCATION=1` 已开。用 `osThreadNew` 的 `attr.cb_mem`(`StaticTask_t` + 静态栈数组)可把栈完全移出堆,预算 100% 透明、堆只剩小对象分配。改动面涉及 CubeMX 生成的 `freertos.c` 属性块,收益(~15KB 确定性)与改动成本权衡后决定,**不作为必做项**。
@@ -546,16 +554,16 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/
## 5. 预期效果
| 项目 | 现状(2026-09-21 实测,**主 SRAM 数字已随代码更新,以 §2.1 为准**) | 剩余项全部落地后(1.1/1.3/1.5/2.2) |
| 项目 | 现状(2026-09-21 实测,**主 SRAM 数字已随代码更新,以 §2.1 为准**) | 剩余项全部落地后(§4.1.1/§4.1.3/§4.1.5/§4.2.2) |
|---|---|---|
| 主 SRAM 静态占用 | 87,040 B / 128KB(66.4%) | **~27KB / 128KB**(LVGL 池 + UI 缓冲迁 CCM、`ui_net_sink` 关) |
| 主 SRAM 余量 | 44,032 B(43.0KB) | **~101KB** |
| CCM 使用 | 49,184 B / 64KB(75.0%,FreeRTOS 堆,历史当时值。2026-10-12 B5 后堆 56 KB / 87.9%) | ~56KB / 64KB(堆 32K + 池 16K + UI 缓冲 + BBT) |
| 任务栈合计 | 38,912 B(38.0KB) | ~21.5KB(§1.3 修正目标) |
| 任务栈合计 | 38,912 B(38.0KB) | ~21.5KB(§4.1.3 修正目标) |
| FreeRTOS 堆 | 49,152 B(`free=8,784 / min_ever=4,848` B) | 32,768 B(余 ≥8KB,全负载验证) |
| **总余量** | **~68KB**(47.9 + 16.0 + 4.8) | **~120KB** |
> 已落地项(不再计入预期):1.2 堆迁 CCM(2026-09-21)、1.4 lftpd 静态化、2.1 lftpd `malloc`→`pvPortMalloc`(2026-09-10)。
> 已落地项(不再计入预期):§4.1.2 堆迁 CCM(2026-09-21)、§4.1.4 lftpd 静态化、§4.2.1 lftpd `malloc`→`pvPortMalloc`(2026-09-10)。
> 估算假设:LVGL 池缩到 16KB(8KB 未验证)且迁 CCM;`ui_net_sink` 产品构建关闭;`ui_port` 帧缓冲 + BBT 位图迁 CCM;堆缩 48K→32K。落地顺序与每步验证见 §6。
>
> ⏸ **2026-10-15 用户裁定:本表"目标"列所依赖的搬迁与缩堆整簇暂缓**(理由与重开条件 → `../问题记录/项目审查与优化建议报告.md` §5.3)。已落地部分只有 **1.1 池 40→16 KB**(SRAM1 实测 **73,568 B / 56.1%**,余 57,504 B)与 1.4;本表保留作重开时的收益测算依据,**当前实际水位以 `RAM预算台账.md` 受管区为准**,不要拿本表"目标"列当现状。
@@ -567,12 +575,12 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/
| S1 | 0.1 + 0.2(溢出钩子 + `stack_probe_poll` 栈水位探针,`Src/freertos.c` USER CODE 区) | `@build` 0错0警;`@test` 串口出现每任务 `[STACK]` 报告 | ✅ 2026-09-21 |
| S2 | 0.3 `ram_report.py` + 基线 | 脚本输出与 map 人工核对一致 | ✅ 2026-09-21(`data/ram_report_20260921.md`) |
| S3 | 全功能回归跑数(菜单/FTP/网络/格式化/浸泡 1h) | 拿到各任务 `free_min` 与堆 free 底值 | ◐ boot + FTP 路径已实测(09-21);全菜单遍历 + 1h 浸泡待补 |
| S4 | 1.4 lftpd 静态化 + 2.1 malloc 修复 | FTP STOR/RETR 大文件 + 长路径回归 | ✅(2.1 于 2026-09-10、1.4 于 2026-09-21 完成) |
| S5 | 1.1 LVGL 池 40K→16K | UI 全量回归(模拟器 + 实机),无断言失败 | ✅ **2026-10-15**(11 页黄金参考 + 池水位 5,448 B + `@test` 真机 `used=3192`;实测峰值 6,568 B = 池的 40%,§2.6.2) |
| S6 | 1.2 CCM(scatter + section + 门禁宏) | map 显示 `RW_CCM`;关 `APP_CCM_ENABLE` 也能编译通过 | ⏸ **暂缓(2026-10-15 裁定)**;已完成部分仅 `RW_CCM` + 堆迁入(09-21),缓冲 section 迁移与门禁宏不再排期 |
| S7 | 1.3 栈缩减(按 S3 数据)+ 复测 | 每任务 `free_min ≥ 新栈×30%`,无溢出钩子触发 | ⏸ **暂缓(2026-10-15 裁定)**(目标值已按 09-21 实测修正,重开时可直接用) |
| S8 | 2.2 堆 48K→32K + 复测 | 全负载 `heap free ≥ 8KB` | ⏸ **暂缓(2026-10-15 裁定)**;⚠ 2026-10-12 B5 反向扩容至 56 KB,重开时须重跑 `stack_report.py` + 上板 `min_ever` |
| S9 | 1.5 小项 + 台账/门禁/AGENTS.md | `@build` + `@test` 全绿 | ◐ AGENTS.md 内存条目已增补(09-21);台账 / 趋势 / `mem-budget` 三件套 ✅ 2026-10-12(**常开,不受暂缓影响**) |
| S4 | §4.1.4 lftpd 静态化 + §4.2.1 malloc 修复 | FTP STOR/RETR 大文件 + 长路径回归 | ✅(§4.2.1 于 2026-09-10、§4.1.4 于 2026-09-21 完成) |
| S5 | §4.1.1 LVGL 池 40K→16K | UI 全量回归(模拟器 + 实机),无断言失败 | ✅ **2026-10-15**(11 页黄金参考 + 池水位 5,448 B + `@test` 真机 `used=3192`;实测峰值 6,568 B = 池的 40%,§2.6.2) |
| S6 | §4.1.2 CCM(scatter + section + 门禁宏) | map 显示 `RW_CCM`;关 `APP_CCM_ENABLE` 也能编译通过 | ⏸ **暂缓(2026-10-15 裁定)**;已完成部分仅 `RW_CCM` + 堆迁入(09-21),缓冲 section 迁移与门禁宏不再排期 |
| S7 | §4.1.3 栈缩减(按 S3 数据)+ 复测 | 每任务 `free_min ≥ 新栈×30%`,无溢出钩子触发 | ⏸ **暂缓(2026-10-15 裁定)**(目标值已按 09-21 实测修正,重开时可直接用) |
| S8 | §4.2.2 堆 48K→32K + 复测 | 全负载 `heap free ≥ 8KB` | ⏸ **暂缓(2026-10-15 裁定)**;⚠ 2026-10-12 B5 反向扩容至 56 KB,重开时须重跑 `stack_report.py` + 上板 `min_ever` |
| S9 | §4.1.5 小项 + 台账/门禁/AGENTS.md | `@build` + `@test` 全绿 | ◐ AGENTS.md 内存条目已增补(09-21);台账 / 趋势 / `mem-budget` 三件套 ✅ 2026-10-12(**常开,不受暂缓影响**) |
每步都必须 `@build` 通过(0 错误 0 警告)后再进入下一步;涉及 UI 的步骤用 `simulator` 先过一遍再上实机。
@@ -629,7 +637,7 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/
| NAND 页缓冲/BBT | `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c:42-50`、`gd5f2gq5ue.c:28-39` |
| FatFs | `Lib/FatFs/ffconf.h:32`(FF_FS_TINY) |
| scatter | `MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct` |
| lftpd 缓冲/malloc | `Drivers/BSP/NET/lftpd/lftpd.c`(大缓冲已静态化,§1.4)、`lftpd_io.c`(`malloc`→`pvPortMalloc` 已修,阶段 2.1) |
| lftpd 缓冲/malloc | `Drivers/BSP/NET/lftpd/lftpd.c`(大缓冲已静态化,§4.1.4)、`lftpd_io.c`(`malloc`→`pvPortMalloc` 已修,阶段 2.1) |
### 8.4 分析口径与数据来源(原《内存使用分析规划》并入)
+2 -2
View File
@@ -73,7 +73,7 @@ UI 16 KB / RTOS 堆 56 KB / 应用 8 KB**。归集口径(按 `.map` 对象名
| CCM 容量 | `RW_CCM` Max 见受管区 | scatter `RW_CCM`;**仅 CPU 可达,DMA 不可达**(RM0090 §3.1.1) |
| 现占用 | 仅 FreeRTOS 堆 `heap_4`(56 KB,2026-09-21 迁入;2026-10-12 B5 扩容 48→56 KB) | 其余候选全部仍在 SRAM1 |
| 现余量 | 受管区 RW_CCM 容量 − 占用 = **7,904 B** | **不够** R-2 后半的 UI 侧待迁体量(R-9 后 **29,570 B**)——⏸ 该搬迁已暂缓,见下行 |
| R-2 后半解锁链 | ⏸ **整簇暂缓(用户裁定 2026-10-15)**,本行保留作重开时的现状基线:① **R-9 ✅ 2026-10-15**:`LV_MEM_SIZE` 40→16 KB → 待迁体量 54,146 → **29,570 B**(SRAM1 74.9% → **56.1%**,实测池峰值 6,568 B)→ ② ~~现在卡在这一步~~:堆 56→32 KB(`docs/内存管理/RAM使用分析与优化方案.md` §2.2,再腾 24 KB;⚠ 2026-10-12 B5 反向扩容至 56 KB——9 任务栈 + 队列实测需要,缩回 32 KB 前必须重跑 `stack_report.py` + 上板 min_ever 复测)→ ③ CCM 余 ≈31.9 KB ≥ 29,570 B → 迁入 | **暂缓期间不启动**;重开条件 → `../问题记录/项目审查与优化建议报告.md` §5.3。**若重开,顺序不可颠倒**;每步前后跑 `stack_report.py` / `ram_report.py`(铁律) |
| R-2 后半解锁链 | ⏸ **整簇暂缓(用户裁定 2026-10-15)**,本行保留作重开时的现状基线:① **R-9 ✅ 2026-10-15**:`LV_MEM_SIZE` 40→16 KB → 待迁体量 54,146 → **29,570 B**(SRAM1 74.9% → **56.1%**,实测池峰值 6,568 B)→ ② ~~现在卡在这一步~~:堆 56→32 KB(`docs/内存管理/RAM使用分析与优化方案.md` §4.2.2(阶段 2 第 2 项"缩堆"),再腾 24 KB;⚠ 2026-10-12 B5 反向扩容至 56 KB——9 任务栈 + 队列实测需要,缩回 32 KB 前必须重跑 `stack_report.py` + 上板 min_ever 复测)→ ③ CCM 余 ≈31.9 KB ≥ 29,570 B → 迁入 | **暂缓期间不启动**;重开条件 → `../问题记录/项目审查与优化建议报告.md` §5.3。**若重开,顺序不可颠倒**;每步前后跑 `stack_report.py` / `ram_report.py`(铁律) |
| 迁入前置 | 目标缓冲必须过 DMA 可达性清单(`内存内容分配策略.md`) | 违例 = 静默传错(N-11 同类坑);克隆芯片 CCM 可靠性风险见 `docs/内存管理/RAM使用分析与优化方案.md` §7 |
## 4. 趋势基线(`scripts/ram_baseline.tsv`)
@@ -81,7 +81,7 @@ UI 16 KB / RTOS 堆 56 KB / 应用 8 KB**。归集口径(按 `.map` 对象名
- **每次 `@build` 后**:`& '.venv\Scripts\python.exe' scripts/ram_baseline.py --update`
追加一行(Code/RO/RW/ZI + 三区水位 + Top-10 RW+ZI 对象;同日重跑覆盖当日行)。
- **看趋势 / 找增量**:`scripts/ram_baseline.py --trend`——高亮较上一行 RW+ZI 增长 >1 KB 的对象,
即 `docs/内存管理/RAM使用分析与优化方案.md` §0.3 "ZI 增量 >1KB 必须在提交说明中解释"的机械化;提交说明直接抄 `--trend` 输出行。
即 `docs/内存管理/RAM使用分析与优化方案.md` §4.0.3 "ZI 增量 >1KB 必须在提交说明中解释"的机械化;提交说明直接抄 `--trend` 输出行。
## 5. 超配额流程
+3 -3
View File
@@ -112,7 +112,7 @@
| `` `看门狗设计说明.md` §10.4 ``(裸文件名,全库唯一) | ✅ 硬门禁 |
| `看门狗设计说明 §10.4`(省略 `.md`,文件名全库唯一) | ✅ 硬门禁 |
| `链路延迟 §8.3`(文档名**前缀**,全库唯一) | ✅ 硬门禁 |
| 「内存方案 §1.4」——已登记别名 | ✅ 硬门禁 |
| 「内存方案 §2.1」——已登记别名 | ✅ 硬门禁 |
| 「报告 §4.2」——昵称歧义、未登记 `[示例]` | ⚠️ 判不了 → 进 `--section-todo` 清单 |
| 本文档内「见 §3.1」 | ✅ 硬门禁(本文档必须有该编号;确实指别篇却省了文档名的,机器判不了 → 跳过并进待办) |
@@ -161,7 +161,7 @@
**它管不到的三件事**(脚本会在结尾提示):
1. **昵称式引用**(「内存方案 §1.4」)与正文里的裸文件名散文 → 落盘后 `git grep -n '<旧文件名>'` 扫一遍;
1. **昵称式引用**(「内存方案 §2.1」)与正文里的裸文件名散文 → 落盘后 `git grep -n '<旧文件名>'` 扫一遍;
2. **生成物**(`docs/images/引用关系图.*`)不改写 → 重跑 `& 'tools\refgraph\gen_refgraph.bat'`;
3. 改的是 `.c/.h` 且**新增了目录** → 还要把目录加进 `uvprojx` 的 `<IncludePath>`(→ `new-source-file` skill),随后 `@build` 验证。
@@ -261,7 +261,7 @@
- 外部 URL(`http/https/mailto`)与纯锚点(`#...`)
- `docs/芯片手册/` 等外部资料目录
**章节引用(D 类)另有的跳过**:昵称式引用(「内存方案 §1.4」)、非紧邻写法、变更记录小节内的历史指称、外部手册编号(RM0090 等)、目标文档本身不用数字编号标题。
**章节引用(D 类)另有的跳过**:昵称式引用(「内存方案 §2.1」)、非紧邻写法、变更记录小节内的历史指称、外部手册编号(RM0090 等)、目标文档本身不用数字编号标题。
> ⚠️ 状态词按**就近**(±40 字符)判定,不是整行——否则文件名本身含「不存在」「删除」等字样时会误豁免同行的其它真实引用(2026-09-25 实测修正)。
+3 -3
View File
@@ -63,8 +63,8 @@
| `iec104Task` | 2560 | Normal | 等网络 → IEC 60870-5-104 上行(:2404 监听 + link 状态机,遥信/遥测/定值区,B4) | `iec104_serv_task` |
| `protectTask` | 2048 | Normal | 保护判据 20ms 节拍:`prot_elm_run()` + L5-4 采样故障闭锁 + L5-1 定值校验和巡检(B5);不依赖网络,纳入 IWDG `required_mask` | `protect_task` |
> `idle`(128 words)/ `timer`(256 words)由 FreeRTOS 内核创建。所有任务栈从 FreeRTOS `heap_4` 堆分配(**已迁 CCM**,2026-10-12 扩容至 56 KB,见 `docs/内存管理/RAM使用分析与优化方案.md` §1.2/§2.3)。
> **栈实测与预算**(2026-10-10~14,工具 `scripts/stack_report.py` = Keil 静态调用图 + 串口 `[STACK]` 水位;详见 `docs/内存管理/RAM使用分析与优化方案.md` §0.6/§2.3):
> `idle`(128 words)/ `timer`(256 words)由 FreeRTOS 内核创建。所有任务栈从 FreeRTOS `heap_4` 堆分配(**已迁 CCM**,2026-10-12 扩容至 56 KB,见 `docs/内存管理/RAM使用分析与优化方案.md` §4.1.2/§2.3)。
> **栈实测与预算**(2026-10-10~14,工具 `scripts/stack_report.py` = Keil 静态调用图 + 串口 `[STACK]` 水位;详见 `docs/内存管理/RAM使用分析与优化方案.md` §4.0.6/§2.3):
> defaultTask 实测峰值 **2152B/5120B(余 41%)**、静态最坏链 3016B;netTask 实测峰 **1116~1164B/3840B**(开机~FTP 传输期,2026-10-14)、静态链 1912B;ftpTask 实测 740B/4096B、静态 1216B;timeSyncTask 实测 616B/2560B、静态 1264B;uiTask 实测 4016B/16384B;adcTask 实测 952B/2560B、静态 1320B;iec104Task 静态 1456B(未上板);protectTask 静态 872B(未上板);testTask 静态 496B(仅测试构建)。
> ⚠ 改栈或加大栈内局部变量前后**必须**重跑该脚本——静态分析低估含递归/间接调用的链(优化前 ftpTask:静态 2528B vs 实测 3480B)。
@@ -336,7 +336,7 @@ flowchart LR
| 墙钟时间维护 | `docs/子系统设计/系统墙钟时间维护说明.md` |
| 控制台单写者日志(USART1 调试流) | `docs/子系统设计/控制台单写者日志设计说明.md` |
| RAM 预算与优化 | `docs/内存管理/RAM使用分析与优化方案.md` |
| 栈/内存观测工具 | `scripts/stack_report.py`(Keil 静态调用图 × 串口 `[STACK]` 水位,判定取两者更坏者)· `scripts/ram_report.py`(.map 静态占用)—— 用法与基线见内存方案 §0.6 |
| 栈/内存观测工具 | `scripts/stack_report.py`(Keil 静态调用图 × 串口 `[STACK]` 水位,判定取两者更坏者)· `scripts/ram_report.py`(.map 静态占用)—— 用法与基线见内存方案 §4.0.6 |
| FTP 功能与陷阱 | `docs/测试说明/FTP_Test_Guide.md` §8.6(启动门)/ §8.7(单客户端)· 脚本 `test/host/ftp_up_dl_test.py`、`test/host/ftp_regression.py`、`test/host/ftp_integrity_test.py` |
| UI 设计架构 / 内容页 / 菜单 | `docs/UI设计/UI_设计架构.md` · `内容页架构.md` · `菜单页面设计.md` |
| UI 自动化测试 | `docs/UI设计/UI_自动化测试流程.md` |
+3 -3
View File
@@ -49,7 +49,7 @@
## 4. PC 端脚本手测(主要手段)
所有脚本支持 `--host`(默认 `192.168.1.100`)、`--port`(默认 21),在 `test/` 目录运行;`ftp_up_dl_test.py` / `ftp_speed_test.py` 内置**连接重试**(单客户端:旧会话未释放时自动等待重试,默认 6 次 × 3s)。
推荐入口:`& '.venv\Scripts\python.exe' test/host/ftp_up_dl_test.py`(功能闭环:登录/TYPE I/PASV/STOR/LIST/RETR/比对/DELE/QUIT)与 `& '.venv\Scripts\python.exe' test/host/ftp_speed_test.py --size 128`(大文件 + 吞吐);后者同时是 **ftpTask 栈水位测量**的负载(见 `docs/内存管理/RAM使用分析与优化方案.md` §0.6)。
推荐入口:`& '.venv\Scripts\python.exe' test/host/ftp_up_dl_test.py`(功能闭环:登录/TYPE I/PASV/STOR/LIST/RETR/比对/DELE/QUIT)与 `& '.venv\Scripts\python.exe' test/host/ftp_speed_test.py --size 128`(大文件 + 吞吐);后者同时是 **ftpTask 栈水位测量**的负载(见 `docs/内存管理/RAM使用分析与优化方案.md` §4.0.6)。
### 4.1 冒烟测试:`ftp_test.py`
@@ -109,7 +109,7 @@
- [ ] 默认构建烧录,串口见 `[FTP] waiting for connection`(缺此行 → 见 §8.6)。
- [ ] `ftp_test.py` 冒烟 PASS(连接 / 登录 / PASV / LIST)。
- [ ] `ftp_up_dl_test.py` 上传下载比对 `MATCH`(10 项全 PASS)。
- [ ] `ftp_speed_test.py` 大文件 `MATCH` + 记录速度基线;同时用 `scripts/stack_report.py` 复核 ftpTask 栈余量(内存方案 §0.6)。
- [ ] `ftp_speed_test.py` 大文件 `MATCH` + 记录速度基线;同时用 `scripts/stack_report.py` 复核 ftpTask 栈余量(内存方案 §4.0.6)。
- [ ] (可选)GUI 客户端手动上传/下载成功。
- [ ] (可选)阶段 8 自动化 `TC-NET-801/802/803` PASS。
@@ -147,7 +147,7 @@
- 现象:串口无 `[FTP] waiting for connection...`、无 `[LFT_INET]` 报错,`[STACK]` 行里 ftpTask 峰值极低(实测 156 B,仅为任务入口),21 端口始终 `connection refused`。
- 根因(2026-09-21 定位并修复):`osEventFlagsWait` 默认**消费**所等待的位,而 `SYS_EVT_NET_READY` / `SYS_EVT_STORAGE_READY` 是**状态位**。`netTask` 等 `STORAGE_READY` 时清掉 bit1,`uiTask` / `timeSyncTask` 等 `NET_READY` 时清掉 bit0 → `ftpTask` 的 `WaitAll(bit0|bit1)` 条件永不成立,任务永久阻塞在内核等待队列(无日志、无报错、静默失效)。
- 修复:**所有就绪位等待加 `osFlagsNoClear`**(`Src/freertos.c` 的 ftpTask、`App/task/ui_task.c`、`App/sync/time_sync.c`、`App/task/net_task.c`、`test/board/ch395f_test_task.c`);命令位(`SYS_EVT_CFG_SAVE` / `SYS_EVT_NET_RECONFIG`)保持消费型等待不变。
- 验证:修后串口出现 `[FTP] waiting for connection...`、21 端口可连;ftpTask 的 `[STACK]` 峰值握手后升到 ~2.5KB、128KB 传输后升到 ~3.4KB(该值曾触发余量告警,已在 2026-09-21 把 lftpd 大局部变量静态化后降到 **1184 B / 4096 B,余量 71%**——见内存方案 §1.4/§0.6)。
- 验证:修后串口出现 `[FTP] waiting for connection...`、21 端口可连;ftpTask 的 `[STACK]` 峰值握手后升到 ~2.5KB、128KB 传输后升到 ~3.4KB(该值曾触发余量告警,已在 2026-09-21 把 lftpd 大局部变量静态化后降到 **1184 B / 4096 B,余量 71%**——见内存方案 §4.1.4/§4.0.6)。
### 8.7 单客户端限制:端口"通"但随后连接被拒(旧会话未释放)
+1 -1
View File
@@ -41,6 +41,6 @@
## 4. 关联
- 沉淀约定:`../测试开发标准流程.md` §7.3
- 栈水位探针与基线:`../../内存管理/RAM使用分析与优化方案.md` §0.2 / §0.6
- 栈水位探针与基线:`../../内存管理/RAM使用分析与优化方案.md` §4.0.2 / §4.0.6
- 当前诊断基线:`../../任务规划/基线与待确认.md` §3
- 本流程立项:原 `../../任务规划/README.md` S-2(2026-09-22 提出;2026-10-05 固化为本节流程后销项)
@@ -512,7 +512,7 @@ $env:DTU_KEIL_UV4 = 'D:\NotExist\UV4.exe' # 仅当前会话,不动用户级
- **任务栈配置不在本文重复**:唯一事实源是 `Src/freertos.c`(`.ioc` 的 `FREERTOS.Tasks01`),
受管表只有两份——根 `README.md` 与 `AGENTS.md` 里由 `scripts/gen_task_table.py --write` 生成的
`TASK_TABLE` 块,由 `task-table` 钩子校验;每任务的静态最坏链、实测峰值与余量台账在
`docs/内存管理/RAM使用分析与优化方案.md` §2.3(配置与实测峰值)与 §0.6(静态最坏链与判定)。
`docs/内存管理/RAM使用分析与优化方案.md` §2.3(配置与实测峰值)与 §4.0.6(静态最坏链与判定)。
- ⚠ **不要再在本文抄一份任务表**:`task-table` 钩子的校验面只有上面那两份受管块,抄出来的副本
没有门禁、改栈后必然漂(2026-10-22 删除本文历史副本时,它已缺 `adcTask` / `iec104Task` /
`protectTask` 三个任务,`defaultTask`/`timeSyncTask` 两值也停在旧数)。
@@ -438,7 +438,7 @@ stateDiagram-v2
|---|---|---|
| 静态 RAM | 48 B(11 个 `uint32_t` = 44 B + 1 个 `uint8_t` 与对齐 4 B) | 判定层的全部驻留成本;无堆分配(自研代码禁 `malloc`,见根 `AGENTS.md` 平台约束) |
| 每拍 CPU | 约 20 条以内的整数加减与比较;无乘除、无浮点、无数组访问 | 与 1 kHz 节拍同频,量级远小于一次 `adc_input_poll()`(实测 8 us) |
| 栈 | 判定本体只有 3 个局部量;最坏链在编排层的日志调用尾 | 判定层的日志尾是 `adcTask` 这条链加深的起点,曾直接触发抬栈一档;当前值与余量台账见 `docs/内存管理/RAM使用分析与优化方案.md` §1.4 |
| 栈 | 判定本体只有 3 个局部量;最坏链在编排层的日志调用尾 | 判定层的日志尾是 `adcTask` 这条链加深的起点,曾直接触发抬栈一档;当前值与余量台账见 `docs/内存管理/RAM使用分析与优化方案.md` §2.3(配置与实测峰值)与 §4.0.6(静态最坏链与判定) |
| Flash | 主要花在编排层的日志调用点,判定层本身很小 | 每条 `DBG_*` 约 90 B Flash,故新增位**不要**顺手加日志 |
**观测盲区**(如实登记,别在别处再假设它们存在):
@@ -590,7 +590,7 @@ stateDiagram-v2
- 但入口必须真的存在并被调用:否则函数头的「限定条件」就是假的,而下一个人(或 agent)
会照着它推理。运行期要重建参照系(例如重新跑了会清零计数的驱动 `*_init()`),调用的就是它。
- 该入口**禁止加日志**:它在 adcTask 的静态最坏调用链上(栈预算口径见本节表末与
`docs/内存管理/RAM使用分析与优化方案.md` §1.4)。
`docs/内存管理/RAM使用分析与优化方案.md` §4.0.6)。
---
@@ -647,7 +647,7 @@ stateDiagram-v2
- `docs/硬件平台/ADC通道映射说明.md` —— 语义映射唯一权威口径(与「哪一侧静默」直接相关)
- `docs/算法/继电保护功能复现规划.md` §3.1 —— L1 功能项与验收口径(本层 = L1-6 行)
- `docs/算法/继电保护功能复现规划.md` §3.5 —— L5 自检与闭锁(本层供数的 L5-4 在此登记)
- `docs/内存管理/RAM使用分析与优化方案.md` §1.4 —— `adcTask` 栈最坏链台账
- `docs/内存管理/RAM使用分析与优化方案.md` §2.3 与 §4.0.6 —— `adcTask` 栈配置、最坏链与余量台账
- `App/algo/README.md` —— 目录级文件清单与构建测试入口
- `docs/子系统设计/ADC采样节拍设计说明.md` §3.4 —— TIM3 启动位置与顺序(兜底门开机不误锁的前提)
@@ -366,7 +366,7 @@ Select-String -Path 'MDK-ARM\STM32F407-Demo\STM32F407-Demo.map' -Pattern '__heap
### 5.2 结构性诊断(三条)
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)。
→ R-2 的剩余部分**只差"缩堆"这一步**(56→32 KB,`../内存管理/RAM使用分析与优化方案.md` §4.2.2(阶段 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 扩容后的值)。
→ **R-6(栈静态化)是把"池内碎片"变"可核算静态区"的正解,前置条件(观测常开 + `stack_report.py` 每任务最坏链)已满足。** 实时水位以串口 `[STACK]` / 自检打印为准,不在本文固化快照。
+3 -3
View File
@@ -11,7 +11,7 @@
C. 文件名规范 目录名 / 文件名不得含空格与路径敏感字符(见 docs/文档规范.md §1)
D. 章节引用 `docs/xxx.md` §N.M —— 目标文档里必须真有该编号的标题。
刻意只校验**紧邻写法**(文档名与 § 之间只允许空白/反引号/括号,
实测可把误报压到 0);昵称式引用如「内存方案 §1.4」不校验。
实测可把误报压到 0);昵称式引用如「内存方案 §2.1」不校验。
豁免(不算失效,不报错):
* 以 `[计划]` 前缀标记的路径(尚未创建的约定产物,见《项目审查与优化建议报告》D-3 建议)
@@ -269,7 +269,7 @@ def stem_candidates(token: str, by_stem):
def load_aliases():
"""昵称别名表(`scripts/section_aliases.json`):「内存方案」→ 具体文档。
存在的意义:昵称式引用(「内存方案 §1.4」)读起来顺、机器判不了;登记一次别名,
存在的意义:昵称式引用(「内存方案 §2.1」)读起来顺、机器判不了;登记一次别名,
正文不用改,引用就纳入校验。表里没有的昵称不猜(宁缺毋滥)。
"""
global _alias_map
@@ -337,7 +337,7 @@ def sec_parent_exists(heads, ref: str) -> bool:
return parent in heads or any(h.startswith(parent + ".") for h in heads)
# 昵称前常挂的动词/介词:「见内存方案 §1.4」「顺序按报告 §6.5」——先剥掉再查别名/文件名
# 昵称前常挂的动词/介词:「见内存方案 §2.1」「顺序按报告 §6.5」——先剥掉再查别名/文件名
CUE_STRIP_PREFIXES = ("参见", "详见", "并同步更新", "教训见", "用法与基线见", "反向依赖在",
"改动前先看", "多口需求见", "写入顺序违反", "宏位与", "顺序按",
"依据", "参照", "对照", "违反", "参考", "按", "见", "如", "该", "以",
+1 -1
View File
@@ -16,7 +16,7 @@
"N-33": {
"to": null,
"status": "void",
"note": "栈余量收口条目已随 M1 台架闭环销项(2026-10-12),登记簿行已删,结论在 docs/内存管理/RAM使用分析与优化方案.md §1.4"
"note": "栈余量收口条目已随 M1 台架闭环销项(2026-10-12),登记簿行已删,结论在 docs/内存管理/RAM使用分析与优化方案.md §4.0.6(原 §1.4,2026-10-22 层级编号)"
},
"N-30": {
"to": null,
+3 -3
View File
@@ -11,7 +11,7 @@ RAM 预算门禁 + 趋势台账(登记簿 N-27 前半 / 审查报告 §5.5 三
CI 盲区登记在 `docs/任务规划/测试与架构专项.md` A-5 残余)。
`--gate` 同时校验台账受管区与 `.map` 一致(防漂移,同 `mem-numbers` 思路)。
2. **趋势基线** `scripts/ram_baseline.tsv`(`--update` 每次 @build 后追加一行;
`--trend` 看趋势并高亮 RW+ZI 较上一行增长 >1 KB 的对象——方案 §0.3
`--trend` 看趋势并高亮 RW+ZI 较上一行增长 >1 KB 的对象——方案 §4.0.3
"ZI 增量 >1KB 必须解释"的机械化)。
3. **台账受管区**(`--write` 同步 `docs/内存管理/RAM预算台账.md` 的
`MEM_BUDGET:BEGIN/END` 区:区域水位 + 子系统配额对照)。
@@ -49,7 +49,7 @@ END = "<!-- MEM_BUDGET:END -->"
THRESHOLD_PCT = 92.0 # 报告 §5.5 第 1 条
GATED_REGIONS = ("RW_IRAM1", "RW_CCM") # 门禁约束区;其余区仅展示
GROWTH_ALERT_B = 1024 # 方案 §0.3:增量 >1 KB 必须解释
GROWTH_ALERT_B = 1024 # 方案 §4.0.3:增量 >1 KB 必须解释
TOP_N = 10 # tsv 记录 / 失败附表的对象数
RTOS_HEAP = "RTOS 堆"
@@ -352,7 +352,7 @@ def cmd_trend(args):
grew.append((obj, prev.get(obj, 0), int(b), delta))
if grew:
print(f"\n⚠ 较上一行 RW+ZI 增长 >{GROWTH_ALERT_B} B 的对象"
f"(提交说明须解释,方案 §0.3):")
f"(提交说明须解释,方案 §4.0.3):")
for obj, old, new, d in sorted(grew, key=lambda t: -t[3]):
print(f" +{d:>6,} B {obj} ({old:,} → {new:,})")
else:
+1 -1
View File
@@ -378,7 +378,7 @@ def main(argv=None):
if old_abs.suffix.lower() in (".c", ".h") or new_abs.suffix.lower() in (".c", ".h"):
print("ℹ 改了 .c/.h:已同步 uvprojx 与 CMakeLists;若**新增目录**还需把目录加进 "
"uvprojx 的 <IncludePath>(见 new-source-file skill),随后跑 @build 验证")
print("ℹ 正文里的**裸文件名/昵称引用**(如「内存方案 §1.4」「app_main.c/h」)无法机械判定,"
print("ℹ 正文里的**裸文件名/昵称引用**(如「内存方案 §2.1」「app_main.c/h」)无法机械判定,"
"已尽量改写,仍建议 grep 一遍旧名:git grep -n '<旧文件名>'")
# 3) 复核