Zephyr看门狗-硬件看门狗

硬件看门狗是一个专用独立的硬件定时器,当系统出现故障(如死循环、崩溃)时,如果未能在规定时间内"喂狗",它会重启系统。Zephyr在zephyr/include/zephyr/drivers/watchdog.h中为硬件看门狗抽象了专门的接口,以屏蔽不同SoC的硬件看门狗差异。

硬件看门狗接口 链接到标题

API 链接到标题

  • int wdt_setup(const struct device *dev, uint8_t options):配置看门狗实例,需要在安装超时配置后调用。
    • 参数:设备实例dev和配置选项options
      • WDT_OPT_PAUSE_IN_SLEEP:在CPU睡眠时暂停看门狗计时器。
      • WDT_OPT_PAUSE_HALTED_BY_DBG:在CPU被调试器暂停时暂停看门狗计时器。
    • 返回值:成功返回0,失败返回错误码。
  • int wdt_disable(const struct device *dev):禁用看门狗实例,会自动卸载所有超时配置。
    • 参数:设备实例dev
    • 返回值:成功返回0,失败返回错误码。
  • int wdt_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg):安装新的超时配置。
    • 参数:设备实例dev和超时配置结构体cfg
    • 返回值:成功返回通道ID,失败返回错误码。
  • int wdt_feed(const struct device *dev, int channel_id):对指定的看门狗超时进行喂狗操作。
    • 参数:设备实例dev和通道IDchannel_id
    • 返回值:成功返回0,失败返回错误码。

配置数据结构&类型 链接到标题

typedef void (*wdt_callback_t)(const struct device *dev, int channel_id);

struct wdt_timeout_cfg {
    struct wdt_window window;    // 时间窗口配置
    wdt_callback_t callback;     // 超时回调函数
    uint8_t flags;               // 看门狗行为标志
};

struct wdt_window {
    uint32_t min;  // 最小超时时间(毫秒)
    uint32_t max;  // 最大超时时间(毫秒)
};

看门狗行为标志

  • WDT_FLAG_RESET_NONE:不重启。
  • WDT_FLAG_RESET_CPU_CORE:重启CPU核心。
  • WDT_FLAG_RESET_SOC:重启SoC。

使用方法 链接到标题

在Devicetree中开启硬件看门狗,不同的SoC,看门狗的节点名不一样,下面示例esp32c3的Devicetree配置:

/ {
    model = "Espressif ESP32C3-DevkitC";
    compatible = "espressif,esp32c3";
    aliases {
        watchdog0 = &wdt0;
    };
};

&wdt0 {
    status = "okay";
};

Kconfig配置启动WDT

CONFIG_WATCHDOG=y

看门狗API使用方法

//步骤1:获取设备
const struct device *const wdt = DEVICE_DT_GET(DT_ALIAS(watchdog0));
if (!device_is_ready(wdt)) {
    printk("%s: device not ready.\n", wdt->name);
    return 0;
}
    
//步骤2:配置超时参数
struct wdt_timeout_cfg wdt_config = {
    .flags = WDT_FLAG_RESET_SOC,  // 超时后重启整个SoC
    .window.min = 0,              // 最小时间窗口
    .window.max = 1000,           // 最大时间窗口(1秒)
    .callback = my_callback,      // 可选的回调函数
};

//步骤3:安装超时配置
int wdt_channel_id = wdt_install_timeout(wdt, &wdt_config);
if (wdt_channel_id < 0) {
    printk("Failed to install watchdog timeout\n");
    return -1;
}

//步骤4:启动看门狗
int err = wdt_setup(wdt, WDT_OPT_PAUSE_HALTED_BY_DBG);
if (err < 0) {
    printk("Failed to setup watchdog\n");
    return -1;
}

//步骤5:定期喂狗
while (1) {
    // 应用程序主循环
    do_something();
    
    // 定期喂狗,防止系统重启, 如果该主循序因不能执行,WDT超时会重启SoC
    wdt_feed(wdt, wdt_channel_id);
    
    k_sleep(K_MSEC(500));  // 每500ms喂一次
}

硬件看门狗驱动的实现 链接到标题

驱动实现方式 链接到标题

硬件看门狗的驱动实现在zephyr/drivers/watchdog下, Zephyr下绝大部分的SoC 硬件看门狗驱动都有实现,其实现范式和其它驱动模型一样,不同的SoC的硬件驱动各自按照下面接口定义调用hal实现对应的功能, 并使用DEVICE_DT_DEFINE注册即可

  • 看门狗的驱动实现在zephyr/drivers/watchdog下,Zephyr下绝大部分的SoC WDT驱动都有实现,其实现范式和其它驱动模型一样,不同的soc的WDT驱动各自按照下面接口定义调用hal实现对应的功能, 并使用DEVICE_DT_DEFINE注册即可
__subsystem struct wdt_driver_api {
wdt_api_setup setup;
wdt_api_disable disable;
wdt_api_install_timeout install_timeout;
wdt_api_feed feed;
};

typedef int (*wdt_api_setup)(const struct device *dev, uint8_t options);
typedef int (*wdt_api_disable)(const struct device *dev);
typedef int (*wdt_api_install_timeout)(const struct device *dev,
                    const struct wdt_timeout_cfg *cfg);
typedef int (*wdt_api_feed)(const struct device *dev, int channel_id);

例如esp32的zephyr/drivers/watchdog/xt_wdt_esp32.c

static DEVICE_API(wdt, esp32_xt_wdt_api) = {
    .setup = esp32_xt_wdt_setup,
    .disable = esp32_xt_wdt_disable,
    .install_timeout = esp32_xt_wdt_install_timeout,
    .feed = esp32_xt_wdt_feed
};

DEVICE_DT_DEFINE(DT_NODELABEL(xt_wdt),
        &esp32_xt_wdt_init,
        NULL,
        &esp32_xt_wdt_data0,
        &esp32_xt_wdt_config0,
        POST_KERNEL,
        CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
        &esp32_xt_wdt_api);

Kconfig配置 链接到标题

公共配置 链接到标题

CONFIG_WDT_DISABLE_AT_BOOT 在系统启动时主动禁用看门狗, 不同的SoC支持情况不同,如果对应驱动的Kconfig中有HAS_WDT_DISABLE_AT_BOOT就是支持,例如Kconfig.esp32j就是支持

config WDT_ESP32
    bool "ESP32 Watchdog (WDT) Driver"
    default y
    depends on DT_HAS_ESPRESSIF_ESP32_WATCHDOG_ENABLED
    select HAS_WDT_DISABLE_AT_BOOT
    help
        Enable WDT driver for ESP32.

CONFIG_WDT_MULTISTAGE 启用多阶段看门狗超时机制,同的SoC支持情况不同,如果对应驱动的Kconfig中有HAS_WDT_DISABLE_AT_BOOT就是支持,目前只有OpenTitan支持,例如Kconfig.opentitan

config WDT_OPENTITAN
    bool "OpenTitan Always-On (AON) Timer"
    depends on DT_HAS_LOWRISC_OPENTITAN_AONTIMER_ENABLED
    default y
    select HAS_WDT_MULTISTAGE
    help
        This option enables support for the watchdog portion of the OpenTitan AON
        timer.

私有配置 链接到标题

不同的SoC的WDT有不同的特性,会有自己的Kconfig选项,这些可以从不同的Kconfig中找到并根据需要配置,例如zephyr/drivers/watchdog/Kconfig.renesas_ra中可以配置WDT产生NMI中断

config WDT_RENESAS_RA_NMI
    bool "NMI interrupt enable"
    default y
    select RUNTIME_NMI
    help
    Watchdog timer generates NMI at value 0

自动配置项 链接到标题

并不是所以的SoC都支持WDT Timeout的callback,和实际的驱动实现与配置相关,是否支持callback由HAS_WDT_NO_CALLBACKS来控制体现,例如zephyr/drivers/watchdog/Kconfig.gd32中使用FWDGT就支持callback而使用WWDGT就不支持callback

config FWDGT_GD32
    bool "GD32 Free watchdog timer (FWDGT) driver"
    default y
    depends on DT_HAS_GD_GD32_FWDGT_ENABLED
    select HAS_WDT_DISABLE_AT_BOOT
    select USE_GD32_FWDGT
    help
        Enable the Free watchdog timer (FWDGT) driver for GD32 SoCs.

config WWDGT_GD32
    bool "GD32 Window watchdog timer (WWDGT) Driver"
    default y
    depends on DT_HAS_GD_GD32_WWDGT_ENABLED
    select HAS_WDT_NO_CALLBACKS
    select USE_GD32_WWDGT
    help
        Enable the Window watchdog timer (WWDGT) driver for GD32 SoCs.

将普通的硬件Counter作为WDT 链接到标题

在设备树中做如下配置,可以将普通的硬件Counter作为WDT

&timer0 {
    status = "okay";
    interrupts = <8 0>;
    zli;st, <&wdt_counter>;
};

&wdt0 {
    status = "disabled";
};

/ {
    wdt_counter: wdt-counter {
        compatible = "zephyr,counter-watchdog";
        status = "okay";
        counter = <&timer0>;
    };
};

zephyr/drivers/watchdog/Kconfig通过设备树生成的DT_HAS_ZEPHYR_COUNTER_WATCHDOG_ENABLED选中WDT_COUNTER

config WDT_COUNTER
    bool "Counter based watchdog"
    default y
    depends on DT_HAS_ZEPHYR_COUNTER_WATCHDOG_ENABLED
    select COUNTER

zephyr/drivers/watchdog/CMakeLists.txt发现有CONFIG_WDT_COUNTER将counter实现wdt的驱动wdt_counter.c加入编译

zephyr_library_sources_ifdef(CONFIG_WDT_COUNTER wdt_counter.c)

参考 链接到标题

https://docs.zephyrproject.org/latest/hardware/peripherals/watchdog.html