esp_system: Tidy up INT WDT

This commit tidys the INT WDT code formatting, comments, and API descriptions.
This commit is contained in:
Darian Leung 2022-05-05 17:04:59 +08:00
parent 7dc6fd9ff6
commit bd8b03888c
4 changed files with 89 additions and 136 deletions

View File

@ -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

View File

@ -6,35 +6,31 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#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

View File

@ -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. */

View File

@ -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 <xtensa/coreasm.h>
@ -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. */