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

  • pxIdleTaskFunction = prvIdleTask; 设置当前idle任务的处理函数为prvIdleTask
  • 创建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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/924118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NLP论文速读(EMNLP2024)|多风格可控生成的动态多奖励权重

论文速读|Dynamic Multi-Reward Weighting for Multi-Style Controllable Generation 论文信息&#xff1a; 简介&#xff1a; 本文探讨了文本风格在沟通中的重要性&#xff0c;指出文本风格传达了除原始语义内容之外的多种信息&#xff0c;如人际关系动态&#xff08;例如正式…

【AI】Sklearn

长期更新&#xff0c;建议关注、收藏、点赞。 友情链接&#xff1a; AI中的数学_线代微积分概率论最优化 Python numpy_pandas_matplotlib_spicy 建议路线&#xff1a;机器学习->深度学习->强化学习 目录 预处理模型选择分类实例&#xff1a; 二分类比赛 网格搜索实例&…

Dockerfile打包部署

Dockerfile打包 先找到打包完的目录下创建一个Dockerfile文件 touch Dockerfile 进去文件内编写 vim Dockerfile # 基础镜像 FROM openjdk:8 # author MAINTAINER yxh # 挂载目录 VOLUME /home/project # 创建目录 RUN mkdir -p /home/project # 指定路径 WORKDIR /home/pr…

鸿蒙学习使用模拟器运行应用(开发篇)

文章目录 1、系统类型和运行环境要求2、创建模拟器3、启动和关闭模拟器4、安装应用程序包和上传文件QA:在Windows电脑上启动模拟器&#xff0c;提示未开启Hyper-V 1、系统类型和运行环境要求 Windows 10 企业版、专业版或教育版及以上&#xff0c;且操作系统版本不低于10.0.18…

数组学习后记——递归

数组这块学得有点乱,条理性欠佳。这次正好总结一下。上周的课堂内容没有更新, 因为小白自己也还没来得及吸收呢qwq。也解释一下为什么文中有这么多例题。因为我呢喜欢就着题去分析和学习,直接灌输知识不太能理解,有例子就能及时检验和应用了的。 先看看B3817 基础的双数组…

每天五分钟深度学习:神经网络的前向传播的计算过程(单样本)

本文重点 本节课程我们学习神经网络的输出是如何计算的,这个过程叫做神经网络的前向传播。 神经网络的结构 如上所示是一个具有单隐藏层的神经网络,其中输入层不算神经网络的层数。 在这个神经网络中,x表示输入特征,a表示每个神经元的输出,W表示权重参数。 神经网络的…

C++——多态(下)

目录 引言 多态 4.多态的原理 4.1 虚函数表指针 4.2 多态的原理 5.单继承和多继承关系的虚函数表 5.1 单继承中的虚函数表 5.2 多继承中的虚函数表 结束语 引言 接下来我们继续学习多态。 没有阅读多态&#xff08;上&#xff09;的可以点击下面的链接哦~ C——多态…

【CSS in Depth 2 精译_061】9.4 CSS 中的模式库 + 9.5 本章小结

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第九章 CSS 的模块化与作用域】 ✔️ 9.1 模块的定义 9.1.1 模块和全局样式9.1.2 一个简单的 CSS 模块9.1.3 模块的变体9.1.4 多元素模块 9.2 将模块组合为更大的结构 9.2.1 模块中多个职责的拆分…

DHCP服务(包含配置过程)

目录 一、 DHCP的定义 二、 使用DHCP的好处 三、 DHCP的分配方式 四、 DHCP的租约过程 1. 客户机请求IP 2. 服务器响应 3. 客户机选择IP 4. 服务器确定租约 5. 重新登录 6. 更新租约 五、 DHCP服务配置过程 一、 DHCP的定义 DHCP&#xff08;Dynamic Host Configur…

技术实践 | AI 安全:通过大模型解决高危WEB应用识别问题

一、引言 在日常企业安全能力建设中&#xff0c;收敛企业外网高危资产&#xff0c;以保障公司外部安全是企业安全的重要工作。WEB 高危服务&#xff08;如&#xff1a;管理后台、内部系统等&#xff09;外开是企业所面临的一个重要风险。针对该风险&#xff0c;传统的方式是基…

C 语言函数递归探秘:从基础概念到复杂问题求解的进阶之路

我的个人主页 我的专栏&#xff1a;C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 目录 什么是函数递归递归的基本组成递归的工作原理递归的优缺点递归的经典案例 5.1 阶乘计算5.2 斐波那契数列5.3 汉诺塔问题5.4 二分查找 递归的高级…

多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测

多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 多输入多输出 | Matlab实现…

「Mac畅玩鸿蒙与硬件33」UI互动应用篇10 - 数字猜谜游戏

本篇将带你实现一个简单的数字猜谜游戏。用户输入一个数字&#xff0c;应用会判断是否接近目标数字&#xff0c;并提供提示“高一点”或“低一点”&#xff0c;直到用户猜中目标数字。这个小游戏结合状态管理和用户交互&#xff0c;是一个入门级的互动应用示例。 关键词 UI互…

el-table根据接口返回某一个字段合并行

根据名称相同合并行 <template><div><el-table :data"responseSearchIntegralAddData" :span-method"objectSpanMethod1" border style"width: 100%"><el-table-column prop"integralTypeName" label"名称…

Linux系统之fuser命令的基本使用

Linux系统之fuser命令的基本使用 一、fuser命令介绍二、fuser命令使用帮助2.1 help帮助信息2.1 基本语法①通用选项②文件/设备相关选项③网络相关选项④进程操作选项⑤其他选项 三、fuser命令的基本使用3.1 查找挂载点的进程3.2 查看指定设备进程信息3.3 查找监听特定端口的进…

守护进程

目录 守护进程 前台进程 后台进程 session&#xff08;进程会话&#xff09; 前台任务和后台任务比较好 本质 绘画和终端都关掉了&#xff0c;那些任务仍然在 bash也退了&#xff0c;然后就托孤了 ​编辑 守护进程化---不想受到任何用户登陆和注销的影响​编辑 如何…

网络安全在现代企业中的重要作用

网络安全是这个数字时代最令人担忧的事情之一。对技术的依赖性越来越强&#xff0c;使其同时面临多种网络威胁。其声誉和法律后果的大幅下降可能归因于一次妥协。 这使得良好的网络安全成为所有企业的选择和必需品。本文介绍了网络安全的重要性、企业中常见的网络威胁以及公司…

C++学习日记---第14天(蓝桥杯备赛)

笔记复习 1.对象的初始化和清理 对象的初始化和清理是两个非常重要的安全问题&#xff0c;一个对象或者变量没有初始状态&#xff0c;对其使用后果是未知&#xff0c;同样的使用完一个对象或者变量&#xff0c;没有及时清理&#xff0c;也会造成一定的安全问题 构造函数&…

Kotlin DSL Gradle 指南

本文是关于 Kotlin DSL Gradle 的指南&#xff08;上篇&#xff09;&#xff0c;介绍了 Gradle 作为 Android 开发构建工具的作用及优势&#xff0c;包括初始配置、生命周期、依赖管理、Task 相关内容。如 Task 的创建、自定义、各种方法和属性&#xff0c;以及文件操作等&…

深度学习笔记之BERT(三)RoBERTa

深度学习笔记之RoBERTa 引言回顾&#xff1a;BERT的预训练策略RoBERTa训练过程分析静态掩码与动态掩码的比较模型输入模式与下一句预测使用大批量进行训练使用Byte-pair Encoding作为子词词元化算法更大的数据集和更多的训练步骤 RoBERTa配置 引言 本节将介绍一种基于 BERT \t…