diff --git a/components/esp32/test/test_dport.c b/components/esp32/test/test_dport.c index ed2c082777..84b62be367 100644 --- a/components/esp32/test/test_dport.c +++ b/components/esp32/test/test_dport.c @@ -371,7 +371,7 @@ static void accessDPORT2_stall_other_cpu(void *pvParameters) 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; dport_test_result = false; diff --git a/components/esp_system/test/test_sleep.c b/components/esp_system/test/test_sleep.c index 4b6aed6fe7..5e6d90197c 100644 --- a/components/esp_system/test/test_sleep.c +++ b/components/esp_system/test/test_sleep.c @@ -282,7 +282,7 @@ static void check_wake_stub(void) 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, check_wake_stub); diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 1448ede230..42acba49ab 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -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 on the implementation of the port malloc function and whether or not static allocation is being used. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) ); 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 the implementation of the port malloc function and whether or not static allocation is being used. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) ); if( pxNewTCB != NULL ) { /* 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 * ) 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 ) { @@ -821,12 +821,12 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority ) StackType_t *pxStack; /* 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 ) { /* 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 ) { @@ -1313,7 +1313,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode not return. */ uxTaskNumber++; - if( pxTCB == pxCurrentTCB[xPortGetCoreID()] || + if( pxTCB == pxCurrentTCB[core] || (portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !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 - hence xYieldPending is used to latch that a context switch is required. */ - portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] ); + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[core] ); } else { @@ -1362,7 +1362,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode been deleted. */ if( xSchedulerRunning != pdFALSE ) { - if( pxTCB == pxCurrentTCB[xPortGetCoreID()] ) + if( pxTCB == pxCurrentTCB[core] ) { configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_SUSPENDED ); 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 running task is being raised. Is the priority being raised above that of the running task? */ - if( uxNewPriority >= pxCurrentTCB[xPortGetCoreID()]->uxPriority ) + if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, uxNewPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1857,6 +1861,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode void vTaskSuspend( TaskHandle_t xTaskToSuspend ) { TCB_t *pxTCB; + TCB_t *curTCB; taskENTER_CRITICAL( &xTaskQueueMutex ); { @@ -1888,6 +1893,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; #if( configUSE_TASK_NOTIFICATIONS == 1 ) { @@ -1917,12 +1923,16 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode mtCOVERAGE_TEST_MARKER(); } - if( pxTCB == pxCurrentTCB[xPortGetCoreID()] ) + if( pxTCB == curTCB ) { if( xSchedulerRunning != pdFALSE ) { /* 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(); } else @@ -1936,7 +1946,9 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode NULL so when the next task is created pxCurrentTCB will be set to point to it no matter what its relative priority is. */ - pxCurrentTCB[xPortGetCoreID()] = NULL; + taskENTER_CRITICAL(&xTaskQueueMutex); + pxCurrentTCB[ xPortGetCoreID() ] = NULL; + taskEXIT_CRITICAL(&xTaskQueueMutex); } else { @@ -1946,7 +1958,21 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } 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. */ configASSERT( xTaskToResume ); + taskENTER_CRITICAL( &xTaskQueueMutex ); /* The parameter cannot be NULL as it is impossible to resume the currently executing task. */ 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 ); - - /* 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 ) - { - /* 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(); - } + /* 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(); } } - taskEXIT_CRITICAL( &xTaskQueueMutex ); + else + { + mtCOVERAGE_TEST_MARKER(); + } } else { mtCOVERAGE_TEST_MARKER(); } + taskEXIT_CRITICAL( &xTaskQueueMutex ); } #endif /* INCLUDE_vTaskSuspend */ @@ -2314,64 +2338,63 @@ void vTaskSuspendAll( void ) #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 ) { 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 - variable is used as a bit map. If bits other than the least - 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 ) + taskENTER_CRITICAL(&xTaskQueueMutex); + if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority > tskIDLE_PRIORITY ) { 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 time slicing is used then the very next tick interrupt must be processed. */ 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 { xReturn = xNextTaskUnblockTime - xTickCount; } + taskEXIT_CRITICAL(&xTaskQueueMutex); return xReturn; } - + #endif /* configUSE_TICKLESS_IDLE */ /*----------------------------------------------------------*/ @@ -2464,7 +2487,7 @@ TickType_t xTicksToNextUnblockTime; decremented below. */ 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 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 == NULL ) { - xTCB = pxCurrentTCB[xPortGetCoreID()]; + xTCB = xTaskGetCurrentTaskHandle(); } else { @@ -4429,23 +4452,27 @@ TCB_t *pxTCB; TaskHandle_t xTaskGetCurrentTaskHandle( void ) { TaskHandle_t xReturn; + unsigned state; - /* A critical section is not required as this is not called from - an interrupt and the current TCB will always be the same for any - individual execution thread. */ - xReturn = pxCurrentTCB[xPortGetCoreID()]; + state = portENTER_CRITICAL_NESTED(); + xReturn = pxCurrentTCB[ xPortGetCoreID() ]; + portEXIT_CRITICAL_NESTED(state); return xReturn; } TaskHandle_t xTaskGetCurrentTaskHandleForCPU( BaseType_t cpuid ) { - (void)cpuid; - return xTaskGetCurrentTaskHandle(); + TaskHandle_t xReturn=NULL; + + //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 ) ) */ /*-----------------------------------------------------------*/ @@ -4484,6 +4511,7 @@ TCB_t *pxTCB; TCB_t * const pxMutexHolderTCB = pxMutexHolder; BaseType_t xReturn = pdFALSE; + taskENTER_CRITICAL(&xTaskQueueMutex); /* 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 needed as interrupts can no longer use mutexes? */ @@ -4560,6 +4588,7 @@ TCB_t *pxTCB; { mtCOVERAGE_TEST_MARKER(); } + taskEXIT_CRITICAL(&xTaskQueueMutex); return xReturn; } @@ -4574,6 +4603,7 @@ TCB_t *pxTCB; TCB_t * const pxTCB = pxMutexHolder; BaseType_t xReturn = pdFALSE; + taskENTER_CRITICAL(&xTaskQueueMutex); if( pxMutexHolder != NULL ) { /* A task can only have an inherited priority if it holds the mutex. @@ -4640,6 +4670,7 @@ TCB_t *pxTCB; { mtCOVERAGE_TEST_MARKER(); } + taskEXIT_CRITICAL(&xTaskQueueMutex); return xReturn; } @@ -4654,7 +4685,8 @@ TCB_t *pxTCB; TCB_t * const pxTCB = pxMutexHolder; UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; - + + taskENTER_CRITICAL(&xTaskQueueMutex); if( pxMutexHolder != NULL ) { /* If pxMutexHolder is not NULL then the holder must hold at least @@ -4747,6 +4779,7 @@ TCB_t *pxTCB; { mtCOVERAGE_TEST_MARKER(); } + taskEXIT_CRITICAL(&xTaskQueueMutex); } #endif /* configUSE_MUTEXES */ @@ -5071,11 +5104,13 @@ TickType_t uxTaskResetEventItemValue( void ) { 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 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; } @@ -5083,16 +5118,21 @@ TickType_t uxReturn; #if ( configUSE_MUTEXES == 1 ) - TaskHandle_t pvTaskIncrementMutexHeldCount( void ) + void *pvTaskIncrementMutexHeldCount( void ) { - /* If xSemaphoreCreateMutex() is called before any tasks have been created - then pxCurrentTCB[xPortGetCoreID()] will be NULL. */ - if( pxCurrentTCB[xPortGetCoreID()] != NULL ) - { - ( pxCurrentTCB[xPortGetCoreID()]->uxMutexesHeld )++; - } + TCB_t *curTCB; - 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 */ @@ -5615,7 +5655,10 @@ TickType_t uxReturn; uint32_t ulTaskGetIdleRunTimeCounter( void ) { + taskENTER_CRITICAL(&xTaskQueueMutex); tskTCB *pxTCB = (tskTCB *)xIdleTaskHandle[xPortGetCoreID()]; + taskEXIT_CRITICAL(&xTaskQueueMutex); + return pxTCB->ulRunTimeCounter; } @@ -5794,7 +5837,7 @@ const TickType_t xConstTickCount = xTickCount; for (i = 0; i < portNUM_PROCESSORS; i++) { if( uxTask >= uxArraySize ) break; - prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( xPendingReadyList[xPortGetCoreID()]) ); + prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( xPendingReadyList[i]) ); } #if( INCLUDE_vTaskDelete == 1 ) diff --git a/components/freertos/test/test_freertos_backported_functions.c b/components/freertos/test/test_freertos_backported_functions.c new file mode 100644 index 0000000000..f3fab9a8e6 --- /dev/null +++ b/components/freertos/test/test_freertos_backported_functions.c @@ -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 +#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 + } + } +} + diff --git a/components/freertos/timers.c b/components/freertos/timers.c index e41180b2a2..9dcc2a7111 100644 --- a/components/freertos/timers.c +++ b/components/freertos/timers.c @@ -246,13 +246,14 @@ BaseType_t xReturn = pdFAIL; uint32_t ulTimerTaskStackSize; vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); - xTimerTaskHandle = xTaskCreateStatic( prvTimerTask, + xTimerTaskHandle = xTaskCreateStaticPinnedToCore( prvTimerTask, configTIMER_SERVICE_TASK_NAME, ulTimerTaskStackSize, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, pxTimerTaskStackBuffer, - pxTimerTaskTCBBuffer ); + pxTimerTaskTCBBuffer, + 0 ); if( xTimerTaskHandle != NULL ) { @@ -261,12 +262,12 @@ BaseType_t xReturn = pdFAIL; } #else { - xReturn = xTaskCreate( prvTimerTask, + xReturn = xTaskCreatePinnedToCore( prvTimerTask, configTIMER_SERVICE_TASK_NAME, configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, - &xTimerTaskHandle ); + &xTimerTaskHandle, 0 ); } #endif /* configSUPPORT_STATIC_ALLOCATION */ } diff --git a/components/wear_levelling/test/test_wl.c b/components/wear_levelling/test/test_wl.c index 18d49aa1f9..f46e28c404 100644 --- a/components/wear_levelling/test/test_wl.c +++ b/components/wear_levelling/test/test_wl.c @@ -129,7 +129,7 @@ done: 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(); wl_handle_t handle;