From bd8b03888c10d5206b03f94db4e6ac7946c5eae3 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Thu, 5 May 2022 17:04:59 +0800 Subject: [PATCH] esp_system: Tidy up INT WDT This commit tidys the INT WDT code formatting, comments, and API descriptions. --- .../include/esp_private/esp_int_wdt.h | 52 ++---- components/esp_system/int_wdt.c | 151 ++++++++---------- .../esp_system/port/soc/esp32/highint_hdl.S | 2 +- .../esp_system/port/soc/esp32s3/highint_hdl.S | 20 +-- 4 files changed, 89 insertions(+), 136 deletions(-) diff --git a/components/esp_system/include/esp_private/esp_int_wdt.h b/components/esp_system/include/esp_private/esp_int_wdt.h index 105c0ec280..38cd30bb7b 100644 --- a/components/esp_system/include/esp_private/esp_int_wdt.h +++ b/components/esp_system/include/esp_private/esp_int_wdt.h @@ -4,56 +4,30 @@ * SPDX-License-Identifier: Apache-2.0 */ -#ifndef __ESP_INT_WDT_H -#define __ESP_INT_WDT_H +#pragma once #ifdef __cplusplus extern "C" { #endif -/** @addtogroup Watchdog_APIs - * @{ - */ - -/* -This routine enables a watchdog to catch instances of processes disabling -interrupts for too long, or code within interrupt handlers taking too long. -It does this by setting up a watchdog which gets fed from the FreeRTOS -task switch interrupt. When this watchdog times out, initially it will call -a high-level interrupt routine that will panic FreeRTOS in order to allow -for forensic examination of the state of the both CPUs. When this interrupt -handler is not called and the watchdog times out a second time, it will -reset the SoC. - -This uses the TIMERG1 WDT. -*/ - - /** - * @brief Initialize the non-CPU-specific parts of interrupt watchdog. - * This is called in the init code if the interrupt watchdog - * is enabled in menuconfig. - * - */ + * @brief Initialize the non-CPU-specific parts of interrupt watchdog. + * + * This function is automatically called during application startup if the + * interrupt watchdog is enabled in menuconfig. + */ void esp_int_wdt_init(void); /** - * @brief Enable the interrupt watchdog on the current CPU. This is called - * in the init code by both CPUs if the interrupt watchdog is enabled - * in menuconfig. - * - */ + * @brief Enable the interrupt watchdog on the current CPU. + * + * This function is automatically called during application startup for each CPU + * that has enabled the interrupt watchdog in menuconfig. + * + * @note esp_int_wdt_init() must be called first before calling this function + */ void esp_int_wdt_cpu_init(void); - - -/** - * @} - */ - - #ifdef __cplusplus } #endif - -#endif diff --git a/components/esp_system/int_wdt.c b/components/esp_system/int_wdt.c index 4b794219d5..3e95dc02af 100644 --- a/components/esp_system/int_wdt.c +++ b/components/esp_system/int_wdt.c @@ -6,35 +6,31 @@ #include #include -#include #include #include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "hal/cpu_hal.h" +#include "hal/wdt_hal.h" +#include "hal/interrupt_controller_hal.h" #include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_types.h" #include "esp_err.h" -#include "esp_intr_alloc.h" #include "esp_attr.h" #include "esp_log.h" +#include "esp_intr_alloc.h" #include "esp_chip_info.h" #include "esp_freertos_hooks.h" -#include "soc/timer_periph.h" #include "esp_private/periph_ctrl.h" #include "esp_private/esp_int_wdt.h" #include "esp_private/system_internal.h" -#include "hal/cpu_hal.h" -#include "hal/timer_types.h" -#include "hal/wdt_hal.h" -#include "hal/interrupt_controller_hal.h" #if CONFIG_ESP_INT_WDT -#define WDT_INT_NUM ETS_T1_WDT_INUM - +#define WDT_INT_NUM ETS_T1_WDT_INUM #define IWDT_INSTANCE WDT_MWDT1 -#define IWDT_PRESCALER MWDT1_TICK_PRESCALER //Tick period of 500us if WDT source clock is 80MHz +#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_INITIAL_TIMEOUT_S 5 + static wdt_hal_context_t iwdt_context; #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX @@ -43,122 +39,113 @@ static wdt_hal_context_t iwdt_context; * identify the live lock. */ #define IWDT_LIVELOCK_TIMEOUT_MS (20) - extern uint32_t _lx_intr_livelock_counter, _lx_intr_livelock_max; #endif -//Take care: the tick hook can also be called before esp_int_wdt_init() is called. #if CONFIG_ESP_INT_WDT_CHECK_CPU1 -//Not static; the ISR assembly checks this. -bool int_wdt_app_cpu_ticked = false; +volatile bool int_wdt_cpu1_ticked = false; +#endif static void IRAM_ATTR tick_hook(void) { +#if CONFIG_ESP_INT_WDT_CHECK_CPU1 if (cpu_hal_get_core_id() != 0) { - int_wdt_app_cpu_ticked = true; + int_wdt_cpu1_ticked = true; } else { - //Only feed wdt if app cpu also ticked. - if (int_wdt_app_cpu_ticked) { - //Todo: Check if there's a way to avoid reconfiguring the stages on each feed. + // Only feed wdt if app cpu also ticked. + if (int_wdt_cpu1_ticked) { + // Todo: Check if there's a way to avoid reconfiguring the stages on each feed. wdt_hal_write_protect_disable(&iwdt_context); - //Reconfigure stage timeouts + // Reconfigure stage timeouts #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX _lx_intr_livelock_counter = 0; wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, - CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US / (_lx_intr_livelock_max + 1), WDT_STAGE_ACTION_INT); //Set timeout before interrupt + CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US / (_lx_intr_livelock_max + 1), WDT_STAGE_ACTION_INT); // Set timeout before interrupt #else - wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); //Set timeout before interrupt + wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); // Set timeout before interrupt #endif - wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); //Set timeout before reset + wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); // Set timeout before reset wdt_hal_feed(&iwdt_context); wdt_hal_write_protect_enable(&iwdt_context); - int_wdt_app_cpu_ticked = false; + int_wdt_cpu1_ticked = false; } } -} -#else -static void IRAM_ATTR tick_hook(void) -{ -#if !CONFIG_FREERTOS_UNICORE +#else // CONFIG_ESP_INT_WDT_CHECK_CPU1 if (cpu_hal_get_core_id() != 0) { return; + } else { + // Todo: Check if there's a way to avoid reconfiguring the stages on each feed. + wdt_hal_write_protect_disable(&iwdt_context); + // Reconfigure stage timeouts + wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); // Set timeout before interrupt + wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); // Set timeout before reset + wdt_hal_feed(&iwdt_context); + wdt_hal_write_protect_enable(&iwdt_context); } -#endif - //Todo: Check if there's a way to avoid reconfiguring the stages on each feed. - wdt_hal_write_protect_disable(&iwdt_context); - //Reconfigure stage timeouts - wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); //Set timeout before interrupt - wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); //Set timeout before reset - wdt_hal_feed(&iwdt_context); - wdt_hal_write_protect_enable(&iwdt_context); +#endif // CONFIG_ESP_INT_WDT_CHECK_CPU1 } -#endif - void esp_int_wdt_init(void) { periph_module_enable(PERIPH_TIMG1_MODULE); - //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets - //it to their actual value. + /* + * Initialize the WDT timeout stages. Note that the initial timeout is set to 5 seconds as variable startup times of + * each CPU can lead to a timeout. The tick hooks will set the WDT timers to the actual timeout. + * Todo: Fix this + */ wdt_hal_init(&iwdt_context, IWDT_INSTANCE, IWDT_PRESCALER, true); wdt_hal_write_protect_disable(&iwdt_context); - //1st stage timeout: interrupt wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); - //2nd stage timeout: reset system wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); - //Enable WDT wdt_hal_enable(&iwdt_context); wdt_hal_write_protect_enable(&iwdt_context); - #if (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI) + #define APB_DCRSET (0x200c) + #define APB_ITCTRL (0x3f00) + #define ERI_ADDR(APB) (0x100000 + (APB)) + #define _SYM2STR(x) # x + #define SYM2STR(x) _SYM2STR(x) -#define APB_DCRSET (0x200c) -#define APB_ITCTRL (0x3f00) - -#define ERI_ADDR(APB) (0x100000 + (APB)) - -#define _SYM2STR(x) # x -#define SYM2STR(x) _SYM2STR(x) uint32_t eriadrs, scratch = 0, immediate = 0; if (soc_has_cache_lock_bug()) { if (xPortGetCoreID() != CONFIG_BTDM_CTRL_PINNED_TO_CORE) { __asm__ __volatile__ ( - /* Enable Xtensa Debug Module Integration Mode */ - "movi %[ERI], " SYM2STR(ERI_ADDR(APB_ITCTRL)) "\n" - "rer %[REG], %[ERI]\n" - "movi %[IMM], 1\n" - "or %[REG], %[IMM], %[REG]\n" - "wer %[REG], %[ERI]\n" - /* Enable Xtensa Debug Module BreakIn signal */ - "movi %[ERI], " SYM2STR(ERI_ADDR(APB_DCRSET)) "\n" - "rer %[REG], %[ERI]\n" - "movi %[IMM], 0x10000\n" - "or %[REG], %[IMM], %[REG]\n" - "wer %[REG], %[ERI]\n" - : [ERI] "=r" (eriadrs), [REG] "+r" (scratch), [IMM] "+r" (immediate) - ); + /* Enable Xtensa Debug Module Integration Mode */ + "movi %[ERI], " SYM2STR(ERI_ADDR(APB_ITCTRL)) "\n" + "rer %[REG], %[ERI]\n" + "movi %[IMM], 1\n" + "or %[REG], %[IMM], %[REG]\n" + "wer %[REG], %[ERI]\n" + /* Enable Xtensa Debug Module BreakIn signal */ + "movi %[ERI], " SYM2STR(ERI_ADDR(APB_DCRSET)) "\n" + "rer %[REG], %[ERI]\n" + "movi %[IMM], 0x10000\n" + "or %[REG], %[IMM], %[REG]\n" + "wer %[REG], %[ERI]\n" + : [ERI] "=r" (eriadrs), [REG] "+r" (scratch), [IMM] "+r" (immediate) + ); } } -#endif +#endif // (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI) } void esp_int_wdt_cpu_init(void) { - assert((CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (portTICK_PERIOD_MS << 1)) && "Interrupt watchdog timeout needs to meet twice the RTOS tick period!"); - esp_register_freertos_tick_hook_for_cpu(tick_hook, cpu_hal_get_core_id()); - ESP_INTR_DISABLE(WDT_INT_NUM); - #if SOC_TIMER_GROUPS > 1 + assert((CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (portTICK_PERIOD_MS << 1)) && "Interrupt watchdog timeout needs to be at least twice the RTOS tick period!"); + // Register tick hook for current CPU to feed the INT WDT + esp_register_freertos_tick_hook_for_cpu(tick_hook, cpu_hal_get_core_id()); + /* + * Register INT WDT interrupt for current CPU. We do this manually as the timeout interrupt should call an assembly + * panic handler (see riscv/vector.S and xtensa_vectors.S). + */ + esp_intr_disable_source(WDT_INT_NUM); esp_rom_route_intr_matrix(cpu_hal_get_core_id(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); -#endif - - /* Set the type and priority to watch dog interrupts */ #if SOC_CPU_HAS_FLEXIBLE_INTC interrupt_controller_hal_set_int_type(WDT_INT_NUM, INTR_TYPE_LEVEL); interrupt_controller_hal_set_int_level(WDT_INT_NUM, SOC_INTERRUPT_LEVEL_MEDIUM); #endif - #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX /* * This is a workaround for issue 3.15 in "ESP32 ECO and workarounds for @@ -171,11 +158,11 @@ void esp_int_wdt_cpu_init(void) _lx_intr_livelock_max = CONFIG_ESP_INT_WDT_TIMEOUT_MS / IWDT_LIVELOCK_TIMEOUT_MS - 1; } #endif - - // We do not register a handler for the watchdog interrupt because: - // 1. Interrupt level 4 on Xtensa architecture is not servicable from C - // 2. Instead, we set the entry of watchdog interrupt to the panic handler, see riscv/vector.S and xtensa_vectors.S - ESP_INTR_ENABLE(WDT_INT_NUM); + esp_intr_enable_source(WDT_INT_NUM); +#else // SOC_TIMER_GROUPS > 1 + // TODO: Clean up code for ESP32-C2, IDF-4114 + ESP_EARLY_LOGW("INT_WDT", "ESP32-C2 only has one timer group"); +#endif // SOC_TIMER_GROUPS > 1 } -#endif +#endif // CONFIG_ESP_INT_WDT diff --git a/components/esp_system/port/soc/esp32/highint_hdl.S b/components/esp_system/port/soc/esp32/highint_hdl.S index a6c713e331..a07850ae87 100644 --- a/components/esp_system/port/soc/esp32/highint_hdl.S +++ b/components/esp_system/port/soc/esp32/highint_hdl.S @@ -259,7 +259,7 @@ xt_highintx: #endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */ #if CONFIG_ESP_INT_WDT_CHECK_CPU1 /* Check if the cause is the app cpu failing to tick.*/ - movi a0, int_wdt_app_cpu_ticked + movi a0, int_wdt_cpu1_ticked l32i a0, a0, 0 bnez a0, 2f /* It is. Modify cause. */ diff --git a/components/esp_system/port/soc/esp32s3/highint_hdl.S b/components/esp_system/port/soc/esp32s3/highint_hdl.S index 12c01adf76..78a5c711a3 100644 --- a/components/esp_system/port/soc/esp32s3/highint_hdl.S +++ b/components/esp_system/port/soc/esp32s3/highint_hdl.S @@ -1,16 +1,8 @@ -// Copyright 2015-2020 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: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include @@ -92,7 +84,7 @@ xt_highint4: 1: #if CONFIG_ESP_INT_WDT_CHECK_CPU1 /* Check if the cause is the app cpu failing to tick.*/ - movi a0, int_wdt_app_cpu_ticked + movi a0, int_wdt_cpu1_ticked l32i a0, a0, 0 bnez a0, 2f /* It is. Modify cause. */