diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/FreeRTOSConfig_smp.h b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/FreeRTOSConfig_smp.h index 511ac13fd9..35f7a61e9e 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/FreeRTOSConfig_smp.h +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/FreeRTOSConfig_smp.h @@ -17,54 +17,14 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #include //For configASSERT() #endif /* def __ASSEMBLER__ */ -#if __XTENSA__ -/* Required for configuration-dependent settings. */ -#include "xtensa_config.h" - -/* -------------------------------------------- Xtensa Additional Config ---------------------------------------------- - * - Provide Xtensa definitions usually given by -D option when building with xt-make (see readme_xtensa.txt) - * - xtensa_rtos.h and xtensa_timer.h will default some of these values - * - XT_SIMULATOR configXT_SIMULATOR - * - XT_BOARD configXT_BOARD - * - XT_CLOCK_FREQ Should not be defined as we are using XT_BOARD mode - * - XT_TICK_PER_SEC Defaults to configTICK_RATE_HZ - * - XT_TIMER_INDEX Defaults to configXT_TIMER_INDEX - * - XT_INTEXC_HOOKS Defaults to configXT_INTEXC_HOOKS - * - XT_USE_OVLY We don't define this (unused) - * - XT_USE_SWPRI We don't define this (unused) - * ------------------------------------------------------------------------------------------------------------------ */ - -#define configXT_SIMULATOR 0 -#define configXT_BOARD 1 /* Board mode */ -#if CONFIG_FREERTOS_CORETIMER_0 -#define configXT_TIMER_INDEX 0 -#elif CONFIG_FREERTOS_CORETIMER_1 -#define configXT_TIMER_INDEX 1 -#endif -#define configXT_INTEXC_HOOKS 0 - -#define configBENCHMARK 0 -#endif // __XTENSA__ - /* ------------------------------------------------ ESP-IDF Additions -------------------------------------------------- * * ------------------------------------------------------------------------------------------------------------------ */ -#if __XTENSA__ -/* The Xtensa port uses a separate interrupt stack. Adjust the stack size - * to suit the needs of your specific application. - * Size needs to be aligned to the stack increment, since the location of - * the stack for the 2nd CPU will be calculated using configISR_STACK_SIZE. - */ -#define configSTACK_ALIGNMENT 16 -#ifndef configISR_STACK_SIZE -#define configISR_STACK_SIZE ((CONFIG_FREERTOS_ISR_STACKSIZE + configSTACK_ALIGNMENT - 1) & (~(configSTACK_ALIGNMENT - 1))) -#endif -#else // RISC-V #ifndef configISR_STACK_SIZE #define configISR_STACK_SIZE (CONFIG_FREERTOS_ISR_STACKSIZE) #endif -#endif // __XTENSA__ + /* ----------------------------------------------------- Helpers ------------------------------------------------------- * - Macros that the FreeRTOS configuration macros depend on * ------------------------------------------------------------------------------------------------------------------ */ @@ -188,6 +148,8 @@ This file get's pulled into assembly sources. Therefore, some includes need to b #endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS #define configSTACK_DEPTH_TYPE uint32_t #define configUSE_NEWLIB_REENTRANT 1 +#define configNEWLIB_REENTRANT_IS_DYNAMIC 1 // IDF Newlib supports dynamic reentrancy. + // We provide our own __getreent() function #define configENABLE_BACKWARD_COMPATIBILITY 0 #define configASSERT(a) assert(a) #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 1 diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h index 1bda71b504..3fc6c99c52 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h @@ -49,11 +49,8 @@ typedef uint32_t TickType_t; #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) -//TODO: Check this // interrupt module will mask interrupt with priority less than threshold #define RVHAL_EXCM_LEVEL 4 -// TODO: Check this end - /* ----------------------------------------------- Port Configurations ------------------------------------------------- * - Configurations values supplied by each port @@ -67,127 +64,28 @@ typedef uint32_t TickType_t; #define portNOP() __asm volatile (" nop ") /* ---------------------------------------------- Forward Declarations ------------------------------------------------- - * - Forward declarations of all the port functions and macros need to implement the FreeRTOS porting interface + * - Forward declarations of all the port functions and macros needed to implement the FreeRTOS porting interface * - These must come before definition/declaration of the FreeRTOS porting interface * ------------------------------------------------------------------------------------------------------------------ */ /* ---------------------- Spinlocks ------------------------ - Spinlocks added to match API with SMP FreeRTOS. Single core RISC-V does not need spin locks - - Because single core does not have a primitive spinlock data type, we have to implement one here - * @note [refactor-todo] Refactor critical section API so that this is no longer required * ------------------------------------------------------ */ -/** - * @brief Spinlock object - * Owner: - * - Set to 0 if uninitialized - * - Set to portMUX_FREE_VAL when free - * - Set to CORE_ID_REGVAL_PRO or CORE_ID_REGVAL_AP when locked - * - Any other value indicates corruption - * Count: - * - 0 if unlocked - * - Recursive count if locked - * - * @note Not a true spinlock as single core RISC-V does not have atomic compare and set instruction - * @note Keep portMUX_INITIALIZER_UNLOCKED in sync with this struct - */ -//typedef struct { -// uint32_t owner; -// uint32_t count; -//} portMUX_TYPE; typedef spinlock_t portMUX_TYPE; /**< Spinlock type used by FreeRTOS critical sections */ -#if 0 -#define portMUX_INITIALIZER_UNLOCKED { \ - .owner = portMUX_FREE_VAL, \ - .count = 0, \ - } -#define portMUX_FREE_VAL SPINLOCK_FREE /**< Spinlock is free. [refactor-todo] check if this is still required */ -#define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /**< When passed for 'timeout_cycles', spin forever if necessary. [refactor-todo] check if this is still required */ -#define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /**< Try to acquire the spinlock a single time only. [refactor-todo] check if this is still required */ -#define portMUX_INITIALIZE(mux) ({ \ - (mux)->owner = portMUX_FREE_VAL; \ - (mux)->count = 0; \ -}) -#endif + #define portMUX_INITIALIZER_UNLOCKED SPINLOCK_INITIALIZER /**< Spinlock initializer */ #define portMUX_FREE_VAL SPINLOCK_FREE /**< Spinlock is free. [refactor-todo] check if this is still required */ #define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /**< When passed for 'timeout_cycles', spin forever if necessary. [refactor-todo] check if this is still required */ #define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /**< Try to acquire the spinlock a single time only. [refactor-todo] check if this is still required */ #define portMUX_INITIALIZE(mux) spinlock_initialize(mux) /*< Initialize a spinlock to its unlocked state */ - -// ----------------------- Memory -------------------------- - // --------------------- Interrupts ------------------------ BaseType_t xPortCheckIfInISR(void); -// TODO: Check this -#if 0 -/** - * @brief Checks if the current core is in an ISR context - * - * - ISR context consist of Low/Mid priority ISR, or time tick ISR - * - High priority ISRs aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. - * - * @note [refactor-todo] Check if this should be inlined - * @return - * - pdTRUE if in ISR - * - pdFALSE otherwise - */ -BaseType_t xPortInIsrContext(void); -#endif -// TODO: Check this end - -// TODO: Check this -/** - * @brief Check if in ISR context from High priority ISRs - * - * - Called from High priority ISR - * - Checks if the previous context (before high priority interrupt) was in ISR context (meaning low/med priority) - * - * @note [refactor-todo] Check if this should be inlined - * @return - * - pdTRUE if in previous in ISR context - * - pdFALSE otherwise - */ -BaseType_t xPortInterruptedFromISRContext(void); -// TODO: Check this end - -// TODO: Check this -/* ---------------------- Spinlocks ------------------------*/ -/** - * @brief Wrapper for atomic compare-and-set instruction - * - * @note Isn't a real atomic CAS. - * @note [refactor-todo] check if we still need this - * @note [refactor-todo] Check if this function should be renamed (due to void return type) - * - * @param[inout] addr Pointer to target address - * @param[in] compare Compare value - * @param[inout] set Pointer to set value - */ -static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set); - -/** - * @brief Wrapper for atomic compare-and-set instruction in external RAM - * - * @note Isn't a real atomic CAS. - * @note [refactor-todo] check if we still need this - * @note [refactor-todo] Check if this function should be renamed (due to void return type) - * - * @param[inout] addr Pointer to target address - * @param[in] compare Compare value - * @param[inout] set Pointer to set value - */ -static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set); -// TODO: Check this end - // ------------------ Critical Sections -------------------- -UBaseType_t uxPortEnterCriticalFromISR( void ); -void vPortExitCriticalFromISR( UBaseType_t level ); - /* These are always called with interrupts already disabled. We simply need to get/release the spinlocks */ @@ -198,55 +96,24 @@ extern portMUX_TYPE port_xISRLock; void vPortTakeLock( portMUX_TYPE *lock ); void vPortReleaseLock( portMUX_TYPE *lock ); -/** - * @brief Enter a critical section - * - * - Simply disable interrupts - * - Can be nested - */ -void vPortEnterCritical(void); - -/** - * @brief Exit a critical section - * - * - Reenables interrupts - * - Can be nested - */ -void vPortExitCritical(void); - // ---------------------- Yielding ------------------------- void vPortYield( void ); static inline void __attribute__((always_inline)) vPortYieldCore( BaseType_t xCoreID ); -// TODO: Check this -//static inline void __attribute__((always_inline)) vPortYieldFromISR( void ); -// TODO: Check this end /** * @brief Set interrupt mask and return current interrupt enable register * - * @note [refactor-todo] Check if this function should be renamed (due to int return type) - * @return int Current interrupt enable register before set + * @return UBaseType_t Current interrupt enable register before set */ -int vPortSetInterruptMask(void); +UBaseType_t ulPortSetInterruptMask(void); /** * @brief Clear current interrupt mask and set given mask * * @param mask Interrupt mask */ -void vPortClearInterruptMask(int mask); - -// TODO: Check this -#if 0 -/** - * @brief Perform a context switch from a task - * - * @note [refactor-todo] The rest of ESP-IDF should call taskYield() instead - */ -void vPortYield(void); -#endif -// TODO: Check this end +void vPortClearInterruptMask(UBaseType_t mask); /** * @brief Perform a context switch from an ISR @@ -260,31 +127,6 @@ void vPortYieldFromISR(void); }) #define portYIELD_FROM_ISR_NO_CHECK() vPortYieldFromISR() -// TODO: Check this -#if 0 -/** - * @brief Yields the other core - * - * @note Added to be compatible with SMP API - * @note [refactor-todo] Put this into private macros as its only called from task.c and is not public API - * @param coreid ID of core to yield - */ -void vPortYieldOtherCore(BaseType_t coreid); - -/** - * @brief Checks if the current core can yield - * - * - A core cannot yield if its in an ISR or in a critical section - * - * @note [refactor-todo] See if this can be separated from port macro - * @note [refactor-todo] Check if this function should be renamed (due to bool return type) - * @return true Core can yield - * @return false Core cannot yield - */ -static inline bool xPortCanYield(void); -#endif -// TODO: Check this end - // ----------------------- System -------------------------- static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void ); @@ -293,87 +135,15 @@ static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void ); void vPortCleanUpTCB ( void *pxTCB ); -// TODO: Check this -#if 0 -// ------------------- Hook Functions ---------------------- - -/** - * @brief Hook function called on entry to tickless idle - * - * - Implemented in pm_impl.c - * - * @param xExpectedIdleTime Expected idle time - */ -void vApplicationSleep(TickType_t xExpectedIdleTime); -#endif -// TODO: Check this end - -// TODO: Check this -#if 0 -// ----------------------- System -------------------------- - -/** - * @brief Get the tick rate per second - * - * @note [refactor-todo] make this inline - * @note [refactor-todo] Check if this function should be renamed (due to uint return type) - * @return uint32_t Tick rate in Hz - */ -uint32_t xPortGetTickRateHz(void); - -/** - * @brief Set a watchpoint to watch the last 32 bytes of the stack - * - * Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack watchpoint - * around. - * - * @param pxStackStart Pointer to the start of the stack - */ -void vPortSetStackWatchpoint(void *pxStackStart); - -/** - * @brief Get the current core's ID - * - * @note Added to be compatible with SMP API - * @note [refactor-todo] IDF should call a FreeRTOS like macro instead of port function directly - * @return BaseType_t Core ID - */ -static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) -{ - return (BaseType_t) cpu_hal_get_core_id(); -} -#endif -// TODO: Check this end - - - /* ------------------------------------------- FreeRTOS Porting Interface ---------------------------------------------- * - Contains all the mappings of the macros required by FreeRTOS * - Most come after forward declare as porting macros map to declared functions * - Maps to forward declared functions * ------------------------------------------------------------------------------------------------------------------ */ -// ----------------------- Memory -------------------------- - -// TODO: Check this -#if 0 -/** - * @brief Task memory allocation macros - * - * @note Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force the stack - * memory to always be internal. - * @note [refactor-todo] Update portable.h to match v10.4.3 to use new malloc prototypes - */ -#define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) -#define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) -#define pvPortMallocTcbMem(size) pvPortMalloc(size) -#define pvPortMallocStackMem(size) pvPortMalloc(size) -#endif -// TODO: Check this end - // --------------------- Interrupts ------------------------ -#define portDISABLE_INTERRUPTS() vPortSetInterruptMask() +#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask() #define portENABLE_INTERRUPTS() vPortClearInterruptMask(1) #define portRESTORE_INTERRUPTS(x) vPortClearInterruptMask(x) @@ -420,51 +190,11 @@ extern void vTaskExitCritical( void ); #endif #define portYIELD_CORE(x) vPortYieldCore(x) -// TODO: Check this -#if 0 -#define portYIELD_FROM_ISR_NO_ARG() vPortYieldFromISR() -#define portYIELD_FROM_ISR_ARG(xHigherPriorityTaskWoken) ({ \ - if (xHigherPriorityTaskWoken == pdTRUE) { \ - vPortYieldFromISR(); \ - } \ -}) -/** - * @note The macro below could be used when passing a single argument, or without any argument, - * it was developed to support both usages of portYIELD inside of an ISR. Any other usage form - * might result in undesired behavior - */ -#if defined(__cplusplus) && (__cplusplus > 201703L) -#define portYIELD_FROM_ISR(...) CHOOSE_MACRO_VA_ARG(portYIELD_FROM_ISR_ARG, portYIELD_FROM_ISR_NO_ARG __VA_OPT__(,) __VA_ARGS__)(__VA_ARGS__) -#else -#define portYIELD_FROM_ISR(...) CHOOSE_MACRO_VA_ARG(portYIELD_FROM_ISR_ARG, portYIELD_FROM_ISR_NO_ARG, ##__VA_ARGS__)(__VA_ARGS__) -#endif - - -#define portEND_SWITCHING_ISR(xSwitchRequired) if(xSwitchRequired) vPortYield() -/* Yielding within an API call (when interrupts are off), means the yield should be delayed - until interrupts are re-enabled. - To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This - is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is - happening on the same CPU. -*/ -#define portYIELD_WITHIN_API() portYIELD() -#endif -// TODO: Check this end - -// // ----------------------- System -------------------------- #define portGET_CORE_ID() xPortGetCoreID() #define portCHECK_IF_IN_ISR() xPortCheckIfInISR() -// TODO: Check this -#if 0 -// ------------------- Hook Functions ---------------------- - -#define portSUPPRESS_TICKS_AND_SLEEP(idleTime) vApplicationSleep(idleTime) -#endif -// TODO: Check this end - // ------------------- Run Time Stats ---------------------- #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() @@ -484,10 +214,6 @@ extern void vTaskExitCritical( void ); * - For implementation of non-inlined functions, see port.c, port_common.c, or other assembly files * ------------------------------------------------------------------------------------------------------------------ */ -// --------------------- Interrupts ------------------------ - -// ------------------ Critical Sections -------------------- - // ---------------------- Yielding ------------------------- static inline void __attribute__((always_inline)) vPortYieldCore( BaseType_t xCoreID ) @@ -495,20 +221,6 @@ static inline void __attribute__((always_inline)) vPortYieldCore( BaseType_t xCo esp_crosscore_int_send_yield( xCoreID ); } -// TODO: Check this -#if 0 -static inline bool IRAM_ATTR xPortCanYield(void) -{ - uint32_t threshold = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); - /* when enter critical code, FreeRTOS will mask threshold to RVHAL_EXCM_LEVEL - * and exit critical code, will recover threshold value (1). so threshold <= 1 - * means not in critical code - */ - return (threshold <= 1); -} -#endif -// TODO: Check this end - // ----------------------- System -------------------------- static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void ) @@ -520,8 +232,6 @@ static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void ) * - These macros and functions need to be defined for IDF to compile * ------------------------------------------------------------------------------------------------------------------ */ -// --------------------- Interrupts ------------------------ - static inline BaseType_t xPortInIsrContext(void) { //Just call the FreeRTOS port interface version @@ -530,11 +240,33 @@ static inline BaseType_t xPortInIsrContext(void) // ---------------------- Spinlocks ------------------------ +/** + * @brief Wrapper for atomic compare-and-set instruction + * + * @note Isn't a real atomic CAS. + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this function should be renamed (due to void return type) + * + * @param[inout] addr Pointer to target address + * @param[in] compare Compare value + * @param[inout] set Pointer to set value + */ static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { compare_and_set_native(addr, compare, set); } +/** + * @brief Wrapper for atomic compare-and-set instruction in external RAM + * + * @note Isn't a real atomic CAS. + * @note [refactor-todo] check if we still need this + * @note [refactor-todo] Check if this function should be renamed (due to void return type) + * + * @param[inout] addr Pointer to target address + * @param[in] compare Compare value + * @param[inout] set Pointer to set value + */ static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { #if defined(CONFIG_SPIRAM) @@ -606,7 +338,7 @@ extern int xPortSwitchFlag; // --------------------- Debugging ------------------------- #if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION -#define UNTESTED_FUNCTION() { esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) +#define UNTESTED_FUNCTION() do{ esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) #else #define UNTESTED_FUNCTION() #endif diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c index f307d2e728..2902e39c16 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c @@ -46,10 +46,10 @@ static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but BaseType_t uxSchedulerRunning = 0; -UBaseType_t uxInterruptNesting = 0; +volatile UBaseType_t uxInterruptNesting = 0; portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED; portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED; -BaseType_t xPortSwitchFlag = 0; +volatile BaseType_t xPortSwitchFlag = 0; __attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE]; StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); @@ -61,26 +61,32 @@ static UBaseType_t port_uxCriticalOldInterruptStateIDF = 0; * - These need to be defined for IDF to compile * ------------------------------------------------------------------------------------------------------------------ */ -// --------------------- Interrupts ------------------------ - -BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void) -{ - /* For single core, this can be the same as xPortCheckIfInISR() because reading it is atomic */ - return uxInterruptNesting; -} - // ------------------ Critical Sections -------------------- -// ----------------------- System -------------------------- - -// TODO: Check this -#if 0 -uint32_t xPortGetTickRateHz(void) +void vPortEnterCriticalIDF(void) { - return (uint32_t)configTICK_RATE_HZ; + // Save current interrupt threshold and disable interrupts + UBaseType_t old_thresh = ulPortSetInterruptMask(); + // Update the IDF critical nesting count + port_uxCriticalNestingIDF++; + if (port_uxCriticalNestingIDF == 1) { + // Save a copy of the old interrupt threshold + port_uxCriticalOldInterruptStateIDF = (UBaseType_t) old_thresh; + } } -#endif -// TODO: Check this end + +void vPortExitCriticalIDF(void) +{ + if (port_uxCriticalNestingIDF > 0) { + port_uxCriticalNestingIDF--; + if (port_uxCriticalNestingIDF == 0) { + // Restore the saved interrupt threshold + vPortClearInterruptMask((int)port_uxCriticalOldInterruptStateIDF); + } + } +} + +// ----------------------- System -------------------------- #define STACK_WATCH_AREA_SIZE 32 #define STACK_WATCH_POINT_NUMBER (SOC_CPU_WATCHPOINTS_NUM - 1) @@ -282,6 +288,50 @@ void esp_startup_start_app(void) // --------------------- Interrupts ------------------------ +UBaseType_t ulPortSetInterruptMask(void) +{ + int ret; + unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + 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; +} + +void vPortClearInterruptMask(UBaseType_t 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" ); +} + BaseType_t xPortCheckIfInISR(void) { return uxInterruptNesting; @@ -289,19 +339,45 @@ BaseType_t xPortCheckIfInISR(void) // ------------------ Critical Sections -------------------- -void vPortTakeLock( portMUX_TYPE *lock ) +void IRAM_ATTR vPortTakeLock( portMUX_TYPE *lock ) { spinlock_acquire( lock, portMUX_NO_TIMEOUT); } -void vPortReleaseLock( portMUX_TYPE *lock ) +void IRAM_ATTR vPortReleaseLock( portMUX_TYPE *lock ) { spinlock_release( lock ); } // ---------------------- Yielding ------------------------- -// ----------------------- System -------------------------- +void vPortYield(void) +{ + if (uxInterruptNesting) { + vPortYieldFromISR(); + } else { + + esp_crosscore_int_send_yield(0); + /* There are 3-4 instructions of latency between triggering the software + interrupt and the CPU interrupt happening. Make sure it happened before + we return, otherwise vTaskDelay() may return and execute 1-2 + instructions before the delay actually happens. + + (We could use the WFI instruction here, but there is a chance that + the interrupt will happen while evaluating the other two conditions + for an instant yield, and if that happens then the WFI would be + waiting for the next interrupt to occur...) + */ + while (uxSchedulerRunning && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {} + } +} + +void vPortYieldFromISR( void ) +{ + //traceISR_EXIT_TO_SCHEDULER(); + uxSchedulerRunning = 1; + xPortSwitchFlag = 1; +} /* ------------------------------------------------ FreeRTOS Portable -------------------------------------------------- * - Provides implementation for functions required by FreeRTOS @@ -507,8 +583,6 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC return (StackType_t *)frame; } -// -------------------- Co-Processor ----------------------- - // ------- Thread Local Storage Pointers Deletion Callbacks ------- #if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) @@ -617,111 +691,3 @@ void vPortCleanUpTCB ( void *pxTCB ) vPortTLSPointersDelCb( pxTCB ); #endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ } - -/* ---------------------------------------------- Port Implementations ------------------------------------------------- - * - * ------------------------------------------------------------------------------------------------------------------ */ - -// ------------------ Critical Sections -------------------- - -void vPortEnterCriticalIDF(void) -{ - // Save current interrupt threshold and disable interrupts - int old_thresh = vPortSetInterruptMask(); - // Update the IDF critical nesting count - port_uxCriticalNestingIDF++; - if (port_uxCriticalNestingIDF == 1) { - // Save a copy of the old interrupt threshold - port_uxCriticalOldInterruptStateIDF = (UBaseType_t) old_thresh; - } -} - -void vPortExitCriticalIDF(void) -{ - if (port_uxCriticalNestingIDF > 0) { - port_uxCriticalNestingIDF--; - if (port_uxCriticalNestingIDF == 0) { - // Restore the saved interrupt threshold - vPortClearInterruptMask((int)port_uxCriticalOldInterruptStateIDF); - } - } -} - -// ---------------------- Yielding ------------------------- - -int vPortSetInterruptMask(void) -{ - int ret; - unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); - 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; -} - -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" ); -} - -void vPortYield(void) -{ - if (uxInterruptNesting) { - vPortYieldFromISR(); - } else { - - esp_crosscore_int_send_yield(0); - /* There are 3-4 instructions of latency between triggering the software - interrupt and the CPU interrupt happening. Make sure it happened before - we return, otherwise vTaskDelay() may return and execute 1-2 - instructions before the delay actually happens. - - (We could use the WFI instruction here, but there is a chance that - the interrupt will happen while evaluating the other two conditions - for an instant yield, and if that happens then the WFI would be - waiting for the next interrupt to occur...) - */ - while (uxSchedulerRunning && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {} - } -} - -void vPortYieldFromISR( void ) -{ - //traceISR_EXIT_TO_SCHEDULER(); - uxSchedulerRunning = 1; - xPortSwitchFlag = 1; -} - -void vPortYieldOtherCore(BaseType_t coreid) -{ - esp_crosscore_int_send_yield(coreid); -} diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h index bee5834be0..e89322d18a 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h @@ -507,7 +507,7 @@ extern int xPortSwitchFlag; // --------------------- Debugging ------------------------- #if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION -#define UNTESTED_FUNCTION() { esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) +#define UNTESTED_FUNCTION() do{ esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) #else #define UNTESTED_FUNCTION() #endif diff --git a/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h index edaf65ddb8..8ac4344e04 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h @@ -760,7 +760,7 @@ extern uint32_t port_switch_flag[]; // --------------------- Debugging ------------------------- #if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION -#define UNTESTED_FUNCTION() { esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) +#define UNTESTED_FUNCTION() do{ esp_rom_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) #else #define UNTESTED_FUNCTION() #endif diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 134b181f42..393d08d6e3 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -5,6 +5,7 @@ menu "FreeRTOS" config FREERTOS_SMP bool "Run the SMP FreeRTOS kernel instead (FEATURE UNDER DEVELOPMENT)" + depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 default "n" help This will cause the FreeRTOS component to compile with the SMP FreeRTOS kernel instead.