kopia lustrzana https://github.com/espressif/esp-idf
freertos: Fix SMP RISC-V Port IDF Style critical sections
Previously the RV port was routing IDF style critical section API to call FreeRTOS style critical section API. For example, a call to "portENTER_CRITICAL(mux)" would eventually call `vTaskEnterCritical()" via the following call flow: - portENTER_CRITICAL(mux) - vPortEnterCritical() - portSET_INTERRUPT_MASK_FROM_ISR() - vTaskEnterCritical() This commit fixes the IDF style critical section by making sure that they are completely orthogonal to FreeRTOS critical sectionspull/9179/head
rodzic
8fd953c627
commit
71eef9a9b0
|
@ -377,17 +377,6 @@ static inline BaseType_t IRAM_ATTR xPortGetCoreID(void)
|
|||
#define portENABLE_INTERRUPTS() vPortClearInterruptMask(1)
|
||||
#define portRESTORE_INTERRUPTS(x) vPortClearInterruptMask(x)
|
||||
|
||||
#define portSET_INTERRUPT_MASK_FROM_ISR() ({ \
|
||||
unsigned int cur_level; \
|
||||
cur_level = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); \
|
||||
vTaskEnterCritical(); \
|
||||
cur_level; \
|
||||
})
|
||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) ({ \
|
||||
vTaskExitCritical(); \
|
||||
portRESTORE_INTERRUPTS(x); \
|
||||
})
|
||||
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
#define portGET_TASK_LOCK() vPortTakeLock(&port_xTaskLock)
|
||||
|
@ -395,8 +384,6 @@ static inline BaseType_t IRAM_ATTR xPortGetCoreID(void)
|
|||
#define portGET_ISR_LOCK() vPortTakeLock(&port_xISRLock)
|
||||
#define portRELEASE_ISR_LOCK() vPortReleaseLock(&port_xISRLock)
|
||||
|
||||
#define portENTER_CRITICAL_IDF(mux) {(void)mux; vPortEnterCritical();}
|
||||
#define portEXIT_CRITICAL_IDF(mux) {(void)mux; vPortExitCritical();}
|
||||
|
||||
//Critical sections used by FreeRTOS SMP
|
||||
extern void vTaskEnterCritical( void );
|
||||
|
@ -412,33 +399,16 @@ extern void vTaskExitCritical( void );
|
|||
#define portEXIT_CRITICAL(...) CHOOSE_MACRO_VA_ARG(portEXIT_CRITICAL_IDF, portEXIT_CRITICAL_SMP, ##__VA_ARGS__)(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define portTRY_ENTER_CRITICAL(mux, timeout) ({ \
|
||||
(void)mux; (void)timeout; \
|
||||
vPortEnterCritical(); \
|
||||
BaseType_t ret = pdPASS; \
|
||||
ret; \
|
||||
#define portSET_INTERRUPT_MASK_FROM_ISR() ({ \
|
||||
unsigned int cur_level; \
|
||||
cur_level = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG); \
|
||||
vTaskEnterCritical(); \
|
||||
cur_level; \
|
||||
})
|
||||
//In single-core RISC-V, we can use the same critical section API
|
||||
#define portENTER_CRITICAL_ISR(mux) portENTER_CRITICAL(mux)
|
||||
#define portEXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL(mux)
|
||||
#define portTRY_ENTER_CRITICAL_ISR(mux, timeout) portTRY_ENTER_CRITICAL(mux, timeout)
|
||||
|
||||
/* [refactor-todo] on RISC-V, both ISR and non-ISR cases result in the same call. We can redefine this macro */
|
||||
#define portENTER_CRITICAL_SAFE(mux) ({ \
|
||||
if (xPortInIsrContext()) { \
|
||||
portENTER_CRITICAL_ISR(mux); \
|
||||
} else { \
|
||||
portENTER_CRITICAL(mux); \
|
||||
} \
|
||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) ({ \
|
||||
vTaskExitCritical(); \
|
||||
portRESTORE_INTERRUPTS(x); \
|
||||
})
|
||||
#define portEXIT_CRITICAL_SAFE(mux) ({ \
|
||||
if (xPortInIsrContext()) { \
|
||||
portEXIT_CRITICAL_ISR(mux); \
|
||||
} else { \
|
||||
portEXIT_CRITICAL(mux); \
|
||||
} \
|
||||
})
|
||||
#define portTRY_ENTER_CRITICAL_SAFE(mux, timeout) portENTER_CRITICAL_SAFE(mux, timeout)
|
||||
|
||||
// ---------------------- Yielding -------------------------
|
||||
|
||||
|
@ -574,6 +544,25 @@ static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t comp
|
|||
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
/*
|
||||
IDF style critical sections which are orthogonal to FreeRTOS critical sections. However, on single core, the IDF style
|
||||
critical sections simply disable interrupts, thus we discard the lock and timeout arguments.
|
||||
*/
|
||||
void vPortEnterCriticalIDF(void);
|
||||
void vPortExitCriticalIDF(void);
|
||||
|
||||
//IDF task critical sections
|
||||
#define portTRY_ENTER_CRITICAL(lock, timeout) {((void) lock; (void) timeout; vPortEnterCriticalIDF(); pdPASS;)}
|
||||
#define portENTER_CRITICAL_IDF(lock) ({(void) lock; vPortEnterCriticalIDF();})
|
||||
#define portEXIT_CRITICAL_IDF(lock) ({(void) lock; vPortExitCriticalIDF();})
|
||||
//IDF ISR critical sections
|
||||
#define portTRY_ENTER_CRITICAL_ISR(lock, timeout) {((void) lock; (void) timeout; vPortEnterCriticalIDF(); pdPASS;)}
|
||||
#define portENTER_CRITICAL_ISR(lock) ({(void) lock; vPortEnterCriticalIDF();})
|
||||
#define portEXIT_CRITICAL_ISR(lock) ({(void) lock; vPortExitCriticalIDF();})
|
||||
//IDF safe critical sections (they're the same)
|
||||
#define portENTER_CRITICAL_SAFE(lock) ({(void) lock; vPortEnterCriticalIDF();})
|
||||
#define portEXIT_CRITICAL_SAFE(lock) ({(void) lock; vPortExitCriticalIDF();})
|
||||
|
||||
// ---------------------- Yielding -------------------------
|
||||
|
||||
static inline bool IRAM_ATTR xPortCanYield(void)
|
||||
|
|
|
@ -45,21 +45,17 @@
|
|||
|
||||
static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but
|
||||
|
||||
/**
|
||||
* @brief A variable is used to keep track of the critical section nesting.
|
||||
* @note This variable has to be stored as part of the task context and must be initialized to a non zero value
|
||||
* to ensure interrupts don't inadvertently become unmasked before the scheduler starts.
|
||||
* As it is stored as part of the task context it will automatically be set to 0 when the first task is started.
|
||||
*/
|
||||
static UBaseType_t uxCriticalNesting = 0;
|
||||
static UBaseType_t uxSavedInterruptState = 0;
|
||||
BaseType_t uxSchedulerRunning = 0;
|
||||
UBaseType_t uxInterruptNesting = 0;
|
||||
portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED;
|
||||
portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED;
|
||||
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)));
|
||||
portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED;
|
||||
portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// Variables used for IDF style critical sections. These are orthogonal to FreeRTOS critical sections
|
||||
static UBaseType_t port_uxCriticalNestingIDF = 0;
|
||||
static UBaseType_t port_uxCriticalOldInterruptStateIDF = 0;
|
||||
|
||||
/* ------------------------------------------------ IDF Compatibility --------------------------------------------------
|
||||
* - These need to be defined for IDF to compile
|
||||
|
@ -318,7 +314,7 @@ extern void esprv_intc_int_set_threshold(int); // FIXME, this function is in ROM
|
|||
BaseType_t xPortStartScheduler(void)
|
||||
{
|
||||
uxInterruptNesting = 0;
|
||||
uxCriticalNesting = 0;
|
||||
port_uxCriticalNestingIDF = 0;
|
||||
uxSchedulerRunning = 0;
|
||||
|
||||
/* Setup the hardware to generate the tick. */
|
||||
|
@ -421,7 +417,6 @@ __attribute__((noreturn)) static void _prvTaskExitError(void)
|
|||
|
||||
Artificially force an assert() to be triggered if configASSERT() is
|
||||
defined, then stop here so application writers can catch the error. */
|
||||
configASSERT(uxCriticalNesting == ~0UL);
|
||||
portDISABLE_INTERRUPTS();
|
||||
abort();
|
||||
}
|
||||
|
@ -629,22 +624,25 @@ void vPortCleanUpTCB ( void *pxTCB )
|
|||
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
void vPortEnterCritical(void)
|
||||
void vPortEnterCriticalIDF(void)
|
||||
{
|
||||
BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
uxCriticalNesting++;
|
||||
|
||||
if (uxCriticalNesting == 1) {
|
||||
uxSavedInterruptState = state;
|
||||
// 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 vPortExitCritical(void)
|
||||
void vPortExitCriticalIDF(void)
|
||||
{
|
||||
if (uxCriticalNesting > 0) {
|
||||
uxCriticalNesting--;
|
||||
if (uxCriticalNesting == 0) {
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptState);
|
||||
if (port_uxCriticalNestingIDF > 0) {
|
||||
port_uxCriticalNestingIDF--;
|
||||
if (port_uxCriticalNestingIDF == 0) {
|
||||
// Restore the saved interrupt threshold
|
||||
vPortClearInterruptMask((int)port_uxCriticalOldInterruptStateIDF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -712,7 +710,7 @@ void vPortYield(void)
|
|||
for an instant yield, and if that happens then the WFI would be
|
||||
waiting for the next interrupt to occur...)
|
||||
*/
|
||||
while (uxSchedulerRunning && uxCriticalNesting == 0 && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {}
|
||||
while (uxSchedulerRunning && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue