test(fw-stamp): 身份戳覆盖测试宏——头依赖 + suite/dstr 变体串
问题:__DATE__/__TIME__ 只在所属 TU 被重编时才刷新,而 app_main.c 不依赖 test/board/test_config.h → 只切套件宏的增量构建不重编它,测试镜像与产品 镜像顶着同一个 fw build: 戳,@test 的戳校验对"两种构建互换"完全失明 (2026-10-11 N-31 复测实测:两者戳同为 Oct 11 2026 14:44:11,只有 -CmpFile 回读才发现镜像不同)。 修复(App/app/app_main.c,两条防线): - #include "test_config.h" 建立头依赖 → 改 TEST_SUITE_* / ENABLE_DESTRUCTIVE_TESTS 必重编本 TU,戳必刷新(根因修复) - 戳串追加 FW_BUILD_VARIANT = " suite=<TEST_SUITE_NAME> dstr=<on|off>" → 戳内容本身区分构建,时间撞车也能分辨 实测:产品 20:36:11 suite=NONE dstr=off ↔ 测试 20:36:40 suite=CH395F dstr=on ↔ 还原宏后 20:36:55 suite=NONE dstr=off(时间戳每次都变 = 头依赖 真的生效)。@test 全闭环 PASS:戳匹配 + 回读 No difference found + 锚点 x1 + 零 [ERR]。run_boot_test.py 无需改动(戳按"标记后连续可打印 ASCII"抠取,天然吃下新后缀)。 代价:Code 196764 → 196788(+24 B),@build 0 错 0 警。 文档:自动化测试流程.md 补「戳覆盖构建配置」小节与变更记录行;失败模式 表新增一行——戳命中但 suite= 与要跑的套件不符 = 烧的是产品镜像。 门禁:mem-numbers 钩子拦下「RAM 文档快照与 .map 不一致」→ 用 check_mem_numbers.py --write 刷新生成区(ER_IROM1 213,256 → 213,396 B)。
This commit is contained in:
+32
-2
@@ -27,6 +27,9 @@
|
||||
#include "phasor.h" /* L2 相量层:phasor_init()(系数表 + 实例 + 双区交换) */
|
||||
#include "sys_log.h"
|
||||
#include "task_watchdog.h" /* task_wd_seg_report():round 8 复位不丢"进段打点"上报 */
|
||||
#include "test_config.h" /* 套件开关:身份戳的构建变体串(FW_BUILD_VARIANT)依赖本头,
|
||||
* 改 TEST_SUITE_* / ENABLE_DESTRUCTIVE_TESTS 必重编本 TU →
|
||||
* __DATE__/__TIME__ 随之刷新,见 app_main_init() 的戳注释 */
|
||||
|
||||
#define DBG_TAG "[APP]"
|
||||
#include "dbg_log.h"
|
||||
@@ -37,6 +40,26 @@
|
||||
* 放这里而非 main.c,是因为需要与其它应用级全局量同层、且日志侧更好引用。 */
|
||||
volatile uint32_t g_rcc_csr_latched;
|
||||
|
||||
/*
|
||||
* 构建变体串(2026-10-11):把"测试套件开关"编进固件身份戳,见 app_main_init() 的戳注释。
|
||||
* TEST_SUITE_NAME 由 test/board/test_config.h 定义(产品构建 = "NONE");
|
||||
* 模拟器等未纳入该头的编译单元退化为空串,不影响戳的可搜索性。
|
||||
*/
|
||||
#if defined(TEST_SUITE_NAME)
|
||||
#define FW_BUILD_SUITE " suite=" TEST_SUITE_NAME
|
||||
#else
|
||||
#define FW_BUILD_SUITE ""
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_DESTRUCTIVE_TESTS)
|
||||
#define FW_BUILD_DSTR " dstr=on"
|
||||
#else
|
||||
#define FW_BUILD_DSTR " dstr=off"
|
||||
#endif
|
||||
|
||||
/* 身份戳后缀:一段连续 ASCII,保证 .hex 里可原样搜到(run_boot_test.py 抠戳用) */
|
||||
#define FW_BUILD_VARIANT FW_BUILD_SUITE FW_BUILD_DSTR
|
||||
|
||||
/*
|
||||
* 函数功能:把 RCC_CSR 锁存值翻译为可读的复位原因串(见手册 RM0090 §6.3.22)
|
||||
* 入口参数:csr - 锁存的 RCC_CSR 值
|
||||
@@ -114,8 +137,15 @@ int app_main_init(void) {
|
||||
* 用途:@test 把"芯片里正在跑的镜像"和"刚编出来的镜像"对上号。此前开机标记
|
||||
* 全是固定串(App init OK / Network Init / ...),旧镜像也照样全中,于是
|
||||
* "反复重下载、芯片一直是旧镜像"这类事故在自动闭环里完全不可见
|
||||
* (AGENTS.md 烧录铁律记的正是它)。代价:1 条 DBG ≈ 90 B Flash + 30 B 常量。 */
|
||||
DBG_INFO("fw build: " __DATE__ " " __TIME__);
|
||||
* (AGENTS.md 烧录铁律记的正是它)。代价:1 条 DBG ≈ 90 B Flash + 30 B 常量。
|
||||
* ⚠ 戳只在**本 TU 被重编**时才刷新(增量构建),故本文件必须 #include
|
||||
* "test_config.h":否则只切套件宏(TEST_SUITE_* / ENABLE_DESTRUCTIVE_TESTS)
|
||||
* 不重编本 TU,产品镜像与测试镜像顶着同一个戳,戳校验对"两种构建互换"失明
|
||||
* (2026-10-11 实测:切宏后的测试构建与产品构建的戳一模一样,只有回读校验
|
||||
* 才发现镜像不同)。两条防线:① 头依赖 → 改宏必重编、戳必刷新;
|
||||
* ② FW_BUILD_VARIANT 把套件名与破坏性开关编进戳内容 → 即便戳时间相同,
|
||||
* 产品串与测试串也不同。代价:约 20 B 常量。 */
|
||||
DBG_INFO("fw build: " __DATE__ " " __TIME__ FW_BUILD_VARIANT);
|
||||
|
||||
/* 0) 复位原因上报(锁存值由 Src/main.c 在 HAL_Init() 后立即采得)。
|
||||
* 放在日志引擎之后、业务初始化之前:任何一次非上电复位都值得在
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 |
|
||||
|---|---|---|---|
|
||||
| ER_IROM1 | 213,256 B (208.26 KB) | 1,048,576 B (1024 KB) | 20.3% |
|
||||
| ER_IROM1 | 213,396 B (208.39 KB) | 1,048,576 B (1024 KB) | 20.4% |
|
||||
| RW_IRAM1 | 94,768 B (92.55 KB) | 131,072 B (128 KB) | 72.3% |
|
||||
| RW_CCM | 49,184 B (48.03 KB) | 65,280 B (64 KB) | 75.3% |
|
||||
| **Flash (ROM 合计)** | **214,224 B (209.2 KB)** | 1,048,576 B (1024 KB) | **20.4%** |
|
||||
| **Flash (ROM 合计)** | **214,364 B (209.3 KB)** | 1,048,576 B (1024 KB) | **20.4%** |
|
||||
<!-- MEM_SNAPSHOT:END -->
|
||||
|
||||
各区的容量与常量来源:
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
- `1` = FAIL:存在缺失标记(`[MISS]`)或违规模式(`[HIT]`)
|
||||
- `2` = 日志文件不可读
|
||||
- **固件身份戳(2026-10-10 加)**:`App/app/app_main.c` 开机打印 `fw build: <__DATE__> <__TIME__>`(编译期展开成一段**连续 ASCII**,`.hex` 里可原样搜到)。`run_boot_test.py` 从**待烧的 hex** 里抠出该串并追加成 Required 标记 → 芯片里跑的必须就是这份构建。反例实测:把 hex 里的戳改成 `Oct 9 2026 01:00:00` 再跑,其余标记全中但 `[MISS] fw build: Oct 9 2026 01:00:00` → FAIL——这正是过去"反复重下载、芯片一直是旧镜像"在闭环里**完全隐形**的场景。`--no-fw-check` 关闭;hex 里没有该串(旧构建)只 WARN 跳过、不假判。
|
||||
- **戳覆盖构建配置(2026-10-11 加)**:`__DATE__`/`__TIME__` 只在**该 TU 被重编**时才刷新——增量构建下只切 `test/board/test_config.h` 的套件宏**不会**重编 `app_main.c`,于是产品镜像与测试镜像顶着同一个戳(实测:切宏后的测试构建与产品构建戳同为 `Oct 11 2026 14:44:11`,只有回读校验才发现镜像不同)。两条防线:① `app_main.c` `#include "test_config.h"` 建立头依赖 → 改宏必重编本 TU、戳必刷新;② 戳串追加构建变体 ` suite=<TEST_SUITE_NAME> dstr=<on|off>`(`FW_BUILD_VARIANT`)→ 产品 = `suite=NONE dstr=off`、CH395F 测试 = `suite=CH395F dstr=on`,**即便戳时间相同也能一眼分辨**。实测:切宏前后 `Oct 11 2026 20:36:11 suite=NONE dstr=off` → `Oct 11 2026 20:36:40 suite=CH395F dstr=on`;`@test` 闭环对新格式匹配通过(Required 行就是完整戳串,脚本按"标记后连续可打印 ASCII"抠取,无需改脚本)。
|
||||
- **烧录回读(2026-10-10 加)**:采集结束后自动跑 `stlink_flash.py cmp`(= `-CmpFile` 全片逐字节比对),判定按**行首** `No difference found`(进度条与帮助文本里也含该短语,故不能全文匹配);不一致 → `RUN_TEST_ERROR: flash readback mismatch` + 退出码 2(本次运行作废:日志是"某个未知镜像"跑出来的)。⚠ **顺序不能颠倒**——`-CmpFile` 连接时会复位目标(实测板子当场重打一遍开机日志)。`--no-verify-flash` 关闭。
|
||||
- **启动锚点计数(2026-10-10 加)**:`reset cause:` 在一次采集里出现 **0 次**(采集没从复位开始,标记全中也可能只是上一轮残留)或 **>1 次**(板子中途自己复位:IWDG 咬狗 / Fault 后 `NVIC_SystemReset` / 掉电重启)→ FAIL;`--max-boot-count N` 放宽(跑故意复位的套件时用),`--expect-reset-cause 'software;power-on'` 对第一个锚点做原因白名单(SWD 软复位实测为 `software (NVIC_SystemReset)`,CSR=0x14000000)。
|
||||
- **判定可读性 + 时序(2026-10-10 第三批)**:
|
||||
@@ -81,7 +82,7 @@
|
||||
| daemon 无响应 / 自动拉起失败 | 查 `.dsh/skills/serial-assistant/scripts/state/daemon.log`;`serial_tool.py restart` 重建 daemon;检查 COM 是否被其他程序占用 |
|
||||
| 抓到日志缺 `Network Init` | `-SettleMs` 太小(boot 任务间有 ~3-4s 自然静默间隙),用默认 8000ms;2026-10-10 起首字节到达前不启动静默计时(`--first-wait-ms` 默认 12000),首行晚到不再被提前收尾 |
|
||||
| 烧录后无数据(`no_data`) | 2026-10-10 起 `no_data` = **本次等待期间零字节**(旧实现比 daemon 累计字节,常驻 daemon 下永不触发,只能靠"日志里没有 [RX] 行"兜底);开串口 + clear 必须在烧录**前**完成(`run_boot_test.py` 已保证);核对 COM 号、板子供电,看 `state/serial.log` 有无 SYS 异常行 |
|
||||
| `[MISS] fw build: <戳>`(其余标记全中) | 芯片里跑的不是这份构建:烧录没落地 / 烧错文件 / hex 未重编译。处置:`stlink_flash.py flash <hex>` 重烧并看 `READBACK: OK`;确认 `@build` 已重跑(戳取自编译时刻) |
|
||||
| `[MISS] fw build: <戳>`(其余标记全中) | 芯片里跑的不是这份构建:烧录没落地 / 烧错文件 / hex 未重编译。处置:`stlink_flash.py flash <hex>` 重烧并看 `READBACK: OK`;确认 `@build` 已重跑(戳取自编译时刻)。⚠ 若戳**命中**但里面的 `suite=` 与本次要跑的套件不符(想跑 CH395F 却显示 `suite=NONE dstr=off`)→ 烧的是产品镜像,`@build` 不会替你切宏(→ `board-test-suite` skill) |
|
||||
| `READBACK: FAIL` / `flash readback mismatch` | 芯片内容与 hex 逐字节不一致(写入未落地、选项字节/RDP 异常、烧错目标)。处置:按 `docs/测试说明/ST-LINK_CLI_复位使用说明.md` §7 排查;`stlink_flash.py flash` 重烧后闭环会自动复核 |
|
||||
| `boot banner x2 > allowed 1` | 采集窗口内板子自己复位过(IWDG 咬狗 / Fault 后 `NVIC_SystemReset` / 掉电重启)。处置:看判定里逐条打印的 `reset cause:` 定位;跑**故意复位**的套件用 `--max-boot-count N` 放宽 |
|
||||
| `marker order violated` | 标记全中但**先后顺序颠倒** = 某步在另一步之后补做(就绪屏障失序 / 失败重试补跑)。处置:看判定打印的各标记首次行号定位;`--require-order` 只在需要强约束时启用 |
|
||||
@@ -123,3 +124,4 @@
|
||||
| 2026-10-10 | 采集链可靠性修复(serial_tool 第一批):`wait-idle` 的 `no_data` 改按本次增量判(旧实现常驻 daemon 下永不触发,"烧录后无数据"这一失败模式实际测不出来)+ 首字节门 `--first-timeout-ms`(`run_boot_test.py` 新增 `--first-wait-ms`,默认 12000);`log --grep` 改先过滤后截尾(旧顺序导致 marker 必然查不到)并加 `-i`/`--regex`;`wait-idle` CLI 超时自动推导,不再需手工 `--timeout`;HTTP 口独占绑定修正(旧选项名/数值均无效,实际未生效) |
|
||||
| 2026-10-10 | **第二批:闭环补上"烧进去的是哪份镜像"与"跑的过程中有没有偷偷复位"**:① 固件身份戳——`App/app/app_main.c` 开机打印 `fw build: <__DATE__> <__TIME__>`,`run_boot_test.py` 从待烧 hex 里抠出该串追加成 Required 标记(旧镜像/错镜像此前所有固定标记照样全中);② 烧录回读校验进闭环——`reset.bat` 新增 `cmp` 子命令(`-CmpFile` 全片比对),采集后自动跑,不一致 exit 2;实测 `-CmpFile` 连接时会复位目标,故必须排在采集之后;③ 启动锚点计数——`verify_log.py` 新增 `--max-boot-count`/`--boot-marker`/`--expect-reset-cause`,`reset cause:` 出现 0 次或 >1 次判 FAIL;④ 修 `--flash-cmd` 带参数时预检必 missing、以及 `cmd /c` 对含空格路径(本仓库目录名 `DTU Code`)的引号解析把回读假判成"芯片不一致"(改为直接 CreateProcess 跑 .bat)。真机验证:完整烧录闭环 PASS(含戳匹配 + `No difference found` + 锚点 x1);伪造戳 → 其余标记全中仍 FAIL |
|
||||
| 2026-10-10 | **第三批:判定可读性 + 时序 + 采集卫生**:① Forbidden 命中逐行打印行号+内容(旧只报条数);② boot_log 每行加 `[+Nms]` daemon 毫秒时基(`--raw-log` 关闭),据此量开机耗时(实测 `net init: OK` = **+537ms**;`--max-boot-ms` 默认 0 只报告);③ `--require-order` 标记先后顺序校验(全中但失序此前不可见);④ `--json` 结构化判定(退出码不变);⑤ `--keep-out`(默认 20)清理 out 目录旧产物(本机曾堆到 31+7 份);⑥ daemon 侧:无换行残行 64KB 封顶 + open/close/clear 会话边界丢弃残行 + 截头按行边界对齐(旧实现截出来的首行是半行)+ `open` 不带 `--dtr/--rts` 时保持现状、显式给则现场应用;⑦ `ports` 回 `configured_present`/`hint`,`status` 回 `rx_age_ms`。selftest 23/23、verify_log 离线回归 17/17、真机闭环 PASS |
|
||||
| 2026-10-11 | **身份戳覆盖测试宏**(N-31 复测时发现):切 `test/board/test_config.h` 的套件宏后 `app_main.c` 未被重编,测试镜像与产品镜像顶着同一个 `fw build:` 戳 → 戳校验对"两种构建互换"失明(当时只有 `-CmpFile` 回读发现镜像不同)。修复:`app_main.c` `#include "test_config.h"`(头依赖 → 改宏必重编、戳必刷新)+ 戳串追加 ` suite=<TEST_SUITE_NAME> dstr=<on|off>`(`FW_BUILD_VARIANT`,戳内容本身区分构建)。实测:`20:36:11 suite=NONE dstr=off` ↔ `20:36:40 suite=CH395F dstr=on`;`@test` 闭环对新格式 PASS(脚本无需改动,戳按"标记后连续可打印 ASCII"抠取)。失败模式表补一行:戳命中但 `suite=` 与要跑的套件不符 = 烧了产品镜像 |
|
||||
|
||||
Reference in New Issue
Block a user