/* * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "sdkconfig.h" #include "esp_assert.h" #include "esp_heap_caps.h" #include "freertos/idf_additions.h" #if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT #include "esp_private/freertos_debug.h" #endif /* CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT */ #include "esp_private/freertos_idf_additions_priv.h" /** * This file will be included in `tasks.c` file, thus, it is treated as a source * file instead of a header file, and must NOT be included by any (other) file. * This file is used to add additional functions to `tasks.c`. See the * `esp_additions/include` directory of the headers that expose these `tasks.c` * additional API. */ /* ------------------------------------------------- Static Asserts ------------------------------------------------- */ /* * Both StaticTask_t and TCB_t structures are provided by FreeRTOS sources. * This is just an additional check of the consistency of these structures. */ _Static_assert( offsetof( StaticTask_t, pxDummy6 ) == offsetof( TCB_t, pxStack ) ); _Static_assert( offsetof( StaticTask_t, pxDummy8 ) == offsetof( TCB_t, pxEndOfStack ) ); _Static_assert( tskNO_AFFINITY == ( BaseType_t ) CONFIG_FREERTOS_NO_AFFINITY, "CONFIG_FREERTOS_NO_AFFINITY must be the same as tskNO_AFFINITY" ); /* ------------------------------------------------- Kernel Control ------------------------------------------------- */ #if ( !CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) /* * Wrapper function to take "xKerneLock" */ void prvTakeKernelLock( void ) { /* We call the tasks.c critical section macro to take xKernelLock */ taskENTER_CRITICAL( &xKernelLock ); } #endif /* ( !CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) */ /*----------------------------------------------------------*/ #if ( !CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) /* * Wrapper function to release "xKerneLock" */ void prvReleaseKernelLock( void ) { /* We call the tasks.c critical section macro to release xKernelLock */ taskEXIT_CRITICAL( &xKernelLock ); } #endif /* ( !CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) */ /*----------------------------------------------------------*/ #if ( CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) /* * Workaround for non-thread safe multi-core OS startup (see IDF-4524) */ void prvStartSchedulerOtherCores( void ) { /* This function is always called with interrupts disabled*/ xSchedulerRunning = pdTRUE; } #endif /* ( CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) */ /*----------------------------------------------------------*/ #if ( !CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) BaseType_t xTaskIncrementTickOtherCores( void ) { /* Minor optimization. This function can never switch cores mid * execution */ BaseType_t xCoreID = portGET_CORE_ID(); BaseType_t xSwitchRequired = pdFALSE; /* This function should never be called by Core 0. */ configASSERT( xCoreID != 0 ); /* Called by the portable layer each time a tick interrupt occurs. * Increments the tick then checks to see if the new tick value will * cause any tasks to be unblocked. */ traceTASK_INCREMENT_TICK( xTickCount ); if( uxSchedulerSuspended[ xCoreID ] == ( UBaseType_t ) 0U ) { /* We need take the kernel lock here as we are about to access * kernel data structures. */ taskENTER_CRITICAL_ISR( &xKernelLock ); /* A task being unblocked cannot cause an immediate context switch * if preemption is turned off. */ #if ( configUSE_PREEMPTION == 1 ) { /* Check if core 0 calling xTaskIncrementTick() has * unblocked a task that can be run. */ if( uxTopReadyPriority > pxCurrentTCBs[ xCoreID ]->uxPriority ) { xSwitchRequired = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } #endif /* if ( configUSE_PREEMPTION == 1 ) */ /* Tasks of equal priority to the currently running task will share * processing time (time slice) if preemption is on, and the application * writer has not explicitly turned time slicing off. */ #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) { if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCBs[ xCoreID ]->uxPriority ] ) ) > ( UBaseType_t ) 1 ) { xSwitchRequired = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ /* Release the previously taken kernel lock as we have finished * accessing the kernel data structures. */ taskEXIT_CRITICAL_ISR( &xKernelLock ); #if ( configUSE_PREEMPTION == 1 ) { if( xYieldPending[ xCoreID ] != pdFALSE ) { xSwitchRequired = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } } #endif /* configUSE_PREEMPTION */ } #if ( configUSE_TICK_HOOK == 1 ) { vApplicationTickHook(); } #endif return xSwitchRequired; } #endif /* ( !CONFIG_FREERTOS_SMP && ( configNUM_CORES > 1 ) ) */ /*----------------------------------------------------------*/ /* -------------------------------------------------- Task Creation ------------------------------------------------- */ #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, const BaseType_t xCoreID ) { BaseType_t xReturn; configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE || xCoreID == tskNO_AFFINITY ); #if CONFIG_FREERTOS_SMP { /* If using Amazon SMP FreeRTOS. This function is just a wrapper around * xTaskCreate() or xTaskCreateAffinitySet(). */ #if ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) { /* Convert xCoreID into an affinity mask */ UBaseType_t uxCoreAffinityMask; /* Bit shifting << xCoreID is only valid if we have less than * 32 cores. */ ESP_STATIC_ASSERT( configNUM_CORES < 32 ); if( xCoreID == tskNO_AFFINITY ) { uxCoreAffinityMask = tskNO_AFFINITY; } else { uxCoreAffinityMask = ( 1 << xCoreID ); } xReturn = xTaskCreateAffinitySet( pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, uxCoreAffinityMask, pxCreatedTask ); } #else /* ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ { xReturn = xTaskCreate( pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ); } #endif /* ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ } #else /* CONFIG_FREERTOS_SMP */ { TCB_t * pxNewTCB; /* If the stack grows down then allocate the stack then the TCB so the * stack does not grow into the TCB. Likewise if the stack grows up * then allocate the TCB then the stack. */ #if ( portSTACK_GROWTH > 0 ) { /* Allocate space for the TCB. Where the memory comes from depends on * the implementation of the port malloc function and whether or not static * allocation is being used. */ pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); if( pxNewTCB != NULL ) { memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); /* Allocate space for the stack used by the task being created. * The base of the stack memory stored in the TCB so the task can * be deleted later if required. */ pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ if( pxNewTCB->pxStack == NULL ) { /* Could not allocate the stack. Delete the allocated TCB. */ vPortFree( pxNewTCB ); pxNewTCB = NULL; } } } #else /* portSTACK_GROWTH */ { StackType_t * pxStack; /* Allocate space for the stack used by the task being created. */ pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */ if( pxStack != NULL ) { /* Allocate space for the TCB. */ pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */ if( pxNewTCB != NULL ) { memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); /* Store the stack location in the TCB. */ pxNewTCB->pxStack = pxStack; } else { /* The stack cannot be used as the TCB was not created. Free * it again. */ vPortFreeStack( pxStack ); } } else { pxNewTCB = NULL; } } #endif /* portSTACK_GROWTH */ if( pxNewTCB != NULL ) { #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */ { /* Tasks can be created statically or dynamically, so note this * task was created dynamically in case it is later deleted. */ pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; } #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL, xCoreID ); prvAddNewTaskToReadyList( pxNewTCB ); xReturn = pdPASS; } else { xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; } } #endif /* CONFIG_FREERTOS_SMP */ return xReturn; } #endif /* ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ /*----------------------------------------------------------*/ #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const pxTaskBuffer, const BaseType_t xCoreID ) { TaskHandle_t xReturn; configASSERT( portVALID_STACK_MEM( puxStackBuffer ) ); configASSERT( portVALID_TCB_MEM( pxTaskBuffer ) ); configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE || xCoreID == tskNO_AFFINITY ); #if CONFIG_FREERTOS_SMP { /* If using Amazon SMP FreeRTOS. This function is just a wrapper around * xTaskCreateStatic() or xTaskCreateStaticAffinitySet(). */ #if ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) { /* Convert xCoreID into an affinity mask */ UBaseType_t uxCoreAffinityMask; /* Bit shifting << xCoreID is only valid if we have less than * 32 cores. */ ESP_STATIC_ASSERT( configNUM_CORES < 32 ); if( xCoreID == tskNO_AFFINITY ) { uxCoreAffinityMask = tskNO_AFFINITY; } else { uxCoreAffinityMask = ( 1 << xCoreID ); } xReturn = xTaskCreateStaticAffinitySet( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer, uxCoreAffinityMask ); } #else /* ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ { xReturn = xTaskCreateStatic( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer ); } #endif /* ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ } #else /* CONFIG_FREERTOS_SMP */ { TCB_t * pxNewTCB; #if ( configASSERT_DEFINED == 1 ) { /* Sanity check that the size of the structure used to declare a * variable of type StaticTask_t equals the size of the real task * structure. */ volatile size_t xSize = sizeof( StaticTask_t ); configASSERT( xSize == sizeof( TCB_t ) ); ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */ } #endif /* configASSERT_DEFINED */ if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) { /* The memory used for the task's TCB and stack are passed into this * function - use them. */ pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ { /* Tasks can be created statically or dynamically, so note this * task was created statically in case the task is later deleted. */ pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; } #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL, xCoreID ); prvAddNewTaskToReadyList( pxNewTCB ); } else { xReturn = NULL; } } #endif /* CONFIG_FREERTOS_SMP */ return xReturn; } #endif /* ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ /*----------------------------------------------------------*/ #if ( configUSE_TIMERS == 1 ) /* * In ESP-IDF, configUSE_TIMERS is always defined as 1 (i.e., not user configurable). * However, tasks.c: vTaskStartScheduler() will always call xTimerCreateTimerTask() * if ( configUSE_TIMERS == 1 ), thus causing the linker to link timers.c and * wasting some memory (due to the timer task being created)/ * * If we provide a weak version of xTimerCreateTimerTask(), this version will be * compiled if the application does not call any other FreeRTOS timer functions. * Thus we can save some text/RAM as timers.c will not be linked and the timer * task never created. */ BaseType_t __attribute__( ( weak ) ) xTimerCreateTimerTask( void ) { return pdPASS; } #endif /* configUSE_TIMERS */ /*----------------------------------------------------------*/ /* ------------------------------------------------- Task Utilities ------------------------------------------------- */ BaseType_t xTaskGetCoreID( TaskHandle_t xTask ) { BaseType_t xReturn; #if ( configNUM_CORES > 1 ) { #if CONFIG_FREERTOS_SMP UBaseType_t uxCoreAffinityMask; /* Get the core affinity mask and covert it to an ID */ uxCoreAffinityMask = vTaskCoreAffinityGet( xTask ); /* If the task is not pinned to a particular core, treat it as tskNO_AFFINITY */ if( uxCoreAffinityMask & ( uxCoreAffinityMask - 1 ) ) /* If more than one bit set */ { xReturn = tskNO_AFFINITY; } else { int iIndexPlusOne = __builtin_ffs( uxCoreAffinityMask ); assert( iIndexPlusOne >= 1 ); xReturn = iIndexPlusOne - 1; } #else /* CONFIG_FREERTOS_SMP */ TCB_t * pxTCB; /* Todo: Remove xCoreID for single core builds (IDF-7894) */ pxTCB = prvGetTCBFromHandle( xTask ); xReturn = pxTCB->xCoreID; #endif /* CONFIG_FREERTOS_SMP */ } #else /* configNUM_CORES > 1 */ { /* Single-core. Just return a core ID of 0 */ xReturn = 0; } #endif /* configNUM_CORES > 1 */ return xReturn; } /*----------------------------------------------------------*/ #if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) TaskHandle_t xTaskGetIdleTaskHandleForCore( BaseType_t xCoreID ) { /* If xTaskGetIdleTaskHandle() is called before the scheduler has been * started, then xIdleTaskHandle will be NULL. */ configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); configASSERT( ( xIdleTaskHandle[ xCoreID ] != NULL ) ); return xIdleTaskHandle[ xCoreID ]; } #endif /* INCLUDE_xTaskGetIdleTaskHandle */ /*----------------------------------------------------------*/ #if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) TaskHandle_t xTaskGetCurrentTaskHandleForCore( BaseType_t xCoreID ) { TaskHandle_t xReturn; configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); #if ( CONFIG_FREERTOS_SMP ) { xReturn = xTaskGetCurrentTaskHandleCPU( ( UBaseType_t ) xCoreID ); } #else /* CONFIG_FREERTOS_SMP */ { /* A critical section is not required as this function does not * guarantee that the TCB will still be valid when this function * returns. */ xReturn = pxCurrentTCBs[ xCoreID ]; } #endif /* CONFIG_FREERTOS_SMP */ return xReturn; } #endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */ /*----------------------------------------------------------*/ #if ( !CONFIG_FREERTOS_SMP && ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimeCounterForCore( BaseType_t xCoreID ) { uint32_t ulRunTimeCounter; configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); /* For SMP, we need to take the kernel lock here as we are about to * access kernel data structures. */ prvENTER_CRITICAL_SMP_ONLY( &xKernelLock ); { ulRunTimeCounter = xIdleTaskHandle[ xCoreID ]->ulRunTimeCounter; } /* Release the previously taken kernel lock. */ prvEXIT_CRITICAL_SMP_ONLY( &xKernelLock ); return ulRunTimeCounter; } #endif /* ( !CONFIG_FREERTOS_SMP && ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) */ /*----------------------------------------------------------*/ #if ( !CONFIG_FREERTOS_SMP && ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimePercentForCore( BaseType_t xCoreID ) { configRUN_TIME_COUNTER_TYPE ulTotalTime, ulReturn; configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); ulTotalTime = portGET_RUN_TIME_COUNTER_VALUE(); /* For percentage calculations. */ ulTotalTime /= ( configRUN_TIME_COUNTER_TYPE ) 100; /* Avoid divide by zero errors. */ if( ulTotalTime > ( configRUN_TIME_COUNTER_TYPE ) 0 ) { /* For SMP, we need to take the kernel lock here as we are about * to access kernel data structures. */ prvENTER_CRITICAL_SMP_ONLY( &xKernelLock ); { ulReturn = xIdleTaskHandle[ xCoreID ]->ulRunTimeCounter / ulTotalTime; } /* Release the previously taken kernel lock. */ prvEXIT_CRITICAL_SMP_ONLY( &xKernelLock ); } else { ulReturn = 0; } return ulReturn; } #endif /* ( !CONFIG_FREERTOS_SMP && ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) */ /*-----------------------------------------------------------*/ uint8_t * pxTaskGetStackStart( TaskHandle_t xTask ) { TCB_t * pxTCB; uint8_t * uxReturn; pxTCB = prvGetTCBFromHandle( xTask ); uxReturn = ( uint8_t * ) pxTCB->pxStack; return uxReturn; } /*----------------------------------------------------------*/ #if ( INCLUDE_vTaskPrioritySet == 1 ) void prvTaskPriorityRaise( prvTaskSavedPriority_t * pxSavedPriority, UBaseType_t uxNewPriority ) { TCB_t * pxTCB; UBaseType_t uxPriorityUsedOnEntry; configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ); /* Ensure the new priority is valid. */ if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) { uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; } #if CONFIG_FREERTOS_SMP taskENTER_CRITICAL(); #else taskENTER_CRITICAL( &xKernelLock ); #endif { pxTCB = prvGetTCBFromHandle( NULL ); #if ( configUSE_MUTEXES == 1 ) { pxSavedPriority->uxPriority = pxTCB->uxPriority; pxSavedPriority->uxBasePriority = pxTCB->uxBasePriority; /* If uxNewPriority < uxBasePriority, then there is nothing else to * do, as uxBasePriority is always <= uxPriority. */ if( uxNewPriority > pxTCB->uxBasePriority ) { pxTCB->uxBasePriority = uxNewPriority; /* Remember the task's current priority before attempting to * change it. If the task's current priority is changed, it must * be done so before moving the task between task lists) in order * for the taskRESET_READY_PRIORITY() macro to function correctly. */ uxPriorityUsedOnEntry = pxTCB->uxPriority; if( uxNewPriority > pxTCB->uxPriority ) { pxTCB->uxPriority = uxNewPriority; /* Only reset the event list item value if the value is not * being used for anything else. */ if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) { listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ } /* If the task is in the blocked or suspended list we need do * nothing more than change its priority variable. However, if * the task is in a ready list it needs to be removed and placed * in the list appropriate to its new priority. */ if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) { /* The task is currently in its ready list - remove before * adding it to its new ready list. As we are in a critical * section we can do this even if the scheduler is suspended. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { /* It is known that the task is in its ready list so * there is no need to check again and the port level * reset macro can be called directly. */ portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); } prvAddTaskToReadyList( pxTCB ); } } } } #else /* if ( configUSE_MUTEXES == 1 ) */ { pxSavedPriority->uxPriority = pxTCB->uxPriority; if( uxNewPriority > pxTCB->uxPriority ) { vTaskPrioritySet( NULL, uxNewPriority ); } } #endif /* if ( configUSE_MUTEXES == 1 ) */ } #if CONFIG_FREERTOS_SMP taskEXIT_CRITICAL(); #else taskEXIT_CRITICAL( &xKernelLock ); #endif } #endif /* INCLUDE_vTaskPrioritySet == 1 */ /*----------------------------------------------------------*/ #if ( INCLUDE_vTaskPrioritySet == 1 ) void prvTaskPriorityRestore( prvTaskSavedPriority_t * pxSavedPriority ) { TCB_t * pxTCB; UBaseType_t uxNewPriority; UBaseType_t uxPriorityUsedOnEntry; UBaseType_t uxBasePriorityUsedOnEntry; BaseType_t xYieldRequired = pdFALSE; #if CONFIG_FREERTOS_SMP taskENTER_CRITICAL(); #else taskENTER_CRITICAL( &xKernelLock ); #endif { pxTCB = prvGetTCBFromHandle( NULL ); #if ( configUSE_MUTEXES == 1 ) { /* If the saved uxBasePriority == the task's uxBasePriority, it means * that prvTaskPriorityRaise() never raised the task's uxBasePriority. * In that case, there is nothing else to do. */ if( pxSavedPriority->uxBasePriority != pxTCB->uxBasePriority ) { uxBasePriorityUsedOnEntry = pxTCB->uxBasePriority; pxTCB->uxBasePriority = pxSavedPriority->uxBasePriority; /* Remember the task's current priority before attempting to * change it. If the task's current priority is changed, it must * be done so before moving the task between task lists in order * for the taskRESET_READY_PRIORITY() macro to function correctly. */ uxPriorityUsedOnEntry = pxTCB->uxPriority; /* Check if the task inherited a priority after prvTaskPriorityRaise(). * If this is the case, there is nothing else to do. The priority * will be restored when the task disinherits its priority. */ if( pxTCB->uxPriority == uxBasePriorityUsedOnEntry ) { if( pxTCB->uxMutexesHeld == 0 ) { /* The task may have inherited a priority before prvTaskPriorityRaise() * then disinherited a priority after prvTaskPriorityRaise(). * Thus we need set the uxPriority to the saved base priority * so that the task's priority gets restored to the priority * before any inheritance or raising. */ pxTCB->uxPriority = pxSavedPriority->uxBasePriority; } else { /* The task may have inherited a priority before prvTaskPriorityRaise() * was called. Thus, we need to restore uxPriority to the * "saved uxPriority" so that the task still retains that * inherited priority. */ pxTCB->uxPriority = pxSavedPriority->uxPriority; } uxNewPriority = pxTCB->uxPriority; if( uxNewPriority < uxPriorityUsedOnEntry ) { /* Setting the priority of the running task down means * there may now be another task of higher priority that * is ready to execute. */ xYieldRequired = pdTRUE; } /* Only reset the event list item value if the value is not * being used for anything else. */ if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) { listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ } /* If the task is in the blocked or suspended list we need do * nothing more than change its priority variable. However, if * the task is in a ready list it needs to be removed and placed * in the list appropriate to its new priority. */ if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) { /* The task is currently in its ready list - remove before * adding it to its new ready list. As we are in a critical * section we can do this even if the scheduler is suspended. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { /* It is known that the task is in its ready list so * there is no need to check again and the port level * reset macro can be called directly. */ portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); } prvAddTaskToReadyList( pxTCB ); } if( xYieldRequired != pdFALSE ) { taskYIELD_IF_USING_PREEMPTION(); } } } } #else /* if ( configUSE_MUTEXES == 1 ) */ { vTaskPrioritySet( NULL, pxSavedPriority->uxPriority ); } #endif /* if ( configUSE_MUTEXES == 1 ) */ } #if CONFIG_FREERTOS_SMP taskEXIT_CRITICAL(); #else taskEXIT_CRITICAL( &xKernelLock ); #endif } #endif /* ( INCLUDE_vTaskPrioritySet == 1 ) */ /*----------------------------------------------------------*/ /* --------------------------------------------- TLSP Deletion Callbacks -------------------------------------------- */ #if CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS void vTaskSetThreadLocalStoragePointerAndDelCallback( TaskHandle_t xTaskToSet, BaseType_t xIndex, void * pvValue, TlsDeleteCallbackFunction_t pvDelCallback ) { /* If TLSP deletion callbacks are enabled, then configNUM_THREAD_LOCAL_STORAGE_POINTERS * is doubled in size so that the latter half of the pvThreadLocalStoragePointers * stores the deletion callbacks. */ if( xIndex < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ) { TCB_t * pxTCB; #if ( configNUM_CORES > 1 ) { /* For SMP, we need a critical section as another core could also * update this task's TLSP at the same time. */ #if CONFIG_FREERTOS_SMP { taskENTER_CRITICAL(); } #else /* CONFIG_FREERTOS_SMP */ { taskENTER_CRITICAL( &xKernelLock ); } #endif /* CONFIG_FREERTOS_SMP */ } #endif /* configNUM_CORES > 1 */ pxTCB = prvGetTCBFromHandle( xTaskToSet ); /* Store the TLSP by indexing the first half of the array */ pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; /* Store the TLSP deletion callback by indexing the second half * of the array. */ pxTCB->pvThreadLocalStoragePointers[ ( xIndex + ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ) ] = ( void * ) pvDelCallback; #if ( configNUM_CORES > 1 ) { #if CONFIG_FREERTOS_SMP { taskEXIT_CRITICAL(); } #else /* CONFIG_FREERTOS_SMP */ { taskEXIT_CRITICAL( &xKernelLock ); } #endif /* CONFIG_FREERTOS_SMP */ } #endif /* configNUM_CORES > 1 */ } } #endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ /*----------------------------------------------------------*/ /* ----------------------------------------------------- Newlib ----------------------------------------------------- */ #if ( configUSE_NEWLIB_REENTRANT == 1 ) /** * @brief Get reentrancy structure of the current task * * - This funciton is required by newlib (when __DYNAMIC_REENT__ is enabled) * - It will return a pointer to the current task's reent struct * - If FreeRTOS is not running, it will return the global reent struct * * @return Pointer to a the (current taks's)/(globa) reent struct */ struct _reent * __getreent( void ) { /* No lock needed because if this changes, we won't be running anymore. */ TCB_t * pxCurTask = ( TCB_t * ) xTaskGetCurrentTaskHandle(); struct _reent * ret; if( pxCurTask == NULL ) { /* No task running. Return global struct. */ ret = _GLOBAL_REENT; } else { /* We have a task; return its reentrant struct. */ #if ( CONFIG_FREERTOS_SMP ) { ret = &pxCurTask->xNewLib_reent; } #else /* CONFIG_FREERTOS_SMP */ { ret = &pxCurTask->xTLSBlock; } #endif /* CONFIG_FREERTOS_SMP */ } return ret; } #endif /* configUSE_NEWLIB_REENTRANT == 1 */ /* -------------------------------------------------- Task Snapshot ------------------------------------------------- */ /** * @brief List of all task lists in FreeRTOS * * @note There are currently differing number of task list between SMP FreeRTOS and ESP-IDF FreeRTOS */ static List_t * non_ready_task_lists[] = { #ifdef CONFIG_FREERTOS_SMP &xPendingReadyList, #else /* CONFIG_FREERTOS_SMP */ &xPendingReadyList[ 0 ], #ifndef CONFIG_FREERTOS_UNICORE &xPendingReadyList[ 1 ], #endif /* CONFIG_FREERTOS_UNICORE */ #endif /* CONFIG_FREERTOS_SMP */ &xDelayedTaskList1, &xDelayedTaskList2, #if ( INCLUDE_vTaskDelete == 1 ) &xTasksWaitingTermination, #endif #if ( INCLUDE_vTaskSuspend == 1 ) &xSuspendedTaskList, #endif }; /*----------------------------------------------------------*/ /** * @brief Get the task list from state lists by index * * - This function returns the task list based on the specified index. * - The index is relative to the below order of the task state lists * - Ready lists (highest to lowers priority) * - Pending ready list(s) * - Delayed list 1 * - Delayed list 2 * - Waiting termination list * - Suspended list * * @param uxListIndex The index of the desired task list. * @return A pointer to the task list at the specified index. * Returns NULL if the index is out of bounds or list is corrupted. */ static List_t * pxGetTaskListByIndex( UBaseType_t uxListIndex ) { List_t * pxTaskList; const size_t xNonReadyTaskListsCnt = ( sizeof( non_ready_task_lists ) / sizeof( List_t * ) ); if( uxListIndex < configMAX_PRIORITIES ) { pxTaskList = &pxReadyTasksLists[ configMAX_PRIORITIES - 1 - uxListIndex ]; } else if( uxListIndex < configMAX_PRIORITIES + xNonReadyTaskListsCnt + 1 ) { pxTaskList = non_ready_task_lists[ uxListIndex - configMAX_PRIORITIES ]; } else { pxTaskList = NULL; } /* sanity check */ if( pxTaskList ) { if( !portVALID_LIST_MEM( pxTaskList ) ) { pxTaskList = NULL; } } return pxTaskList; } /*----------------------------------------------------------*/ /** * @brief Get the total count of task lists. * * The count includes both the ready task lists (based on priority) and non-ready task lists. * * @return The total count of task lists. * */ static inline UBaseType_t pxGetTaskListCount( void ) { return configMAX_PRIORITIES + ( sizeof( non_ready_task_lists ) / sizeof( List_t * ) ); } /*----------------------------------------------------------*/ /** * @brief Get the next task using the task iterator. * * This function retrieves the next task in the traversal sequence. * * @param xIterator Pointer to the task iterator structure. * * @return Index of the current task list. Returns -1 if all tasks have been traversed. * * @note The task iterator keeps track of the current state during task traversal, * including the index of the current task list and the pointer of the next task list item. * When all tasks have been traversed, this function returns -1. * If a broken or corrupted task is encountered, the task handle is set to NULL. */ int xTaskGetNext( TaskIterator_t * xIterator ) { if( !xIterator ) { return -1; } ListItem_t * pxNextListItem = xIterator->pxNextListItem; UBaseType_t uxCurListIdx = xIterator->uxCurrentListIndex; UBaseType_t uxMaxListIdx = pxGetTaskListCount(); for( ; uxCurListIdx < uxMaxListIdx; ++uxCurListIdx ) { List_t * pxCurrentTaskList = pxGetTaskListByIndex( uxCurListIdx ); if( !pxCurrentTaskList || ( listCURRENT_LIST_LENGTH( pxCurrentTaskList ) == 0 ) ) { continue; } const ListItem_t * pxCurrListItem = listGET_END_MARKER( pxCurrentTaskList ); if( !pxNextListItem ) { /* We are here if the traversal starts from the beginning or when we finish traversing * for one of the state lists */ pxNextListItem = listGET_NEXT( pxCurrListItem ); } if( !portVALID_LIST_MEM( pxNextListItem ) ) { /* Nothing to do with the corrupted list item. We will skip to the next task state list. * pxNextListItem should be NULL at the beggining of each task list. */ pxNextListItem = NULL; continue; } TCB_t * pxTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxNextListItem ); if( !portVALID_TCB_MEM( pxTCB ) ) { pxTCB = NULL; } xIterator->pxTaskHandle = pxTCB; xIterator->uxCurrentListIndex = uxCurListIdx; if( pxCurrListItem->pxPrevious == pxNextListItem ) { /* If this is the last item of the current state list */ xIterator->uxCurrentListIndex++; xIterator->pxNextListItem = NULL; } else { xIterator->pxNextListItem = listGET_NEXT( pxNextListItem ); } return uxCurListIdx; } return -1; /* end of the task list */ } /*----------------------------------------------------------*/ BaseType_t vTaskGetSnapshot( TaskHandle_t pxTask, TaskSnapshot_t * pxTaskSnapshot ) { if( ( portVALID_TCB_MEM( pxTask ) == false ) || ( pxTaskSnapshot == NULL ) ) { return pdFALSE; } TCB_t * pxTCB = ( TCB_t * ) pxTask; pxTaskSnapshot->pxTCB = pxTCB; pxTaskSnapshot->pxTopOfStack = ( StackType_t * ) pxTCB->pxTopOfStack; pxTaskSnapshot->pxEndOfStack = ( StackType_t * ) pxTCB->pxEndOfStack; return pdTRUE; } /*----------------------------------------------------------*/ UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArrayLength, UBaseType_t * const pxTCBSize ) { UBaseType_t uxArrayNumFilled = 0; /* Traverse all of the tasks lists */ TaskIterator_t xTaskIter = { 0 }; /* Point to the first task list */ while( xTaskGetNext( &xTaskIter ) != -1 && uxArrayNumFilled < uxArrayLength ) { vTaskGetSnapshot( xTaskIter.pxTaskHandle, &pxTaskSnapshotArray[ uxArrayNumFilled ] ); uxArrayNumFilled++; } *pxTCBSize = sizeof( TCB_t ); return uxArrayNumFilled; } /*----------------------------------------------------------*/ /* ----------------------------------------------------- Misc ----------------------------------------------------- */ void * pvTaskGetCurrentTCBForCore( BaseType_t xCoreID ) { void * pvRet; configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); #if CONFIG_FREERTOS_SMP /* SMP FreeRTOS defines pxCurrentTCB as a macro function call */ pvRet = ( void * ) pxCurrentTCB; #else /* CONFIG_FREERTOS_SMP */ pvRet = ( void * ) pxCurrentTCBs[ xCoreID ]; #endif /* CONFIG_FREERTOS_SMP */ return pvRet; } /* ----------------------------------------------------- OpenOCD ---------------------------------------------------- */ #if CONFIG_FREERTOS_DEBUG_OCDAWARE /** * Debug param indexes. DO NOT change the order. OpenOCD uses the same indexes * Entries in FreeRTOS_openocd_params must match the order of these indexes */ enum { ESP_FREERTOS_DEBUG_TABLE_SIZE = 0, ESP_FREERTOS_DEBUG_TABLE_VERSION, ESP_FREERTOS_DEBUG_KERNEL_VER_MAJOR, ESP_FREERTOS_DEBUG_KERNEL_VER_MINOR, ESP_FREERTOS_DEBUG_KERNEL_VER_BUILD, ESP_FREERTOS_DEBUG_UX_TOP_USED_PIORITY, ESP_FREERTOS_DEBUG_PX_TOP_OF_STACK, ESP_FREERTOS_DEBUG_PC_TASK_NAME, /* New entries must be inserted here */ ESP_FREERTOS_DEBUG_TABLE_END, }; const DRAM_ATTR uint8_t FreeRTOS_openocd_params[ ESP_FREERTOS_DEBUG_TABLE_END ] = { ESP_FREERTOS_DEBUG_TABLE_END, /* table size */ 1, /* table version */ tskKERNEL_VERSION_MAJOR, tskKERNEL_VERSION_MINOR, tskKERNEL_VERSION_BUILD, configMAX_PRIORITIES - 1, /* uxTopUsedPriority */ offsetof( TCB_t, pxTopOfStack ), /* thread_stack_offset; */ offsetof( TCB_t, pcTaskName ), /* thread_name_offset; */ }; #endif /* CONFIG_FREERTOS_DEBUG_OCDAWARE */ /*----------------------------------------------------------*/ /* ----------------------------------------------------- PSRAM ---------------------------------------------------- */ #if CONFIG_SPIRAM #if CONFIG_FREERTOS_SMP BaseType_t prvTaskCreateDynamicAffinitySetWithCaps( TaskFunction_t pxTaskCode, const char * const pcName, const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, UBaseType_t uxCoreAffinityMask, UBaseType_t uxStackMemoryCaps, TaskHandle_t * const pxCreatedTask ) #else /* CONFIG_FREERTOS_SMP */ BaseType_t prvTaskCreateDynamicPinnedToCoreWithCaps( TaskFunction_t pxTaskCode, const char * const pcName, const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, const BaseType_t xCoreID, UBaseType_t uxStackMemoryCaps, TaskHandle_t * const pxCreatedTask ) #endif /* CONFIG_FREERTOS_SMP */ { TCB_t * pxNewTCB; BaseType_t xReturn; StackType_t * pxStack; configASSERT( uxStackMemoryCaps & ( MALLOC_CAP_8BIT ) ); configASSERT( ( uxStackMemoryCaps & MALLOC_CAP_SPIRAM ) || ( uxStackMemoryCaps & MALLOC_CAP_INTERNAL ) ); #if ( !CONFIG_FREERTOS_SMP ) { configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE || xCoreID == tskNO_AFFINITY ); } #endif /* !CONFIG_FREERTOS_SMP */ /* Allocate space for the stack used by the task being created. */ pxStack = heap_caps_malloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), uxStackMemoryCaps ); if( pxStack != NULL ) { /* Allocate space for the TCB. */ pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); if( pxNewTCB != NULL ) { memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); /* Store the stack location in the TCB. */ pxNewTCB->pxStack = pxStack; } else { /* The stack cannot be used as the TCB has not been created. Free it. */ heap_caps_free( pxStack ); } } else { pxNewTCB = NULL; } if( pxNewTCB != NULL ) { #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) { /* Tasks can be created statically or dynamically, so note this * task was created dynamically in case it is later deleted. */ pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; } #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ #if CONFIG_FREERTOS_SMP { prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); #if ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) { /* Set the task's affinity before scheduling it */ pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask; } #endif /* ( ( configNUM_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ } #else /* CONFIG_FREERTOS_SMP */ { prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL, xCoreID ); } #endif /* CONFIG_FREERTOS_SMP */ prvAddNewTaskToReadyList( pxNewTCB ); xReturn = pdPASS; } else { xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; } return xReturn; } #endif /* CONFIG_SPIRAM */ /*----------------------------------------------------------*/