From 774f0101961567b99f9a999b767020f8e4fc7c6d Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Wed, 10 Feb 2021 16:18:04 +0800 Subject: [PATCH] freertos: Fix delay between interrupt request and trigger on RISC-V NOP instructions have been added in order to prevent the code from executing code it shouldn't execute. This is due to a delay between the moment an interrupt is requested and the moment it is fired. It only happens on RISC-V SoC. --- components/freertos/port/riscv/port.c | 29 +++++++++++++++++++ .../freertos/test/test_task_suspend_resume.c | 4 --- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/components/freertos/port/riscv/port.c b/components/freertos/port/riscv/port.c index f327e0775b..e53c20eb2c 100644 --- a/components/freertos/port/riscv/port.c +++ b/components/freertos/port/riscv/port.c @@ -178,6 +178,24 @@ void prvTaskExitError(void) void vPortClearInterruptMask(int mask) { REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, mask); + /** + * The delay between the moment we unmask the interrupt threshold register + * and the moment the potential requested interrupt is triggered is not + * null: up to three machine cycles/instructions can be executed. + * + * When compilation size optimization is enabled, this function and its + * callers returning void will have NO epilogue, thus the instruction + * following these calls will be executed. + * + * If the requested interrupt is a context switch to a higher priority + * task then the one currently running, we MUST NOT execute any instruction + * before the interrupt effectively happens. + * In order to prevent this, force this routine to have a 3-instruction + * delay before exiting. + */ + asm volatile ( "nop" ); + asm volatile ( "nop" ); + asm volatile ( "nop" ); } /* Set interrupt mask and return current interrupt enable register */ @@ -188,6 +206,17 @@ int vPortSetInterruptMask(void) ret = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, RVHAL_EXCM_LEVEL); RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); + /** + * In theory, this function should not return immediately as there is a + * delay between the moment we mask the interrupt threshold register and + * the moment a potential lower-priority interrupt is triggered (as said + * above), it should have a delay of 2 machine cycles/instructions. + * + * However, in practice, this function has an epilogue of one instruction, + * thus the instruction masking the interrupt threshold register is + * followed by two instructions: `ret` and `csrrs` (RV_SET_CSR). + * That's why we don't need any additional nop instructions here. + */ return ret; } diff --git a/components/freertos/test/test_task_suspend_resume.c b/components/freertos/test/test_task_suspend_resume.c index 1644c242e5..31ff29dba7 100644 --- a/components/freertos/test/test_task_suspend_resume.c +++ b/components/freertos/test/test_task_suspend_resume.c @@ -89,9 +89,6 @@ TEST_CASE("Suspend/resume task on other core", "[freertos]") } #endif -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C3) -// TODO ESP32C3 IDF-2588 - /* Task suspends itself, then sets a flag and deletes itself */ static void task_suspend_self(void *vp_resumed) { @@ -118,7 +115,6 @@ TEST_CASE("Suspend the current running task", "[freertos]") // Shouldn't need any delay here, as task should resume on this CPU immediately TEST_ASSERT_TRUE(resumed); } -#endif // !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C3) volatile bool timer_isr_fired;