FreeRTOS之ARM CR5栈结构操作示意图
- 1 FreeRTOS源码下载地址
- 2 ARM CR5栈结构操作宏和接口
- 2.1 portSAVE_CONTEXT宏
- 2.1.1 portSAVE_CONTEXT源码
- 2.1.2 portSAVE_CONTEXT宏操作栈结构变化示意图
- 2.2 portRESTORE_CONTEXT宏
- 2.2.1 portRESTORE_CONTEXT源码
- 2.2.2 portRESTORE_CONTEXT宏操作栈结构变化示意图
- 2.3 pxPortInitialiseStack
- 2.3.1 pxPortInitialiseStack源码
- 3.2.2 pxPortInitialiseStack栈结构变化示意图
- 2.4 pxPortInitialiseStack调用关系
- 2.5 portRESTORE_CONTEXT调用关系
下面以FreeRTOS源码中arm cortex-r5处理器的栈处理为例来介绍栈结构操作前后变化。
1 FreeRTOS源码下载地址
https://www.freertos.org/
2 ARM CR5栈结构操作宏和接口
.macro portSAVE_CONTEXT
.macro portRESTORE_CONTEXT
pxPortInitialiseStack
2.1 portSAVE_CONTEXT宏
2.1.1 portSAVE_CONTEXT源码
.macro portSAVE_CONTEXT
/* Save the LR and SPSR onto the system mode stack before switching to
system mode to save the remaining system mode registers. */
SRSDB sp!, #SYS_MODE
CPS #SYS_MODE
PUSH {R0-R12, R14}
/* Push the critical nesting count. */
LDR R2, ulCriticalNestingConst
LDR R1, [R2]
PUSH {R1}
#if defined( __ARM_FP )
/* Does the task have a floating point context that needs saving? If
ulPortTaskHasFPUContext is 0 then no. */
LDR R2, ulPortTaskHasFPUContextConst
LDR R3, [R2]
CMP R3, #0
/* Save the floating point context, if any. */
FMRXNE R1, FPSCR
PUSHNE {R1}
VPUSHNE {D0-D15}
/* Save ulPortTaskHasFPUContext itself. */
PUSH {R3}
#endif /* __ARM_FP */
/* Save the stack pointer in the TCB. */
LDR R0, pxCurrentTCBConst
LDR R1, [R0]
STR SP, [R1]
.endm
2.1.2 portSAVE_CONTEXT宏操作栈结构变化示意图
2.2 portRESTORE_CONTEXT宏
2.2.1 portRESTORE_CONTEXT源码
.macro portRESTORE_CONTEXT
/* Set the SP to point to the stack of the task being restored. */
LDR R0, pxCurrentTCBConst
LDR R1, [R0]
LDR SP, [R1]
#if defined( __ARM_FP )
/*
* Is there a floating point context to restore? If the restored
* ulPortTaskHasFPUContext is zero then no.
*/
LDR R0, ulPortTaskHasFPUContextConst
POP {R1}
STR R1, [R0]
CMP R1, #0
/* Restore the floating point context, if any. */
VPOPNE {D0-D15}
POPNE {R0}
VMSRNE FPSCR, R0
#endif /* __ARM_FP */
/* Restore the critical section nesting depth. */
LDR R0, ulCriticalNestingConst
POP {R1}
STR R1, [R0]
/* Ensure the priority mask is correct for the critical nesting depth. */
LDR R2, ulICCPMRConst
LDR R2, [R2]
CMP R1, #0
MOVEQ R4, #255
LDRNE R4, ulMaxAPIPriorityMaskConst
LDRNE R4, [R4]
STR R4, [R2]
/* Restore all system mode registers other than the SP (which is already
being used). */
POP {R0-R12, R14}
/* Return to the task code, loading CPSR on the way. */
RFEIA sp!
.endm
2.2.2 portRESTORE_CONTEXT宏操作栈结构变化示意图
2.3 pxPortInitialiseStack
2.3.1 pxPortInitialiseStack源码
/*
* See header file for description.
*/
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
/*
* Setup the initial stack of the task. The stack is set exactly as
* expected by the portRESTORE_CONTEXT() macro.
*
* The fist real value on the stack is the status register, which is set for
* system mode, with interrupts enabled. A few NULLs are added first to ensure
* GDB does not try decoding a non-existent return address.
*/
*pxTopOfStack = ( StackType_t ) NULL;
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) NULL;
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) NULL;
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
if( ( ( uint32_t ) pxCode & portTHUMB_MODE_ADDRESS ) != 0x00UL )
{
/* The task will start in THUMB mode. */
*pxTopOfStack |= portTHUMB_MODE_BIT;
}
pxTopOfStack--;
/* Next the return address, which in this case is the start of the task. */
*pxTopOfStack = ( StackType_t ) pxCode;
pxTopOfStack--;
/* Next all the registers other than the stack pointer. */
*pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* R14 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x12121212; /* R12 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x11111111; /* R11 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x10101010; /* R10 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x09090909; /* R9 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x08080808; /* R8 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x07070707; /* R7 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x06060606; /* R6 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x05050505; /* R5 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x04040404; /* R4 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x03030303; /* R3 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x02020202; /* R2 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x01010101; /* R1 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
/*
* The task will start with a critical nesting count of 0 as interrupts are
* enabled.
*/
pxTopOfStack--;
*pxTopOfStack = portNO_CRITICAL_NESTING;
#if ( configUSE_TASK_FPU_SUPPORT == 1 )
{
/*
* The task will start without a floating point context.
* A task that uses the floating point hardware must call
* vPortTaskUsesFPU() before executing any floating point
* instructions.
*/
pxTopOfStack--;
*pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
}
#elif ( configUSE_TASK_FPU_SUPPORT == 2 )
{
/*
* The task will start with a floating point context. Leave enough
* space for the registers and ensure they are initialized to 0.
*/
pxTopOfStack -= portFPU_REGISTER_WORDS;
memset( pxTopOfStack, 0x00, portFPU_REGISTER_WORDS * sizeof( StackType_t ) );
pxTopOfStack--;
*pxTopOfStack = pdTRUE;
ulPortTaskHasFPUContext = pdTRUE;
}
#elif ( configUSE_TASK_FPU_SUPPORT != 0 )
{
#error Invalid configUSE_TASK_FPU_SUPPORT setting - configUSE_TASK_FPU_SUPPORT must be set to 0, 1, or 2.
}
#endif /* configUSE_TASK_FPU_SUPPORT */
return pxTopOfStack;
}
3.2.2 pxPortInitialiseStack栈结构变化示意图
2.4 pxPortInitialiseStack调用关系
|- xTaskCreate( pxIdleTaskFunction, ...)
|- prvCreateTask
|- pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
|- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
|- prvInitialiseNewTask
|- vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
|- vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
|- pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
2.5 portRESTORE_CONTEXT调用关系
|- vTaskStartScheduler
|- prvCreateIdleTasks()
|- xTaskCreate( pxIdleTaskFunction, ...)
|- prvCreateTask
|- pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
|- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
|- prvInitialiseNewTask
|- vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
|- vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
|- pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
|- *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
|- prvAddNewTaskToReadyList
|- prvInitialiseTaskLists
|- prvAddTaskToReadyList
|- listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );
|- xTimerCreateTimerTask()
|- xTaskCreateAffinitySet
|- prvCreateTask
|- pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );
|- pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
|- prvInitialiseNewTask
|- vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
|- vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
|- pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
|- *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
|- prvAddNewTaskToReadyList
|- prvInitialiseTaskLists
|- prvAddTaskToReadyList
|- listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) )
|- pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask;
|- prvAddNewTaskToReadyList( pxNewTCB );
|- xPortStartScheduler()
|- vPortRestoreTaskContext
|- portRESTORE_CONTEXT