Hi !
I’m trying to make sense of the following code: uxPriority
is 1
!
void vStartSemaphoreTasks( UBaseType_t uxPriority )
{
xSemaphoreParameters * pxFirstSemaphoreParameters, * pxSecondSemaphoreParameters;
const TickType_t xBlockTime = ( TickType_t ) 100;
/* Create the structure used to pass parameters to the first two tasks. */
pxFirstSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );
if( pxFirstSemaphoreParameters != NULL )
{
/* Create the semaphore used by the first two tasks. */
pxFirstSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary();
if( pxFirstSemaphoreParameters->xSemaphore != NULL )
{
xSemaphoreGive( pxFirstSemaphoreParameters->xSemaphore );
/* Create the variable which is to be shared by the first two tasks. */
pxFirstSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) );
/* Initialise the share variable to the value the tasks expect. */
*( pxFirstSemaphoreParameters->pulSharedVariable ) = semtstNON_BLOCKING_EXPECTED_VALUE;
/* The first two tasks do not block on semaphore calls. */
pxFirstSemaphoreParameters->xBlockTime = ( TickType_t ) 0;
/* Spawn the first two tasks. As they poll they operate at the idle priority. */
xTaskCreate( prvSemaphoreTest, "PS1", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );
xTaskCreate( prvSemaphoreTest, "PS2", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );
/* vQueueAddToRegistry() adds the semaphore to the registry, if one
* is in use. The registry is provided as a means for kernel aware
* debuggers to locate semaphores 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( ( QueueHandle_t ) pxFirstSemaphoreParameters->xSemaphore, "Counting_Sem_1" );
}
}
/* Do exactly the same to create the second set of tasks, only this time
* provide a block time for the semaphore calls. */
pxSecondSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );
if( pxSecondSemaphoreParameters != NULL )
{
pxSecondSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary();
if( pxSecondSemaphoreParameters->xSemaphore != NULL )
{
xSemaphoreGive( pxSecondSemaphoreParameters->xSemaphore );
pxSecondSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) );
*( pxSecondSemaphoreParameters->pulSharedVariable ) = semtstBLOCKING_EXPECTED_VALUE;
pxSecondSemaphoreParameters->xBlockTime = xBlockTime / portTICK_PERIOD_MS;
xTaskCreate( prvSemaphoreTest, "BS1", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );
xTaskCreate( prvSemaphoreTest, "BS2", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );
/* vQueueAddToRegistry() adds the semaphore to the registry, if one
* is in use. The registry is provided as a means for kernel aware
* debuggers to locate semaphores 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( ( QueueHandle_t ) pxSecondSemaphoreParameters->xSemaphore, "Counting_Sem_2" );
}
}
}
/*-----------------------------------------------------------*/
#define TEMP_STR_LEN 9
#define DEBUG_PRINT(message) \
portENTER_CRITICAL(); \
sprintf((char *) tempstr, "%s:%s\n", tasknamestr, message); \
uart_printf((BYTE *) tempstr, TEMP_STR_LEN); \
portEXIT_CRITICAL();
#define DEBUG_PRINT_NUM(num) \
portENTER_CRITICAL(); \
sprintf((char *) tempstr, "%s:%3d\n", tasknamestr, num); \
uart_printf((BYTE *) tempstr, TEMP_STR_LEN); \
portEXIT_CRITICAL();
static portTASK_FUNCTION( prvSemaphoreTest, pvParameters )
{
xSemaphoreParameters * pxParameters;
volatile uint32_t * pulSharedVariable, ulExpectedValue;
uint32_t ulCounter;
short sError = pdFALSE, sCheckVariableToUse;
// Sid - 2022.02.23
char *tasknamestr = pcTaskGetName( NULL );
char tempstr[TEMP_STR_LEN];
/* See which check variable to use. sNextCheckVariable is not semaphore
* protected! */
portENTER_CRITICAL();
sCheckVariableToUse = sNextCheckVariable;
sNextCheckVariable++;
portEXIT_CRITICAL();
/* A structure is passed in as the parameter. This contains the shared
* variable being guarded. */
pxParameters = ( xSemaphoreParameters * ) pvParameters;
pulSharedVariable = pxParameters->pulSharedVariable;
/* If we are blocking we use a much higher count to ensure loads of context
* switches occur during the count. */
if( pxParameters->xBlockTime > ( TickType_t ) 0 )
{
ulExpectedValue = semtstBLOCKING_EXPECTED_VALUE;
}
else
{
ulExpectedValue = semtstNON_BLOCKING_EXPECTED_VALUE;
}
for( ; ; )
{
DEBUG_PRINT("IFS"); // Sid - IFS = Infinite For loop Started
/* Try to obtain the semaphore. */
if( xSemaphoreTake( pxParameters->xSemaphore, pxParameters->xBlockTime ) == pdPASS )
{
DEBUG_PRINT("STS"); // Sid - STS = Semaphore Take Success
/* We have the semaphore and so expect any other tasks using the
* shared variable to have left it in the state we expect to find
* it. */
if( *pulSharedVariable != ulExpectedValue )
{
sError = pdTRUE;
DEBUG_PRINT("SNE"); // Sid - SNE = Shared variable Not Expected value
}
/* Clear the variable, then count it back up to the expected value
* before releasing the semaphore. Would expect a context switch or
* two during this time. */
for( ulCounter = ( uint32_t ) 0; ulCounter <= ulExpectedValue; ulCounter++ )
{
*pulSharedVariable = ulCounter;
if( *pulSharedVariable != ulCounter )
{
// Sid - 2022.02.28
if (!sError)
{
DEBUG_PRINT("SNC"); // Sid - SNC = Shared variable Not Counter
}
sError = pdTRUE;
}
}
/* Release the semaphore, and if no errors have occurred increment the check
* variable. */
if( xSemaphoreGive( pxParameters->xSemaphore ) == pdFALSE )
{
DEBUG_PRINT("SGF"); // Sid - SGF = Semaphore Give Failure
sError = pdTRUE;
}
if( sError == pdFALSE )
{
DEBUG_PRINT("NEO"); // Sid - NEO = No Error Occurred
if( sCheckVariableToUse < semtstNUM_TASKS )
{
( sCheckVariables[ sCheckVariableToUse ] )++;
DEBUG_PRINT_NUM(sCheckVariables[ sCheckVariableToUse ]);
}
}
/* If we have a block time then we are running at a priority higher
* than the idle priority. This task takes a long time to complete
* a cycle (deliberately so to test the guarding) so will be starving
* out lower priority tasks. Block for some time to allow give lower
* priority tasks some processor time. */
if( pxParameters->xBlockTime != ( TickType_t ) 0 )
{
DEBUG_PRINT("CVD"); // Sid - CVD = Calling Vtask Delay
vTaskDelay( 10 ); //vTaskDelay( pxParameters->xBlockTime * semtstDELAY_FACTOR );
}
}
else
{
DEBUG_PRINT("STF"); // Sid - STF = Semaphore Take Failure
if( pxParameters->xBlockTime == ( TickType_t ) 0 )
{
DEBUG_PRINT("CTY"); // Sid - CTY = Calling Task Yield
/* We have not got the semaphore yet, so no point using the
* processor. We are not blocking when attempting to obtain the
* semaphore. */
taskYIELD();
}
}
}
}
Here is the initial part of my output:
BS1:IFS
BS1:STS
xQS/GS.
BS1:NEO
BS2:IFS
BS2:STS
xQS/GS.
BS2:NEO
BS1: 1
BS1:CVD
BS2: 1
BS2:CVD
PS1:IFS
PS1:STS
xQS/GS.
PS1:NEO
PS2:IFS
PS2:STS
xQS/GS.
PS2:NEO
PS2: 1
PS1: 1
PS1:IFS
PS1:STS
xQS/GS.
PS2:IFS
PS2:STF -> STF without SGF
PS2:CTY
PS1:NEO
PS2:IFS
PS2:STS
xQS/GS.
PS2:NEO
PS1: 2
PS1:IFS
PS1:STS
xQS/GS.
PS2: 2
PS2:IFS
PS2:STF -> STF without SGF
PS2:CTY
PS1:NEO
Why am I seeing no Semaphore Give Failure (SGF)
messages before Semaphore Take Failure (STF)
messages ?
Is it possible to treat xQueueSemaphoreTake()
as a black box & yet understand FreeRTOS semaphores ? (xQueueSemaphoreTake()
seems to be a large function & I’m not sure if diving into it is smart !)
Thanks in advance !