Sfoglia il codice sorgente

TWDT: refactor the TWDT to be driver agnostic

This refactoring brings a private API for the TWDT implementation, which
can now use a hardware timer (Timer Group) or a software timer (esp_timer)
Omar Chebib 3 anni fa
parent
commit
30d12af191

+ 6 - 1
components/esp_pm/linker.lf

@@ -38,6 +38,10 @@ entries:
         task_wdt:find_entry_from_task_handle_and_check_all_reset (noflash)
         task_wdt:esp_task_wdt_reset (noflash)
         task_wdt:esp_task_wdt_reset_user (noflash)
+        if ESP_TASK_WDT_USE_ESP_TIMER = y:
+            task_wdt_impl_esp_timer:esp_task_wdt_impl_timer_feed (noflash)
+        else:
+            task_wdt_impl_timergroup:esp_task_wdt_impl_timer_feed (noflash)
 
 [mapping:esp_timer_pm]
 archive: libesp_timer.a
@@ -45,7 +49,8 @@ entries:
     if PM_SLP_IRAM_OPT = y:
         # esp_timer_feed is called from task_wdt_timer_feed, so put it
         # in IRAM if task_wdt_timer_feed itself is in IRAM.
-        esp_timer:esp_timer_feed (noflash)
+        if ESP_TASK_WDT_USE_ESP_TIMER = y:
+            esp_timer:esp_timer_feed (noflash)
         if ESP_TIMER_IMPL_TG0_LAC = y:
             esp_timer_impl_lac:esp_timer_impl_lock (noflash)
             esp_timer_impl_lac:esp_timer_impl_unlock (noflash)

+ 7 - 1
components/esp_system/CMakeLists.txt

@@ -26,7 +26,13 @@ else()
             "debug_stubs.c")
 
     if(CONFIG_ESP_TASK_WDT)
-        list(APPEND srcs "task_wdt.c")
+        list(APPEND srcs "task_wdt/task_wdt.c")
+
+        if(CONFIG_ESP_TASK_WDT_USE_ESP_TIMER)
+            list(APPEND srcs "task_wdt/task_wdt_impl_esp_timer.c")
+        else()
+            list(APPEND srcs "task_wdt/task_wdt_impl_timergroup.c")
+        endif()
     endif()
 
     if(CONFIG_ESP_SYSTEM_USE_EH_FRAME)

+ 4 - 1
components/esp_system/include/esp_private/crosscore_int.h

@@ -61,6 +61,7 @@ void esp_crosscore_int_send_gdb_call(int core_id);
  */
 void esp_crosscore_int_send_print_backtrace(int core_id);
 
+#if CONFIG_ESP_TASK_WDT
 /**
  * Send an interrupt to a CPU indicating it call `task_wdt_timeout_abort_xtensa`.
  * This will make the CPU abort, using the interrupted task frame.
@@ -72,7 +73,9 @@ void esp_crosscore_int_send_print_backtrace(int core_id);
  * @param core_id Core that should abort
  */
 void esp_crosscore_int_send_twdt_abort(int core_id);
-#endif
+
+#endif // CONFIG_ESP_TASK_WDT
+#endif // !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP32C2
 
 #ifdef __cplusplus
 }

+ 11 - 0
components/esp_system/include/esp_private/esp_task_wdt.h

@@ -15,6 +15,17 @@
 extern "C" {
 #endif
 
+/**
+ * @brief Type used to define the context of a Task WatchDog Timer implementation.
+ *        This is used internally in the TWDT driver, it is implementation specific.
+ */
+typedef void* twdt_ctx_t;
+
+/**
+ * @brief Type of the function used as an ISR callback.
+ */
+typedef void (*twdt_isr_callback)(void*);
+
 /**
  * @brief   Stop the Task Watchdog Timer (TWDT)
  *

+ 101 - 0
components/esp_system/include/esp_private/esp_task_wdt_impl.h

@@ -0,0 +1,101 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "esp_err.h"
+#include "../esp_task_wdt.h"
+#include "esp_private/esp_task_wdt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @brief   Allocate and initialize the Task Watchdog Timer (TWDT) with the given configuration.
+ *
+ * @param[in] config Pointer to the configuration structure
+ * @param[out] obj Abstract context for the current timer, this will be passed to all the other functions
+ *
+ * @return
+ *  - ESP_OK: Successfully initialized and configured the timer
+ *  - Other: Failed to initialize the timer
+ */
+esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
+                                           twdt_isr_callback callback,
+                                           twdt_ctx_t *obj);
+
+
+/**
+ * @brief   Reconfigure a timer.
+ *
+ * The timer must be stopped when calling this function. The timer will not be restarted at the end of this
+ * function.
+ *
+ * @param[in] config Pointer to the configuration structure
+ *
+ * @return
+ *  - ESP_OK: Successfully reconfigured the timer
+ *  - Other: Failed to reconfigure the timer
+ */
+esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config);
+
+/**
+ * @brief   Free the Task Watchdog Timer (TWDT).
+ *
+ * @param[in] obj Abstract implementation context
+ *
+ */
+void esp_task_wdt_impl_timer_free(twdt_ctx_t obj);
+
+
+/**
+ * @brief   Feed the Task Watchdog Timer (TWDT)
+ *
+ * Feed the timer underneath to prevent it from triggering for the next period (configured at initialization).
+ *
+ * @param[in] obj Abstract implementation context
+ * @return
+ *  - ESP_OK: timer successfully feeded
+ *  - Other: failed to feed the timer
+ */
+esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj);
+
+
+/**
+ * @brief   Function invoked as soon as the Task Watchdog Timer (TWDT) ISR callback is called.
+ *
+ * @param[in] obj Abstract implementation context
+ */
+void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj);
+
+
+/**
+ * @brief   Stop the Task Watchdog Timer (TWDT).
+ *
+ * @param[in] obj Abstract implementation context
+ *
+ */
+esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj);
+
+
+/**
+ * @brief   Restart the Task Watchdog Timer (TWDT)
+ *
+ * This function will restart/resume the timer after it has been stopped.
+ *
+ * @param[in] obj Abstract implementation context
+ * @return
+ *  - ESP_OK: timer successfully stopped
+ *  - Other: failed to stop the timer
+ */
+esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj);
+
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 3
components/esp_system/include/esp_private/system_internal.h

@@ -11,8 +11,9 @@ extern "C" {
 #endif
 
 #include "esp_system.h"
+#include "soc/soc_caps.h"
 
-#if !CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
+#if SOC_TIMER_GROUPS >= 2
 
 /* All the targets that have more than one timer group are using
  * APB clock by default, which frequency is 80MHz.
@@ -24,8 +25,9 @@ extern "C" {
 
 #else
 
-/* The targets that have a single timer group use XTAL clock as the
- * default clock. XTAL clock frequency is 40MHz. */
+/* The targets that have a single timer group use a 40MHz clock for the
+ * Timer Group 0. Let's adapt the prescaler value accordingly.
+ */
 #define MWDT0_TICK_PRESCALER    20000
 #define MWDT0_TICKS_PER_US      500
 

+ 19 - 2
components/esp_system/include/esp_task_wdt.h

@@ -32,19 +32,36 @@ typedef struct esp_task_wdt_user_handle_s * esp_task_wdt_user_handle_t;
 /**
  * @brief  Initialize the Task Watchdog Timer (TWDT)
  *
- * This function configures and initializes the TWDT. If the TWDT is already initialized when this function is called,
- * this function will update the TWDT's current configuration. This funciton will also subscribe the idle tasks if
+ * This function configures and initializes the TWDT. This function will subscribe the idle tasks if
  * configured to do so. For other tasks, users can subscribe them using esp_task_wdt_add() or esp_task_wdt_add_user().
+ * This function won't start the timer if no task have been registered yet.
  *
  * @note esp_task_wdt_init() must only be called after the scheduler is started. Moreover, it must not be called by
  *       multiple tasks simultaneously.
  * @param[in] config Configuration structure
  * @return
  *  - ESP_OK: Initialization was successful
+ *  - ESP_ERR_INVALID_STATE: Already initialized
  *  - Other: Failed to initialize TWDT
  */
 esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config);
 
+/**
+ * @brief Reconfigure the Task Watchdog Timer (TWDT)
+ *
+ * The function reconfigures the running TWDT. It must already be initialized when this function is called.
+ *
+ * @note esp_task_wdt_reconfigure() must not be called by multiple tasks simultaneously.
+ *
+ * @param[in] config Configuration structure
+ *
+ * @return
+ *  - ESP_OK: Reconfiguring was successful
+ *  - ESP_ERR_INVALID_STATE: TWDT not initialized yet
+ *  - Other: Failed to initialize TWDT
+ */
+esp_err_t esp_task_wdt_reconfigure(const esp_task_wdt_config_t *config);
+
 /**
  * @brief   Deinitialize the Task Watchdog Timer (TWDT)
  *

+ 131 - 218
components/esp_system/task_wdt.c → components/esp_system/task_wdt/task_wdt.c

@@ -11,20 +11,18 @@
 #include "sdkconfig.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
-#include "hal/wdt_hal.h"
+#include "freertos/task_snapshot.h"
 #include "esp_err.h"
 #include "esp_attr.h"
 #include "esp_check.h"
 #include "esp_log.h"
-#include "esp_intr_alloc.h"
 #include "esp_debug_helpers.h"
 #include "esp_freertos_hooks.h"
 #include "esp_task_wdt.h"
-#include "esp_private/periph_ctrl.h"
 #include "esp_private/system_internal.h"
 #include "esp_private/crosscore_int.h"
-#include "freertos/task_snapshot.h"
-#include "esp_timer.h"
+#include "esp_private/esp_task_wdt.h"
+#include "esp_private/esp_task_wdt_impl.h"
 
 #if CONFIG_ESP_SYSTEM_USE_EH_FRAME
 #include "esp_private/eh_frame_parser.h"
@@ -40,6 +38,9 @@ extern void panic_print_registers(const void *frame, int core);
  * a different context than the one it's called from. */
 extern void xt_unhandled_exception(void *frame);
 
+/* Forward declaration of the idle hook callback */
+static bool idle_hook_cb(void);
+
 /* Global flag set to make the `panic` mechanism think a real `abort()` was
  * called. This is used in the ISR handler, in case we have to panic when
  * a task doesn't feed its timer. */
@@ -50,18 +51,6 @@ bool g_twdt_isr = false;
 
 // --------------------------------------------------- Definitions -----------------------------------------------------
 
-// ----------------------- Macros --------------------------
-
-// Use a hardware timer implementation or a software implementation
-#define TWDT_HARDWARE_IMPL      !CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
-
-#if TWDT_HARDWARE_IMPL
-// HAL related variables and constants only defined in a hardware implementation
-#define TWDT_INSTANCE           WDT_MWDT0
-#define TWDT_TICKS_PER_US       MWDT0_TICKS_PER_US
-#define TWDT_PRESCALER          MWDT0_TICK_PRESCALER   // Tick period of 500us if WDT source clock is 80MHz
-#endif // TWDT_HARDWARE_IMPL
-
 // ---------------------- Typedefs -------------------------
 
 /**
@@ -78,16 +67,11 @@ struct twdt_entry {
 // Structure used to hold run time configuration of the TWDT
 typedef struct twdt_obj twdt_obj_t;
 struct twdt_obj {
-#if TWDT_HARDWARE_IMPL
-    wdt_hal_context_t hal;
-    intr_handle_t intr_handle;
-#else // TWDT_HARDWARE_IMPL
-    esp_timer_handle_t sw_timer; // We use esp_timer to simulate a hardware WDT
-    uint32_t period_ms;
-#endif // TWDT_HARDWARE_IMPL
+    twdt_ctx_t impl_ctx;
     SLIST_HEAD(entry_list_head, twdt_entry) entries_slist;
     uint32_t idle_core_mask;    // Current core's who's idle tasks are subscribed
     bool panic; // Flag to trigger panic when TWDT times out
+    bool waiting_for_task; // Flag to start the timer as soon as a task is added
 };
 
 // ----------------------- Objects -------------------------
@@ -104,109 +88,17 @@ static char core_user_names[portNUM_PROCESSORS][CORE_USER_NAME_LEN];
 
 // ----------------------------------------------------- Private -------------------------------------------------------
 
-// ---------------------- Callbacks ------------------------
-
-/**
- * @brief Idle hook callback
- *
- * Idle hook callback called by the idle tasks to feed the TWDT
- *
- * @return Whether the idle tasks should continue idling
- */
-static bool idle_hook_cb(void)
-{
-#if CONFIG_FREERTOS_SMP
-    esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]);
-#else // CONFIG_FREERTOS_SMP
-    esp_task_wdt_reset();
-#endif // CONFIG_FREERTOS_SMP
-    return true;
-}
-
 // ----------------------- Helpers -------------------------
 
-#if !TWDT_HARDWARE_IMPL
-
-/**
- * Private API provided by esp_timer component to feed a timer without
- * the need of disabling it, removing it and inserting it manually.
- */
-esp_err_t esp_timer_feed(esp_timer_handle_t timer);
-
-#endif // !TWDT_HARDWARE_IMPL
-
-
-static esp_err_t task_wdt_timer_stop(twdt_obj_t *obj)
-{
-    esp_err_t ret = ESP_OK;
-
-    if (obj == NULL) {
-        return ESP_ERR_INVALID_STATE;
-    }
-
-#if TWDT_HARDWARE_IMPL
-    // All tasks have reset; Feed the underlying timer.
-    wdt_hal_write_protect_disable(&obj->hal);
-    wdt_hal_disable(&obj->hal);
-    wdt_hal_write_protect_enable(&obj->hal);
-#else // TWDT_HARDWARE_IMPL
-    if (obj->sw_timer == NULL) {
-        ret = ESP_ERR_INVALID_STATE;
-    }
-
-    if (ret == ESP_OK) {
-        esp_timer_stop(obj->sw_timer);
-    }
-#endif // TWDT_HARDWARE_IMPL
-
-    return ret;
-}
-
-
-static esp_err_t task_wdt_timer_restart(twdt_obj_t *obj)
-{
-    esp_err_t ret = ESP_OK;
-
-    if (obj == NULL) {
-        return ESP_ERR_INVALID_STATE;
-    }
-
-#if TWDT_HARDWARE_IMPL
-    // All tasks have reset; Feed the underlying timer.
-    wdt_hal_write_protect_disable(&obj->hal);
-    wdt_hal_enable(&obj->hal);
-    wdt_hal_feed(&obj->hal);
-    wdt_hal_write_protect_enable(&obj->hal);
-#else // TWDT_HARDWARE_IMPL
-    if (obj->sw_timer == NULL) {
-        ret = ESP_ERR_INVALID_STATE;
-    }
-
-    if (ret == ESP_OK) {
-        esp_timer_start_periodic(obj->sw_timer, obj->period_ms * 1000);
-    }
-#endif // TWDT_HARDWARE_IMPL
-
-    return ret;
-}
-
 /**
  * @brief Reset the timer and reset flags of each entry
  * When entering this function, the spinlock has already been taken, no need to take it back.
  */
 static void task_wdt_timer_feed(void)
 {
-#if TWDT_HARDWARE_IMPL
-    // All tasks have reset; time to reset the hardware timer.
-    wdt_hal_write_protect_disable(&p_twdt_obj->hal);
-    wdt_hal_feed(&p_twdt_obj->hal);
-    wdt_hal_write_protect_enable(&p_twdt_obj->hal);
-#else // TWDT_HARDWARE_IMPL
-    /* No matter if feeding succeeded or not, we have to reset each list entry's flags.
-     * Thus, ignore the return value. */
-    esp_timer_feed(p_twdt_obj->sw_timer);
-#endif // TWDT_HARDWARE_IMPL
-    //Clear the has_reset flag in each entry
+    esp_task_wdt_impl_timer_feed(p_twdt_obj->impl_ctx);
+
+    /* Clear the has_reset flag in each entry */
     twdt_entry_t *entry;
     SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
         entry->has_reset = false;
@@ -300,6 +192,11 @@ static esp_err_t add_entry(bool is_task, void *entry_data, twdt_entry_t **entry_
     }
     // Add entry to list
     SLIST_INSERT_HEAD(&p_twdt_obj->entries_slist, entry, slist_entry);
+    // Start the timer if it has not been started yet and was waiting on a task to registered
+    if (p_twdt_obj->waiting_for_task) {
+        esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
+        p_twdt_obj->waiting_for_task = false;
+    }
     if (all_reset) {   //Reset hardware timer if all other tasks in list have reset in
         task_wdt_timer_feed();
     }
@@ -340,8 +237,15 @@ static esp_err_t delete_entry(bool is_task, void *entry_data)
     }
     // Remove entry
     SLIST_REMOVE(&p_twdt_obj->entries_slist, entry, twdt_entry, slist_entry);
-    // Reset hardware timer if all remaining tasks have reset
-    if (all_reset) {
+    /* Stop the timer if we don't have any more tasks/objects to watch */
+    if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
+        p_twdt_obj->waiting_for_task = true;
+        esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
+    } else {
+        p_twdt_obj->waiting_for_task = false;
+    }
+    /* Reset hardware timer if all remaining tasks have reset and if the list of tasks is not empty */
+    if (!p_twdt_obj->waiting_for_task && all_reset) {
         task_wdt_timer_feed();
     }
     portEXIT_CRITICAL(&spinlock);
@@ -569,6 +473,25 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic)
 #endif // CONFIG_IDF_TARGET_ARCH_RISCV
 
 
+// ---------------------- Callbacks ------------------------
+
+/**
+ * @brief Idle hook callback
+ *
+ * Idle hook callback called by the idle tasks to feed the TWDT
+ *
+ * @return Whether the idle tasks should continue idling
+ */
+static bool idle_hook_cb(void)
+{
+#if CONFIG_FREERTOS_SMP
+    esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]);
+#else // CONFIG_FREERTOS_SMP
+    esp_task_wdt_reset();
+#endif // CONFIG_FREERTOS_SMP
+    return true;
+}
+
 /**
  * @brief TWDT timeout ISR function
  *
@@ -580,12 +503,7 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic)
 static void task_wdt_isr(void *arg)
 {
     portENTER_CRITICAL_ISR(&spinlock);
-#if TWDT_HARDWARE_IMPL
-    // Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset)
-    wdt_hal_write_protect_disable(&p_twdt_obj->hal);
-    wdt_hal_handle_intr(&p_twdt_obj->hal);  // Feeds WDT and clears acknowledges interrupt
-    wdt_hal_write_protect_enable(&p_twdt_obj->hal);
-#endif // TWDT_HARDWARE_IMPL
+    esp_task_wdt_impl_timeout_triggered(p_twdt_obj->impl_ctx);
 
     // If there are no entries, there's nothing to do.
     if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
@@ -667,78 +585,6 @@ static void task_wdt_isr(void *arg)
     task_wdt_timeout_handling(cpus_fail, panic);
 }
 
-static esp_err_t task_wdt_timer_allocate(twdt_obj_t *obj, const esp_task_wdt_config_t *config)
-{
-#if TWDT_HARDWARE_IMPL
-    esp_err_t ret = esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &obj->intr_handle);
-
-    if (ret == ESP_OK) {
-        periph_module_enable(PERIPH_TIMG0_MODULE);
-        wdt_hal_init(&obj->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
-        // Assign the driver object
-        wdt_hal_write_protect_disable(&obj->hal);
-        // Configure 1st stage timeout and behavior
-        wdt_hal_config_stage(&obj->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
-        // Configure 2nd stage timeout and behavior
-        wdt_hal_config_stage(&obj->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
-        // Enable the WDT
-        wdt_hal_enable(&obj->hal);
-        wdt_hal_write_protect_enable(&obj->hal);
-    }
-
-    return ret;
-#else // TWDT_HARDWARE_IMPL
-
-    const esp_timer_create_args_t timer_args = {
-        .callback = task_wdt_isr,
-        .arg = NULL,
-        .dispatch_method = ESP_TIMER_ISR,
-        .name = "Task software watchdog",
-        .skip_unhandled_events = true
-    };
-
-    /* Software Task timer. As we don't have a spare hardware watchdog timer, we will use esp_timer to simulate one. */
-    esp_err_t ret = esp_timer_create(&timer_args, &obj->sw_timer);
-    ESP_GOTO_ON_FALSE((ret == ESP_OK), ret, reterr, TAG, "could not start periodic timer");
-
-    /* Configure it as a periodic timer, so that we check the Tasks everytime it is triggered.
-     * Its parameter is in microseconds, but the config's is in milliseconds, convert it. */
-    obj->period_ms = config->timeout_ms;
-    ret = esp_timer_start_periodic(obj->sw_timer, config->timeout_ms * 1000);
-    ESP_GOTO_ON_FALSE((ret == ESP_OK), ret, freeret, TAG, "could not start periodic timer");
-
-    return ret;
-freeret:
-    /* If we reach this point, it means that we were unable to program the timer as a periodic one, so
-     * no need to stop it before deleting it. */
-    esp_timer_delete(obj->sw_timer);
-reterr:
-    return ret;
-#endif // TWDT_HARDWARE_IMPL
-}
-
-static void task_wdt_timer_disable(twdt_obj_t *obj)
-{
-    esp_err_t ret = ESP_OK;
-    ret = task_wdt_timer_stop(obj);
-#if TWDT_HARDWARE_IMPL
-    // Stop hardware timer and the interrupt associated
-    wdt_hal_deinit(&obj->hal);
-    esp_intr_disable(obj->intr_handle);
-#endif // TWDT_HARDWARE_IMPL
-    assert(ret == ESP_OK);
-}
-
-
-static void task_wdt_timer_free(twdt_obj_t *obj)
-{
-#if TWDT_HARDWARE_IMPL
-    ESP_ERROR_CHECK(esp_intr_free(obj->intr_handle));   // Deregister interrupt
-#else // TWDT_HARDWARE_IMPL
-    esp_timer_delete(obj->sw_timer);
-#endif // TWDT_HARDWARE_IMPL
-}
-
 // ----------------------------------------------------- Public --------------------------------------------------------
 
 esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
@@ -747,56 +593,123 @@ esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
     ESP_RETURN_ON_FALSE(p_twdt_obj == NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT already initialized");
     esp_err_t ret = ESP_OK;
     twdt_obj_t *obj = NULL;
-    uint32_t old_core_mask = 0;
 
-    // Allocate and initialize the global object
+    /* Allocate and initialize the global object */
     obj = calloc(1, sizeof(twdt_obj_t));
     ESP_GOTO_ON_FALSE((obj != NULL), ESP_ERR_NO_MEM, err, TAG, "insufficient memory");
     SLIST_INIT(&obj->entries_slist);
     obj->panic = config->trigger_panic;
 
-    // Allocate the timer itself
-    ret = task_wdt_timer_allocate(obj, config);
+    /* Allocate the timer itself, NOT STARTED */
+    ret = esp_task_wdt_impl_timer_allocate(config, task_wdt_isr, &obj->impl_ctx);
     if (ret != ESP_OK) {
         goto err;
     }
 
-    // No error so far, we can assign it to the driver object
+    /* No error so far, we can assign it to the driver object */
     p_twdt_obj = obj;
 
-    // Update which core's idle tasks are subscribed
-    old_core_mask = p_twdt_obj->idle_core_mask;
+    /* Update which core's idle tasks are subscribed */
     p_twdt_obj->idle_core_mask = config->idle_core_mask;
-    if (old_core_mask) {
-        // Unsubscribe all previously watched core idle tasks
-        unsubscribe_idle(old_core_mask);
-    }
     if (config->idle_core_mask) {
-        // Subscribe the new cores idle tasks
+        /* Subscribe the new cores idle tasks */
         subscribe_idle(config->idle_core_mask);
     }
 
+    /* Start the timer only if we are watching some tasks */
+    if (!SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
+        p_twdt_obj->waiting_for_task = false;
+        esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
+    } else {
+        p_twdt_obj->waiting_for_task = true;
+    }
+
     return ESP_OK;
 err:
     free(obj);
     return ret;
 }
 
-esp_err_t esp_task_wdt_stop(void)
+esp_err_t esp_task_wdt_reconfigure(const esp_task_wdt_config_t *config)
 {
+    ESP_RETURN_ON_FALSE((config != NULL && config->idle_core_mask < (1 << portNUM_PROCESSORS)), ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
+    ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT not initialized yet");
+    uint32_t old_core_mask = 0;
     esp_err_t ret = ESP_OK;
+
+    /* Stop the timer to make sure we don't get into the ISR while reconfiguring the TWDT */
     portENTER_CRITICAL(&spinlock);
-    ret = task_wdt_timer_stop(p_twdt_obj);
+    ret = esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
+    if (ret != ESP_OK) {
+        goto err;
+    }
+
+    /* We can start reconfiguring the tasks */
+    p_twdt_obj->panic = config->trigger_panic;
+
+    /* Reconfigure the timer underneath (without restarting it) */
+    ret = esp_task_wdt_impl_timer_reconfigure(p_twdt_obj->impl_ctx, config);
+    if (ret != ESP_OK) {
+        goto err;
+    }
+
+    old_core_mask = p_twdt_obj->idle_core_mask;
+    /* If the new mask is different than the old one, we have to subscribe the new idle tasks */
+    if (old_core_mask != config->idle_core_mask) {
+        p_twdt_obj->idle_core_mask = config->idle_core_mask;
+
+        /* Unsubscribe all previously watched core idle tasks */
+        unsubscribe_idle(old_core_mask);
+
+        if (config->idle_core_mask) {
+            /* Subscribe the new cores idle tasks */
+            subscribe_idle(config->idle_core_mask);
+        }
+    }
+
+    /* Start the timer only if we are watching some tasks */
+    if (!SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
+        esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
+    }
+
     portEXIT_CRITICAL(&spinlock);
+err:
+    return ESP_OK;
+}
+
+esp_err_t esp_task_wdt_stop(void)
+{
+    esp_err_t ret = ESP_OK;
+
+    /* If the timer has not been initialized, do not attempt to stop it */
+    if (p_twdt_obj == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        portENTER_CRITICAL(&spinlock);
+        ret = esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
+        portEXIT_CRITICAL(&spinlock);
+    }
+
     return ret;
 }
 
 esp_err_t esp_task_wdt_restart(void)
 {
     esp_err_t ret = ESP_OK;
-    portENTER_CRITICAL(&spinlock);
-    ret = task_wdt_timer_restart(p_twdt_obj);
-    portEXIT_CRITICAL(&spinlock);
+
+    /* If the timer has not been initialized, do not attempt to stop it */
+    if (p_twdt_obj == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        portENTER_CRITICAL(&spinlock);
+        ret = esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
+        portEXIT_CRITICAL(&spinlock);
+    }
+
     return ret;
 }
 
@@ -813,10 +726,10 @@ esp_err_t esp_task_wdt_deinit(void)
     ESP_GOTO_ON_FALSE_ISR(SLIST_EMPTY(&p_twdt_obj->entries_slist), ESP_ERR_INVALID_STATE, err, TAG, "Tasks/users still subscribed");
 
     // Disable the timer
-    task_wdt_timer_disable(p_twdt_obj);
+    esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
 
     // Free driver resources
-    task_wdt_timer_free(p_twdt_obj);
+    esp_task_wdt_impl_timer_free(p_twdt_obj->impl_ctx);
 
     // Free the global object
     free(p_twdt_obj);

+ 155 - 0
components/esp_system/task_wdt/task_wdt_impl_esp_timer.c

@@ -0,0 +1,155 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "sdkconfig.h"
+#include "hal/wdt_hal.h"
+#include "esp_err.h"
+#include "esp_attr.h"
+#include "esp_check.h"
+#include "esp_log.h"
+#include "esp_debug_helpers.h"
+#include "esp_timer.h"
+#include "esp_private/esp_task_wdt_impl.h"
+
+/**
+ * Private API provided by esp_timer component to feed a timer without
+ * the need of disabling it, removing it and inserting it manually.
+ */
+esp_err_t esp_timer_feed(esp_timer_handle_t timer);
+
+/**
+ * Context for the software implementation of the Task WatchDog Timer.
+ * This will be passed as a parameter to public functions below. */
+typedef struct {
+    esp_timer_handle_t sw_timer;
+    uint32_t period_ms;
+} twdt_ctx_soft_t;
+
+/**
+ * Declare the initial context as static. It will be passed to the
+ * task_wdt implementation as the implementation context in the
+ * init function. */
+static twdt_ctx_soft_t init_context;
+
+static const char *TAG = "task_wdt_impl_soft";
+
+
+esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
+                                           twdt_isr_callback callback,
+                                           twdt_ctx_t *obj)
+{
+    twdt_ctx_soft_t *ctx = &init_context;
+    const esp_timer_create_args_t timer_args = {
+        .callback = callback,
+        .arg = NULL,
+        .dispatch_method = ESP_TIMER_ISR,
+        .name = "Task software watchdog",
+        .skip_unhandled_events = true
+    };
+
+    /* Software Task timer. As we don't have a spare hardware watchdog timer, we will use esp_timer to simulate one */
+    esp_err_t ret = esp_timer_create(&timer_args, &ctx->sw_timer);
+    ESP_GOTO_ON_FALSE((ret == ESP_OK), ret, reterr, TAG, "could not start periodic timer");
+
+    /* Configure it as a periodic timer, so that we check the Tasks everytime it is triggered.
+     * No need to start the timer here, it will be started later with `esp_task_wdt_impl_timer_restart` */
+    ctx->period_ms = config->timeout_ms;
+
+    /* Return our context to the caller */
+    *obj = (twdt_ctx_t) ctx;
+
+reterr:
+    return ret;
+}
+
+esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config)
+{
+    esp_err_t ret = ESP_OK;
+    twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
+
+    if (config == NULL || ctx == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        /* The timer is stopped, we only need to update the period in our context, next time we start the
+         * timer with `esp_task_wdt_impl_timer_restart`, we will pass the context's period to the
+         * underlying esp_timer instance. */
+        ctx->period_ms = config->timeout_ms;
+    }
+
+    return ret;
+}
+
+
+void esp_task_wdt_impl_timer_free(twdt_ctx_t obj)
+{
+    const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
+
+    if (ctx != NULL && ctx->sw_timer != NULL) {
+        ESP_ERROR_CHECK(esp_timer_delete(ctx->sw_timer));
+    }
+}
+
+
+esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)
+{
+    esp_err_t ret = ESP_OK;
+    const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
+
+    if (ctx == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        ret = esp_timer_feed(ctx->sw_timer);
+    }
+
+    return ret;
+}
+
+
+void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)
+{
+    (void) obj;
+}
+
+
+esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)
+{
+    esp_err_t ret = ESP_OK;
+    const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
+
+    if (ctx == NULL || ctx->sw_timer == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        ret = esp_timer_stop(ctx->sw_timer);
+    }
+
+    return ret;
+}
+
+
+esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)
+{
+    esp_err_t ret = ESP_OK;
+    twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
+
+    if (ctx == NULL || ctx->sw_timer == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        esp_timer_start_periodic(ctx->sw_timer, ctx->period_ms * 1000);
+    }
+
+    return ret;
+}

+ 184 - 0
components/esp_system/task_wdt/task_wdt_impl_timergroup.c

@@ -0,0 +1,184 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "sdkconfig.h"
+#include "hal/wdt_hal.h"
+#include "esp_err.h"
+#include "esp_attr.h"
+#include "esp_intr_alloc.h"
+#include "esp_private/system_internal.h"
+#include "esp_private/periph_ctrl.h"
+#include "esp_private/esp_task_wdt_impl.h"
+
+#define TWDT_INSTANCE           WDT_MWDT0
+#define TWDT_TICKS_PER_US       MWDT0_TICKS_PER_US
+#define TWDT_PRESCALER          MWDT0_TICK_PRESCALER   // Tick period of 500us if WDT source clock is 80MHz
+#define TWDT_PERIPH_MODULE      PERIPH_TIMG0_MODULE
+#define TWDT_INTR_SOURCE        ETS_TG0_WDT_LEVEL_INTR_SOURCE
+
+/**
+ * Context for the software implementation of the Task WatchDog Timer.
+ * This will be passed as a parameter to public functions below. */
+typedef struct {
+    wdt_hal_context_t hal;
+    intr_handle_t intr_handle;
+} twdt_ctx_hard_t;
+
+/**
+ * Declare the initial context as static. It will be passed to the
+ * task_wdt implementation as the implementation context in the
+ * init function. */
+static twdt_ctx_hard_t init_context;
+
+
+
+esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
+                                           twdt_isr_callback callback,
+                                           twdt_ctx_t *obj)
+{
+    esp_err_t ret = ESP_OK;
+    twdt_ctx_hard_t *ctx = &init_context;
+
+    if (config == NULL || obj == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        esp_intr_alloc(TWDT_INTR_SOURCE, 0, callback, NULL, &ctx->intr_handle);
+    }
+
+    if (ret == ESP_OK) {
+        periph_module_enable(TWDT_PERIPH_MODULE);
+        wdt_hal_init(&ctx->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
+
+        wdt_hal_write_protect_disable(&ctx->hal);
+        // Configure 1st stage timeout and behavior
+        wdt_hal_config_stage(&ctx->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
+        // Configure 2nd stage timeout and behavior
+        wdt_hal_config_stage(&ctx->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
+        // No need to enable to enable the WDT here, it will be enabled with `esp_task_wdt_impl_timer_restart`
+        wdt_hal_write_protect_enable(&ctx->hal);
+
+        /* Return the implementation context to the caller */
+        *obj = (twdt_ctx_t) ctx;
+    }
+
+    return ret;
+}
+
+
+esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config)
+{
+    esp_err_t ret = ESP_OK;
+
+    twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
+
+    if (config == NULL || ctx == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        wdt_hal_write_protect_disable(&ctx->hal);
+        /* Reconfigure the 1st and 2nd stage timeout */
+        wdt_hal_config_stage(&ctx->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
+        wdt_hal_config_stage(&ctx->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
+        wdt_hal_write_protect_enable(&ctx->hal);
+    }
+
+    return ret;
+}
+
+
+void esp_task_wdt_impl_timer_free(twdt_ctx_t obj)
+{
+    twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
+
+    if (ctx != NULL) {
+        /* Stop hardware timer and the interrupt associated */
+        wdt_hal_deinit(&ctx->hal);
+        ESP_ERROR_CHECK(esp_intr_disable(ctx->intr_handle));
+
+        /* Disable the Timer Group module */
+        periph_module_enable(TWDT_PERIPH_MODULE);
+
+        /* Deregister interrupt */
+        ESP_ERROR_CHECK(esp_intr_free(ctx->intr_handle));
+    }
+}
+
+
+esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)
+{
+    esp_err_t ret = ESP_OK;
+    twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
+
+    if (ctx == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        wdt_hal_write_protect_disable(&ctx->hal);
+        wdt_hal_feed(&ctx->hal);
+        wdt_hal_write_protect_enable(&ctx->hal);
+    }
+
+    return ret;
+}
+
+
+void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)
+{
+    twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
+
+    if (ctx != NULL) {
+        /* Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset) */
+        wdt_hal_write_protect_disable(&ctx->hal);
+        wdt_hal_handle_intr(&ctx->hal);  // Feeds WDT and clears acknowledges interrupt
+        wdt_hal_write_protect_enable(&ctx->hal);
+    }
+}
+
+
+esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)
+{
+    esp_err_t ret = ESP_OK;
+    twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
+
+    if (ctx == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        wdt_hal_write_protect_disable(&ctx->hal);
+        wdt_hal_disable(&ctx->hal);
+        wdt_hal_write_protect_enable(&ctx->hal);
+    }
+
+    return ret;
+}
+
+
+esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)
+{
+    esp_err_t ret = ESP_OK;
+    twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
+
+    if (ctx == NULL) {
+        ret = ESP_ERR_INVALID_STATE;
+    }
+
+    if (ret == ESP_OK) {
+        wdt_hal_write_protect_disable(&ctx->hal);
+        wdt_hal_enable(&ctx->hal);
+        wdt_hal_feed(&ctx->hal);
+        wdt_hal_write_protect_enable(&ctx->hal);
+    }
+
+    return ret;
+}

+ 54 - 0
components/esp_system/test/test_task_wdt.c

@@ -40,6 +40,60 @@ TEST_CASE("Task WDT task timeout", "[task_wdt]")
     TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
 }
 
+TEST_CASE("Task WDT inactive when no task to watch", "[task_wdt]")
+{
+    /* Make sure a timeout is NOT trigger when we have no task to watch */
+    timeout_flag = false;
+    esp_task_wdt_config_t twdt_config = {
+        .timeout_ms = TASK_WDT_TIMEOUT_MS,
+        .idle_core_mask = 0,
+        .trigger_panic = false,
+    };
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_init(&twdt_config));
+    esp_rom_delay_us(2 * TASK_WDT_TIMEOUT_MS * 1000);
+    TEST_ASSERT_EQUAL(false, timeout_flag);
+    /* Add a task to watch, it should start the watchdog */
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add(NULL));
+    esp_rom_delay_us(TASK_WDT_TIMEOUT_MS * 1000);
+    TEST_ASSERT_EQUAL(true, timeout_flag);
+    /* Remove the task we just addded and make sure the WDT is stopped*/
+    timeout_flag = false;
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete(NULL));
+    esp_rom_delay_us(2 * TASK_WDT_TIMEOUT_MS * 1000);
+    TEST_ASSERT_EQUAL(false, timeout_flag);
+    /* Success, terminate the test */
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
+}
+
+TEST_CASE("Task WDT can be reconfigured", "[task_wdt]")
+{
+    /* Make sure a timeout is NOT trigger when we have no task to watch */
+    timeout_flag = false;
+    esp_task_wdt_config_t twdt_config = {
+        .timeout_ms = TASK_WDT_TIMEOUT_MS / 2,
+        .idle_core_mask = 0,
+        .trigger_panic = false,
+    };
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_init(&twdt_config));
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add(NULL));
+    /* Timer started, check that a timeout is raised after a while */
+    esp_rom_delay_us((TASK_WDT_TIMEOUT_MS / 2 + 1) * 1000);
+    TEST_ASSERT_EQUAL(true, timeout_flag);
+    /* Reconfigure the timer with a bigger timeout. The timer is restarted
+     * after reconfiguring it. */
+    twdt_config.timeout_ms = TASK_WDT_TIMEOUT_MS;
+    timeout_flag = false;
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_reconfigure(&twdt_config));
+    esp_rom_delay_us((TASK_WDT_TIMEOUT_MS / 2 + 1) * 1000);
+    TEST_ASSERT_EQUAL(false, timeout_flag);
+    /* Should be triggered now, we've spent TASK_WDT_TIMEOUT_MS waiting */
+    esp_rom_delay_us((TASK_WDT_TIMEOUT_MS / 2 + 1) * 1000);
+    TEST_ASSERT_EQUAL(true, timeout_flag);
+    /* Success, terminate the test */
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete(NULL));
+    TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
+}
+
 TEST_CASE("Task WDT task feed", "[task_wdt]")
 {
     timeout_flag = false;

+ 43 - 0
components/esp_timer/test/test_esp_timer.c

@@ -866,6 +866,49 @@ TEST_CASE("Test a latency between a call of callback and real event", "[esp_time
     TEST_ESP_OK(esp_timer_delete(periodic_timer));
 }
 
+static void test_periodic_timer_feed(void* timer1_fed)
+{
+    *((int*) timer1_fed) = 1;
+}
+
+/**
+ * Feed function is not part of the esp_timer header file: it's a public in the sense that it is not static,
+ * but it is only meant to be used in IDF components.
+ */
+esp_err_t esp_timer_feed(esp_timer_handle_t timer);
+
+TEST_CASE("periodic esp_timer can be fed", "[esp_timer]")
+{
+    const int delay_ms = 100;
+    int timer_fed = 0;
+    esp_timer_handle_t timer1;
+    esp_timer_create_args_t create_args = {
+            .callback = &test_periodic_timer_feed,
+            .arg = &timer_fed,
+            .name = "timer1",
+    };
+    TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
+    TEST_ESP_OK(esp_timer_start_periodic(timer1, delay_ms * 1000));
+    /* Sleep for delay_ms/2 and feed the timer */
+    vTaskDelay((delay_ms / 2) * portTICK_PERIOD_MS);
+    /* Check that the alarm was not triggered */
+    TEST_ASSERT_EQUAL(0, timer_fed);
+    /* Reaching this point, the timer will be triggered in delay_ms/2.
+     * Let's feed the timer now. */
+    TEST_ESP_OK(esp_timer_feed(timer1));
+    /* Sleep for a bit more than delay_ms/2 */
+    vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
+    /* If the alarm was triggered, feed didn't work */
+    TEST_ASSERT_EQUAL(0, timer_fed);
+    /* Else, wait for another delay_ms/2, which should trigger the alarm */
+    vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
+    TEST_ASSERT_EQUAL(1, timer_fed);
+
+    TEST_ESP_OK( esp_timer_stop(timer1) );
+    TEST_ESP_OK( esp_timer_delete(timer1) );
+}
+
+
 #ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
 static int64_t old_time[2];