Files
STM32F4-Base/docs/测试说明/板级测试/实机测试实施指南.md
T
wandering1 7efa58d9a1
CI / 静态门禁 (pre-commit) (push) Failing after 25s
CI / 主机侧单测 (MSVC) (push) Successful in 48s
network: 台架 IP 迁到 192.168.31.100,并把重复写死的值收进单一真源
分两段(用户裁定 2026-11-02):先做地址迁移,再把同类字面量收敛成
单一真源,以后再改地址只动一处。

一、IP 迁移 192.168.1.100 → 192.168.31.100(gw 同步 → .31.1)
- 工厂默认:Drivers/BSP/NETCFG/net_cfg.c 的 NET_CFG_DEF_IP/_GW
- 注释/文档示例、主机与板级测试、台架 py 脚本、两份 sim_config.ini
- 字节序教学点同步换算 hex:0x6401A8C0 → 0x641FA8C0(内存
  C0 A8 1F 64),旧版示例引文与历史 Trap 记录按冻结纪律不动
- UI 黄金基准:网口设置 4 态 + shot_info 共 5 张 .bin 由
  check_content_pages.py --update 重刷(渲染值 IP 变了,属预期变更)
- 保留判定:客户点表引用(IEC104/继电保护规划)、芯片手册摘录、
  日期冻结的变更记录行、已退役台架档案 MDK-ARM/retracted_bench_*

二、单一真源收口(A+B+C+D,用户批准全做)
- 新建 test/host/bench_defaults.py:台架 py 默认值唯一定义点
  (DEVICE_IP/DEVICE_GW/PC_IP/NET_BCAST),22 个脚本改为引用;
  MDK-ARM 两探针与 scripts/board_ui_nav.py 经 sys.path shim 引入
- test/board/net_test_task.h 新增 TEST_PC_PEER_IP,板测三处
  PHASE*_REMOTE_IP 改引该宏(保留 -D 覆盖);Phase1 断言改
  memcmp 数组比较,数组各只出现一次
- simulator/tests/check_content_pages.py 从 net_cfg.c 正则解析
  NET_CFG_DEF_* 推导期望值(解析失败即报错、不静默兜底),
  脚本内台架 IP 字面量清零
- 装饰性注释去具体值(net_cfg.h/net_socket.h/net_socket.c/
  net_socket_sim.c);字节序教学 hex 对刻意保留为唯一写值点
- 刻意不动:主机单测夹具 IP(逐文件钉住的任意值是正确测试
  实践)、文档正文 IP(那是文档内容本身)

顺带:scripts/gen_task_table.py 规范串清掉「,B5」——编号清退提交
72b4b094 改了受管区没重跑生成器,task-table 门禁因此一直红

验证:产品 @build 0/0;CH395F 套件开关构建 0/0 后还原产品态;
6 套主机单测 rc=0;content_pages(含宏推导路径)/菜单 8 态/
推流位比对全 PASS;bom-drift 99 无漂移;doc-links/coding-standard/
cppcheck 绿;py_compile 23 文件 + --help 冒烟(含 shim 绝对路径)
2026-11-02 14:46:42 +08:00

36 KiB
Raw Blame History

实机测试实施指南(On-Board Test Guide)

文档定位:本文 = 上板(真机)测试的执行流程——按顺序执行。 每步给出「目的 → 判据(可观测、量化)→ 命令 → 状态(✅ 已闭环 / ⬜ 待执行)」。 已完成事项(S7、A1~A4)的实施细节与证据不在本文,见对应模块文档(§10 状态与去向)。 前置铁律:本文所有步骤均在接上 MCU / 串口 / ST-LINK 之后执行; 无实机时只读本文,不执行任何烧录。 上游:docs/任务规划/README.md(统一登记簿:P-x / N·S·T-x / S1x·A-x / 阶段排序 / 诊断基线)· 子系统设计/看门狗设计说明.md(§7 验证记录)。 约定:本文事项完成后先销上游登记,再把结论写进正文对应小节(本文不再记流水,过程查 git log); 结论与证据沉淀进对应模块文档/问题记录(2026-10-02 台架窗口的完整取证链—— IWDG PR/RLR 静默错配 / dhara resume 回滚 / ORE 僵尸通道——见 看门狗设计说明.md 2026-10-02 变更记录、 STM32F4-Base存储架构说明.md §5.4.1、控制台单写者日志设计说明.md §12;修复已随 930215f 提交)。


1. 执行流程总览

台架窗口就绪后,按下列顺序执行(P0 → P1 → P2 → P3):

步骤 事项 状态 章节
步骤 1 环境预检(P0-A,每次上台架必跑) 常设 §2
步骤 2 产品 /serial-boot-test 闭环 + 自检结果判定(P0-B) ✅ 已闭环 §3
步骤 3 IWDG 故障注入 V2/V4 + 长跑 V5(P1-A) ✅ 已闭环 §4
步骤 4 堆低水位告警实测(P1-B) ✅ 已闭环 §5
步骤 5 GD5F 错误通道回归(P2-A) ✅ 已闭环 §6
步骤 6 网络稳定性与基线重锁(P2-B / 日志静默观察项 + 队列往返基线待重验) ◐ 部分闭环(7.1/7.2 ✅;7.3 待 PC peer) §7
步骤 7 工程卫生(P3 + 待核实项) ✅ 已闭环 §8

每步出口:/keil-build 0/0 → 一轮 /serial-boot-test PASS → ST-LINK 全片回读 No difference found → 上游登记销项 + 把结论写进正文对应小节。

参考(非执行步骤):§9 执行注意(实测陷阱)· §10 已完成事项与证据去向 · §11 引用闭环。


2. 步骤 1:环境预检(P0-A,每次上台架前必跑)

目的:避免"改动没问题、台架有问题"的误判(历史上多次把接线/权限问题误判为代码缺陷)。

# 检查 命令 通过判据
A1 工作区干净 git status --short 无输出(或有明确待提交项)
A2 产品构建 0/0 & '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py 末行 BUILD_OK: 0 errors, 0 warnings
A3 构建产物为产品态 检查 test/board/test_config.h L30-33 四个 TEST_SUITE_* 全部注释
A4 .map 非测试态 看 MDK-ARM/STM32F407-Demo/STM32F407-Demo.map 的 ER_IROM1 产品态基线随已接受的修复增长:262,100(初版)→ 262,836 B(10-02 第 16 轮台架)→ 264,284 B(第 79 轮台架)→ 264,936 B(当前,含 A2/A3/A4 补码);明显阶跃 ≈3.7KB 及以上(10-02 实测产品 262,836 → 套件 266,540)= 停在套件态 → 重跑 A2
A5 全量静态门禁 pre-commit run --all-files(现只覆盖 pre-commit 阶段 47 根;manual 阶段三根另跑 --hook-stage manual <id>) 当时 17/17 Passed(2026-10-03 实测;含新增 test-cppcheck)
A6 ST-LINK 探针可见 & '.venv\Scripts\python.exe' .dsh\skills\stlink-flash\scripts\stlink_flash.py list(CLI 经 DTU_STLINK_CLI / PATH 解析) 列出探针 SN 0B250D12A218303030303032
A7 串口端口正确 检查 .serialtest/config.json 的 portName 端口单一配置源 = 此文件(当前 COM6,2026-09-21 台架重插后由 COM7 变更);变更后先杀旧 daemon 再 open
A8 串口助手 daemon 可用 & '.venv\Scripts\python.exe' .dsh/skills/serial-assistant/scripts/serial_tool.py status 返回 JSON,open: true/false 可读

⚠️ A4 的陷阱(2026-10-02 实测):/keil-build 是增量构建,run_all_suites.py 跑完 套件后 .map 会停在测试套件体积;仅还原 test_config.h 不够,必须再编一次产品固件 (run_all_suites.py 已在 finally 内自动补这一步)。

预检失败处置:任一项 FAIL → 不上台架,先修到全绿(避免"带病测试"浪费台架窗口)。


3. 步骤 2:产品 /serial-boot-test 闭环 + 自检结果判定(P0-B) · ✅ 已闭环

S7 判定已落地并投用;A1~A4 已闭环。实施细节与真机验证证据见 §10 状态与去向,本节只给复跑命令与判据。

3.1 执行

/serial-boot-test 闭环除串口标记外,还判定设备侧自检结论(v1 只记故障、不记通过): selftest.log 缺失 / 0 字节 → 无故障 → PASS;filesize/64 > 0(任一 64B 定长记录) = 有故障 → FAIL,并打印故障条目(时间戳 + 描述)便于定位。

# 一键闭环(selftest.log 自动 FTP 抓取;无故障记录 = PASS)
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py --require-selftest-pass --device-ip 192.168.31.100

# 或手动联合判定(本轮串口日志 + selftest.log 本地副本)
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/verify_log.py \
    --log-path <本轮串口日志> \
    --require-selftest-pass \
    --selftest-log <下载的 selftest.log 本地副本>
#    判据:退出码 0;输出含 "[OK] selftest: no fault record"

3.2 自检项清单(S7 判定覆盖面,20 处)

来源:全库 syslog_post(SYSLOG_SELFTEST, ...) 调用点。这是 S7 的判定覆盖面基准。

文件 自检描述串
App/app/app_main.c watchdog reset / clock chip fault / NAND fault / ADC fault
App/cfg/cfg_file.c cfg heap alloc fail(×3) / cfg template write fail / cfg load oversized / cfg load read fail / cfg save read fail / cfg save overflow / cfg save write fail / cfg poll oversized
App/task/default_task.c filesystem fault(×2) / storage fault(×2)
App/task/net_task.c comms fault / net init fail / net reinit fail

联动:syslog_post 的两级自愈(板端测试套件文件级重试 / 主机侧回归错误去重 s_fault_reported[]) 决定持续故障只留第一条——故 S7 判定"有记录即 FAIL"是充分的(不会被去重掩盖)。


4. 步骤 3:IWDG 故障注入与长跑(P1-A 收尾) · ✅ 已闭环

2026-10-03 台架结论(实施细节与证据见 子系统设计/看门狗设计说明.md 变更记录): V2/V4/V5 三用例全部通过——V2(任务饿死)声明 stall 后 ≤2.5s reset cause: IWDG;V4(关中断死循环)每 ~4.9s 稳定 IWDG 复位(IWDG 独立于中断);V5(30 min 真实负载)120/120 次 FTP 传输全 PASS、零 IWDG 复位。 遗留观察:V5 期间 44 次 stall 声明 100% 与 e02_probe 探针的 time_sync 30s 掉线+监听重建重合;隔离实验(仅 FTP 120s)0 次 stall → 探针场景专属,非一般负载误杀(已登记 docs/任务规划/登记簿.md)。

来源:子系统设计/看门狗设计说明.md §7 验证记录(原实施步骤已并入该文档文末变更记录)。 判定状态机与 IWDG 配置(PR/RLR 按设计值写入)已落地并经真机验证(2026-10-02,930215f, 见设计说明 2026-10-02 变更记录);故障注入 + 长跑验收结论见上文 2026-10-03 段落。

4.1 三个验收用例

# 用例 注入方法 通过判据
V2 任务饿死 临时在 netTask 循环插 while(1); 800 ms 后串口 [ERR] WATCHDOG: task stall mask=0x...;≤2.5 s 复位;重启后 [WD] reset cause=watchdog reset
V4 关中断死循环 临时插 taskDISABLE_INTERRUPTS(); while(1); ≤2.5 s 复位(检验 IWDG 独立于中断);cause=watchdog reset
V5 长期不误杀(核心验收项) FTP 大文件传输 + UI 菜单遍历 + 配置保存 + 网络重配 ≥30 min 零复位(重点验证 docs/子系统设计/看门狗设计说明.md §4.3 白名单是否完备)

⚠️ V5 是核心验收项:误杀比不杀更有害。必须在真实业务负载下跑足时长。

4.2 执行步骤

# --- V2/V4(故障注入,需临时改码)---
# 1) 基线:跑一轮产品 /serial-boot-test 确认无复位
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py

# 2) 注入:按 `docs/子系统设计/看门狗设计说明.md` §7 在 netTask 循环插 while(1);(或 taskDISABLE_INTERRUPTS(); while(1);)
#    —— 改完 /keil-build,然后烧录
& '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py
& '.venv\Scripts\python.exe' .dsh\skills\stlink-flash\scripts\stlink_flash.py flash MDK-ARM\STM32F407-Demo\STM32F407-Demo.hex

# 3) 抓串口日志,确认 [ERR] WATCHDOG task stall + 复位原因
# 4) 回读校验复位后镜像(ST-LINK_CLI 经 DTU_STLINK_CLI 解析,未设时回退 PATH,不写死路径)
$cli = if ($env:DTU_STLINK_CLI) { $env:DTU_STLINK_CLI } else { (Get-Command ST-LINK_CLI.exe).Source }
& $cli -c SWD -CmpFile MDK-ARM\STM32F407-Demo\STM32F407-Demo.hex 0x08000000
#    判据:No difference found

# 5) 撤销注入,/keil-build 回产品态,再跑一轮 /serial-boot-test 确认正常

# --- V5(长跑,需真实负载)---
# 1) 用 test/host/ftp_speed_test.py / ftp_up_dl_test.py 持续传输大文件
& '.venv\Scripts\python.exe' test/host/ftp_speed_test.py
# 2) 同时人工/脚本遍历 UI 菜单 + 反复保存配置 + 触发网络重配
# 3) 持续 ≥30 min,全程串口记录
#    判据:零复位(日志无 "reset cause=watchdog reset");业务不中断

风险/回滚:

  • 故障注入用例会复位设备多次——确认设备非在用/非已部署。
  • 长跑涉及 NAND 写入(FTP 上传)消耗 NAND 块——若设备已接近容量上限,改用只读传输。
  • ⚠️ R5 风险(docs/子系统设计/看门狗设计说明.md §9):NAND 擦写偶发超 800 ms 可能误复位——若 V5 出现偶发复位, 先按设计说明增大容忍次数或把存储操作改分段置位,不要盲目放宽全局超时。

5. 步骤 4:堆低水位告警实测(P1-B,S11) · ✅ 已闭环

2026-10-03 台架结论:告警代码(default_task.c 的 HEAP_LOW_WATER_B=3072 门闩 + [HEAP] 告警 + syslog 自检)已落地;复用 §4.2 V5 窗口 30 min 长跑,全程未触发(min_ever=4728 B > 3072 B)→ 记录"当前负载下 min_ever 仍高于阈值"为基线,已更新 内存管理/RAM使用分析与优化方案.md。

现状证据:2026-10-03 上板复测 free=8,688 / min_ever=3,712~4,728 B (两次开机,运行间波动 ≈1KB;初始 configTOTAL_HEAP_SIZE 48KB)—— min_ever 下限已跌破 4KB,余量不到 8%,新增任务/缓冲可能触发分配失败。 现有代码仅有周期性 [STACK] 报告(每周期打印 heap free/min_ever),无阈值告警、不写自检记录。

待补代码(约 15 行 + 1 个编译期常量,在 defaultTask 低频循环(LED 闪烁节拍)内): 检查 xPortGetMinimumEverFreeHeapSize(),低于阈值(建议 3072 B,低于 10-03 实测下限 3,712 B,防运行间波动误报)时 DBG_ERROR("[HEAP] low water: %u B") + 写 syslog selftest。

判据:台架长跑(复用 §4.2 V5 窗口)下,若发生低水位 → 串口出现告警行 + selftest.log 有记录; 若全程未触发 → 记录"当前负载下 min_ever 仍高于阈值"作为基线。

执行步骤:

# 1) 代码改动(约 15 行 + 1 个编译期常量)在 defaultTask 低频循环内
# 2) /keil-build 0/0 后,与 §4.2 V5 长跑合并执行
# 3) 长跑结束读取 min_ever:
#    - 串口 [HEAP] 告警行(若有)
#    - 或 FTP 取 /log/selftest.log
# 4) 更新 docs/内存管理/RAM使用分析与优化方案.md 的 min_ever 基线

成本/回滚:约 15 行 + 零额外缓冲;阈值设编译期常量便于按新基线调整。 若长跑中频繁触发但系统正常 → 说明阈值偏紧,据实测值调整(不是缺陷)。


6. 步骤 5:GD5F 错误通道统一(P2-A,S9 = 上游 C2) · ✅ 已闭环

2026-10-03 台架结论:四要素(分类/透传/分级/留痕)+ 返回类型统一 int32_t 已落地。 套件回归:test/run_all_suites.py --suites gd5f → === GD5F2GQ5UE Tests: 16/16 PASSED (failed=0) ===(只读模式,4 个破坏性用例按设计跳过);编排器 [GD5F] -> PASS。 顺带修复(编排器缺陷,非本次代码引入):run_all_suites.py 原对所有套件统一 --forbidden "HardFault;assert;[ERR]",而 GD5F 的 TC-GD5F-704(CCM 非 DMA 可达缓冲)会预期打出 [ERR][NAND] RX DMA buffer not DMA-reachable ... fallback poll(用例 PASS)→ GD5F 套件永远被判 FAIL(自 2055de0 起)。按 verify_log.py 的说明改为按套件覆盖 --forbidden(GD5F = HardFault;assert),修复后 PASS。

来源:第二档 #1; docs/产品需求/产品需求设计文档.md §7.1(错误码通道四要素骨架,设计基线)。

现状证据:ch395f.c(1330 行) 与 gd5f2gq5ue.c(1496 行) 是同代驱动,但只有 CH395F 形成了"分类 + 传播 + 审计"完整体系;GD5F 错误处理散落各函数,Trap_Records 仅 198 行。

统一错误通道四要素(以 CH395F 为范式):

要素 内容
① 分类 wait_busy 区分「超时 / 真故障 / 参数错」——不得混为一个返回码
② 透传 底层错误码逐层上报,不吞不猜(禁 (void)ret)
③ 分级 可恢复(重试)/ 不可恢复(上报自检 + 降级)显式判定
④ 留痕 不可恢复项写 syslog selftest;可恢复项计数并在 UI/日志可见
— 返回类型统一 int32_t

实施顺序(数据安全优先):

  1. 先建分类函数(gd5f_wait_busy 内检查三次 HAL 调用)——所有下游判断的基础;
  2. 再修最危险的:gd5f_bbt_scan 失败放弃持久化(保护好副本)——数据安全优先;
  3. 补 init 全链路检查 + 重试 1~2 次;
  4. 回读 B0h 校验 ECC 使能(bit4);
  5. gd5f2gq5ue_read 的 raw 契约文档化或加 ECCS 检查;
  6. 全程在 GD5F2GQ5UE_Trap_Records.md 留痕,同步 GD5F2GQ5UE_Test_Guide.md。
  7. RAM 预算:新增错误分支不得引入新的大缓冲(先跑 ram_report.py 核算)。

判据:

  • 错误通道审计项全部销项;
  • GD5F 套件回归 PASS:
# 套件回归(需实机)
& '.venv\Scripts\python.exe' test/run_all_suites.py --suite gd5f
#    判据:=== GD5F Tests: N/M PASSED (failed=0) === 且 N>0

# 只编译验证(离机,改动后随手跑)
& '.venv\Scripts\python.exe' test/run_all_suites.py --check-build

⚠️ 纪律(呼应 AGENTS.md 铁律):自愈逻辑严禁把"名字/参数类错误码" (如 FR_INVALID_NAME)归入"卷损坏触发集"——.fschk 无限重格式化事故即此类(2026-09-20)。 ⚠️ 破坏性用例:GD5F 套件默认只跑只读;ENABLE_DESTRUCTIVE_TESTS 跑一次消耗一个 NAND 块 (实测 95248→95200),仅在产线首启或明确需要时打开。


7. 步骤 6:网络稳定性与基线重锁(P2-B) · ◐ 部分闭环(7.1/7.2 已闭环;7.3 待 PC peer)

7.1 网络重建失败恢复(A2;实施细节见 网络/Net层阻塞模型架构设计.md,状态见 §10) · ✅ 正常路径已闭环

判据:拔插网线/改 IP 触发重建后——

  • 失败路径写 syslog 自检(不再零日志);
  • 30s 周期重试,不自驻留死循环;
  • 恢复后各 TCP 服务(FTP 21 等)据代次号重建监听,功能正常。

验证手段:test/host/e02_probe.py(网络探针)+ UI 网口设置页改 IP。

2026-10-03 台架结论:

  • 正常路径 ✅:V5 长跑(30 min,FTP 512 KB 上/下传)120/120 PASS、零 IWDG 复位, 网络重建后的 FTP/time_sync 服务据代次号重建监听、功能正常(证据 /tmp/v5_soak_serial_20261003.log)。
  • 负向路径(拔线/芯片级 net_init 失败)受台架拓扑限制(CH395↔交换机, 拔交换机侧线缆链路仍 UP;芯片级失败需 SPI 故障注入)→ 已登记 docs/任务规划/登记簿.md 条目「网络负向场景台架验证」 (① PHY 拔线负向 ② A2 net_init 失败分支 ③ A4 短写压测),需专项窗口。

7.2 日志静默观察项:49s 全系统日志静默复现与定位 · ✅ 判据②已满足 / 判据①未复现

现状证据(登记簿条目「日志静默观察项」):2026-09-22 台架实测,串口日志 49s 无行 (testTask/netTask/[STACK] 行全缺),期间 net 栈照常处理外部连接 → 日志管线停滞, 队列满后各任务日志行被丢;系统功能不受损。

判据与核实(2026-10-03):

  • 判据②(队列满丢弃计数/告警)→ ✅ 代码已满足:日志管线已有显式丢弃可观测性—— App/log/console_log.c 的 s_con_drop 计数 + 排空末尾 [ERR] CONSOLE dropped %lu lines 提示 + dbg_console_dropped() 访问器;App/log/sys_log_mcu.c 的队列满/preboot 满分别 打 DBG_ERROR("queue full, drop: …") / ("preboot full, drop: …")。另 UART TX 卡滞 已由僵尸通道自愈覆盖(con_uart_reinit() + [WARN] CONSOLE tx fault: UART reinit, s_con_tx_fault 计数)。"现静默丢行、排障不可见"已不成立。
  • 判据①(复现 49s 并定位卡点)→ 未复现:对 V5 30 min 长跑日志(6031 条带时间戳行) 做间隔分析,最大静默 41s(发生在开机/烧录边界,非运行期),运行期无 49s 静默。 运行期 >10s 的静默共 24 处,全部为 ~28s,且均落在 e02_probe 的 time_sync 30s 挂起客户端窗口内——与「探针窗口心跳缺口」同源(探针场景专属,非通用负载)。
  • 结论:49s 静默在当前固件 + V5 负载下不复现;可观测性缺口已由既有代码补齐。 日志静默观察项降级为低风险(保留登记,若再现则借 [ERR] CONSOLE dropped 计数定位)。
  • 后续(2026-10-25,道F 第二轮 合并评估):窗内静默的同源阻塞机制 (drop_conn 同步占用 netTask 400-600ms)已随「登记式拆链」修复 (../../网络/网络自愈架构设计.md §4.6);日志静默观察项与日志单写者化的合并评估已完成,结论与「无源 vs 真停滞」判别口径落 ../../子系统设计/控制台单写者日志设计说明.md 文末附录—— 再现静默先按三本账(dbg_console_dropped/_tx_fault/_tx_timeout)+ syslog 丢弃 账对照定性,再决定是否开卡;销项条件 = 探针窗口心跳缺口修复版 V5 复跑(e02_probe + FTP ≥30 min)运行期 task stall 与真停滞嫌疑静默段双零(2026-10-25 v1.1 起 MISS start 降级为诊断行不计销项——见下方判读工具与 ../../子系统设计/看门狗设计说明.md §6.1)。
  • 判读工具 v1.1(2026-10-25 大窗判读 F1 起): '.venv/Scripts/python.exe' scripts/v5_log_gap_report.py <serial-assistant 日志路径> ——自动列 >10s 静默段(--gap-threshold-s 可调),每段带判定(NO-SOURCE / PROBE-WINDOW / BOOT-WINDOW / TRUE-STALL-SUSPECT)与贴边证据行。违规口径 = task stall 行 >0 或 真停滞嫌疑段 >0;退出码 0=达标 / 1=违规 / 2=日志无有效时戳;--json 机读、--report-only 只出报告、--selftest 内嵌合成样例自检(无台架可跑)。v1.1 变更(判读口径,先文档后脚本): ① MISS start 降级为诊断行(看门狗 v1.5 按位分账后它只是复合缺席边沿, 不代表停喂,../../子系统设计/看门狗设计说明.md §6.1)——报告改出归因诊断: 开机良性 / 探针窗内 / 窗外运行期 + 窗外每分钟速率 top;v1.0 把运行期 MISS 计违规属收紧过头(2026-10-25 台架 70min 窗 1262 条经逐窗归因实为风暴复合边沿 与传输窗抖动),予以纠正;② task stall 行语义 = v1.5 起「同一任务 800ms 单串真停摆」(行文本不变,../../子系统设计/看门狗设计说明.md §5.3),每行都是 真猎物;开机豁免口径(§9 第 8 条)保留、仅作用于 MISS 诊断分类。口径唯一 来源 = ../../子系统设计/控制台单写者日志设计说明.md 文末附录 + §9 第 8 条 + 看门狗设计说明——改口径先改文档再改脚本(自检断言会拦漂移)。

执行步骤:长跑(§4.2 V5)期间周期性打点 + 对照 CH395F 无关的 SD2506 侧日志判别卡点。

7.3 队列往返基线待重验 · ⬜ 待 PC peer

现状:L5 基线(1223/988µs)属分片时代;人工目检对该路径逐位不变 → 机制上无影响, 但严格重锁需跑一轮 Phase 13(需 PC peer)。

判据:Phase 13 窗口跑一轮,基线数值与阈值余量(>8×)对照,重锁数值。

2026-10-03 台架:本窗口无 PC peer,Phase 13 无法执行 → 保留待办(低风险: 阈值余量 >8×,机制上 人工目检对该路径逐位不变)。


8. 步骤 7:工程卫生(P3) · ✅ 已闭环

S5 / S4 落地(离机可做),待核实项已实机核实并修复。本节留复跑命令与判据。

8.1 S5:test/ 代码宽松静态检查

现状证据:test/ 完全豁免 pre-commit("自律"要求),而 test/board/ 下 6300+ 行 C 代码 (实测 .c+.h 合计 6,313 行)且编入固件。曾出现 s_rx_buf 使用先于声明。

目标:抓 test/board/ 固件测试代码的真实缺陷(未初始化/越界/空指针), 但不引入存量 style 噪音。

执行步骤(先跑基线,离机可做):

# 1) 基线统计(不需实机)
cppcheck --enable=warning --error-exitcode=1 test/ 2>&1 | tee test_cppcheck_baseline.txt

# 2) 若存量可控(建议 <20 条)→ 加 test-cppcheck 钩子,只开 warning
# 3) 若存量较多 → 先建 suppress 清单(明确每条豁免理由),再挂钩子
# 4) 务必排除模拟器桩/第三方头(test/host/watchdog/stub/ 等)

判据:test-cppcheck 在 CI PASS;新增测试代码越界/未初始化被自动拦截。

注意:若基线告警过多,保持现状亦可接受——此时应把该决策写明确("理由:存量 N 条")。

结论(✅ 已闭环,2026-10-03):已加 test-cppcheck 钩子(.pre-commit-config.yaml id: test-cppcheck,仅开 warning、排除模拟器桩/第三方头)。基线可控, pre-commit run test-cppcheck --all-files PASS。新增 test/board/ 代码的 未初始化/越界/空指针问题现由钩子自动拦截。

8.2 S4:用例追溯脚本

现状:文档声称 91 个 TC、代码有 222 个 TEST_CHECK(断言点数 > 用例数是正常的), 但双向是否一一对应无人校验。

目标:新增 [计划] scripts/check_test_traceability.py—— 输入 test/board/*_test_task.c 的 TC 注释 + docs/测试说明/板级测试/*Test_Guide.md 的 TC 编号; 输出 ① 文档有、代码无(未实现)② 代码有、文档无(未登记)。 默认仅告警(warning),不阻断提交(与 doc-links 演进路径一致)。

判据:一键列出双向差异清单;差异收敛后再考虑升为门禁。

结论(✅ 已闭环,2026-10-03):scripts/check_test_traceability.py 已落地并跑通, 默认仅告警(exit 0)、不阻断提交。2026-10-26 重打:当前差异 25 处(文档有代码无 + 代码有文档无两向), 另有约 1,486 处属"代码有该号、号旁写着人话意图",按「代码即属主登记处」放过——弱于文档的保证, 对外举证仍需模块级说明。当年列出的孤儿号(GD5F 那三个 11xx、存储 06 号)已在 2026-10-26 清号批次 里从代码侧删号收口。差异清单可追踪,后续按需补登记;暂不升门禁(与 §8.2 目标一致)。

8.3 待核实项:TEST_SUITE_SELECTED 未含 TEST_SUITE_RTC · ✅ 已核实(非缺陷)

原疑点(test/board/test_config.h):派生标记 TEST_SUITE_SELECTED 只含 CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 testTask 不被创建 → 套件不执行。

2026-10-03 实机核实结论:

  • TEST_SUITE_SELECTED 是死代码:grep -rn TEST_SUITE_SELECTED 全仓仅本文件有定义、 无任何使用点。Src/freertos.c 无条件创建 testTask;test/board/test_config.c 的 start_test_task() 直接按 TEST_SUITE_RTC 等宏分支(不经 TEST_SUITE_SELECTED)。 → "testTask 不被创建 → RTC 不执行"的推断不成立。
  • RTC 套件确实执行:选 TEST_SUITE_RTC 构建烧录后,串口出现完整 RTC 输出 (SD2506 test suite started … Phase 1 basic: 3/3 PASSED … Phase 2 API: 6/6 PASSED)。
  • 真正的缺口 = 缺套件级汇总行:原 sd2506_test_run() 只打 Phase 级汇总,没有 === RTC Tests: N/M PASSED (failed=0) === → run_all_suites.py --require-suite-summary "RTC Tests" 判 FAIL(设备实际 12/12 全过)。属判据缺口,非设备缺陷。

修复(2026-10-03):

  1. test/board/sd2506_test_task.c:各 Phase 进入前自清零,末尾输出套件级 === RTC Tests: N/M PASSED (failed=0) ===(复用 SD2506_TEST_REPORT 宏,格式与 test_common.h 统一)。顺带修正 Phase 级汇总行累计问题——原 Phase 2 显示的是 含 Phase 1 的累计值(9/9),现各阶段独立:Phase 1 = 3/3、Phase 2 = 6/6、套件级 = 9/9。
  2. test/board/test_config.h:TEST_SUITE_SELECTED 补齐 RTC 并注释其"当前无引用", 防止将来真有分支复用时重演"漏 RTC"。

复跑命令:& '.venv\Scripts\python.exe' test/run_all_suites.py --suites RTC 判据:[RTC] -> PASS、VERDICT: PASS,日志含 === RTC Tests: 9/9 PASSED (failed=0) ===。


9. 执行注意:实测陷阱(2026-10-02 台架窗口,照做可少绕一天)

  1. 写入"复位后消失"的两层原因(窗口最大坑,根因均已修复,详见模块文档):
    • 板端测试套件 dhara checkpoint 回滚(架构设计/STM32F4-Base存储架构说明.md §5.4.1):环回绕后 dhara_journal_resume() 恢复旧 checkpoint,之后写的一切整段丢弃 (已修:epoch 锚点改比较两候选的新旧)。
    • 主机侧回归 FTL 单页写回缓存:一次 disk_write 里不满整页(2048 B)的写,在写到另一个 LPN / 整页写 / CTRL_SYNC 之前只存在于 RAM(nand_ftl.c 的 s_cache_buf/s_cache_dirty)。 但正常 f_close 就会走到 CTRL_SYNC(FatFs sync_fs → disk_ioctl → ftl_flush_cache), 所以**"写完即关"的路径(日志、配置保存)关文件即落盘,不必再"写另一个文件逼刷";会丢的 只有写事务进行中掉电/硬复位(含 IWDG 误复位)里最后那笔未跨页的写(≤512 B)—— FTP STOR 传输途中掉电就落在这一档(已登记观察;「写回缓存空闲 flush」于 2026-10-22 裁定暂不做, 缺的是"空闲/掉电前周期 flush",且本机无掉电检测硬件,掉电时点无法补救)。 → 排查写入丢失时先看开机日志的 head/tail/epoch**(Map head=… tail=… epoch=…, 纯内存字段、零 I/O):head 复位后倒退 = 板端测试套件(resume 错);head 正常 = 主机侧回归(缓存未刷)。 (旧口径"清台账/写证据后写另一个文件"的 flush 诱导属 板端测试套件时代的产物:现码 f_close 自带 CTRL_SYNC,正常写完即关就不需要它;这条诱导只在 f_close 失败时才还有兜底意义。)
  2. FTP 路径:lftpd 相对路径基准是根目录(SIZE selftest.log → 550),一律用 绝对路径 /log/xxx;LIST /log 实测列的是根目录内容(lftpd 怪癖)——查状态只用 SIZE / RETR。
  3. 0 字节 STOR 的数据连接存活期仅 1050ms,短于芯片/网络层轮询粒度 → 芯片只见 DISCONNECTED (chip tcp=0x00) → accept 失败(425 或控制连接被重置);2026-10-02 实测客户端 PASV→STOR 间隔达 11 s(数据口 LISTEN 半开)。这是与看门狗误复位不同的另一类 问题(CH395F 数据通道);若修复后仍复现 425,转 §7.1 专项。
  4. 串口采集铁律:open → clear → 烧录/复位 → wait-idle --timeout 90 --settle-ms 8000 --max-ms 45000 → 直接 Read/Grep .dsh/skills/serial-assistant/scripts/state/serial.log (log --grep 子命令不可信);daemon 残留 → taskkill /F /PID <旧> 后重新 open。
  5. 只用 .venv\Scripts\python.exe(PATH 里的 python 是 msys64);每次烧录后必须 ST-LINK 全片回读 No difference found。
  6. 误复位会使台账 +1 条 watchdog reset:取证/修复期间台账只会增长, 属预期(清台账留到最后一次修复后的出口闭环)。
  7. ST-LINK_CLI 取证三硬知识(完整用法见 ST-LINK_CLI_复位使用说明.md):
    • -R32 不会 halt 内核(-CoreReg 才 halt,实测约 30ms/次且可重复)→ 用 -R32 做 无扰动存活性采样,只在需要 PC 时用 -CoreReg(halt 期间 TIM7 ISR 不执行, 会让中断计数出现"假停顿",须用非 halt 读法甄别)。
    • -R32 <addr> <count> 的 count 单位是字节(-R32 0x1000FF00 16 得 4 个字); 按字理解会把读取静默截断。
    • -W32 <addr> <data> 的 data 按十六进制解析(写 1249 会变成 0x1249, 被 12 位 RLR 截断成 0x249=585);要写十进制 1249 得传 0x4E1。另 IWDG->KR 是 只写寄存器(读回恒 0),-W32 写 KR 必报 Error!——那是 ST-LINK 的写-回读 校验失败,写入其实成功。
    • 现场改寄存器(如 IWDG PR/RLR 验证手段)须在一次调用内完成整段键序列 (解锁→参数→键锁)——分多次调用会超过看门狗超时、中途被复位;寄存器随复位丢失, 只能用于验证,不是修复。
  8. 开机良性 MISS start 行(2026-10-03 上板):看门狗抽样在开机屏障等待期偶见 UI 位单抽样缺席,每 episode 记一条裸 UART MISS start snap=0x… req=0x… t=… (task_watchdog.c 的停摆起点标记,round-7 取证件)。实测每次开机 2 条 (t≈0.6s / t≈2.8s,snap 仅缺 UI 位),NET_READY 解除后立即恢复——未达 800ms 停喂 阈值、不触发复位,属分片心跳与 100ms 抽样边界的固有交错,非回归; 处置:见到该行不必恐慌,连续出现 task stall 行/复位才是真停摆。
  9. 导航类自动化脚本默认零 ENTER(2026-10-26 台架实证,task-41 收口;口径也见 启动日志原子性复验流程.md):装置维护的 强制复归 / 清除记录 是两段式确认—— 第一次 ENTER 只把该行标成「确认?」,第二次 ENTER 才真执行(复归保护位并写 SOE / 清 SOE·运行·操作记录)。因此任何"认不出屏就按 ENTER 试一下"的回收逻辑,落到这两页上 等于替操作员按下执行键;更隐蔽的是它只在画面异常时才发作(新字库缺字、白框、状态行 变化——恰恰是回归最想抓的那几帧),平时零流量、改动时无声。 scripts/board_ui_nav.py 的两条硬闸与 --selftest-quick 里的 7 条常驻用例(假设备 + 内存空白帧 + 按键只记账, 不连 7003、不开串口、不碰台架,提交钩子 uinav-selftest 就跑这档)把它钉成回归: ① UNKNOWN 退避的 ENTER 上限 BACKOFF_MAX_E(政策钉死 ≤1,需要第二次就当场停机并 dump 帧, 报错文案点名执行类风险);② EXEC_PAGE_IDS(clear / reset)默认拒绝下钻且拒绝发生在 任何键投出去之前(真机实测:连轨迹文件都不生成),显式 --allow-exec-pages 才放行并打警告。 用例还钉住两个常量本身:上限被调到 2 以上、清单被清空、或默认值被翻成"允许"—— 都必须在 --selftest-quick 里报红(这三条都用"故意改坏再跑"逐条证过能红,不是空跑)。 通用要求:导航 = 移光标 + 取帧;ENTER 只能出现在明确、非执行类的用例步骤里; 要看执行类页面的显示正确性,去模拟器黄金参考(那里没有真机后果)。

10. 已完成事项与证据去向

已完成事项的实施细节与真机证据不在本文,按下列去向查阅;本文只保留其复跑命令与判据。

事项 状态 实施细节 / 证据所在
S7 自检结果判定 ✅ 已落地并投用 docs/测试说明/流程与规范/自动化测试流程.md;提交 4a4cd29
A1 cfg_file 堆分配改造 ✅ 已闭环(含真机持久化) 子系统设计/配置文件设计说明.md(实施 v1.11 + 2026-10-03 真机验证记录 + 2026-10-04 独立复跑记录)
A2 网络重建失败恢复 ✅ 已闭环(正常路径零回归;故障分支见「网络负向场景台架验证」) 网络/Net层阻塞模型架构设计.md
A3 CH395F 字节命令超时 ✅ 已闭环(正常路径零回归;故障分支待专项窗口) —
A4 remo_send 返回值 ✅ 已闭环(正常路径零回归;推流压测待专项窗口) —
2026-10-02 台架取证链(IWDG PR/RLR 错配 / dhara resume 回滚 / ORE 僵尸通道) ✅ 已修 子系统设计/看门狗设计说明.md 2026-10-02 变更记录 · 架构设计/STM32F4-Base存储架构说明.md §5.4.1 · 子系统设计/控制台单写者日志设计说明.md §12;提交 930215f

真机判定汇总(2026-10-03,本机 MCU,新固件全闭环 @test PASS 7/7):A1 ✅ PASS(含配置持久化); A2/A3/A4 正常路径零回归;芯片级失败分支 / 短写 / 拔线负向场景记 docs/任务规划/登记簿.md 条目「网络负向场景台架验证」,待专项台架窗口。


11. 与既有文档的引用闭环

本文项 上游文档 下游落地文档
S7 自检判定 —(原复审项,已闭环 2026-10-02) docs/测试说明/流程与规范/自动化测试流程.md(参数已落地,4a4cd29)
V2/V4/V5 子系统设计/看门狗设计说明.md §7 验证记录 子系统设计/看门狗设计说明.md 变更记录(2026-10-03)
S11 堆告警 —(原复审项,已闭环 2026-10-03) 内存管理/RAM使用分析与优化方案.md(基线更新)
S9 GD5F —(第二档 #1) 问题记录/GD5F2GQ5UE_Trap_Records.md、docs/测试说明/板级测试/GD5F2GQ5UE_Test_Guide.md
日志静默观察项 docs/任务规划/登记簿.md 网络/Net层阻塞模型架构设计.md(头阻塞,历史编号)
网络重建 —(原台账项,已闭环 2026-10-02) 网络/Net层阻塞模型架构设计.md
S5/S4 —(原复审项,已闭环 2026-10-03) docs/测试说明/流程与规范/测试开发标准流程.md
模拟器差距 docs/测试说明/专项验证/模拟器差距清单.md §3 决策规则 本文为"需回真机"项的执行面

模拟器差距清单 §3 的六条"必须回真机"规则决定"哪些改动**必须在本规划内验证—— 改动触及 Drivers/BSP/ 寄存器时序 / 模拟器差距清单的硬件专属项 / 配置变更检测与回调 / 网络重建并发 / 启动段 / 内存预算, 一律在本文件找到对应批次。