mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 11:03:11 +00:00
fix(esp32): Fix IRAM_AS_8BIT_ACCESSIBLE_MEMORY accessible from ISR1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -8,14 +8,35 @@
|
||||
Test for LoadStore exception handlers. This test performs unaligned load and store in 32bit aligned addresses
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA && CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <stdio.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_random.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "xtensa_api.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA && CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
|
||||
#define SW_ISR_NUM_L1 7 // CPU interrupt number for internal SW0 (level 1)
|
||||
#define SW_ISR_NUM_L3 29 // CPU interrupt number for internal SW1 (level 3)
|
||||
|
||||
typedef struct {
|
||||
uint8_t b8;
|
||||
uint16_t b16;
|
||||
uint8_t b8_2;
|
||||
uint32_t b32;
|
||||
} iram_data_t;
|
||||
|
||||
static volatile iram_data_t *s_iram;
|
||||
static volatile int isr_hits;
|
||||
|
||||
TEST_CASE("LoadStore Exception handler", "[freertos]")
|
||||
{
|
||||
int32_t val0 = 0xDEADBEEF;
|
||||
@@ -125,4 +146,75 @@ TEST_CASE("LoadStore Exception handler", "[freertos]")
|
||||
TEST_ASSERT_TRUE(heap_caps_check_integrity_all(true));
|
||||
heap_caps_free(arr);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR level_isr(void *arg)
|
||||
{
|
||||
const int cpu_intr_num = *(const int *)arg;
|
||||
|
||||
// Clear the SW interrupt source
|
||||
xt_set_intclear(1 << cpu_intr_num);
|
||||
|
||||
// Schedule another pending level interrupt while still inside this ISR
|
||||
// to keep dispatch_c_isr in the interrupt loop handling. The pending
|
||||
// interrupt will be handled after this ISR returns.
|
||||
if (isr_hits == 0) {
|
||||
xt_set_intset(1 << cpu_intr_num);
|
||||
}
|
||||
|
||||
// 8/16-bit accesses to s_iram will trigger LoadStore/Alignment exceptions which changes the EXCSAVE_1 value
|
||||
s_iram->b16++;
|
||||
s_iram->b8 = 0x5A;
|
||||
s_iram->b8_2 = 0xA5;
|
||||
|
||||
isr_hits++;
|
||||
}
|
||||
|
||||
static void run_sw_isr_level(int int_level)
|
||||
{
|
||||
memset((void *)s_iram, 0, sizeof(iram_data_t));
|
||||
isr_hits = 0;
|
||||
|
||||
intr_handle_t handle;
|
||||
int cpu_intr_num = (int_level == 1) ? SW_ISR_NUM_L1 : SW_ISR_NUM_L3;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_intr_alloc((int_level == 1) ? ETS_INTERNAL_SW0_INTR_SOURCE : ETS_INTERNAL_SW1_INTR_SOURCE,
|
||||
(int_level == 1) ? ESP_INTR_FLAG_LEVEL1 : ESP_INTR_FLAG_LEVEL3,
|
||||
level_isr,
|
||||
(void *)&cpu_intr_num,
|
||||
&handle));
|
||||
|
||||
// Trigger the first interrupt; ISR will queue one more while running
|
||||
xt_set_intset(1 << cpu_intr_num);
|
||||
|
||||
// Wait for the ISR to be invoked twice
|
||||
TickType_t start = xTaskGetTickCount();
|
||||
while (isr_hits < 2 && (xTaskGetTickCount() - start) < pdMS_TO_TICKS(500)) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(2, isr_hits);
|
||||
TEST_ASSERT_EQUAL_HEX8(0x5A, s_iram->b8);
|
||||
TEST_ASSERT_EQUAL_HEX8(0xA5, s_iram->b8_2);
|
||||
TEST_ASSERT_EQUAL_UINT16(2, s_iram->b16);
|
||||
TEST_ASSERT_EQUAL_UINT32(0, s_iram->b32);
|
||||
|
||||
esp_intr_free(handle);
|
||||
}
|
||||
|
||||
TEST_CASE("LoadStore: 8/16-bit field access in IRAM from ISRs when pending interrupts are present", "[freertos]")
|
||||
{
|
||||
s_iram = heap_caps_calloc(1, sizeof(*s_iram), MALLOC_CAP_IRAM_8BIT);
|
||||
TEST_ASSERT_NOT_NULL(s_iram);
|
||||
|
||||
esp_rom_printf("Running test in level-1 ISR...\n");
|
||||
run_sw_isr_level(1);
|
||||
esp_rom_printf("test passed\n");
|
||||
|
||||
esp_rom_printf("Running test in level-3 ISR...\n");
|
||||
run_sw_isr_level(3);
|
||||
esp_rom_printf("test passed\n");
|
||||
|
||||
heap_caps_free((void *)s_iram);
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // Wait for memory to be freed, to avoid affecting other tests.
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA && CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
|
||||
|
||||
@@ -39,6 +39,8 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
'esp32c3',
|
||||
(pytest.mark.flash_suspend,),
|
||||
),
|
||||
# IRAM as 8-bit accessible memory
|
||||
('iram_as_8bit_access_mem', 'esp32'),
|
||||
],
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY=y
|
||||
CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2016-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2015-2019 Cadence Design Systems, Inc.
|
||||
@@ -104,7 +104,6 @@
|
||||
#include "soc/soc.h"
|
||||
#include "xt_asm_utils.h"
|
||||
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------------------
|
||||
In order for backtracing to be able to trace from the pre-exception stack
|
||||
@@ -129,6 +128,21 @@
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(XT_DEBUG_BACKTRACE) && defined(CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY)
|
||||
/*
|
||||
* For CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY we cannot rely on EXCSAVE_1
|
||||
* because accessing unaligned IRAM memory from a level-1 ISR would cause
|
||||
* LoadStore/Alignment exceptions and will overwrite EXCSAVE_1 before we read
|
||||
* it for backtracing. Keep a copy of EXCSAVE_1 in RAM instead.
|
||||
*/
|
||||
.section .noinit,"aw",@nobits
|
||||
.align 4
|
||||
_xt_excsave1_sp:
|
||||
.word 0
|
||||
.section .text
|
||||
#endif /* XT_DEBUG_BACKTRACE CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY */
|
||||
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------------------
|
||||
Defines used to access _xtos_interrupt_table.
|
||||
@@ -229,7 +243,17 @@
|
||||
*/
|
||||
#ifdef XT_DEBUG_BACKTRACE
|
||||
#ifndef __XTENSA_CALL0_ABI__
|
||||
#ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
|
||||
.if \level == 1
|
||||
/* For level 1, nested LoadStore/Alignment Exceptions may clobber EXCSAVE_1, this is why we use _xt_excsave1_sp. */
|
||||
movi a0, _xt_excsave1_sp
|
||||
l32i a0, a0, 0
|
||||
.else
|
||||
rsr a0, XT_REG_EXCSAVE_1 + \level - 1 /* EXCSAVE_x for this level */
|
||||
.endif
|
||||
#else
|
||||
rsr a0, XT_REG_EXCSAVE_1 + \level - 1 /* Get exception frame pointer stored in EXCSAVE_x */
|
||||
#endif /* CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY */
|
||||
l32i a3, a0, XT_STK_A0 /* Copy pre-exception a0 (return address) */
|
||||
s32e a3, a1, -16
|
||||
l32i a3, a0, XT_STK_A1 /* Copy pre-exception a1 (stack pointer) */
|
||||
@@ -1212,12 +1236,20 @@ _xt_lowint1:
|
||||
movi a0, _xt_user_exit /* save exit point for dispatch */
|
||||
s32i a0, sp, XT_STK_EXIT
|
||||
|
||||
/* EXCSAVE_1 should now be free to use. Use it to keep a copy of the
|
||||
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
|
||||
#ifdef XT_DEBUG_BACKTRACE
|
||||
#ifndef __XTENSA_CALL0_ABI__
|
||||
#ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
|
||||
/* EXCSAVE_1 can not be used to store a copy of the current stack pointer for debug backtrace,
|
||||
because it is used to store the return address for the load/store error handler.
|
||||
Use a fixed memory location (_xt_excsave1_sp) in RAM instead. */
|
||||
movi a0, _xt_excsave1_sp
|
||||
s32i sp, a0, 0 /* store frame pointer (SP) to RAM */
|
||||
#else
|
||||
/* EXCSAVE_1 should now be free to use. Use it to keep a copy of the
|
||||
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
|
||||
mov a0, sp
|
||||
wsr a0, XT_REG_EXCSAVE_1
|
||||
#endif /* CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -37,8 +37,11 @@ All DRAM memory is single-byte accessible, thus all DRAM heaps possess the ``MAL
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
If ran out of ``MALLOC_CAP_8BIT``, the users can use ``MALLOC_CAP_IRAM_8BIT`` instead. In that case, IRAM can still be used as a "reserve" pool of internal memory if the users only access it in a 32-bit aligned manner, or if they enable ``CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY)``.
|
||||
If users run out of ``MALLOC_CAP_8BIT``, they can use ``MALLOC_CAP_IRAM_8BIT`` instead. In that case, IRAM can still be used as a "reserve" pool of internal memory if the users only access it in a 32-bit aligned manner, or if they enable ``CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY``.
|
||||
|
||||
.. note::
|
||||
|
||||
This option is available only on single-core ESP32 configuration (``CONFIG_FREERTOS_UNICORE=y``). The ``CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY`` option has a significant disadvantage compared to regular memory: every byte, half-word, and arbitrary unaligned access to ``MALLOC_CAP_IRAM_8BIT`` memory triggers a ``LoadStore`` or ``Alignment`` exception. Although these exceptions are handled in software to ensure correct values are read or written, each access incurs approximately 167 CPU cycles of overhead and can cause significant performance degradation if accessed frequently. Therefore, it should be avoided in performance-critical sections of code such as ISRs or tight loops. Consider to refactor the code to use 32-bit aligned (``uint32_t``) accesses.
|
||||
|
||||
When calling ``malloc()``, the ESP-IDF ``malloc()`` internally calls ``heap_caps_malloc_default(size)``. This will allocate memory with the capability ``MALLOC_CAP_DEFAULT``, which is byte-addressable.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user