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)
{
setup_values();
esp_task_wdt_init(1, true);
esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0));
esp_task_wdt_config_t twdt_config = {
.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);
}

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

View File

@ -1,8 +1,11 @@
# Task Watchdog Example
This test code shows how to initialize the task watchdog, add tasks to the
watchdog task list, feeding the tasks, deleting tasks from the watchdog task
list, and deinitializing the task watchdog.
The following example demonstrates how to use the following features of the task watchdog timer (TWDT):
- 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
@ -15,7 +18,7 @@ Before project configuration and build, be sure to set the correct chip target u
### 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
@ -29,31 +32,33 @@ See the [ESP-IDF Getting Started Guide](https://idf.espressif.com/) for all the
## Example Output
As you run the example, you will see the following log:
With `esp_task_wdt_reset()`:
When the example runs normally, the following output will be observed:
```
I (316) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Initialize TWDT
TWDT initialized
Subscribed to TWDT
Delay for 10 seconds
Unsubscribing and deleting tasks
Complete
Unsubscribed from TWDT
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.
Initialize TWDT
TWDT initialized
Subscribed to TWDT
Delay for 10 seconds
E (6316) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (6316) task_wdt: - reset task (CPU 0)
E (6316) task_wdt: - reset task (CPU 1)
E (6316) task_wdt: Tasks currently running:
E (6316) task_wdt: CPU 0: IDLE
E (6316) task_wdt: CPU 1: IDLE
E (6316) task_wdt: Print CPU 0 (current core) backtrace
E (6326) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (6326) task_wdt: - task (CPU 0)
E (6326) task_wdt: Tasks currently running:
E (6326) task_wdt: CPU 0: IDLE
E (6326) task_wdt: CPU 1: IDLE
E (6326) task_wdt: Print CPU 0 (current core) backtrace
```
## Troubleshooting

View File

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

View File

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