mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-18 06:35:35 +03:00
Merge branch 'fix/zol_reset_lcounter' into 'master'
fix(xtensa): Reset zero overhead loop counter in ISRs Closes IDF-15322 See merge request espressif/esp-idf!45865
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <xtensa/coreasm.h>
|
||||
#include <xtensa/corebits.h>
|
||||
#include <xtensa/config/system.h>
|
||||
#include "xtensa/config/core-isa.h"
|
||||
#include "xtensa_context.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc.h"
|
||||
@@ -87,12 +88,16 @@ xt_highint4:
|
||||
s32i a2, a0, 4
|
||||
rsr a2, XT_REG_SAR
|
||||
s32i a2, a0, 8
|
||||
#if XCHAL_HAVE_LOOPS
|
||||
rsr a2, XT_REG_LBEG
|
||||
s32i a2, a0, 12
|
||||
rsr a2, XT_REG_LEND
|
||||
s32i a2, a0, 16
|
||||
rsr a2, XT_REG_LCOUNT
|
||||
/* Save and disable active zero-overhead loop state while executing high-level ISR code. */
|
||||
movi a2, 0
|
||||
xsr a2, XT_REG_LCOUNT /* a2 = LCOUNT, LCOUNT = 0 */
|
||||
s32i a2, a0, 20
|
||||
#endif
|
||||
rsr a2, EPC1
|
||||
s32i a2, a0, 24
|
||||
|
||||
@@ -189,12 +194,14 @@ _highint4_stack_switch:
|
||||
movi a0, _l4_save_ctx + SPECREG_OFFSET
|
||||
l32i a2, a0, 8
|
||||
wsr a2, XT_REG_SAR
|
||||
#if XCHAL_HAVE_LOOPS
|
||||
l32i a2, a0, 12
|
||||
wsr a2, XT_REG_LBEG
|
||||
l32i a2, a0, 16
|
||||
wsr a2, XT_REG_LEND
|
||||
l32i a2, a0, 20
|
||||
wsr a2, XT_REG_LCOUNT
|
||||
#endif
|
||||
l32i a2, a0, 24
|
||||
wsr a2, EPC1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -60,7 +60,7 @@ Expected:
|
||||
TEST_CASE("FPU: Usage in level 1 ISR", "[freertos]")
|
||||
{
|
||||
intr_handle_t isr_handle;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, &fpu_isr, NULL, &isr_handle));
|
||||
TEST_ESP_OK(esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, &fpu_isr, NULL, &isr_handle));
|
||||
/*
|
||||
Use the FPU (calculate a different value than in the ISR)
|
||||
- We test using a calculation that will cause a change in mantissa and exponent for extra thoroughness
|
||||
@@ -85,8 +85,9 @@ TEST_CASE("FPU: Usage in level 1 ISR", "[freertos]")
|
||||
// We allow a 0.1% delta on the final result in case of any loss of precision from floating point calculations
|
||||
TEST_ASSERT_FLOAT_WITHIN(0.00001f, 0.01f, test_float);
|
||||
|
||||
// Free the ISR
|
||||
esp_intr_free(isr_handle);
|
||||
// Free the ISR for ongoing tests
|
||||
TEST_ESP_OK(esp_intr_free(isr_handle));
|
||||
vTaskDelay(10); // Short delay to allow task memory to be freed
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
@@ -125,11 +126,9 @@ static void unpinned_task(void *arg)
|
||||
|
||||
// Allocate an ISR to use the FPU
|
||||
intr_handle_t isr_handle;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, &fpu_isr, NULL, &isr_handle));
|
||||
TEST_ESP_OK(esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, &fpu_isr, NULL, &isr_handle));
|
||||
// Trigger the ISR
|
||||
xt_set_intset(1 << SW_ISR_LEVEL_1);
|
||||
// Free the ISR
|
||||
esp_intr_free(isr_handle);
|
||||
|
||||
// Task should remain unpinned after the ISR uses the FPU
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
@@ -139,13 +138,16 @@ static void unpinned_task(void *arg)
|
||||
TEST_ASSERT_EQUAL(tskNO_AFFINITY, xTaskGetCoreID(NULL));
|
||||
#endif
|
||||
#endif // !CONFIG_FREERTOS_UNICORE
|
||||
// Reenable scheduling/preemption
|
||||
// Re-enable scheduling/preemption
|
||||
#if ( ( CONFIG_FREERTOS_SMP ) && ( !CONFIG_FREERTOS_UNICORE ) )
|
||||
vTaskPreemptionEnable(NULL);
|
||||
#else
|
||||
xTaskResumeAll();
|
||||
#endif
|
||||
|
||||
// Free the ISR for ongoing tests
|
||||
TEST_ESP_OK(esp_intr_free(isr_handle));
|
||||
|
||||
// Indicate done and self delete
|
||||
xTaskNotifyGive((TaskHandle_t)arg);
|
||||
vTaskDelete(NULL);
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "xtensa/config/core-isa.h"
|
||||
#include "xtensa/xt_specreg.h"
|
||||
#include "xtensa_api.h"
|
||||
|
||||
#if XCHAL_HAVE_LOOPS
|
||||
|
||||
#define SW_ISR_LEVEL_1 7
|
||||
#define SW_ISR_MASK (1U << SW_ISR_LEVEL_1)
|
||||
|
||||
static volatile uint32_t s_isr_body_hits;
|
||||
|
||||
static inline uint32_t read_lbeg(void)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ volatile("rsr.lbeg %0" : "=r"(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint32_t read_lend(void)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ volatile("rsr.lend %0" : "=r"(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint32_t read_lcount(void)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ volatile("rsr.lcount %0" : "=r"(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-entry reproducer:
|
||||
* - Task call: program loop regs via loopnez, trigger SW interrupt from loop body.
|
||||
* - ISR call: enter same function but skip loopnez (do not program loop regs).
|
||||
* If stale LCOUNT/LBEG/LEND leaked into ISR, reaching old LEND will loop.
|
||||
*/
|
||||
static uint32_t IRAM_ATTR __attribute__((noinline, noclone)) zol_reentry_probe(uint32_t setup_loop)
|
||||
{
|
||||
uint32_t body_hits = 0;
|
||||
uint32_t loop_count = 256;
|
||||
uint32_t trigger_once = setup_loop == 1;
|
||||
|
||||
esp_rom_printf("From %s: setup_loop=%u\n", xPortInIsrContext() ? "ISR" : "TASK", setup_loop);
|
||||
esp_rom_printf("LBEG=0x%08x LEND=0x%08x LCOUNT=0x%08x\n", read_lbeg(), read_lend(), read_lcount());
|
||||
|
||||
__asm__ volatile(
|
||||
"beqz %[setup], 1f\n"
|
||||
"loopnez %[cnt], .loopEnd\n"
|
||||
"addi %[hits], %[hits], 1\n"
|
||||
"beqz %[trigger], 1f\n"
|
||||
"wsr.intset %[int_mask]\n" // trigger SW interrupt
|
||||
"movi %[trigger], 0\n"
|
||||
"1:\n" // if setup == 0, skip loopnez and LCOUNT should be 0.
|
||||
"nop\n"
|
||||
".loopEnd:\n"
|
||||
: [hits] "+r"(body_hits),
|
||||
[cnt] "+r"(loop_count),
|
||||
[trigger] "+r"(trigger_once)
|
||||
: [setup] "r"(setup_loop),
|
||||
[int_mask] "r"(SW_ISR_MASK)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
return body_hits;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR lcount_probe_isr(void *arg)
|
||||
{
|
||||
TaskHandle_t waiter_task = (TaskHandle_t)arg;
|
||||
BaseType_t should_yield = pdFALSE;
|
||||
|
||||
xt_set_intclear(SW_ISR_MASK);
|
||||
s_isr_body_hits = zol_reentry_probe(0);
|
||||
|
||||
vTaskNotifyGiveFromISR(waiter_task, &should_yield);
|
||||
if (should_yield == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Xtensa: ISR does not inherit active ZOL state", "[freertos]")
|
||||
{
|
||||
intr_handle_t isr_handle = NULL;
|
||||
TaskHandle_t this_task = xTaskGetCurrentTaskHandle();
|
||||
|
||||
xt_set_intclear(SW_ISR_MASK);
|
||||
TEST_ESP_OK(esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, lcount_probe_isr, (void *)this_task, &isr_handle));
|
||||
|
||||
s_isr_body_hits = 0xFFFF;
|
||||
uint32_t task_body_hits = zol_reentry_probe(1);
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ESP_OK(esp_intr_free(isr_handle));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT32(0, s_isr_body_hits);
|
||||
TEST_ASSERT_EQUAL_UINT32(256, task_body_hits);
|
||||
vTaskDelay(10); // Short delay to allow task memory to be freed
|
||||
}
|
||||
|
||||
#endif /* XCHAL_HAVE_LOOPS */
|
||||
#endif /* CONFIG_IDF_TARGET_ARCH_XTENSA */
|
||||
@@ -199,7 +199,7 @@ static void run_sw_isr_level(int int_level)
|
||||
TEST_ASSERT_EQUAL_UINT16(2, s_iram->b16);
|
||||
TEST_ASSERT_EQUAL_UINT32(0, s_iram->b32);
|
||||
|
||||
esp_intr_free(handle);
|
||||
TEST_ESP_OK(esp_intr_free(handle));
|
||||
}
|
||||
|
||||
TEST_CASE("LoadStore: 8/16-bit field access in IRAM from ISRs when pending interrupts are present", "[freertos]")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2016-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2015-2019 Cadence Design Systems, Inc.
|
||||
@@ -135,7 +135,9 @@ _xt_context_save:
|
||||
s32i a3, sp, XT_STK_LBEG
|
||||
rsr a3, XT_REG_LEND
|
||||
s32i a3, sp, XT_STK_LEND
|
||||
rsr a3, XT_REG_LCOUNT
|
||||
/* Save and disable active zero-overhead loop state while ISR/exception code runs. */
|
||||
movi a3, 0
|
||||
xsr a3, XT_REG_LCOUNT /* a3 = LCOUNT, LCOUNT = 0 */
|
||||
s32i a3, sp, XT_STK_LCOUNT
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user