FreeRTOS之vTaskStartScheduler实现分析
FreeRTOS之vTaskStartScheduler实现分析
- 1 FreeRTOS源码下载地址
- 2 函数接口
- 2.1 函数接口
- 2.2 函数参数简介
- 3 vTaskDelete的调用关系
- 3.1 调用关系
- 3.2 调用关系示意图
- 4 函数源码分析
- 4.1 vTaskStartScheduler
- 4.2 prvCreateIdleTasks
- 4.2.1 prvCreateIdleTasks
- 4.2.2 xTaskCreate
- 4.3 xTimerCreateTimerTask
- 4.4 xTaskCreateAffinitySet
- 4.5 xPortStartScheduler
- 4.6 vPortRestoreTaskContext
- 4.7 portRESTORE_CONTEXT
- 4.8 pxPortInitialiseStack
1 FreeRTOS源码下载地址
https://www.freertos.org/
2 函数接口
2.1 函数接口
void vTaskStartScheduler( void )
2.2 函数参数简介
参数 - 无
3 vTaskDelete的调用关系
3.1 调用关系
|- 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
3.2 调用关系示意图
4 函数源码分析
4.1 vTaskStartScheduler
- xReturn = prvCreateIdleTasks(); 创建idle线程
- xReturn = xTimerCreateTimerTask(); 创建timer线程
- xSchedulerRunning = pdTRUE; 将当前系统的运行状态设置为pdTRUE
- ( void ) xPortStartScheduler(); 开始调度,这个和当前处理器的架构相关的,和也栈结构的设置相关。
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
traceENTER_vTaskStartScheduler();
#if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 )
{
/* Sanity check that the UBaseType_t must have greater than or equal to
* the number of bits as confNUMBER_OF_CORES. */
configASSERT( ( sizeof( UBaseType_t ) * taskBITS_PER_BYTE ) >= configNUMBER_OF_CORES );
}
#endif /* #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) */
xReturn = prvCreateIdleTasks();
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */
if( xReturn == pdPASS )
{
/* freertos_tasks_c_additions_init() should only be called if the user
* definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
* the only macro called by the function. */
#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
{
freertos_tasks_c_additions_init();
}
#endif
/* Interrupts are turned off here, to ensure a tick does not occur
* before or during the call to xPortStartScheduler(). The stacks of
* the created tasks contain a status word with interrupts switched on
* so interrupts will automatically get re-enabled when the first task
* starts to run. */
portDISABLE_INTERRUPTS();
#if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 )
{
/* Switch C-Runtime's TLS Block to point to the TLS
* block specific to the task that will run first. */
configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock );
}
#endif
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
/* If configGENERATE_RUN_TIME_STATS is defined then the following
* macro must be defined to configure the timer/counter used to generate
* the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS
* is set to 0 and the following line fails to build then ensure you do not
* have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
* FreeRTOSConfig.h file. */
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
traceTASK_SWITCHED_IN();
traceSTARTING_SCHEDULER( xIdleTaskHandles );
/* Setting up the timer tick is hardware specific and thus in the
* portable interface. */
/* The return value for xPortStartScheduler is not required
* hence using a void datatype. */
( void ) xPortStartScheduler();
/* In most cases, xPortStartScheduler() will not return. If it
* returns pdTRUE then there was not enough heap memory available
* to create either the Idle or the Timer task. If it returned
* pdFALSE, then the application called xTaskEndScheduler().
* Most ports don't implement xTaskEndScheduler() as there is
* nothing to return to. */
}
else
{
/* This line will only be reached if the kernel could not be started,
* because there was not enough FreeRTOS heap to create the idle task
* or the timer task. */
configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
}
/* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
* meaning xIdleTaskHandles are not used anywhere else. */
( void ) xIdleTaskHandles;
/* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority
* from getting optimized out as it is no longer used by the kernel. */
( void ) uxTopUsedPriority;
traceRETURN_vTaskStartScheduler();
}
4.2 prvCreateIdleTasks
4.2.1 prvCreateIdleTasks
for( xCoreID = ( BaseType_t ) 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ )
遍历所有的core,为所有的core 都创建对应的idle task,并将其加入到Ready列表。pxIdleTaskFunction = prvIdleTask;
对于主核core,设置当前idle任务的处理函数为prvIdleTaskpxIdleTaskFunction = prvPassiveIdleTask;
对于从核,其idle task的处理函数为prvPassiveIdleTask- 创建idle task
xReturn = xTaskCreate( pxIdleTaskFunction,
cIdleName,
configMINIMAL_STACK_SIZE,
( void * ) NULL,
portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
&xIdleTaskHandles[ xCoreID ] );
static BaseType_t prvCreateIdleTasks( void )
{
BaseType_t xReturn = pdPASS;
BaseType_t xCoreID;
char cIdleName[ configMAX_TASK_NAME_LEN ];
TaskFunction_t pxIdleTaskFunction = NULL;
BaseType_t xIdleTaskNameIndex;
for( xIdleTaskNameIndex = ( BaseType_t ) 0; xIdleTaskNameIndex < ( BaseType_t ) configMAX_TASK_NAME_LEN; xIdleTaskNameIndex++ )
{
cIdleName[ xIdleTaskNameIndex ] = configIDLE_TASK_NAME[ xIdleTaskNameIndex ];
/* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
* configMAX_TASK_NAME_LEN characters just in case the memory after the
* string is not accessible (extremely unlikely). */
if( cIdleName[ xIdleTaskNameIndex ] == ( char ) 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* Add each idle task at the lowest priority. */
for( xCoreID = ( BaseType_t ) 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ )
{
#if ( configNUMBER_OF_CORES == 1 )
{
pxIdleTaskFunction = prvIdleTask;
}
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
{
/* In the FreeRTOS SMP, configNUMBER_OF_CORES - 1 passive idle tasks
* are also created to ensure that each core has an idle task to
* run when no other task is available to run. */
if( xCoreID == 0 )
{
pxIdleTaskFunction = prvIdleTask;
}
else
{
pxIdleTaskFunction = prvPassiveIdleTask;
}
}
#endif /* #if ( configNUMBER_OF_CORES == 1 ) */
/* Update the idle task name with suffix to differentiate the idle tasks.
* This function is not required in single core FreeRTOS since there is
* only one idle task. */
#if ( configNUMBER_OF_CORES > 1 )
{
/* Append the idle task number to the end of the name if there is space. */
if( xIdleTaskNameIndex < ( BaseType_t ) configMAX_TASK_NAME_LEN )
{
cIdleName[ xIdleTaskNameIndex ] = ( char ) ( xCoreID + '0' );
/* And append a null character if there is space. */
if( ( xIdleTaskNameIndex + 1 ) < ( BaseType_t ) configMAX_TASK_NAME_LEN )
{
cIdleName[ xIdleTaskNameIndex + 1 ] = '\0';
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* if ( configNUMBER_OF_CORES > 1 ) */
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
StaticTask_t * pxIdleTaskTCBBuffer = NULL;
StackType_t * pxIdleTaskStackBuffer = NULL;
configSTACK_DEPTH_TYPE uxIdleTaskStackSize;
/* The Idle task is created using user provided RAM - obtain the
* address of the RAM then create the idle task. */
#if ( configNUMBER_OF_CORES == 1 )
{
vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize );
}
#else
{
if( xCoreID == 0 )
{
vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize );
}
else
{
vApplicationGetPassiveIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize, ( BaseType_t ) ( xCoreID - 1 ) );
}
}
#endif /* if ( configNUMBER_OF_CORES == 1 ) */
xIdleTaskHandles[ xCoreID ] = xTaskCreateStatic( pxIdleTaskFunction,
cIdleName,
uxIdleTaskStackSize,
( void * ) NULL,
portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
pxIdleTaskStackBuffer,
pxIdleTaskTCBBuffer );
if( xIdleTaskHandles[ xCoreID ] != NULL )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
}
#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
{
/* The Idle task is being created using dynamically allocated RAM. */
xReturn = xTaskCreate( pxIdleTaskFunction,
cIdleName,
configMINIMAL_STACK_SIZE,
( void * ) NULL,
portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
&xIdleTaskHandles[ xCoreID ] );
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/* Break the loop if any of the idle task is failed to be created. */
if( xReturn != pdPASS )
{
break;
}
else
{
#if ( configNUMBER_OF_CORES == 1 )
{
mtCOVERAGE_TEST_MARKER();
}
#else
{
/* Assign idle task to each core before SMP scheduler is running. */
xIdleTaskHandles[ xCoreID ]->xTaskRunState = xCoreID;
pxCurrentTCBs[ xCoreID ] = xIdleTaskHandles[ xCoreID ];
}
#endif
}
}
return xReturn;
}
4.2.2 xTaskCreate
xTaskCreate的处理流程可以参考FreeRTOS之xTaskCreate,其调用流程如下所示:
|- xTaskCreate
|- 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 ) );
4.3 xTimerCreateTimerTask
- 多核并设置亲和性:创建Timer 任务,同时做亲和性设置,该处理是在多核的情况下做的处理,如果是单核或者没有开启亲和性的配置则会调用xTaskCreate去创建Timer任务。
xReturn = xTaskCreateAffinitySet( prvTimerTask,
configTIMER_SERVICE_TASK_NAME,
configTIMER_TASK_STACK_DEPTH,
NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
configTIMER_SERVICE_TASK_CORE_AFFINITY,
&xTimerTaskHandle );
- 单核或者未开启亲和性设置:
xReturn = xTaskCreate( prvTimerTask,
configTIMER_SERVICE_TASK_NAME,
configTIMER_TASK_STACK_DEPTH,
NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
&xTimerTaskHandle );
BaseType_t xTimerCreateTimerTask( void )
{
BaseType_t xReturn = pdFAIL;
traceENTER_xTimerCreateTimerTask();
/* This function is called when the scheduler is started if
* configUSE_TIMERS is set to 1. Check that the infrastructure used by the
* timer service task has been created/initialised. If timers have already
* been created then the initialisation will already have been performed. */
prvCheckForValidListAndQueue();
if( xTimerQueue != NULL )
{
#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) )
{
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
StaticTask_t * pxTimerTaskTCBBuffer = NULL;
StackType_t * pxTimerTaskStackBuffer = NULL;
configSTACK_DEPTH_TYPE uxTimerTaskStackSize;
vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &uxTimerTaskStackSize );
xTimerTaskHandle = xTaskCreateStaticAffinitySet( prvTimerTask,
configTIMER_SERVICE_TASK_NAME,
uxTimerTaskStackSize,
NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
pxTimerTaskStackBuffer,
pxTimerTaskTCBBuffer,
configTIMER_SERVICE_TASK_CORE_AFFINITY );
if( xTimerTaskHandle != NULL )
{
xReturn = pdPASS;
}
}
#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
{
xReturn = xTaskCreateAffinitySet( prvTimerTask,
configTIMER_SERVICE_TASK_NAME,
configTIMER_TASK_STACK_DEPTH,
NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
configTIMER_SERVICE_TASK_CORE_AFFINITY,
&xTimerTaskHandle );
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
}
#else /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */
{
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
StaticTask_t * pxTimerTaskTCBBuffer = NULL;
StackType_t * pxTimerTaskStackBuffer = NULL;
configSTACK_DEPTH_TYPE uxTimerTaskStackSize;
vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &uxTimerTaskStackSize );
xTimerTaskHandle = xTaskCreateStatic( prvTimerTask,
configTIMER_SERVICE_TASK_NAME,
uxTimerTaskStackSize,
NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
pxTimerTaskStackBuffer,
pxTimerTaskTCBBuffer );
if( xTimerTaskHandle != NULL )
{
xReturn = pdPASS;
}
}
#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
{
xReturn = xTaskCreate( prvTimerTask,
configTIMER_SERVICE_TASK_NAME,
configTIMER_TASK_STACK_DEPTH,
NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
&xTimerTaskHandle );
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
}
#endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
configASSERT( xReturn );
traceRETURN_xTimerCreateTimerTask( xReturn );
return xReturn;
}
4.4 xTaskCreateAffinitySet
- pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ); 创建pcName任务,任务的处理函数为pxTaskCode;
- pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask; 将当前的任务绑定到指定的core上去处理。
- rvAddNewTaskToReadyList( pxNewTCB ); 将任务添加到Ready任务链表。
BaseType_t xTaskCreateAffinitySet( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE uxStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
UBaseType_t uxCoreAffinityMask,
TaskHandle_t * const pxCreatedTask )
{
TCB_t * pxNewTCB;
BaseType_t xReturn;
traceENTER_xTaskCreateAffinitySet( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, uxCoreAffinityMask, pxCreatedTask );
pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask );
if( pxNewTCB != NULL )
{
/* Set the task's affinity before scheduling it. */
pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask;
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
traceRETURN_xTaskCreateAffinitySet( xReturn );
return xReturn;
}
4.5 xPortStartScheduler
该函数和具体的处理器架构以及其对应处理器的实现有关,下面以Cortex-R5的为例来介绍
__asm volatile ( "MRS %0, APSR" : "=r" ( ulAPSR )::"memory" );
读取APSR
寄存器的状态值,用于后面,随后将处理器模式设置为System模式portCPU_IRQ_DISABLE();
disable IRQconfigSETUP_TICK_INTERRUPT();
开启Tick中断。vPortRestoreTaskContext();
开启第一个任务的执行
BaseType_t xPortStartScheduler( void )
{
uint32_t ulAPSR, ulCycles = 8; /* 8 bits per byte. */
#if ( configASSERT_DEFINED == 1 )
{
volatile uint8_t ucOriginalPriority;
volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( configINTERRUPT_CONTROLLER_BASE_ADDRESS + portINTERRUPT_PRIORITY_REGISTER_OFFSET );
volatile uint8_t ucMaxPriorityValue;
/*
* Determine how many priority bits are implemented in the GIC.
* Save the interrupt priority value that is about to be clobbered.
*/
ucOriginalPriority = *pucFirstUserPriorityRegister;
/*
* Determine the number of priority bits available. First write to
* all possible bits.
*/
*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
/* Read the value back to see how many bits stuck. */
ucMaxPriorityValue = *pucFirstUserPriorityRegister;
/* Shift to the least significant bits. */
while( ( ucMaxPriorityValue & portBIT_0_SET ) != portBIT_0_SET )
{
ucMaxPriorityValue >>= ( uint8_t ) 0x01;
/*
* If ulCycles reaches 0 then ucMaxPriorityValue must have been
* read as 0, indicating a misconfiguration.
*/
ulCycles--;
if( ulCycles == 0 )
{
break;
}
}
/*
* Sanity check configUNIQUE_INTERRUPT_PRIORITIES matches the read
* value.
*/
configASSERT( ucMaxPriorityValue == portLOWEST_INTERRUPT_PRIORITY );
/*
* Restore the clobbered interrupt priority register to its original
* value.
*/
*pucFirstUserPriorityRegister = ucOriginalPriority;
}
#endif /* configASSERT_DEFINED */
/*
* Only continue if the CPU is not in User mode. The CPU must be in a
* Privileged mode for the scheduler to start.
*/
__asm volatile ( "MRS %0, APSR" : "=r" ( ulAPSR )::"memory" );
ulAPSR &= portAPSR_MODE_BITS_MASK;
configASSERT( ulAPSR != portAPSR_USER_MODE );
if( ulAPSR != portAPSR_USER_MODE )
{
/*
* Only continue if the binary point value is set to its lowest possible
* setting. See the comments in vPortValidateInterruptPriority() below for
* more information.
*/
configASSERT( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE );
if( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE )
{
/*
* Interrupts are turned off in the CPU itself to ensure tick does
* not execute while the scheduler is being started. Interrupts are
* automatically turned back on in the CPU when the first task starts
* executing.
*/
portCPU_IRQ_DISABLE();
/* Start the timer that generates the tick ISR. */
configSETUP_TICK_INTERRUPT();
/* Start the first task executing. */
vPortRestoreTaskContext();
}
}
/*
* Will only get here if vTaskStartScheduler() was called with the CPU in
* a non-privileged mode or the binary point register was not set to its lowest
* possible value. prvTaskExitError() is referenced to prevent a compiler
* warning about it being defined but not referenced in the case that the user
* defines their own exit address.
*/
( void ) prvTaskExitError;
return 0;
}
4.6 vPortRestoreTaskContext
- CPS #SYS_MODE 切换当前处理器的模式为SYS模式
- portRESTORE_CONTEXT 回复用户栈
/******************************************************************************
* vPortRestoreTaskContext is used to start the scheduler.
*****************************************************************************/
.type vPortRestoreTaskContext, %function
vPortRestoreTaskContext:
/* Switch to system mode. */
CPS #SYS_MODE
portRESTORE_CONTEXT
4.7 portRESTORE_CONTEXT
- 取第一个任务,获取其对应的任务TCB,然后通过TCB获取其栈顶。
LDR R0, pxCurrentTCBConst
LDR R1, [R0]
LDR SP, [R1]
- 恢复用户设置的critical section nesting depth
LDR R0, ulCriticalNestingConst
POP {R1}
STR R1, [R0]
- 恢复用户栈
POP {R0-R12, R14}
.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
4.8 pxPortInitialiseStack
针对Cortex-R5处理器所设置的栈结构如下图所示:
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;
}