freertos: FreeRTOS SMP RISC-V port cleanup and enable esp32c3 in KConfig

This commit does general cleanup of the risc-v port files and restricts
FreeRTOS SMP config option to only esp32 and esp32c3.
This commit is contained in:
Sudeep Mohanty 2022-06-09 16:00:03 +02:00
parent 71eef9a9b0
commit 12e2312aaa
6 changed files with 132 additions and 471 deletions

View File

@ -17,54 +17,14 @@ This file get's pulled into assembly sources. Therefore, some includes need to b
#include <assert.h> //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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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.