mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
83c686c744
- vTaskPreemptionDisable()/vTaskPreemptionEnable() are no longer available in single-core builds. - xTaskIncrementTick() calls must now be wrapped by critical section - Minimal Idle Task renamed to Passive Idle Task - Port critical section APIs updated
1234 lines
48 KiB
C
1234 lines
48 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2015-2024 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 ) );
|
|
#if !CONFIG_IDF_TARGET_LINUX // Disabled for linux builds due to differences in types
|
|
_Static_assert( tskNO_AFFINITY == ( BaseType_t ) CONFIG_FREERTOS_NO_AFFINITY, "CONFIG_FREERTOS_NO_AFFINITY must be the same as tskNO_AFFINITY" );
|
|
#endif
|
|
|
|
/* ------------------------------------------------- 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
|
|
* on a core other than core 0. */
|
|
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 );
|
|
|
|
/* 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 ( ( !CONFIG_FREERTOS_SMP ) && ( 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 /* ( ( !CONFIG_FREERTOS_SMP ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
#if ( ( !CONFIG_FREERTOS_SMP ) && ( ( 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 /* ( ( !CONFIG_FREERTOS_SMP ) && ( ( 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 )
|
|
{
|
|
#if CONFIG_FREERTOS_SMP
|
|
taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB );
|
|
#else
|
|
taskYIELD_IF_USING_PREEMPTION();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#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 currently executing task. Return its reentrant struct. */
|
|
ret = &pxCurTask->xTLSBlock;
|
|
}
|
|
|
|
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++;
|
|
}
|
|
|
|
if( pxTCBSize != NULL )
|
|
{
|
|
*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 */
|
|
/*----------------------------------------------------------*/
|