/* * FreeRTOS V202212.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /* * The first test creates three tasks - two counter tasks (one continuous count * and one limited count) and one controller. A "count" variable is shared * between all three tasks. The two counter tasks should never be in a "ready" * state at the same time. The controller task runs at the same priority as * the continuous count task, and at a lower priority than the limited count * task. * * One counter task loops indefinitely, incrementing the shared count variable * on each iteration. To ensure it has exclusive access to the variable it * raises its priority above that of the controller task before each * increment, lowering it again to its original priority before starting the * next iteration. * * The other counter task increments the shared count variable on each * iteration of its loop until the count has reached a limit of 0xff - at * which point it suspends itself. It will not start a new loop until the * controller task has made it "ready" again by calling vTaskResume(). * This second counter task operates at a higher priority than controller * task so does not need to worry about mutual exclusion of the counter * variable. * * The controller task is in two sections. The first section controls and * monitors the continuous count task. When this section is operational the * limited count task is suspended. Likewise, the second section controls * and monitors the limited count task. When this section is operational the * continuous count task is suspended. * * In the first section the controller task first takes a copy of the shared * count variable. To ensure mutual exclusion on the count variable it * suspends the continuous count task, resuming it again when the copy has been * taken. The controller task then sleeps for a fixed period - during which * the continuous count task will execute and increment the shared variable. * When the controller task wakes it checks that the continuous count task * has executed by comparing the copy of the shared variable with its current * value. This time, to ensure mutual exclusion, the scheduler itself is * suspended with a call to vTaskSuspendAll (). This is for demonstration * purposes only and is not a recommended technique due to its inefficiency. * * After a fixed number of iterations the controller task suspends the * continuous count task, and moves on to its second section. * * At the start of the second section the shared variable is cleared to zero. * The limited count task is then woken from its suspension by a call to * vTaskResume (). As this counter task operates at a higher priority than * the controller task the controller task should not run again until the * shared variable has been counted up to the limited value causing the counter * task to suspend itself. The next line after vTaskResume () is therefore * a check on the shared variable to ensure everything is as expected. * * * The second test consists of a couple of very simple tasks that post onto a * queue while the scheduler is suspended. This test was added to test parts * of the scheduler not exercised by the first test. * */ #include /* Scheduler include files. */ #include "FreeRTOS.h" #include "task.h" #include "semphr.h" /* Demo app include files. */ #include "dynamic.h" /* Function that implements the "limited count" task as described above. */ static portTASK_FUNCTION_PROTO( vLimitedIncrementTask, pvParameters ); /* Function that implements the "continuous count" task as described above. */ static portTASK_FUNCTION_PROTO( vContinuousIncrementTask, pvParameters ); /* Function that implements the controller task as described above. */ static portTASK_FUNCTION_PROTO( vCounterControlTask, pvParameters ); static portTASK_FUNCTION_PROTO( vQueueReceiveWhenSuspendedTask, pvParameters ); static portTASK_FUNCTION_PROTO( vQueueSendWhenSuspendedTask, pvParameters ); /* Demo task specific constants. */ #ifndef priSUSPENDED_RX_TASK_STACK_SIZE #define priSUSPENDED_RX_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE ) #endif #define priSTACK_SIZE ( configMINIMAL_STACK_SIZE ) #define priSLEEP_TIME pdMS_TO_TICKS( 128 ) #define priLOOPS ( 5 ) #define priMAX_COUNT ( ( uint32_t ) 0xff ) #define priNO_BLOCK ( ( TickType_t ) 0 ) #define priSUSPENDED_QUEUE_LENGTH ( 1 ) /*-----------------------------------------------------------*/ /* Handles to the two counter tasks. These could be passed in as parameters * to the controller task to prevent them having to be file scope. */ static TaskHandle_t xContinuousIncrementHandle, xLimitedIncrementHandle; /* The shared counter variable. This is passed in as a parameter to the two * counter variables for demonstration purposes. */ static uint32_t ulCounter; /* Variables used to check that the tasks are still operating without error. * Each complete iteration of the controller task increments this variable * provided no errors have been found. The variable maintaining the same value * is therefore indication of an error. */ static volatile uint16_t usCheckVariable = ( uint16_t ) 0; static volatile BaseType_t xSuspendedQueueSendError = pdFALSE; static volatile BaseType_t xSuspendedQueueReceiveError = pdFALSE; /* Queue used by the second test. */ QueueHandle_t xSuspendedTestQueue; /* The value the queue receive task expects to receive next. This is file * scope so xAreDynamicPriorityTasksStillRunning() can ensure it is still * incrementing. */ static uint32_t ulExpectedValue = ( uint32_t ) 0; /*-----------------------------------------------------------*/ /* * Start the three tasks as described at the top of the file. * Note that the limited count task is given a higher priority. */ void vStartDynamicPriorityTasks( void ) { xSuspendedTestQueue = xQueueCreate( priSUSPENDED_QUEUE_LENGTH, sizeof( uint32_t ) ); if( xSuspendedTestQueue != NULL ) { /* vQueueAddToRegistry() adds the queue to the queue registry, if one is * in use. The queue registry is provided as a means for kernel aware * debuggers to locate queues and has no purpose if a kernel aware debugger * is not being used. The call to vQueueAddToRegistry() will be removed * by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is * defined to be less than 1. */ vQueueAddToRegistry( xSuspendedTestQueue, "Suspended_Test_Queue" ); xTaskCreate( vContinuousIncrementTask, "CNT_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY, &xContinuousIncrementHandle ); xTaskCreate( vLimitedIncrementTask, "LIM_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY + 1, &xLimitedIncrementHandle ); xTaskCreate( vCounterControlTask, "C_CTRL", priSUSPENDED_RX_TASK_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); xTaskCreate( vQueueSendWhenSuspendedTask, "SUSP_TX", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); xTaskCreate( vQueueReceiveWhenSuspendedTask, "SUSP_RX", priSUSPENDED_RX_TASK_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); } } /*-----------------------------------------------------------*/ /* * Just loops around incrementing the shared variable until the limit has been * reached. Once the limit has been reached it suspends itself. */ static portTASK_FUNCTION( vLimitedIncrementTask, pvParameters ) { volatile uint32_t * pulCounter; /* Take a pointer to the shared variable from the parameters passed into * the task. */ pulCounter = ( volatile uint32_t * ) pvParameters; /* This will run before the control task, so the first thing it does is * suspend - the control task will resume it when ready. */ vTaskSuspend( NULL ); for( ; ; ) { /* Just count up to a value then suspend. */ ( *pulCounter )++; if( *pulCounter >= priMAX_COUNT ) { vTaskSuspend( NULL ); } } } /*-----------------------------------------------------------*/ /* * Just keep counting the shared variable up. The control task will suspend * this task when it wants. */ static portTASK_FUNCTION( vContinuousIncrementTask, pvParameters ) { volatile uint32_t * pulCounter; UBaseType_t uxOurPriority; /* Take a pointer to the shared variable from the parameters passed into * the task. */ pulCounter = ( volatile uint32_t * ) pvParameters; /* Query our priority so we can raise it when exclusive access to the * shared variable is required. */ uxOurPriority = uxTaskPriorityGet( NULL ); for( ; ; ) { /* Raise the priority above the controller task to ensure a context * switch does not occur while the variable is being accessed. */ vTaskPrioritySet( NULL, uxOurPriority + 1 ); { configASSERT( ( uxTaskPriorityGet( NULL ) == ( uxOurPriority + 1 ) ) ); ( *pulCounter )++; } vTaskPrioritySet( NULL, uxOurPriority ); #if ( configUSE_PREEMPTION == 0 ) taskYIELD(); #endif configASSERT( ( uxTaskPriorityGet( NULL ) == uxOurPriority ) ); } } /*-----------------------------------------------------------*/ /* * Controller task as described above. */ static portTASK_FUNCTION( vCounterControlTask, pvParameters ) { uint32_t ulLastCounter; short sLoops; short sError = pdFALSE; /* Just to stop warning messages. */ ( void ) pvParameters; for( ; ; ) { /* Start with the counter at zero. */ ulCounter = ( uint32_t ) 0; /* First section : */ /* Check the continuous count task is running. */ for( sLoops = 0; sLoops < priLOOPS; sLoops++ ) { /* Suspend the continuous count task so we can take a mirror of the * shared variable without risk of corruption. This is not really * needed as the other task raises its priority above this task's * priority. */ vTaskSuspend( xContinuousIncrementHandle ); { #if ( INCLUDE_eTaskGetState == 1 ) { configASSERT( eTaskGetState( xContinuousIncrementHandle ) == eSuspended ); } #endif /* INCLUDE_eTaskGetState */ ulLastCounter = ulCounter; } vTaskResume( xContinuousIncrementHandle ); #if ( configUSE_PREEMPTION == 0 ) taskYIELD(); #endif #if ( INCLUDE_eTaskGetState == 1 ) { #if ( configNUMBER_OF_CORES > 1 ) { eTaskState eState = eTaskGetState( xContinuousIncrementHandle ); configASSERT( ( eState == eReady ) || ( eState == eRunning ) ); } #else { configASSERT( eTaskGetState( xContinuousIncrementHandle ) == eReady ); } #endif } #endif /* INCLUDE_eTaskGetState */ /* Now delay to ensure the other task has processor time. */ vTaskDelay( priSLEEP_TIME ); /* Check the shared variable again. This time to ensure mutual * exclusion the whole scheduler will be locked. This is just for * demo purposes! */ vTaskSuspendAll(); { if( ulLastCounter == ulCounter ) { /* The shared variable has not changed. There is a problem * with the continuous count task so flag an error. */ sError = pdTRUE; } } xTaskResumeAll(); } /* Second section: */ /* Suspend the continuous counter task so it stops accessing the shared * variable. */ vTaskSuspend( xContinuousIncrementHandle ); /* Reset the variable. */ ulCounter = ( uint32_t ) 0; #if ( INCLUDE_eTaskGetState == 1 ) { configASSERT( eTaskGetState( xLimitedIncrementHandle ) == eSuspended ); } #endif /* INCLUDE_eTaskGetState */ /* Resume the limited count task which has a higher priority than us. * We should therefore not return from this call until the limited count * task has suspended itself with a known value in the counter variable. */ vTaskResume( xLimitedIncrementHandle ); #if ( configUSE_PREEMPTION == 0 ) taskYIELD(); #endif /* This task should not run again until xLimitedIncrementHandle has * suspended itself. */ #if ( INCLUDE_eTaskGetState == 1 ) { configASSERT( eTaskGetState( xLimitedIncrementHandle ) == eSuspended ); } #endif /* INCLUDE_eTaskGetState */ /* Does the counter variable have the expected value? */ if( ulCounter != priMAX_COUNT ) { sError = pdTRUE; } if( sError == pdFALSE ) { /* If no errors have occurred then increment the check variable. */ portENTER_CRITICAL(); usCheckVariable++; portEXIT_CRITICAL(); } /* Resume the continuous count task and do it all again. */ vTaskResume( xContinuousIncrementHandle ); #if ( configUSE_PREEMPTION == 0 ) taskYIELD(); #endif } } /*-----------------------------------------------------------*/ static portTASK_FUNCTION( vQueueSendWhenSuspendedTask, pvParameters ) { static uint32_t ulValueToSend = ( uint32_t ) 0; /* Just to stop warning messages. */ ( void ) pvParameters; for( ; ; ) { vTaskSuspendAll(); { /* We must not block while the scheduler is suspended! */ if( xQueueSend( xSuspendedTestQueue, ( void * ) &ulValueToSend, priNO_BLOCK ) != pdTRUE ) { xSuspendedQueueSendError = pdTRUE; } } xTaskResumeAll(); vTaskDelay( priSLEEP_TIME ); ++ulValueToSend; } } /*-----------------------------------------------------------*/ static portTASK_FUNCTION( vQueueReceiveWhenSuspendedTask, pvParameters ) { uint32_t ulReceivedValue; BaseType_t xGotValue; /* Just to stop warning messages. */ ( void ) pvParameters; for( ; ; ) { do { /* Suspending the scheduler here is fairly pointless and * undesirable for a normal application. It is done here purely * to test the scheduler. The inner xTaskResumeAll() should * never return pdTRUE as the scheduler is still locked by the * outer call. */ vTaskSuspendAll(); { vTaskSuspendAll(); { xGotValue = xQueueReceive( xSuspendedTestQueue, ( void * ) &ulReceivedValue, priNO_BLOCK ); } if( xTaskResumeAll() != pdFALSE ) { xSuspendedQueueReceiveError = pdTRUE; } } xTaskResumeAll(); #if configUSE_PREEMPTION == 0 { taskYIELD(); } #endif } while( xGotValue == pdFALSE ); if( ulReceivedValue != ulExpectedValue ) { xSuspendedQueueReceiveError = pdTRUE; } if( xSuspendedQueueReceiveError != pdTRUE ) { /* Only increment the variable if an error has not occurred. This * allows xAreDynamicPriorityTasksStillRunning() to check for stalled * tasks as well as explicit errors. */ ++ulExpectedValue; } } } /*-----------------------------------------------------------*/ /* Called to check that all the created tasks are still running without error. */ BaseType_t xAreDynamicPriorityTasksStillRunning( void ) { /* Keep a history of the check variables so we know if it has been incremented * since the last call. */ static uint16_t usLastTaskCheck = ( uint16_t ) 0; static uint32_t ulLastExpectedValue = ( uint32_t ) 0U; BaseType_t xReturn = pdTRUE; /* Check the tasks are still running by ensuring the check variable * is still incrementing. */ if( usCheckVariable == usLastTaskCheck ) { /* The check has not incremented so an error exists. */ xReturn = pdFALSE; } if( ulExpectedValue == ulLastExpectedValue ) { /* The value being received by the queue receive task has not * incremented so an error exists. */ xReturn = pdFALSE; } if( xSuspendedQueueSendError == pdTRUE ) { xReturn = pdFALSE; } if( xSuspendedQueueReceiveError == pdTRUE ) { xReturn = pdFALSE; } usLastTaskCheck = usCheckVariable; ulLastExpectedValue = ulExpectedValue; return xReturn; }