From 0aa384d40c690aeb4fa9a05493d5f7940dcc371c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 12:57:59 +0300 Subject: [PATCH] esp32: add API to get reset reason --- components/esp32/brownout.c | 3 +- components/esp32/esp_system_internal.h | 56 ++++++++++++ components/esp32/include/esp_system.h | 36 +++++--- components/esp32/include/rom/rtc.h | 1 + components/esp32/panic.c | 27 +++++- components/esp32/reset_reason.c | 116 +++++++++++++++++++++++++ components/esp32/system_api.c | 1 + components/esp32/task_wdt.c | 2 + 8 files changed, 229 insertions(+), 13 deletions(-) create mode 100644 components/esp32/esp_system_internal.h create mode 100644 components/esp32/reset_reason.c diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c index 50d8ac0442..1d78006140 100644 --- a/components/esp32/brownout.c +++ b/components/esp32/brownout.c @@ -21,7 +21,7 @@ #include "soc/cpu.h" #include "soc/rtc_cntl_reg.h" #include "rom/ets_sys.h" -#include "esp_system.h" +#include "esp_system_internal.h" #include "driver/rtc_cntl.h" #include "freertos/FreeRTOS.h" @@ -42,6 +42,7 @@ static void rtc_brownout_isr_handler() * at the same time as the following ets_printf. */ esp_cpu_stall(!xPortGetCoreID()); + esp_reset_reason_set_hint(ESP_RST_BROWNOUT); ets_printf("\r\nBrownout detector was triggered\r\n\r\n"); esp_restart_noos(); } diff --git a/components/esp32/esp_system_internal.h b/components/esp32/esp_system_internal.h new file mode 100644 index 0000000000..5bda73fb7a --- /dev/null +++ b/components/esp32/esp_system_internal.h @@ -0,0 +1,56 @@ +// 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_system.h" + +/** + * @brief Internal function to restart PRO and APP CPUs. + * + * @note This function should not be called from FreeRTOS applications. + * Use esp_restart instead. + * + * This is an internal function called by esp_restart. It is called directly + * by the panic handler and brownout detector interrupt. + */ +void esp_restart_noos() __attribute__ ((noreturn)); + +/** + * @brief Internal function to set reset reason hint + * + * The hint is used do distinguish different reset reasons when software reset + * is performed. + * + * The hint is stored in RTC store register, RTC_RESET_CAUSE_REG. + * + * @param hint Desired esp_reset_reason_t value for the real reset reason + */ +void esp_reset_reason_set_hint(esp_reset_reason_t hint); + +/** + * @brief Internal function to get the reset hint value + * @return - Reset hint value previously stored into RTC_RESET_CAUSE_REG using + * esp_reset_reason_set_hint function + * - ESP_RST_UNKNOWN if the value in RTC_RESET_CAUSE_REG is invalid + */ +esp_reset_reason_t esp_reset_reason_get_hint(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp32/include/esp_system.h b/components/esp32/include/esp_system.h index 0d57d84d84..01feea32d2 100644 --- a/components/esp32/include/esp_system.h +++ b/components/esp32/include/esp_system.h @@ -35,6 +35,25 @@ typedef enum { #define FOUR_UNIVERSAL_MAC_ADDR 4 #define UNIVERSAL_MAC_ADDR_NUM CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS + +/** + * @brief Reset reasons + */ +typedef enum { + ESP_RST_UNKNOWN, //!< Reset reason can not be determined + ESP_RST_POWERON, //!< Reset due to power-on event + ESP_RST_EXT, //!< Reset by external pin (not applicable for ESP32) + ESP_RST_SW, //!< Software reset via esp_restart + ESP_RST_PANIC, //!< Software reset due to exception/panic + ESP_RST_INT_WDT, //!< Reset (software or hardware) due to interrupt watchdog + ESP_RST_TASK_WDT, //!< Reset due to task watchdog + ESP_RST_WDT, //!< Reset due to other watchdogs + ESP_RST_DEEPSLEEP, //!< Reset after exiting deep sleep mode + ESP_RST_BROWNOUT, //!< Brownout reset (software or hardware) + ESP_RST_SDIO, //!< Reset over SDIO +} esp_reset_reason_t; + +/** @cond */ /** * @attention application don't need to call this function anymore. It do nothing and will * be removed in future version. @@ -68,17 +87,6 @@ esp_err_t esp_register_shutdown_handler(shutdown_handler_t handle); */ void esp_restart(void) __attribute__ ((noreturn)); -/** - * @brief Internal function to restart PRO and APP CPUs. - * - * @note This function should not be called from FreeRTOS applications. - * Use esp_restart instead. - * - * This is an internal function called by esp_restart. It is called directly - * by the panic handler and brownout detector interrupt. - */ -void esp_restart_noos() __attribute__ ((noreturn)); - /** * @brief Restart system. * @@ -87,6 +95,12 @@ void esp_restart_noos() __attribute__ ((noreturn)); */ void system_restart(void) __attribute__ ((deprecated, noreturn)); +/** + * @brief Get reason of last reset + * @return See description of esp_reset_reason_t for explanation of each value. + */ +esp_reset_reason_t esp_reset_reason(void); + /** * @brief Get system time, unit: microsecond. * diff --git a/components/esp32/include/rom/rtc.h b/components/esp32/include/rom/rtc.h index 08d8ace094..9ebd1020b5 100644 --- a/components/esp32/include/rom/rtc.h +++ b/components/esp32/include/rom/rtc.h @@ -68,6 +68,7 @@ extern "C" { #define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG #define RTC_APB_FREQ_REG RTC_CNTL_STORE5_REG #define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG +#define RTC_RESET_CAUSE_REG RTC_CNTL_STORE6_REG #define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 5a000574be..e8640c0256 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -40,7 +40,7 @@ #include "esp_spi_flash.h" #include "esp_cache_err_int.h" #include "esp_app_trace.h" -#include "esp_system.h" +#include "esp_system_internal.h" #include "sdkconfig.h" #if CONFIG_SYSVIEW_ENABLE #include "SEGGER_RTT.h" @@ -121,6 +121,20 @@ void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, s abort(); } +/* These two weak stubs for esp_reset_reason_{get,set}_hint are used when + * the application does not call esp_reset_reason() function, and + * reset_reason.c is not linked into the output file. + */ +void __attribute__((weak)) esp_reset_reason_set_hint(esp_reset_reason_t hint) +{ +} + +esp_reset_reason_t __attribute__((weak)) esp_reset_reason_get_hint(void) +{ + return ESP_RST_UNKNOWN; +} + + static bool abort_called; static __attribute__((noreturn)) inline void invoke_abort() @@ -147,6 +161,12 @@ void abort() #if !CONFIG_ESP32_PANIC_SILENT_REBOOT ets_printf("abort() was called at PC 0x%08x on core %d\r\n", (intptr_t)__builtin_return_address(0) - 3, xPortGetCoreID()); #endif + /* Calling code might have set other reset reason hint (such as Task WDT), + * don't overwrite that. + */ + if (esp_reset_reason_get_hint() == ESP_RST_UNKNOWN) { + esp_reset_reason_set_hint(ESP_RST_PANIC); + } invoke_abort(); } @@ -234,6 +254,10 @@ void panicHandler(XtExcFrame *frame) } #endif //!CONFIG_FREERTOS_UNICORE + if (frame->exccause == PANIC_RSN_INTWDT_CPU0 || frame->exccause == PANIC_RSN_INTWDT_CPU1) { + esp_reset_reason_set_hint(ESP_RST_INT_WDT); + } + haltOtherCore(); esp_dport_access_int_abort(); panicPutStr("Guru Meditation Error: Core "); @@ -333,6 +357,7 @@ void xt_unhandled_exception(XtExcFrame *frame) return; } panicPutStr(". Exception was unhandled.\r\n"); + esp_reset_reason_set_hint(ESP_RST_PANIC); } commonErrorHandler(frame); } diff --git a/components/esp32/reset_reason.c b/components/esp32/reset_reason.c new file mode 100644 index 0000000000..191271df0f --- /dev/null +++ b/components/esp32/reset_reason.c @@ -0,0 +1,116 @@ +// 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. + +#include "esp_system.h" +#include "esp_system_internal.h" +#include "rom/rtc.h" +#include "soc/rtc_cntl_reg.h" + +static esp_reset_reason_t s_reset_reason; + +static esp_reset_reason_t get_reset_reason(RESET_REASON rtc_reset_reason, esp_reset_reason_t reset_reason_hint) +{ + switch (rtc_reset_reason) { + case POWERON_RESET: + return ESP_RST_POWERON; + + /* For ESP32, ESP_RST_EXT is never returned */ + + + case SW_CPU_RESET: + case SW_RESET: + case EXT_CPU_RESET: /* unused */ + if (reset_reason_hint == ESP_RST_PANIC || + reset_reason_hint == ESP_RST_BROWNOUT || + reset_reason_hint == ESP_RST_TASK_WDT || + reset_reason_hint == ESP_RST_INT_WDT) { + return reset_reason_hint; + } + return ESP_RST_SW; + + case DEEPSLEEP_RESET: + return ESP_RST_DEEPSLEEP; + + case TG0WDT_SYS_RESET: + return ESP_RST_TASK_WDT; + + case TG1WDT_SYS_RESET: + return ESP_RST_INT_WDT; + + case OWDT_RESET: + case RTCWDT_SYS_RESET: + case RTCWDT_RTC_RESET: + case RTCWDT_CPU_RESET: /* unused */ + case TGWDT_CPU_RESET: /* unused */ + return ESP_RST_WDT; + + case RTCWDT_BROWN_OUT_RESET: /* unused */ + return ESP_RST_BROWNOUT; + + case SDIO_RESET: + return ESP_RST_SDIO; + + case INTRUSION_RESET: /* unused */ + default: + return ESP_RST_UNKNOWN; + } +} + +static void __attribute__((constructor)) esp_reset_reason_init(void) +{ + s_reset_reason = get_reset_reason(rtc_get_reset_reason(PRO_CPU_NUM), + esp_reset_reason_get_hint()); + esp_reset_reason_set_hint(ESP_RST_UNKNOWN); +} + +esp_reset_reason_t esp_reset_reason(void) +{ + return s_reset_reason; +} + +/* Reset reason hint is stored in RTC_RESET_CAUSE_REG, a.k.a. RTC_CNTL_STORE6_REG, + * a.k.a. RTC_ENTRY_ADDR_REG. It is safe to use this register both for the + * deep sleep wake stub entry address and for reset reason hint, since wake stub + * is only used for deep sleep reset, and in this case the reason provided by + * rtc_get_reset_reason is unambiguous. + * + * Same layout is used as for RTC_APB_FREQ_REG (a.k.a. RTC_CNTL_STORE5_REG): + * the value is replicated in low and high half-words. In addition to that, + * MSB is set to 1, which doesn't happen when RTC_CNTL_STORE6_REG contains + * deep sleep wake stub address. + */ + +#define RST_REASON_BIT 0x80000000 +#define RST_REASON_MASK 0x7FFF +#define RST_REASON_SHIFT 16 + +/* in IRAM, can be called from panic handler */ +void IRAM_ATTR esp_reset_reason_set_hint(esp_reset_reason_t hint) +{ + assert((hint & (~RST_REASON_MASK)) == 0); + uint32_t val = hint | (hint << RST_REASON_SHIFT) | RST_REASON_BIT; + REG_WRITE(RTC_RESET_CAUSE_REG, val); +} + +/* in IRAM, can be called from panic handler */ +esp_reset_reason_t IRAM_ATTR esp_reset_reason_get_hint(void) +{ + uint32_t reset_reason_hint = REG_READ(RTC_RESET_CAUSE_REG); + uint32_t high = (reset_reason_hint >> RST_REASON_SHIFT) & RST_REASON_MASK; + uint32_t low = reset_reason_hint & RST_REASON_MASK; + if ((reset_reason_hint & RST_REASON_BIT) == 0 || high != low) { + return ESP_RST_UNKNOWN; + } + return (esp_reset_reason_t) low; +} diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index f56bf622e2..ce111bd596 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -36,6 +36,7 @@ #include "freertos/task.h" #include "freertos/xtensa_api.h" #include "esp_heap_caps.h" +#include "esp_system_internal.h" static const char* TAG = "system_api"; diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 0e0b87c658..fb10aac05e 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -35,6 +35,7 @@ #include "driver/timer.h" #include "driver/periph_ctrl.h" #include "esp_task_wdt.h" +#include "esp_system_internal.h" //Assertion macro where, if 'cond' is false, will exit the critical section and return 'ret' #define ASSERT_EXIT_CRIT_RETURN(cond, ret) ({ \ @@ -155,6 +156,7 @@ static void task_wdt_isr(void *arg) if (twdt_config->panic){ //Trigger Panic if configured to do so ets_printf("Aborting.\n"); portEXIT_CRITICAL(&twdt_spinlock); + esp_reset_reason_set_hint(ESP_RST_TASK_WDT); abort(); } -- 2.50.1