看门狗定时器:系统跑飞时的最后一根保险绳
更新时间:2026-09-24。本文是「集成电路大类 · 芯片设计」入门层(beginner) 数字逻辑基础的一篇正文。再鲁棒的状态机也可能因强干扰、软件死循环、跨时钟域错误彻底卡死,而且卡死现场往往没有任何人在场按复位键。这一篇讲一个朴素但救命的部件——看门狗定时器(Watchdog Timer,WDT):你不按时报到,它就替你重启。
本文要回答的问题
- 看门狗怎么知道程序「跑飞」了?为什么软件必须定期「喂狗」?
- 为什么看门狗必须用独立时钟?挂在主时钟上会出什么事?
- 喂狗应该放在程序的什么位置?为什么「在中断里无脑喂」等于没装看门狗?
- 普通看门狗和「窗口看门狗」差在哪?为什么喂太早也是错?
- 超时后是直接复位还是先中断?两阶段响应怎么做?
- 上电复位 POR、欠压复位 BOR 和看门狗复位是什么关系?
一、一个倒数的计数器:按时清零就活,归零就咬
看门狗定时器的本体简单到出人意料:它就是一个从装载值往 0 倒数的计数器。正常运行时,软件在主循环的健康检查点执行一次「喂狗」(kick,写特定寄存器或一段口令序列),把计数器重新装回初值;只要两次喂狗的间隔小于超时时间,计数器永远到不了 0。一旦软件死循环、卡在某个等待、跑飞到非法地址取指,没人再来喂狗,计数器一路倒数到 0,WDT 就输出复位请求(或先产生不可屏蔽中断),把芯片拉回复位后的已知状态重新启动。
用十几行 Verilog 就能写出它的核心:
module watchdog #(parameter W=24)(
input logic clk, rst_n, // 通常是独立低速时钟(见第二节)
input logic kick, // 喂狗脉冲(来自软件写寄存器)
output logic wdt_rst // 超时复位请求
);
logic [W-1:0] cnt;
always_ff @(posedge clk)
if (!rst_n || kick) cnt <= '1; // 复位或喂狗:重装
else if (cnt!=0) cnt <= cnt - 1; // 否则继续倒数
assign wdt_rst = (cnt==0); // 到 0:请求复位
endmodule下面这张时序示意图画出了两条命运分叉的路:上半条是健康系统,每次计数器还没见底就被一次 kick 拉回满值;下半条是程序在星号处跑飞,kick 从此消失,计数器归零、复位脉冲落下。
读图时抓住一个要点:看门狗检测的从来不是「具体出了什么错」,它分不清你是空指针、死锁还是栈溢出,它只问一个问题——系统还在按预期节奏推进吗? 这在可靠性术语里叫活性(liveness)检测。这和安全 FSM 给等待态配超时退出是同一种思想,区别只是粒度:FSM 超时保护的是一个状态,看门狗保护的是整个系统。
超时时间由装载值和时钟频率决定,比如 24 位计数器在 32.768kHz 时钟下,最大超时约为 2^24 / 32768 = 512 秒。实际产品会按任务周期选一个中间值:常见做法是主循环每几十毫秒到几百毫秒检查一次,超时设在 1~2 秒,既给任务抖动留裕量,又不让用户等太久才恢复。
二、铁律:看门狗必须跑在独立时钟上
看门狗有一条几乎不容商量的设计铁律:它的时钟不能来自被监控系统。
设想把 WDT 挂在主控自己的主时钟上。一旦主时钟停振——晶振损坏、主控 PLL 失锁,或者软件配置时钟寄存器时手滑把时钟门关掉了——整个系统冻住的同一瞬间,看门狗的计数器也一起停摆,永远数不到 0,复位永远不会发生。保险绳和要保险的人在同一棵树上,树倒了一起摔。
所以真正承担安全职责的 WDT 都使用独立振荡器:最常见的是片内 32kHz 上下的低速 RC 振荡器,或外部 32.768kHz 晶振,它们与主时钟、主电源处于不同的故障域。主时钟跑几百 MHz 还是停振,都不影响这只低速的狗在角落里默默倒数。这类独立看门狗在 MCU 手册里常标为 IWDG(Independent WDT)。代价也要知道:片内 RC 精度差,频率偏差可能有百分之几甚至十几,温度变化时还会漂移,所以超时窗口必须按最差精度留足裕量,不能卡着边界设计。
这也是芯片时钟/复位拓扑里一条固定安排的由来:看门狗、实时时钟 RTC、上电与欠压检测电路,永远跑在「总是使能」的低速域,详见时钟与复位入门。它底层的道理可以浓缩成一句话——监控者不能和被监控者共因失效,这句话第六节还会再出现一次。
比片内看门狗更极端的做法是片外看门狗芯片。在功能安全等级较高的板级系统里,看门狗根本不放进主控芯片,而是用一颗独立的监控芯片(集成在电源管理芯片 PMIC 里,或单独的电压监控/看门狗器件)通过一根 GPIO 接收主控的脉冲,再用另一根线驱动主控的复位脚。这样即使主控整片硅片失效、内核电源塌陷,看门狗依然活着。车规系统里常见的「问答式看门狗」还更进一步:软件不能自由喂狗,必须回答监控芯片抛出的伪随机问题(按约定算法算出下一个口令),简单地定时翻转 GPIO 骗不过去,跑飞程序碰巧喂对的概率接近于零。
无论片内还是片外,看门狗寄存器还普遍要求口令序列而不是随意一写:必须按顺序写入两个魔数(如先写 0x5555 再写 0xAAAA)才认作一次 kick,单次写无效。这个设计针对的是跑飞程序胡乱写总线的情形——随机翻转的程序几乎不可能恰好按正确顺序写对两个数。
三、喂狗策略:在哪里喂,决定了狗是不是真狗
硬件只负责倒数,「这只狗有没有用」其实大半是软件设计问题。工程上反复验证过的喂狗策略有三条。
第一,只在主循环的健康检查点喂。走到 kick 这一步之前,先确认关键任务确实在推进:消息队列在流动、传感器周期内有新数据、控制状态机在前进、看门狗自己覆盖的各个任务都上报了「我还活着」。确认全部为真才喂。这种写法让喂狗动作成为一份「体检报告」,而不是一口白给的饭。
第二,绝不要在硬件定时器中断里无条件喂狗。这是实际项目里最常见的「看门狗形同虚设」:主程序已经死循环,但硬件定时器中断照常每 10ms 触发一次,中断服务程序里一句 kick 就让计数器永远满着——主程序死了三年,狗一次都没咬过。定时器中断能活着,只证明中断控制器和那一个定时器活着,证明不了任何业务逻辑活着。
第三,喂狗间隔与超时之间留两级裕量。喂得太接近超时边界,一次正常的任务卡顿(闪存擦写、总线重试)就可能造成误复位;喂得太勤,又掩盖早期变慢。经验区间是正常周期只用掉超时窗口的 1/2 到 3/4。
| 喂狗位置 | 能检测主循环死循环吗 | 能检测中断/时钟故障吗 | 评价 |
|---|---|---|---|
| 主循环固定位置无条件 kick | 能 | 部分 | 及格,但查不出「假活」 |
| 主循环健康检查全部通过后 kick | 能 | 较好 | 推荐写法 |
| 定时器中断里无条件 kick | 不能 | 不能 | 等同关闭看门狗 |
| 多个任务各自「投票」,集齐才 kick | 能 | 好 | 多任务系统的进阶做法 |
多任务系统常用最后一种:给每个关键任务一个独立的监测标志或小看门狗,主循环确认所有标志在本周期都被更新过,才统一喂狗。任何一个任务饿死,狗都能咬到。
四、窗口看门狗:喂太晚是错,喂太早也是错
普通看门狗只防「喂太晚」。但有一类故障恰好相反——系统喂狗喂得太快:时钟频率因为配置错误飙高、程序跑飞后掉进一个疯狂写看门狗寄存器的小循环、时序被打断后漏掉了大段正常流程。计数器每次都在很大的值上被重装,普通看门狗永远不超时。
窗口看门狗(Window WDT) 为此设了一个合法区间,喂狗只能在「窗口」内发生:
- 计数器值高于窗口上沿时喂狗 = 喂太早,判故障,立即复位;
- 计数器值低于窗口下沿(接近 0)还没喂 = 喂太晚,判超时,复位;
- 只有上下沿之间的中间窗口内 kick 才被接受。
| 一个倒数周期内的位置 | 此时 kick 的判定 |
|---|---|
| 上沿以上(计数器刚重装,数值很大) | 喂太早:判时钟异常 / 狂喂,立即复位 |
| 上下沿之间的合法窗口 | 接受,计数器重装 |
| 下沿以下(计数器快归零) | 喂太晚:超时复位 |
这张「三段表」把一个倒数周期从上到下切开来看:窗口看门狗实际上同时约束了软件执行时间的上限和下限。代价是它对软件实时性要求更硬,任务抖动稍大就可能在上沿附近误触发,所以窗口上下沿同样要留裕量,通常只在安全相关的 ECU(电子控制单元)上强制使用。
五、超时之后:先 NMI 抢救,再硬复位的两阶段响应
超时信号一旦产生,最粗暴的做法是立刻拉全局复位。但很多系统希望在死掉之前做最后两件事:把故障现场(程序计数器、栈、关键状态、复位原因)写进保持寄存器或非易失存储器,供下次开机后诊断;以及执行安全关断——电机停转、阀门归位、切断高压输出。硬复位会在微秒级夺走所有逻辑,这些动作一件都来不及。
于是有了两阶段看门狗:第一次超时先产生一个不可屏蔽中断(NMI,普通中断屏蔽位挡不住它),把 CPU 强行拽进故障处理程序;软件在一个很短的宽限期内抢救现场、安全关断,然后主动请求复位。如果软件连 NMI 都响应不了(CPU 时钟已坏、死在更高优先级异常里),宽限期结束后第二阶段硬复位照常落下——抢救机会是恩赐,不是义务。
一次典型的「跑飞后自动恢复」完整流程如下:
调试场景下看门狗还要学会「暂停」。几乎所有现代 MCU 都有一个配置:内核被调试器 halt(停住)单步或命中断点时,看门狗计数器自动冻结,否则你盯着寄存器看两分钟,狗已经把芯片复位十几次,断点上下文全部丢失。但要注意这是调试口的特权,量产固件烧录后调试口通常关闭,看门狗恢复全力运行——开发阶段「一切正常」而现场频繁复位,有一部分案例就是开发时狗一直被冻结、问题从未暴露。另外,启动阶段要尽早重新使能看门狗: bootloader 到应用程序跳转的过程里存在一段「无人喂狗」的真空期,长固件升级(擦写大片 Flash 可能耗时数秒)期间要么周期性暂停-喂狗,要么把超时临时调大,升级完成再调回,否则一次正常的远程升级就会被自己的狗咬断。
图里最后一格常被初学者忽略,却极其关键:复位原因必须被记录。复位控制器一般会提供一个复位原因寄存器,用不同位标记本次启动是 POR、BOR、WDT 还是软件请求,这个标志位在复位过程中由独立电源保持,开机后软件读取并落盘。现场维修时看到「一个月内 200 次 WDT 复位」,和看到「从未复位但用户投诉死机」,是完全不同的两条排查路径。更讲究的系统还会统计 WDT 复位频次,短时间反复复位就进入安全降级模式(限制功率、点亮故障灯),避免带病运行酿成事故。
六、复位家族:POR、BOR 与 WDT 各司其职
看门狗不是唯一的自动复位源。一颗 MCU 或 SoC 通常携带一整套复位来源,它们全部汇入复位控制器,经过复位域跨越与同步释放处理后再分发到全芯片:
| 复位源 | 触发条件 | 守护的场景 |
|---|---|---|
| POR 上电复位 | 电源刚上电、跨过工作门限 | 保证开机时寄存器从已知态起步 |
| BOR 欠压/掉电复位 | 运行中 VDD 跌落到阈值以下 | 电压不足时及时复位,防止半电压下误写 Flash、逻辑乱跳 |
| WDT 看门狗复位 | 软件未在窗口内喂狗 | 运行期跑飞、死锁后的自动恢复 |
| 软件复位 SWRST | 软件主动写复位寄存器 | 升级后重启、故障后主动重来 |
| 外部复位脚 | 复位按键、调试器、上位机 | 人工或调试干预 |
读这张表可以串起整条时间线:开机的一瞬间 POR 管;电源抖动时 BOR 管;正常运行期间逻辑跑飞则归 WDT 管。其中 BOR 的检测电路和 WDT 一样,通常放在独立低速域、用独立电源保持,原因在第二节已经说过——主电源正在崩溃的那一刻,依赖主电源的电路是救不了主电源的。可靠性设计翻来覆去就围绕这一条:监控者不能与被监控者共因失效。
这一篇在知识体系里的位置
本篇属于芯片设计入门层的数字逻辑与可靠性基础,回答「没人在场时,芯片怎么把自己救回来」。向前读,看门狗的硬件本体就是一个计数器与移位寄存器,它在系统中的位置要看懂时钟与复位入门;横向读,单状态的超时保护见鲁棒 FSM 与故障恢复,复位信号如何汇入控制器、如何同步释放见复位设计和复位域跨越 RDC;向应用背景读,看门狗之所以是强制部件,源于车规芯片与可靠性基础的失效率要求,复位行为的实测则属于芯片测试与量测的覆盖范围。
总结
看门狗定时器与超时复位是一个跑在独立低速时钟上的倒数计数器:软件在确认各任务真实推进的健康检查点定期「喂狗」重装,一旦跑飞、死循环或卡等待没人喂,计数器归零就先经 NMI 给软件一次保存现场、安全关断的机会,宽限期后再硬复位让系统自动重启;喂狗绝不能放在定时器中断里无脑进行,窗口看门狗还把「喂太早」也判为故障;它与 POR(上电)、BOR(欠压)、软件和外部复位共同汇入复位控制器并同步释放,而整套设计的总原则只有一句——监控者不能与被监控者共因失效。