Skip to content

Commit

Permalink
WDT: implement interrupt wdt and task wdt for ESP32-C2
Browse files Browse the repository at this point in the history
ESP32-C2 has a single group timer, thus it will use it for the interrupt watchdog,
which is more critical than the task watchdog. The latter is implement in
software thanks to the `esp_timer`component.
  • Loading branch information
o-marshmallow committed Dec 1, 2022
1 parent b447086 commit b675bb2
Show file tree
Hide file tree
Showing 37 changed files with 500 additions and 155 deletions.
2 changes: 1 addition & 1 deletion .gitlab/ci/target-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ UT_S2_SDSPI:

UT_C2:
extends: .unit_test_esp32c2_template
parallel: 21
parallel: 22
tags:
- ESP32C2_IDF
- UT_T1_1
Expand Down
3 changes: 0 additions & 3 deletions components/esp_event/test/test_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
#include "test_utils.h"


#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
//IDF-4035
static const char* TAG = "test_event";

#define TEST_CONFIG_ITEMS_TO_REGISTER 5
Expand Down Expand Up @@ -2021,4 +2019,3 @@ TEST_CASE("can post events from interrupt handler", "[event]")
}

#endif // CONFIG_ESP_EVENT_POST_FROM_ISR
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
16 changes: 16 additions & 0 deletions components/esp_hw_support/sleep_modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "esp_private/sleep_retention.h"
#include "esp_private/esp_clk.h"
#include "esp_private/startup_internal.h"
#include "esp_private/esp_task_wdt.h"

#ifdef CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h"
Expand Down Expand Up @@ -662,6 +663,13 @@ static inline bool can_power_down_vddsdio(const uint32_t vddsdio_pd_sleep_durati

esp_err_t esp_light_sleep_start(void)
{
#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
esp_err_t timerret = ESP_OK;

/* If a task watchdog timer is running, we have to stop it. */
timerret = esp_task_wdt_stop();
#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER

s_config.ccount_ticks_record = esp_cpu_get_cycle_count();
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&light_sleep_lock);
Expand Down Expand Up @@ -820,6 +828,14 @@ esp_err_t esp_light_sleep_start(void)
}
portEXIT_CRITICAL(&light_sleep_lock);
s_config.sleep_time_overhead_out = (esp_cpu_get_cycle_count() - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL);

#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
/* Restart the Task Watchdog timer as it was stopped before sleeping. */
if (timerret == ESP_OK) {
esp_task_wdt_restart();
}
#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER

return err;
}

Expand Down
5 changes: 4 additions & 1 deletion components/esp_pm/linker.lf
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ entries:
freertos_hooks:esp_vApplicationIdleHook (noflash)
if PM_SLP_IRAM_OPT = y:
task_wdt:idle_hook_cb (noflash)
task_wdt:reset_hw_timer (noflash)
task_wdt:task_wdt_timer_feed (noflash)
task_wdt:find_entry_and_check_all_reset (noflash)
task_wdt:find_entry_from_task_handle_and_check_all_reset (noflash)
task_wdt:esp_task_wdt_reset (noflash)
Expand All @@ -42,6 +42,9 @@ entries:
archive: libesp_timer.a
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_TIMER_IMPL_TG0_LAC = y:
esp_timer_impl_lac:esp_timer_impl_lock (noflash)
esp_timer_impl_lac:esp_timer_impl_unlock (noflash)
Expand Down
5 changes: 4 additions & 1 deletion components/esp_system/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ else()
"startup.c"
"system_time.c"
"stack_check.c"
"task_wdt.c"
"ubsan.c"
"xt_wdt.c"
"debug_stubs.c")

if(CONFIG_ESP_TASK_WDT)
list(APPEND srcs "task_wdt.c")
endif()

if(CONFIG_ESP_SYSTEM_USE_EH_FRAME)
list(APPEND srcs "eh_frame_parser.c")
endif()
Expand Down
36 changes: 26 additions & 10 deletions components/esp_system/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ menu "ESP System Settings"

config ESP_INT_WDT
bool "Interrupt watchdog"
default n if IDF_TARGET_ESP32C2 # add support in IDF-4114
default y
help
This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time,
Expand All @@ -392,18 +391,35 @@ menu "ESP System Settings"
Also detect if interrupts on CPU 1 are disabled for too long.

config ESP_TASK_WDT
bool "Initialize Task Watchdog Timer on startup"
bool "Enable Task Watchdog Timer"
default y
select FREERTOS_ENABLE_TASK_SNAPSHOT
help
The Task Watchdog Timer can be used to make sure individual tasks are still
running. Enabling this option will cause the Task Watchdog Timer to be
initialized automatically at startup. The Task Watchdog timer can be
initialized after startup as well (see Task Watchdog Timer API Reference)
running. Enabling this option will enable the Task Watchdog Timer. It can be
either initialized automatically at startup or initialized after startup
(see Task Watchdog Timer API Reference)

config ESP_TASK_WDT_USE_ESP_TIMER
# Software implementation of Task Watchdog, handy for targets with only a single
# Timer Group, such as the ESP32-C2
bool
depends on ESP_TASK_WDT
default y if IDF_TARGET_ESP32C2
default n if !IDF_TARGET_ESP32C2
select ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD

config ESP_TASK_WDT_INIT
bool "Initialize Task Watchdog Timer on startup"
depends on ESP_TASK_WDT
default y
help
Enabling this option will cause the Task Watchdog Timer to be initialized
automatically at startup.

config ESP_TASK_WDT_PANIC
bool "Invoke panic handler on Task Watchdog timeout"
depends on ESP_TASK_WDT
depends on ESP_TASK_WDT_INIT
default n
help
If this option is enabled, the Task Watchdog Timer will be configured to
Expand All @@ -412,7 +428,7 @@ menu "ESP System Settings"

config ESP_TASK_WDT_TIMEOUT_S
int "Task Watchdog timeout period (seconds)"
depends on ESP_TASK_WDT
depends on ESP_TASK_WDT_INIT
range 1 60
default 5
help
Expand All @@ -421,7 +437,7 @@ menu "ESP System Settings"

config ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
bool "Watch CPU0 Idle Task"
depends on ESP_TASK_WDT
depends on ESP_TASK_WDT_INIT
default y
help
If this option is enabled, the Task Watchdog Timer will watch the CPU0
Expand All @@ -432,10 +448,10 @@ menu "ESP System Settings"

config ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
bool "Watch CPU1 Idle Task"
depends on ESP_TASK_WDT && !FREERTOS_UNICORE
depends on ESP_TASK_WDT_INIT && !FREERTOS_UNICORE
default y
help
If this option is enabled, the Task Wtachdog Timer will wach the CPU1
If this option is enabled, the Task Watchdog Timer will wach the CPU1
Idle Task.

config ESP_XT_WDT
Expand Down
4 changes: 4 additions & 0 deletions components/esp_system/crosscore_int.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,14 @@ static void IRAM_ATTR esp_crosscore_isr(void *arg) {
esp_backtrace_print(100);
}

#if CONFIG_ESP_TASK_WDT
if (my_reason_val & REASON_TWDT_ABORT) {
extern void task_wdt_timeout_abort_xtensa(bool);
/* Called from a crosscore interrupt, thus, we are not the core that received
* the TWDT interrupt, call the function with `false` as a parameter. */
task_wdt_timeout_abort_xtensa(false);
}
#endif // CONFIG_ESP_TASK_WDT
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA
}

Expand Down Expand Up @@ -171,7 +173,9 @@ void IRAM_ATTR esp_crosscore_int_send_print_backtrace(int core_id)
esp_crosscore_int_send(core_id, REASON_PRINT_BACKTRACE);
}

#if CONFIG_ESP_TASK_WDT
void IRAM_ATTR esp_crosscore_int_send_twdt_abort(int core_id) {
esp_crosscore_int_send(core_id, REASON_TWDT_ABORT);
}
#endif // CONFIG_ESP_TASK_WDT
#endif
22 changes: 22 additions & 0 deletions components/esp_system/include/esp_private/esp_int_wdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,32 @@

#pragma once

#include "system_internal.h"
#include "soc/periph_defs.h"

#ifdef __cplusplus
extern "C" {
#endif

#if SOC_TIMER_GROUPS > 1

/* If we have two hardware timer groups, use the second one for interrupt watchdog. */
#define WDT_LEVEL_INTR_SOURCE ETS_TG1_WDT_LEVEL_INTR_SOURCE
#define IWDT_PRESCALER MWDT1_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#define IWDT_TICKS_PER_US MWDT1_TICKS_PER_US
#define IWDT_INSTANCE WDT_MWDT1
#define IWDT_INITIAL_TIMEOUT_S 5

#else

#define WDT_LEVEL_INTR_SOURCE ETS_TG0_WDT_LEVEL_INTR_SOURCE
#define IWDT_PRESCALER MWDT0_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#define IWDT_TICKS_PER_US MWDT0_TICKS_PER_US
#define IWDT_INSTANCE WDT_MWDT0
#define IWDT_INITIAL_TIMEOUT_S 5

#endif // SOC_TIMER_GROUPS > 1

/**
* @brief Initialize the non-CPU-specific parts of interrupt watchdog.
*
Expand Down
46 changes: 46 additions & 0 deletions components/esp_system/include/esp_private/esp_task_wdt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include "sdkconfig.h"
#include "esp_err.h"

#if CONFIG_ESP_TASK_WDT

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Stop the Task Watchdog Timer (TWDT)
*
* This function will temporarily stop the timer until it is restarted/resumed by a call to esp_task_wdt_restart().
* @note esp_task_wdt_stop() must not be called by multiple tasks simultaneously.
* @return
* - ESP_OK: TWDT successfully stopped
* - Other: Failed to stop the TWDT
*/
esp_err_t esp_task_wdt_stop(void);

/**
* @brief Restart the Task Watchdog Timer (TWDT)
*
* This function will restart/resume the timer after it has been stopped by esp_task_wdt_stop().
* @note esp_task_wdt_restart() must not be called by multiple tasks simultaneously.
* @return
* - ESP_OK: TWDT successfully stopped
* - Other: Failed to stop the TWDT
*/
esp_err_t esp_task_wdt_restart(void);

#ifdef __cplusplus
}
#endif

#endif // CONFIG_ESP_TASK_WDT
32 changes: 19 additions & 13 deletions components/esp_system/include/esp_private/system_internal.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

Expand All @@ -20,11 +12,25 @@ extern "C" {

#include "esp_system.h"

#if !CONFIG_ESP_TASK_WDT_USE_ESP_TIMER

/* All the targets that have more than one timer group are using
* APB clock by default, which frequency is 80MHz.
* Thus, we can determine the default parameter for the prescaler here */
#define MWDT0_TICK_PRESCALER 40000
#define MWDT0_TICKS_PER_US 500
#define MWDT1_TICK_PRESCALER 40000
#define MWDT1_TICKS_PER_US 500

#else

/* The targets that have a single timer group use XTAL clock as the
* default clock. XTAL clock frequency is 40MHz. */
#define MWDT0_TICK_PRESCALER 20000
#define MWDT0_TICKS_PER_US 500

#endif

/**
* @brief Internal function to restart PRO and APP CPUs.
*
Expand Down
14 changes: 13 additions & 1 deletion components/esp_system/include/esp_task_wdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ typedef struct esp_task_wdt_user_handle_s * esp_task_wdt_user_handle_t;
* this function will update the TWDT's current configuration. This funciton will also 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().
*
* @note esp_task_wdt_init() must only be called after the scheduler is started
* @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
Expand All @@ -51,6 +52,7 @@ esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config);
* are still subscribed to the TWDT, or when the TWDT is already deinitialized, will result in an error code being
* returned.
*
* @note esp_task_wdt_deinit() must not be called by multiple tasks simultaneously.
* @return
* - ESP_OK: TWDT successfully deinitialized
* - Other: Failed to deinitialize TWDT
Expand Down Expand Up @@ -150,6 +152,16 @@ esp_err_t esp_task_wdt_delete_user(esp_task_wdt_user_handle_t user_handle);
*/
esp_err_t esp_task_wdt_status(TaskHandle_t task_handle);

/**
* @brief User ISR callback placeholder
*
* This function is called by task_wdt_isr function (ISR for when TWDT times out). It can be defined in user code to
* handle TWDT events.
*
* @note It has the same limitations as the interrupt function. Do not use ESP_LOGx functions inside.
*/
void __attribute__((weak)) esp_task_wdt_isr_user_handler(void);

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit b675bb2

Please sign in to comment.