概要
ウォッチドッグタイマ(Watchdog Timer、WDT)は、ソフトウェアの暴走・ハング・デッドロックを検知して、自動的にシステムをリセット・安全状態へ移行させるためのハードウェア安全機構です。
動作原理は「番犬(Watchdog)」の名が示す通り、ソフトウェアが正常動作していれば定期的にタイマーをリセット(「餌やり」とも呼ばれる)し続けます。何らかの障害でソフトウェアが正常動作を停止してリセットを行わなくなると、タイマーがカウントアップしてオーバーフローし、ハードウェアがシステムリセットを発生させます。
ウォッチドッグはリアルタイムシステムの最後の安全網であり、特にハードリアルタイムシステムや安全要件の高い機器(医療・車載・産業)では必須のコンポーネントです。
歴史・背景
ウォッチドッグタイマの概念は1970〜80年代の産業用制御システムに起源があります。PLCや初期のマイコン制御システムでは、ソフトウェアの暴走がアクチュエータの誤動作につながる危険があり、独立したハードウェアによるシステム監視が必要とされました。
1980年代以降、マイコンにウォッチドッグタイマが内蔵されるようになり、外付け回路なしでソフトウェア暴走対策が可能になりました。現代のARM Cortex-Mベースのマイコン(STM32、nRF52等)には独立型ウォッチドッグ(IWDG)と窓型ウォッチドッグ(WWDG)の両方が内蔵されるのが標準的です。
機能安全規格(IEC 61508、ISO 26262)では、Safety Integrity Level(SIL)に応じたウォッチドッグの設計要件が規定されています。高SILシステムでは「独立したウォッチドッグ回路」と「外部ウォッチドッグIC」の併用が求められることもあります。
技術仕様
ウォッチドッグの種類
| 種類 | 説明 | 特徴 |
|---|---|---|
| 独立型ウォッチドッグ(IWDG) | 独自のクロック源(LSI等)で動作 | メインクロック停止でも動作・最も信頼性が高い |
| 窓型ウォッチドッグ(WWDG) | リセットタイミングに「窓」を設ける | 早すぎるリセットも検出できる |
| 外付けウォッチドッグIC | CPUの外部に独立したIC(MAX706等) | CPU電源異常・クロック異常も検出可能 |
| ソフトウェアウォッチドッグ | RTOS上のタイマ・タスク監視 | ハードウェアWDTより柔軟だが信頼性は低い |
STM32のIWDG仕様
STM32F4シリーズのIWDG(独立型ウォッチドッグ):
| 項目 | 仕様 |
|---|---|
| クロック源 | 内部RC発振器(LSI: 32kHz±10%) |
| プリスケーラ | /4, /8, /16, /32, /64, /128, /256 |
| タイムアウト範囲 | 最短0.5ms〜最長26.2秒(LSI 32kHzの場合) |
| キー(リセット) | 0xAAAAをKR(Key Register)へ書き込み |
| 起動後の停止 | 不可能(一度起動したら停止できない) |
| リセット原因記録 | RCCレジスタで確認可能(IWDGRST フラグ) |
STM32のWWDG仕様
| 項目 | 仕様 |
|---|---|
| クロック源 | APBクロック(システムクロック依存) |
| 窓機能 | 規定時間より早くリセットしても検出される |
| タイムアウト範囲 | 数ms(APBクロックとプリスケーラに依存) |
| 割り込み | タイムアウト前に早期警告割り込みを発生可能 |
IWDGはメインクロックが停止しても動作するため、クロック異常による暴走にも対応できます。安全性の高いシステムでは両方(IWDGとWWDG)を組み合わせます。
動作原理
基本的なウォッチドッグの使い方
/* STM32 IWDG の設定と使用(HAL使用) */
IWDG_HandleTypeDef hiwdg;
void WatchdogInit(void) {
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64; /* 32kHz / 64 = 500Hz */
hiwdg.Init.Reload = 250; /* 250 / 500Hz = 500ms タイムアウト */
HAL_IWDG_Init(&hiwdg);
/* 初期化後、500ms以内に毎回リフレッシュが必要 */
}
/* メインループまたはタスク内での定期リフレッシュ */
void WatchdogRefresh(void) {
HAL_IWDG_Refresh(&hiwdg); /* KRレジスタに0xAAAAを書き込む */
}
int main(void) {
SystemInit();
WatchdogInit();
while (1) {
doMainProcessing(); /* 500ms以内に完了すること */
WatchdogRefresh(); /* WDTリセット */
}
}
RTOSでの複数タスク監視
RTOSを使う場合、単純なメインループのリフレッシュでは「どのタスクがハングしているか」を検出できません。専用の「ウォッチドッグタスク」パターンを使います。
/* ウォッチドッグタスクによる全タスク監視 */
#define NUM_TASKS 4
#define WDT_TIMEOUT_MS 1000
volatile uint32_t task_checkin[NUM_TASKS] = {0};
volatile uint32_t task_deadline[NUM_TASKS] = {200, 100, 500, 1000}; /* 各タスクの期待周期ms */
/* 各タスクが定期的に呼ぶチェックイン関数 */
void wdt_checkin(uint8_t task_id) {
task_checkin[task_id] = xTaskGetTickCount();
}
/* 最高優先度のウォッチドッグタスク */
void watchdogTask(void *pvParam) {
for (;;) {
TickType_t now = xTaskGetTickCount();
for (int i = 0; i < NUM_TASKS; i++) {
TickType_t elapsed = now - task_checkin[i];
if (elapsed > pdMS_TO_TICKS(task_deadline[i] * 3)) {
/* タスクiがチェックインを怠っている → 異常 */
log_error("Task %d timeout: %dms", i, elapsed);
enterSafeState(); /* 安全状態へ移行 */
/* またはHWリセットを発動 */
}
}
/* 全タスク正常 → HWウォッチドッグをリフレッシュ */
HAL_IWDG_Refresh(&hiwdg);
vTaskDelay(pdMS_TO_TICKS(100)); /* 100ms周期で監視 */
}
}
/* 監視対象タスクの例 */
void sensorTask(void *pvParam) {
TickType_t xLastWake = xTaskGetTickCount();
for (;;) {
readSensor();
wdt_checkin(0); /* ウォッチドッグタスクへ生存報告 */
vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(200));
}
}
窓型ウォッチドッグ(WWDG)による早期リフレッシュ検出
通常のウォッチドッグは「リフレッシュが遅すぎる」場合のみ検出しますが、WWDGは「早すぎるリフレッシュ」も検出します。制御ループが高速すぎる(無限ループでリフレッシュしてしまう)異常も検知できます。
WWDGの窓設定例:
下限(Window Value): 80 → これより早いリフレッシュはエラー
上限(Counter Value): 127 → これを超えるとタイムアウト
リフレッシュ許可期間: Counter値が127→80の区間のみ
早すぎ(Counter > Window): リセット発生
遅すぎ(Counter = 0x40): リセット発生
正常: 窓の範囲内でリフレッシュ
用途・ユースケース
自動車ECU
自動車の電子制御ユニット(ECU)ではウォッチドッグが必須で、ISO 26262(機能安全)に準拠した設計が求められます。多くの場合、内蔵IWDGと外付けウォッチドッグICを二重化します。
- エンジンECU: WDTタイムアウト検知→スロットル閉鎖・点火停止→安全停止
- EPS(電動パワーステアリング): WDT検知→モータ電流遮断→手動操舵に移行
- AUTOSAR: WdgM(Watchdog Manager)コンポーネントがソフトウェアWDTを管理
医療機器
- 人工呼吸器: ソフトウェア異常でバルブ制御が停止すると患者の危険。WDTで確実にリセットまたはフェイルセーフ動作に移行
- 輸液ポンプ: WDT検知でポンプを安全停止
産業・IoT
- 産業用センサ・PLC: 通信途絶・処理ハングを検知して安全停止または再起動
- IoTゲートウェイ: 長期稼働中のメモリリーク等によるハングをWDTで自動回復
実装・開発のポイント
ウォッチドッグの正しい設計原則
-
「証拠」を持ったリフレッシュ メインループの末尾で無条件にリフレッシュするだけでは、ループ内の処理がスキップされていても検出できません。重要な処理の実行を確認してからリフレッシュします。
/* 悪い例:無条件リフレッシュ(処理が失敗していても検出できない) */ while (1) { if (sensor_ok) read_sensor(); /* センサ異常でスキップされても... */ HAL_IWDG_Refresh(&hiwdg); /* ...無条件にリフレッシュ */ } /* 良い例:重要処理の完了を確認してリフレッシュ */ while (1) { bool sensor_read = read_sensor_with_timeout(); bool control_done = run_control_loop(); if (sensor_read && control_done) { HAL_IWDG_Refresh(&hiwdg); /* 両方完了した場合のみリフレッシュ */ } /* どちらかが失敗すれば500msでWDTリセット */ } -
WDTタイムアウトの適切な設定 タイムアウトは「正常動作での最大リフレッシュ間隔 × 安全係数」で設定します。短すぎると誤動作、長すぎると障害検知が遅れます。
-
リセット原因の記録と診断 WDTリセット後の起動時にリセット原因を確認し、不揮発メモリ(EEPROM/Flash)に記録します。フィールドでの問題診断に不可欠です。
void checkResetCause(void) { if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) { /* IWDGリセット発生 → ログ記録 */ nvm_write_log(LOG_IWDG_RESET, xTaskGetTickCount()); __HAL_RCC_CLEAR_RESET_FLAGS(); } } -
デバッグ中のWDT一時停止 ブレークポイントでシステムが停止中にWDTリセットが発生するのを防ぐため、デバッグ接続中(JTAG/SWD)はWDTを一時停止する設定が多くのマイコンにあります(STM32: DBGMCUレジスタ)。
/* デバッグ中はWDTを停止(開発時のみ) */ __HAL_DBGMCU_FREEZE_IWDG();
他技術との比較
| 観点 | IWDG(独立型) | WWDG(窓型) | 外付けWDT IC | ソフトウェアWDT |
|---|---|---|---|---|
| クロック源 | 独立(LSI) | APBバス | 独立 | システムクロック依存 |
| クロック停止の検出 | 可 | 不可 | 可 | 不可 |
| 早期リフレッシュ検出 | 不可 | 可 | 可(設定依存) | 可 |
| 信頼性 | 高 | 高 | 最高(外部) | 低(バグの影響を受けやすい) |
| 安全規格 | ISO 26262・IEC 61508対応 | 同左 | 同左 | 補助的な使用のみ |
| 使用場面 | 一般的な組み込み全般 | 制御周期の正確な監視 | 高SILシステム | RTOS上の多タスク監視 |
ウォッチドッグは組み込みシステムの「最後の安全網」です。設計段階から組み込み、デバッグ・テスト時も常に有効にして動作を確認することが、信頼性の高いシステムを構築するうえで欠かせません。