Преглед на файлове

esp32: Switch SPIRAM stack in esp_restart_noos() to internal stack

If esp_restart_noos() is run and the stack address points to external memory (SPIRAM)
then Cache_Read_Disable() raises up the error "Cache disabled but cached memory region accessed"
to fix this we switch stack to internal RAM before disable cache.

Added unit tests.

Closes: https://github.com/espressif/esp-idf/issues/5107
KonstantinKondrashov преди 5 години
родител
ревизия
d925b564cd

+ 12 - 0
components/esp32/system_api_esp32.c

@@ -31,6 +31,8 @@
 #include "soc/cpu.h"
 #include "soc/rtc.h"
 #include "soc/rtc_wdt.h"
+#include "soc/soc_memory_layout.h"
+#include "xt_instr_macros.h"
 #include "hal/timer_ll.h"
 #include "freertos/xtensa_api.h"
 
@@ -79,6 +81,16 @@ void IRAM_ATTR esp_restart_noos(void)
     uart_tx_wait_idle(1);
     uart_tx_wait_idle(2);
 
+#ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
+    if (esp_ptr_external_ram(get_sp())) {
+        // If stack_addr is from External Memory (CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY is used)
+        // then need to switch SP to Internal Memory otherwise
+        // we will get the "Cache disabled but cached memory region accessed" error after Cache_Read_Disable.
+        uint32_t new_sp = SOC_DRAM_LOW + (SOC_DRAM_HIGH - SOC_DRAM_LOW) / 2;
+        SET_STACK(new_sp);
+    }
+#endif
+
     // Disable cache
     Cache_Read_Disable(0);
     Cache_Read_Disable(1);

+ 86 - 0
components/esp32/test/test_reset_reason.c

@@ -244,4 +244,90 @@ TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_BROWNOUT after brownout event",
         check_reset_reason_brownout);
 
 
+#ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
+#ifndef CONFIG_FREERTOS_UNICORE
+#include "soc/soc_memory_layout.h"
+#include "xt_instr_macros.h"
+#include "xtensa/config/specreg.h"
+
+static int size_stack = 1024 * 3;
+static StackType_t *start_addr_stack;
+
+static int fibonacci(int n, void* func(void))
+{
+    int tmp1 = n, tmp2 = n;
+    uint32_t base, start;
+    RSR(WINDOWBASE, base);
+    RSR(WINDOWSTART, start);
+    printf("WINDOWBASE = %-2d   WINDOWSTART = 0x%x\n", base, start);
+    if (n <= 1) {
+        StackType_t *last_addr_stack = get_sp();
+        StackType_t *used_stack = (StackType_t *) (start_addr_stack - last_addr_stack);
+        printf("addr_stack = %p, used[%p]/all[0x%x] space in stack\n", last_addr_stack, used_stack, size_stack);
+        func();
+        return n;
+    }
+    int fib = fibonacci(n - 1, func) + fibonacci(n - 2, func);
+    printf("fib = %d\n", (tmp1 - tmp2) + fib);
+    return fib;
+}
+
+static void test_task(void *func)
+{
+    start_addr_stack = get_sp();
+    if (esp_ptr_external_ram(start_addr_stack)) {
+        printf("restart_task: uses external stack, addr_stack = %p\n", start_addr_stack);
+    } else {
+        printf("restart_task: uses internal stack, addr_stack = %p\n", start_addr_stack);
+    }
+    fibonacci(35, func);
+}
+
+static void func_do_exception(void)
+{
+    *((int *) 0) = 0;
+}
+
+static void init_restart_task(void)
+{
+    StackType_t *stack_for_task = (StackType_t *) heap_caps_calloc(1, size_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    printf("init_task: current addr_stack = %p, stack_for_task = %p\n", get_sp(), stack_for_task);
+    static StaticTask_t task_buf;
+    xTaskCreateStaticPinnedToCore(test_task, "test_task", size_stack, esp_restart, 5, stack_for_task, &task_buf, 1);
+    while (1) { };
+}
+
+static void init_task_do_exception(void)
+{
+    StackType_t *stack_for_task = (StackType_t *) heap_caps_calloc(1, size_stack, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    printf("init_task: current addr_stack = %p, stack_for_task = %p\n", get_sp(), stack_for_task);
+    static StaticTask_t task_buf;
+    xTaskCreateStaticPinnedToCore(test_task, "test_task", size_stack, func_do_exception, 5, stack_for_task, &task_buf, 1);
+    while (1) { };
+}
+
+static void test1_finish(void)
+{
+    TEST_ASSERT_EQUAL(ESP_RST_SW, esp_reset_reason());
+    printf("test - OK\n");
+}
+
+static void test2_finish(void)
+{
+    TEST_ASSERT_EQUAL(ESP_RST_PANIC, esp_reset_reason());
+    printf("test - OK\n");
+}
+
+TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_SW after restart in a task with spiram stack", "[spiram_stack][reset=SW_CPU_RESET]",
+        init_restart_task,
+        test1_finish);
+
+TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_PANIC after an exception in a task with spiram stack", "[spiram_stack][reset=StoreProhibited,SW_CPU_RESET]",
+        init_task_do_exception,
+        test2_finish);
+
+#endif // CONFIG_FREERTOS_UNICORE
+#endif // CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
+
+
 /* Not tested here: ESP_RST_SDIO */

+ 89 - 0
components/xtensa/include/xt_instr_macros.h

@@ -0,0 +1,89 @@
+// Copyright 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.
+
+#pragma once
+
+/* The SET_STACK implements a setting a new stack pointer (sp or a1).
+ * to do this the need reset PS_WOE, reset WINDOWSTART, update SP, and return PS_WOE.
+ *
+ * Note: It has 2 implementations one for using in assembler files (*.S) and one for using in C.
+ *
+ * C code prototype for SET_STACK:
+ *   uint32_t ps_reg;
+ *   uint32_t w_base;
+ *   RSR(PS, ps_reg);
+ *   ps_reg &= ~(PS_WOE_MASK | PS_OWB_MASK | PS_CALLINC_MASK);
+ *   WSR(PS, ps_reg);
+ *
+ *   RSR(WINDOWBASE, w_base);
+ *   WSR(WINDOWSTART, (1 << w_base));
+ *
+ *   asm volatile ( "movi sp, "XTSTR( (SOC_DRAM_LOW + (SOC_DRAM_HIGH - SOC_DRAM_LOW) / 2) )"");
+ *
+ *   RSR(PS, ps_reg);
+ *   ps_reg |= (PS_WOE_MASK);
+ *   WSR(PS, ps_reg);
+*/
+#ifdef __ASSEMBLER__
+    .macro SET_STACK  new_sp tmp1 tmp2
+    rsr.ps \tmp1
+    movi \tmp2, ~(PS_WOE_MASK | PS_OWB_MASK | PS_CALLINC_MASK)
+    and \tmp1, \tmp1, \tmp2
+    wsr.ps \tmp1
+    rsync
+
+    rsr.windowbase \tmp1
+    ssl	\tmp1
+    movi \tmp1, 1
+    sll	\tmp1, \tmp1
+    wsr.windowstart \tmp1
+    rsync
+
+    mov sp, \new_sp
+
+    rsr.ps \tmp1
+    movi \tmp2, (PS_WOE)
+    or \tmp1, \tmp1, \tmp2
+    wsr.ps \tmp1
+    rsync
+    .endm
+#else
+#define SET_STACK(new_sp) \
+    do { \
+        uint32_t tmp1 = 0, tmp2 = 0; \
+        asm volatile ( \
+          "rsr.ps %1 \n"\
+          "movi %2, ~" XTSTR( PS_WOE_MASK | PS_OWB_MASK | PS_CALLINC_MASK ) " \n"\
+          "and %1, %1, %2 \n"\
+          "wsr.ps %1 \n"\
+          "rsync \n"\
+          " \n"\
+          "rsr.windowbase %1 \n"\
+          "ssl	%1 \n"\
+          "movi %1, 1 \n"\
+          "sll	%1, %1 \n"\
+          "wsr.windowstart %1 \n"\
+          "rsync \n"\
+          " \n"\
+          "mov sp, %0 \n"\
+          "rsr.ps %1 \n"\
+          " \n"\
+          "movi %2, " XTSTR( PS_WOE_MASK ) "\n"\
+          " \n"\
+          "or %1, %1, %2 \n"\
+          "wsr.ps %1 \n"\
+          "rsync \n"\
+          : "+r"(new_sp), "+r"(tmp1), "+r"(tmp2)); \
+    } while (0);
+#endif // __ASSEMBLER__

+ 1 - 0
tools/unit-test-app/configs/psram_2

@@ -3,3 +3,4 @@ CONFIG_ESP32_SPIRAM_SUPPORT=y
 CONFIG_ESP_INT_WDT_TIMEOUT_MS=800
 CONFIG_SPIRAM_OCCUPY_NO_HOST=y
 CONFIG_ESP32_WIFI_RX_IRAM_OPT=n
+CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y