freertos: pin timer task in core 0 plus fixed in SMP race conditions

freertos: replace the freertos regular malloc to the specific malloc from xtensa port for tcb and stack allocations

freertos: avoid the cpu1 to unwind pended ticks when xTaskResumeAll is called insed of an ISR

freertos: protected the xPortGetCoreID functions with missing critical sections

tests: re-eanble the ignored tests that was failling before race-condition fixes
This commit is contained in:
Felipe Neves
2020-09-30 12:56:52 -03:00
committed by Angus Gratton
parent 3057b76a7e
commit dfa2d547a7
6 changed files with 403 additions and 105 deletions

View File

@@ -371,7 +371,7 @@ static void accessDPORT2_stall_other_cpu(void *pvParameters)
vTaskDelete(NULL); vTaskDelete(NULL);
} }
TEST_CASE("Check stall workaround DPORT and Hi-interrupt", "[esp32] [ignore]") TEST_CASE("Check stall workaround DPORT and Hi-interrupt", "[esp32]")
{ {
xt_highint5_read_apb = 0; xt_highint5_read_apb = 0;
dport_test_result = false; dport_test_result = false;

View File

@@ -282,7 +282,7 @@ static void check_wake_stub(void)
TEST_ASSERT_NULL(esp_get_deep_sleep_wake_stub()); TEST_ASSERT_NULL(esp_get_deep_sleep_wake_stub());
} }
TEST_CASE_MULTIPLE_STAGES("can set sleep wake stub", "[deepsleep][ignore][reset=DEEPSLEEP_RESET]", TEST_CASE_MULTIPLE_STAGES("can set sleep wake stub", "[deepsleep][reset=DEEPSLEEP_RESET]",
prepare_wake_stub, prepare_wake_stub,
check_wake_stub); check_wake_stub);

View File

@@ -742,7 +742,7 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
/* Allocate space for the TCB. Where the memory comes from depends /* Allocate space for the TCB. Where the memory comes from depends
on the implementation of the port malloc function and whether or on the implementation of the port malloc function and whether or
not static allocation is being used. */ not static allocation is being used. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) );
if( pxNewTCB != NULL ) if( pxNewTCB != NULL )
{ {
@@ -799,14 +799,14 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
/* Allocate space for the TCB. Where the memory comes from depends on /* Allocate space for the TCB. Where the memory comes from depends on
the implementation of the port malloc function and whether or not static the implementation of the port malloc function and whether or not static
allocation is being used. */ allocation is being used. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) );
if( pxNewTCB != NULL ) if( pxNewTCB != NULL )
{ {
/* Allocate space for the stack used by the task being created. /* 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 The base of the stack memory stored in the TCB so the task can
be deleted later if required. */ be deleted later if required. */
pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStackMem( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
if( pxNewTCB->pxStack == NULL ) if( pxNewTCB->pxStack == NULL )
{ {
@@ -821,12 +821,12 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
StackType_t *pxStack; StackType_t *pxStack;
/* Allocate space for the stack used by the task being created. */ /* Allocate space for the stack used by the task being created. */
pxStack = pvPortMalloc( ( ( ( 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. */ pxStack = pvPortMallocStackMem( ( ( ( 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 ) if( pxStack != NULL )
{ {
/* Allocate space for the TCB. */ /* 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. */ pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( 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 ) if( pxNewTCB != NULL )
{ {
@@ -1313,7 +1313,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
not return. */ not return. */
uxTaskNumber++; uxTaskNumber++;
if( pxTCB == pxCurrentTCB[xPortGetCoreID()] || if( pxTCB == pxCurrentTCB[core] ||
(portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core ]) || (portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core ]) ||
(portNUM_PROCESSORS > 1 && pxTCB->xCoreID == (!core)) ) (portNUM_PROCESSORS > 1 && pxTCB->xCoreID == (!core)) )
{ {
@@ -1334,7 +1334,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
after which it is not possible to yield away from this task - after which it is not possible to yield away from this task -
hence xYieldPending is used to latch that a context switch is hence xYieldPending is used to latch that a context switch is
required. */ required. */
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] ); portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[core] );
} }
else else
{ {
@@ -1362,7 +1362,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
been deleted. */ been deleted. */
if( xSchedulerRunning != pdFALSE ) if( xSchedulerRunning != pdFALSE )
{ {
if( pxTCB == pxCurrentTCB[xPortGetCoreID()] ) if( pxTCB == pxCurrentTCB[core] )
{ {
configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_SUSPENDED ); configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_SUSPENDED );
portYIELD_WITHIN_API(); portYIELD_WITHIN_API();
@@ -1719,10 +1719,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
/* The priority of a task other than the currently /* The priority of a task other than the currently
running task is being raised. Is the priority being running task is being raised. Is the priority being
raised above that of the running task? */ raised above that of the running task? */
if( uxNewPriority >= pxCurrentTCB[xPortGetCoreID()]->uxPriority ) if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority )
{ {
xYieldRequired = pdTRUE; xYieldRequired = pdTRUE;
} }
else if ( pxTCB->xCoreID != xPortGetCoreID() )
{
taskYIELD_OTHER_CORE( pxTCB->xCoreID, uxNewPriority );
}
else else
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
@@ -1857,6 +1861,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
void vTaskSuspend( TaskHandle_t xTaskToSuspend ) void vTaskSuspend( TaskHandle_t xTaskToSuspend )
{ {
TCB_t *pxTCB; TCB_t *pxTCB;
TCB_t *curTCB;
taskENTER_CRITICAL( &xTaskQueueMutex ); taskENTER_CRITICAL( &xTaskQueueMutex );
{ {
@@ -1888,6 +1893,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
} }
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
curTCB = pxCurrentTCB[ xPortGetCoreID() ];
#if( configUSE_TASK_NOTIFICATIONS == 1 ) #if( configUSE_TASK_NOTIFICATIONS == 1 )
{ {
@@ -1917,12 +1923,16 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
if( pxTCB == pxCurrentTCB[xPortGetCoreID()] ) if( pxTCB == curTCB )
{ {
if( xSchedulerRunning != pdFALSE ) if( xSchedulerRunning != pdFALSE )
{ {
/* The current task has just been suspended. */ /* The current task has just been suspended. */
configASSERT( uxSchedulerSuspended[xPortGetCoreID()] == 0 ); taskENTER_CRITICAL(&xTaskQueueMutex);
BaseType_t core = xPortGetCoreID();
taskEXIT_CRITICAL(&xTaskQueueMutex);
configASSERT( uxSchedulerSuspended[core] == 0 );
portYIELD_WITHIN_API(); portYIELD_WITHIN_API();
} }
else else
@@ -1936,7 +1946,9 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
NULL so when the next task is created pxCurrentTCB will NULL so when the next task is created pxCurrentTCB will
be set to point to it no matter what its relative priority be set to point to it no matter what its relative priority
is. */ is. */
pxCurrentTCB[xPortGetCoreID()] = NULL; taskENTER_CRITICAL(&xTaskQueueMutex);
pxCurrentTCB[ xPortGetCoreID() ] = NULL;
taskEXIT_CRITICAL(&xTaskQueueMutex);
} }
else else
{ {
@@ -1946,7 +1958,21 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
} }
else else
{ {
mtCOVERAGE_TEST_MARKER(); if( xSchedulerRunning != pdFALSE )
{
/* A task other than the currently running task was suspended,
reset the next expected unblock time in case it referred to the
task that is now in the Suspended state. */
taskENTER_CRITICAL(&xTaskQueueMutex);
{
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL(&xTaskQueueMutex);
}
else
{
mtCOVERAGE_TEST_MARKER();
}
} }
} }
@@ -2008,50 +2034,48 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
/* It does not make sense to resume the calling task. */ /* It does not make sense to resume the calling task. */
configASSERT( xTaskToResume ); configASSERT( xTaskToResume );
taskENTER_CRITICAL( &xTaskQueueMutex );
/* The parameter cannot be NULL as it is impossible to resume the /* The parameter cannot be NULL as it is impossible to resume the
currently executing task. */ currently executing task. */
if( ( pxTCB != pxCurrentTCB[xPortGetCoreID()] ) && ( pxTCB != NULL ) ) if( ( pxTCB != pxCurrentTCB[xPortGetCoreID()] ) && ( pxTCB != NULL ) )
{ {
taskENTER_CRITICAL( &xTaskQueueMutex ); if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
{ {
if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) traceTASK_RESUME( pxTCB );
/* The ready list can be accessed even if the scheduler is
suspended because this is inside a critical section. */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
/* We may have just resumed a higher priority task. */
if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority )
{ {
traceTASK_RESUME( pxTCB ); /* This yield may not cause the task just resumed to run,
but will leave the lists in the correct state for the
/* The ready list can be accessed even if the scheduler is next yield. */
suspended because this is inside a critical section. */ taskYIELD_IF_USING_PREEMPTION();
( void ) uxListRemove( &( pxTCB->xStateListItem ) ); }
prvAddTaskToReadyList( pxTCB ); else if( pxTCB->xCoreID != xPortGetCoreID() )
{
/* We may have just resumed a higher priority task. */ taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority );
if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority )
{
/* This yield may not cause the task just resumed to run,
but will leave the lists in the correct state for the
next yield. */
taskYIELD_IF_USING_PREEMPTION();
}
else if( pxTCB->xCoreID != xPortGetCoreID() )
{
taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
} }
else else
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
} }
taskEXIT_CRITICAL( &xTaskQueueMutex ); else
{
mtCOVERAGE_TEST_MARKER();
}
} }
else else
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
taskEXIT_CRITICAL( &xTaskQueueMutex );
} }
#endif /* INCLUDE_vTaskSuspend */ #endif /* INCLUDE_vTaskSuspend */
@@ -2314,64 +2338,63 @@ void vTaskSuspendAll( void )
#if ( configUSE_TICKLESS_IDLE != 0 ) #if ( configUSE_TICKLESS_IDLE != 0 )
#if ( portNUM_PROCESSORS > 1 )
static BaseType_t xHaveReadyTasks( void )
{
for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i)
{
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ i ] ) ) > 0 )
{
return pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
return pdFALSE;
}
#endif // portNUM_PROCESSORS > 1
static TickType_t prvGetExpectedIdleTime( void ) static TickType_t prvGetExpectedIdleTime( void )
{ {
TickType_t xReturn; TickType_t xReturn;
UBaseType_t uxHigherPriorityReadyTasks = pdFALSE;
/* uxHigherPriorityReadyTasks takes care of the case where
configUSE_PREEMPTION is 0, so there may be tasks above the idle priority
task that are in the Ready state, even though the idle task is
running. */
#if( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
{
if( uxTopReadyPriority > tskIDLE_PRIORITY )
{
uxHigherPriorityReadyTasks = pdTRUE;
}
}
#else
{
const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01;
/* When port optimised task selection is used the uxTopReadyPriority taskENTER_CRITICAL(&xTaskQueueMutex);
variable is used as a bit map. If bits other than the least if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority > tskIDLE_PRIORITY )
significant bit are set then there are tasks that have a priority
above the idle priority that are in the Ready state. This takes
care of the case where the co-operative scheduler is in use. */
if( uxTopReadyPriority > uxLeastSignificantBit )
{
uxHigherPriorityReadyTasks = pdTRUE;
}
}
#endif
if( pxCurrentTCB[xPortGetCoreID()]->uxPriority > tskIDLE_PRIORITY )
{ {
xReturn = 0; xReturn = 0;
} }
else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ) #if portNUM_PROCESSORS > 1
/* This function is called from Idle task; in single core case this
* means that no higher priority tasks are ready to run, and we can
* enter sleep. In SMP case, there might be ready tasks waiting for
* the other CPU, so need to check all ready lists.
*/
else if( xHaveReadyTasks() )
{
xReturn = 0;
}
#endif // portNUM_PROCESSORS > 1
else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > portNUM_PROCESSORS )
{ {
/* There are other idle priority tasks in the ready state. If /* There are other idle priority tasks in the ready state. If
time slicing is used then the very next tick interrupt must be time slicing is used then the very next tick interrupt must be
processed. */ processed. */
xReturn = 0; xReturn = 0;
} }
else if( uxHigherPriorityReadyTasks != pdFALSE )
{
/* There are tasks in the Ready state that have a priority above the
idle priority. This path can only be reached if
configUSE_PREEMPTION is 0. */
xReturn = 0;
}
else else
{ {
xReturn = xNextTaskUnblockTime - xTickCount; xReturn = xNextTaskUnblockTime - xTickCount;
} }
taskEXIT_CRITICAL(&xTaskQueueMutex);
return xReturn; return xReturn;
} }
#endif /* configUSE_TICKLESS_IDLE */ #endif /* configUSE_TICKLESS_IDLE */
/*----------------------------------------------------------*/ /*----------------------------------------------------------*/
@@ -2464,7 +2487,7 @@ TickType_t xTicksToNextUnblockTime;
decremented below. */ decremented below. */
xTicksToNextUnblockTime = ( TickType_t ) 1; xTicksToNextUnblockTime = ( TickType_t ) 1;
} }
else if( xTicksToNextUnblockTime > ( TickType_t ) 1 ) else if( xTicksToNextUnblockTime > ( TickType_t ) 1)
{ {
/* Move the tick count one short of the next unblock /* Move the tick count one short of the next unblock
time, then call xTaskIncrementTick() to move the tick time, then call xTaskIncrementTick() to move the tick
@@ -3170,7 +3193,7 @@ BaseType_t xSwitchRequired = pdFALSE;
/* If xTask is NULL then we are calling our own task hook. */ /* If xTask is NULL then we are calling our own task hook. */
if( xTask == NULL ) if( xTask == NULL )
{ {
xTCB = pxCurrentTCB[xPortGetCoreID()]; xTCB = xTaskGetCurrentTaskHandle();
} }
else else
{ {
@@ -4429,23 +4452,27 @@ TCB_t *pxTCB;
TaskHandle_t xTaskGetCurrentTaskHandle( void ) TaskHandle_t xTaskGetCurrentTaskHandle( void )
{ {
TaskHandle_t xReturn; TaskHandle_t xReturn;
unsigned state;
/* A critical section is not required as this is not called from state = portENTER_CRITICAL_NESTED();
an interrupt and the current TCB will always be the same for any xReturn = pxCurrentTCB[ xPortGetCoreID() ];
individual execution thread. */ portEXIT_CRITICAL_NESTED(state);
xReturn = pxCurrentTCB[xPortGetCoreID()];
return xReturn; return xReturn;
} }
TaskHandle_t xTaskGetCurrentTaskHandleForCPU( BaseType_t cpuid ) TaskHandle_t xTaskGetCurrentTaskHandleForCPU( BaseType_t cpuid )
{ {
(void)cpuid; TaskHandle_t xReturn=NULL;
return xTaskGetCurrentTaskHandle();
//Xtensa-specific: the pxCurrentPCB pointer is atomic so we shouldn't need a lock.
if (cpuid < portNUM_PROCESSORS) {
xReturn = pxCurrentTCB[ cpuid ];
}
return xReturn;
} }
#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */ #endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
@@ -4484,6 +4511,7 @@ TCB_t *pxTCB;
TCB_t * const pxMutexHolderTCB = pxMutexHolder; TCB_t * const pxMutexHolderTCB = pxMutexHolder;
BaseType_t xReturn = pdFALSE; BaseType_t xReturn = pdFALSE;
taskENTER_CRITICAL(&xTaskQueueMutex);
/* If the mutex was given back by an interrupt while the queue was /* If the mutex was given back by an interrupt while the queue was
locked then the mutex holder might now be NULL. _RB_ Is this still locked then the mutex holder might now be NULL. _RB_ Is this still
needed as interrupts can no longer use mutexes? */ needed as interrupts can no longer use mutexes? */
@@ -4560,6 +4588,7 @@ TCB_t *pxTCB;
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
taskEXIT_CRITICAL(&xTaskQueueMutex);
return xReturn; return xReturn;
} }
@@ -4574,6 +4603,7 @@ TCB_t *pxTCB;
TCB_t * const pxTCB = pxMutexHolder; TCB_t * const pxTCB = pxMutexHolder;
BaseType_t xReturn = pdFALSE; BaseType_t xReturn = pdFALSE;
taskENTER_CRITICAL(&xTaskQueueMutex);
if( pxMutexHolder != NULL ) if( pxMutexHolder != NULL )
{ {
/* A task can only have an inherited priority if it holds the mutex. /* A task can only have an inherited priority if it holds the mutex.
@@ -4640,6 +4670,7 @@ TCB_t *pxTCB;
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
taskEXIT_CRITICAL(&xTaskQueueMutex);
return xReturn; return xReturn;
} }
@@ -4654,7 +4685,8 @@ TCB_t *pxTCB;
TCB_t * const pxTCB = pxMutexHolder; TCB_t * const pxTCB = pxMutexHolder;
UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse;
const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1;
taskENTER_CRITICAL(&xTaskQueueMutex);
if( pxMutexHolder != NULL ) if( pxMutexHolder != NULL )
{ {
/* If pxMutexHolder is not NULL then the holder must hold at least /* If pxMutexHolder is not NULL then the holder must hold at least
@@ -4747,6 +4779,7 @@ TCB_t *pxTCB;
{ {
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
taskEXIT_CRITICAL(&xTaskQueueMutex);
} }
#endif /* configUSE_MUTEXES */ #endif /* configUSE_MUTEXES */
@@ -5071,11 +5104,13 @@ TickType_t uxTaskResetEventItemValue( void )
{ {
TickType_t uxReturn; TickType_t uxReturn;
uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB[xPortGetCoreID()]->xEventListItem ) ); taskENTER_CRITICAL(&xTaskQueueMutex);
uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB[ xPortGetCoreID() ]->xEventListItem ) );
/* Reset the event list item to its normal value - so it can be used with /* Reset the event list item to its normal value - so it can be used with
queues and semaphores. */ queues and semaphores. */
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB[xPortGetCoreID()]->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB[xPortGetCoreID()]->uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ listSET_LIST_ITEM_VALUE( &( pxCurrentTCB[ xPortGetCoreID() ]->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
taskEXIT_CRITICAL(&xTaskQueueMutex);
return uxReturn; return uxReturn;
} }
@@ -5083,16 +5118,21 @@ TickType_t uxReturn;
#if ( configUSE_MUTEXES == 1 ) #if ( configUSE_MUTEXES == 1 )
TaskHandle_t pvTaskIncrementMutexHeldCount( void ) void *pvTaskIncrementMutexHeldCount( void )
{ {
/* If xSemaphoreCreateMutex() is called before any tasks have been created TCB_t *curTCB;
then pxCurrentTCB[xPortGetCoreID()] will be NULL. */
if( pxCurrentTCB[xPortGetCoreID()] != NULL )
{
( pxCurrentTCB[xPortGetCoreID()]->uxMutexesHeld )++;
}
return pxCurrentTCB[xPortGetCoreID()]; /* If xSemaphoreCreateMutex() is called before any tasks have been created
then pxCurrentTCB will be NULL. */
taskENTER_CRITICAL(&xTaskQueueMutex);
if( pxCurrentTCB[ xPortGetCoreID() ] != NULL )
{
( pxCurrentTCB[ xPortGetCoreID() ]->uxMutexesHeld )++;
}
curTCB = pxCurrentTCB[ xPortGetCoreID() ];
taskEXIT_CRITICAL(&xTaskQueueMutex);
return curTCB;
} }
#endif /* configUSE_MUTEXES */ #endif /* configUSE_MUTEXES */
@@ -5615,7 +5655,10 @@ TickType_t uxReturn;
uint32_t ulTaskGetIdleRunTimeCounter( void ) uint32_t ulTaskGetIdleRunTimeCounter( void )
{ {
taskENTER_CRITICAL(&xTaskQueueMutex);
tskTCB *pxTCB = (tskTCB *)xIdleTaskHandle[xPortGetCoreID()]; tskTCB *pxTCB = (tskTCB *)xIdleTaskHandle[xPortGetCoreID()];
taskEXIT_CRITICAL(&xTaskQueueMutex);
return pxTCB->ulRunTimeCounter; return pxTCB->ulRunTimeCounter;
} }
@@ -5794,7 +5837,7 @@ const TickType_t xConstTickCount = xTickCount;
for (i = 0; i < portNUM_PROCESSORS; i++) { for (i = 0; i < portNUM_PROCESSORS; i++) {
if( uxTask >= uxArraySize ) if( uxTask >= uxArraySize )
break; break;
prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( xPendingReadyList[xPortGetCoreID()]) ); prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( xPendingReadyList[i]) );
} }
#if( INCLUDE_vTaskDelete == 1 ) #if( INCLUDE_vTaskDelete == 1 )

View File

@@ -0,0 +1,254 @@
/*
* Test features that are backported from version FreeRTOS 9.0.0.
*
* 1) Test backported timer functions
* - xTimerCreateStatic(), vTimerSetTimerId(), xTimerGetPeriod(), xTimerGetExpiryTime()
* 2) Test backported queue/semaphore functions
* - xQueueCreateStatic()
* - xSemaphoreCreateBinaryStatic(), xSemaphoreCreateCountingStatic(), uxSemaphoreGetCount()
* - xSemaphoreCreateMutexStatic(), xSemaphoreCreateRecursiveMutexStatic()
* 3) Test static allocation of tasks
* - xTaskCreateStaticPinnedToCore()
* 4) Test static allocation of event group
* - xEventGroupCreateStatic()
* 5) Test Thread Local Storage Pointers and Deletion Callbacks
* - vTaskSetThreadLocalStoragePointerAndDelCallback()
* - pvTaskGetThreadLocalStoragePointer()
*
* Note: The *pcQueueGetName() function is also backported, but is not tested in
* the following test cases (see Queue Registry test cases instead)
* For more details please refer the the ESP-IDF FreeRTOS changes documentation
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "unity.h"
#include "test_utils.h"
/* ---------------------Test 1: Backported Timer functions-----------------------
* Test xTimerCreateStatic(), vTimerSetTimerId(), xTimerGetPeriod(), xTimerGetExpiryTime()
*
* This test creates a one-shot static timer, sets/checks the timer's id and period. Then ensures
* the timer cb is executed in a timely fashion.
*/
#define TMR_PERIOD_TICKS 10
#define TIMER_ID 0xFF
#define TICK_DELTA 5
static StaticTimer_t timer_buffer;
static TickType_t tmr_ideal_exp;
static void tmr_cb(TimerHandle_t xtimer)
{
//Check cb is called in timely fashion
TEST_ASSERT_UINT32_WITHIN(TICK_DELTA, tmr_ideal_exp, xTaskGetTickCount());
}
//No need for smp test as Timer Task always runs on core 0
TEST_CASE("Test FreeRTOS backported timer functions", "[freertos]")
{
//Create one shot static timer with period TMR_PERIOD_TICKS
TimerHandle_t tmr_handle = xTimerCreateStatic("static_tmr", TMR_PERIOD_TICKS, pdFALSE, NULL, tmr_cb, &timer_buffer);
TEST_ASSERT_EQUAL(TMR_PERIOD_TICKS, xTimerGetPeriod(tmr_handle)); //Test xTimerGetPeriod()
vTimerSetTimerID(tmr_handle, (void *)TIMER_ID);
TEST_ASSERT_EQUAL(TIMER_ID, (uint32_t)pvTimerGetTimerID(tmr_handle)); //Test vTimerSetTimerID()
TEST_ASSERT_EQUAL(pdTRUE, xTimerStart(tmr_handle, 1)); //Start Timer
tmr_ideal_exp = xTaskGetTickCount() + TMR_PERIOD_TICKS; //Calculate ideal expiration time
vTaskDelay(2); //Need to yield to allow daemon task to process start command, or else expiration time will be NULL
TEST_ASSERT_UINT32_WITHIN(TICK_DELTA, tmr_ideal_exp, xTimerGetExpiryTime(tmr_handle)); //Test xTimerGetExpiryTime()
vTaskDelay(2*TMR_PERIOD_TICKS); //Delay until one shot timer has triggered
TEST_ASSERT_EQUAL(pdPASS, xTimerDelete(tmr_handle, portMAX_DELAY)); //Clean up
}
/* ---------------Test backported queue/semaphore functions-------------------
* xQueueCreateStatic()
* xSemaphoreCreateBinaryStatic(), xSemaphoreCreateCountingStatic()
* xSemaphoreCreateMutexStatic(), xSemaphoreCreateRecursiveMutexStatic()
* uxSemaphoreGetCount() is also tested on the static counting semaphore
*
* This test creates various static queue/semphrs listed above and tests them by
* doing a simple send/give and rec/take.
*/
#define ITEM_SIZE 3
#define NO_OF_ITEMS 3
#define DELAY_TICKS 2
static StaticQueue_t queue_buffer; //Queues, Semaphores, and Mutex use the same queue structure
static uint8_t queue_storage_area[(ITEM_SIZE*NO_OF_ITEMS)]; //Queue storage provided in separate buffer to queue struct
TEST_CASE("Test FreeRTOS backported Queue and Semphr functions", "[freertos]")
{
//Test static queue
uint8_t queue_item_to_send[ITEM_SIZE];
uint8_t queue_item_received[ITEM_SIZE];
for(int i = 0; i < ITEM_SIZE; i++){
queue_item_to_send[i] = (0xF << i);
}
QueueHandle_t handle = xQueueCreateStatic(NO_OF_ITEMS, ITEM_SIZE,(uint8_t*) &queue_storage_area, &queue_buffer);
TEST_ASSERT_EQUAL(pdTRUE, xQueueSendToBack(handle, &queue_item_to_send, DELAY_TICKS));
vTaskDelay(1);
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(handle, queue_item_received, DELAY_TICKS));
vTaskDelay(1);
for(int i = 0; i < ITEM_SIZE; i++){
TEST_ASSERT_EQUAL(queue_item_to_send[i], queue_item_received[i]); //Check received contents are correct
}
vQueueDelete(handle); //Technically not needed as deleting static queue/semphr doesn't clear static memory
//Test static binary semaphore
handle = xSemaphoreCreateBinaryStatic(&queue_buffer); //Queue and Semphr handles are the same
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle));
vTaskDelay(1);
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS));
vTaskDelay(1);
vSemaphoreDelete(handle);
//Test static counting semaphore and uxSemaphoreGetCount()
handle = xSemaphoreCreateCountingStatic(NO_OF_ITEMS, 0, &queue_buffer);
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL(NO_OF_ITEMS, uxSemaphoreGetCount(handle)); //Test uxSemaphoreGetCount()
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL(0, uxSemaphoreGetCount(handle));
vSemaphoreDelete(handle);
//Test static mutex
handle = xSemaphoreCreateMutexStatic(&queue_buffer);
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS));
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR((void *)xTaskGetCurrentTaskHandle(), xSemaphoreGetMutexHolder(handle)); //Current task should now hold mutex
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle));
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR(NULL, xSemaphoreGetMutexHolder(handle)); //Mutex should have been released
vSemaphoreDelete(handle);
//Test static mutex recursive
handle = xSemaphoreCreateRecursiveMutexStatic(&queue_buffer);
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTakeRecursive(handle, DELAY_TICKS));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR((void *)xTaskGetCurrentTaskHandle(), xSemaphoreGetMutexHolder(handle)); //Current task should hold mutex
for(int i = 0; i < NO_OF_ITEMS; i++){
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGiveRecursive(handle));
}
vTaskDelay(1);
TEST_ASSERT_EQUAL_PTR(NULL, xSemaphoreGetMutexHolder(handle)); //Mutex should have been released
vSemaphoreDelete(handle);
}
/* -----------------Test backported static task allocation -------------------
* Test xTaskCreateStaticPinnedToCore() but creating static task on each core
* and checking the task cb has run successfully.
*/
#define STACK_SIZE 2048 //Task stack size
static StackType_t task_stack[STACK_SIZE]; //Static buffer for task stack
static StaticTask_t task_buffer; //Static buffer for TCB
static bool has_run[portNUM_PROCESSORS];
static void task(void *arg)
{
has_run[xPortGetCoreID()] = true; //Signify static task cb has run
vTaskDelete(NULL);
}
TEST_CASE("Test FreeRTOS static task allocation", "[freertos]")
{
for(int core = 0; core < portNUM_PROCESSORS; core++){
has_run[core] = false; //Clear has_run flag
TaskHandle_t handle = xTaskCreateStaticPinnedToCore(task, "static task", STACK_SIZE, NULL,
UNITY_FREERTOS_PRIORITY + 1, (StackType_t *)&task_stack,
(StaticTask_t *)&task_buffer, core);
vTaskDelay(5); //Allow for static task to run, delete, and idle to clean up
TEST_ASSERT_NOT_EQUAL(NULL, handle); //Check static task was successfully allocated
TEST_ASSERT_TRUE(has_run[core]) //Check static task has run
}
}
/* ------------- Test backported static event group allocation -------------------
* Test xEventGroupCreateStatic() but creating static event group then waiting
* for an event.
*/
#define WAIT_BITS 0x01 //Wait for first bit
static StaticEventGroup_t event_group;
static EventGroupHandle_t eg_handle;
TEST_CASE("Test FreeRTOS backported eventgroup functions", "[freertos]")
{
eg_handle = xEventGroupCreateStatic((StaticEventGroup_t *)&event_group);
xEventGroupSetBits(eg_handle, WAIT_BITS);
TEST_ASSERT_EQUAL(WAIT_BITS, xEventGroupWaitBits(eg_handle, WAIT_BITS, pdTRUE, pdTRUE, portMAX_DELAY));
//Cleanup static event
vEventGroupDelete(eg_handle);
}
/* --------Test backported thread local storage pointer and deletion cb feature----------
* vTaskSetThreadLocalStoragePointerAndDelCallback()
* pvTaskGetThreadLocalStoragePointer(),
*
* This test creates a task and set's the task's TLSPs. The task is then deleted
* which should trigger the deletion cb.
*/
#define NO_OF_TLSP configNUM_THREAD_LOCAL_STORAGE_POINTERS
#define TLSP_SET_BASE 0x0F //0b1111 to be bit shifted by index
#define TLSP_DEL_BASE 0x05 //0b0101 to be bit shifted by index
//The variables pointed to by Thread Local Storage Pointer
static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0};
static void del_cb(int index, void *ptr)
{
*((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value
}
static void task_cb(void *arg)
{
int core = xPortGetCoreID();
for(int i = 0; i < NO_OF_TLSP; i++){
task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number
vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element
}
for(int i = 0; i < NO_OF_TLSP; i++){
uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i);
TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value
}
vTaskDelete(NULL); //Delete Task to Trigger TSLP deletion callback
}
TEST_CASE("Test FreeRTOS thread local storage pointers and del cb", "[freertos]")
{
//Create Task
for(int core = 0; core < portNUM_PROCESSORS; core++){
xTaskCreatePinnedToCore(task_cb, "task", 1024, NULL, UNITY_FREERTOS_PRIORITY+1, NULL, core);
}
vTaskDelay(10); //Delay long enough for tasks to run to completion
for(int core = 0; core < portNUM_PROCESSORS; core++){
for(int i = 0; i < NO_OF_TLSP; i++){
TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value
}
}
}

View File

@@ -246,13 +246,14 @@ BaseType_t xReturn = pdFAIL;
uint32_t ulTimerTaskStackSize; uint32_t ulTimerTaskStackSize;
vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize );
xTimerTaskHandle = xTaskCreateStatic( prvTimerTask, xTimerTaskHandle = xTaskCreateStaticPinnedToCore( prvTimerTask,
configTIMER_SERVICE_TASK_NAME, configTIMER_SERVICE_TASK_NAME,
ulTimerTaskStackSize, ulTimerTaskStackSize,
NULL, NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
pxTimerTaskStackBuffer, pxTimerTaskStackBuffer,
pxTimerTaskTCBBuffer ); pxTimerTaskTCBBuffer,
0 );
if( xTimerTaskHandle != NULL ) if( xTimerTaskHandle != NULL )
{ {
@@ -261,12 +262,12 @@ BaseType_t xReturn = pdFAIL;
} }
#else #else
{ {
xReturn = xTaskCreate( prvTimerTask, xReturn = xTaskCreatePinnedToCore( prvTimerTask,
configTIMER_SERVICE_TASK_NAME, configTIMER_SERVICE_TASK_NAME,
configTIMER_TASK_STACK_DEPTH, configTIMER_TASK_STACK_DEPTH,
NULL, NULL,
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
&xTimerTaskHandle ); &xTimerTaskHandle, 0 );
} }
#endif /* configSUPPORT_STATIC_ALLOCATION */ #endif /* configSUPPORT_STATIC_ALLOCATION */
} }

View File

@@ -129,7 +129,7 @@ done:
vTaskDelete(NULL); vTaskDelete(NULL);
} }
TEST_CASE("multiple tasks can access wl handle simultaneously", "[wear_levelling][ignore]") TEST_CASE("multiple tasks can access wl handle simultaneously", "[wear_levelling]")
{ {
const esp_partition_t *partition = get_test_data_partition(); const esp_partition_t *partition = get_test_data_partition();
wl_handle_t handle; wl_handle_t handle;