From ea99137e6240903c7f8dcf062694975df4bb90a7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 19 Nov 2019 11:19:29 +0100 Subject: [PATCH 1/3] esp32s2beta: implement esp_reset_reason API --- components/esp32s2beta/CMakeLists.txt | 1 + components/esp32s2beta/panic.c | 25 ++++ components/esp32s2beta/reset_reason.c | 123 ++++++++++++++++++ components/esp32s2beta/task_wdt.c | 3 +- .../esp_rom/include/esp32s2beta/rom/rtc.h | 1 + 5 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 components/esp32s2beta/reset_reason.c diff --git a/components/esp32s2beta/CMakeLists.txt b/components/esp32s2beta/CMakeLists.txt index f0d46758b..1892720ed 100644 --- a/components/esp32s2beta/CMakeLists.txt +++ b/components/esp32s2beta/CMakeLists.txt @@ -26,6 +26,7 @@ else() "panic.c" "pm_esp32s2beta.c" "pm_trace.c" + "reset_reason.c" "sleep_modes.c" "spiram.c" "spiram_psram.c" diff --git a/components/esp32s2beta/panic.c b/components/esp32s2beta/panic.c index 0082e0a85..5dd20615d 100644 --- a/components/esp32s2beta/panic.c +++ b/components/esp32s2beta/panic.c @@ -123,6 +123,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(void) @@ -149,6 +163,12 @@ void abort(void) #if !CONFIG_ESP32S2_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(); } @@ -326,6 +346,10 @@ void panicHandler(XtExcFrame *frame) } #endif //!CONFIG_FREERTOS_UNICORE + if (frame->exccause == PANIC_RSN_INTWDT_CPU0) { + esp_reset_reason_set_hint(ESP_RST_INT_WDT); + } + haltOtherCore(); panicPutStr("Guru Meditation Error: Core "); panicPutDec(core_id); @@ -428,6 +452,7 @@ void xt_unhandled_exception(XtExcFrame *frame) } #endif panicPutStr(". Exception was unhandled.\r\n"); + esp_reset_reason_set_hint(ESP_RST_PANIC); } commonErrorHandler(frame); } diff --git a/components/esp32s2beta/reset_reason.c b/components/esp32s2beta/reset_reason.c new file mode 100644 index 000000000..1ed5bb765 --- /dev/null +++ b/components/esp32s2beta/reset_reason.c @@ -0,0 +1,123 @@ +// 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 "esp32s2beta/rom/rtc.h" +#include "esp_private/system_internal.h" +#include "soc/rtc_periph.h" + +static void esp_reset_reason_clear_hint(void); + +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; + + case RTC_SW_CPU_RESET: + case RTC_SW_SYS_RESET: + 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 RTCWDT_SYS_RESET: + case RTCWDT_RTC_RESET: + case SUPER_WDT_RESET: + case RTCWDT_CPU_RESET: /* unused */ + case TG0WDT_CPU_RESET: /* unused */ + case TG1WDT_CPU_RESET: /* unused */ + return ESP_RST_WDT; + + case RTCWDT_BROWN_OUT_RESET: + 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) +{ + esp_reset_reason_t hint = esp_reset_reason_get_hint(); + s_reset_reason = get_reset_reason(rtc_get_reset_reason(PRO_CPU_NUM), + hint); + if (hint != ESP_RST_UNKNOWN) { + esp_reset_reason_clear_hint(); + } +} + +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; +} +static void esp_reset_reason_clear_hint(void) +{ + REG_WRITE(RTC_RESET_CAUSE_REG, 0); +} + diff --git a/components/esp32s2beta/task_wdt.c b/components/esp32s2beta/task_wdt.c index e8ca2f773..cd562e3b7 100644 --- a/components/esp32s2beta/task_wdt.c +++ b/components/esp32s2beta/task_wdt.c @@ -168,8 +168,7 @@ static void task_wdt_isr(void *arg) if (twdt_config->panic){ //Trigger Panic if configured to do so ESP_EARLY_LOGE(TAG, "Aborting."); portEXIT_CRITICAL_ISR(&twdt_spinlock); - // TODO: Add support reset reason for esp32s2beta. - // esp_reset_reason_set_hint(ESP_RST_TASK_WDT); + esp_reset_reason_set_hint(ESP_RST_TASK_WDT); abort(); } diff --git a/components/esp_rom/include/esp32s2beta/rom/rtc.h b/components/esp_rom/include/esp32s2beta/rom/rtc.h index 3406eab33..c46c9c1f5 100644 --- a/components/esp_rom/include/esp32s2beta/rom/rtc.h +++ b/components/esp_rom/include/esp32s2beta/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 From ad986849a62d93be378a216980a5af58db0d9a19 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 18 Nov 2019 13:51:03 +0100 Subject: [PATCH 2/3] timer: remove check for POWERON_RESET in the test case The test case may run after an RTC_WDT_RESET (if we are on rev. 0 ESP32), or software reset (when running test cases locally). Also moving the test case next to the other timer group driver tests. --- components/driver/test/test_timer.c | 53 +++++++++++++++++++ components/esp32/test/test_reset_reason.c | 62 ----------------------- 2 files changed, 53 insertions(+), 62 deletions(-) diff --git a/components/driver/test/test_timer.c b/components/driver/test/test_timer.c index c859d4598..ebd23ffc2 100644 --- a/components/driver/test/test_timer.c +++ b/components/driver/test/test_timer.c @@ -836,3 +836,56 @@ TEST_CASE("Timer interrupt register", "[hw_timer]") } TEST_ASSERT_INT_WITHIN(100, heap_size, esp_get_free_heap_size()); } + +// The following test cases are used to check if the timer_group fix works. +// Some applications use a software reset, at the reset time, timer_group happens to generate an interrupt. +// but software reset does not clear interrupt status, this is not safe for application when enable the interrupt of timer_group. +// This case will check under this fix, whether the interrupt status is cleared after timer_group initialization. +static void timer_group_test_init(void) +{ + static const uint32_t time_ms = 100; //Alarm value 100ms. + static const uint16_t timer_div = 10; //Timer prescaler + static const uint32_t ste_val = time_ms * (TIMER_BASE_CLK / timer_div / 1000); + timer_config_t config = { + .divider = timer_div, + .counter_dir = TIMER_COUNT_UP, + .counter_en = TIMER_PAUSE, + .alarm_en = TIMER_ALARM_EN, + .intr_type = TIMER_INTR_LEVEL, + .auto_reload = true, + }; + timer_init(TIMER_GROUP_0, TIMER_0, &config); + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL); + timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, ste_val); + //Now the timer is ready. + //We only need to check the interrupt status and don't have to register a interrupt routine. +} + +static void timer_group_test_first_stage(void) +{ + static uint8_t loop_cnt = 0; + timer_group_test_init(); + //Start timer + timer_start(TIMER_GROUP_0, TIMER_0); + //Waiting for timer_group to generate an interrupt + while( !(timer_group_get_intr_status_in_isr(TIMER_GROUP_0) & TIMER_INTR_T0) && + loop_cnt++ < 100) { + vTaskDelay(200); + } + //TIMERG0.int_raw.t0 == 1 means an interruption has occurred + TEST_ASSERT(timer_group_get_intr_status_in_isr(TIMER_GROUP_0) & TIMER_INTR_T0); + esp_restart(); +} + +static void timer_group_test_second_stage(void) +{ + TEST_ASSERT_EQUAL(ESP_RST_SW, esp_reset_reason()); + timer_group_test_init(); + //After the timer_group is initialized, TIMERG0.int_raw.t0 should be cleared. + TEST_ASSERT_EQUAL(0, TIMERG0.int_raw.t0); +} + +TEST_CASE_MULTIPLE_STAGES("timer_group software reset test", + "[intr_status][intr_status = 0]", + timer_group_test_first_stage, + timer_group_test_second_stage); diff --git a/components/esp32/test/test_reset_reason.c b/components/esp32/test/test_reset_reason.c index 4f8316328..3877dfee9 100644 --- a/components/esp32/test/test_reset_reason.c +++ b/components/esp32/test/test_reset_reason.c @@ -244,66 +244,4 @@ TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_BROWNOUT after brownout event", check_reset_reason_brownout); -// The following test cases are used to check if the timer_group fix works. -// Some applications use a software reset, at the reset time, timer_group happens to generate an interrupt. -// but software reset does not clear interrupt status, this is not safe for application when enable the interrupt of timer_group. -// This case will check under this fix, whether the interrupt status is cleared after timer_group initialization. -static void timer_group_test_init(void) -{ - static const uint32_t time_ms = 100; //Alarm value 100ms. - static const uint16_t timer_div = 10; //Timer prescaler - static const uint32_t ste_val = time_ms * (TIMER_BASE_CLK / timer_div / 1000); - timer_config_t config = { - .divider = timer_div, - .counter_dir = TIMER_COUNT_UP, - .counter_en = TIMER_PAUSE, - .alarm_en = TIMER_ALARM_EN, - .intr_type = TIMER_INTR_LEVEL, - .auto_reload = true, - }; - timer_init(TIMER_GROUP_0, TIMER_0, &config); - timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL); - timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, ste_val); - //Now the timer is ready. - //We only need to check the interrupt status and don't have to register a interrupt routine. -} - -static void timer_group_test_first_stage(void) -{ - RESET_REASON rst_res = rtc_get_reset_reason(0); - if(rst_res != POWERON_RESET){ - printf("Not power on reset\n"); - } - TEST_ASSERT_EQUAL(POWERON_RESET, rst_res); - static uint8_t loop_cnt = 0; - timer_group_test_init(); - //Start timer - timer_start(TIMER_GROUP_0, TIMER_0); - //Waiting for timer_group to generate an interrupt - while( !(timer_group_get_intr_status_in_isr(TIMER_GROUP_0) & TIMER_INTR_T0) && - loop_cnt++ < 100) { - vTaskDelay(200); - } - //TIMERG0.int_raw.t0 == 1 means an interruption has occurred - TEST_ASSERT(timer_group_get_intr_status_in_isr(TIMER_GROUP_0) & TIMER_INTR_T0); - esp_restart(); -} - -static void timer_group_test_second_stage(void) -{ - RESET_REASON rst_res = rtc_get_reset_reason(0); - if(rst_res != SW_CPU_RESET){ - printf("Not software reset\n"); - } - TEST_ASSERT_EQUAL(SW_CPU_RESET, rst_res); - timer_group_test_init(); - //After the timer_group is initialized, TIMERG0.int_raw.t0 should be cleared. - TEST_ASSERT_EQUAL(0, TIMERG0.int_raw.t0); -} - -TEST_CASE_MULTIPLE_STAGES("timer_group software reset test", - "[intr_status][intr_status = 0]", - timer_group_test_first_stage, - timer_group_test_second_stage); - /* Not tested here: ESP_RST_SDIO */ From acdb2a5dc0d310cf12532103918ff0d3575b2490 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 22 Nov 2019 10:36:51 +0100 Subject: [PATCH 3/3] ci: add test job with ESP32S2BETA_IDF tag --- tools/ci/config/target-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/config/target-test.yml b/tools/ci/config/target-test.yml index 9a90a7963..e8a253264 100644 --- a/tools/ci/config/target-test.yml +++ b/tools/ci/config/target-test.yml @@ -488,7 +488,7 @@ UT_034: UT_035: extends: .unit_test_template - parallel: 34 + parallel: 35 tags: - ESP32S2BETA_IDF - UT_T1_1