esp_system: Update task watchdog unit tests and example

This commit does the following:

- Update existing unit tests that use the TWDT to call the new
  esp_task_wdt_init() API
- Add a set of dedicate TWDT unit tests
- Updates the TWDT example
This commit is contained in:
Darian Leung 2022-04-14 18:56:02 +08:00
parent 5953bca376
commit 7c02bde904
6 changed files with 209 additions and 88 deletions

View File

@ -216,8 +216,12 @@ TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_INT_WDT after interrupt watchdog
static void do_task_wdt(void) static void do_task_wdt(void)
{ {
setup_values(); setup_values();
esp_task_wdt_init(1, true); esp_task_wdt_config_t twdt_config = {
esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0)); .timeout_ms = 1000,
.idle_core_mask = (1 << 0), // Watch core 0 idle
.trigger_panic = true,
};
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_init(&twdt_config));
while(1); while(1);
} }

View File

@ -0,0 +1,95 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdbool.h>
#include "unity.h"
#include "esp_rom_sys.h"
#include "esp_task_wdt.h"
#define TASK_WDT_TIMEOUT_MS 1000
static bool timeout_flag;
void esp_task_wdt_isr_user_handler(void)
{
timeout_flag = true;
}
TEST_CASE("Task WDT task timeout", "[task_wdt]")
{
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));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add(NULL));
// Short delay to allow timeout to occur
esp_rom_delay_us(TASK_WDT_TIMEOUT_MS * 1000);
TEST_ASSERT_EQUAL(true, timeout_flag);
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;
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));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add(NULL));
// Feed the watchdog after a short delay
esp_rom_delay_us((TASK_WDT_TIMEOUT_MS * 1000) / 2);
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_reset());
esp_rom_delay_us((TASK_WDT_TIMEOUT_MS * 1000) / 2);
TEST_ASSERT_EQUAL(false, timeout_flag);
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete(NULL));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
TEST_CASE("Task WDT user timeout", "[task_wdt]")
{
const char *user_name = "test_user";
esp_task_wdt_user_handle_t user_handle;
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));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add_user(user_name, &user_handle));
// Short delay to allow timeout to occur
esp_rom_delay_us(TASK_WDT_TIMEOUT_MS * 1000);
TEST_ASSERT_EQUAL(true, timeout_flag);
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete_user(user_handle));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
TEST_CASE("Task WDT user feed", "[task_wdt]")
{
const char *user_name = "test_user";
esp_task_wdt_user_handle_t user_handle;
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));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add_user(user_name, &user_handle));
// Feed the watchdog after a short delay
esp_rom_delay_us((TASK_WDT_TIMEOUT_MS * 1000) / 2);
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_reset_user(user_handle));
esp_rom_delay_us((TASK_WDT_TIMEOUT_MS * 1000) / 2);
TEST_ASSERT_EQUAL(false, timeout_flag);
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete_user(user_handle));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}

View File

@ -545,9 +545,12 @@ TEST_CASE("mbedtls RSA Generate Key", "[mbedtls][timeout=60]")
#if CONFIG_MBEDTLS_MPI_USE_INTERRUPT #if CONFIG_MBEDTLS_MPI_USE_INTERRUPT
/* Check that generating keys doesnt starve the watchdog if interrupt-based driver is used */ /* Check that generating keys doesnt starve the watchdog if interrupt-based driver is used */
const int timeout_s = 1; esp_task_wdt_config_t twdt_config = {
esp_task_wdt_init(timeout_s, true); .timeout_ms = 1000,
esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0)); .idle_core_mask = (1 << 0), // Watch core 0 idle
.trigger_panic = true,
};
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_init(&twdt_config));
#endif //CONFIG_MBEDTLS_MPI_USE_INTERRUPT #endif //CONFIG_MBEDTLS_MPI_USE_INTERRUPT
mbedtls_rsa_init(&ctx); mbedtls_rsa_init(&ctx);
@ -563,8 +566,7 @@ TEST_CASE("mbedtls RSA Generate Key", "[mbedtls][timeout=60]")
mbedtls_entropy_free(&entropy); mbedtls_entropy_free(&entropy);
#if CONFIG_MBEDTLS_MPI_USE_INTERRUPT #if CONFIG_MBEDTLS_MPI_USE_INTERRUPT
esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
esp_task_wdt_deinit();
#endif //CONFIG_MBEDTLS_MPI_USE_INTERRUPT #endif //CONFIG_MBEDTLS_MPI_USE_INTERRUPT
} }

View File

@ -1,8 +1,11 @@
# Task Watchdog Example # Task Watchdog Example
This test code shows how to initialize the task watchdog, add tasks to the The following example demonstrates how to use the following features of the task watchdog timer (TWDT):
watchdog task list, feeding the tasks, deleting tasks from the watchdog task
list, and deinitializing the task watchdog. - How to initialize and deinitialize the TWDT
- How to subscribe and unsubscribe tasks to the TWDT
- How to subscribe and unsubscribe users to the TWDT
- How to tasks and users can reset (i.e., feed) the TWDT
## How to use example ## How to use example
@ -15,7 +18,7 @@ Before project configuration and build, be sure to set the correct chip target u
### Configure the project ### Configure the project
Program should run without error. Comment out `esp_task_wdt_reset()` to observe a watchdog timeout. Program should run correctly without needing any special configuration. However, users can disable `CONFIG_ESP_TASK_WDT` which will prevent the TWDT from being automatically initialized on startup. If disabled, the example will manually initialize the TWDT.
### Build and Flash ### Build and Flash
@ -29,31 +32,33 @@ See the [ESP-IDF Getting Started Guide](https://idf.espressif.com/) for all the
## Example Output ## Example Output
As you run the example, you will see the following log: When the example runs normally, the following output will be observed:
With `esp_task_wdt_reset()`:
``` ```
I (316) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU. I (0) cpu_start: Starting scheduler on APP CPU.
Initialize TWDT TWDT initialized
Subscribed to TWDT
Delay for 10 seconds Delay for 10 seconds
Unsubscribing and deleting tasks Unsubscribed from TWDT
Complete TWDT deinitialized
Example complete
``` ```
Without `esp_task_wdt_reset()`: Users can comment out any of the `esp_task_wdt_reset()` or `esp_task_wdt_reset_user()` calls to trigger the TWDT, which in turn will result in the following output:
``` ```
I (316) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU. I (0) cpu_start: Starting scheduler on APP CPU.
Initialize TWDT TWDT initialized
Subscribed to TWDT
Delay for 10 seconds Delay for 10 seconds
E (6316) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time: E (6326) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (6316) task_wdt: - reset task (CPU 0) E (6326) task_wdt: - task (CPU 0)
E (6316) task_wdt: - reset task (CPU 1) E (6326) task_wdt: Tasks currently running:
E (6316) task_wdt: Tasks currently running: E (6326) task_wdt: CPU 0: IDLE
E (6316) task_wdt: CPU 0: IDLE E (6326) task_wdt: CPU 1: IDLE
E (6316) task_wdt: CPU 1: IDLE E (6326) task_wdt: Print CPU 0 (current core) backtrace
E (6316) task_wdt: Print CPU 0 (current core) backtrace
``` ```
## Troubleshooting ## Troubleshooting

View File

@ -6,80 +6,98 @@
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. CONDITIONS OF ANY KIND, either express or implied.
*/ */
#include "sdkconfig.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_err.h"
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#define TWDT_TIMEOUT_S 3 #define TWDT_TIMEOUT_MS 3000
#define TASK_RESET_PERIOD_S 2 #define TASK_RESET_PERIOD_MS 2000
#define MAIN_DELAY_MS 10000
/* static volatile bool run_loop;
* Macro to check the outputs of TWDT functions and trigger an abort if an static esp_task_wdt_user_handle_t func_a_twdt_user_hdl;
* incorrect code is returned. static esp_task_wdt_user_handle_t func_b_twdt_user_hdl;
*/
#define CHECK_ERROR_CODE(returned, expected) ({ \
if(returned != expected){ \
printf("TWDT ERROR\n"); \
abort(); \
} \
})
static TaskHandle_t task_handles[portNUM_PROCESSORS]; static void func_a(void)
//Callback for user tasks created in app_main()
void reset_task(void *arg)
{ {
//Subscribe this task to TWDT, then check if it is subscribed esp_task_wdt_reset_user(func_a_twdt_user_hdl);
CHECK_ERROR_CODE(esp_task_wdt_add(NULL), ESP_OK); }
CHECK_ERROR_CODE(esp_task_wdt_status(NULL), ESP_OK);
while(1){ static void func_b(void)
//reset the watchdog every 2 seconds {
CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK); //Comment this line to trigger a TWDT timeout esp_task_wdt_reset_user(func_b_twdt_user_hdl);
vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_S * 1000)); }
void task_func(void *arg)
{
// Subscribe this task to TWDT, then check if it is subscribed
ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
ESP_ERROR_CHECK(esp_task_wdt_status(NULL));
// Subscribe func_a and func_b as users of the the TWDT
ESP_ERROR_CHECK(esp_task_wdt_add_user("func_a", &func_a_twdt_user_hdl));
ESP_ERROR_CHECK(esp_task_wdt_add_user("func_b", &func_b_twdt_user_hdl));
printf("Subscribed to TWDT\n");
while (run_loop) {
// Reset the task and each user periodically
/*
Note: Comment out any one of the calls below to trigger the TWDT
*/
esp_task_wdt_reset();
func_a();
func_b();
vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_MS));
} }
// Unsubscribe this task, func_a, and func_b
ESP_ERROR_CHECK(esp_task_wdt_delete_user(func_a_twdt_user_hdl));
ESP_ERROR_CHECK(esp_task_wdt_delete_user(func_b_twdt_user_hdl));
ESP_ERROR_CHECK(esp_task_wdt_delete(NULL));
printf("Unsubscribed from TWDT\n");
// Notify main task of deletion
xTaskNotifyGive((TaskHandle_t)arg);
vTaskDelete(NULL);
} }
void app_main(void) void app_main(void)
{ {
printf("Initialize TWDT\n"); #if !CONFIG_ESP_TASK_WDT
//Initialize or reinitialize TWDT // If the TWDT was not initialized automatically on startup, manually intialize it now
CHECK_ERROR_CODE(esp_task_wdt_init(TWDT_TIMEOUT_S, false), ESP_OK); esp_task_wdt_config_t twdt_config = {
.timeout_ms = TWDT_TIMEOUT_MS,
.idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores
.trigger_panic = false,
};
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
printf("TWDT initialized\n");
#endif // CONFIG_ESP_TASK_WDT
//Subscribe Idle Tasks to TWDT if they were not subscribed at startup // Create a task
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 run_loop = true;
esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0)); xTaskCreatePinnedToCore(task_func, "task", 2048, xTaskGetCurrentTaskHandle(), 10, NULL, 0);
#endif
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 && !CONFIG_FREERTOS_UNICORE
esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(1));
#endif
//Create user tasks and add them to watchdog // Let the created task run for a while
for(int i = 0; i < portNUM_PROCESSORS; i++){ printf("Delay for %d seconds\n", MAIN_DELAY_MS/1000);
xTaskCreatePinnedToCore(reset_task, "reset task", 1024, NULL, 10, &task_handles[i], i); vTaskDelay(pdMS_TO_TICKS(MAIN_DELAY_MS));
}
printf("Delay for 10 seconds\n"); // Stop the created task
vTaskDelay(pdMS_TO_TICKS(10000)); //Delay for 10 seconds run_loop = false;
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
printf("Unsubscribing and deleting tasks\n"); #if !CONFIG_ESP_TASK_WDT
//Delete and unsubscribe Users Tasks from Task Watchdog, then unsubscribe idle task // If we manually initialized the TWDT, deintialize it now
for(int i = 0; i < portNUM_PROCESSORS; i++){ ESP_ERROR_CHECK(esp_task_wdt_deinit());
vTaskDelete(task_handles[i]); //Delete user task first (prevents the resetting of an unsubscribed task) printf("TWDT deinitialized\n");
CHECK_ERROR_CODE(esp_task_wdt_delete(task_handles[i]), ESP_OK); //Unsubscribe task from TWDT #endif // CONFIG_ESP_TASK_WDT
CHECK_ERROR_CODE(esp_task_wdt_status(task_handles[i]), ESP_ERR_NOT_FOUND); //Confirm task is unsubscribed printf("Example complete\n");
//unsubscribe idle task
CHECK_ERROR_CODE(esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(i)), ESP_OK); //Unsubscribe Idle Task from TWDT
CHECK_ERROR_CODE(esp_task_wdt_status(xTaskGetIdleTaskHandleForCPU(i)), ESP_ERR_NOT_FOUND); //Confirm Idle task has unsubscribed
}
//Deinit TWDT after all tasks have unsubscribed
CHECK_ERROR_CODE(esp_task_wdt_deinit(), ESP_OK);
CHECK_ERROR_CODE(esp_task_wdt_status(NULL), ESP_ERR_INVALID_STATE); //Confirm TWDT has been deinitialized
printf("Complete\n");
} }

View File

@ -9,7 +9,4 @@ from pytest_embedded import Dut
@pytest.mark.generic @pytest.mark.generic
def test_task_watchdog(dut: Dut) -> None: def test_task_watchdog(dut: Dut) -> None:
dut.expect_exact('Initialize TWDT') dut.expect_exact('Example complete')
dut.expect_exact('Delay for 10 seconds')
dut.expect_exact('Unsubscribing and deleting tasks')
dut.expect_exact('Complete')