Merge branch 'feature/freertos_xtensa_disable_port_using_mpu_wrappers' into 'master'

FreeRTOS: Fix FPU bugs on Amazon SMP FreeRTOS, port over FPU updates to IDF FreeRTOS

Closes IDF-5803

See merge request espressif/esp-idf!20280
This commit is contained in:
Darian
2022-12-23 19:17:49 +08:00
22 changed files with 649 additions and 402 deletions

View File

@@ -537,6 +537,11 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackFrame(UBaseType_t uxStackPointer,
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters) StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
{ {
#ifdef __clang_analyzer__
// Teach clang-tidy that pxTopOfStack cannot be a pointer to const
volatile StackType_t * pxTemp = pxTopOfStack;
pxTopOfStack = pxTemp;
#endif /*__clang_analyzer__ */
/* /*
HIGH ADDRESS HIGH ADDRESS
|---------------------------| <- pxTopOfStack on entry |---------------------------| <- pxTopOfStack on entry

View File

@@ -170,6 +170,15 @@ The implementation may use only a2-4, a15 (all other regs must be preserved).
// void* XT_RTOS_CP_STATE(void) // void* XT_RTOS_CP_STATE(void)
#define XT_RTOS_CP_STATE _frxt_task_coproc_state #define XT_RTOS_CP_STATE _frxt_task_coproc_state
/*
RTOS provided hook function that is called on every coprocessor exception. May
only be called from assembly code and by the 'call0' instruction.
The implementation may use only a2-4, a15 (all other regs must be preserved).
*/
// void XT_RTOS_CP_EXC_HOOK(void)
#if XCHAL_CP_NUM > 0
#define XT_RTOS_CP_EXC_HOOK _frxt_coproc_exc_hook
#endif
/******************************************************************************* /*******************************************************************************

View File

@@ -41,9 +41,25 @@
_Static_assert(portBYTE_ALIGNMENT == 16, "portBYTE_ALIGNMENT must be set to 16"); _Static_assert(portBYTE_ALIGNMENT == 16, "portBYTE_ALIGNMENT must be set to 16");
/* /* ---------------------------------------------------- Variables ------------------------------------------------------
OS state variables * - Various variables used to maintain the FreeRTOS port's state. Used from both port.c and various .S files
*/ * - Constant offsets are used by assembly to jump to particular TCB members or a stack area (such as the CPSA). We use
* C constants instead of preprocessor macros due to assembly lacking "offsetof()".
* ------------------------------------------------------------------------------------------------------------------ */
#if XCHAL_CP_NUM > 0
/* Offsets used to navigate to a task's CPSA on the stack */
const DRAM_ATTR uint32_t offset_pxEndOfStack = offsetof(StaticTask_t, pxDummy8);
const DRAM_ATTR uint32_t offset_cpsa = XT_CP_SIZE; /* Offset to start of the CPSA area on the stack. See uxInitialiseStackCPSA(). */
#if configNUM_CORES > 1
/* Offset to TCB_t.uxCoreAffinityMask member. Used to pin unpinned tasks that use the FPU. */
const DRAM_ATTR uint32_t offset_uxCoreAffinityMask = offsetof(StaticTask_t, uxDummy25);
#if configUSE_CORE_AFFINITY != 1
#error "configUSE_CORE_AFFINITY must be 1 on multicore targets with coprocessor support"
#endif
#endif /* configNUM_CORES > 1 */
#endif /* XCHAL_CP_NUM > 0 */
volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis
unsigned int port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit unsigned int port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit
//FreeRTOS SMP Locks //FreeRTOS SMP Locks
@@ -423,12 +439,6 @@ static void vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
} }
#endif #endif
const DRAM_ATTR uint32_t offset_pxEndOfStack = offsetof(StaticTask_t, pxDummy8);
#if ( configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )
const DRAM_ATTR uint32_t offset_uxCoreAffinityMask = offsetof(StaticTask_t, uxDummy25);
#endif // ( configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )
const DRAM_ATTR uint32_t offset_cpsa = XT_CP_SIZE;
/** /**
* @brief Align stack pointer in a downward growing stack * @brief Align stack pointer in a downward growing stack
* *
@@ -674,10 +684,15 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
void * pvParameters ) void * pvParameters )
#endif #endif
{ {
#ifdef __clang_analyzer__
// Teach clang-tidy that pxTopOfStack cannot be a pointer to const
volatile StackType_t * pxTemp = pxTopOfStack;
pxTopOfStack = pxTemp;
#endif /*__clang_analyzer__ */
/* /*
HIGH ADDRESS HIGH ADDRESS
|---------------------------| <- pxTopOfStack on entry |---------------------------| <- pxTopOfStack on entry
| Coproc Save Area | | Coproc Save Area | (CPSA MUST BE FIRST)
| ------------------------- | | ------------------------- |
| TLS Variables | | TLS Variables |
| ------------------------- | <- Start of useable stack | ------------------------- | <- Start of useable stack
@@ -697,7 +712,7 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0); configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0);
#if XCHAL_CP_NUM > 0 #if XCHAL_CP_NUM > 0
// Initialize the coprocessor save area // Initialize the coprocessor save area. THIS MUST BE THE FIRST AREA due to access from _frxt_task_coproc_state()
uxStackPointer = uxInitialiseStackCPSA(uxStackPointer); uxStackPointer = uxInitialiseStackCPSA(uxStackPointer);
configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0); configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0);
#endif /* XCHAL_CP_NUM > 0 */ #endif /* XCHAL_CP_NUM > 0 */
@@ -717,25 +732,25 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
// -------------------- Co-Processor ----------------------- // -------------------- Co-Processor -----------------------
#if ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) #if ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )
void _xt_coproc_release(volatile void *coproc_sa_base, BaseType_t xCoreID); void _xt_coproc_release(volatile void *coproc_sa_base, BaseType_t xTargetCoreID);
void vPortCleanUpCoprocArea( void *pxTCB ) void vPortCleanUpCoprocArea( void *pxTCB )
{ {
StackType_t *coproc_area; UBaseType_t uxCoprocArea;
BaseType_t xCoreID; BaseType_t xTargetCoreID;
/* Calculate the coproc save area in the stack from the TCB base */ /* Get pointer to the task's coprocessor save area from TCB->pxEndOfStack. See uxInitialiseStackCPSA() */
coproc_area = ( StackType_t * ) ( ( uint32_t ) ( pxTCB + offset_pxEndOfStack )); uxCoprocArea = ( UBaseType_t ) ( ( ( StaticTask_t * ) pxTCB )->pxDummy8 ); /* Get TCB_t.pxEndOfStack */
coproc_area = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) coproc_area ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); uxCoprocArea = STACKPTR_ALIGN_DOWN(16, uxCoprocArea - XT_CP_SIZE);
coproc_area = ( StackType_t * ) ( ( ( uint32_t ) coproc_area - XT_CP_SIZE ) & ~0xf );
/* Extract core ID from the affinity mask */ /* Extract core ID from the affinity mask */
xCoreID = __builtin_ffs( * ( UBaseType_t * ) ( pxTCB + offset_uxCoreAffinityMask ) ); xTargetCoreID = ( ( StaticTask_t * ) pxTCB )->uxDummy25 ;
assert( xCoreID >= 1 ); xTargetCoreID = ( BaseType_t ) __builtin_ffs( ( int ) xTargetCoreID );
xCoreID -= 1; assert( xTargetCoreID >= 1 ); // __builtin_ffs always returns first set index + 1
xTargetCoreID -= 1;
/* If task has live floating point registers somewhere, release them */ /* If task has live floating point registers somewhere, release them */
_xt_coproc_release( coproc_area, xCoreID ); _xt_coproc_release( (void *)uxCoprocArea, xTargetCoreID );
} }
#endif // ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) #endif // ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )

View File

@@ -34,32 +34,45 @@
#define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */ #define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */
.extern pxCurrentTCBs .extern pxCurrentTCBs
#if XCHAL_CP_NUM > 0
/* Offsets used to get a task's coprocessor save area (CPSA) from its TCB */
.extern offset_pxEndOfStack .extern offset_pxEndOfStack
.extern offset_cpsa .extern offset_cpsa
#if configNUM_CORES > 1
/* Offset to TCB_t.uxCoreAffinityMask member. Used to pin unpinned tasks that use the FPU. */
.extern offset_uxCoreAffinityMask
#endif /* configNUM_CORES > 1 */
#endif /* XCHAL_CP_NUM > 0 */
/* /*
Macro to get a task's coprocessor save area (CPSA) from its TCB --------------------------------------------------------------------------------
Macro get_cpsa_from_tcb - get the pointer to a task's CPSA form its TCB
Entry: Entry:
- reg_A contains a pointer to the TCB - "reg_A" contains a pointer to the task's TCB
Exit: Exit:
- reg_A contains a pointer to the CPSA - "reg_A" contains pointer the the task's CPSA
- reg_B destroyed - "reg_B" clobbered
The two arguments must be different AR registers.
--------------------------------------------------------------------------------
*/ */
#if XCHAL_CP_NUM > 0
.macro get_cpsa_from_tcb reg_A reg_B .macro get_cpsa_from_tcb reg_A reg_B
// Get TCB.pxEndOfStack from reg_A /* Get TCB.pxEndOfStack from reg_A */
movi \reg_B, offset_pxEndOfStack /* Move &offset_pxEndOfStack into reg_B */ movi \reg_B, offset_pxEndOfStack /* Move &offset_pxEndOfStack into reg_B */
l32i \reg_B, \reg_B, 0 /* Load offset_pxEndOfStack into reg_B */ l32i \reg_B, \reg_B, 0 /* Load offset_pxEndOfStack into reg_B */
add \reg_A, \reg_A, \reg_B /* Calculate &pxEndOfStack to reg_A (&TCB + offset_pxEndOfStack) */ add \reg_A, \reg_A, \reg_B /* Calculate &pxEndOfStack to reg_A (&TCB + offset_pxEndOfStack) */
l32i \reg_A, \reg_A, 0 /* Load TCB.pxEndOfStack into reg_A */ l32i \reg_A, \reg_A, 0 /* Load TCB.pxEndOfStack into reg_A */
//Offset to start of coproc save area /* Offset to start of CP save area */
movi \reg_B, offset_cpsa /* Move &offset_cpsa into reg_B */ movi \reg_B, offset_cpsa /* Move &offset_cpsa into reg_B */
l32i \reg_B, \reg_B, 0 /* Load offset_cpsa into reg_B */ l32i \reg_B, \reg_B, 0 /* Load offset_cpsa into reg_B */
sub \reg_A, \reg_A, \reg_B /* Subtract offset_cpsa from pxEndOfStack to get to start of CP save area (unaligned) */ sub \reg_A, \reg_A, \reg_B /* Subtract offset_cpsa from pxEndOfStack to get to start of CP save area (unaligned) */
//Align down start of CP save area to 16 byte boundary /* Align down start of CP save area to 16 byte boundary */
movi \reg_B, ~(0xF) movi \reg_B, ~(0xF)
and \reg_A, \reg_A, \reg_B /* Align CPSA pointer to 16 bytes */ and \reg_A, \reg_A, \reg_B /* Align CP save area pointer to 16 bytes */
.endm .endm
#endif /* XCHAL_CP_NUM > 0 */
.global port_IntStack .global port_IntStack
.global port_switch_flag //Required by sysview_tracing build .global port_switch_flag //Required by sysview_tracing build
@@ -692,3 +705,54 @@ _frxt_task_coproc_state:
2: ret 2: ret
#endif /* XCHAL_CP_NUM > 0 */ #endif /* XCHAL_CP_NUM > 0 */
/*
**********************************************************************************************************
* _frxt_coproc_exc_hook
* void _frxt_coproc_exc_hook(void)
*
* Implements the Xtensa RTOS porting layer's XT_RTOS_CP_EXC_HOOK function for FreeRTOS.
*
* May only be called from assembly code by the 'call0' instruction. Does NOT obey ABI conventions.
* May only only use a2-4, a15 (all other regs must be preserved).
* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
*
**********************************************************************************************************
*/
#if XCHAL_CP_NUM > 0
.globl _frxt_coproc_exc_hook
.type _frxt_coproc_exc_hook,@function
.align 4
_frxt_coproc_exc_hook:
#if configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1
getcoreid a2 /* a2 = xCurCoreID */
/* if (port_xSchedulerRunning[xCurCoreID] == 0) */
movi a3, port_xSchedulerRunning
addx4 a3, a2, a3
l32i a3, a3, 0
beqz a3, 1f /* Scheduler hasn't started yet. Return. */
/* if (port_interruptNesting[xCurCoreID] != 0) */
movi a3, port_interruptNesting
addx4 a3, a2, a3
l32i a3, a3, 0
bnez a3, 1f /* We are in an interrupt. Return*/
/* CP operations are incompatible with unpinned tasks. Thus we pin the task
to the current running core by updating its TCB.uxCoreAffinityMask field. */
movi a3, pxCurrentTCBs
addx4 a3, a2, a3
l32i a3, a3, 0 /* a3 = pxCurrentTCBs[xCurCoreID] */
movi a4, offset_uxCoreAffinityMask
l32i a4, a4, 0 /* a4 = offset_uxCoreAffinityMask */
add a3, a3, a4 /* a3 = &TCB.uxCoreAffinityMask */
ssl a2 /* Use xCurCoreID as left shift amount */
movi a4, 1
sll a4, a4 /* a4 = (1 << xCurCoreID) */
s32i a4, a3, 0 /* TCB.uxCoreAffinityMask = a4 = (1 << xCurCoreID) */
1:
#endif /* configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 */
ret
#endif /* XCHAL_CP_NUM > 0 */

View File

@@ -72,4 +72,66 @@
#endif #endif
.endm .endm
#endif /*
--------------------------------------------------------------------------------
Macro spinlock_take
This macro will repeatedley attempt to atomically set a spinlock variable
using the s32c1i instruciton. A spinlock is considered free if its value is 0.
Entry:
- "reg_A/B" as scratch registers
- "lock_var" spinlock variable's symbol
- Interrupts must already be disabled by caller
Exit:
- Spinlock set to current core's ID (PRID)
- "reg_A/B" clobbered
--------------------------------------------------------------------------------
*/
#if portNUM_PROCESSORS > 1
.macro spinlock_take reg_A reg_B lock_var
movi \reg_A, \lock_var /* reg_A = &lock_var */
.L_spinlock_loop:
movi \reg_B, 0 /* Load spinlock free value (0) into SCOMPARE1 */
wsr \reg_B, SCOMPARE1
rsync /* Ensure that SCOMPARE1 is set before s32c1i executes */
rsr \reg_B, PRID /* Load the current core's ID into reg_B */
s32c1i \reg_B, \reg_A, 0 /* Attempt *lock_var = reg_B */
bnez \reg_B, .L_spinlock_loop /* If the write was successful (i.e., lock was free), 0 will have been written back to reg_B */
.endm
#endif /* portNUM_PROCESSORS > 1 */
/*
--------------------------------------------------------------------------------
Macro spinlock_release
This macro will release a spinlock variable previously taken by the
spinlock_take macro.
Entry:
- "reg_A/B" as scratch registers
- "lock_var" spinlock variable's symbol
- Interrupts must already be disabled by caller
Exit:
- "reg_A/B" clobbered
--------------------------------------------------------------------------------
*/
#if portNUM_PROCESSORS > 1
.macro spinlock_release reg_A reg_B lock_var
movi \reg_A, \lock_var /* reg_A = &lock_var */
movi \reg_B, 0
s32i \reg_B, \reg_A, 0 /* Release the spinlock (*reg_A = 0) */
.endm
#endif /* portNUM_PROCESSORS > 1 */
#endif /* __XT_ASM_UTILS_H */

View File

@@ -397,18 +397,16 @@ May be called when a thread terminates or completes but does not delete
the co-proc save area, to avoid the exception handler having to save the the co-proc save area, to avoid the exception handler having to save the
thread's co-proc state before another thread can use it (optimization). thread's co-proc state before another thread can use it (optimization).
Needs to be called on the processor the thread was running on. Unpinned threads
won't have an entry here because they get pinned as soon they use a coprocessor.
Entry Conditions: Entry Conditions:
A2 = Pointer to base of co-processor state save area. A2 = Pointer to base of co-processor state save area.
A3 = Core ID of the pinned task A3 = Core ID of the task (must be pinned) who's coproc ownership we are
releasing.
Exit conditions: Exit conditions:
None. None.
Obeys ABI conventions per prototype: Obeys ABI conventions per prototype:
void _xt_coproc_release(void * coproc_sa_base, BaseType_t xCoreID) void _xt_coproc_release(void * coproc_sa_base, BaseType_t xTargetCoreID)
*******************************************************************************/ *******************************************************************************/
@@ -421,43 +419,33 @@ Obeys ABI conventions per prototype:
.align 4 .align 4
_xt_coproc_release: _xt_coproc_release:
ENTRY0 /* a2 = base of save area */ ENTRY0 /* a2 = base of save area */
/* a3 = core ID */ /* a3 = xTargetCoreID */
rsil a7, XCHAL_EXCM_LEVEL /* lock interrupts */ movi a4, XCHAL_CP_MAX << 2 /* a4 = size of an owner array */
mull a4, a3, a4 /* a4 = offset to the owner array of the target core */
movi a3, _xt_coproc_owner_sa /* a3 = base of all owner arrays */
add a3, a3, a4 /* a3 = base of owner array of the target core */
addi a4, a3, XCHAL_CP_MAX << 2 /* a4 = top+1 of owner array of the target core */
movi a5, 0 /* a5 = 0 (unowned) */
/* Aquire spinlock before proceeding with the routine. rsil a6, XCHAL_EXCM_LEVEL /* lock interrupts */
* Refer _xt_coproc_exc for details on the puspose of #if portNUM_PROCESSORS > 1
* the _xt_coproc_owner_sa_lock lock and its intended use. /* If multicore, we must also acquire the _xt_coproc_owner_sa_lock spinlock
*/ * to ensure thread safe access of _xt_coproc_owner_sa between cores. */
.L_spinlock_loop: spinlock_take a7 a8 _xt_coproc_owner_sa_lock
mov a8, a3 /* Save a copy of the core ID in a8 */ #endif /* portNUM_PROCESSORS > 1 */
movi a10, _xt_coproc_owner_sa_lock /* a10 = base address of lock variable */
addx4 a10, a8, a10 /* Use core ID in a8 to calculate the offset to the lock variable for the core */
movi a11, 0 /* a11 = 0 */
wsr a11, scompare1 /* scompare1 = a11 :- Expect the spinlock to be free (value = 0) */
movi a11, 1 /* a11 = 1 :- Write 1 to take the spinlock */
s32c1i a11, a10, 0 /* if (lock == scompare1) {tmp = lock; lock = a11; a11 = tmp} else {a11 = lock} */
bnez a11, .L_spinlock_loop /* if (a11 != 0) {loop} :- Keep spinning until the spinlock is available */
movi a4, XCHAL_CP_MAX << 2 1: l32i a7, a3, 0 /* a7 = owner at a3 */
mull a3, a3, a4 bne a2, a7, 2f /* if (coproc_sa_base == owner) */
movi a4, _xt_coproc_owner_sa /* a4 = base of owner array */ s32i a5, a3, 0 /* owner = unowned */
add a4, a4, a3 2: addi a3, a3, 1<<2 /* a3 = next entry in owner array */
bltu a3, a4, 1b /* repeat until end of array */
addi a5, a4, XCHAL_CP_MAX << 2 /* a5 = top+1 of owner array */ #if portNUM_PROCESSORS > 1
movi a6, 0 /* a6 = 0 (unowned) */ /* Release previously taken spinlock */
spinlock_release a7 a8 _xt_coproc_owner_sa_lock
1: l32i a8, a4, 0 /* a8 = owner at a4 */ #endif /* portNUM_PROCESSORS > 1 */
bne a2, a8, 2f /* if (coproc_sa_base == owner) */ wsr a6, PS /* restore interrupts */
s32i a3, a4, 0 /* owner = unowned */
2: addi a4, a4, 1<<2 /* a4 = next entry in owner array */
bltu a4, a5, 1b /* repeat until end of array */
3: wsr a7, PS /* restore interrupts */
/* Release spinlock */
movi a11, 0 /* a11 = 0 */
s32ri a11, a10, 0 /* a10 = base address of lock variable. Write 0 to release the lock */
RET0 RET0

View File

@@ -102,15 +102,8 @@
#include "esp_private/panic_reason.h" #include "esp_private/panic_reason.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "soc/soc.h" #include "soc/soc.h"
#include "xt_asm_utils.h"
/*
Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used.
Please change this when the tcb structure is changed
*/
.extern pxCurrentTCBs
#if ( configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )
.extern offset_uxCoreAffinityMask
#endif // ( configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )
/* /*
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -859,22 +852,22 @@ _xt_coproc_owner_sa:
/* Spinlock per core for accessing _xt_coproc_owner_sa array /* Spinlock per core for accessing _xt_coproc_owner_sa array
* *
* 0 = Spinlock available * 0 = Spinlock available
* 1 = Spinlock taken * PRID = Spinlock taken
* *
* The lock provides mutual exclusion for accessing the _xt_coproc_owner_sa array. * The lock provides mutual exclusion for accessing the _xt_coproc_owner_sa array.
* This array can be modified by both _xt_coproc_exc and _xt_coproc_release routines * The array can be modified by multiple cores simultaneously (via _xt_coproc_exc
* simultaneously owing to the fact that the FreeRTOS SMP Kernel allows cross-core * and _xt_coproc_release). Therefore, this spinlock is defined to ensure thread
* task deletion. Therefore, the same memory location in the owner save-area array * safe access of the _xt_coproc_owner_sa array.
* could be modified at the same time.
*/ */
#if portNUM_PROCESSORS > 1
.global _xt_coproc_owner_sa_lock .global _xt_coproc_owner_sa_lock
.type _xt_coproc_owner_sa_lock,@object .type _xt_coproc_owner_sa_lock,@object
.align 16 /* minimize crossing cache boundaries */ .align 16 /* minimize crossing cache boundaries */
_xt_coproc_owner_sa_lock: _xt_coproc_owner_sa_lock:
.space (portNUM_PROCESSORS) << 2 .space 4
#endif /* portNUM_PROCESSORS > 1 */
.section .iram1,"ax" .section .iram1,"ax"
.align 4 .align 4
.L_goto_invalid: .L_goto_invalid:
j .L_xt_coproc_invalid /* not in a thread (invalid) */ j .L_xt_coproc_invalid /* not in a thread (invalid) */
@@ -924,51 +917,15 @@ _xt_coproc_exc:
s32i a4, sp, XT_STK_A4 s32i a4, sp, XT_STK_A4
s32i a15, sp, XT_STK_A15 s32i a15, sp, XT_STK_A15
/* Aquire spinlock before proceeding with the exception handler. /* Call the RTOS coprocessor exception hook */
* (Refer _xt_coproc_release for competing routine for the lock.) call0 XT_RTOS_CP_EXC_HOOK
*
* [refactor-todo]: The spinlock aquire/release routine can be
* refactored in to a macro later if the need arises to use it
* at more than one place in the port assembler files.
*/
.L_spinlock_loop:
movi a2, _xt_coproc_owner_sa_lock /* a2 = base address of lock variable */
getcoreid a0 /* get the core ID in a0 to calculate the offset of the lock variable */
addx4 a2, a0, a2 /* a2 = address of desired lock variable */
movi a0, 0 /* a0 = 0 */
wsr a0, scompare1 /* scompare1 = a0 :- Expect the spinlock to be free (value = 0) */
movi a0, 1 /* a0 = 1 :- Write 1 to take the spinlock */
s32c1i a0, a2, 0 /* if (lock == scompare1) {tmp = lock; lock = a0; a0 = tmp} else {a0 = lock} */
bnez a0, .L_spinlock_loop /* if (a0 != 0) {loop} :- Keep spinning until the spinlock is available */
/* Get co-processor state save area of new owner thread. */ /* Get co-processor state save area of new owner thread. */
call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */ call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */
#if CONFIG_FREERTOS_FPU_IN_ISR #if !CONFIG_FREERTOS_FPU_IN_ISR
beqz a15, .L_skip_core_pin /* CP used in ISR, skip task pinning */
#else
beqz a15, .L_goto_invalid /* not in a thread (invalid) */ beqz a15, .L_goto_invalid /* not in a thread (invalid) */
#endif #endif
#if ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )
/* CP operations are incompatible with unpinned tasks. Thus we pin the task
to the current running core. */
movi a2, pxCurrentTCBs
getcoreid a3 /* a3 = current core ID */
addx4 a2, a3, a2
l32i a2, a2, 0 /* a2 = start of pxCurrentTCBs[cpuid] */
movi a4, offset_uxCoreAffinityMask
l32i a4, a4, 0 /* a4 = offset_uxCoreAffinityMask */
add a2, a2, a4 /* a2 = &TCB.uxCoreAffinityMask */
ssl a3 /* Use core ID as shift amount */
movi a4, 1
sll a4, a4 /* a4 = uxCoreAffinityMask = (1 << core ID) */
s32i a4, a2, 0 /* Store affinity mask to TCB.uxCoreAffinityMask */
#endif // ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 )
#if CONFIG_FREERTOS_FPU_IN_ISR
.L_skip_core_pin:
#endif
/* Enable the co-processor's bit in CPENABLE. */ /* Enable the co-processor's bit in CPENABLE. */
movi a0, _xt_coproc_mask movi a0, _xt_coproc_mask
rsr a4, CPENABLE /* a4 = CPENABLE */ rsr a4, CPENABLE /* a4 = CPENABLE */
@@ -978,17 +935,18 @@ _xt_coproc_exc:
or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */ or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */
wsr a4, CPENABLE wsr a4, CPENABLE
/* /* Grab the xt_coproc_owner_sa owner array for current core */
Keep loading _xt_coproc_owner_sa[n] atomic (=load once, then use that value
everywhere): _xt_coproc_release assumes it works like this in order not to need
locking.
*/
/* Grab correct xt_coproc_owner_sa for this core */
getcoreid a3 /* a3 = current core ID */ getcoreid a3 /* a3 = current core ID */
movi a2, XCHAL_CP_MAX << 2 movi a2, XCHAL_CP_MAX << 2 /* a2 = size of an owner array */
mull a2, a2, a3 /* multiply by current processor id */ mull a2, a2, a3 /* a2 = offset to the owner array of the current core*/
movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ movi a3, _xt_coproc_owner_sa /* a3 = base of all owner arrays */
add a3, a3, a2 /* a3 = owner area needed for this processor */ add a3, a3, a2 /* a3 = base of owner array of the current core */
#if portNUM_PROCESSORS > 1
/* If multicore, we must also acquire the _xt_coproc_owner_sa_lock spinlock
* to ensure thread safe access of _xt_coproc_owner_sa between cores. */
spinlock_take a0 a2 _xt_coproc_owner_sa_lock
#endif /* portNUM_PROCESSORS > 1 */
/* Get old coprocessor owner thread (save area ptr) and assign new one. */ /* Get old coprocessor owner thread (save area ptr) and assign new one. */
addx4 a3, a5, a3 /* a3 = &_xt_coproc_owner_sa[n] */ addx4 a3, a5, a3 /* a3 = &_xt_coproc_owner_sa[n] */
@@ -996,13 +954,21 @@ locking.
s32i a15, a3, 0 /* _xt_coproc_owner_sa[n] = new */ s32i a15, a3, 0 /* _xt_coproc_owner_sa[n] = new */
rsync /* ensure wsr.CPENABLE is complete */ rsync /* ensure wsr.CPENABLE is complete */
#if portNUM_PROCESSORS > 1
/* Release previously taken spinlock */
spinlock_release a0 a2 _xt_coproc_owner_sa_lock
#endif /* portNUM_PROCESSORS > 1 */
/* Only need to context switch if new owner != old owner. */ /* Only need to context switch if new owner != old owner. */
/* If float is necessary on ISR, we need to remove this check */ /* If float is necessary on ISR, we need to remove this check */
/* below, because on restoring from ISR we may have new == old condition used /* below, because on restoring from ISR we may have new == old condition used
* to force cp restore to next thread * to force cp restore to next thread
* Todo: IDF-6418
*/ */
#ifndef CONFIG_FREERTOS_FPU_IN_ISR #if !CONFIG_FREERTOS_FPU_IN_ISR
beq a15, a2, .L_goto_done /* new owner == old, we're done */ bne a15, a2, .L_switch_context
j .L_goto_done /* new owner == old, we're done */
.L_switch_context:
#endif #endif
/* If no old owner then nothing to save. */ /* If no old owner then nothing to save. */
@@ -1072,14 +1038,6 @@ locking.
/* Restore interruptee's saved registers. */ /* Restore interruptee's saved registers. */
/* Can omit rsync for wsr.CPENABLE here because _xt_user_exit does it. */ /* Can omit rsync for wsr.CPENABLE here because _xt_user_exit does it. */
.L_xt_coproc_done: .L_xt_coproc_done:
/* Release spinlock */
movi a2, _xt_coproc_owner_sa_lock /* a2 = base address of the lock variable */
getcoreid a0 /* a0 = core ID to calculate the offset of the lock variable */
addx4 a2, a0, a2 /* a2 = address of the lock variable */
movi a0, 0 /* a0 = 0 */
s32ri a0, a2, 0 /* a2 = a0 :- Write 0 to release the lock */
l32i a15, sp, XT_STK_A15 l32i a15, sp, XT_STK_A15
l32i a5, sp, XT_STK_A5 l32i a5, sp, XT_STK_A5
l32i a4, sp, XT_STK_A4 l32i a4, sp, XT_STK_A4
@@ -1107,14 +1065,6 @@ locking.
/* Co-processor exception occurred outside a thread (not supported). */ /* Co-processor exception occurred outside a thread (not supported). */
.L_xt_coproc_invalid: .L_xt_coproc_invalid:
/* Release spinlock */
movi a2, _xt_coproc_owner_sa_lock /* a2 = base address of the lock variable */
getcoreid a0 /* a0 = core ID to calculate the offset of the lock variable */
addx4 a2, a0, a2 /* a2 = address of the lock variable */
movi a0, 0 /* a0 = 0 */
s32ri a0, a2, 0 /* a2 = a0 :- Write 0 to release the lock */
movi a0,PANIC_RSN_COPROCEXCEPTION movi a0,PANIC_RSN_COPROCEXCEPTION
wsr a0,EXCCAUSE wsr a0,EXCCAUSE
call0 _xt_panic /* not in a thread (invalid) */ call0 _xt_panic /* not in a thread (invalid) */
@@ -1735,7 +1685,7 @@ _Level6Vector:
.global xt_nmi .global xt_nmi
.align 4 .align 4
_NMIExceptionVector: _NMIExceptionVector:
wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */ wsr a0, EXCSAVE + XCHAL_NMILEVEL /* preserve a0 */
call0 xt_nmi /* load interrupt handler */ call0 xt_nmi /* load interrupt handler */
/* never returns here - call0 is used as a jump (see note at top) */ /* never returns here - call0 is used as a jump (see note at top) */
@@ -1856,9 +1806,9 @@ _xt_alloca_exc:
wsr a2, PS /* update PS.OWB to new window base */ wsr a2, PS /* update PS.OWB to new window base */
rsync rsync
_bbci.l a4, 31, _WindowUnderflow4 bbci.l a4, 31, _WindowUnderflow4
rotw -1 /* original a0 goes to a8 */ rotw -1 /* original a0 goes to a8 */
_bbci.l a8, 30, _WindowUnderflow8 bbci.l a8, 30, _WindowUnderflow8
rotw -1 rotw -1
j _WindowUnderflow12 j _WindowUnderflow12

View File

@@ -274,6 +274,11 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackFrame(UBaseType_t uxStackPointer,
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters) StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
{ {
#ifdef __clang_analyzer__
// Teach clang-tidy that pxTopOfStack cannot be a pointer to const
volatile StackType_t * pxTemp = pxTopOfStack;
pxTopOfStack = pxTemp;
#endif /*__clang_analyzer__ */
/* /*
HIGH ADDRESS HIGH ADDRESS
|---------------------------| <- pxTopOfStack on entry |---------------------------| <- pxTopOfStack on entry

View File

@@ -642,48 +642,13 @@ FORCE_INLINE_ATTR BaseType_t xPortGetCoreID(void)
/* ------------------------------------------------------ Misc --------------------------------------------------------- /* ------------------------------------------------------ Misc ---------------------------------------------------------
* - Miscellaneous porting macros * - Miscellaneous porting macros
* - These are not port of the FreeRTOS porting interface, but are used by other FreeRTOS dependent components * - These are not port of the FreeRTOS porting interface, but are used by other FreeRTOS dependent components
* - [refactor-todo] Remove dependency on MPU wrappers by modifying TCB
* ------------------------------------------------------------------------------------------------------------------ */ * ------------------------------------------------------------------------------------------------------------------ */
// -------------------- Co-Processor ----------------------- // -------------------- Co-Processor -----------------------
// When coprocessors are defined, we maintain a pointer to coprocessors area.
// We currently use a hack: redefine field xMPU_SETTINGS in TCB block as a structure that can hold:
// MPU wrappers, coprocessor area pointer, trace code structure, and more if needed.
// The field is normally used for memory protection. FreeRTOS should create another general purpose field.
typedef struct {
#if XCHAL_CP_NUM > 0 #if XCHAL_CP_NUM > 0
volatile StackType_t *coproc_area; // Pointer to coprocessor save area; MUST BE FIRST void vPortCleanUpCoprocArea(void *pvTCB);
#endif #define portCLEAN_UP_COPROC(pvTCB) vPortCleanUpCoprocArea(pvTCB)
#if portUSING_MPU_WRAPPERS
// Define here mpu_settings, which is port dependent
int mpu_setting; // Just a dummy example here; MPU not ported to Xtensa yet
#endif
} xMPU_SETTINGS;
// Main hack to use MPU_wrappers even when no MPU is defined (warning: mpu_setting should not be accessed; otherwise move this above xMPU_SETTINGS)
#if (XCHAL_CP_NUM > 0) && !portUSING_MPU_WRAPPERS // If MPU wrappers not used, we still need to allocate coproc area
#undef portUSING_MPU_WRAPPERS
#define portUSING_MPU_WRAPPERS 1 // Enable it to allocate coproc area
#define MPU_WRAPPERS_H // Override mpu_wrapper.h to disable unwanted code
#define PRIVILEGED_FUNCTION
#define PRIVILEGED_DATA
#endif
void _xt_coproc_release(volatile void *coproc_sa_base);
/*
* The structures and methods of manipulating the MPU are contained within the
* port layer.
*
* Fills the xMPUSettings structure with the memory region information
* contained in xRegions.
*/
#if( portUSING_MPU_WRAPPERS == 1 )
struct xMEMORY_REGION;
void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION *const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth ) PRIVILEGED_FUNCTION;
void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings );
#endif #endif
// -------------------- Heap Related ----------------------- // -------------------- Heap Related -----------------------

View File

@@ -170,6 +170,15 @@ The implementation may use only a2-4, a15 (all other regs must be preserved).
// void* XT_RTOS_CP_STATE(void) // void* XT_RTOS_CP_STATE(void)
#define XT_RTOS_CP_STATE _frxt_task_coproc_state #define XT_RTOS_CP_STATE _frxt_task_coproc_state
/*
RTOS provided hook function that is called on every coprocessor exception. May
only be called from assembly code and by the 'call0' instruction.
The implementation may use only a2-4, a15 (all other regs must be preserved).
*/
// void XT_RTOS_CP_EXC_HOOK(void)
#if XCHAL_CP_NUM > 0
#define XT_RTOS_CP_EXC_HOOK _frxt_coproc_exc_hook
#endif
/******************************************************************************* /*******************************************************************************

View File

@@ -81,9 +81,21 @@ _Static_assert(tskNO_AFFINITY == CONFIG_FREERTOS_NO_AFFINITY, "incorrect tskNO_A
/* ---------------------------------------------------- Variables ------------------------------------------------------ /* ---------------------------------------------------- Variables ------------------------------------------------------
* * - Various variables used to maintain the FreeRTOS port's state. Used from both port.c and various .S files
* - Constant offsets are used by assembly to jump to particular TCB members or a stack area (such as the CPSA). We use
* C constants instead of preprocessor macros due to assembly lacking "offsetof()".
* ------------------------------------------------------------------------------------------------------------------ */ * ------------------------------------------------------------------------------------------------------------------ */
#if XCHAL_CP_NUM > 0
/* Offsets used to navigate to a task's CPSA on the stack */
const DRAM_ATTR uint32_t offset_pxEndOfStack = offsetof(StaticTask_t, pxDummy8);
const DRAM_ATTR uint32_t offset_cpsa = XT_CP_SIZE; /* Offset to start of the CPSA area on the stack. See uxInitialiseStackCPSA(). */
#if configNUM_CORES > 1
/* Offset to TCB_t.xCoreID member. Used to pin unpinned tasks that use the FPU. */
const DRAM_ATTR uint32_t offset_xCoreID = offsetof(StaticTask_t, xDummyCoreID);
#endif /* configNUM_CORES > 1 */
#endif /* XCHAL_CP_NUM > 0 */
volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis
unsigned port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit unsigned port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit
BaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0}; BaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
@@ -389,16 +401,26 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackFrame(UBaseType_t uxStackPointer,
return uxStackPointer; return uxStackPointer;
} }
#if portUSING_MPU_WRAPPERS #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged) StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
StackType_t * pxEndOfStack,
TaskFunction_t pxCode,
void * pvParameters )
#else #else
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
#endif #endif
{ {
#ifdef __clang_analyzer__
// Teach clang-tidy that pxTopOfStack cannot be a pointer to const
volatile StackType_t * pxTemp = pxTopOfStack;
pxTopOfStack = pxTemp;
#endif /*__clang_analyzer__ */
/* /*
HIGH ADDRESS HIGH ADDRESS
|---------------------------| <- pxTopOfStack on entry |---------------------------| <- pxTopOfStack on entry
| Coproc Save Area | | Coproc Save Area | (CPSA MUST BE FIRST)
| ------------------------- | | ------------------------- |
| TLS Variables | | TLS Variables |
| ------------------------- | <- Start of useable stack | ------------------------- | <- Start of useable stack
@@ -417,7 +439,7 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
// Make sure the incoming stack pointer is aligned on 16 // Make sure the incoming stack pointer is aligned on 16
configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0); configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0);
#if XCHAL_CP_NUM > 0 #if XCHAL_CP_NUM > 0
// Initialize the coprocessor save area // Initialize the coprocessor save area. THIS MUST BE THE FIRST AREA due to access from _frxt_task_coproc_state()
uxStackPointer = uxInitialiseStackCPSA(uxStackPointer); uxStackPointer = uxInitialiseStackCPSA(uxStackPointer);
// Each allocated section on the stack must have a size aligned on 16 // Each allocated section on the stack must have a size aligned on 16
configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0); configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0);
@@ -583,33 +605,24 @@ void vPortSetStackWatchpoint( void *pxStackStart )
esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, 32, ESP_CPU_WATCHPOINT_STORE); esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, 32, ESP_CPU_WATCHPOINT_STORE);
} }
/* ---------------------------------------------- Misc Implementations -------------------------------------------------
*
* ------------------------------------------------------------------------------------------------------------------ */
// -------------------- Co-Processor ----------------------- // -------------------- Co-Processor -----------------------
/*
* Used to set coprocessor area in stack. Current hack is to reuse MPU pointer for coprocessor area.
*/
#if portUSING_MPU_WRAPPERS
void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION *const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth )
{
#if XCHAL_CP_NUM > 0 #if XCHAL_CP_NUM > 0
xMPUSettings->coproc_area = ( StackType_t * ) ( ( uint32_t ) ( pxBottomOfStack + usStackDepth - 1 )); void _xt_coproc_release(volatile void *coproc_sa_base, BaseType_t xTargetCoreID);
xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) xMPUSettings->coproc_area ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( uint32_t ) xMPUSettings->coproc_area - XT_CP_SIZE ) & ~0xf );
void vPortCleanUpCoprocArea(void *pvTCB)
/* NOTE: we cannot initialize the coprocessor save area here because FreeRTOS is going to
* clear the stack area after we return. This is done in pxPortInitialiseStack().
*/
#endif
}
void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings )
{ {
UBaseType_t uxCoprocArea;
BaseType_t xTargetCoreID;
/* Get a pointer to the task's coprocessor save area */
uxCoprocArea = ( UBaseType_t ) ( ( ( StaticTask_t * ) pvTCB )->pxDummy8 ); /* Get TCB_t.pxEndOfStack */
uxCoprocArea = STACKPTR_ALIGN_DOWN(16, uxCoprocArea - XT_CP_SIZE);
/* Get xTargetCoreID from the TCB.xCoreID */
xTargetCoreID = ( ( StaticTask_t * ) pvTCB )->xDummyCoreID;
/* If task has live floating point registers somewhere, release them */ /* If task has live floating point registers somewhere, release them */
_xt_coproc_release( xMPUSettings->coproc_area ); _xt_coproc_release( (void *)uxCoprocArea, xTargetCoreID );
} }
#endif /* portUSING_MPU_WRAPPERS */ #endif /* XCHAL_CP_NUM > 0 */

View File

@@ -32,9 +32,47 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */ #define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */
#define CP_TOPOFSTACK_OFFS 0x04 /* xMPU_SETTINGS.coproc_area */
.extern pxCurrentTCB .extern pxCurrentTCB
#if XCHAL_CP_NUM > 0
/* Offsets used to get a task's coprocessor save area (CPSA) from its TCB */
.extern offset_pxEndOfStack
.extern offset_cpsa
#if configNUM_CORES > 1
/* Offset to TCB_t.xCoreID member. Used to pin unpinned tasks that use the FPU. */
.extern offset_xCoreID
#endif /* configNUM_CORES > 1 */
#endif /* XCHAL_CP_NUM > 0 */
/*
--------------------------------------------------------------------------------
Macro get_cpsa_from_tcb - get the pointer to a task's CPSA form its TCB
Entry:
- "reg_A" contains a pointer to the task's TCB
Exit:
- "reg_A" contains pointer the the task's CPSA
- "reg_B" clobbered
The two arguments must be different AR registers.
--------------------------------------------------------------------------------
*/
#if XCHAL_CP_NUM > 0
.macro get_cpsa_from_tcb reg_A reg_B
/* Get TCB.pxEndOfStack from reg_A */
movi \reg_B, offset_pxEndOfStack /* Move &offset_pxEndOfStack into reg_B */
l32i \reg_B, \reg_B, 0 /* Load offset_pxEndOfStack into reg_B */
add \reg_A, \reg_A, \reg_B /* Calculate &pxEndOfStack to reg_A (&TCB + offset_pxEndOfStack) */
l32i \reg_A, \reg_A, 0 /* Load TCB.pxEndOfStack into reg_A */
/* Offset to start of CP save area */
movi \reg_B, offset_cpsa /* Move &offset_cpsa into reg_B */
l32i \reg_B, \reg_B, 0 /* Load offset_cpsa into reg_B */
sub \reg_A, \reg_A, \reg_B /* Subtract offset_cpsa from pxEndOfStack to get to start of CP save area (unaligned) */
/* Align down start of CP save area to 16 byte boundary */
movi \reg_B, ~(0xF)
and \reg_A, \reg_A, \reg_B /* Align CP save area pointer to 16 bytes */
.endm
#endif /* XCHAL_CP_NUM > 0 */
.global port_IntStack .global port_IntStack
.global port_switch_flag //Required by sysview_tracing build .global port_switch_flag //Required by sysview_tracing build
@@ -120,23 +158,19 @@ _frxt_int_enter:
mull a2, a4, a2 mull a2, a4, a2
add a1, a1, a2 /* for current proc */ add a1, a1, a2 /* for current proc */
#ifdef CONFIG_FREERTOS_FPU_IN_ISR #if CONFIG_FREERTOS_FPU_IN_ISR && XCHAL_CP_NUM > 0
#if XCHAL_CP_NUM > 0
rsr a3, CPENABLE /* Restore thread scope CPENABLE */ rsr a3, CPENABLE /* Restore thread scope CPENABLE */
addi sp, sp,-4 /* ISR will manage FPU coprocessor by forcing */ addi sp, sp,-4 /* ISR will manage FPU coprocessor by forcing */
s32i a3, a1, 0 /* its trigger */ s32i a3, a1, 0 /* its trigger */
#endif #endif
#endif
.Lnested: .Lnested:
1: 1:
#ifdef CONFIG_FREERTOS_FPU_IN_ISR #if CONFIG_FREERTOS_FPU_IN_ISR && XCHAL_CP_NUM > 0
#if XCHAL_CP_NUM > 0
movi a3, 0 /* whilst ISRs pending keep CPENABLE exception active */ movi a3, 0 /* whilst ISRs pending keep CPENABLE exception active */
wsr a3, CPENABLE wsr a3, CPENABLE
rsync rsync
#endif #endif
#endif
mov a0, a12 /* restore return addr and return */ mov a0, a12 /* restore return addr and return */
ret ret
@@ -174,14 +208,12 @@ _frxt_int_exit:
s32i a2, a3, 0 /* save nesting count */ s32i a2, a3, 0 /* save nesting count */
bnez a2, .Lnesting /* !=0 after decr so still nested */ bnez a2, .Lnesting /* !=0 after decr so still nested */
#ifdef CONFIG_FREERTOS_FPU_IN_ISR #if CONFIG_FREERTOS_FPU_IN_ISR && XCHAL_CP_NUM > 0
#if XCHAL_CP_NUM > 0
l32i a3, sp, 0 /* Grab last CPENABLE before leave ISR */ l32i a3, sp, 0 /* Grab last CPENABLE before leave ISR */
addi sp, sp, 4 addi sp, sp, 4
wsr a3, CPENABLE wsr a3, CPENABLE
rsync /* ensure CPENABLE was modified */ rsync /* ensure CPENABLE was modified */
#endif #endif
#endif
movi a2, pxCurrentTCB movi a2, pxCurrentTCB
addx4 a2, a4, a2 addx4 a2, a4, a2
@@ -460,11 +492,11 @@ _frxt_dispatch:
#if XCHAL_CP_NUM > 0 #if XCHAL_CP_NUM > 0
/* Restore CPENABLE from task's co-processor save area. */ /* Restore CPENABLE from task's co-processor save area. */
movi a3, pxCurrentTCB /* cp_state = */ movi a2, pxCurrentTCB /* cp_state = */
getcoreid a2 getcoreid a3
addx4 a3, a2, a3 addx4 a2, a3, a2
l32i a3, a3, 0 l32i a2, a2, 0
l32i a2, a3, CP_TOPOFSTACK_OFFS /* StackType_t *pxStack; */ get_cpsa_from_tcb a2, a3 /* After this, pointer to CP save area is in a2, a3 is destroyed */
l16ui a3, a2, XT_CPENABLE /* CPENABLE = cp_state->cpenable; */ l16ui a3, a2, XT_CPENABLE /* CPENABLE = cp_state->cpenable; */
wsr a3, CPENABLE wsr a3, CPENABLE
#endif #endif
@@ -563,7 +595,7 @@ vPortYield:
#if XCHAL_CP_NUM > 0 #if XCHAL_CP_NUM > 0
/* Clear CPENABLE, also in task's co-processor state save area. */ /* Clear CPENABLE, also in task's co-processor state save area. */
l32i a2, a2, CP_TOPOFSTACK_OFFS /* a2 = pxCurrentTCB->cp_state */ get_cpsa_from_tcb a2, a3 /* After this, pointer to CP save area is in a2, a3 is destroyed */
movi a3, 0 movi a3, 0
wsr a3, CPENABLE wsr a3, CPENABLE
beqz a2, 1f beqz a2, 1f
@@ -604,12 +636,12 @@ vPortYieldFromInt:
#if XCHAL_CP_NUM > 0 #if XCHAL_CP_NUM > 0
/* Save CPENABLE in task's co-processor save area, and clear CPENABLE. */ /* Save CPENABLE in task's co-processor save area, and clear CPENABLE. */
movi a3, pxCurrentTCB /* cp_state = */ movi a2, pxCurrentTCB /* cp_state = */
getcoreid a2 getcoreid a3
addx4 a3, a2, a3 addx4 a2, a3, a2
l32i a3, a3, 0 l32i a2, a2, 0
l32i a2, a3, CP_TOPOFSTACK_OFFS get_cpsa_from_tcb a2, a3 /* After this, pointer to CP save area is in a2, a3 is destroyed */
rsr a3, CPENABLE rsr a3, CPENABLE
s16i a3, a2, XT_CPENABLE /* cp_state->cpenable = CPENABLE; */ s16i a3, a2, XT_CPENABLE /* cp_state->cpenable = CPENABLE; */
@@ -663,10 +695,58 @@ _frxt_task_coproc_state:
l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */ l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */
beqz a15, 2f beqz a15, 2f
l32i a15, a15, CP_TOPOFSTACK_OFFS get_cpsa_from_tcb a15, a3 /* After this, pointer to CP save area is in a15, a3 is destroyed */
ret ret
1: movi a15, 0 1: movi a15, 0
2: ret 2: ret
#endif /* XCHAL_CP_NUM > 0 */ #endif /* XCHAL_CP_NUM > 0 */
/*
**********************************************************************************************************
* _frxt_coproc_exc_hook
* void _frxt_coproc_exc_hook(void)
*
* Implements the Xtensa RTOS porting layer's XT_RTOS_CP_EXC_HOOK function for FreeRTOS.
*
* May only be called from assembly code by the 'call0' instruction. Does NOT obey ABI conventions.
* May only only use a2-4, a15 (all other regs must be preserved).
* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
*
**********************************************************************************************************
*/
#if XCHAL_CP_NUM > 0
.globl _frxt_coproc_exc_hook
.type _frxt_coproc_exc_hook,@function
.align 4
_frxt_coproc_exc_hook:
#if configNUM_CORES > 1
getcoreid a2 /* a2 = xCurCoreID */
/* if (port_xSchedulerRunning[xCurCoreID] == 0) */
movi a3, port_xSchedulerRunning
addx4 a3, a2, a3
l32i a3, a3, 0
beqz a3, 1f /* Scheduler hasn't started yet. Return. */
/* if (port_interruptNesting[xCurCoreID] != 0) */
movi a3, port_interruptNesting
addx4 a3, a2, a3
l32i a3, a3, 0
bnez a3, 1f /* We are in an interrupt. Return*/
/* CP operations are incompatible with unpinned tasks. Thus we pin the task
to the current running core. */
movi a3, pxCurrentTCB
addx4 a3, a2, a3
l32i a3, a3, 0 /* a3 = pxCurrentTCB[xCurCoreID] */
movi a4, offset_xCoreID
l32i a4, a4, 0 /* a4 = offset_xCoreID */
add a3, a3, a4 /* a3 = &TCB.xCoreID */
s32i a2, a3, 0 /* TCB.xCoreID = xCurCoreID */
1:
#endif /* configNUM_CORES > 1 */
ret
#endif /* XCHAL_CP_NUM > 0 */

View File

@@ -72,4 +72,66 @@
#endif #endif
.endm .endm
#endif /*
--------------------------------------------------------------------------------
Macro spinlock_take
This macro will repeatedley attempt to atomically set a spinlock variable
using the s32c1i instruciton. A spinlock is considered free if its value is 0.
Entry:
- "reg_A/B" as scratch registers
- "lock_var" spinlock variable's symbol
- Interrupts must already be disabled by caller
Exit:
- Spinlock set to current core's ID (PRID)
- "reg_A/B" clobbered
--------------------------------------------------------------------------------
*/
#if portNUM_PROCESSORS > 1
.macro spinlock_take reg_A reg_B lock_var
movi \reg_A, \lock_var /* reg_A = &lock_var */
.L_spinlock_loop:
movi \reg_B, 0 /* Load spinlock free value (0) into SCOMPARE1 */
wsr \reg_B, SCOMPARE1
rsync /* Ensure that SCOMPARE1 is set before s32c1i executes */
rsr \reg_B, PRID /* Load the current core's ID into reg_B */
s32c1i \reg_B, \reg_A, 0 /* Attempt *lock_var = reg_B */
bnez \reg_B, .L_spinlock_loop /* If the write was successful (i.e., lock was free), 0 will have been written back to reg_B */
.endm
#endif /* portNUM_PROCESSORS > 1 */
/*
--------------------------------------------------------------------------------
Macro spinlock_release
This macro will release a spinlock variable previously taken by the
spinlock_take macro.
Entry:
- "reg_A/B" as scratch registers
- "lock_var" spinlock variable's symbol
- Interrupts must already be disabled by caller
Exit:
- "reg_A/B" clobbered
--------------------------------------------------------------------------------
*/
#if portNUM_PROCESSORS > 1
.macro spinlock_release reg_A reg_B lock_var
movi \reg_A, \lock_var /* reg_A = &lock_var */
movi \reg_B, 0
s32i \reg_B, \reg_A, 0 /* Release the spinlock (*reg_A = 0) */
.endm
#endif /* portNUM_PROCESSORS > 1 */
#endif /* __XT_ASM_UTILS_H */

View File

@@ -397,17 +397,16 @@ May be called when a thread terminates or completes but does not delete
the co-proc save area, to avoid the exception handler having to save the the co-proc save area, to avoid the exception handler having to save the
thread's co-proc state before another thread can use it (optimization). thread's co-proc state before another thread can use it (optimization).
Needs to be called on the processor the thread was running on. Unpinned threads
won't have an entry here because they get pinned as soon they use a coprocessor.
Entry Conditions: Entry Conditions:
A2 = Pointer to base of co-processor state save area. A2 = Pointer to base of co-processor state save area.
A3 = Core ID of the task (must be pinned) who's coproc ownership we are
releasing.
Exit conditions: Exit conditions:
None. None.
Obeys ABI conventions per prototype: Obeys ABI conventions per prototype:
void _xt_coproc_release(void * coproc_sa_base) void _xt_coproc_release(void * coproc_sa_base, BaseType_t xTargetCoreID)
*******************************************************************************/ *******************************************************************************/
@@ -420,17 +419,21 @@ Obeys ABI conventions per prototype:
.align 4 .align 4
_xt_coproc_release: _xt_coproc_release:
ENTRY0 /* a2 = base of save area */ ENTRY0 /* a2 = base of save area */
/* a3 = xTargetCoreID */
getcoreid a5 movi a4, XCHAL_CP_MAX << 2 /* a4 = size of an owner array */
movi a3, XCHAL_CP_MAX << 2 mull a4, a3, a4 /* a4 = offset to the owner array of the target core */
mull a5, a5, a3 movi a3, _xt_coproc_owner_sa /* a3 = base of all owner arrays */
movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ add a3, a3, a4 /* a3 = base of owner array of the target core */
add a3, a3, a5 addi a4, a3, XCHAL_CP_MAX << 2 /* a4 = top+1 of owner array of the target core */
addi a4, a3, XCHAL_CP_MAX << 2 /* a4 = top+1 of owner array */
movi a5, 0 /* a5 = 0 (unowned) */ movi a5, 0 /* a5 = 0 (unowned) */
rsil a6, XCHAL_EXCM_LEVEL /* lock interrupts */ rsil a6, XCHAL_EXCM_LEVEL /* lock interrupts */
#if portNUM_PROCESSORS > 1
/* If multicore, we must also acquire the _xt_coproc_owner_sa_lock spinlock
* to ensure thread safe access of _xt_coproc_owner_sa between cores. */
spinlock_take a7 a8 _xt_coproc_owner_sa_lock
#endif /* portNUM_PROCESSORS > 1 */
1: l32i a7, a3, 0 /* a7 = owner at a3 */ 1: l32i a7, a3, 0 /* a7 = owner at a3 */
bne a2, a7, 2f /* if (coproc_sa_base == owner) */ bne a2, a7, 2f /* if (coproc_sa_base == owner) */
@@ -438,7 +441,11 @@ _xt_coproc_release:
2: addi a3, a3, 1<<2 /* a3 = next entry in owner array */ 2: addi a3, a3, 1<<2 /* a3 = next entry in owner array */
bltu a3, a4, 1b /* repeat until end of array */ bltu a3, a4, 1b /* repeat until end of array */
3: wsr a6, PS /* restore interrupts */ #if portNUM_PROCESSORS > 1
/* Release previously taken spinlock */
spinlock_release a7 a8 _xt_coproc_owner_sa_lock
#endif /* portNUM_PROCESSORS > 1 */
wsr a6, PS /* restore interrupts */
RET0 RET0

View File

@@ -102,13 +102,8 @@
#include "esp_private/panic_reason.h" #include "esp_private/panic_reason.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "soc/soc.h" #include "soc/soc.h"
#include "xt_asm_utils.h"
/*
Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used.
Please change this when the tcb structure is changed
*/
#define TASKTCB_XCOREID_OFFSET (0x38+configMAX_TASK_NAME_LEN+3)&~3
.extern pxCurrentTCB
/* /*
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -855,9 +850,25 @@ _xt_coproc_mask:
_xt_coproc_owner_sa: _xt_coproc_owner_sa:
.space (XCHAL_CP_MAX * portNUM_PROCESSORS) << 2 .space (XCHAL_CP_MAX * portNUM_PROCESSORS) << 2
/* Spinlock per core for accessing _xt_coproc_owner_sa array
*
* 0 = Spinlock available
* PRID = Spinlock taken
*
* The lock provides mutual exclusion for accessing the _xt_coproc_owner_sa array.
* The array can be modified by multiple cores simultaneously (via _xt_coproc_exc
* and _xt_coproc_release). Therefore, this spinlock is defined to ensure thread
* safe access of the _xt_coproc_owner_sa array.
*/
#if portNUM_PROCESSORS > 1
.global _xt_coproc_owner_sa_lock
.type _xt_coproc_owner_sa_lock,@object
.align 16 /* minimize crossing cache boundaries */
_xt_coproc_owner_sa_lock:
.space 4
#endif /* portNUM_PROCESSORS > 1 */
.section .iram1,"ax" .section .iram1,"ax"
.align 4 .align 4
.L_goto_invalid: .L_goto_invalid:
j .L_xt_coproc_invalid /* not in a thread (invalid) */ j .L_xt_coproc_invalid /* not in a thread (invalid) */
@@ -907,29 +918,15 @@ _xt_coproc_exc:
s32i a4, sp, XT_STK_A4 s32i a4, sp, XT_STK_A4
s32i a15, sp, XT_STK_A15 s32i a15, sp, XT_STK_A15
/* Call the RTOS coprocessor exception hook */
call0 XT_RTOS_CP_EXC_HOOK
/* Get co-processor state save area of new owner thread. */ /* Get co-processor state save area of new owner thread. */
call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */ call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */
#if CONFIG_FREERTOS_FPU_IN_ISR #if !CONFIG_FREERTOS_FPU_IN_ISR
beqz a15, .L_skip_core_pin /* CP used in ISR, skip task pinning */
#else
beqz a15, .L_goto_invalid /* not in a thread (invalid) */ beqz a15, .L_goto_invalid /* not in a thread (invalid) */
#endif #endif
#if configNUM_CORES > 1
/* CP operations are incompatible with unpinned tasks. Thus we pin the task
to the current running core. */
movi a2, pxCurrentTCB
getcoreid a3 /* a3 = current core ID */
addx4 a2, a3, a2
l32i a2, a2, 0 /* a2 = start of pxCurrentTCB[cpuid] */
addi a2, a2, TASKTCB_XCOREID_OFFSET /* a2 = &TCB.xCoreID */
s32i a3, a2, 0 /* TCB.xCoreID = current core ID */
#endif // configNUM_CORES > 1
#if CONFIG_FREERTOS_FPU_IN_ISR
.L_skip_core_pin:
#endif
/* Enable the co-processor's bit in CPENABLE. */ /* Enable the co-processor's bit in CPENABLE. */
movi a0, _xt_coproc_mask movi a0, _xt_coproc_mask
rsr a4, CPENABLE /* a4 = CPENABLE */ rsr a4, CPENABLE /* a4 = CPENABLE */
@@ -939,16 +936,18 @@ _xt_coproc_exc:
or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */ or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */
wsr a4, CPENABLE wsr a4, CPENABLE
/* /* Grab the xt_coproc_owner_sa owner array for current core */
Keep loading _xt_coproc_owner_sa[n] atomic (=load once, then use that value getcoreid a3 /* a3 = current core ID */
everywhere): _xt_coproc_release assumes it works like this in order not to need movi a2, XCHAL_CP_MAX << 2 /* a2 = size of an owner array */
locking. mull a2, a2, a3 /* a2 = offset to the owner array of the current core*/
*/ movi a3, _xt_coproc_owner_sa /* a3 = base of all owner arrays */
/* Grab correct xt_coproc_owner_sa for this core */ add a3, a3, a2 /* a3 = base of owner array of the current core */
movi a2, XCHAL_CP_MAX << 2
mull a2, a2, a3 /* multiply by current processor id */ #if portNUM_PROCESSORS > 1
movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ /* If multicore, we must also acquire the _xt_coproc_owner_sa_lock spinlock
add a3, a3, a2 /* a3 = owner area needed for this processor */ * to ensure thread safe access of _xt_coproc_owner_sa between cores. */
spinlock_take a0 a2 _xt_coproc_owner_sa_lock
#endif /* portNUM_PROCESSORS > 1 */
/* Get old coprocessor owner thread (save area ptr) and assign new one. */ /* Get old coprocessor owner thread (save area ptr) and assign new one. */
addx4 a3, a5, a3 /* a3 = &_xt_coproc_owner_sa[n] */ addx4 a3, a5, a3 /* a3 = &_xt_coproc_owner_sa[n] */
@@ -956,12 +955,18 @@ locking.
s32i a15, a3, 0 /* _xt_coproc_owner_sa[n] = new */ s32i a15, a3, 0 /* _xt_coproc_owner_sa[n] = new */
rsync /* ensure wsr.CPENABLE is complete */ rsync /* ensure wsr.CPENABLE is complete */
#if portNUM_PROCESSORS > 1
/* Release previously taken spinlock */
spinlock_release a0 a2 _xt_coproc_owner_sa_lock
#endif /* portNUM_PROCESSORS > 1 */
/* Only need to context switch if new owner != old owner. */ /* Only need to context switch if new owner != old owner. */
/* If float is necessary on ISR, we need to remove this check */ /* If float is necessary on ISR, we need to remove this check */
/* below, because on restoring from ISR we may have new == old condition used /* below, because on restoring from ISR we may have new == old condition used
* to force cp restore to next thread * to force cp restore to next thread
* Todo: IDF-6418
*/ */
#ifndef CONFIG_FREERTOS_FPU_IN_ISR #if !CONFIG_FREERTOS_FPU_IN_ISR
bne a15, a2, .L_switch_context bne a15, a2, .L_switch_context
j .L_goto_done /* new owner == old, we're done */ j .L_goto_done /* new owner == old, we're done */
.L_switch_context: .L_switch_context:

View File

@@ -1415,18 +1415,8 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
* not return. */ * not return. */
uxTaskNumber++; uxTaskNumber++;
/* /* We cannot free the task immediately if it is currently running (on either core) */
* We cannot immediately a task that is
* - Currently running on either core
* - If the task is not currently running but is pinned to the other (due to FPU cleanup)
* Todo: Allow deletion of tasks pinned to other core (IDF-5803)
*/
#if ( configNUM_CORES > 1 )
xFreeNow = ( taskIS_CURRENTLY_RUNNING( pxTCB ) || ( pxTCB->xCoreID == !xCurCoreID ) ) ? pdFALSE : pdTRUE;
#else
xFreeNow = ( taskIS_CURRENTLY_RUNNING( pxTCB ) ) ? pdFALSE : pdTRUE; xFreeNow = ( taskIS_CURRENTLY_RUNNING( pxTCB ) ) ? pdFALSE : pdTRUE;
#endif /* configNUM_CORES > 1 */
if( xFreeNow == pdFALSE ) if( xFreeNow == pdFALSE )
{ {
/* A task is deleting itself. This cannot complete within the /* A task is deleting itself. This cannot complete within the
@@ -1455,7 +1445,7 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
#if ( configNUM_CORES > 1 ) #if ( configNUM_CORES > 1 )
if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, !xCurCoreID ) ) if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, !xCurCoreID ) )
{ {
/* SMP case of deleting a task running on a different core. Same issue /* SMP case of deleting a task currently running on a different core. Same issue
* as a task deleting itself, but we need to send a yield to this task now * as a task deleting itself, but we need to send a yield to this task now
* before we release xKernelLock. * before we release xKernelLock.
* *
@@ -4505,60 +4495,47 @@ static void prvInitialiseTaskLists( void )
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
static void prvCheckTasksWaitingTermination( void ) static void prvCheckTasksWaitingTermination( void )
{ {
/** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/ /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/
#if ( INCLUDE_vTaskDelete == 1 ) #if ( INCLUDE_vTaskDelete == 1 )
{ {
BaseType_t xListIsEmpty; TCB_t * pxTCB;
BaseType_t core = xPortGetCoreID();
/* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL( &xKernelLock ) /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL()
* being called too often in the idle task. */ * being called too often in the idle task. */
while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U )
{ {
TCB_t * pxTCB = NULL; #if ( configNUM_CORES > 1 )
pxTCB = NULL;
taskENTER_CRITICAL( &xKernelLock ); taskENTER_CRITICAL( &xKernelLock );
{ {
xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); /* List may have already been cleared by the other core. Check again */
if ( listLIST_IS_EMPTY( &xTasksWaitingTermination ) == pdFALSE )
if( xListIsEmpty == pdFALSE )
{ {
/* We only want to kill tasks that ran on this core because e.g. _xt_coproc_release needs to /* We can't delete a task if it is still running on
* be called on the core the process is pinned on, if any */ * the other core. Keep walking the list until we
ListItem_t * target = listGET_HEAD_ENTRY( &xTasksWaitingTermination ); * find a task we can free, or until we walk the
* entire list. */
for( ; target != listGET_END_MARKER( &xTasksWaitingTermination ); target = listGET_NEXT( target ) ) /*Walk the list */ ListItem_t *xEntry;
for ( xEntry = listGET_HEAD_ENTRY( &xTasksWaitingTermination ); xEntry != listGET_END_MARKER( &xTasksWaitingTermination ); xEntry = listGET_NEXT( xEntry ) )
{ {
TCB_t * tgt_tcb = ( TCB_t * ) listGET_LIST_ITEM_OWNER( target ); if ( !taskIS_CURRENTLY_RUNNING( ( ( TCB_t * ) listGET_LIST_ITEM_OWNER( xEntry ) ) ) )
int affinity = tgt_tcb->xCoreID;
/*Self deleting tasks are added to Termination List before they switch context. Ensure they aren't still currently running */
if( ( pxCurrentTCB[ core ] == tgt_tcb ) || ( ( configNUM_CORES > 1 ) && ( pxCurrentTCB[ !core ] == tgt_tcb ) ) )
{ {
continue; /*Can't free memory of task that is still running */ pxTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( xEntry );
} ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
--uxCurrentNumberOfTasks;
if( ( affinity == core ) || ( affinity == tskNO_AFFINITY ) ) /*Find first item not pinned to other core */ --uxDeletedTasksWaitingCleanUp;
{
pxTCB = tgt_tcb;
break; break;
} }
} }
}
}
taskEXIT_CRITICAL( &xKernelLock );
if ( pxTCB != NULL ) if ( pxTCB != NULL )
{
( void ) uxListRemove( target ); /*Remove list item from list */
--uxCurrentNumberOfTasks;
--uxDeletedTasksWaitingCleanUp;
}
}
}
taskEXIT_CRITICAL( &xKernelLock ); /*Need to call deletion callbacks outside critical section */
if( pxTCB != NULL ) /*Call deletion callbacks and free TCB memory */
{ {
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS == 1 ) #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS == 1 )
prvDeleteTLS( pxTCB ); prvDeleteTLS( pxTCB );
@@ -4567,9 +4544,24 @@ static void prvCheckTasksWaitingTermination( void )
} }
else else
{ {
mtCOVERAGE_TEST_MARKER(); /* No task found to delete, break out of loop */
break; /*No TCB found that could be freed by this core, break out of loop */ break;
} }
#else
taskENTER_CRITICAL( &xKernelLock );
{
pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
--uxCurrentNumberOfTasks;
--uxDeletedTasksWaitingCleanUp;
}
taskEXIT_CRITICAL( &xKernelLock );
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS == 1 )
prvDeleteTLS( pxTCB );
#endif
prvDeleteTCB( pxTCB );
#endif /* configNUM_CORES > 1 */
} }
} }
#endif /* INCLUDE_vTaskDelete */ #endif /* INCLUDE_vTaskDelete */
@@ -4848,6 +4840,10 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
vPortReleaseTaskMPUSettings( &( pxTCB->xMPUSettings ) ); vPortReleaseTaskMPUSettings( &( pxTCB->xMPUSettings ) );
#endif #endif
#ifdef portCLEAN_UP_COPROC
portCLEAN_UP_COPROC( ( void * ) pxTCB );
#endif
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) )
{ {
/* The task can only have been allocated dynamically - free both /* The task can only have been allocated dynamically - free both

View File

@@ -171,7 +171,7 @@ This file get's pulled into assembly sources. Therefore, some includes need to b
#elif CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY #elif CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY
#define configCHECK_FOR_STACK_OVERFLOW 2 #define configCHECK_FOR_STACK_OVERFLOW 2
#endif #endif
#define configRECORD_STACK_HIGH_ADDRESS 1 #define configRECORD_STACK_HIGH_ADDRESS 1 // This must be set as the port requires TCB.pxEndOfStack
// ------------------- Run-time Stats ---------------------- // ------------------- Run-time Stats ----------------------

View File

@@ -13,10 +13,7 @@ entries:
port: pxPortInitialiseStack (default) port: pxPortInitialiseStack (default)
port: xPortStartScheduler (default) port: xPortStartScheduler (default)
if IDF_TARGET_ESP32 = y || IDF_TARGET_ESP32S3 = y : if IDF_TARGET_ESP32 = y || IDF_TARGET_ESP32S3 = y :
port: vPortReleaseTaskMPUSettings (default) port: vPortCleanUpCoprocArea (default)
tasks: xTaskCreateRestricted (default)
port: vPortStoreTaskMPUSettings (default)
tasks: vTaskAllocateMPURegions (default)
tasks: prvTaskCheckFreeStackSpace (default) tasks: prvTaskCheckFreeStackSpace (default)
tasks: prvInitialiseNewTask (default) tasks: prvInitialiseNewTask (default)
tasks: prvInitialiseTaskLists (default) tasks: prvInitialiseTaskLists (default)

View File

@@ -23,16 +23,21 @@ Test FPU usage from a task context
Purpose: Purpose:
- Test that the FPU can be used from a task context - Test that the FPU can be used from a task context
- Test that FPU context is properly saved and restored - Test that FPU context is properly saved and restored
- Test that FPU context is cleaned up on task deletion by running multiple iterations
Procedure: Procedure:
- Create TEST_PINNED_NUM_TASKS tasks pinned to each core - Create TEST_PINNED_NUM_TASKS tasks pinned to each core
- Start each task - Start each task
- Each task updates a float variable and then blocks (to allow other tasks to run thus forcing the an FPU context - Each task updates a float variable and then blocks (to allow other tasks to run thus forcing the an FPU context
save and restore). save and restore).
- Delete each task
- Repeat test for TEST_PINNED_NUM_ITERS iterations
Expected: Expected:
- Correct float value calculated by each task - Correct float value calculated by each task
- Each task cleans up its FPU context on deletion
*/ */
#define TEST_PINNED_NUM_TASKS 3 #define TEST_PINNED_NUM_TASKS 3
#define TEST_PINNED_NUM_ITERS 5
static void pinned_task(void *arg) static void pinned_task(void *arg)
{ {
@@ -62,6 +67,7 @@ TEST_CASE("FPU: Usage in task", "[freertos]")
SemaphoreHandle_t done_sem = xSemaphoreCreateCounting(configNUM_CORES * TEST_PINNED_NUM_TASKS, 0); SemaphoreHandle_t done_sem = xSemaphoreCreateCounting(configNUM_CORES * TEST_PINNED_NUM_TASKS, 0);
TEST_ASSERT_NOT_EQUAL(NULL, done_sem); TEST_ASSERT_NOT_EQUAL(NULL, done_sem);
for (int iter = 0; iter < TEST_PINNED_NUM_ITERS; iter++) {
TaskHandle_t task_handles[configNUM_CORES][TEST_PINNED_NUM_TASKS]; TaskHandle_t task_handles[configNUM_CORES][TEST_PINNED_NUM_TASKS];
// Create test tasks for each core // Create test tasks for each core
@@ -91,6 +97,8 @@ TEST_CASE("FPU: Usage in task", "[freertos]")
} }
vTaskDelay(10); // Short delay to allow idle task to be free task memory and FPU contexts vTaskDelay(10); // Short delay to allow idle task to be free task memory and FPU contexts
}
vSemaphoreDelete(done_sem); vSemaphoreDelete(done_sem);
} }
@@ -101,18 +109,24 @@ Test FPU usage will pin an unpinned task
Purpose: Purpose:
- Test that unpinned tasks are automatically pinned to the current core on the task's first use of the FPU - Test that unpinned tasks are automatically pinned to the current core on the task's first use of the FPU
- Test that FPU context is cleaned up on task deletion by running multiple iterations
Procedure: Procedure:
- Create an unpinned task - Create an unpinned task
- Task disables scheduling/preemption to ensure that it does not switch cores - Task disables scheduling/preemption to ensure that it does not switch cores
- Task uses the FPU - Task uses the FPU
- Task checks its core affinity after FPU usage - Task checks its core affinity after FPU usage
- Task deletes itself
- Repeat test for TEST_UNPINNED_NUM_ITERS iterations
Expected: Expected:
- Task remains unpinned until its first usage of the FPU - Task remains unpinned until its first usage of the FPU
- The task becomes pinned to the current core after first use of the FPU - The task becomes pinned to the current core after first use of the FPU
- Each task cleans up its FPU context on deletion
*/ */
#if configNUM_CORES > 1 #if configNUM_CORES > 1
#define TEST_UNPINNED_NUM_ITERS 5
static void unpinned_task(void *arg) static void unpinned_task(void *arg)
{ {
// Disable scheduling/preemption to make sure current core ID doesn't change // Disable scheduling/preemption to make sure current core ID doesn't change
@@ -162,12 +176,14 @@ static void unpinned_task(void *arg)
TEST_CASE("FPU: Usage in unpinned task", "[freertos]") TEST_CASE("FPU: Usage in unpinned task", "[freertos]")
{ {
TaskHandle_t unity_task_handle = xTaskGetCurrentTaskHandle(); TaskHandle_t unity_task_handle = xTaskGetCurrentTaskHandle();
for (int iter = 0; iter < TEST_UNPINNED_NUM_ITERS; iter++) {
// Create unpinned task // Create unpinned task
xTaskCreate(unpinned_task, "unpin", 4096, (void *)unity_task_handle, UNITY_FREERTOS_PRIORITY + 1, NULL); xTaskCreate(unpinned_task, "unpin", 4096, (void *)unity_task_handle, UNITY_FREERTOS_PRIORITY + 1, NULL);
// Wait for task to complete // Wait for task to complete
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
vTaskDelay(10); // Short delay to allow task memory to be freed vTaskDelay(10); // Short delay to allow task memory to be freed
} }
}
#endif // configNUM_CORES > 1 #endif // configNUM_CORES > 1
#endif // SOC_CPU_HAS_FPU #endif // SOC_CPU_HAS_FPU

View File

@@ -1,3 +1,5 @@
semphr.h:line: warning: argument 'pxStaticSemaphore' of command @param is not found in the argument list of xSemaphoreCreateCounting(uxMaxCount, uxInitialCount) semphr.h:line: warning: argument 'pxStaticSemaphore' of command @param is not found in the argument list of xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)
task.h:line: warning: argument 'pxTaskDefinition' of command @param is not found in the argument list of vTaskAllocateMPURegions(TaskHandle_t xTask, const MemoryRegion_t *const pxRegions) task.h:line: warning: argument 'pxTaskDefinition' of command @param is not found in the argument list of vTaskAllocateMPURegions(TaskHandle_t xTask, const MemoryRegion_t *const pxRegions)
task.h:line: warning: argument 'pxCreatedTask' of command @param is not found in the argument list of vTaskAllocateMPURegions(TaskHandle_t xTask, const MemoryRegion_t *const pxRegions) task.h:line: warning: argument 'pxCreatedTask' of command @param is not found in the argument list of vTaskAllocateMPURegions(TaskHandle_t xTask, const MemoryRegion_t *const pxRegions)
task.h:line: warning: argument 'pxTaskDefinition' of command @param is not found in the argument list of vTaskAllocateMPURegions(TaskHandle_t xTask, const MemoryRegion_t *const pxRegions)
task.h:line: warning: argument 'pxCreatedTask' of command @param is not found in the argument list of vTaskAllocateMPURegions(TaskHandle_t xTask, const MemoryRegion_t *const pxRegions)

View File

@@ -303,7 +303,6 @@ PREDEFINED = \
configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \ configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
configUSE_APPLICATION_TASK_TAG=1 \ configUSE_APPLICATION_TASK_TAG=1 \
configTASKLIST_INCLUDE_COREID=1 \ configTASKLIST_INCLUDE_COREID=1 \
portUSING_MPU_WRAPPERS=1 \
PRIVILEGED_FUNCTION= \ PRIVILEGED_FUNCTION= \
"ESP_EVENT_DECLARE_BASE(x)=extern esp_event_base_t x" "ESP_EVENT_DECLARE_BASE(x)=extern esp_event_base_t x"

View File

@@ -7,9 +7,7 @@ PREFIX_RISCV ?= riscv32-esp-elf-
PROG_XTENSA := dummy_xtensa.elf PROG_XTENSA := dummy_xtensa.elf
PROG_RISCV := dummy_riscv.elf PROG_RISCV := dummy_riscv.elf
# This actually depends on the value of portUSING_MPU_WRAPPERS. CPPFLAGS_XTENSA := -DTASK_NAME_OFFSET=52
# I.e. ESP32-S2 would also have TASK_NAME_OFFSET=52 since portUSING_MPU_WRAPPERS is 0.
CPPFLAGS_XTENSA := -DTASK_NAME_OFFSET=56
CPPFLAGS_RISCV := -DTASK_NAME_OFFSET=52 CPPFLAGS_RISCV := -DTASK_NAME_OFFSET=52
all: $(PROG_XTENSA) $(PROG_RISCV) all: $(PROG_XTENSA) $(PROG_RISCV)