diff --git a/AGENTS.md b/AGENTS.md index b5727bd0..3f296e7c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ STM32F407ZGTx @168MHz(HSE 8MHz → PLL M=4/N=168/P=2);**仅 Keil MDK-ARM v | uiTask | 4096 | BelowNormal | | timeSyncTask | 512 | Normal | -(实际值以 `Src/freertos.c` 为准;testTask 仅在启用测试套件时创建。改动栈/缓冲前先核对此表与实测。2026-09-21 上调:defaultTask 512→1024(扛 mount/mkfs/FAT探针/cfg/syslog 深层 FatFS I/O,旧值经 `configCHECK_FOR_STACK_OVERFLOW=2` 钩子实测溢出)、testTask 1024→2048(存储套件 deep call chain + 局部缓冲溢出)。FreeRTOS 堆已迁 CCM,任务栈合计已不占 SRAM;2026-09-21 可观测性落地后实测:**ftpTask 峰值 3480B/4096B(余量仅 616B = 15%,低于 1024B 底线)**,处置见 `docs/内存管理/RAM使用分析与优化方案.md` §0.6——建议先做 §1.4(lftpd 大局部变量静态化)再定栈) +(实际值以 `Src/freertos.c` 为准;testTask 仅在启用测试套件时创建。改动栈/缓冲前先核对此表与实测。2026-09-21 上调:defaultTask 512→1024(扛 mount/mkfs/FAT探针/cfg/syslog 深层 FatFS I/O,旧值经 `configCHECK_FOR_STACK_OVERFLOW=2` 钩子实测溢出)、testTask 1024→2048(存储套件 deep call chain + 局部缓冲溢出)。FreeRTOS 堆已迁 CCM,任务栈合计已不占 SRAM;2026-09-21 可观测性落地后实测:ftpTask 峰值曾达 3480B/4096B(余量 15%,低于 1024B 底线)→ 已按 `docs/内存管理/RAM使用分析与优化方案.md` §1.4 把 lftpd 9 处大局部缓冲静态化(BSS +4.6KB),复测峰值 **1184B/4096B(余量 71%)**,6 任务全 PASS) ## 架构要点 diff --git a/Drivers/BSP/NET/lftpd/lftpd.c b/Drivers/BSP/NET/lftpd/lftpd.c index 03e3ce81..5dcacaf2 100644 --- a/Drivers/BSP/NET/lftpd/lftpd.c +++ b/Drivers/BSP/NET/lftpd/lftpd.c @@ -66,8 +66,8 @@ static int send_response(int socket, int code, bool include_code, bool multiline_start, const char *format, ...) { va_list args; - char message[256]; - char response[512]; + static char message[256]; /* static: off-stack, single-client ftpTask */ + static char response[512]; /* static: off-stack, single-client ftpTask */ va_start(args, format); vsnprintf(message, sizeof(message), format, args); @@ -108,7 +108,7 @@ static int send_list(int socket, const char *path) return -1; } - char name[256]; + static char name[256]; /* static: off-stack, single-client ftpTask */ uint32_t size; int is_dir; @@ -133,7 +133,7 @@ static int send_nlst(int socket, const char *path) return -1; } - char name[256]; + static char name[256]; /* static: off-stack, single-client ftpTask */ uint32_t size; int is_dir; @@ -155,7 +155,7 @@ static int send_file(int socket, const char *path) return -1; } - unsigned char buffer[1024]; + static unsigned char buffer[1024]; /* static: 1KB off-stack, single-client ftpTask */ int read_len; while ((read_len = lftpd_io_read(fh, buffer, sizeof(buffer))) > 0) { @@ -184,7 +184,7 @@ static int receive_file(int socket, const char *path) return -1; } - unsigned char buffer[1024]; + static unsigned char buffer[1024]; /* static: 1KB off-stack, single-client ftpTask */ int err; int total = 0; @@ -550,7 +550,7 @@ static int handle_control_channel(lftpd_client_t *client) goto cleanup; } - char read_buffer[512]; + static char read_buffer[512]; /* static: off-stack, single-client ftpTask */ while (err == 0) { int line_len = lftpd_inet_read_line(client->socket, read_buffer, @@ -584,7 +584,7 @@ static int handle_control_channel(lftpd_client_t *client) bool matched = false; for (int i = 0; commands[i].command; i++) { if (strcmp(commands[i].command, command_tmp) == 0) { - char arg_buf[512]; + static char arg_buf[512]; /* static: off-stack, single-client ftpTask */ const char *arg = NULL; if (index < (int)strlen(read_buffer)) { @@ -649,7 +649,7 @@ int lftpd_start(const char *directory, int port, lftpd_t *lftpd) } } - lftpd_client_t client; + static lftpd_client_t client; /* static: off-stack session context (single session) */ memset(&client, 0, sizeof(client)); strncpy(client.directory, directory, sizeof(client.directory) - 1); client.directory[sizeof(client.directory) - 1] = '\0'; diff --git a/Drivers/BSP/NET/lftpd/lftpd_log.c b/Drivers/BSP/NET/lftpd/lftpd_log.c index 61bbf969..6918590a 100644 --- a/Drivers/BSP/NET/lftpd/lftpd_log.c +++ b/Drivers/BSP/NET/lftpd/lftpd_log.c @@ -6,7 +6,7 @@ void lftpd_log_internal(const char* level, const char* format, ...) { - char buffer[256]; + static char buffer[256]; /* static: called at depth in ftpTask, keep off-stack */ va_list args; va_start(args, format); int err = vsnprintf(buffer, sizeof(buffer), format, args); diff --git a/docs/UI 设计/菜单页面设计.md b/docs/UI 设计/菜单页面设计.md index eab851f9..72215e1e 100644 --- a/docs/UI 设计/菜单页面设计.md +++ b/docs/UI 设计/菜单页面设计.md @@ -772,7 +772,7 @@ ESC/LEFT : d>0 → menu_back() (根框 d=0 不响 | **小计** | | **~0.35 KB**(逻辑层 17 B + 显示层 ~0.33 KB) | **LVGL 池(动态,`LV_MEM` 40 KB 预算内)**:行自绘无对象,池只占**屏上框体**(2~3 个 `lv_obj`)+ 根框装饰/状态行, -典型 ≈ **1~2 KB**,仍远小于 40 KB(池可再缩减,见 `docs/待处理内容/RAM使用分析与优化方案.md`)。 +典型 ≈ **1~2 KB**,仍远小于 40 KB(池可再缩减,见 `docs/内存管理/RAM使用分析与优化方案.md` §1.1;开机实测 UI 池仅用约 3 KB)。 --- diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index 4f7f4571..022ad7a4 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -1,25 +1,29 @@ -# STM32F4-Base RAM 使用分析与优化方案 +# STM32F4-Base RAM 使用分析与优化方案(内存管理主文档) -> 版本:v1.0 日期:2026-09-07 +> 版本:v1.1 日期:2026-09-21 > 适用工程:STM32F4-Base(STM32F407ZGTx @168MHz,FreeRTOS V10.3.1 + CMSIS-RTOS2) -> 数据来源:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(2026-09-07 构建)+ 源码静态分析 + 网络调研 +> 数据来源:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(`@build` 重建)+ `scripts/ram_report.py`(静态台账)+ `scripts/stack_report.py`(静态调用图 × 运行时栈水位)+ 源码静态分析 + 网络调研 +> **本文档是内存管理主文档**(2026-09-21 合并):原《STM32F4-Base 内存使用分析报告》(现状审计/快照)与《内存使用分析规划》(执行规划,已全部闭环)已并入本文;**区域放置与 DMA 可达性规范**单列于 `内存内容分配策略.md`(本文只引用、不重复)。 +> 快照产物:`data/ram_report_<日期>.md`(静态台账)、`data/stack_report_<日期>.md`(栈预算)。 ## 1. 背景与目标 `待处理内容.md` 已提出:"所有任务的内存都是往大的给的,导致内存不够用,要仔细排查内存的预算情况"。 -当前状态(详见第 2 节): +分析起点(2026-09-11 快照,逐符号见 §2.2.1): -- 主 SRAM 128KB 中静态区已占 **122.7KB**(余量仅 5.3KB) -- FreeRTOS 堆 48KB 里还要装下 **~33.5KB 的任务栈**,真正可分配余量只有 ~12KB -- F407 另有 **64KB CCM RAM 完全未用** -- 没有任何运行时测量手段:不知道每个任务实际用了多少栈,改栈全靠拍脑袋 +- 主 SRAM 128KB 中静态区曾占 **122.7KB**,余量仅 5.3KB +- FreeRTOS 堆 48KB 里装下 ~33.5KB 任务栈,可分配余量只有 ~12KB +- F407 另有 **64KB CCM RAM**:**已于 2026-09-21 启用**并把 FreeRTOS 堆迁入(§1.2) +- 起步时没有任何运行时测量手段:不知道每个任务实际用了多少栈,改栈全靠拍脑袋 + +当前状态(2026-09-21,`data/ram_report_20260921.md` + `data/stack_report_20260921.md`):RW_IRAM1 **82,048 / 131,072 B(62.6%)**、RW_CCM **49,184 / 65,536 B(75.0%)**、6 个任务栈全部 PASS(ftpTask 余量 71%,§0.6)。 本方案三个目标: -1. **可测量**:建立"每任务 RAM 消耗 + 每模块静态占用 + 堆余量"的常态化观测手段 -2. **降消耗**:通过 LVGL 内存池、任务栈、CCM RAM、驱动缓冲等手段释放 RAM,把余量从 ~17KB 提升到 ~90KB+ -3. **可持续**:建立 RAM 预算台账与门禁,防止再次"往大了给" +1. **可测量**:建立"每任务 RAM 消耗 + 每模块静态占用 + 堆余量"的常态化观测手段(§0.1~§0.6,已落地) +2. **降消耗**:通过 LVGL 内存池、任务栈、CCM RAM、驱动缓冲等手段释放 RAM +3. **可持续**:建立 RAM 预算台账与门禁,防止再次"往大了给"(§3 长效机制,部分为 `[计划]`) ## 2. 现状测量 @@ -50,38 +54,118 @@ **静态合计 125600B(122.7KB)/ 128KB**(map:16118 `Size: 0x0001ec78`,Max `0x00020000`)。 +#### 2.2.1 逐符号明细(RW_IRAM1,2026-09-21 当前) + +按大小降序(完整台账:`data/ram_report_20260921.md`): + +| # | 大小 (B) | 对象文件 | 内容 | +|---|---|---|---| +| 1 | 40,960 | `lv_mem_core_builtin.o` | LVGL 静态内存池 `ucMemPool[LV_MEM_SIZE]`(§2.6.2) | +| 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) | +| 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 封装静态区 | +| 9 | 1,608 | `freertos.o` | `default_fatfs`(含 512 B 扇区窗)+ `fs_work[512]` + RTOS 句柄 | +| 10 | 1,220 | `tasks.o` | FreeRTOS 内核调度静态区 | +| 11 | 1,024 | `startup_stm32f407xx.o` | C 库启动栈(仅启动期) | +| 12 | 704 | `cfg_file.o` | 配置引擎静态态(双端共享核心) | +| 13–17 | ≤560 | `spi.o` / `usart.o` / `lftpd_io.o` / `lv_init.o` / `net_socket.o` | 外设句柄、`FIL`、LVGL 全局、socket 表 | +| 其余 | ~1.4 KB | 约 30 个 .o | 小型 RW/ZI | + +> **DMA 可达性**:第 4/6/7 项(NAND FTL、CH395F SPI2、GD5F BBT)为 DMA 缓冲,**必须留在主 SRAM**、禁止迁 CCM/SRAM3——权威清点与规则见 `内存内容分配策略.md`(本文不重复)。 +> 2026-09-11 快照(含 `heap_4.o` 49,152 B 在 SRAM1 的历史布局)见 `data/ram_report_20260911.md`;堆已于 2026-09-21 迁至 CCM(§1.2)。 + ### 2.3 任务栈(从 48KB 堆动态分配) -`Src/freertos.c` 中 `osThreadAttr_t.stack_size` 单位为**字节**(CubeMX 生成代码,`512 * 4` 表示 2048 字节): +![任务栈预算](../images/任务栈预算.svg) -| 任务 | 配置栈 (freertos.c 行号) | 字节 | 实际职责 | 问题 | +`Src/freertos.c` 中 `osThreadAttr_t.stack_size` 单位为**字节**(CubeMX 生成代码,`1024 * 4` 表示 4096 字节): + +| 任务 | 配置栈 | 字节 | 实际职责 | 2026-09-21 实测峰值(§0.6) | |---|---|---|---|---| -| uiTask | `4096*4` (:99) | 16KB | LVGL 1bpp 离屏渲染(渲染缓冲在静态区,栈只走调用链) | 明显过大,无实测依据 | -| netTask | `1024*4` (:78) | 4KB | CH395F 轮询(DMA 搬运,栈压力小) | 偏大 | -| ftpTask | `1024*4` (:84) | 4KB | lftpd(有大栈局部变量,见 2.4) | 4KB 接近其峰值,需实测 | -| testTask | `1024*4` (:90) | 4KB | 产品构建下空转 | 产品构建可减到 1KB | -| defaultTask | `512*4` (:71) | 2KB | FatFS 挂载 + LED 闪烁 | 偏大 | -| timeSyncTask | `512*4` (:63) | 2KB | TCP 时间同步 | 偏大 | -| idle | `configMINIMAL_STACK_SIZE=128` | 0.5KB | 空闲 | 正常 | -| timer | `configTIMER_TASK_STACK_DEPTH=256` | 1KB | 软定时器 | 正常 | +| uiTask | `4096*4` | 16KB | LVGL 1bpp 离屏渲染(渲染缓冲在静态区,栈只走调用链) | 3824 B(余量 76%)→ 可大幅缩减 | +| netTask | `1024*4` | 4KB | CH395F 轮询(DMA 搬运,栈压力小) | 588 B(余量 85%) | +| ftpTask | `1024*4` | 4KB | lftpd FTP 服务 | 1184 B(余量 71%,§1.4 静态化后) | +| testTask | `2048*4` | 8KB | 测试套件(产品构建空转;存储套件深链需要) | 348 B(产品构建) | +| defaultTask | `1024*4` | 4KB | FatFS 挂载/探针 + 配置引擎 + syslog + 栈水位探针 | 2136 B(余量 47%) | +| timeSyncTask | `512*4` | 2KB | TCP 时间同步 | 420 B(余量 79%) | +| idle(内核) | `configMINIMAL_STACK_SIZE=128` | 0.5KB | 空闲 | 未观测(未取句柄) | +| timer(内核) | `configTIMER_TASK_STACK_DEPTH=256` | 1KB | 软定时器 | 未观测 | -**6 个应用任务栈合计 32KB,加 idle+timer 共 ~33.5KB,全部从 48KB 堆里扣** → 堆的真实余量 ≈ 48 − 33.5 − TCB(含 FPU 上下文 ~2.3KB) − 队列/事件组(~0.5KB) ≈ **11~12KB**。 +> 任务栈全部从 FreeRTOS 堆(48KB,2026-09-21 已迁 CCM)分配。**"配置栈"列以 `Src/freertos.c` 为唯一来源;实测峰值/余量判定以 §0.6 的 `stack_report` 基线为准**(本表数值同步自该基线)。 ### 2.4 动态分配点与隐患 1. **lftpd 用 C 库 `malloc`(不是 FreeRTOS 堆)**:`Drivers/BSP/NET/lftpd/lftpd_io.c:44,49,54,189` 调用 `malloc()`。工程没有把 C 库 malloc 重定向到 `pvPortMalloc`,C 库堆只有 startup 的 **512B**(map:16173)→ FTP 路径名稍长(>250 字符左右)就必然分配失败。**必须修**(见 4.4)。 -2. **lftpd 大栈局部变量**:`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])。单次调用链栈峰值可达 2~3KB,是 ftpTask 需要 4KB 栈的主因。 +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+ 栈。 3. **DMA 缓冲位置**:CH395F(SPI2 DMA)、GD5F(SPI1 DMA)、UART DMA 缓冲必须由 DMA 访问 → **只能放主 SRAM,不能放 CCM**(CCM 仅 CPU 可达,见 4.3)。 -### 2.5 结论:紧张的三个根因 +### 2.5 结论:紧张的三个根因(含 2026-09-11 审计量化) | 根因 | 占用 | 可回收性 | |---|---|---| -| ① LVGL 内存池 40KB 对 1bpp 小屏菜单过大 | 40KB | 高(目标 8~16KB) | -| ② 任务栈"往大了给",32KB 无实测依据 | 32KB | 高(目标 ~15KB) | -| ③ 64KB CCM 未利用,UI 类大缓冲挤在主 SRAM | — | 高(可整体迁 CCM ~29KB) | +| ① LVGL 内存池 40KB 对 1bpp 小屏菜单过大 | 40KB | 高(目标 8~16KB;**开机实测 UI 仅用 ~3KB**,见 §2.6.2) | +| ② 任务栈"往大了给",无实测依据 | ~39KB | 高(§0.6 已有实测:uiTask 3.8/16KB、netTask 0.6/4KB) | +| ③ 64KB CCM 曾完全未利用,缓冲挤在主 SRAM | — | ✅ 2026-09-21 已启用(堆迁入 CCM),CCM 余量 ~16KB | -另有小项:FF_FS_TINY=0(每打开文件 512B 扇区窗)、ui_net_sink 调试缓冲常驻、lftpd 栈局部变量。 +量化补充(来源:2026-09-11 审计快照,口径与方法见 §8.4): + +- 两池(FreeRTOS 48KB + LVGL 40KB)曾占主 SRAM 的 **68%**,是当时"SRAM 几乎耗尽(95.4%,余 5.8KB)"的直接原因;**堆迁 CCM 后该结构性压力已解除**(现 RW_IRAM1 62.6%) +- heap_4 **不合并相邻空闲块** → 长期运行需盯碎片,`min_ever`(现 4,848 B)是硬指标而非 `free` +- 未映射资源:SRAM3 64KB(scatter 未映射;clone 芯片可能无此区);CCM 已用 +- 曾经的三条中风险项:新任务无处安置(已缓解)、堆碎片(仍存)、栈溢出检测缺失 → **已于阶段 0 开启 `configCHECK_FOR_STACK_OVERFLOW=2` 并实测抓到过溢出**(§0.1) + +小项:FF_FS_TINY=0(每打开文件 512B 扇区窗)、ui_net_sink 调试缓冲常驻(lftpd 栈局部变量 ✅ 2026-09-21 已静态化,见 §1.4)。 + +### 2.6 堆 / LVGL 池 / C 库 / Flash 明细 + +#### 2.6.1 FreeRTOS 堆(48 KB;2026-09-21 已迁 CCM `RW_CCM`) + +`configTOTAL_HEAP_SIZE = 49,152`(heap_4)。内部构成: + +| 组件 | 大小 | 依据 | +|---|---|---| +| 6 × 应用任务栈 | 38,912 B | 各 `stack_size` 求和(§2.3;2026-09-11 快照为 34,304 B) | +| 2 × 内核任务栈(idle/timer) | 1,536 B | `configMINIMAL_STACK_SIZE` / `configTIMER_TASK_STACK_DEPTH` | +| 8 × TCB | ~640 B | `sizeof(TCB_t) ≈ 76 B`(8 字节对齐后 80 B)——逐字段核算见旧报告附录,字段来源 `tasks.c:252-329` | +| netMsgQueue | ~448 B | `osMessageQueueNew(8, 48)` = `Queue_t` 56 B + 8×48 B | +| sys_evt 事件组 | ~24 B | `osEventFlagsNew` | +| **实测水位(2026-09-21,含 FTP 负载)** | `free = 8,784 B` / **`min_ever = 4,848 B`** | 串口 `[STACK] heap …` | + +> lftpd 会话期间还会 `pvPortMalloc` 路径缓冲/DIR(<1 KB 瞬态)。**堆余量底线按 ≥4 KB 守住**(`min_ever` 是全负载历史最低点,比 `free` 更有约束力)。 + +#### 2.6.2 LVGL 内存池(40 KB) + +| 参数 | 值 | 来源 | +|---|---|---| +| `LV_MEM_SIZE` | 40,960 B (40 KB) | `Lib/lv_conf.h`(模拟器 `simulator/lv_conf.h` 需同步) | +| 分配器 / 色深 / OS 抽象 | TLSF / 1 (1bpp) / 无 | 同上 | + +池内装 UI 对象树、样式、字符串、动画对象,均为运行时分配;TLSF 元数据开销约 10~15%,净可用 ≈34~36 KB。**开机实测**(2026-09-21 日志):`menu build start: total=39476 free=37248` → `menu build done: used=2976`,即当前 UI 仅占约 **3 KB**——这是 §1.1 把池缩到 8~16 KB 的直接依据。静态无法估算池占用,需运行时 `LV_USE_MEM_MONITOR`(`lv_mem_monitor_t`)。 + +#### 2.6.3 C 库堆/栈(microlib) + +| 组件 | 大小 | 说明 | +|---|---|---| +| 启动栈 | 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 库代码(Flash) | ~4.2 KB | printf / 浮点 / 字符串 | + +#### 2.6.4 Flash 1 MB 分布(2026-09-21) + +| 段 | 大小 (B) | 占比 | +|---|---|---| +| Code (inc. data) | 234,976 | 22.4% | +| RO Data | 13,824 | 1.3% | +| RW 初值 | 632 | — | +| **合计 ROM** | **249,432** | **23.8%**(余 ~780 KB) | + +![Flash 布局](../images/内存布局_FLASH.svg) + +> 2026-09-11 快照的模块级分解(LVGL ~72 KB / 应用+UI ~47 KB / FatFS+dhara ~11 KB / CH395F ~10 KB / HAL ~9 KB / C 库 ~4.3 KB / FreeRTOS ~4.6 KB)见 `data/ram_report_20260911.md`;重跑 `scripts/ram_report.py` 即得当前分解。 ## 3. 网络调研摘要 @@ -208,22 +292,21 @@ void vApplicationMallocFailedHook(void) | 任务 | 配置 (B) | 静态最坏 (B) | 实测峰值 (B) | 最坏 (B) | 余量 | 判定 | |---|---|---|---|---|---|---| | defaultTask | 4096 | **2776** | 2136 | 2776 | 1320 (32%) | PASS | -| **ftpTask** | 4096 | 2528 | **3480** | **3480** | **616 (15%)** | **FAIL** | -| netTask | 4096 | 440 | 600 | 600 | 3496 (85%) | PASS | -| uiTask | 16384 | 912 | 3804 | 3804 | 12580 (76%) | PASS | +| **ftpTask**(优化前) | 4096 | 2528 | **3480** | **3480** | **616 (15%)** | **FAIL** | +| **ftpTask**(§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 | | testTask(产品构建) | 8192 | 168 | 348 | 348 | 7844 (95%) | PASS | 实测堆:`free=8784 B / min_ever=4848 B`(48KB 池)。 -**待办(由本次实测直接得出)**:ftpTask 余量仅 616 B(15%),低于 1024 B / 30% 底线。两条路: -① 按 §1.3 定栈规则 `峰值×1.5 + 1KB ≈ 6.2KB` → ftpTask 提到 **1600 words (6400 B)**(最省事); -② 先做 §1.4(lftpd 大局部变量移静态:`handle_control_channel` 1104 B、`send_file`/`receive_file` 1088 B、`send_response` 800 B)再复测,栈只需小步上调。建议 **②→①** 顺序:先砍宽度,避免长期背 6.4KB。 +**处置结果(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)。 **静态与实测是互补的两套数据(必须都留)**: - defaultTask:静态 2776 > 实测 2136——静态是**上界**(最深分支未必走到),实测是**已走路径**。**判栈够不够看两者更坏者,做定栈优化看实测。** -- netTask / uiTask / testTask / timeSyncTask / **ftpTask**:实测 > 静态——静态是**下界**(互递归、间接调用、ISR 上下文保存 ~100B 未计入)。ftpTask 最典型:静态 2528 B(控制通道握手链)**低估了 950 B**,真实峰值 3480 B 只在 128KB STOR 传输时暴露 → **不跑真实业务就定栈 = 拍脑袋。** +- netTask / uiTask / testTask / timeSyncTask / **ftpTask**:实测 > 静态——静态是**下界**(互递归、间接调用、ISR 上下文保存 ~100B 未计入)。ftpTask 最典型:优化前静态 2528 B(控制通道握手链)**低估了 950 B**,真实峰值 3480 B 只在 128KB STOR 传输时暴露 → **不跑真实业务就定栈 = 拍脑袋。** §1.4 静态化后静态 472 B / 实测 1184 B,两者重新收敛(差距回到 ISR/上下文量级)。 - 本次可观测性的第一个产出不是数字而是**缺陷**:ftpTask 因就绪位被其它等待者消费而**从未启动**(21 端口一直无监听),修好后才有了上面这组数据——详见 `docs/测试说明/FTP_Test_Guide.md` §8.8。 要点与边界: @@ -231,7 +314,7 @@ void vApplicationMallocFailedHook(void) - defaultTask 的静态最坏链是**配置保存/旧档迁移**路径,不是首启探针路径: `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 实测完全一致。 -- 帧宽榜直接点名宽度大户(自身帧):`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——与 §1.3/§1.4 的整改项一一对得上。 +- 帧宽榜直接点名宽度大户(自身帧):`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)。 - **静态分析的天然盲区**:递归/互递归(LVGL widget 树、`layout_update_core`、`apply_theme` 等)与间接调用只能按 "Cycle" 近似,armlink 明确标注 `+ Unknown(Cycles, Untraceable Function Pointers)`。因此 uiTask 的 912 B 是**下限**(实测 3804 B),必须靠运行时水位。 - testTask 在产品构建里只是 `osDelay` 空循环(168 B);2048 words 是给**存储套件构建**(`TEST_SUITE_STORAGE`)预留的,测套件时应重跑本脚本看该构建的静态值。 - **测 ftpTask 的复现配方**:设备就绪后跑 `& '.venv\Scripts\python.exe' test/ftp_speed_test.py --size 128`(STOR 128KB + RETR + 逐字节比对),再按上面命令叠加串口日志出报告。**不要用裸 TCP 工具探测 21 端口**——CH395F 单客户端复用,探测会占掉唯一会话(见 FTP 指南 §8.9)。 @@ -302,11 +385,25 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/ 每改一批 → `@build` + 全功能回归 + 复测高水位(`free_min` 应 ≥ 新栈×30%),不满足则回调。 -#### 1.4 lftpd 大栈局部变量 → 静态缓冲(ftpTask 栈峰值降 1~2KB) +#### 1.4 lftpd 大栈局部变量 → 静态缓冲(✅ 2026-09-21 完成;ftpTask 峰值 3480 → 1184 B) -`lftpd.c` 中 `buffer[1024]`×2(:156,185)、`response[512]`(:68)等改为文件级 `static`(lftpd 单客户端、由 ftpTask 串行处理,静态化安全)。 +已把 `lftpd.c` / `lftpd_log.c` 中 **9 个大局部缓冲改为函数级 `static`**(lftpd 单客户端,全部调用与日志都在 ftpTask 内串行执行,静态化安全;各函数**各自独立缓冲**,无嵌套/别名冲突): -> lftpd 属第三方代码(pre-commit 排除范围),只做最小侵入改动,改动点在本台账登记。 +| 位置 | 原栈占用 (B) | 说明 | +|---|---|---| +| `lftpd.c:69,70` `send_response` | 256 + 512 | 每条响应都用到 | +| `lftpd.c:111,136` `send_list` / `send_nlst` | 256 × 2 | LIST/NLST 目录名 | +| `lftpd.c:158,187` `send_file` / `receive_file` | **1024 × 2** | 传输缓冲(原峰值主因) | +| `lftpd.c:553,587` `handle_control_channel` | 512 + 512 | 命令行 / 参数(`arg` 指向它,静态化后生命周期只增不减) | +| `lftpd.c:652` `lftpd_start` 的 `client` 上下文 | ~264 | `lftpd_client_t` 会话上下文 | +| `lftpd_log.c:9` `lftpd_log_internal` | 256 | 深层日志点 | + +- **结果**:ftpTask 实测峰值 **3480 → 1184 B**(余量 616 → **2912 B / 71%**,判定 PASS);静态调用图 2528 → 472 B;帧宽榜中 lftpd 全部退出前 15。 +- **代价**:静态 BSS **+4,616 B**(RW_IRAM1 77,432 → 82,048 B / 128 KB,余量仍 ~48 KB;CCM 与 Flash 不变)。 +- **验证**:`@build` 0/0 → 烧录 → `@test` PASS → `ftp_up_dl_test.py` 10/10 PASS + `ftp_speed_test.py --size 128` 逐字节一致(功能与搬迁前完全一致,反证无别名/重入问题)。 +- **结论:原 ① 方案(ftpTask 涨到 1600 words)不再需要**,栈保持 1024 words(4096 B)。 + +> lftpd 属第三方代码(pre-commit 排除范围),只做最小侵入改动(每处仅加 `static` + 单行注释),改动点在本台账登记。 #### 1.5 其他小项 @@ -332,7 +429,7 @@ ISR 余量:该任务运行期间可能被中断打断的最深中断链(SPI/ ### 阶段 3:长效机制(0.5 天 + 长期) -1. **RAM 预算台账**(本节模板落地为 `docs/内存管理/RAM预算台账.md`):记录每个 Region/缓冲/栈的计划值、实测值、责任人、变更历史;每次改内存相关代码必须更新 +1. **RAM 预算台账 `[计划]`**(模板见 §8.2;落地文件 `docs/内存管理/RAM预算台账.md` **尚未创建**):记录每个 Region/缓冲/栈的计划值、实测值、变更历史;每次改内存相关代码必须更新 2. **AGENTS.md 增补**:新增任务/缓冲/栈前必须先查台账余量(把现有"内存预算紧张"条目升级为可执行 checklist:先跑 `ram_report.ps1`,算总账,再动手) 3. **pre-commit 门禁**(可选):钩子解析最新 map,RW_IRAM1 使用率 >92% 或 RW_IRAM2 超限 → 提交拦截并提示 4. **@test 扩展**:`verify_log.py` 增加可选 Required 标记 `"[MEM] heap free"`,把堆余量下限纳入自动化回归 @@ -415,4 +512,51 @@ 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:67-185,551,583`、`lftpd_io.c:44-54,189` | +| lftpd 缓冲/malloc | `Drivers/BSP/NET/lftpd/lftpd.c`(大缓冲已静态化,§1.4)、`lftpd_io.c:44-54,189`(`malloc` 隐患,阶段 2.1) | + +### 8.4 分析口径与数据来源(原《内存使用分析规划》并入) + +**芯片物理内存**(数据源 RM0090 §3.1、`STM32F407-Demo.sct`):Flash 1024 KB @`0x0800_0000`;SRAM1 112 KB + SRAM2 16 KB 构成 `RW_IRAM1` 的 128 KB @`0x2000_0000`;SRAM3 64 KB @`0x2002_0000` **未映射**(clone 芯片可能无此区);CCM 64 KB @`0x1000_0000` 仅 CPU 可达(**DMA 不可达**)。区域放置规则见 `内存内容分配策略.md`。 + +**口径**: + +- **静态区** = `RW_IRAM1` / `RW_CCM` 内由链接器直接分配的 RW + ZI(含两个池的本体、驱动缓冲、C 库 HEAP/STACK 节) +- **运行时动态区** = 两个池**内部**的再分配(任务栈 + TCB + 队列;LVGL 对象/样式/字符串)——池本体已在静态区全额计入,**内部细分不重复加总**,只做占比说明 +- **Flash** = `Total ROM Size`(Code + RO + RW 初值) + +**四视角 + 交叉校验**: + +``` +V1 链接视图(map:Grand Totals / Image component sizes / Symbol Table) + ← 静态数字的唯一权威来源 + │ 交叉校验 + ├── V2 运行时堆视图(手算 48KB 池占用;分配点由源码遍历确认) + ├── V3 LVGL 池视图(本体在静态区,只说明用途与运行时占用量级) + └── V4 源码/配置视图(给符号赋语义:用途 / 大小 / 是否 DMA 可达) +``` + +V4 逐项相加须与 V1 的 `Image component sizes` 逐 .o 吻合;不等则以 V1 为准并记录差异原因。 + +**数据来源清单**:`STM32F407-Demo.sct`(区域)、`Inc/FreeRTOSConfig.h`(堆/优先级/静态分配开关)、`cmsis_os2.c:472-476`(**栈大小单位 = 字节**:内部 `/ sizeof(StackType_t)`)、`Src/freertos.c`(任务/队列/事件组属性)、`Lib/lv_conf.h`、`Lib/FatFs/ffconf.h`、`App/app_cfg.h` + `test/test_config.h`(构建口径)、`Drivers/BSP/**` + `App/**`(缓冲语义/用途/DMA 归属);**权威数据 = `@build` 重建后的 `.map`**(历史 map 不得用于结论)。 + +### 8.5 图表与台账重生成 + +```powershell +& '.venv\Scripts\python.exe' scripts/ram_report.py -o "docs\内存管理\data\ram_report_<日期>.md" # 静态台账 +& '.venv\Scripts\python.exe' scripts/stack_report.py -s tools\serial_tool\state\serial.log ` + -o "docs\内存管理\data\stack_report_<日期>.md" # 栈预算(静态 × 实测) +& '.venv\Scripts\python.exe' tools/arch_diagram/gen_memory_svg.py # 内存布局/任务栈预算图 +``` + +产物:`docs/images/内存布局_RAM.svg`、`内存布局_FLASH.svg`、`任务栈预算.svg`(脚本与 SVG 一并提交;PNG 本地预览不提交)。 + +### 8.6 自洽校验(2026-09-11 快照) + +| 层 | 指标 | Per-file 求和 | Grand Totals | 判定 | +|---|---|---|---|---| +| Flash | Code | 219,976 | 219,976 | PASS | +| Flash | RO | 11,496 | 11,496 | PASS | +| SRAM | RW | 528 | 528 | PASS | +| SRAM | ZI | 124,576 | 124,576 | PASS | + +> 差异来自 linker 的 `(incl. Padding)` 行(约 302 B code + 64 B RO + 8 B RW + 2 B ZI);`scripts/ram_report.py` 已排除该行,故 per-file 求和与 Grand Totals 对不上时先查它。 diff --git a/docs/内存管理/STM32F4-Base内存使用分析报告.md b/docs/内存管理/STM32F4-Base内存使用分析报告.md deleted file mode 100644 index 9dfd5986..00000000 --- a/docs/内存管理/STM32F4-Base内存使用分析报告.md +++ /dev/null @@ -1,366 +0,0 @@ -# STM32F4-Base 内存使用分析报告 - -> **版本**: v1.0 **日期**: 2026-09-11 -> **基线**: `02a32be` (ui-research 分支) -> **Map**: `MDK-ARM/STM32F407-Demo/STM32F407-Demo.map` (2026-09-11 23:12:55) -> **构建**: 0 errors, 0 warnings - ---- - -## 1. 摘要 - -STM32F407ZGTx 采用 **128KB 主 SRAM**(SRAM1+SRAM2)和 **1MB Flash**。本报告对两者进行逐层审计。 - -**核心指标** - -| 资源 | 已用 | 总量 | 利用率 | 空闲 | -|------|------|------|--------|------| -| **SRAM** (RW+ZI) | 125,104 B (122.2 KB) | 131,072 B (128 KB) | **95.4%** | 5,968 B (5.8 KB) | -| **Flash** (Code+RO+RW) | 231,472 B (226 KB) | 1,048,576 B (1 MB) | **22.1%** | 817,104 B (798 KB) | -| **FreeRTOS 堆** (48KB 池) | ~35,416 B (34.6 KB) | 49,152 B (48 KB) | **72.1%** | ~13,736 B (13.4 KB) | -| **LVGL 池** (40KB 静态) | 40,960 B (40 KB) — 容量 | 40,960 B | **100%** (全保留) | 运行时未量化 | - -> ⚠ **SRAM 利用率 95.4%,剩余仅 5.8KB。** 空闲 5,968B 无法容纳新的大型静态缓冲或任务栈。新增任何 RAM 需求前必须先释放或裁减现有分配。 - ---- - -## 2. 内存资源总览 - -### 2.1 硬件内存布局 - -``` -STM32F407ZGTx 内存地图 -┌─────────────────────────────────────────────────────────────┐ -│ Flash 1MB (0x0800_0000) │ -│ ├─ Code 214.8 KB (22.1%) │ -│ ├─ RO Data 11.2 KB │ -│ └─ RW Init Data 0.5 KB │ -├─────────────────────────────────────────────────────────────┤ -│ SRAM1+2 = 128KB (0x2000_0000) ← 主 SRAM (RW_IRAM1) │ -│ ├─ RW Data 528 B (初始化) │ -│ ├─ ZI Data 124,576 B (零初始化) │ -│ └─ 空闲 5,968 B (4.6%) │ -├─────────────────────────────────────────────────────────────┤ -│ SRAM3 64KB (0x2001_C000) ← 未使用 (scatter 未映射) │ -│ CCM 64KB (0x1000_0000) ← 未使用 (scatter 未映射) │ -└─────────────────────────────────────────────────────────────┘ -``` - -> SRAM3 和 CCM 在 `.sct` 分散加载文件中未映射。若需扩展 RAM,可考虑将非 DMA 缓冲(如 LVGL 池)迁移至 SRAM3。 - -### 2.2 全局数据量 - -| 指标 | 字节 | KB | 来源 | -|------|------|----|------| -| Code (含 inc. data) | 219,976 | 214.8 | Grand Totals | -| RO Data | 11,496 | 11.2 | Grand Totals | -| RW Data (初始化) | 528 | 0.5 | Grand Totals | -| ZI Data (零初始化) | 124,576 | 121.7 | Grand Totals | -| **Total RO** | **231,472** | **226.0** | Code + RO | -| **Total RW** | **125,104** | **122.2** | RW + ZI | -| **Total ROM** | **232,000** | **226.6** | Code + RO + RW | - ---- - -## 3. 主 SRAM (128KB) 静态分布 - -### 3.1 功能分区总览 - -![SRAM 布局图](../images/内存布局_RAM.svg) - -| 分类 | 大小 (B) | KB | 占比 | 内容说明 | -|------|----------|----|------|----------| -| FreeRTOS 堆池 | 49,152 | 48.0 | 37.5% | `heap_4.o` — 任务栈 + TCB + 队列 | -| LVGL 内存池 | 40,960 | 40.0 | 31.2% | `lv_mem_core_builtin.o` — UI 对象/样式 | -| UI 帧缓冲 | ~13,158 | ~12.8 | 10.1% | 帧缓冲 2×3200 + RemoDispBus 6477 + 状态 | -| FTL / 存储 | ~12,416 | ~12.1 | 9.5% | NAND FTL 页缓冲 + GD5F BBT | -| 网络 / CH395F | ~7,110 | ~6.9 | 5.5% | SPI2 DMA 缓冲 + Socket 控制块 | -| 内核 / CMSIS | ~3,024 | ~2.9 | 2.3% | tasks.o + cmsis_os2.o + timers.o | -| HAL 驱动 | ~1,400 | ~1.4 | 1.1% | SPI/USART/I2C/TIM 句柄 + DMA LG | -| 其他静态 | ~498 | ~0.5 | 0.4% | ADC 状态、RS485 缓冲等 | -| **已用合计** | **125,104** | **122.2** | **95.4%** | | -| **空闲** | **5,968** | **5.8** | **4.6%** | | - -### 3.2 逐符号明细 (RW_IRAM1 Memory Map) - -以下为 `.map` 文件中 RW_IRAM1 区域的逐符号条目,按大小降序。总计 125,094B(差 10B 为对齐填充)。 - -| # | 大小 (B) | 类型 | 对象文件 | 内容 | -|---|----------|------|----------|------| -| 1 | 49,152 | ZI | `heap_4.o` | `ucHeap[configTOTAL_HEAP_SIZE]` — FreeRTOS 堆池 | -| 2 | 40,960 | ZI | `lv_mem_core_builtin.o` | `ucMemPool[LV_MEM_SIZE]` — LVGL 内存池 | -| 3 | 6,746 | ZI | `ui_net_sink.o` | `g_tx[3209]` + `g_lcdmem_data[3204]` + `g_rx[64]` + 状态 | -| 4 | 6,400 | ZI | `ui_port.o` | `g_ui_fbuf[3200]` + `g_ui_draw_buf[3200]` — 160×160 1bpp 帧缓冲 | -| 5 | 6,200 | ZI | `nand_ftl.o` | `s_page_buf/s_cache_buf/s_copy_buf` 各 2048B + dhara 结构 | -| 6 | 4,104 | ZI | `ch395f.o` | `s_spi2_dma_tx_buf[2052]` + `s_spi2_dma_rx_buf[2052]` | -| 7 | 2,576 | ZI | `gd5f2gq5ue.o` | `s_bbt[256]` + `s_bbt_best[256]` + `s_bbt_page_buf[2048]` | -| 8 | 1,720 | ZI | `cmsis_os2.o` | CMSIS-RTOS2 封装静态区 | -| 9 | 1,220 | ZI | `tasks.o` | FreeRTOS 内核调度(就绪链表等) | -| 10 | 1,072 | ZI | `freertos.o` | `default_fatfs`(含 512B 扇区窗) + `fs_work[512]` + RTOS 句柄 | -| 11 | 1,024 | ZI | `startup_stm32f407xx.o` | C 库启动栈(1KB,仅启动期) | -| 12 | 560 | ZI | `spi.o` | `hspi1/hspi2` + `hdma_spi1/2 rx/tx` 句柄 | -| 13 | 556 | ZI | `lftpd_io.o` | `s_ftp_file` (FIL 含 512B 扇区窗) | -| 14 | 552 | ZI | `usart.o` | `huart1/uart4/uart5/uart2/uart3` + DMA 句柄 | -| 15 | 496 | ZI | `lv_init.o` | LVGL 全局状态 | -| 16 | 288 | ZI | `net_socket.o` | `s_net_socks[NET_MAX_SOCKETS]` | -| 17 | 280 | ZI | `timers.o` | FreeRTOS 软件定时器静态区 | -| 18 | 268 | ZI | `rs485.o` | RS-485 设备结构(`rx_buf[256]` + 状态) | -| 19–50 | 920 | RW/ZI | 其他 32 个 .o | 各模块初始化数据与小型 ZI | - -### 3.3 关键 DMA 缓冲约束 - -以下缓冲被 SPI/DMA 引擎直接访问,**必须驻留在主 SRAM**(SRAM1/2,0x20000000–0x2001FFFF): - -| 模块 | 缓冲 | 大小 | DMA 通道 | -|------|------|------|----------| -| CH395F | `s_spi2_dma_tx_buf` + `s_spi2_dma_rx_buf` | 2 × 2,052 = 4,104 B | SPI2 TX/RX DMA | -| NAND FTL | `s_page_buf` + `s_cache_buf` + `s_copy_buf` | 3 × 2,048 = 6,144 B | SPI1 TX/RX DMA | -| GD5F BBT | `s_bbt_page_buf` | 2,048 B | SPI1 RX DMA | - -> **迁移警告**:任何尝试将上述缓冲移至 SRAM3 或 CCM 的操作都会导致 DMA 传输失败。 - ---- - -## 4. FreeRTOS 堆 48KB 内部拆解 - -`configTOTAL_HEAP_SIZE = 49,152` (48KB),heap_4 分配器。 - -![任务栈预算](../images/任务栈预算.svg) - -### 4.1 任务栈分配 - -> 注:栈大小在 `osThreadAttr_t.stack_size` 中以**字节**为单位;CMSIS-RTOS2 在 `cmsis_os2.c:472–476` 将其除以 `sizeof(StackType_t)=4` 转为 words。 - -| 任务 | stack_size (B) | stack_size (words) | 优先级 | 功能 | -|------|----------------|---------------------|--------|------| -| defaultTask | 2,048 | 512 | Normal | FatFS 挂载、LED 闪烁 | -| netTask | 4,096 | 1,024 | Normal | CH395F 轮询、消息处理 | -| ftpTask | 4,096 | 1,024 | Normal | lftpd FTP 服务(含 1KB 栈局部 buffer[1024]) | -| testTask | 4,096 | 1,024 | BelowNormal | 测试套件(产品模式下空转) | -| uiTask | 16,384 | 4,096 | BelowNormal | LVGL UI 主循环 | -| timeSyncTask | 2,048 | 512 | Normal | TCP 墙钟时间同步 | -| idle (内核) | 512 | 128 | 0 (最低) | FreeRTOS 空闲任务 | -| timer (内核) | 1,024 | 256 | 31 (最高) | FreeRTOS 软件定时器 | -| **合计** | **34,304** | **8,576** | | | - -### 4.2 堆内存消耗明细 - -| 组件 | 大小 (B) | 计算方法 | -|------|----------|----------| -| 8 × 任务栈 | 34,304 | 各任务 stack_size 求和 | -| 8 × TCB (任务控制块) | ~640 | sizeof(TCB_t) ≈ 76B(8字节对齐后 80B × 8) | -| netMsgQueue | ~448 | `osMessageQueueNew(8, 48)` = Queue_t(56B) + 8×48B | -| sys_evt 事件组 | ~24 | `osEventFlagsNew` = EventGroup_t | -| **稳态合计** | **~35,416** | | -| **堆空闲** | **~13,736** | 49,152 − 35,416 | - -> **注意**:lftpd 在 FTP 服务期间通过 `pvPortMalloc` 临时分配路径缓冲和 DIR 对象(合计 <1KB),属于瞬态峰值。空闲 13.4KB 在无 FTP 活动时可用。 - -### 4.3 TCB 结构体核算 - -`sizeof(TCB_t)` 基于 `tasks.c:252–329` 逐字段统计(FreeRTOS V10.3.1,ARM Cortex-M4F 配置): - -| 字段 | 类型 | 大小 | 配置条件 | -|------|------|------|----------| -| `pxTopOfStack` | StackType_t* | 4B | 必有 | -| `xStateListItem` | ListItem_t | 12B | 必有 | -| `xEventListItem` | ListItem_t | 12B | 必有 | -| `uxPriority` | UBaseType_t | 4B | 必有 | -| `pxStack` | StackType_t* | 4B | 必有 | -| `pcTaskName[16]` | char[16] | 16B | configMAX_TASK_NAME_LEN=16 | -| `uxTCBNumber` + `uxTaskNumber` | UBaseType_t × 2 | 8B | configUSE_TRACE_FACILITY=1 | -| `uxBasePriority` + `uxMutexesHeld` | UBaseType_t × 2 | 8B | configUSE_MUTEXES=1 | -| `ulNotifiedValue` + `ucNotifyState` | uint32_t + uint8_t | 5B | configUSE_TASK_NOTIFICATIONS=1 | -| `ucStaticallyAllocated` | uint8_t | 1B | tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE | -| **合计(含对齐)** | | **76B → 80B** | 8字节对齐 | - ---- - -## 5. LVGL 40KB 内存池说明 - -`lv_mem_core_builtin.o` 占据 40,960B ZI — LVGL 的静态内存池。 - -| 参数 | 值 | 来源 | -|------|----|------| -| LV_MEM_SIZE | 40,960 (40KB) | `Lib/lv_conf.h` | -| 分配器 | TLSF (Two-Level Segregated Fit) | LVGL v9 builtin | -| LV_COLOR_DEPTH | 1 (1bpp) | 同上 | -| LV_USE_OS | 0 (无 OS 抽象) | 同上 | - -**池内内容**:UI widget 对象树、style 数据、字符串资源、动画对象等,均为运行时动态分配。LVGL v9 的 TLSF 分配器有约 10–15% 的内部元数据开销(free block headers),实际可用净空间约 34–36KB。 - -**为何无法静态估算**:LVGL 池的运行时使用取决于当前 UI 页面、控件数量、样式复杂度。可通过 `LV_USE_MEM_MONITOR`(`lv_mem_monitor_t`)在运行时获取已用/碎片化数据。 - ---- - -## 6. C 库堆与栈 - -构建使用 **microlib**(ARM C library minimal),非标准 newlib。 - -| 组件 | 大小 (B) | 说明 | -|------|----------|------| -| 启动栈 (`.ARM.heap` + `.ARM.stack`) | 1,024 | `startup_stm32f407xx.o`,仅 HAL_Init 期间使用 | -| C 库代码 (Flash) | 4,230 | `mc_w.l` (3,162) + `mf_w.l` (1,066),含 printf、浮点、字符串函数 | -| C 库 ZI (RW_IRAM1) | ~8 | `stdout.o` (4B) + 一些静态标志 | - -> microlib 不提供独立的 `malloc()` 堆。所有动态分配通过 FreeRTOS 的 `pvPortMalloc` 或 LVGL 池完成。C 库的栈仅用于启动期和 printf 格式化。 - ---- - -## 7. Flash 1MB 分布 - -![Flash 布局图](../images/内存布局_FLASH.svg) - -### 7.1 总体使用 - -| 段 | 大小 (B) | KB | 占比 | -|----|----------|----|------| -| Code (含 inc. data) | 219,976 | 214.8 | 21.0% | -| RO Data | 11,496 | 11.2 | 1.1% | -| RW Init Data | 528 | 0.5 | <0.1% | -| **已用合计** | **231,472** | **226.0** | **22.1%** | -| **空闲** | **817,104** | **798.0** | **77.9%** | - -### 7.2 代码段按模块分解 - -| 模块 | Code (B) | RO Data (B) | 合计 KB | 说明 | -|------|----------|-------------|---------|------| -| LVGL 库 | ~69,364 | ~4,200 | ~71.8 | lv_draw / lv_obj / lv_indev / lv_label 等 | -| 应用代码 + UI | ~42,540 | ~5,800 | ~47.2 | ui_menu / ui_net_sink / app_main / net_socket 等 | -| FatFS + dhara | ~8,500 | ~3,000 | ~11.2 | ff.o + dhara FTL journal | -| CH395F 网络 | ~8,800 | ~1,000 | ~9.6 | ch395f SPI 驱动 + lftpd | -| HAL 驱动 | ~9,400 | ~200 | ~9.4 | HAL_SPI / HAL_UART / HAL_DMA / HAL_RCC 等 | -| C 库 (microlib) | ~4,230 | ~200 | ~4.3 | printf / memcpy / strlen / 浮点 | -| FreeRTOS | ~4,240 | ~500 | ~4.6 | tasks / queues / event_groups / timers / cmsis_os2 | -| 其他 | ~73,902 | ~596 | ~72.8 | STM32 HAL 基础 / system / gpio / dma / i2c 等 | - ---- - -## 8. 任务栈明细与预算 - -### 8.1 栈分配汇总 - -| 任务 | 分配栈 (B) | 优先级 | 主要消耗者 | -|------|-----------|--------|------------| -| uiTask | **16,384** | BelowNormal | LVGL draw/indev (16KB!) | -| netTask | 4,096 | Normal | CH395F SPI + socket 操作 + 消息处理 | -| ftpTask | 4,096 | Normal | lftpd 命令解析 + `buffer[1024]` 栈局部 + FatFS 调用 | -| testTask | 4,096 | BelowNormal | 测试套件(产品模式下 idle loop) | -| defaultTask | 2,048 | Normal | FatFS 挂载 + LED 闪烁 | -| timeSyncTask | 2,048 | Normal | TCP socket + 时间解析 | - -### 8.2 堆 vs 任务栈的关系 - -``` -FreeRTOS 堆 48KB 内存池 -├── 任务栈 32KB (6 app tasks) ← 63% of heap -├── 内核栈 1.5KB (idle + timer) ← 3% -├── TCBs ~640B (8 × ~80B) ← 1.3% -├── netMsgQueue ~448B ← 0.9% -├── eventGroup ~24B ← 0.05% -├── 稳态合计 ~35.4KB ← 72.1% of heap -└── 空闲 ~13.4KB ← 27.9% of heap -``` - -> **关键观察**:uiTask 占 16KB(堆的 33%),是最大的单一消费者。其栈需求主要来自 LVGL v9 的深层 widget 树遍历和 draw 回调链。该分配是否过大需要运行时 `uxTaskGetStackHighWaterMark()` 验证。 - ---- - -## 9. 关键发现与风险 - -### 9.1 SRAM 几乎耗尽 (95.4%) - -| 风险 | 等级 | 说明 | -|------|------|------| -| 新任务/缓冲无法容纳 | **高** | 空闲 5.8KB 不足以创建新任务(最小 2KB 栈 + 80B TCB) | -| 堆碎片化 | **中** | heap_4 不支持合并空闲块,长期运行后碎片可能阻断分配 | -| 栈溢出检测缺失 | **中** | 未启用 `configCHECK_FOR_STACK_OVERFLOW`;溢出将静默破坏相邻内存 | - -### 9.2 两个大池(FreeRTOS 48KB + LVGL 40KB)占 SRAM 68% - -这两个池合计 88KB(占 128KB 的 69%),是 SRAM 紧张的根本原因: - -- **FreeRTOS 48KB**:由 6 个任务栈 + 内核对象消耗。uiTask 16KB 是最大的单项。 -- **LVGL 40KB**:UI 对象/样式的动态池。实际使用量取决于 UI 复杂度(运行时未知)。 - -### 9.3 可用但未映射的 SRAM - -| 区域 | 大小 | 现状 | 迁移可行性 | -|------|------|------|-----------| -| SRAM3 | 64KB | scatter 未映射 | 可映射;适合非 DMA 的大数据缓冲(LVGL 池等) | -| CCM | 64KB | scatter 未映射 | 仅 CPU 可访问;适合栈或静态缓冲(非 DMA、非外设访问) | - -> 将 LVGL 池(40KB)迁移至 SRAM3 可立即释放 40KB,将 SRAM 利用率从 95% 降至 64%。 - -### 9.4 任务栈可能过大 - -根据 `待处理内容.md`:"所有任务的内存都是往大的给的,导致内存不够用,要仔细排查内存的预算情况"。当前栈分配基于最坏估计,尚未通过运行时高水位标记验证实际使用量。 - -**建议的高水位检查**(在各任务循环中周期调用): - -```c -UBaseType_t watermark = uxTaskGetStackHighWaterMark(NULL); -// watermark × 4 = 剩余栈字节数 -``` - ---- - -## 10. 优化方向(指向既有方案) - -详细的优化方案见 [`RAM使用分析与优化方案.md`](./RAM使用分析与优化方案.md)。本节仅列出与内存使用直接相关的核心方向: - -| 方向 | 预期收益 | 复杂度 | -|------|----------|--------| -| 运行时高水位标记审计 | 量化各任务栈实际使用 | 低 | -| LVGL 池迁移至 SRAM3 | 释放 40KB 主 SRAM | 中(需改分散加载+初始化) | -| uiTask 栈裁减(需高水位数据) | 可能节省 4–8KB | 低 | -| 测试任务栈裁减(产品模式 idle) | 可能节省 2–3KB | 低 | -| 启用 `configCHECK_FOR_STACK_OVERFLOW` | 防止静默溢出 | 低 | -| 使能 LVGL `LV_USE_MEM_MONITOR` | 运行时监控池使用率 | 低 | -| 裁减未使用 LVGL 组件(draw blend_to_rgb565 等) | 节省 Flash + 可能减少池压力 | 中 | - ---- - -## 11. 附录 - -### A. 数据来源 - -| 数据 | 来源 | 路径 | -|------|------|------| -| Grand Totals / RW_IRAM1 明细 | .map 文件 | `MDK-ARM/STM32F407-Demo/STM32F407-Demo.map` | -| 逐模块 RW+ZI + Flash Top-30 | 自动生成台账 | `docs/内存管理/data/ram_report_20260911.md` | -| 解析脚本 | `scripts/ram_report.py` | 可复现重生成台账 | -| FreeRTOSConfig 参数 | `Inc/FreeRTOSConfig.h` | heap=48KB, 通知/互斥/跟踪等开关 | -| LVGL 配置 | `Lib/lv_conf.h` | LV_MEM_SIZE=40KB, COLOR_DEPTH=1 | -| 任务创建 | `Src/freertos.c:142–179` | 6 个应用任务的栈大小和属性 | -| TCB 结构体 | `tasks.c:252–329` | sizeof(TCB_t) ≈ 76B (本配置) | - -### B. 自动生成台账 - -运行以下命令重新生成台账(构建后): - -```powershell -& '.venv\Scripts\python.exe' scripts/ram_report.py -o "docs\内存管理\data\ram_report_20260911.md" -``` - -### C. SVG 图表重新生成 - -```powershell -& '.venv\Scripts\python.exe' tools/arch_diagram/gen_memory_svg.py -``` - -生成文件: -- `docs/images/内存布局_RAM.svg` -- `docs/images/内存布局_FLASH.svg` -- `docs/images/任务栈预算.svg` - -### D. 自洽校验结果 - -| 层 | 指标 | Per-file sum | Grand Totals | 差值 | 判定 | -|----|------|-------------|-------------|------|------| -| Flash | Code | 219,976 | 219,976 | 0 | PASS | -| Flash | RO | 11,496 | 11,496 | 0 | PASS | -| SRAM | RW | 528 | 528 | 0 | PASS | -| SRAM | ZI | 124,576 | 124,576 | 0 | PASS | - -> Per-file sum 与 Grand Totals 的差异来自 linker "(incl. Padding)" 行(约 302B code + 64B RO + 8B RW + 2B ZI),已在台账解析器中正确处理。 diff --git a/docs/内存管理/data/ram_report_20260921.md b/docs/内存管理/data/ram_report_20260921.md new file mode 100644 index 00000000..f00166b4 --- /dev/null +++ b/docs/内存管理/data/ram_report_20260921.md @@ -0,0 +1,177 @@ +# STM32F4-Base RAM/Flash Usage Report + +> Generated: 2026-09-21 04:33 Map: `STM32F407-Demo.map` + +## 1. Grand Totals (全局总量) + +| Metric | Value | +|---|---| +| Code (inc. data) | 234,976 B (229.5 KB) | +| RO Data | 13,824 B (13.5 KB) | +| RW Data (initialized) | 632 B (0.62 KB) | +| ZI Data (zero-init) | 130,600 B (127.5 KB) | +| **Total RO (Code + RO)** | **248,800 B (243.0 KB)** | +| **Total RW (RW + ZI) = SRAM used** | **131,232 B (128.16 KB)** | +| **Total ROM (Code + RO + RW init)** | **249,432 B (243.6 KB)** | + +## 2. 执行区水位 (Execution Regions) + +| Region | Base | Size | Max | Util% | +|---|---|---|---|---| +| ER_IROM1 | 0x08000000 | 248,800 B (242.97 KB) | 1,048,576 B (1024 KB) | 23.7% | +| RW_CCM | 0x10000000 | 49,184 B (48.03 KB) | 65,536 B (64 KB) | 75.0% | +| RW_IRAM1 | 0x20000000 | 82,048 B (80.12 KB) | 131,072 B (128 KB) | 62.6% | + +## 3. RW_IRAM1 逐符号明细(ZI + RW,按大小降序) + +| # | Address | Size (B) | Size (KB) | Type | Section | Object | +|---|---|---|---|---|---|---| +| 1 | 0x20006824 | 40,960 | 40.00 | Zero | .bss | lv_mem_core_builtin.o | +| 2 | 0x20012226 | 6,746 | 6.59 | Zero | .bss | ui_net_sink.o | +| 3 | 0x20010824 | 6,408 | 6.26 | Zero | .bss | ui_port.o | +| 4 | 0x20002CF8 | 6,200 | 6.05 | Zero | .bss | nand_ftl.o | +| 5 | 0x20004530 | 4,616 | 4.51 | Zero | .bss | lftpd.o | +| 6 | 0x20001094 | 4,104 | 4.01 | Zero | .bss | ch395f.o | +| 7 | 0x2000209C | 2,576 | 2.52 | Zero | .bss | gd5f2gq5ue.o | +| 8 | 0x20005F7C | 1,720 | 1.68 | Zero | .bss | cmsis_os2.o | +| 9 | 0x20000258 | 1,608 | 1.57 | Zero | .bss | freertos.o | +| 10 | 0x200059A0 | 1,220 | 1.19 | Zero | .bss | tasks.o | +| 11 | 0x20013C80 | 1,024 | 1.00 | Zero | | startup_stm32f407xx.o | +| 12 | 0x20000D94 | 704 | 0.69 | Zero | .bss | cfg_file.o | +| 13 | 0x200008F4 | 560 | 0.55 | Zero | .bss | spi.o | +| 14 | 0x20000B24 | 552 | 0.54 | Zero | .bss | usart.o | +| 15 | 0x20005738 | 552 | 0.54 | Zero | .bss | lftpd_io.o | +| 16 | 0x20006634 | 496 | 0.48 | Zero | .bss | lv_init.o | +| 17 | 0x20002BD8 | 288 | 0.28 | Zero | .bss | net_socket.o | +| 18 | 0x20005E64 | 280 | 0.27 | Zero | .bss | timers.o | +| 19 | 0x20002ACC | 268 | 0.26 | Zero | .bss | rs485.o | +| 20 | 0x200000BC | 152 | 0.15 | Data | .data | lftpd.o | +| 21 | 0x2001212C | 152 | 0.15 | Zero | .bss | ui_menu_view.o | +| 22 | 0x200008A0 | 84 | 0.08 | Zero | .bss | i2c.o | +| 23 | 0x20000D4C | 72 | 0.07 | Zero | .bss | stm32f4xx_hal_timebase_tim.o | +| 24 | 0x20001054 | 64 | 0.06 | Zero | .bss | sys_log_mcu.o | +| 25 | 0x20005960 | 64 | 0.06 | Zero | .bss | queue.o | +| 26 | 0x20000158 | 60 | 0.06 | Data | .data | tasks.o | +| 27 | 0x200121C4 | 49 | 0.05 | Zero | .bss | ui_page_runlog.o | +| 28 | 0x200121F5 | 49 | 0.05 | Zero | .bss | ui_page_selftest.o | +| 29 | 0x20000000 | 40 | 0.04 | Data | .data | freertos.o | +| 30 | 0x20000088 | 36 | 0.04 | Data | .data | net_cfg.o | +| 31 | 0x20000220 | 32 | 0.03 | Data | .data | ui_standby.o | +| 32 | 0x20002AAC | 32 | 0.03 | Zero | .bss | tpafe5160.o | +| 33 | 0x20000028 | 20 | 0.02 | Data | .data | cfg_file.o | +| 34 | 0x20000060 | 20 | 0.02 | Data | .data | gd5f2gq5ue.o | +| 35 | 0x20000194 | 20 | 0.02 | Data | .data | timers.o | +| 36 | 0x200001D8 | 18 | 0.02 | Data | .data | ui_menu.o | +| 37 | 0x20000078 | 16 | 0.02 | Data | .data | net_socket.o | +| 38 | 0x200000AC | 16 | 0.02 | Data | .data | nand_ftl.o | +| 39 | 0x20000240 | 16 | 0.02 | Data | .data | ui_net_sink.o | +| 40 | 0x2000004C | 12 | 0.01 | Data | .data | stm32f4xx_hal.o | +| 41 | 0x200001AC | 12 | 0.01 | Data | .data | port.o | +| 42 | 0x200001CC | 12 | 0.01 | Data | .data | ui_port.o | +| 43 | 0x200001F4 | 12 | 0.01 | Data | .data | ui_content_page.o | +| 44 | 0x20000200 | 12 | 0.01 | Data | .data | ui_page_password.o | +| 45 | 0x2000003C | 8 | 0.01 | Data | .data | sys_log_mcu.o | +| 46 | 0x20000044 | 8 | 0.01 | Data | .data | sys_clock.o | +| 47 | 0x200001B8 | 8 | 0.01 | Data | .data | ff.o | +| 48 | 0x200001C0 | 8 | 0.01 | Data | .data | ffsystem.o | +| 49 | 0x200001EC | 8 | 0.01 | Data | .data | ui_menu_view.o | +| 50 | 0x20000210 | 8 | 0.01 | Data | .data | ui_page_runlog.o | +| 51 | 0x20000218 | 8 | 0.01 | Data | .data | ui_page_selftest.o | +| 52 | 0x20000058 | 4 | 0.00 | Data | .data | system_stm32f4xx.o | +| 53 | 0x20000154 | 4 | 0.00 | Data | .data | lftpd_io.o | +| 54 | 0x200001A8 | 4 | 0.00 | Data | .data | cmsis_os2.o | +| 55 | 0x200001C8 | 4 | 0.00 | Data | .data | lv_textarea.o | +| 56 | 0x20000250 | 4 | 0.00 | Data | .data | mc_w.l(stdout.o) | +| 57 | 0x20000254 | 4 | 0.00 | Data | .data | mc_w.l(errno.o) | +| 58 | 0x20000074 | 2 | 0.00 | Data | .data | tpafe5160.o | +| 59 | 0x2000020C | 2 | 0.00 | Data | .data | ui_page_net_cfg.o | +| 60 | 0x2000005C | 1 | 0.00 | Data | .data | ch395f.o | +| | | **82,039** | **80.12** | | | **Total** | + +## 4. Image Component Sizes(RW + ZI > 0,按大小降序) + +| # | Object | Code | RO | RW | ZI | RW+ZI | +|---|---|---|---|---|---|---| +| 1 | heap_4.o | 508 | 0 | 32 | 49,152 | 49,184 | +| 2 | lv_mem_core_builtin.o | 410 | 0 | 0 | 40,960 | 40,960 | +| 3 | ui_net_sink.o | 882 | 0 | 16 | 6,746 | 6,762 | +| 4 | ui_port.o | 266 | 0 | 12 | 6,408 | 6,420 | +| 5 | nand_ftl.o | 2,008 | 65 | 16 | 6,200 | 6,216 | +| 6 | lftpd.o | 4,896 | 503 | 152 | 4,616 | 4,768 | +| 7 | ch395f.o | 2,738 | 262 | 1 | 4,104 | 4,105 | +| 8 | gd5f2gq5ue.o | 2,974 | 270 | 20 | 2,576 | 2,596 | +| 9 | cmsis_os2.o | 1,278 | 0 | 4 | 1,720 | 1,724 | +| 10 | freertos.o | 1,624 | 570 | 40 | 1,608 | 1,648 | +| 11 | tasks.o | 3,040 | 0 | 60 | 1,220 | 1,280 | +| 12 | startup_stm32f407xx.o | 36 | 392 | 0 | 1,024 | 1,024 | +| 13 | cfg_file.o | 5,036 | 726 | 20 | 704 | 724 | +| 14 | spi.o | 584 | 0 | 0 | 560 | 560 | +| 15 | lftpd_io.o | 1,018 | 0 | 4 | 552 | 556 | +| 16 | usart.o | 908 | 0 | 0 | 552 | 552 | +| 17 | lv_init.o | 156 | 0 | 0 | 496 | 496 | +| 18 | net_socket.o | 4,262 | 139 | 16 | 288 | 304 | +| 19 | timers.o | 1,012 | 0 | 20 | 280 | 300 | +| 20 | rs485.o | 120 | 0 | 0 | 268 | 268 | +| 21 | ui_menu_view.o | 2,010 | 108 | 8 | 152 | 160 | +| 22 | i2c.o | 168 | 0 | 0 | 84 | 84 | +| 23 | stm32f4xx_hal_timebase_tim.o | 152 | 0 | 0 | 72 | 72 | +| 24 | sys_log_mcu.o | 1,574 | 83 | 8 | 64 | 72 | +| 25 | queue.o | 2,392 | 0 | 0 | 64 | 64 | +| 26 | ui_page_runlog.o | 168 | 93 | 8 | 49 | 57 | +| 27 | ui_page_selftest.o | 168 | 93 | 8 | 49 | 57 | +| 28 | net_cfg.o | 748 | 103 | 36 | 0 | 36 | +| 29 | tpafe5160.o | 440 | 0 | 2 | 32 | 34 | +| 30 | ui_standby.o | 1,148 | 159 | 32 | 0 | 32 | +| 31 | ui_menu.o | 744 | 137 | 18 | 0 | 18 | +| 32 | port.o | 786 | 0 | 12 | 0 | 12 | +| 33 | stm32f4xx_hal.o | 116 | 0 | 12 | 0 | 12 | +| 34 | ui_content_page.o | 1,652 | 199 | 12 | 0 | 12 | +| 35 | ui_page_password.o | 686 | 167 | 12 | 0 | 12 | +| 36 | (incl. Padding) | 326 | 74 | 9 | 0 | 9 | +| 37 | ff.o | 7,942 | 282 | 8 | 0 | 8 | +| 38 | ffsystem.o | 204 | 20 | 8 | 0 | 8 | +| 39 | sys_clock.o | 1,748 | 286 | 8 | 0 | 8 | +| 40 | lv_textarea.o | 3,632 | 48 | 4 | 0 | 4 | +| 41 | system_stm32f4xx.o | 16 | 24 | 4 | 0 | 4 | +| 42 | errno.o | 8 | 0 | 4 | 0 | 4 | +| 43 | stdout.o | 0 | 0 | 4 | 0 | 4 | +| 44 | ui_page_net_cfg.o | 1,004 | 93 | 2 | 0 | 2 | +| | **All .o files** | **234,976** | **13,824** | **632** | **130,600** | **131,232** | + +## 5. Flash (Code + RO Data) Top-30(按大小降序) + +| # | Object | Code | RO Data | Code+RO | +|---|---|---|---|---| +| 1 | ff.o | 7,942 | 282 | 8,224 | +| 2 | lv_draw_sw_blend_to_rgb565_swapped.o | 7,648 | 0 | 7,648 | +| 3 | lv_draw_sw_blend_to_rgb565.o | 7,020 | 0 | 7,020 | +| 4 | cfg_file.o | 5,036 | 726 | 5,762 | +| 5 | lv_draw_sw_blend_to_argb8888.o | 5,412 | 0 | 5,412 | +| 6 | lftpd.o | 4,896 | 503 | 5,399 | +| 7 | lv_draw_sw_blend_to_i1.o | 5,272 | 0 | 5,272 | +| 8 | lv_indev.o | 4,958 | 0 | 4,958 | +| 9 | ui_font_cn_12.o | 0 | 4,626 | 4,626 | +| 10 | lv_draw_sw_blend_to_rgb888.o | 4,560 | 0 | 4,560 | +| 11 | lv_obj_pos.o | 4,526 | 16 | 4,542 | +| 12 | lv_refr.o | 4,486 | 0 | 4,486 | +| 13 | net_socket.o | 4,262 | 139 | 4,401 | +| 14 | lv_draw_sw_transform.o | 4,340 | 0 | 4,340 | +| 15 | lv_obj_style.o | 4,138 | 0 | 4,138 | +| 16 | lv_obj_scroll.o | 4,126 | 0 | 4,126 | +| 17 | lv_label.o | 4,042 | 45 | 4,087 | +| 18 | lv_draw_sw_mask.o | 3,862 | 0 | 3,862 | +| 19 | lv_draw_sw_blend_to_l8.o | 3,778 | 0 | 3,778 | +| 20 | lv_buttonmatrix.o | 3,586 | 121 | 3,707 | +| 21 | lv_textarea.o | 3,632 | 48 | 3,680 | +| 22 | stm32f4xx_hal_spi.o | 3,586 | 0 | 3,586 | +| 23 | lv_draw_sw_blend_to_al88.o | 3,560 | 0 | 3,560 | +| 24 | lv_draw_sw_blend_to_argb8888_premultiplied.o | 3,332 | 0 | 3,332 | +| 25 | lv_draw_sw_box_shadow.o | 3,246 | 0 | 3,246 | +| 26 | gd5f2gq5ue.o | 2,974 | 270 | 3,244 | +| 27 | lv_indev_scroll.o | 3,068 | 0 | 3,068 | +| 28 | tasks.o | 3,040 | 0 | 3,040 | +| 29 | lv_obj.o | 2,982 | 43 | 3,025 | +| 30 | ch395f.o | 2,738 | 262 | 3,000 | +| | **All .o files** | **234,976** | **13,824** | **248,800** | + +> 自洽校验:per-file 234,976/13,824/632/130,600 vs Grand Totals 234,976/13,824/632/130,600(CodeΔ=+0 / ROΔ=+0 / RWΔ=+0 / ZIΔ=+0;精确一致(含 '('incl. Generated)'-'(incl. Padding)' 台账行))→ **PASS** diff --git a/docs/内存管理/data/stack_report_20260921.md b/docs/内存管理/data/stack_report_20260921.md index 5071b383..eead278d 100644 --- a/docs/内存管理/data/stack_report_20260921.md +++ b/docs/内存管理/data/stack_report_20260921.md @@ -1,7 +1,7 @@ # Stack budget report - Source: `MDK-ARM\STM32F407-Demo\STM32F407-Demo.htm` (Keil static call graph) -- Linker run: Mon Sep 21 04:08:58 2026 +- Linker run: Mon Sep 21 04:28:54 2026 - Global maximum stack usage: **2776 B** (any call tree) - Runtime watermark: 6 task(s) from `tools\serial_tool\state\serial.log` @@ -10,9 +10,9 @@ Rule: `margin = cfg - max(static_max_depth, runtime_peak)` must be >= max(1024 B | Task | Entry | Cfg (B) | Static max (B) | Runtime peak (B) | Worst (B) | Margin (B) | Margin % | Verdict | |---|---|---|---|---|---|---|---|---| | defaultTask | `StartDefaultTask` | 4096 | 2776 | 2136 | 2776 | 1320 | 32% | PASS | -| netTask | `StartNetTask` | 4096 | 440 | 600 | 600 | 3496 | 85% | PASS | -| ftpTask | `StartFtpTask` | 4096 | 2528 | 3480 | 3480 | 616 | 15% | FAIL | -| uiTask | `StartUiTask` | 16384 | 912 | 3804 | 3804 | 12580 | 76% | PASS | +| netTask | `StartNetTask` | 4096 | 440 | 588 | 588 | 3508 | 85% | PASS | +| ftpTask | `StartFtpTask` | 4096 | 472 | 1184 | 1184 | 2912 | 71% | PASS | +| uiTask | `StartUiTask` | 16384 | 912 | 3824 | 3824 | 12560 | 76% | PASS | | testTask | `start_test_task` | 8192 | 168 | 348 | 348 | 7844 | 95% | PASS | | timeSyncTask | `time_sync_task` | 2048 | 336 | 420 | 420 | 1628 | 79% | PASS | @@ -34,7 +34,7 @@ StartDefaultTask -> net_cfg_load -> net_cfg_legacy_migrate -> net_cfg_save -> cf StartNetTask -> net_task_func -> net_stack_bring_up -> net_init -> ch395f_reset -> sys_clock_get_str -> unix_to_sd2506 -> __aeabi_uldivmod ``` -### ftpTask: 2528 B (own frame 24 B) +### ftpTask: 472 B (own frame 24 B) ``` StartFtpTask -> lftpd_start -> handle_control_channel -> send_response -> lftpd_inet_write_string -> net_send -> osMessageQueuePut -> xQueueGenericSend -> xTaskResumeAll -> xTaskIncrementTick @@ -62,10 +62,6 @@ time_sync_task -> time_sync_handle_conn -> sys_clock_set_unix -> sys_clock_set - | Bytes | Function | |---|---| -| 1104 | `handle_control_channel` | -| 1088 | `receive_file` | -| 1088 | `send_file` | -| 800 | `send_response` | | 704 | `s_append` | | 624 | `lv_draw_sw_arc` | | 624 | `draw_indic` | @@ -76,4 +72,8 @@ time_sync_task -> time_sync_handle_conn -> sys_clock_set_unix -> sys_clock_set - | 560 | `draw_main` | | 400 | `draw_line_skew` | | 352 | `lv_draw_sw_triangle` | -| 336 | `lftpd_start` | +| 328 | `lv_obj_redraw` | +| 328 | `view_frame_draw_cb` | +| 312 | `nc_draw_cb` | +| 304 | `lv_draw_label_iterate_characters` | +| 304 | `lv_draw_sw_box_shadow` | diff --git a/docs/内存管理/内存使用分析规划.md b/docs/内存管理/内存使用分析规划.md deleted file mode 100644 index fd60b1d8..00000000 --- a/docs/内存管理/内存使用分析规划.md +++ /dev/null @@ -1,189 +0,0 @@ -# STM32F4-Base 内存使用分析 —— 执行规划 - -> 版本:v1.0 日期:2026-09-11 -> 目标平台:STM32F407ZGTx @168MHz(1MB Flash / 192KB SRAM + 64KB CCM) -> 软件:FreeRTOS V10.3.1(CMSIS-RTOS2)+ LVGL + FatFs/dhara FTL -> 构建口径:**产品默认构建**(`test/test_config.h` 全注释 = 无测试套件;`App/app_cfg.h` 中 `APP_NET_ENABLE`/`APP_ADC_ENABLE`/`APP_RS485_ENABLE` 全开) - ---- - -## 1. 任务目标与交付物 - -### 1.1 目标 - -对工程做一次**严谨、可复现、可追溯**的内存使用审计,回答三个问题: - -1. **主 SRAM 128KB 到底被谁、占用多少、还剩多少?**(静态区逐模块 + 逐大项) -2. **FreeRTOS 48KB 堆内部怎么分布?**(任务栈 / TCB / 队列 / 事件组 / 临时分配 / 碎片余量) -3. **LVGL 40KB 内存池、C 库堆栈、Flash 1MB 各自怎么用?** - -要求:**有总览、有细节**,每个数字都能追到 `map` 文件或源码行号,做到"每一部分分配了多少 RAM"一目了然。 - -### 1.2 交付物 - -| # | 交付物 | 落位 | -|---|---|---| -| D1 | 本规划文档(执行中逐项打勾) | `docs/内存管理/内存使用分析规划.md` | -| D2 | map 解析脚本(可复用、纯标准库) | `scripts/ram_report.py` | -| D3 | 数据台账(map 原始抽取结果) | `docs/内存管理/data/ram_report_YYYYMMDD.md`(脚本生成) | -| D4 | 配图(SVG,`draw-diagram` 规范) | `docs/images/内存布局_RAM.svg`、`内存布局_FLASH.svg`、`任务栈预算.svg` | -| D5 | SVG 生成器 | `tools/arch_diagram/gen_memory_svg.py` | -| D6 | **最终报告**(图文并茂) | `docs/内存管理/STM32F4-Base内存使用分析报告.md` | - -> 与既有 `docs/内存管理/RAM使用分析与优化方案.md`(2026-09-07,偏"优化方案/整改计划")互为补充:本报告聚焦**现状审计与分布**,结论指向该优化方案。报告内交叉引用,不重复展开整改动作。 - ---- - -## 2. 范围与口径(内存资源真相) - -### 2.1 芯片物理内存(数据源:RM0090 §3.1、`STM32F407-Demo.sct`) - -| 区域 | 起始地址 | 容量 | 本工程是否使用 | 依据 | -|---|---|---|---|---| -| Flash | `0x0800_0000` | 1024 KB | ✅ 执行区 `LR_IROM1/ER_IROM1` | `.sct:7-13` | -| SRAM1 | `0x2000_0000` | 112 KB | ✅ `RW_IRAM1` 的一部分 | `.sct:14-16` | -| SRAM2 | `0x2001_C000` | 16 KB | ✅ `RW_IRAM1` 的一部分 | 同上 | -| SRAM3 | `0x2002_0000` | 64 KB | ❌ **未映射**(注释:clone MCU 可能无此区) | `.sct:1-5` | -| CCM SRAM | `0x1000_0000` | 64 KB | ❌ **未使用**(仅 CPU 可达,DMA 不可达) | `.sct`(无 CCM 区) | - -**关键结论(本报告的分析边界)**:实际可分配的 SRAM = `RW_IRAM1` 的 **128KB**。SRAM3(64KB)与 CCM(64KB)是**潜在预算**,不计入当前占用,仅在结论中作为"可挖潜空间"提及。 - -### 2.2 口径定义 - -- **静态区**:`RW_IRAM1` 内由链接器直接分配的所有 `RW + ZI`(含 `ucHeap` 堆池本体、`ucMemPool` LVGL 池本体、各驱动缓冲、C 库 `HEAP`/`STACK` 节)。 -- **运行时动态区**:从 FreeRTOS 堆(`ucHeap`)内部再分配的部分 = 任务栈 + TCB + 队列/事件组 + 临时分配;从 LVGL 池(40KB)内部再分配的部分 = 对象/样式/字符串。**注意**:这两层"池"的本体在静态区已全额计入,其内部细分**不重复加总**,只做占比说明。 -- **Flash**:`RO + RW 初值`(`Total ROM Size`),按模块与功能区(向量表/代码/只读数据/中文字库)拆分。 - ---- - -## 3. 数据来源(一手材料清单) - -| 类别 | 文件 | 用途 | -|---|---|---| -| 链接配置 | `MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct` | 区域划分 | -| RTOS 配置 | `Inc/FreeRTOSConfig.h` | 堆大小/优先级/静态分配开关 | -| RTOS 封装语义 | `Middlewares/.../CMSIS_RTOS_V2/cmsis_os2.c:472-476` | **栈大小单位 = 字节**(`/sizeof(StackType_t)`) | -| 任务定义 | `Src/freertos.c:60-184` | 任务属性、队列、事件组 | -| LVGL 配置 | `Lib/lv_conf.h` | `LV_MEM_SIZE`、`LV_COLOR_DEPTH`、`LV_USE_OS` | -| FatFs 配置 | `Lib/FatFs/ffconf.h` | `FF_MAX_SS`/`FF_FS_TINY`/`FF_USE_LFN` | -| 模块开关 | `App/app_cfg.h`、`test/test_config.h` | 构建口径 | -| **权威数据** | `MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(`@build` 重建) | 总量 + 逐 .o + 逐符号 | -| 源码 | `Drivers/BSP/**`、`App/**` | 缓冲语义/用途/DMA 归属 | - -> **重建要求**:既有 map 为历史构建产物,分析前必须重新 `@build` 生成与当前源码一致的 map(S1)。 - ---- - -## 4. 分析方法论(四视角 + 交叉校验) - -``` - ┌── V1 链接视图(map)─────┐ - │ Grand Totals / Region │ 总量、水位 - │ Image component sizes │ 逐 .o 的 RO/RW/ZI - │ Image Symbol Table │ 逐符号地址+大小 - └────────────┬─────────────┘ - │ 交叉校验 - ┌─────────────────────────┼─────────────────────────┐ - ▼ ▼ ▼ - V2 运行时堆视图 V3 LVGL 池视图 V4 源码/配置视图 - (48KB ucHeap 内部) (40KB ucMemPool 内部) (编译期常量缓冲) - 任务栈+TCB+队列+临时 对象/样式/字符串 逐模块 static/global -``` - -- **V1 链接视图**:脚本解析 map,产出 `Region 水位` + `Image component sizes`(全部 .o 的 ZI/RW 排序)+ `Symbol Table`(大对象清单)。**这是所有静态数字的唯一权威来源。** -- **V2 运行时堆视图**:按 FreeRTOS `heap_4` 分配规则**手算** 48KB 池的占用(任务栈、TCB、队列、事件组、软定时器、idle、lftpd 临时分配)。分配点的存在性由源码遍历确认。 -- **V3 LVGL 池视图**:确认 `LV_MEM_SIZE` 本体在静态区,说明其内部用途;不重复加总。 -- **V4 源码/配置视图**:逐模块列出大缓冲的**用途、大小、是否 DMA 可达**,用于给 V1 的符号赋语义(map 只有符号名,没有"这是干什么的")。 -- **交叉校验**:V4 逐项相加应与 V1 的 `Image component sizes` 对每个 .o 吻合;不等则以 V1 为准并记录差异原因。 - ---- - -## 5. 执行步骤(S0–S9,含验收与状态) - -> 每步完成即勾选,并在第 9 节进度表登记。涉及代码/文件的改动遵守 AGENTS.md 编码铁律(只用 Edit;新文件 UTF-8 无 BOM)。 - -| 步骤 | 内容 | 验收标准 | 状态 | -|---|---|---|---| -| **S0** | 编写本规划文档 | 文档落位并被确认 | ✅ 完成 | -| **S1** | `@build` 重建;确认 `build_log` 0 错 0 警、map 时间戳更新 | map 存在且时间戳为本次 | ✅ 完成 | -| **S2** | 编写 `scripts/ram_report.py`:解析 map → 地表/区域水位/逐 .o/大符号;输出台账 `ram_report_YYYYMMDD.md` | 脚本可复跑;数字与 map 人工抽查一致 | ✅ 完成 | -| **S3** | 逐模块源码审计:枚举全部大缓冲(用途/大小/DMA 归属/文件行号) | 覆盖 Top-N 全部大项,无遗漏 | ✅ 完成 | -| **S4** | FreeRTOS 堆核算:任务栈/TCB/队列/事件组/定时器/idle + 临时分配上限 | 手算总和 ≤ 48KB 且给出余量 | ✅ 完成 | -| **S5** | LVGL 池核算 + C 库堆/栈 + Flash 构成核算 | 各池/各功能区数字闭合 | ✅ 完成 | -| **S6** | 汇总对账:三层总表(SRAM 静态 / 堆内部 / 池内部)+ Flash 总表 | 加总闭合;V1↔V4 差异已解释 | ✅ 完成 | -| **S7** | 生成 SVG 配图(RAM/FLASH/任务栈)并渲染目检 | 3 张 SVG 生成、像素渲染无溢出 | ✅ 完成 | -| **S8** | 撰写最终报告 `STM32F4-Base内存使用分析报告.md` | 图文并茂、结构清晰、数字可追溯 | ✅ 完成 | -| **S9** | 校验:数字自洽 / 图文一致 / U+FFFD / 引用路径 / pre-commit | 全部通过 | ✅ 完成 | - -### 各步骤细化 - -- **S2 解析器输出**: - 1. `Execution Region` 表(`RW_IRAM1` 的 `Base/Size/Max`、`ER_IROM1`) - 2. `Image component sizes` 全表 → 按 `ZI+RW` 降序 Top-N(RAM);按 `Code+RO` 降序 Top-N(Flash) - 3. `Image Symbol Table` 中 `ZI/RW` 大对象(≥128B)→ 符号名 + 地址 + 大小 - 4. 生成 Markdown 台账文件 -- **S3 审计清单**(预期含):`heap_4.o:ucHeap`、`lv_mem_core_builtin.o:ucMemPool`、`ui_port.o` 帧缓冲、`ui_net_sink*.o` 远程显示缓冲、`nand_ftl.o` 页缓冲、`ch395f.o` SPI DMA 缓冲、`gd5f2gq5ue.o` BBT、`lftpd.o`、`rs485.o`、`tpafe5160.o`、`usart.o`、`sd2506.o`、`sys_clock.o`、HAL 句柄、FatFs 对象(`default_fatfs`、`fs_work`)、CMSIS/内核静态区。 -- **S4 手算规则**:TSK 栈字节 + `sizeof(TCB_t)` + 队列(`sizeof(Queue_t)+8×sizeof(net_msg_t)`)+ 事件组 + 定时器任务栈/TCB + idle 栈/TCB + lftpd/FatFS 峰值临时分配。 -- **S6 三张总表**:① SRAM 128KB 静态区(逐模块)② FreeRTOS 堆 48KB 内部 ③ LVGL 池 40KB 内部(说明性)+ Flash 表。 -- **S7 配图**: - - `内存布局_RAM.svg`:128KB 主 SRAM 横/竖条 → 静态区(按模块堆叠)+ 堆池(内部细分)+ 空闲 - - `内存布局_FLASH.svg`:1MB Flash → 向量/代码/只读数据(含中文字库)/库/空闲 - - `任务栈预算.svg`:6 任务配置栈 vs 职责估算对比柱状 - ---- - -## 6. 最终报告结构(D6 目录) - -1. 摘要(一页看懂:总预算 / 已用 / 余量 / 三大占用来源) -2. 内存资源总览(芯片物理图 + 本工程映射图) -3. 主 SRAM 静态分布(总表 + 逐模块表 + Top-N 大项) -4. FreeRTOS 堆 48KB 内部拆解(任务栈表 + TCB/队列表 + 余量) -5. LVGL 内存池 40KB 说明 -6. C 库堆/栈 -7. Flash 1MB 分布(代码/只读数据/字体) -8. 任务栈明细(配置 vs 职责,含 `freertos.c` 行号) -9. 关键发现与风险(如:静态区水位、堆余量、未用 CCM/SRAM3) -10. 优化方向(指向既有《RAM 使用分析与优化方案》,不展开) -11. 附录:数据来源、map 取数方法、脚本用法、术语与地址表 - ---- - -## 7. 产出物与落位汇总 - -| 类型 | 路径 | 脚本生成? | -|---|---|---| -| 规划文档 | `docs/内存管理/内存使用分析规划.md` | 手写 | -| 报告 | `docs/内存管理/STM32F4-Base内存使用分析报告.md` | 手写 | -| 台账 | `docs/内存管理/data/ram_report_YYYYMMDD.md` | `scripts/ram_report.py` | -| 配图 | `docs/images/{内存布局_RAM,内存布局_FLASH,任务栈预算}.svg` | `tools/arch_diagram/gen_memory_svg.py` | -| 脚本 | `scripts/ram_report.py`、`tools/arch_diagram/gen_memory_svg.py` | — | - ---- - -## 8. 风险、边界与假设 - -| 项 | 说明 | 处置 | -|---|---|---| -| map 与源码不同步 | 历史 map 已过期 | S1 强制重建 | -| 动态分配不可静态枚举 | 任务栈实际峰值需运行时高水位 | 本报告给"配置值 + 职责估算";实测高水位列为后续(指向优化方案阶段 0) | -| 池内部占用为运行时行为 | `ucHeap`/`ucMemPool` 内部随负载变化 | 以"分配点存在性 + 理论上限"核算,不声称实测 | -| 口径 | 只算 `RW_IRAM1`(128KB) | SRAM3/CCM 单列为潜力,不计入占用 | -| 编码 | 报告含中文 | `.md` 为 UTF-8 无 BOM;只用 Write/Edit,避免整文件重写 | -| 构建一致性 | app_cfg/test_config 若变更,数字随之变 | 报告标注构建口径与 map 时间戳 | - ---- - -## 9. 进度追踪 - -| 步骤 | 状态 | 备注 | - -| S0 规划 | ✅ | 本文件 | -| S1 重建 | ✅ | `@build` 0 错 0 警;map 2026-09-11 23:12:55 | -| S2 解析脚本 | ✅ | `scripts/ram_report.py`;台账含自洽校验行(PASS) | -| S3 源码审计 | ✅ | 关键缓冲语义已核对(UI/FTL/BBT/CH395F/lftpd/FatFs) | -| S4 堆核算 | ✅ | 栈 34,304 + TCB 640 + 队列 448 + 事件组 24 ≈ 35,416B,余 13,736B | -| S5 池/Flash 核算 | ✅ | LVGL 40KB 静态占用;Flash 22.1% | -| S6 汇总对账 | ✅ | 台账解析器数字与 Grand Totals 完全一致(差值=incl.padding) | -| S7 配图 | ✅ | 3 张 SVG 生成 + Chrome 渲染像素验证 | -| S8 报告 | ✅ | `STM32F4-Base内存使用分析报告.md` 已落位 | -| S9 校验 | ✅ | U+FFFD 0 / 引用路径有效 / pre-commit 全过 | diff --git a/docs/内存管理/内存内容分配策略.md b/docs/内存管理/内存内容分配策略.md index 4c09e9f8..5f1504a3 100644 --- a/docs/内存管理/内存内容分配策略.md +++ b/docs/内存管理/内存内容分配策略.md @@ -3,7 +3,7 @@ > 版本:v1.0 日期:2026-09-21 > 适用工程:STM32F4-Base(STM32F407ZGTx @168MHz,FreeRTOS V10.3.1 + CMSIS-RTOS2,Keil MDK v5 / ARMCC) > 状态:**当前有效策略**(台架验证:产品构建 gen68 干净启动 61 行;存储套件 20/20 PASSED;恢复跑 3/3 PASSED) -> 关联文档:`STM32F4-Base内存使用分析报告.md`(容量审计)/ `RAM使用分析与优化方案.md`(优化路线) +> 关联文档:`RAM使用分析与优化方案.md`(**内存管理主文档**:容量审计 + 优化方案 + 观测工具 + 基线数据) > 布局实现:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct` ## 1. 本文档目的 @@ -24,7 +24,7 @@ 3. CCM 存在性已用 `ST-LINK_CLI -r32 0x10000000` 本机实测可读写;**克隆片可能缺 SRAM3,换料必须重测并回退布局**。 4. 工程不进入 Stop/Standby 低功耗模式,两区域掉电保留行为无差异,不构成分配判据。 -> 勘误:`STM32F4-Base内存使用分析报告.md`(2026-09-11)内存地图中 "SRAM3 64KB (0x2001_C000)" 为笔误——F407 的 SRAM3 即 CCM,位于 0x10000000(AXI);AHB2 侧只有 SRAM1(112KB)+SRAM2(16KB)。以本文为准。 +> 勘误(原《STM32F4-Base 内存使用分析报告》2026-09-11 快照,随主文档合并保留于此):该报告内存地图中 "SRAM3 64KB (0x2001_C000)" 为笔误——F407 的 SRAM3 即 CCM,位于 `0x10000000`(AXI);AHB2 侧只有 SRAM1(112KB)+SRAM2(16KB)。**以本文为准**(主文档 §8.4 与 `内存布局_RAM.svg` 均按此口径)。 ## 3. 全板 DMA 使用方清点(DMA 缓冲必须为 SRAM 静态) diff --git a/docs/测试说明/FTP_Test_Guide.md b/docs/测试说明/FTP_Test_Guide.md index 73ebaada..1f7618a7 100644 --- a/docs/测试说明/FTP_Test_Guide.md +++ b/docs/测试说明/FTP_Test_Guide.md @@ -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/time_sync.c`、`App/task/net_task.c`、`test/ch395f_test_task.c`);命令位(`SYS_EVT_CFG_SAVE` / `SYS_EVT_NET_RECONFIG`)保持消费型等待不变。 -- 验证:修后串口出现 `[FTP] waiting for connection...`、21 端口可连;ftpTask 的 `[STACK]` 峰值握手后升到 ~2.5KB、128KB 传输后升到 ~3.4KB(见内存方案 §0.6,余量告警)。 +- 验证:修后串口出现 `[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)。 ### 8.7 单客户端限制:端口"通"但随后连接被拒(旧会话未释放) diff --git a/docs/项目审查与优化建议报告.md b/docs/项目审查与优化建议报告.md index bd635358..2c6794f1 100644 --- a/docs/项目审查与优化建议报告.md +++ b/docs/项目审查与优化建议报告.md @@ -243,6 +243,7 @@ graph TD #### D-4(P2)孤儿文档 `docs/内存管理/内存使用分析规划.md` 无任何入链(项目自有文档中唯一孤儿)。**建议**:并入 `RAM使用分析与优化方案.md` 的"规划"章节,或在 `docs/README.md` 索引中显式列出。 +> ✅ **2026-09-21 已处理**:该文档与《STM32F4-Base 内存使用分析报告》一并并入 `RAM使用分析与优化方案.md`(现为内存管理主文档),两个文件已删除;口径与方法论见其 §8.4,快照数据见 `docs/内存管理/data/`。 #### D-5(P2)缺一个文档入口/索引 diff --git a/docs/项目架构总览.md b/docs/项目架构总览.md index f7fbb598..db2c5b47 100644 --- a/docs/项目架构总览.md +++ b/docs/项目架构总览.md @@ -61,8 +61,8 @@ > `idle`(128 words)/ `timer`(256 words)由 FreeRTOS 内核创建。所有任务栈从 48KB heap_4 堆分配。 > **栈实测与预算**(2026-09-21,工具 `scripts/stack_report.py` = Keil 静态调用图 + 串口 `[STACK]` 水位;详见 `docs/内存管理/RAM使用分析与优化方案.md` §0.6): -> defaultTask 静态最坏 2776B/4096B(余量 32%)、**ftpTask 实测 3480B/4096B(余量仅 616B = 15%,低于 1024B 底线,待处置)**、uiTask 实测 3804B/16384B(缩栈空间大,内存方案 §1.3 目标 6KB)、netTask 600B、timeSyncTask 420B、testTask(产品构建)348B。 -> ⚠ 改栈或加大栈内局部变量前后**必须**重跑该脚本——静态分析低估含递归/间接调用的链(ftpTask 静态 2528B vs 实测 3480B)。 +> defaultTask 静态最坏 2776B/4096B(余量 32%)、ftpTask 实测 **1184B/4096B(余量 71%)**(lftpd 大局部缓冲静态化前为 3480B / 15%,见内存方案 §1.4)、uiTask 实测 3824B/16384B(缩栈空间大,内存方案 §1.3 目标 6KB)、netTask 588B、timeSyncTask 420B、testTask(产品构建)348B。 +> ⚠ 改栈或加大栈内局部变量前后**必须**重跑该脚本——静态分析低估含递归/间接调用的链(优化前 ftpTask:静态 2528B vs 实测 3480B)。 ### 2.2 就绪屏障(Event Flags,取代魔法延时) @@ -336,4 +336,5 @@ flowchart LR | 2026-09-13 | `sys_clock` 由 ③基础服务层改归 **④中间件层**(通用平台时间服务,日志/文件/对时共用;文件移入 `App/util/` 与 crc/ringbuf 同目录,include 路径不变):`FatFs` → `sys_clock` 成为**层内引用**,工程反向依赖消除;同日新增的 §1.2 "反向依赖例外" 条目撤销 | | 2026-09-14 | 通用配置文件(P3):网口配置存储由 `/cfg/netcfg.bin`(NCFG 二进制)迁移至 `/config.ini`(INI `[network] 段`,`App/cfg_file` 引擎 + `cfg_file_store` 双端后端);`net_cfg` 对外 API 不变;旧档首启自动迁移;`cfg_file` 列为 MCU/模拟器共享源码 | | 2026-09-14 | 运行/自检记录:新增 `App/log/sys_log`(定长 64B 文本记录引擎,MCU 后端 `/log/run.log`·`selftest.log`,defaultTask 唯一写者)+ UI 页面插件 `ui_page_runlog`/`ui_page_selftest`(动态列表)+ `ui_standby` 待机屏(空闲超时切入);密码页错误提示;字体 111→122 字(217 字形);详见 `docs/运行与自检记录设计说明.md` | -| 2026-09-21 | **§2.2 屏障语义修正 + FTP 永不监听缺陷修复**:`osEventFlagsWait` 默认消费所等位,就绪位(`NET_READY`/`STORAGE_READY`)被 netTask/uiTask/timeSync 先消费后,`ftpTask` 的 `WaitAll(两位)` 永不成立 → 任务永久阻塞、21 端口从未监听(静默);5 处就绪位等待统一加 `osFlagsNoClear`(命令位 `CFG_SAVE`/`NET_RECONFIG` 保持消费型)。同轮新增**栈可观测性**:`Src/freertos.c` 的 `stack_probe_poll`(串口 `[STACK]` 水位)+ `scripts/stack_report.py`(Keil 静态调用图 × 运行时水位,判定取两者更坏者);§2.1 任务表补齐实测栈数据(ftpTask 3480B/4096B 余量 15% 待处置)并修正过期的 defaultTask/testTask 栈配置,§3 时序图补 `NoClear` 与 FTP 启动日志,§5 补 FTP 启动门/单客户端实测注意 | +| 2026-09-21 | **§2.2 屏障语义修正 + FTP 永不监听缺陷修复**:`osEventFlagsWait` 默认消费所等位,就绪位(`NET_READY`/`STORAGE_READY`)被 netTask/uiTask/timeSync 先消费后,`ftpTask` 的 `WaitAll(两位)` 永不成立 → 任务永久阻塞、21 端口从未监听(静默);5 处就绪位等待统一加 `osFlagsNoClear`(命令位 `CFG_SAVE`/`NET_RECONFIG` 保持消费型)。同轮新增**栈可观测性**:`Src/freertos.c` 的 `stack_probe_poll`(串口 `[STACK]` 水位)+ `scripts/stack_report.py`(Keil 静态调用图 × 运行时水位,判定取两者更坏者);§2.1 任务表补齐实测栈数据并修正过期的 defaultTask/testTask 栈配置,§3 时序图补 `NoClear` 与 FTP 启动日志,§5 补 FTP 启动门/单客户端实测注意 | +| 2026-09-21 | **栈宽整改落地(内存方案 §1.4:lftpd 大局部缓冲静态化)**:`Drivers/BSP/NET/lftpd/lftpd.c` + `lftpd_log.c` 共 **9 处**大局部数组(`message[256]`/`response[512]`/`name[256]`×2/`buffer[1024]`×2/`read_buffer[512]`/`arg_buf[512]`/`client` 会话上下文/日志 `buffer[256]`)改为函数级 `static`(lftpd 单客户端、全部在 ftpTask 内串行);**ftpTask 实测峰值 3480 → 1184 B(余量 15% → 71%,判定 PASS)**,静态调用图 2528 → 472 B,代价静态 BSS +4,616 B(RW_IRAM1 82,048 / 131,072 B,余量仍 ~48 KB);§2.1 栈实测数据同步,原"涨栈到 1600 words"方案取消;FTP 功能回归 10/10 + 128KB 逐字节一致 | diff --git a/docs/项目部署环境要求.md b/docs/项目部署环境要求.md index 3797a1cf..54f49e2a 100644 --- a/docs/项目部署环境要求.md +++ b/docs/项目部署环境要求.md @@ -387,7 +387,7 @@ pre-commit run --all-files ### 3.3 资源预算(部署红线) -实测(2026-09-11,`docs/内存管理/STM32F4-Base内存使用分析报告.md`): +实测(2026-09-11 快照口径,现汇总于 `docs/内存管理/RAM使用分析与优化方案.md`): | 资源 | 已用 | 总量 | 利用率 | 余量 | 红线说明 | |---|---|---|---|---|---| @@ -517,7 +517,7 @@ pre-commit run --all-files |---|---| | 项目全景架构 | `docs/项目架构总览.md` | | 启动顺序 / 就绪屏障 | `docs/项目启动顺序管理.md` | -| 内存预算详析 | `docs/内存管理/STM32F4-Base内存使用分析报告.md` | +| 内存预算详析 / 优化方案 / 栈观测 | `docs/内存管理/RAM使用分析与优化方案.md`(主文档)· `docs/内存管理/内存内容分配策略.md`(区域与 DMA 规则) | | 网口配置 / 生效链路 | `docs/网络配置说明.md` | | 远程显示协议 | `docs/UI 设计/RemoDispBus协议与接收设计说明.md` | | LVGL 模拟器部署 | `docs/UI 设计/LVGL模拟器部署与使用说明.md`、`simulator/README.md` |