fc0d3e4b5c5346a7948b88e4ab368f65eb7d1197
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4a4ad6bc5f |
chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名, 不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体 run_boot_test.py 的真实调用。 - AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改 「编译(/keil-build)」「串口回归(/serial-boot-test)」 - keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree 方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围) - 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入 等 41 行历史证据保留旧名 - gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑 - 顺手补并行方案同行漏网计数 51→52 钩子 验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52 钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。 |
||
|
|
d9b887c0af |
feat(S-5 R4): 返回值不得裸丢弃成为机械检查,并修掉照出的第一处 ECC 静默降级
B 的第一步(加规则)与第二步(修最小最值的那处)。用户裁定"B 按你说的进行"。 **为什么只能项目侧自己做**(两条实测负结果,已写进 §2.27 与 bsp_err.h 边界纪律 4): · ARMCC v5 对 __attribute__((warn_unused_result)) 不生效(凌晨在 rs485_transmit 上实测:属性 在位、调用点裸调不接返回值,构建仍 0 错 0 警告); · cppcheck 也照不出来——按本仓自己的 --enable=warning,style,performance,portability 跑 gd5f2gq5ue.c,对 14 处丢弃 HAL_SPI_* 返回值、以及"未初始化局部量直接进 P_FAIL 判定" **一条不报**(它假定 gd5f_read_status(&status) 会写出参,而失败路径根本不写)。 结论:**丢弃底层传输的返回值,今天没有任何工具会响**。 **R4 上线即照出 37 条存量丢弃**:gd5f2gq5ue.c 29(14 处 HAL + 15 处自家原语)、net_socket.c 8、 ui_net_sink.c 4(net_close)、rs485.c 2、nand_ftl.c 1。按增量拦截老规矩入基线、只减不增—— 这是覆盖面扩大新暴露的历史债,不是新增违规;基线 19 → 1 → **38**。 **R4 自己被修三次(逐条记下,都是"规则比我想的更脆")**: ① 首版漏判"裸调用行尾带注释"这种写法(正则的 \s*$ 卡死),而那恰是最常见的"带说明的丢弃" → 补可选注释分支,并做 6 条正/反例自检; ② 把 net_set_errno 八处误判成丢弃(它是 setter,返回 int 是状态回声不是调用成败)→ **按真实 反例把规则收窄**(谓词 + setter 两类豁免),而不是塞进基线洗掉假阳性; ③ must-use 名单只从**头文件**取,gd5f_set_feature 这类 static 原语整个在视野外——而下面那处 真窟窿恰恰是 static、靠人工才发现 → 名单扩到 .c 内 static 定义(同一套启发式),补完立刻 又照出 gd5f_bbt_save#1:**运行时标坏后 BBT 落盘结果被丢弃 = 坏块记账可能没持久化**,已入 基线记为待还,本步不动行为。 另修一处自己的排版误判:R3 原本要求注释块出现字面"返回值",规范 V1.0 允许列对齐写法 「返 回 值:」→ 改为忽略空格匹配(门禁不该规定排版)。 **修掉的第一处真窟窿**:bbt_rebuild 的注释写着"无论扫描成败都须恢复 ECC,否则后续 I/O 在无 ECC 下运行",而它下面 gd5f_set_feature(0xB0,…) + gd5f_wait_busy(100) **返回值全丢**:SPI 一抖 ECC 就保持关闭,且 gd5f_check_ecc() 在 ECC 关闭时恒读"无 ECC 错"——整条链再没有任何信号。 补法复用 init 路径的 E-10(写完回读 B0h 验 ECC_EN 位真生效,"写了但没生效"与写失败同罪), 并把"关 ECC 失败"改成**立即中止**(此时 ECC 仍开着,是安全侧;带着 ECC 去扫 spare 反而会把 厂标好块误判成坏块,"重建 BBT"变成制造坏块)。恢复失败时按 (void)gd5f_bbt_load() 回读盘上 副本,避免 RAM 留着"比盘上更新"的表而下次上电又退回旧表。 **一条定位纠正(差点把修复记大)**:查链接映射发现 Removing gd5f2gq5ue.o (i.gd5f2gq5ue_bbt_rebuild), (604 bytes)——该函数在产品镜像里**根本没被链接**(产品构建无调用 者),只由 ENABLE_STORAGE_RECOVERY_TESTS 的恢复构建经 testTask 调用。所以本次修的是**恢复流程 自身的安全**(跑恢复时若 ECC 没恢复,设备会带着"已恢复"的结论在无保护状态下继续跑),不是 现场日常路径;相应地镜像 Code 的 +28 B 也不是它带来的(未逐字节追,线索是 gd5f_private_get_feature 由单调用点变多调用点、内联决策可能变化)。验证因此不能只跑产品构建: 临时打开 TEST_SUITE_STORAGE + ENABLE_STORAGE_RECOVERY_TESTS 编一次(0 错 0 警告且该函数不再 出现在 Removing 列表),随即还原两处开关并重编产品构建核对(test/board 无残留差异)。 **编号门禁把我自己的自造编号拦下了**:我起初用 B-1/B2/B3/B4 标记这批缺陷,id-check 报"未登记 前缀"13 处(且 B2/B3/B4 会与真实里程碑号撞号)→ 全部改写成平实语言。这是那条"注释与文档里 不许用没人检索得到的内部阶段编号"的规则第一次在提交前拦住我,规则是对的。 验证:@build 0 错 0 警告,Code 213,648 → **213,676**、RO 18,860、ER_IROM1 **232,536 B / 22.2%** (ROM 合计 233,724 B;RAM 逐字段不变);ram_report 重生成 + ram_baseline.py --update + 配图常量 同步(mem-numbers Passed);@test **PASS**(到 net init: OK 682 ms、[ERR] 0);R4 两处**探针 实证**(临时加一条裸调用 → 立刻成为新增并 RC=1;还原 → 38/38/0);--prune 点名本次还掉的 2 条 已删;pre-commit run 全绿 15 Passed;check_doc_links --all --sections-strict 147 篇 0 问题; id-check --strict 增量拦截通过。 文档:AGENTS.md 与 .pre-commit-config.yaml 的 bsp-err 口径(四条规则、基线 38); pre-commit使用说明.md §2.27(R4 全量口径 + 两条负结果 + 名单来源与豁免形状 + 单行限度); bsp_err.h 边界纪律 4;报告 §0.4 / §5.1 / §9 / 附录 A + 变更记录;存储维护与容量恢复.md 新增 §4.3(运维判据:出现 ECC restore FAILED / ECC not enabled 即本次恢复**不算完成**,须断电重启 重走 init,不许接着 nand_ftl_format() 当作已修好;并记明该函数产品镜像不链接)——顺带修该文 结构缺陷:原 ## 5. 变更记录 误插在 §4.1 与 §4.2 之间,会让 §4.2 变成 §5 的子节。 |
||
|
|
78248a1cdf |
feat(S-5): 收编 SD2506/TPAFE 并给 dhara 失败分类,门禁基线 19→17
前一笔立契约与门禁(`Drivers/BSP/bsp_err.h` + `bsp-err` 钩子 + RS485 试点);这一笔按用户
指定顺序 C3 → C1 → C2 把三个模块真正收编,并让存储失败第一次变得可读、可分类。
**C3 · SD2506 整模块(11 个公共 API + 6 个内部 helper)**:三枚私有宏 `SD2506_OK`/
`SD2506_ERROR`/`SD2506_I2C_ERROR` **删除而不是留别名**——留别名等于新代码还能用旧形状,
把迁移洗掉;编译器替我们找全所有调用点。新增 `sd2506_err_from_hal()`,**把 I2C 超时从
"和没人应答共用一个 −2"里拆出来归成 `BSP_ERR_TIMEOUT`(可重试)**——SD2506 是启动链第一站、
I2C1 上电时序敏感,这是本波唯一真正的语义改善,不是改名。`sd2506_init` 的 charge 寄存器
**回读校验不符**原返参数类错误,改归 `BSP_ERR_IO`(那不是调用方的错)。
**C1 · 同形两义**:`GD5F_BUSY_TIMEOUT` → `GD5F_WAIT_EXPIRED`(**值 −2 不变**,纯消"名字叫
TIMEOUT 其实是返回码")。**GD5F 私有码集整体收编刻意没做**:169 处引用里 48 处在板级套件、
还要动 Trap 记录用词,属另一波带真机+套件回归的工作,不夹带。TPAFE5160 收编 23 处:
`BUSY_TIMEOUT → BSP_ERR_TIMEOUT`、`ERROR → BSP_ERR_PARAM`(**同值 −1,行为零变化**)、
`FRSTDATA_ERR → BSP_ERR_NO_DEVICE`(E-44 的本义就是"芯片没驱动 DB 总线",归到器件无响应比
私有 −3 有用,且避开与 `BSP_ERR_TIMEOUT` 撞值)。实测 `tpafe5160.o`/`gd5f2gq5ue.o` 的 Code
增量为 **0**——改名不产生一条指令。
⚠ **`tpafe5160_snapshot()` 刻意不套成败码**:它是 1=有新帧 / 0=无新帧 / −1=参数错 的三态
**状态**返回,0 不是错误而是"这一拍没有新数据",强行收编会把慢消费者的正常路径变成失败。
顺带订正我上一版在 `bsp_err.h` 的断言——"本层只有 `net_*` 把数量与错误挤同一返回值"是错的,
`snapshot`/`adc_input_poll` 就是第二族;两族现都已写明并各注保留理由。
**C2 · FTL 坍缩点分类**:`ftl_flush_cache`/`ftl_read_page` 不再把 dhara 失败压成
`GD5F_ERROR`,
按**处置差异**分三类,并在最早同时知道 LPN 与原始码的位置就地打印:`ECC`/`TOO_BAD` →
`BSP_ERR_CRC`(页级,重试或换页,不代表卷坏);`JOURNAL_FULL`/`MAP_FULL` →
`BSP_ERR_NO_SPACE`(介质健康但块池/日志见底,此时重格式化只是把问题推回去);
`BAD_BLOCK`/`RECOVER`/`CORRUPT_MAP`/越界 → `BSP_ERR_IO`(完整性故障,保卷 + 上报,不许自愈
重格式化)。这与 **E-31** 是同一条纪律的两端。可读名用自建短表:**本仓 dhara 没有编
`error.c`,用 `dhara_strerror()` 会直接链接失败**。`disk_read`/`disk_write` 仍回
`RES_ERROR`
(FatFs 契约);要让 defaultTask 卷健康探针按类分支还差跨调用存活的快照 → C4 待还。
**两次靠核实拦住的自我错误**:① 我一度认定 `DBG_ERROR("... err=%d", err)` 是把结构体塞进
varargs 的格式 bug——核 `Lib/dhara/error.h` 后确认 `dhara_error_t` 是 `enum`(标量),那行日志
本来合法,改了反而是引入错误;② `bsp-err` 一次 `replace_all` 越界把 8 处 **GD5F 码空间**的
判据改成 `!= BSP_OK`(数值同为 0、无行为变化),但那正是"把两个码空间混起来",已逐一还原。
`cppcheck` 亦抓到 `cls` 声明作用域过宽,已收进失败分支。
**验证**:`@build` 0 错 0 警告(`Code` 213,648、RO 18,860、`ER_IROM1` 232,508 B /
22.2%、
`ROM 合计` 233,696;**RAM 三项零增量**);`@test` PASS(692 ms,烧入戳 `Oct 23 01:35:32`)
且逐条读到被改模块的锚点:`SD2506 init: OK (ret=0)` 与 ID/时间/温度/电池四条读回正常、
`ADC init: OK`、`NAND init: OK`、`Map resumed: 14094/97504`、`adc=` 帧计数持续递增(证明
snapshot 三态未被改坏)、`[ERR]`/Fault/FAIL 0 条。主机单测:系统时钟 100/100、dhara NAND
移植层 149/149(两者都因本次改动需补 `Drivers/BSP` include 路径,补后在钩子内实跑 Passed)。
`pre-commit run` 全量 RC=0(20 个实跑钩子 Passed);`bsp-err` 基线 **19 → 17**(还掉两个
`*_BUSY_TIMEOUT = -2`)、`--prune` 干净;`doc-links --all --sections-strict` 147 篇
0 问题;
`id-check --strict`、`layer-deps`、`mem-numbers`、`mem-budget`、`ui-golden` 全过;33
个文件
字节卫生 0 异常(无 CR / 无 U+FFFD / 无行尾空格 / 均有尾换行)。
**口径与数字联动**:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.4(三类码与处置
理由);`pre-commit使用说明.md` §2.27 与 `AGENTS.md` 基线计数 19 → 17;TPAFE README、
GD5F 测试指南与 Trap 记录均**保留旧名留痕**以便对旧日志;配图桶按 .map 对象级增量重算
(`dhara/FTL` 10,931 → 11,222,+291 全部落在 `nand_ftl.o`;应用代码/UI +36 落在
`app_main.o`;其余散量入"其他"7,557;桶和 = Code+RO 断言通过、文本溢出 0);
`ram_baseline --update/--write`、`ram_report_20261023.md` 快照、报告 §1.3/§5.1/§5.4
与
部署文档 §3.3 数字全部同步;报告 §0.4/§9 的 S-5 状态改为 🟡 主体已落地。
|
||
|
|
8b0ea17b71 |
fix(lftpd): LIST/NLST 满块续发——大目录不再被静默截断并回 550
现象:`ftp_test.py` 的 `LIST 150->226` 断言失败——1512 B 数据全收到了,完成回包却是 **550**。 **根因**(`send_list`/`send_nlst` 各一处):定案三把"整份目录一次性批量发送"落进 static 1536 B 缓冲时,顺手留下了"装满就 `break` 并回 −1",`cmd_list` 据此回 550。于是目录一旦超过一块,客户端 拿到的是**静默截断**的清单**外加一个错误码**——而清单看起来完全像"目录里就这些"。这比单纯报错 更危险:没有人会去数条目个数。 **台架实测(同一台设备、同一个根目录,改动前后各测一次)**: · 改前:1512 B / **24 条目** / 完成回包 **550**(停在 A0007.BIN,后面 6 个文件被吞) · 改后:1880 B / **30 条目** / 完成回包 **226** **修法:满块续发**,而不是把缓冲区加大。新增 `list_batch_flush()`——装满一块就整块发出、清空 继续装,收尾再发最后一块。为什么仍要"批量":定案三的本体是实测结论——同一连接上背靠背 ~60 B 小段写会毒化 CH395F 的 TX(芯片报 SEND_OK 但对端全丢,无 RST 无 DISCONNECT),而 RETR 的 1 KB 满块连发安全;所以这里只发大块,绝不退回"每行一发"。单行真装不下时**明确失败**而不是少发条目 (8.3 短名 + 定长格式远小于 1536,真发生说明格式被改坏)。两条出口的 `lftpd_io_closedir` 收敛到 循环后单点:卷锁持有至 closedir,任何提前 return 都会漏锁。 **防回退**(关键):修完之后,**目录清空的设备上这颗雷再也暴露不出来**,所以补了 `test/host/ftp_list_fill_test.py`——自造 26 个 8.3 短名文件把清单撑过一块,三条判据(STOR 全 226 /LIST 全文里每个名字都在/完成回包 226 而非 550,用 ftplib `retrlines` 让非 2xx 完成直接抛), 跑完逐个 DELE 还原。真机实测 `收到 56 行 / 3544 字节`(跨 3 块,满块续发被真正走到)→ 三条全 PASS。用法登记进 `docs/测试说明/FTP_Test_Guide.md` §4.5,机理与增补写进 `docs/问题记录/CH395F_Trap_Records.md` 定案三。 **顺带记录的已知偏差(未修)**:`cmd_list`/`cmd_nlst` 忽略命令参数、恒列会话 CWD (`client->directory`)。主流客户端都是 `CWD` + 裸 `LIST`,实际影响小,先记在案。 体量:`Code` 213,264 → **213,436 B(+172)**、RO 不变 → `ER_IROM1` **232,088 B(22.1%) **、 `ROM 合计` 233,276 B;**RAM 一字未动**(`RW_IRAM1` 75,264 B 57.4% / `RW_CCM` 57,376 B 87.9%)。 数字联动照做:`data/ram_report` 重跑、`ram_baseline --update/--write`、 `check_mem_numbers --write`、 FLASH 配图常量重算重生成(桶合计 = 232,088 与 `Code+RO` 相符)、报告 §1.3/§5.1/§5.4 与部署 §3.3。 门禁:`@build` 0 错 0 警告;`@test` PASS(692 ms);`ftp_test.py` → **TEST PASSED**(含此前失败 的 `LIST 150->226`);`ftp_up_dl_test.py` → TEST PASSED;`ftp_list_fill_test.py` → TEST PASSED; `doc-links --all` 146 文档 0 问题;`id-check --strict` 增量拦截通过(可判定 82.82%); `mem-numbers` / `mem-budget` 通过;改动文件字节卫生 BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格。未推送。 |
||
|
|
4c21cff753 |
fix(lftpd): 对端行为不再打 [ERR]——新增 lftpd_peer 级别并划清级别边界
现象:抓自检台账基线那次 FTP 会话一结束,设备就打 `[ERR] [FTP] error reading next command`, 本次 `@test` 直接 FAIL(Forbidden 含 `[ERR]`)。查下去发现 lftpd 把**全部**异常路径一律按 error 级打,其中九处其实由外部输入决定:客户端关窗口、连上就走、PASV 后不建数据连接、 运维改 IP 触发监听重建。两条代价:① 任何人"连 21 端口一下就断开"就能把一次启动测试打成 FAIL;② 同样的动作可以**无限制造固件级 ERROR 日志**——日志面与门禁面都可被外部驱动, 工业现场过不了审。 **做法**:`private/lftpd_log.h` 新增 `lftpd_log_peer()`(info 级,带 `[FTP]` 前缀),而不是 就地改成 `DBG_INFO`——让"谁才算真故障"在读代码时一目了然,也防止后来者顺手把对端行为升回 error。降级的九处:控制通道被对端关闭(原 `error reading next command`,改为 `control channel closed by peer (ret=%d)`,措辞原先把正常收尾写成故障、误导过排查方向)、 发送 220 前对端已走、`listener rebuilt`(网络重建,原因由 accept 层自己记)、四处 `data accept failed`(PASV 后没来数据连接)、`accept timeout`。 **刻意留在 error 的**:create/bind/listen/取端口失败、写通道失败、文件打开失败、 `DOWNLOAD/UPLOAD FAILED`(对端中途退出确实让一次传输没走完 = 数据完整性事件, `../问题记录/CH395F_Trap_Records.md` 的中止家族要靠这些锚点定位)、 `data listener disarmed`(控制连接还活着而我方数据监听已不在服役 = 本侧异常,就地注明)。 **实测划清的边界**:客户端**干净收尾**(发 `QUIT` 或 FIN 半关)现在整条链路没有任何 `[ERR]`; 但 **RST 硬拆**(`SO_LINGER` 清零 / 进程被杀 / 拔线)仍会留一条 `[ERR][NET] sockN TIMEOUT`—— 那是 net 层的套接字超时锚点,网络自愈架构判据要读它,不在本次降级范围。所以"采集窗口内别跑 `data listener disarmed`(控制连接还活着而我方数据监听已不在服役 = 本侧异常,就地注明)。 **真机验证**:`@build` 0 错 0 警告;`@test --require-selftest-pass --device-ip` → **PASS** (基线 29 条 → 本次无新增;`net init: OK` 726 ms);专门写了 RST 复现脚本(登录后 `SO_LINGER` 清零直接关)→ 串口得到 `[FTP] control channel closed by peer (ret=-1)`,**不再是 `[ERR]` 行**; 同一份日志里 QUIT 收尾的那次会话完全没有错误行。 体量:`Code` 213,236 → **213,264 B(+28)**、`RO` 18,580 → **18,652 B(+72)**(措辞变长), 同一份日志里 QUIT 收尾的那次会话完全没有错误行。 `ZI` 131,500、`RW_IRAM1` 75,264 B 57.4% / `RW_CCM` 57,376 B 87.9% 全不变)。数字联动照做: 两份 `data/` 快照、`ram_baseline --update/--write`、`check_mem_numbers --write`、FLASH 配图 常量重算重生成(桶合计 = 231,916 与 `Code+RO` 相符;越界复查 0)、报告 §1.3/§5.1/§5.4 与 部署文档 §3.3 同步。 口径落文:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.3「FTP 日志级别划分:对端行为 vs 固件故障」(含两类的判据表与边界),并更新 `docs/测试说明/自动化测试流程.md` 的采集窗口警示。 顺手把 `private/lftpd_log.h` 工作区的 CRLF 混用归一回 LF(仓库里 blob 本来就是 LF,diff 无影响)。 门禁:`doc-links --all --sections-strict` 146 文档 0 问题、`id-check --strict` 增量拦截通过 (可判定 82.82%)、`mem-numbers` / `mem-budget` / `layer-deps` 全绿;改动文件字节卫生 BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格。未推送。 |
||
|
|
e7f28502bc |
refactor(NET): netMsgQueueHandle 收进 net_socket 私有,改为创建后绑定
结构债 S-2 的最后一步——**该族的 24 处手写跨模块裸 extern 就此全部清零**。 队列对象仍由 CubeMX 在 `MX_FREERTOS_Init()` 创建(**创建点不可外移**: `osKernelInitialize()` 之前的任何创建类 API 会杀死启动,见 `docs/架构设计/项目启动顺序管理.md`);改为创建后立刻在 `Src/freertos.c` 的 `USER CODE BEGIN RTOS_QUEUES` 区调用 `net_msg_queue_adopt(netMsgQueueHandle)`, 把句柄交给唯一使用者。`net_socket.c` 内改用 `static osMessageQueueId_t s_net_msg_queue`(21 处引用改名),BSP 对该全局变量的裸 `extern` 消失——现在 `netMsgQueueHandle` 只出现在生成文件自己的两处(定义 + 创建)。 两个刻意的设计决定: ① **不新建第二个队列**。自建队列 + 删 `.ioc` 里的 netMsgQueue 也能收口,但要 CubeMX 重生成(Ask-first),过渡期还会同时存在两个队列,白占 CCM 堆里 8 × `net_msg_t` 的空间——CCM 正是当下 87.9% 的红线。故选"创建后绑定"。 ② `adopt()` **拒绝重复绑定、保留首次句柄**。真出现两处绑定时,静默换句柄会让 已入队的请求留在旧队列里永不处理;拒掉第二个反而更容易暴露。绑定前的判空 是真实防线:丢了那一行,后果是"所有 net_* 请求被丢弃、网络全线静默不通", 不是崩溃。 **防回退**:`check_cubemx_survival.py` 新增存活项 `net-msgq-adopt`(region `RTOS_QUEUES`、`must_contain=["net_msg_queue_adopt"]`),易失清单 29 → 30 项全部 到位;基线已重建(有意的 USER CODE 区改动,按脚本文档流程 `--write`)。 **体量**:`Code` 213,204 → **213,236 B(+32)**、`ER_IROM1` **231,816 B(22.1%)**; `RW +4 / ZI −4` → `RW_IRAM1` 恒 **75,264 B(57.4%)**、`RW_CCM` 恒 **57,376 B (87.9%)**、`RW+ZI` 总量 132,688 B 不变(没有多占一个字节 RAM)。 **验证**:`@build` 0 错 0 警告;`@test` PASS——624 ms 到 `net init: OK`、`i104: listening on 2404`、无 `[ERR]`/Fault、`heap free=15800 min_ever=8464` 逐项不变。 另在真机跑了一次 FTP 控制通道往返:220 → `USER admin` 331 → `PASS admin` 230 → `PWD` 257 → `TYPE I` 200 → `QUIT` 221。每条命令都是一次过队列的 net_recv/net_send,证明绑定路径真在跑;`.map` 里 `freertos.o(MX_FREERTOS_Init) refers to net_socket.o(i.net_msg_queue_adopt)` 证明镜像含该行。 ⚠ **顺带揪出一条测试工具漂移(本次未修,等定夺)**: `test/host/ftp_up_dl_test.py:123/126` 仍写死 `USER anonymous` / `PASS test@test.com`,而设备自 N-19(2026-10-11)起按 `/config.ini [ftp]` 校验 admin/admin(SHA-256 `pwd_hash`)→ 该脚本必然登录失败。它是手工工具、不在任何 pre-commit 钩子里,所以 11 天没人发现,而 `docs/测试说明/FTP_Test_Guide.md` 把它 列为"推荐入口"。建议加 `--user/--password`(默认 admin/admin),并连 `ftp_speed_test.py` 一起核。 数字联动:两份 `data/` 快照重跑、`ram_baseline --update/--write`、 `check_mem_numbers --write`、FLASH 与任务栈两张配图常量重算并重新生成(逐文本按 字号与锚点复查越界 = 0)、报告 §1.3/§5.1/§5.4 与部署文档 §3.3 同步。报告 §0.4 的 S-2 行按「完成即销项」改写为已闭环,§9 拆出独立 S-2 行标 ✅。 门禁:`@build` 0/0、`@test` PASS、`layer-deps` / `cubemx-survival` / `mem-numbers` (本次改到台账受管区,scan 已过)/ `mem-budget`(57.4% / 87.9%)/ `task-table` / `doc-links --all`(146 文档 0 问题)/ `id-check --strict`(增量拦截通过、可判定 82.84%、存量债 640)全绿;14 个文件字节卫生 BOM 无 / CR 0 / U+FFFD 0 / 无尾空格。 未推送。 |
||
|
|
7e77af08a0 |
fix(BSP): SPI1 完成标志收回 GD5F 私有,HAL 回调改契约转调
推荐清单第 4a 步(结构债 S-2 剩余面里唯一带"静默失效"属性的那条)。 **问题不是风格**:HAL 的 SPI 完成/错误回调是**全局唯一符号**,整个工程只能有一份 实现,当前由 CH395F 代管并按 `hspi->Instance` 分发——于是 GD5F 的完成标志 `g_spi1_dma_done` 被 CH395F 跨模块裸 `extern` 直接写,**GD5F 出错时的"释放等待者" 寄居在别人的 .c 里**。谁哪天做"CH395F 清理",把那两个 `else if (SPI1)` 分支当无关 代码删掉:编译零报错,GD5F 的 DMA 等待在出错时只能干等 `GD5F_SPI_TIMEOUT` 兜底。 **改法(时序一字不动,只换所有权)**: · 标志收为 GD5F 文件内 `static volatile uint8_t s_spi1_dma_done`(6 处引用同步改名) · GD5F 导出 `gd5f_spi1_notify_done()` / `gd5f_spi1_notify_error()`,头文件里写明 "SPI1 分支必须转调这两个函数,否则本模块的 DMA 等待在出错时只能靠超时兜底" · CH395F 删除裸 `extern`、include `gd5f2gq5ue.h`、两个 SPI1 分支改为转调并就地注明 "勿删:少了这行 GD5F 的 DMA 出错只能靠超时退出" `HAL_SPI_AbortCpltCallback`(`HAL_SPI_DMAStop()` 会触发)本次**刻意不做**——它会改 变中止语义、需要真机专项,当前超时兜底可用(已在报告 §0.4 记为观察项)。 **体量代价**:`ch395f.o` −8 B、`gd5f2gq5ue.o` +24 B → `Code` 213,188 → **213,204 B (+16 B)**,RO/RW/ZI 全不变,`ER_IROM1` 231,768 → **231,784 B(22.1%)**。16 B 换 掉一个静默失效类,划算。 **真机 `@test` PASS**:无 `[ERR]`/Fault,复位原因 software;`[STACK] heap free=15800 min_ever=8464` 与改前**逐项相同**;各任务实测峰值 default 2152 / net 1116 / ftp 764 / ui 4052 / timesync 616 / adc 952 / iec104 540 / protect 624, **静态最坏链全部未变**(3024/1912/1456/4052/1264/1320/1456/872)→ 9/9 PASS。 ⚠ 记一条读数陷阱:测试脚本打印的 "boot to net init: OK = 7,907 ms" 是采集窗口 锚点造成的度量偏差,真机日志里 `boot: syslog init OK → net init: OK` 实测 ≈699 ms, 与改前一致,**不是性能回退**。 **数字联动**:`data/ram_report_20261022.md`、`data/stack_report_20261022.md` 重跑; `ram_baseline.py --update/--write`;`check_mem_numbers.py --write`(RAM 方案受管区 + 台账);`gen_memory_svg.py` 常量更新后重新生成 FLASH 与任务栈两张配图(逐文本按 字号与锚点复查越界 = 0;RAM 图无需变更);报告 §1.3/§5.1/§5.4、部署文档 §3.3 的 Flash 数字同步;§0.4/§9 的 S-2 口径改为"24 处手写已消 23 处,只剩 `net_socket.c:77`"。 门禁:`@build` 0 错 0 警告;`cubemx-survival --check`、`mem-numbers`、`mem-budget` (57.4% / 87.9%)、`task-table`、`doc-links --all --sections-strict`(146 文档 0 问题)、 `id-check --strict`(增量拦截通过、可判定 82.83%)全绿;字节卫生 BOM/CR/U+FFFD/尾 空格全清;未提交 UV4 抖动的 `.uvprojx`(本次未被改写)。未推送。 |
||
|
|
f4b9bd57d5 |
docs(部署): 删任务栈副本表改指针;纠 CCM/FPU 事实并刷新配图基线
范围:AGENTS.md 一处指针、部署环境要求 §2.2/§3.1/§3.3、RAM 方案 6 处现状 数字、内存配图生成器 + 3 张 SVG、两份报告脚本的行尾与路径修正、新增两份 .map/.htm 数据快照。未改任何 .c/.h(仍跑 @build 复核:0 错误 0 警告)。 部署环境要求.md - §3.3 删"任务栈预算"副本表、改为指针:该表已缺 adcTask/iec104Task/ protectTask,defaultTask/timeSyncTask 两值停在旧数,注释却写"由 gen_task_table.py 同源"——该钩子的校验面只有 README.md / AGENTS.md 两处受管块,抄出来的副本没有门禁、改栈后必漂。指针指向 Src/freertos.c + 两份受管表 + RAM 方案 §2.3/§0.6,并留一条"别再抄"的成因说明。 - §2.2 链接布局补 `RW_CCM 0x10000000`(scatter 给 0x0000FF00 = 65,280 B, 2026-10-11 起承载 FreeRTOS 堆;另有 256 B UNINIT_CCM 尾区),写明 CCM 不可 DMA。原文"SRAM3/CCM 故意未映射"与 STM32F407-Demo.sct 直接矛盾。 - §3.1 MCU 行更正为**单精度 FPU(VFPv4-SP,无硬件双精度)、无 TrustZone** (原写"双精度 FPU / TrustZone"是芯片事实错误,也正是报告 F-6 双精度软 浮点被长期误判的土壤);SRAM 行改为"两块都在用,仅 SRAM3 未映射"。 - §3.3 资源预算表补 CCM 行,并把"主 SRAM 125,104 B / 95.4% / 极度紧张" 更正为 75,264 B / 57.4%(余 55,808 B);红线改标在 CCM(余 7,904 B)。 - 变更记录补 2026-10-22 行(含上一提交里的资源表刷新那一笔)。 AGENTS.md:任务表注里 "RAM使用分析与优化方案.md §1.4" 是**指错小节**——§1.4 确实 存在(该文档 §4「方案」下的阶段 1 子项,讲 lftpd 大局部缓冲静态化),但栈余量 台账在 §2.3(任务栈配置与实测峰值)与 §0.6(静态最坏链与判定),故改指这两处 再加 内存内容分配策略.md §6。根因是该文档里 §1(背景)与 §4 下的 1.1~1.5 同号 撞面,人和 doc-links(自引用放过)都会看错——本会话我上一轮就被它带错过一次。 配图与数据(draw-diagram 流程:改生成器顶部数据 → 重跑 → 逐文本核越界) - 新增 data/ram_report_20261022.md、data/stack_report_20261022.md;后者 运行时水位取自 2026-10-22 真机 [STACK] 心跳(8 任务高水位 + heap free=15,800 / min_ever=8,464),静态侧来自同日 .htm(全局最深链 3,024 B)。 - gen_memory_svg.py:三组图数据从 2026-09-21 / 10-10 快照换成 2026-10-22 实测;任务栈 6 → 9 行,堆总量 48KB → 56KB,判定 6/6 → 9/9 PASS,新增 AboveNormal 配色;Flash 子条改为"窄段不压字 + 下方四列图例";任务栈 画布 440 → 620 高。CCM 段总量按 scatter 用 65,280 B(与台账同口径)。 - 三张 SVG 重生成;用"CJK≈1.0em / 其余≈0.58em + 锚点"逐文本估算边界, 修掉 5 处文字越界/压字(生成器内置 ElementTree 自检亦通过)。 - ram_report.py / stack_report.py 两处工具性修正:write_text 未指定 newline 参数 → Windows 下把 data/ 快照写成 CRLF(与 .gitattributes 及既有快照 不符);stack_report 的 relative_to 在传相对 -i 时抛 ValueError,且会把 本机绝对路径写进入库快照 → 统一走 resolve+relative_to 的相对路径回退。 RAM 方案:6 处"当前状态"随 .map/[STACK] 更正——RW_IRAM1 89,168 → 75,264、 RW_CCM 49,184 → 57,376、FreeRTOS 堆 48KB → 56KB(两处)、CCM 余量 ~16KB → 7,904 B、min_ever 4,848 → 8,464 B、6 任务全过 → 9/9 PASS;S-1 历史句保留。 门禁:pre-commit 对全部改动文件 Passed(doc-links --sections-strict、 task-table、mem-numbers、mem-budget、id-check 增量拦截);字节卫生自查 BOM / CR / U+FFFD / 行尾空格全清。已提交本地,未推送。 |
||
|
|
96855f6b6b | 内存布局图更新到09-21最新:任务栈预算含实测峰值与判定,RAM双区/Flash布局同步 | ||
|
|
342269fbd0 | 新增内存使用分析文档(报告/规划/优化建议)与 SVG 布局图生成脚本 |