Preemption of higher priority task

ettocap wrote on Tuesday, August 25, 2015:

Hello,

I study FreeRTOS mechanisms on STM32L4 target.
I suppose a problem on one of my tests. High priority task does not preempt low priority task.

This is what is done :
I create two task : Task one (priority 5), task two (priority 3)
Os tick is set to 1ms.
Task two loops on active waiting of 2ms (no OS waiting in its processing).
Task one waits an OS event flag.
Task one event is sent by hardware interruption routine each 5s.

I monitor task execution by switch microcontroller digital outputs.

What I watch :
Each 5s, task one event is sent by interruption, but task one is never unblocked of event waiting. Task two is executed all the time by doing its active waitings.

Do you know why OS scheduler does not apply task two preemption by task one ?

Thanks.

heinbali01 wrote on Tuesday, August 25, 2015:

Hi Fabien

I think it would be useful if you show the source code that you are using.

When you post code, you can put the literal code between two lines which only contain 5 tildas ~
jus tlike I do here (you don’t see the tildas) :

    void vtaskOne( void *pvParameter )
    {
    }

Regards.

ettocap wrote on Tuesday, August 25, 2015:

Hi !
OK, I post my code. I’ve got a small overlay to create tasks, manage event groups,…

Task characterisitics :

STATIC const SV_OS_stOsTask m_akst_osTasksList[AP_OS_eTASK_NUMBER] =
{
  [AP_OS_eTASK_ONE] =
  {
    .psz_name = "",                     /**< Empty name, to limit size of memory used */
    .pv_taskFunc = &AP_OS_taskOne,  /**<  Function of task */
    .pkv_taskParam = NULL,              /**<  Task parameters, not used */
    .u16_stackSize = 512,                /**<  Size of task size, in words (32 bits) */
    .e_priority = SV_OS_eTSK_PRIO_HIGH, /**<  Priority of task */
    .bCreateStarted = TRUE,
  },
  [AP_OS_eTASK_TWO] =
  {
    .psz_name = "",                       /**< Empty name, to limit size of memory used */
    .pv_taskFunc = &AP_OS_taskTwo,     /**<  Function of task */
    .pkv_taskParam = NULL,                /**<  Task parameters, not used */
    .u16_stackSize = 512,                  /**<  Size of task size, in words (32 bits) */
    .e_priority = SV_OS_eTSK_PRIO_NORMAL, /**<  Priority of task */
    .bCreateStarted = TRUE,
  },
};

Task one processing :

void AP_OS_taskOne(void * pv_iArgument)
{
  u32 u32_transmitFlag = 0;

  /* Conditions before test */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);
  /* Start HAL timer */
  HAL_TIM_Base_Start_IT(DV_BOARD_CONFIG_getTIMHandler(CF_BOARD_CONFIG_eTIM2));

  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
  /* Infinite loop */
  for(;;)
  {
    /* Wait for event */
    u32_transmitFlag = xEventGroupWaitBits( SV_OS_getEvtGrp(AP_OS_eEVT_GRP_TASK_ONE),
                                            (AP_OS_CFG_EVT_GRP_TASK_ONE_FLAG1),
                                            TRUE,
                                            FALSE,
                                            60000);

    /* Event raised */
    if(0 != (u32_transmitFlag & AP_OS_CFG_EVT_GRP_TASK_ONE_FLAG1))
    {
      HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
    }
    else
    {
      /* Time-out */
    }
  }
}

Task two processing :

void AP_OS_taskTwo(void * pv_iArgument)
{
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);

  /* Infinite loop */
  for(;;)
  {
    HAL_Delay(2);
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_10);
  }
}

Hardware timer routine (Executed each 5s) :

void AP_OS_PeriodicalActionsForTests_cbk(void)
{
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);

  triggerEventTaskONE();

  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);
}

STATIC void triggerEventTaskONE(void)
{
  BaseType_t  u32_higherPriorityTaskWoken;
  BaseType_t  u32_error;

  /* Raise flag */
  u32_higherPriorityTaskWoken = pdFALSE;
  u32_error = xEventGroupSetBitsFromISR(SV_OS_getEvtGrp(AP_OS_eEVT_GRP_TASK_ONE),
                                        AP_OS_CFG_EVT_GRP_TASK_ONE_FLAG1,
                                        &u32_higherPriorityTaskWoken);

  /* Was the message posted successfully? */
  if(pdFAIL != u32_error)
  {
      /* Request a context switch */
      portYIELD_FROM_ISR( u32_higherPriorityTaskWoken );
  }
  else { /* No need to switch context */ }
}

Don’t hesitate if you’ve got questions ^^.

Regards.

ettocap wrote on Tuesday, August 25, 2015:

Ho !
I just watch that task one is executed when its waiting timeout is reached (60000ms). So I think that problem comes from my timer interruption which sends task one event, or the context switch request (portYIELD_FROM_ISR).

rtel wrote on Tuesday, August 25, 2015:

What does SV_OS_getEvtGrp(AP_OS_eEVT_GRP_TASK_ONE) evaluate too?

Do you have configASSERT()
defined?

Regards.

ettocap wrote on Tuesday, August 25, 2015:

SV_OS_getEvtGrp() returns reference on OS event group. AP_OS_eEVT_GRP_TASK_ONE is an index to find event group address in an array.

EventGroupHandle_t SV_OS_getEvtGrp(u8 u8_iEvtGrpIdx)
{
#if (SV_OS_CFG_NB_EVT_GROUPS > 0)
  if( (u8_iEvtGrpIdx > SV_OS_CFG_NB_EVT_GROUPS) ||
      (NULL == m_apst_osEvtGrpHandleList[u8_iEvtGrpIdx]) )
  {
    return NULL;
  }
  else
  {
    return m_apst_osEvtGrpHandleList[u8_iEvtGrpIdx];
  }
#else
  return NULL;
#endif
}

I don’t have configASSERT().

xEventGroupSetBitsFromISR() returns 1 (pdPASS). u32_higherPriorityTaskWoken is set to 0 (pdFALSE).

Regards.

rtel wrote on Tuesday, August 25, 2015:

Please define configASSERT().

ettocap wrote on Tuesday, August 25, 2015:

Ok, I do that. I don’t know if it’s what you expect.

EventGroupHandle_t SV_OS_getEvtGrp(u8 u8_iEvtGrpIdx)
{
#if (SV_OS_CFG_NB_EVT_GROUPS > 0)
  if( (u8_iEvtGrpIdx > SV_OS_CFG_NB_EVT_GROUPS) ||
      (NULL == m_apst_osEvtGrpHandleList[u8_iEvtGrpIdx]) )
  {
    configASSERT(0);
    //return NULL;
  }
  else
  {
    return m_apst_osEvtGrpHandleList[u8_iEvtGrpIdx];
  }
#else
  return NULL;
#endif
}

ConfigASSERT is not reached.
I check address of event group returned by this function. It’s correct, this is the address returned by xEventGroupCreate() at initialization.

rtel wrote on Tuesday, August 25, 2015:

Have you defined configASSERT() in FreeRTOSConfig.h? Is it defined so
that the code cannot continue if the assert test fails? Like disabling
interrupts and sitting in a loop?

ettocap wrote on Tuesday, August 25, 2015:

ok !
yes, configASSERT is defined in FreeRTOSConfig.h.

#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } 

However, no assert test fails.

rtel wrote on Tuesday, August 25, 2015:

Well I’m afraid I can’t see why it is not functioning.

Are you 100% sure the interrupt is being triggered, and that the line
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3); in AP_OS_taskOne() is not
executing. If there was a problem with the HAL_GPIO_TogglePin() call
then maybe you just aren’t seeing the output - if you set a break point
on the line does it get hit?

Regards.

ettocap wrote on Tuesday, August 25, 2015:

.PNG in attached file presents the result of output measures.

Moreover, by breakpoints in debug, timer routine is well reached. In AP_OS_taskOne(), HAL_GPIO_TogglePin() call is not reached.

ettocap wrote on Tuesday, August 25, 2015:

My next test includes an OS waiting is in task Two (vTaskDelay()). From that, task One is executed.

Task2 processing :

void AP_OS_taskTwo(void * pv_iArgument)
{
  u32 u32_idxWait = 0;

  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);

  /* Infinite loop */
  for(;;)
  {
    HAL_Delay(2);
    for(u32_idxWait = 0; u32_idxWait < 2000; u32_idxWait++);
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_10);
    vTaskDelay(1);
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_10);
  }
}

You can find .PNG in attached file.

rtel wrote on Tuesday, August 25, 2015:

Could you please zip up your complete project, including the build files, and ensuring there are no absoute paths that would prevent it building on my computer, then send it to r dot barry [at] freertos dot org.

ettocap wrote on Tuesday, August 25, 2015:

Ok, I prepare the archive.

ettocap wrote on Tuesday, August 25, 2015:

I sent you the archive by email.

rtel wrote on Tuesday, August 25, 2015:

Assuming the tasks have been created as expected, and the interrupt is interrupting, I’m still struggling to see the source of the problem. I was hoping I would be able to build the project, but I don’t have the correct plug-ins.

I notice the timer task has a lower priority - that should not matter as both of your tasks have delays in them, so the timer task should always execute - but are you ever finding the call to xEventGroupSetBitsFromISR() returns false?

It would be interesting to know the state the tasks were in. You could try calling uxTaskGetSystemState() to ensure the task receiving the event is in the Blocked state as expected.

You could also try reducing the block time of the same task, say to 5 seconds, then each time it unblocks increment a counter. That would let you know the task was still healthy, and that the tick rate really is 1ms (if the counter increments at any other rate than once per 5 seconds you would know there was a timing problem).

heinbali01 wrote on Wednesday, August 26, 2015:

Can you put a break in timers.c in the task-function:

static void prvTimerTask( void *pvParameters )
{
TickType_t xNextExpireTime;
BaseType_t xListWasEmpty;

	/* Just to avoid compiler warnings. */
	( void ) pvParameters;

	for( ;; )
	{
        /* Put a break on this statement: */
		xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );

It sounds like this helping task doesn’t run

Regards.

ettocap wrote on Wednesday, August 26, 2015:

Hi,

thank you very much for your answer.

I checked xEventGroupSetBitsFromISR() returned value. It is always pdPASS.

Then, I used uxTaskGetSystemState() service to get status of tasks.
I’ve got 4 tasks. Task one priority 5, Task two priority 3, Idle task priority 0 and another task priority 2.
When task one eventBit is sent, task one stays in eBlocked status !
Other tasks are in eReady status (Task 2 is in eReady status, I find it strange. It reads task status. In my opinion, it should be in eRunning status)

I set a counter in task one to count number of unblockings. With block time sets to 5s, counter returns 12 after 1 minute. So, task one is still healthy and there is no timing problem.

I think that the problem comes from the hw timer interruption context (priority competition,…).

I test another behavior. Hw timer routine does nothing. Periodically, between 2 active waitings, task two sends event to task one. Yet, task one is unblocked. See attached .PNG file.

Regards.

rtel wrote on Wednesday, August 26, 2015:

I agree with Hein, we need to see at which point the deviation from
expected behaviour occurs, and the first thing to do is see if the timer
task is ever processing the message.

To explain: Setting an event bit is not a deterministic operation
because you don’t know how many tasks will be unblocked. As FreeRTOS
does not permit non-deterministic operations from an interrupt the ‘set
bits’ operation is deferred to the task level - and actually occurs in
the daemon task (the timer task). The function that executes in the
timer task is vEventGroupSetBitsCallback(), which is defined in
event_groups.c. Please place a break point in that function to see if
it ever gets called.

If it does get called then the message is sent to the timer task
correctly, and the timer task is processing the message, so we will have
to follow it up from there.

If it doesn’t get called then we will have to see why not.

Regards.