当前位置: 首页 > article >正文

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任务的处理函数为prvIdleTask
  • pxIdleTaskFunction = 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 IRQ
  • configSETUP_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;
}

http://www.kler.cn/a/418586.html

相关文章:

  • 支持向量机算法:原理、实现与应用
  • ros sensor_msgs::Imu详细介绍 Eigen::Vector3d 详细介绍
  • 宏集eXware物联网网关在水务管理系统上的应用
  • SpringSecurity构建登录模块
  • D84【python 接口自动化学习】- pytest基础用法
  • UE5 打包报错 Unknown structure 的解决方法
  • 电脑提示报错“Directx error”怎么解决?是什么原因导致的?游戏软件提示“Directx error”错误的解决方案
  • 龙蜥 Linux 安装 JDK
  • 华为仓颉编程环境搭建
  • PYNQ 框架 - OV5640驱动 + Linux 驱动分析
  • GPT(Generative Pre-trained Transformer) 和 Transformer的比较
  • 百度智能云千帆部署流程---语音识别和合成
  • 【前端】跨域问题与缓存
  • 泷羽sec- shell编程(8) until循环以及函数基本创建调用 学习笔记
  • 【OceanBase 诊断调优】—— 如何在 OceanBase 数据库 Oracle 模式中定位存储过程内的慢 SQL
  • CH32v20x单片机risc-v内核uint64_t类型移位后变量为0解决办法
  • 【热门主题】000075 探索嵌入式硬件设计的奥秘
  • js原型、原型链和继承
  • go语言的成神之路-筑基篇-gin框架渲染模板
  • 《datawhale2411组队学习 模型压缩技术7:NNI剪枝》
  • Angular v19 (三):增量水合特性详解 - 什么是水合过程?有哪些应用场景?与 Qwik 相比谁更胜一筹?- 哪个技术好我就学哪个,这就是吸心大法吧
  • 使用 client-go 实现 Kubernetes 节点 Drain:详解与实战教程
  • C_接口函数
  • 特性标记清理:GitHub Actions 来帮忙!
  • colorthief.js(图像中自动提取出主色调、调色板或者平均颜色)源码解析MMCQ算法
  • SnowFlake