mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(freertos): Added changes for multi-core RISC-V port for FreeRTOS
This commit updates the FreeRTOS port layer for multi-core RISC-V targets.
This commit is contained in:
parent
14d87655da
commit
4e51c6b049
@ -30,11 +30,13 @@
|
||||
static const char *TAG = "rtc_module";
|
||||
#endif
|
||||
|
||||
// rtc_spinlock is used by other peripheral drivers
|
||||
portMUX_TYPE rtc_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
#if !CONFIG_IDF_TARGET_ESP32C6 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP32P4 // TODO: IDF-8008
|
||||
|
||||
#define NOT_REGISTERED (-1)
|
||||
|
||||
portMUX_TYPE rtc_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
// Disable the interrupt which cannot work without cache.
|
||||
static DRAM_ATTR uint32_t rtc_intr_cache;
|
||||
static DRAM_ATTR uint32_t rtc_intr_enabled;
|
||||
|
@ -36,6 +36,7 @@
|
||||
#define PORTMACRO_H
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
|
||||
/* Macros used instead ofsetoff() for better performance of interrupt handler */
|
||||
#define PORT_OFFSET_PX_STACK 0x30
|
||||
@ -128,6 +129,22 @@ typedef uint32_t TickType_t;
|
||||
|
||||
// --------------------- Interrupts ------------------------
|
||||
|
||||
/**
|
||||
* @brief Disable interrupts in a nested manner (meant to be called from ISRs)
|
||||
*
|
||||
* @warning Only applies to current CPU.
|
||||
* @return UBaseType_t Previous interrupt level
|
||||
*/
|
||||
UBaseType_t xPortSetInterruptMaskFromISR(void);
|
||||
|
||||
/**
|
||||
* @brief Reenable interrupts in a nested manner (meant to be called from ISRs)
|
||||
*
|
||||
* @warning Only applies to current CPU.
|
||||
* @param prev_int_level Previous interrupt level
|
||||
*/
|
||||
void vPortClearInterruptMaskFromISR(UBaseType_t prev_int_level);
|
||||
|
||||
/**
|
||||
* @brief Checks if the current core is in an ISR context
|
||||
*
|
||||
@ -155,63 +172,44 @@ BaseType_t xPortInIsrContext(void);
|
||||
BaseType_t xPortInterruptedFromISRContext(void);
|
||||
|
||||
/* ---------------------- 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
|
||||
* - Modifications made to critical sections to support SMP
|
||||
* - See "Critical Sections & Disabling Interrupts" in docs/api-guides/freertos-smp.rst for more details
|
||||
* - Remark: For the ESP32, portENTER_CRITICAL and portENTER_CRITICAL_ISR both alias vPortEnterCritical, meaning that
|
||||
* either function can be called both from ISR as well as task context. This is not standard FreeRTOS
|
||||
* behavior; please keep this in mind if you need any compatibility with other FreeRTOS implementations.
|
||||
* @note [refactor-todo] Check if these comments are still true
|
||||
* ------------------------------------------------------ */
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
/**
|
||||
* @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;
|
||||
|
||||
#else
|
||||
typedef spinlock_t portMUX_TYPE; /**< Spinlock type used by FreeRTOS critical sections */
|
||||
#endif
|
||||
|
||||
/**< Spinlock initializer */
|
||||
#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; \
|
||||
})
|
||||
#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 */
|
||||
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
/*
|
||||
This RISC-V port provides two kinds of critical section APIs, viz., one those take a spinlock argument and one those do
|
||||
not -
|
||||
|
||||
These sets of APIs are -
|
||||
1. vPortEnterCritical(void) and vPortExitCritical(void)
|
||||
2. vPortEnterCriticalMultiCore(portMUX_TYPE *mux) and vPortExitCriticalMultiCore(portMUX_TYPE *MUX)
|
||||
|
||||
This is primarily done to be compatible with some IDF examples such as esp_zigbee_gateway which have a reference
|
||||
to vPortEnterCritical(void) and vPortExitCritical(void) from precompiled libraries (.a).
|
||||
TODO: IDF-8089
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Enter a critical section
|
||||
*
|
||||
* - Simply disable interrupts
|
||||
* - Can be nested
|
||||
*/
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
void vPortEnterCritical(void);
|
||||
#else
|
||||
void vPortEnterCritical(portMUX_TYPE *mux);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Exit a critical section
|
||||
@ -219,12 +217,121 @@ void vPortEnterCritical(portMUX_TYPE *mux);
|
||||
* - Reenables interrupts
|
||||
* - Can be nested
|
||||
*/
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
void vPortExitCritical(void);
|
||||
#else
|
||||
void vPortExitCritical(portMUX_TYPE *mux);
|
||||
#endif
|
||||
|
||||
#if (configNUM_CORES > 1)
|
||||
/**
|
||||
* @brief Enter an SMP critical section with a timeout
|
||||
*
|
||||
* This function enters an SMP critical section by disabling interrupts then
|
||||
* taking a spinlock with a specified timeout.
|
||||
*
|
||||
* This function can be called in a nested manner.
|
||||
*
|
||||
* @note This function is made non-inline on purpose to reduce code size
|
||||
* @param mux Spinlock
|
||||
* @param timeout Timeout to wait for spinlock in number of CPU cycles.
|
||||
* Use portMUX_NO_TIMEOUT to wait indefinitely
|
||||
* Use portMUX_TRY_LOCK to only getting the spinlock a single time
|
||||
* @retval pdPASS Critical section entered (spinlock taken)
|
||||
* @retval pdFAIL If timed out waiting for spinlock (will not occur if using portMUX_NO_TIMEOUT)
|
||||
*/
|
||||
BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *mux, BaseType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Enter an SMP critical section
|
||||
*
|
||||
* This function enters an SMP critical section by disabling interrupts then
|
||||
* taking a spinlock with an unlimited timeout.
|
||||
*
|
||||
* This function can be called in a nested manner
|
||||
*
|
||||
* @param[in] mux Spinlock
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) vPortEnterCriticalMultiCore(portMUX_TYPE *mux);
|
||||
|
||||
/**
|
||||
* @brief Exit an SMP critical section
|
||||
*
|
||||
* This function can be called in a nested manner. On the outer most level of nesting, this function will:
|
||||
*
|
||||
* - Release the spinlock
|
||||
* - Restore the previous interrupt level before the critical section was entered
|
||||
*
|
||||
* If still nesting, this function simply decrements a critical nesting count
|
||||
*
|
||||
* @note This function is made non-inline on purpose to reduce code size
|
||||
* @param[in] mux Spinlock
|
||||
*/
|
||||
void vPortExitCriticalMultiCore(portMUX_TYPE *mux);
|
||||
|
||||
/**
|
||||
* @brief FreeRTOS Compliant version of xPortEnterCriticalTimeout()
|
||||
*
|
||||
* Compliant version of xPortEnterCriticalTimeout() will ensure that this is
|
||||
* called from a task context only. An abort is called otherwise.
|
||||
*
|
||||
* @note This function is made non-inline on purpose to reduce code size
|
||||
*
|
||||
* @param mux Spinlock
|
||||
* @param timeout Timeout
|
||||
* @return BaseType_t
|
||||
*/
|
||||
BaseType_t xPortEnterCriticalTimeoutCompliance(portMUX_TYPE *mux, BaseType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief FreeRTOS compliant version of vPortEnterCritical()
|
||||
*
|
||||
* Compliant version of vPortEnterCritical() will ensure that this is
|
||||
* called from a task context only. An abort is called otherwise.
|
||||
*
|
||||
* @param[in] mux Spinlock
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(portMUX_TYPE *mux);
|
||||
|
||||
/**
|
||||
* @brief FreeRTOS compliant version of vPortExitCritical()
|
||||
*
|
||||
* Compliant version of vPortExitCritical() will ensure that this is
|
||||
* called from a task context only. An abort is called otherwise.
|
||||
*
|
||||
* @note This function is made non-inline on purpose to reduce code size
|
||||
* @param[in] mux Spinlock
|
||||
*/
|
||||
void vPortExitCriticalCompliance(portMUX_TYPE *mux);
|
||||
|
||||
/**
|
||||
* @brief Safe version of enter critical timeout
|
||||
*
|
||||
* Safe version of enter critical will automatically select between
|
||||
* portTRY_ENTER_CRITICAL() and portTRY_ENTER_CRITICAL_ISR()
|
||||
*
|
||||
* @param mux Spinlock
|
||||
* @param timeout Timeout
|
||||
* @return BaseType_t
|
||||
*/
|
||||
static inline BaseType_t __attribute__((always_inline)) xPortEnterCriticalTimeoutSafe(portMUX_TYPE *mux, BaseType_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Safe version of enter critical
|
||||
*
|
||||
* Safe version of enter critical will automatically select between
|
||||
* portENTER_CRITICAL() and portENTER_CRITICAL_ISR()
|
||||
*
|
||||
* @param[in] mux Spinlock
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) vPortEnterCriticalSafe(portMUX_TYPE *mux);
|
||||
|
||||
/**
|
||||
* @brief Safe version of exit critical
|
||||
*
|
||||
* Safe version of enter critical will automatically select between
|
||||
* portEXIT_CRITICAL() and portEXIT_CRITICAL_ISR()
|
||||
*
|
||||
* @param[in] mux Spinlock
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) vPortExitCriticalSafe(portMUX_TYPE *mux);
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
|
||||
// ---------------------- Yielding -------------------------
|
||||
|
||||
@ -332,13 +439,50 @@ FORCE_INLINE_ATTR BaseType_t xPortGetCoreID(void)
|
||||
|
||||
#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK_FROM_ISR()
|
||||
#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK_FROM_ISR(1)
|
||||
#define portSET_INTERRUPT_MASK_FROM_ISR() vPortSetInterruptMask()
|
||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedStatusValue) vPortClearInterruptMask(uxSavedStatusValue)
|
||||
|
||||
/**
|
||||
* ISR versions to enable/disable interrupts
|
||||
*/
|
||||
#define portSET_INTERRUPT_MASK_FROM_ISR() xPortSetInterruptMaskFromISR()
|
||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(prev_level) vPortClearInterruptMaskFromISR(prev_level)
|
||||
|
||||
/**
|
||||
* @brief Used by FreeRTOS functions to call the correct version of critical section API
|
||||
*/
|
||||
#if ( configNUM_CORES > 1 )
|
||||
#define portCHECK_IF_IN_ISR() xPortInIsrContext()
|
||||
#endif
|
||||
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
#if (configNUM_CORES > 1)
|
||||
/**
|
||||
* @brief FreeRTOS critical section macros
|
||||
*
|
||||
* - Added a spinlock argument for SMP
|
||||
* - Can be nested
|
||||
* - Compliance versions will assert if regular critical section API is used in ISR context
|
||||
* - Safe versions can be called from either contexts
|
||||
*/
|
||||
#ifdef CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE
|
||||
#define portTRY_ENTER_CRITICAL(mux, timeout) xPortEnterCriticalTimeoutCompliance(mux, timeout)
|
||||
#define portENTER_CRITICAL(mux) vPortEnterCriticalCompliance(mux)
|
||||
#define portEXIT_CRITICAL(mux) vPortExitCriticalCompliance(mux)
|
||||
#else
|
||||
#define portTRY_ENTER_CRITICAL(mux, timeout) xPortEnterCriticalTimeout(mux, timeout)
|
||||
#define portENTER_CRITICAL(mux) vPortEnterCriticalMultiCore(mux)
|
||||
#define portEXIT_CRITICAL(mux) vPortExitCriticalMultiCore(mux)
|
||||
#endif /* CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE */
|
||||
|
||||
#define portTRY_ENTER_CRITICAL_ISR(mux, timeout) xPortEnterCriticalTimeout(mux, timeout)
|
||||
#define portENTER_CRITICAL_ISR(mux) vPortEnterCriticalMultiCore(mux)
|
||||
#define portEXIT_CRITICAL_ISR(mux) vPortExitCriticalMultiCore(mux)
|
||||
|
||||
#define portTRY_ENTER_CRITICAL_SAFE(mux, timeout) xPortEnterCriticalTimeoutSafe(mux)
|
||||
#define portENTER_CRITICAL_SAFE(mux) vPortEnterCriticalSafe(mux)
|
||||
#define portEXIT_CRITICAL_SAFE(mux) vPortExitCriticalSafe(mux)
|
||||
#else
|
||||
/* Single-core variants of the critical section macros */
|
||||
#define portENTER_CRITICAL(mux) {(void)mux; vPortEnterCritical();}
|
||||
#define portEXIT_CRITICAL(mux) {(void)mux; vPortExitCritical();}
|
||||
#define portTRY_ENTER_CRITICAL(mux, timeout) ({ \
|
||||
@ -347,16 +491,6 @@ FORCE_INLINE_ATTR BaseType_t xPortGetCoreID(void)
|
||||
BaseType_t ret = pdPASS; \
|
||||
ret; \
|
||||
})
|
||||
#else
|
||||
#define portENTER_CRITICAL(mux) {vPortEnterCritical(mux);}
|
||||
#define portEXIT_CRITICAL(mux) {vPortExitCritical(mux);}
|
||||
#define portTRY_ENTER_CRITICAL(mux, timeout) ({ \
|
||||
(void)timeout; \
|
||||
vPortEnterCritical(mux); \
|
||||
BaseType_t ret = pdPASS; \
|
||||
ret; \
|
||||
})
|
||||
#endif
|
||||
|
||||
//In single-core RISC-V, we can use the same critical section API
|
||||
#define portENTER_CRITICAL_ISR(mux) portENTER_CRITICAL(mux)
|
||||
@ -380,10 +514,8 @@ FORCE_INLINE_ATTR BaseType_t xPortGetCoreID(void)
|
||||
})
|
||||
#define portTRY_ENTER_CRITICAL_SAFE(mux, timeout) portENTER_CRITICAL_SAFE(mux, timeout)
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#define portCHECK_IF_IN_ISR() xPortInIsrContext()
|
||||
#endif
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
|
||||
// ---------------------- Yielding -------------------------
|
||||
|
||||
#define portYIELD() vPortYield()
|
||||
@ -458,17 +590,53 @@ extern void vPortCleanUpTCB ( void *pxTCB );
|
||||
|
||||
// --------------------- Interrupts ------------------------
|
||||
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
#if (configNUM_CORES > 1)
|
||||
static inline void __attribute__((always_inline)) vPortEnterCriticalMultiCore(portMUX_TYPE *mux)
|
||||
{
|
||||
xPortEnterCriticalTimeout(mux, portMUX_NO_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(portMUX_TYPE *mux)
|
||||
{
|
||||
xPortEnterCriticalTimeoutCompliance(mux, portMUX_NO_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline BaseType_t __attribute__((always_inline)) xPortEnterCriticalTimeoutSafe(portMUX_TYPE *mux, BaseType_t timeout)
|
||||
{
|
||||
BaseType_t ret;
|
||||
if (xPortInIsrContext()) {
|
||||
ret = portTRY_ENTER_CRITICAL_ISR(mux, timeout);
|
||||
} else {
|
||||
ret = portTRY_ENTER_CRITICAL(mux, timeout);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void __attribute__((always_inline)) vPortEnterCriticalSafe(portMUX_TYPE *mux)
|
||||
{
|
||||
xPortEnterCriticalTimeoutSafe(mux, portMUX_NO_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline void __attribute__((always_inline)) vPortExitCriticalSafe(portMUX_TYPE *mux)
|
||||
{
|
||||
if (xPortInIsrContext()) {
|
||||
portEXIT_CRITICAL_ISR(mux);
|
||||
} else {
|
||||
portEXIT_CRITICAL(mux);
|
||||
}
|
||||
}
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
|
||||
// ---------------------- Yielding -------------------------
|
||||
|
||||
FORCE_INLINE_ATTR bool xPortCanYield(void)
|
||||
{
|
||||
//TODO: IDF-7566
|
||||
#if SOC_INT_CLIC_SUPPORTED
|
||||
uint32_t threshold = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG + 0x10000 * xPortGetCoreID());
|
||||
threshold = threshold >> (24 + (8 - NLBITS));
|
||||
#else
|
||||
uint32_t threshold = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG);
|
||||
#endif
|
||||
#if SOC_INT_CLIC_SUPPORTED
|
||||
threshold = threshold >> (CLIC_CPU_INT_THRESH_S + (8 - NLBITS));
|
||||
#endif /* SOC_INT_CLIC_SUPPORTED */
|
||||
/* 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
|
||||
@ -513,8 +681,8 @@ bool xPortcheckValidStackMem(const void *ptr);
|
||||
// --------------------- App-Trace -------------------------
|
||||
|
||||
#if CONFIG_APPTRACE_SV_ENABLE
|
||||
extern int xPortSwitchFlag;
|
||||
#define os_task_switch_is_pended(_cpu_) (xPortSwitchFlag)
|
||||
extern volatile UBaseType_t xPortSwitchFlag[portNUM_PROCESSORS];
|
||||
#define os_task_switch_is_pended(_cpu_) (xPortSwitchFlag[_cpu_])
|
||||
#else
|
||||
#define os_task_switch_is_pended(_cpu_) (false)
|
||||
#endif
|
||||
|
@ -57,7 +57,6 @@
|
||||
#include "port_systick.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7566
|
||||
#include "soc/hp_system_reg.h"
|
||||
#endif
|
||||
|
||||
@ -78,42 +77,20 @@ _Static_assert(offsetof( StaticTask_t, pxDummy8 ) == PORT_OFFSET_PX_END_OF_STACK
|
||||
*
|
||||
* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
/**
|
||||
* @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; // Duplicate of xSchedulerRunning, accessible to port files
|
||||
UBaseType_t uxInterruptNesting = 0;
|
||||
BaseType_t xPortSwitchFlag = 0;
|
||||
__attribute__((aligned(16))) StackType_t xIsrStack[configISR_STACK_SIZE];
|
||||
StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
|
||||
|
||||
#else
|
||||
/* uxCriticalNesting will be increased by 1 each time one processor is entering a critical section
|
||||
* and will be decreased by 1 each time one processor is exiting a critical section
|
||||
*/
|
||||
volatile UBaseType_t uxCriticalNesting[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t uxSavedInterruptState[portNUM_PROCESSORS] = {0};
|
||||
volatile BaseType_t uxSchedulerRunning[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t uxInterruptNesting[portNUM_PROCESSORS] = {0};
|
||||
volatile BaseType_t xPortSwitchFlag[portNUM_PROCESSORS] = {0};
|
||||
/* core0 interrupt stack space */
|
||||
__attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE];
|
||||
/* core1 interrupt stack space */
|
||||
__attribute__((aligned(16))) static StackType_t xIsrStack1[configISR_STACK_SIZE];
|
||||
/* core0 interrupt stack top, passed to sp */
|
||||
StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
|
||||
/* core1 interrupt stack top, passed to sp */
|
||||
StackType_t *xIsrStackTop1 = &xIsrStack1[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
|
||||
#endif
|
||||
|
||||
volatile UBaseType_t port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis
|
||||
volatile UBaseType_t port_uxInterruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c
|
||||
volatile UBaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t xPortSwitchFlag[portNUM_PROCESSORS] = {0};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Interrupt stack. The size of the interrupt stack is determined by the config
|
||||
* parameter "configISR_STACK_SIZE" in FreeRTOSConfig.h
|
||||
*******************************************************************************
|
||||
*/
|
||||
__attribute__((aligned(16))) StackType_t xIsrStack[portNUM_PROCESSORS][configISR_STACK_SIZE];
|
||||
StackType_t *xIsrStackTop[portNUM_PROCESSORS] = {0};
|
||||
|
||||
/* ------------------------------------------------ FreeRTOS Portable --------------------------------------------------
|
||||
* - Provides implementation for functions required by FreeRTOS
|
||||
@ -122,30 +99,33 @@ StackType_t *xIsrStackTop1 = &xIsrStack1[0] + (configISR_STACK_SIZE & (~((portPO
|
||||
|
||||
// ----------------- Scheduler Start/End -------------------
|
||||
|
||||
//TODO: IDF-7566
|
||||
BaseType_t xPortStartScheduler(void)
|
||||
{
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
uxInterruptNesting = 0;
|
||||
uxCriticalNesting = 0;
|
||||
uxSchedulerRunning = 0;
|
||||
#else
|
||||
/* Initialize all kernel state tracking variables */
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
uxInterruptNesting[coreID] = 0;
|
||||
uxCriticalNesting[coreID] = 0;
|
||||
uxSchedulerRunning[coreID] = 0;
|
||||
#endif
|
||||
port_uxInterruptNesting[coreID] = 0;
|
||||
port_uxCriticalNesting[coreID] = 0;
|
||||
port_xSchedulerRunning[coreID] = 0;
|
||||
|
||||
/* Initialize ISR Stack top */
|
||||
for (int i = 0; i < portNUM_PROCESSORS; i++) {
|
||||
xIsrStackTop[i] = &xIsrStack[i][0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
|
||||
}
|
||||
|
||||
/* Setup the hardware to generate the tick. */
|
||||
vPortSetupTimer();
|
||||
|
||||
#if !SOC_INT_CLIC_SUPPORTED
|
||||
esprv_intc_int_set_threshold(1); /* set global INTC masking level */
|
||||
#else
|
||||
esprv_intc_int_set_threshold(0); /* set global CLIC masking level. When CLIC is supported, all interrupt priority levels less than or equal to the threshold level are masked. */
|
||||
#endif /* !SOC_INT_CLIC_SUPPORTED */
|
||||
rv_utils_intr_global_enable();
|
||||
|
||||
vPortYield();
|
||||
|
||||
/*Should not get here*/
|
||||
return pdFALSE;
|
||||
/* Should not get here */
|
||||
return pdTRUE;
|
||||
}
|
||||
|
||||
void vPortEndScheduler(void)
|
||||
@ -272,7 +252,7 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackFrame(UBaseType_t uxStackPointer,
|
||||
/*
|
||||
Allocate space for the task's starting interrupt stack frame.
|
||||
- The stack frame must be allocated to a 16-byte aligned address.
|
||||
- We use XT_STK_FRMSZ (instead of sizeof(XtExcFrame)) as it rounds up the total size to a multiple of 16.
|
||||
- We use RV_STK_FRMSZ as it rounds up the total size to a multiple of 16.
|
||||
*/
|
||||
uxStackPointer = STACKPTR_ALIGN_DOWN(16, uxStackPointer - RV_STK_FRMSZ);
|
||||
|
||||
@ -323,6 +303,8 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
|
||||
UBaseType_t uxStackPointer = (UBaseType_t)pxTopOfStack;
|
||||
configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0);
|
||||
|
||||
// IDF-7770: Support FPU context save area for P4
|
||||
|
||||
// Initialize GCC TLS area
|
||||
uint32_t threadptr_reg_init;
|
||||
uxStackPointer = uxInitialiseStackTLS(uxStackPointer, &threadptr_reg_init);
|
||||
@ -334,7 +316,6 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
|
||||
|
||||
// Return the task's current stack pointer address which should point to the starting interrupt stack frame
|
||||
return (StackType_t *)uxStackPointer;
|
||||
//TODO: IDF-2393
|
||||
}
|
||||
|
||||
|
||||
@ -345,112 +326,47 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
|
||||
|
||||
// --------------------- Interrupts ------------------------
|
||||
|
||||
//TODO: IDF-7566
|
||||
BaseType_t xPortInIsrContext(void)
|
||||
{
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
return uxInterruptNesting;
|
||||
#if (configNUM_CORES > 1)
|
||||
unsigned int irqStatus;
|
||||
BaseType_t ret;
|
||||
|
||||
/* Disable interrupts to fetch the coreID atomically */
|
||||
irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
|
||||
/* Return the interrupt nexting counter for this core */
|
||||
ret = port_uxInterruptNesting[xPortGetCoreID()];
|
||||
|
||||
/* Restore interrupts */
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(irqStatus);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
return uxInterruptNesting[coreID];
|
||||
#endif
|
||||
/* Optimize the call for single-core targets */
|
||||
return port_uxInterruptNesting[0];
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
}
|
||||
|
||||
BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void)
|
||||
{
|
||||
/* For single core, this can be the same as xPortInIsrContext() because reading it is atomic */
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
return uxInterruptNesting;
|
||||
#else
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
return uxInterruptNesting[coreID];
|
||||
#endif
|
||||
/* Return the interrupt nexting counter for this core */
|
||||
return port_uxInterruptNesting[xPortGetCoreID()];
|
||||
}
|
||||
|
||||
// ---------------------- Spinlocks ------------------------
|
||||
|
||||
|
||||
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
void vPortEnterCritical(void)
|
||||
UBaseType_t xPortSetInterruptMaskFromISR(void)
|
||||
{
|
||||
BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
uxCriticalNesting++;
|
||||
UBaseType_t prev_int_level = 0;
|
||||
|
||||
if (uxCriticalNesting == 1) {
|
||||
uxSavedInterruptState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void vPortExitCritical(void)
|
||||
{
|
||||
if (uxCriticalNesting > 0) {
|
||||
uxCriticalNesting--;
|
||||
if (uxCriticalNesting == 0) {
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void vPortEnterCritical(portMUX_TYPE *mux)
|
||||
{
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
|
||||
spinlock_acquire((spinlock_t *)mux, SPINLOCK_WAIT_FOREVER);
|
||||
uxCriticalNesting[coreID]++;
|
||||
|
||||
if (uxCriticalNesting[coreID] == 1) {
|
||||
uxSavedInterruptState[coreID] = state;
|
||||
}
|
||||
}
|
||||
|
||||
void vPortExitCritical(portMUX_TYPE *mux)
|
||||
{
|
||||
spinlock_release((spinlock_t *)mux);
|
||||
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
if (uxCriticalNesting[coreID] > 0) {
|
||||
uxCriticalNesting[coreID]--;
|
||||
if (uxCriticalNesting[coreID] == 0) {
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptState[coreID]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------------------- Yielding -------------------------
|
||||
|
||||
//TODO: IDF-7566
|
||||
int vPortSetInterruptMask(void)
|
||||
{
|
||||
int ret;
|
||||
#if !SOC_INT_CLIC_SUPPORTED
|
||||
unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
|
||||
ret = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG);
|
||||
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
prev_int_level = 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);
|
||||
#else
|
||||
#define RVHAL_EXCM_THRESHOLD_VALUE (((RVHAL_EXCM_LEVEL << (8 - NLBITS)) | 0x1f) << CLIC_CPU_INT_THRESH_S)
|
||||
|
||||
REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, RVHAL_EXCM_THRESHOLD_VALUE);
|
||||
/**
|
||||
* TODO: IDF-7898
|
||||
* Here is an issue that,
|
||||
* 1. Set the CLIC_INT_THRESH_REG to mask off interrupts whose level is lower than `intlevel`.
|
||||
* 2. Set MSTATUS_MIE (global interrupt), then program may jump to interrupt vector.
|
||||
* 3. The register value change in Step 1 may happen during Step 2.
|
||||
*
|
||||
* To prevent this, here a fence is used
|
||||
*/
|
||||
rv_utils_memory_barrier();
|
||||
RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE);
|
||||
#endif
|
||||
/* When CLIC is supported, all interrupt priority levels less than or equal to the threshold level are masked. */
|
||||
prev_int_level = rv_utils_set_intlevel(RVHAL_EXCM_LEVEL - 1);
|
||||
#endif /* !SOC_INIT_CLIC_SUPPORTED */
|
||||
/**
|
||||
* In theory, this function should not return immediately as there is a
|
||||
* delay between the moment we mask the interrupt threshold register and
|
||||
@ -462,12 +378,16 @@ int vPortSetInterruptMask(void)
|
||||
* followed by two instructions: `ret` and `csrrs` (RV_SET_CSR).
|
||||
* That's why we don't need any additional nop instructions here.
|
||||
*/
|
||||
return ret;
|
||||
return prev_int_level;
|
||||
}
|
||||
|
||||
void vPortClearInterruptMask(int mask)
|
||||
void vPortClearInterruptMaskFromISR(UBaseType_t prev_int_level)
|
||||
{
|
||||
REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, mask);
|
||||
#if !SOC_INT_CLIC_SUPPORTED
|
||||
REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, prev_int_level);
|
||||
#else
|
||||
rv_utils_restore_intlevel(prev_int_level);
|
||||
#endif /* SOC_INIT_CLIC_SUPPORTED */
|
||||
/**
|
||||
* The delay between the moment we unmask the interrupt threshold register
|
||||
* and the moment the potential requested interrupt is triggered is not
|
||||
@ -488,41 +408,123 @@ void vPortClearInterruptMask(int mask)
|
||||
asm volatile ( "nop" );
|
||||
}
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
void vPortYield(void)
|
||||
// ------------------ Critical Sections --------------------
|
||||
|
||||
#if (configNUM_CORES > 1)
|
||||
BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TYPE *mux, BaseType_t timeout)
|
||||
{
|
||||
if (uxInterruptNesting) {
|
||||
vPortYieldFromISR();
|
||||
} else {
|
||||
/* Interrupts may already be disabled (if this function is called in nested
|
||||
* manner). However, there's no atomic operation that will allow us to check,
|
||||
* thus we have to disable interrupts again anyways.
|
||||
*
|
||||
* However, if this is call is NOT nested (i.e., the first call to enter a
|
||||
* critical section), we will save the previous interrupt level so that the
|
||||
* saved level can be restored on the last call to exit the critical.
|
||||
*/
|
||||
BaseType_t xOldInterruptLevel = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
if (!spinlock_acquire(mux, timeout)) {
|
||||
//Timed out attempting to get spinlock. Restore previous interrupt level and return
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(xOldInterruptLevel);
|
||||
return pdFAIL;
|
||||
}
|
||||
//Spinlock acquired. Increment the critical nesting count.
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
BaseType_t newNesting = port_uxCriticalNesting[coreID] + 1;
|
||||
port_uxCriticalNesting[coreID] = newNesting;
|
||||
//If this is the first entry to a critical section. Save the old interrupt level.
|
||||
if ( newNesting == 1 ) {
|
||||
port_uxOldInterruptState[coreID] = xOldInterruptLevel;
|
||||
}
|
||||
return pdPASS;
|
||||
}
|
||||
|
||||
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.
|
||||
void __attribute__((optimize("-O3"))) vPortExitCriticalMultiCore(portMUX_TYPE *mux)
|
||||
{
|
||||
/* This function may be called in a nested manner. Therefore, we only need
|
||||
* to reenable interrupts if this is the last call to exit the critical. We
|
||||
* can use the nesting count to determine whether this is the last exit call.
|
||||
*/
|
||||
spinlock_release(mux);
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
BaseType_t nesting = port_uxCriticalNesting[coreID];
|
||||
|
||||
(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 && uxCriticalNesting == 0 && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {}
|
||||
if (nesting > 0) {
|
||||
nesting--;
|
||||
port_uxCriticalNesting[coreID] = nesting;
|
||||
//This is the last exit call, restore the saved interrupt level
|
||||
if ( nesting == 0 ) {
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(port_uxOldInterruptState[coreID]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vPortYieldFromISR( void )
|
||||
|
||||
BaseType_t xPortEnterCriticalTimeoutCompliance(portMUX_TYPE *mux, BaseType_t timeout)
|
||||
{
|
||||
traceISR_EXIT_TO_SCHEDULER();
|
||||
uxSchedulerRunning = 1;
|
||||
xPortSwitchFlag = 1;
|
||||
BaseType_t ret;
|
||||
if (!xPortInIsrContext()) {
|
||||
ret = xPortEnterCriticalTimeout(mux, timeout);
|
||||
} else {
|
||||
esp_rom_printf("port*_CRITICAL called from ISR context. Aborting!\n");
|
||||
abort();
|
||||
ret = pdFAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
void vPortExitCriticalCompliance(portMUX_TYPE *mux)
|
||||
{
|
||||
if (!xPortInIsrContext()) {
|
||||
vPortExitCriticalMultiCore(mux);
|
||||
} else {
|
||||
esp_rom_printf("port*_CRITICAL called from ISR context. Aborting!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
|
||||
void vPortEnterCritical(void)
|
||||
{
|
||||
#if (configNUM_CORES > 1)
|
||||
esp_rom_printf("vPortEnterCritical(void) is not supported on single-core targets. Please use vPortEnterCriticalMultiCore(portMUX_TYPE *mux) instead.\n");
|
||||
abort();
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
port_uxCriticalNesting[0]++;
|
||||
|
||||
if (port_uxCriticalNesting[0] == 1) {
|
||||
port_uxOldInterruptState[0] = state;
|
||||
}
|
||||
}
|
||||
|
||||
void vPortExitCritical(void)
|
||||
{
|
||||
#if (configNUM_CORES > 1)
|
||||
esp_rom_printf("vPortExitCritical(void) is not supported on single-core targets. Please use vPortExitCriticalMultiCore(portMUX_TYPE *mux) instead.\n");
|
||||
abort();
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
if (port_uxCriticalNesting[0] > 0) {
|
||||
port_uxCriticalNesting[0]--;
|
||||
if (port_uxCriticalNesting[0] == 0) {
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(port_uxOldInterruptState[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- Yielding -------------------------
|
||||
|
||||
void vPortYield(void)
|
||||
{
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
if (uxInterruptNesting[coreID]) {
|
||||
int system_cpu_int_reg;
|
||||
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
system_cpu_int_reg = SYSTEM_CPU_INTR_FROM_CPU_0_REG;
|
||||
#else
|
||||
system_cpu_int_reg = HP_SYSTEM_CPU_INT_FROM_CPU_0_REG;
|
||||
#endif /* !CONFIG_IDF_TARGET_ESP32P4 */
|
||||
|
||||
if (port_uxInterruptNesting[coreID]) {
|
||||
vPortYieldFromISR();
|
||||
} else {
|
||||
esp_crosscore_int_send_yield(coreID);
|
||||
@ -536,7 +538,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[coreID] && uxCriticalNesting[coreID] == 0 && REG_READ(HP_SYSTEM_CPU_INT_FROM_CPU_0_REG + 4*coreID) != 0) {}
|
||||
while (port_xSchedulerRunning[coreID] && port_uxCriticalNesting[coreID] == 0 && REG_READ(system_cpu_int_reg + 4 * coreID) != 0) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,10 +546,9 @@ void vPortYieldFromISR( void )
|
||||
{
|
||||
traceISR_EXIT_TO_SCHEDULER();
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
uxSchedulerRunning[coreID] = 1;
|
||||
port_xSchedulerRunning[coreID] = 1;
|
||||
xPortSwitchFlag[coreID] = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void vPortYieldOtherCore(BaseType_t coreid)
|
||||
{
|
||||
|
@ -5,17 +5,16 @@
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#include "portmacro.h"
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
#include "esp_private/hw_stack_guard.h"
|
||||
#endif
|
||||
|
||||
.global uxInterruptNesting
|
||||
.global uxSchedulerRunning
|
||||
.global port_uxInterruptNesting
|
||||
.global port_xSchedulerRunning
|
||||
.global xIsrStackTop
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7566
|
||||
.global xIsrStackTop1
|
||||
#endif
|
||||
.global pxCurrentTCB
|
||||
.global vTaskSwitchContext
|
||||
.global xPortSwitchFlag
|
||||
@ -31,8 +30,9 @@
|
||||
.section .text
|
||||
|
||||
/**
|
||||
* This function makes the RTOS aware about a ISR entering, it takes the
|
||||
* current task stack saved, places into the TCB, loads the ISR stack.
|
||||
* This function makes the RTOS aware about an ISR entering. It takes the
|
||||
* current task stack pointer and places it into the pxCurrentTCB.
|
||||
* It then loads the ISR stack into sp.
|
||||
* TODO: ISR nesting code improvements ?
|
||||
*/
|
||||
|
||||
@ -42,59 +42,53 @@ rtos_int_enter:
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7861
|
||||
/* preserve the return address */
|
||||
mv t1, ra
|
||||
mv t2, a0
|
||||
mv t1, ra
|
||||
mv t2, a0
|
||||
#endif
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
/* scheduler not enabled, jump directly to ISR handler */
|
||||
lw t0, uxSchedulerRunning
|
||||
beq t0,zero, rtos_enter_end
|
||||
/* If the scheduler is not enabled, jump directly to the ISR handler */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
csrr t6, mhartid /* t6 = coreID */
|
||||
slli t6, t6, 2 /* t6 = coreID * 4 */
|
||||
la t0, port_xSchedulerRunning /* t0 = &port_xSchedulerRunning */
|
||||
add t0, t0, t6 /* t0 = &port_xSchedulerRunning[coreID] */
|
||||
lw t0, (t0) /* t0 = port_xSchedulerRunning[coreID] */
|
||||
#else
|
||||
/* scheduler not enabled, jump directly to ISR handler */
|
||||
csrr t6, mhartid /* t6 = coreID */
|
||||
slli t6, t6, 2 /* t6 = coreID * 4 */
|
||||
la t0, uxSchedulerRunning /* t0 = &uxSchedulerRunning */
|
||||
add t0, t0, t6 /* t0 = &uxSchedulerRunning[coreID] */
|
||||
lw t0, (t0) /* t0 = uxSchedulerRunning[coreID] */
|
||||
beq t0,zero, rtos_enter_end
|
||||
#endif
|
||||
lw t0, port_xSchedulerRunning /* t0 = port_xSchedulerRunning */
|
||||
#endif /* (configNUM_CORES > 1) */
|
||||
beq t0, zero, rtos_int_enter_end /* if (port_xSchedulerRunning[coreID] == 0) jump to rtos_int_enter_end */
|
||||
|
||||
/* increments the ISR nesting count */
|
||||
la t3, uxInterruptNesting
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7566
|
||||
add t3, t3, t6
|
||||
#endif
|
||||
lw t4, 0x0(t3)
|
||||
addi t5,t4,1
|
||||
sw t5, 0x0(t3)
|
||||
/* Increment the ISR nesting count */
|
||||
la t3, port_uxInterruptNesting /* t3 = &port_usInterruptNesting */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
add t3, t3, t6 /* t3 = &port_uxInterruptNesting[coreID] // t6 already contains coreID * 4 */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
lw t4, 0x0(t3) /* t4 = port_uxInterruptNesting[coreID] */
|
||||
addi t5, t4, 1 /* t5 = t4 + 1 */
|
||||
sw t5, 0x0(t3) /* port_uxInterruptNesting[coreID] = t5 */
|
||||
|
||||
/* If reached here from another low-prio ISR, skip stack pushing to TCB */
|
||||
bne t4,zero, rtos_enter_end
|
||||
/* If we reached here from another low-prio ISR, i.e, port_uxInterruptNesting[coreID] > 0, then skip stack pushing to TCB */
|
||||
bne t4, zero, rtos_int_enter_end /* if (port_uxInterruptNesting[coreID] > 0) jump to rtos_int_enter_end */
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* esp_hw_stack_guard_monitor_stop(); */
|
||||
ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0
|
||||
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
|
||||
|
||||
/* Save current TCB and load the ISR stack */
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
lw t0, pxCurrentTCB
|
||||
sw sp, 0x0(t0)
|
||||
lw sp, xIsrStackTop
|
||||
/* Save the current sp in pxCurrentTCB[coreID] and load the ISR stack on to sp */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
la t0, pxCurrentTCB /* t0 = &pxCurrentTCB */
|
||||
add t0, t0, t6 /* t0 = &pxCurrentTCB[coreID] // t6 already contains coreID * 4 */
|
||||
lw t0, (t0) /* t0 = pxCurrentTCB[coreID] */
|
||||
sw sp, 0x0(t0) /* pxCurrentTCB[coreID] = sp */
|
||||
la t0, xIsrStackTop /* t0 = &xIsrStackTop */
|
||||
add t0, t0, t6 /* t0 = &xIsrStackTop[coreID] // t6 already contains coreID * 4 */
|
||||
lw sp, 0x0(t0) /* sp = xIsrStackTop[coreID] */
|
||||
#else
|
||||
la t0, pxCurrentTCB /* t0 = &pxCurrentTCB */
|
||||
add t0, t0, t6 /* t0 = &pxCurrentTCB[coreID] */
|
||||
lw t0, (t0) /* t0 = pxCurrentTCB[coreID] */
|
||||
sw t2, 0x0(t0)
|
||||
lw sp, xIsrStackTop
|
||||
csrr t6, mhartid
|
||||
beq t6, zero, rtos_enter_end
|
||||
lw sp, xIsrStackTop1
|
||||
#endif
|
||||
lw t0, pxCurrentTCB /* t0 = pxCurrentTCB */
|
||||
sw sp, 0x0(t0) /* pxCurrentTCB = sp */
|
||||
lw sp, xIsrStackTop /* sp = xIsrStackTop */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* esp_hw_stack_guard_set_bounds(xIsrStack, xIsrStackTop); */
|
||||
@ -104,91 +98,99 @@ rtos_int_enter:
|
||||
ESP_HW_STACK_GUARD_MONITOR_START_CPU0
|
||||
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
|
||||
|
||||
rtos_enter_end:
|
||||
rtos_int_enter_end:
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7861
|
||||
mv ra, t1
|
||||
mv ra, t1
|
||||
#endif
|
||||
ret
|
||||
|
||||
/**
|
||||
* Recovers the next task to run stack pointer.
|
||||
* Restore the stack pointer of the next task to run.
|
||||
*/
|
||||
.global rtos_int_exit
|
||||
.type rtos_int_exit, @function
|
||||
rtos_int_exit:
|
||||
/* may skip RTOS aware interrupt since scheduler was not started */
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
lw t0, uxSchedulerRunning
|
||||
/* Skip if the scheduler was not started */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
csrr t1, mhartid /* t1 = coreID */
|
||||
slli t1, t1, 2 /* t1 = t1 * 4 */
|
||||
la t0, port_xSchedulerRunning /* t0 = &port_xSchedulerRunning */
|
||||
add t0, t0, t1 /* t0 = &port_xSchedulerRunning[coreID] */
|
||||
lw t0, (t0) /* t0 = port_xSchedulerRunning[coreID] */
|
||||
#else
|
||||
csrr t1, mhartid
|
||||
slli t1, t1, 2
|
||||
la t0, uxSchedulerRunning /* t0 = &uxSchedulerRunning */
|
||||
add t0, t0, t1 /* t0 = &uxSchedulerRunning[coreID] */
|
||||
lw t0, (t0)
|
||||
#endif
|
||||
beq t0,zero, rtos_exit_end
|
||||
lw t0, port_xSchedulerRunning /* t0 = port_xSchedulerRunning */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
beq t0, zero, rtos_int_exit_end /* if (port_uxSchewdulerRunning == 0) jump to rtos_int_exit_end */
|
||||
|
||||
/* update nesting interrupts counter */
|
||||
la t2, uxInterruptNesting
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7566
|
||||
add t2, t2, t1
|
||||
/* Decrement interrupt nesting counter */
|
||||
la t2, port_uxInterruptNesting /* t2 = &port_uxInterruptNesting */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
add t2, t2, t1 /* t2 = &port_uxInterruptNesting[coreID] // t1 already contains coreID * 4 */
|
||||
#endif
|
||||
lw t3, 0x0(t2)
|
||||
lw t3, 0x0(t2) /* t3 = port_uxInterruptNesting[coreID] */
|
||||
|
||||
/* Already zero, protect against underflow */
|
||||
beq t3, zero, isr_skip_decrement
|
||||
addi t3,t3, -1
|
||||
sw t3, 0x0(t2)
|
||||
/* If the interrupt nesting counter is already zero, then protect against underflow */
|
||||
beq t3, zero, isr_skip_decrement /* if (port_uxInterruptNesting[coreID] == 0) jump to isr_skip_decrement */
|
||||
addi t3, t3, -1 /* t3 = t3 - 1 */
|
||||
sw t3, 0x0(t2) /* port_uxInterruptNesting[coreID] = t3 */
|
||||
|
||||
isr_skip_decrement:
|
||||
|
||||
/* may still have interrupts pending, skip section below and exit */
|
||||
bne t3,zero,rtos_exit_end
|
||||
/* We may still have interrupts pending. Skip the section below and exit */
|
||||
bne t3, zero, rtos_int_exit_end /* (if port_uxInterruptNesting[coreID] > 0) jump to rtos_int_exit_end */
|
||||
|
||||
/* Schedule the next task if a yield is pending */
|
||||
la t0, xPortSwitchFlag
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7566
|
||||
add t0, t0, t1
|
||||
#endif
|
||||
lw t2, 0x0(t0)
|
||||
beq t2, zero, no_switch
|
||||
/* Schedule the next task if an yield is pending */
|
||||
la t0, xPortSwitchFlag /* t0 = &xPortSwitchFlag */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
add t0, t0, t1 /* t0 = &xPortSwitchFlag[coreID] // t1 already contains coreID * 4 */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
lw t2, 0x0(t0) /* t2 = xPortSwitchFlag[coreID] */
|
||||
beq t2, zero, no_switch /* if (xPortSwitchFlag[coreID] == 0) jump to no_switch */
|
||||
|
||||
/* preserve return address and schedule next task
|
||||
stack pointer for riscv should always be 16 byte aligned */
|
||||
addi sp,sp,-16
|
||||
sw ra, 0(sp)
|
||||
call vTaskSwitchContext
|
||||
lw ra, 0(sp)
|
||||
addi sp, sp, 16
|
||||
/* Save the return address on the stack and create space on the stack for the c-routine call to schedule
|
||||
* the next task. Stack pointer for RISC-V should always be 16 byte aligned. After the switch, restore
|
||||
* the return address and sp.
|
||||
*/
|
||||
addi sp, sp, -16 /* sp = sp - 16 */
|
||||
sw ra, 0(sp) /* sp = ra */
|
||||
call vTaskSwitchContext /* vTaskSwitchContext() */
|
||||
lw ra, 0(sp) /* ra = sp */
|
||||
addi sp, sp, 16 /* sp = sp + 16 */
|
||||
|
||||
/* Clears the switch pending flag */
|
||||
la t0, xPortSwitchFlag
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7566
|
||||
/* Clear the switch pending flag */
|
||||
la t0, xPortSwitchFlag /* t0 = &xPortSwitchFlag */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
/* c routine vTaskSwitchContext may change the temp registers, so we read again */
|
||||
csrr t3, mhartid
|
||||
slli t3, t3, 2
|
||||
add t0, t0, t3
|
||||
#endif
|
||||
mv t2, zero
|
||||
sw t2, 0x0(t0)
|
||||
csrr t3, mhartid /* t3 = coreID */
|
||||
slli t3, t3, 2 /* t3 = t3 * 4 */
|
||||
add t0, t0, t3 /* t0 = &xPortSwitchFlag[coreID] */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
mv t2, zero /* t2 = 0 */
|
||||
sw t2, 0x0(t0) /* xPortSwitchFlag[coreID] = t2 */
|
||||
|
||||
no_switch:
|
||||
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4 //TODO: IDF-7566
|
||||
#if SOC_INT_CLIC_SUPPORTED
|
||||
/* Recover the stack of next task and prepare to exit */
|
||||
la a0, pxCurrentTCB /* a0 = &pxCurrentTCB */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
csrr t3, mhartid /* t3 = coreID */
|
||||
slli t3, t3, 2 /* t3 = t3 * 4 */
|
||||
add a0, a0, t3 /* a0 = &pxCurrentTCB[coreID] */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
lw a0, (a0) /* a0 = pxCurrentTCB[coreID] */
|
||||
lw a0, 0x0(a0) /* a0 = previous sp */
|
||||
#else
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* esp_hw_stack_guard_monitor_stop(); */
|
||||
ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0
|
||||
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
|
||||
|
||||
/* Recover the stack of next task */
|
||||
lw t0, pxCurrentTCB
|
||||
lw sp, 0x0(t0)
|
||||
lw t0, pxCurrentTCB
|
||||
lw sp, 0x0(t0)
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* esp_hw_stack_guard_set_bounds(pxCurrentTCB[0]->pxStack,
|
||||
@ -200,17 +202,7 @@ no_switch:
|
||||
/* esp_hw_stack_guard_monitor_start(); */
|
||||
ESP_HW_STACK_GUARD_MONITOR_START_CPU0
|
||||
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
|
||||
#endif /* SOC_INT_CLIC_SUPPORTED */
|
||||
|
||||
#else
|
||||
/* Recover the stack of next task and prepare to exit : */
|
||||
la a0, pxCurrentTCB
|
||||
/* We may come here from a branch, so we re-cal here */
|
||||
csrr t3, mhartid
|
||||
slli t3, t3, 2
|
||||
add a0, a0, t3 /* a0 = &pxCurrentTCB[coreID] */
|
||||
lw a0, (a0) /* a0 = pxCurrentTCB[coreID] */
|
||||
lw a0, 0x0(a0) /* a0 = previous sp */
|
||||
#endif //#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
|
||||
rtos_exit_end:
|
||||
rtos_int_exit_end:
|
||||
ret
|
||||
|
@ -209,7 +209,7 @@ static inline void vPortClearInterruptMaskFromISR(UBaseType_t prev_level);
|
||||
* - See "Critical Sections & Disabling Interrupts" in docs/api-guides/freertos-smp.rst for more details
|
||||
* - Remark: For the ESP32, portENTER_CRITICAL and portENTER_CRITICAL_ISR both alias vPortEnterCritical, meaning that
|
||||
* either function can be called both from ISR as well as task context. This is not standard FreeRTOS
|
||||
* behaviorr; please keep this in mind if you need any compatibility with other FreeRTOS implementations.
|
||||
* behavior; please keep this in mind if you need any compatibility with other FreeRTOS implementations.
|
||||
* @note [refactor-todo] Check if these comments are still true
|
||||
* ------------------------------------------------------ */
|
||||
|
||||
|
@ -185,8 +185,6 @@
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
|
||||
{ \
|
||||
UBaseType_t uxTopPriority; \
|
||||
@ -196,17 +194,6 @@
|
||||
configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \
|
||||
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ 0 ], &( pxReadyTasksLists[ uxTopPriority ] ) ); \
|
||||
} /* taskSELECT_HIGHEST_PRIORITY_TASK() */
|
||||
#else
|
||||
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
|
||||
{ \
|
||||
UBaseType_t uxTopPriority; \
|
||||
\
|
||||
/* Find the highest priority list that contains ready tasks. */ \
|
||||
portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \
|
||||
configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \
|
||||
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ xPortGetCoreID() ], &( pxReadyTasksLists[ uxTopPriority ] ) ); \
|
||||
} /* taskSELECT_HIGHEST_PRIORITY_TASK() */
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
|
@ -30,7 +30,6 @@ menu "FreeRTOS"
|
||||
# Todo: Replace with CONFIG_NUM_CORES (IDF-4986)
|
||||
bool "Run FreeRTOS only on first core"
|
||||
default "y" if IDF_TARGET_ESP32S2 || IDF_TARGET_LINUX
|
||||
default "y" if IDF_TARGET_ESP32P4 #TODO: IDF-7566
|
||||
select ESP_SYSTEM_SINGLE_CORE_MODE
|
||||
help
|
||||
This version of FreeRTOS normally takes control of all cores of the CPU. Select this if you only want
|
||||
|
@ -111,19 +111,10 @@ void esp_startup_start_app_other_cores(void)
|
||||
}
|
||||
|
||||
// Wait for CPU0 to start FreeRTOS before progressing
|
||||
//TODO: IDF-7566
|
||||
#if !CONFIG_IDF_TARGET_ESP32P4
|
||||
extern volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS];
|
||||
while (port_xSchedulerRunning[0] == 0) {
|
||||
;
|
||||
}
|
||||
#else
|
||||
extern volatile unsigned uxSchedulerRunning[portNUM_PROCESSORS];
|
||||
while (uxSchedulerRunning[0] == 0) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if CONFIG_APPTRACE_ENABLE
|
||||
// [refactor-todo] move to esp_system initialization
|
||||
|
Loading…
x
Reference in New Issue
Block a user