Replace task/batch shorthand IDs and residual layer labels in human-facing docs with semantic phrases. Add the new cleanup policy to docs/编号规范.md and update the entry docs. Machine keys (TC/CODE-RULE/MENU-STATE/TRAP/FR), hardware designators/pins/MAC, protocol/version numbers and frozen historical evidence are intentionally kept. Docs only; no code behavior change. Verified with check_doc_links.py --all and git diff --check.
36 KiB
实机测试实施指南(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 僵尸通道——见看门狗设计说明.md2026-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-build0/0 → 一轮/serial-boot-testPASS → 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 第 1 |
| 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.1.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.5sreset 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 |
实施顺序(数据安全优先):
- 先建分类函数(
gd5f_wait_busy内检查三次 HAL 调用)——所有下游判断的基础; - 再修最危险的:
gd5f_bbt_scan失败放弃持久化(保护好副本)——数据安全优先; - 补 init 全链路检查 + 重试 1~2 次;
- 回读 B0h 校验 ECC 使能(bit4);
gd5f2gq5ue_read的 raw 契约文档化或加 ECCS 检查;- 全程在
GD5F2GQ5UE_Trap_Records.md留痕,同步GD5F2GQ5UE_Test_Guide.md。 - 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 拔线负向 ② A2net_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):
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。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 台架窗口,照做可少绕一天)
- 写入"复位后消失"的两层原因(窗口最大坑,根因均已修复,详见模块文档):
- 板端测试套件 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(FatFssync_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失败时才还有兜底意义。)
- 板端测试套件 dhara checkpoint 回滚(
- FTP 路径:lftpd 相对路径基准是根目录(
SIZE selftest.log→ 550),一律用 绝对路径/log/xxx;LIST /log实测列的是根目录内容(lftpd 怪癖)——查状态只用SIZE/RETR。 - 0 字节 STOR 的数据连接存活期仅
1050ms,短于芯片/网络层轮询粒度 → 芯片只见DISCONNECTED (chip tcp=0x00)→ accept 失败(425 或控制连接被重置);2026-10-02 实测客户端PASV→STOR间隔达 11 s(数据口 LISTEN 半开)。这是与看门狗误复位不同的另一类 问题(CH395F 数据通道);若修复后仍复现 425,转 §7.1 专项。 - 串口采集铁律:
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。 - 只用
.venv\Scripts\python.exe(PATH 里的python是 msys64);每次烧录后必须 ST-LINK 全片回读No difference found。 - 误复位会使台账 +1 条
watchdog reset:取证/修复期间台账只会增长, 属预期(清台账留到最后一次修复后的出口闭环)。 - 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 验证手段)须在一次调用内完成整段键序列 (解锁→参数→键锁)——分多次调用会超过看门狗超时、中途被复位;寄存器随复位丢失, 只能用于验证,不是修复。
- 开机良性
MISS start行(2026-10-03 上板):看门狗抽样在开机屏障等待期偶见 UI 位单抽样缺席,每 episode 记一条裸 UARTMISS 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行/复位才是真停摆。 - 导航类自动化脚本默认零 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,新固件全闭环
@testPASS 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/寄存器时序 / 模拟器差距清单的硬件专属项 / 配置变更检测与回调 / 网络重建并发 / 启动段 / 内存预算, 一律在本文件找到对应批次。