fix(看门狗): 停喂判定按位分账——复合串窗不再误停喂(2026-10-25 大窗判读 F2)
道F task-23。动因 = 2026-10-25 台架大窗判读:9 条 stall 行里 16:24/16:28 两起为 复合判据误升级——旧逻辑喂狗前置 = 全必需位同窗跑过,任一窗缺任一位即断喂, 复合连续 8 窗申报 stall;UI 一窗缺、IEC104 下一窗缺、再下窗 FTP 缺这类互不相干 的亚秒抖动被串成 800ms 假停摆(mg=877/918,两事件中无任何任务达单任务容忍线)。 16:39 那起才是真 UI 单任务串(~1.2s,bit4 到 8 连击——受害者面修复 = F3 票)。 【改造】stall/停喂唯一判据 = 任一必需位自己的连续缺席窗数(s_miss_bits[] 分账, +9B BSS):同一任务 8 窗(800ms)没跑过 → 申报 + 停喂 → IWDG 自然溢出 (IWDGRSTF 权威归因不变);所有位连击 <8 的缺席窗照常喂狗;位出现即该位清零, 掩码外位不入账(wd_required_mask 随构建变化自动跟随、不留旧账)。`MISS start` 保留为复合边沿诊断行(不再代表停喂,判读口径以设计说明 §6.1/§11.1 为准)。 代价:真停摆从挂死到复位总时长最坏 +约 0.7s(≈2.5s 最坏 / ≈3.3s 标称),V2 台架判据「声明 stall 后 ≤2.5s 复位」不受影响(申报时点未变,停喂起点即申报)。 【测试】test/host/watchdog 断言按新判据改写 5 处(004/005/006/008/013:缺席窗 照常喂),新增 TC-WATCHDOG-017(20 窗 UI/NET 交替缺席:零 stall + 20 窗持续喂—— 旧复合判据必在第 8 窗误报,回归钉)与 TC-WATCHDOG-018(同位 8/9/10 连击必报停 + 停喂;恢复窗喂、账本重启,第二段亚容忍串重新从零计)。47/47 全绿。 【文档】看门狗设计说明 §0/§2.2(停喂链加「v1.5 起字面成立」注)/§3.2/§5.3/ §6.1/§11.1/§11.6 大窗补记 + 变更记录;看门狗单元测试说明(用例表 + 47 断言 + 变更记录,顺带修正 TC-WATCHDOG-003 行 10-12 后未跟的 `0x37`→`0x1F7`)。 【越界申报】① docs/测试说明/看门狗单元测试说明.md——票面写域外,但该文档 §4 自要求「行为变更须同步改单测与本文」,执行性同步;② 根目录 .cubemx_survival_baseline.json——cubemx-survival 钩子对 task_watchdog.c(watchdog- module 审计项 = 全文哈希基线)明示「有意改动请 --write 重建基线」,重建后 31 通过 / 0 失败。均非他人并行改动面。 【门禁】@build 0 错误 0 警告(Code=224768 RO=24640 RW=1328 ZI=135096)。 --all-files 43 绿;红仅 mem-numbers/mem-budget = 台账漂移(vs 2026-10-25 行: Code +80B / ER_IROM1 249328→249408 / ZI +8B(s_miss_bits)/ IRAM1 约 +8B,待回填)。 id-check 增量拦截通过(首跑拦下变更记录里 TC-003/TC-004 裸形,已改全 ID)、 test-coverage 通过、cubemx-survival 通过。 gate: only mem-budget/mem-numbers pending lead ledger backfill 【台架】等你排窗两轮(各 ≥30min):单独 relisten_stress :7003 / 探针轮。判据按 v1.1 口径 = stall 行零为硬判据;预期本修复后 16:24/16:28 型复合误升级归零, MISS start 仍会出现(诊断量);16:39 型 UI 真单串在 F3 落地前仍可能照报—— 那属于「判据抓对了人」,请连同 mask 一起记给我。
This commit is contained in:
+75
-22
@@ -21,6 +21,14 @@
|
||||
* round 8a 定位到 console 通道后,需钉死是 TIM7(HAL tick)
|
||||
* 还是 SysTick(FreeRTOS tick) 停摆(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
*
|
||||
* v1.5 2026-10-25 DTU 项目组 **停喂判定按位分账**(大窗判读 F2):stall/停喂的唯一
|
||||
* 判据从"复合连续缺席窗数"改为"任一必需位的连续缺席窗数"
|
||||
* (同一任务 800ms 没跑过才停喂申报复位);复合缺席窗仍照打
|
||||
* "MISS start"(降级为诊断行,不再代表停喂)——根治"本窗缺 A、
|
||||
* 下窗缺 B"式互不相干亚秒抖动被累计成复合 800ms 假停摆并停喂
|
||||
* (2026-10-25 大窗 16:24/16:28 两起实证;真单任务停摆如
|
||||
* 16:39 型 UI 单串 800ms+ 仍必报)。判据正文同步
|
||||
* docs/子系统设计/看门狗设计说明.md §4;主机单测 TC-WATCHDOG-017/018
|
||||
* v1.4 2026-10-02 王建锋 **P0 修复**:wd_platform_start() 补写 IWDG 的
|
||||
* PR/RLR(= WD_IWDG_PRESCALER / WD_IWDG_RELOAD,即设计值
|
||||
* /64 + 1249 → 1701~2498ms)。此前为"延后启动"删掉
|
||||
@@ -111,7 +119,15 @@
|
||||
* 单字读-改-写在 Cortex-M4 上天然原子,无需临界区(§3.3) */
|
||||
static volatile uint32_t s_hb_bits;
|
||||
|
||||
static uint32_t s_miss_streak; /* 连续未达标轮数(达到容忍上限即停喂) */
|
||||
static uint32_t s_miss_streak; /* 复合缺席连续窗数(饱和封顶)——自 v1.5 起仅作
|
||||
* "MISS start" 诊断行的 episode 边沿检测,不再
|
||||
* 参与停喂判定(停喂按 s_miss_bits 逐位分账) */
|
||||
/* 按位分账(2026-10-25 大窗判读 F2):每个必需位"连续缺席抽样窗数"。位出现即清零,
|
||||
* 缺席饱和到容忍值。停喂/stall 声明的唯一判据 = 任一必需位连击 >= WD_MISS_TOLERANCE,
|
||||
* 即"同一个任务连续 8 个窗没跑过"。多任务各缺各的(本窗 A、下窗 B)各自清零,
|
||||
* 不再串成复合 800ms 停喂——旧复合判据把互不相干的亚秒抖动累计成真停摆申报
|
||||
* (2026-10-25 大窗 16:24/16:28 两起实证;判据正文 docs/子系统设计/看门狗设计说明.md §4)。 */
|
||||
static uint8_t s_miss_bits[TASK_WD_ID_MAX];
|
||||
static uint32_t s_miss_total; /* 累计未达标轮数(观测用) */
|
||||
static uint32_t s_last_mask; /* 上次抽样到的位图(定位卡死任务用) */
|
||||
static uint32_t s_started; /* 1 = IWDG 已启动 */
|
||||
@@ -439,15 +455,22 @@ void task_wd_start(void) {
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:仅 defaultTask 主循环调用(唯一喂狗者);内部节流到 100ms 一次
|
||||
* 函数说明:取走并清零位图 → 与必需掩码比较:
|
||||
* 全中 → 清零缺失计数并喂狗;
|
||||
* 有缺 → 累计;连续达到 WD_MISS_TOLERANCE 次后**停止喂狗**,
|
||||
* 让 IWDG 自然溢出复位(这样 RCC_CSR 记录的是权威的
|
||||
* IWDGRSTF,而不是 NVIC_SystemReset 留下的 SFTRSTF —— §5.3)。
|
||||
* 函数说明:取走并清零位图 → 与必需掩码比较(v1.5 起按位分账,判据推导见设计说明 §4):
|
||||
* 全中 → 清零复合边沿与证据门闩并喂狗;
|
||||
* 有缺但每位连击 < 容忍 → **继续喂狗**(单窗/亚秒级缺席是负载抖动,不是
|
||||
* 停摆;只按位记账并在复合边沿打一条诊断行);
|
||||
* 任一位连击 >= WD_MISS_TOLERANCE(同一任务 800ms 没跑过)= 真停摆 →
|
||||
* 申报 [WD] task stall 并**停止喂狗**,让 IWDG 自然溢出复位
|
||||
* (RCC_CSR 记权威 IWDGRSTF 而非 SFTRSTF —— §5.3)。
|
||||
* ⚠ 判定故意不用"复合连续缺席窗数"当停喂门槛:那会把"本窗缺 A、下窗缺
|
||||
* B"这类各自远低于容忍度的抖动串成假停摆(2026-10-25 大窗实证)。
|
||||
*/
|
||||
void task_wd_poll(void) {
|
||||
uint32_t now;
|
||||
uint32_t snapshot;
|
||||
uint32_t mask_now;
|
||||
uint32_t worst;
|
||||
uint32_t i;
|
||||
|
||||
if (s_started == 0u) {
|
||||
return; /* 未启动:不做任何判定 */
|
||||
@@ -469,37 +492,58 @@ void task_wd_poll(void) {
|
||||
s_snap_hist_idx = (s_snap_hist_idx + 1u) % WD_SNAP_HIST_DEPTH;
|
||||
#endif
|
||||
|
||||
if (snapshot == wd_required_mask()) {
|
||||
mask_now = wd_required_mask();
|
||||
|
||||
/* 按位分账:位出现→该位清零;缺席→+1(饱和封顶)。掩码外的位不入账
|
||||
* (监视范围随构建配置变化时,账本自动跟随,不会留旧账)。 */
|
||||
worst = 0u;
|
||||
for (i = 0u; i < (uint32_t)TASK_WD_ID_MAX; i++) {
|
||||
if ((mask_now & (1u << i)) == 0u) {
|
||||
s_miss_bits[i] = 0u;
|
||||
continue;
|
||||
}
|
||||
if ((snapshot & (1u << i)) != 0u) {
|
||||
s_miss_bits[i] = 0u;
|
||||
} else if (s_miss_bits[i] < (uint8_t)WD_MISS_TOLERANCE) {
|
||||
s_miss_bits[i]++;
|
||||
}
|
||||
if ((uint32_t)s_miss_bits[i] > worst) {
|
||||
worst = (uint32_t)s_miss_bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (snapshot == mask_now) {
|
||||
s_miss_streak = 0u;
|
||||
s_stall_evidenced = 0u; /* 恢复:允许下次停摆再落一次证据 */
|
||||
wd_platform_feed(); /* 全员健康:喂狗 */
|
||||
return;
|
||||
}
|
||||
|
||||
/* 有任务缺席:累计(不喂狗) */
|
||||
/* 本窗有缺席位(复合缺席):记总数;**复合边沿**(上一窗全中)只打一条
|
||||
* 诊断标记行——v1.5 起该行不再意味着停喂,停喂只看按位连击 */
|
||||
s_miss_total++;
|
||||
if (s_miss_streak < WD_MISS_TOLERANCE) {
|
||||
s_miss_streak++;
|
||||
}
|
||||
|
||||
if (s_miss_streak == 0u) {
|
||||
/* 复合缺席 episode 起点裸 UART 标记(每 episode 一条):证明 poller 仍在
|
||||
* 抽样、并给出**首次**缺席位图——定位谁先停跑(round 7 出身)。⚠ v1.5 起
|
||||
* 本行为**纯诊断**——复合边沿 ≠ 停喂,stall 判定权在下面的按位连击。 */
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
if (s_miss_streak == 1u) {
|
||||
/* 停喂起点裸 UART 标记(每次缺席 episode 仅一条):证明 poller 仍在
|
||||
* 抽样、并给出**首次**缺席的位图——定位是哪一任务先停摆(round 7) */
|
||||
char m[96];
|
||||
int mn = snprintf(m, sizeof(m),
|
||||
"MISS start snap=0x%08X req=0x%08X t=%lu\r\n",
|
||||
(unsigned)snapshot, (unsigned)wd_required_mask(),
|
||||
(unsigned)snapshot, (unsigned)mask_now,
|
||||
(unsigned long)now);
|
||||
if (mn > 0) {
|
||||
dbg_console_write_raw(m, (unsigned)strlen(m));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (s_miss_streak < WD_MISS_TOLERANCE) {
|
||||
s_miss_streak++; /* 饱和封顶;仅供复合 episode 边沿 */
|
||||
}
|
||||
|
||||
if (s_miss_streak >= WD_MISS_TOLERANCE) {
|
||||
/* 连续 800ms 有任务未动过 → 已停喂 → 约 2.5s 后 IWDG 复位。
|
||||
* 最后遗言走裸 UART(不依赖内核、不遍历指针、有界长度)。 */
|
||||
if (worst >= WD_MISS_TOLERANCE) {
|
||||
/* 同一位连续缺席 8 窗(同一任务 800ms 没跑过)= 真停摆 → 停喂 → 约 2.5s
|
||||
* 后 IWDG 复位。最后遗言走裸 UART(不依赖内核、不遍历指针、有界长度)。 */
|
||||
DBG_ERROR("[WD] task stall: mask=0x%08X req=0x%08X miss=%lu",
|
||||
(unsigned)snapshot,
|
||||
(unsigned)wd_required_mask(),
|
||||
@@ -507,8 +551,8 @@ void task_wd_poll(void) {
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
/* round 7 取证:把现场落 /log/wdev.log(掉电不丢)。
|
||||
* 串口 stall 行经僵尸通道可能丢失,故此处独立留盘上证据。
|
||||
* ⚠ 本 episode 只写**一次**(s_stall_evidenced 门闩;注意 s_miss_streak
|
||||
* 达上限后会**饱和**在容忍值,不能用它判"首次"):证据落盘经
|
||||
* ⚠ 本 episode 只写**一次**(s_stall_evidenced 门闩;注意 worst 连击达
|
||||
* 上限后会**饱和**在容忍值,不能用它判"首次"):证据落盘经
|
||||
* sys_log_evidence() 走 NAND 写 → FTL 路径调 nand_ftl_wd_feed()
|
||||
* **直接喂狗**。若每轮 100ms 都写,等于在"停喂"窗口里持续喂狗 →
|
||||
* IWDG 永不溢出 → 停喂语义被证据机制自身抵消(2026-10-03 台架 V2
|
||||
@@ -520,6 +564,11 @@ void task_wd_poll(void) {
|
||||
wd_stall_evidence(snapshot);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* 有缺席位、但每一位连击都未到容忍值(含"本窗缺 A、下窗缺 B"的交替型):
|
||||
* 照常喂狗——多任务各自亚秒抖动不再串成复合停喂(按位分账收口的正身,
|
||||
* 2026-10-25 大窗 16:24/16:28 型事件的根治)。 */
|
||||
wd_platform_feed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,8 +773,12 @@ void nand_ftl_wd_feed(void) {
|
||||
*/
|
||||
#ifdef TASK_WATCHDOG_UNIT_TEST
|
||||
void task_wd_test_reset(void) {
|
||||
uint32_t i;
|
||||
s_hb_bits = 0u;
|
||||
s_miss_streak = 0u;
|
||||
for (i = 0u; i < (uint32_t)TASK_WD_ID_MAX; i++) {
|
||||
s_miss_bits[i] = 0u; /* 按位账本一并清零(v1.5 判定输入) */
|
||||
}
|
||||
s_miss_total = 0u;
|
||||
s_last_mask = 0u;
|
||||
s_started = 0u;
|
||||
|
||||
Reference in New Issue
Block a user