STM32 HAL Tick Interrupt priority

m-wagner wrote on Wednesday, March 30, 2016:

Hi @all,

I’ve come across a problem when using STM32 HAL library in combination with FreeRTOS. I think my problem is quite basic, but I didn’t find any explanation or solution on the web.
I’ve read the Cortex M Interrupt explanation page (http://www.freertos.org/RTOS-Cortex-M3-M4.html), but this also doesn’t help in my case.

I’m using the ARM_CM4F Port for my STM32F303 Cortex M4F device, FreeRTOS8.2.3 and ST HAL Lib V1.4 for the F3 series. I’ve used the FreeRTOSConfig.h from the STM32F407 example and the one that comes with the HAL package as a starting point (they are the same except for some details). I’ve set up the NVIC to have 4 bit priorities and no subpriorities, as recommendet in the docs.

The problem I’m having is that errorhandling within HAL lib interrupt functions doesn’t work in combination with FreeRTOS.
The HAL lib uses the tick interrupt for FreeRTOS as well as it’s own tick, which is then used to detect internal timeouts. This of course only works if the tick interrupt has a higher priority than the active interrupt.

The default configuration of FreeRTOS is to set the tick interrupt priority to the lowest available priority (configKERNEL_INTERRUPT_PRIORITY). It even states that multiple times in the source code port.c ( xPortStartScheduler - /* Make PendSV and SysTick the lowest priority interrupts. / ; xPortSysTickHandler - / The SysTick runs at the lowest interrupt priority, so when this interrupt executes all interrupts must be unmasked. There is therefore no need to save and then restore the interrupt mask value as its value is already known. */

When I increase the tick priority, errorhandling works as expected. But is this a save way to go?

Thanks for your help!

rtel wrote on Wednesday, March 30, 2016:

This has been raised a few times before.

First the FreeRTOS tick and PendSV handlers must have the lowest
possible interrupt priority because that assumption is made when the
tick handler enters and exits a critical section - rather than setting
the basepri register back to whatever value it had when it exits a
critical section it assumes basepri was clear and just sets it to zero.
If the SysTick is running at the lowest priority then this assumption
is safe, as the SysTick would only execute in the first place if basepri
was clear (any other value would mask the interrupt out).

Now you can edit the FreeRTOS source code so the SysTick saves and
restores the basepri register rather than just blindly setting it and
then clearing it back to zero - that will come with the overhead of more
asm instructions and more memory accesses on every tick interrupt though.

However, I don’t understand your explanation of why the HAL tick needs
to be at the highest priority. You say it is checking for timeouts that
won’t occur if the HAL tick is not the highest priority - does that
imply the SysTick is checking for timeouts in other interrupt service
routines. If so, that would imply other interrupt service routines were
somehow busy waiting, which is not good in an ISR, and not needed if you
are using an OS anyway.

Regards.

m-wagner wrote on Thursday, March 31, 2016:

Thank you for your answer!

HAL tick needs to be at the highest priority

not necessarily the highest priority. Only higher than interrupts calling the HAL library.

does that imply the SysTick is checking for timeouts in other interrupt service routines

no, the SysTick itself is not checking for timeouts, but it’s value is used for this purpose.

other interrupt service routines were somehow busy waiting, which is not good in an ISR

yes, that’s the case. Timeout values are hardcoded in HAL lib and are in the range of 10…100ms. I don’t think that I can get rid of those busy-waiting parts without writing my own drivers…

I came across this after adding the HAL I2C DMA driver into my project. I wanted to see if communication errors are reported correctly and simulated an error by shorting the I2C wires. This caused my program to infinitely loop in one of some routines like this:

static HAL_StatusTypeDef I2C_WaitOnSTOPFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout)
{  
  uint32_t tickstart = 0x00;
->tickstart = HAL_GetTick();
  
->while(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_STOPF) == RESET)
  {
    /* Check if a NACK is detected */
    if(I2C_IsAcknowledgeFailed(hi2c, Timeout) != HAL_OK)
    {
      return HAL_ERROR;
    }
		
    /* Check for the Timeout */
--->if((Timeout == 0) || ((HAL_GetTick()-tickstart) > Timeout))
    {
      hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
      hi2c->State= HAL_I2C_STATE_READY;

      /* Process Unlocked */
      __HAL_UNLOCK(hi2c);

      return HAL_TIMEOUT;
    }
  }
  return HAL_OK;
}

Without calling the corresponding HAL_IncTick() function, the loop never exits. This construct exists many time within the HAL lib source code. However, I don’t know how many of those functions are called within an interrupt context.

Do you think it’s a good solution to have separate timers for HAL and FreeRTOS tick function, with different interrupt priorities?

davedoors wrote on Thursday, March 31, 2016:

—>if((Timeout == 0) || ((HAL_GetTick()-tickstart) > Timeout))

If this is meant to be called in an interrupt then this is very very bad. Your system is not going to be real time if interrupts loop for 10s of ms. I doubt the function this line is in is meant to execute in an interrupt.

m-wagner wrote on Thursday, March 31, 2016:

This is called deeply inside the HAL_DMA_IRQHandler() function multiple times. So I think it’s save to say that I’m using it correctly.
As this is code from ST, I can’t tell you if interrupt execution time was in their mind. It’s in the drivers many times, the only real solution would be to write my own drivers, which I would prefer not to do…

rtel wrote on Thursday, March 31, 2016:

Thinking about this more, perhaps the SysTick can run at a higher
priority if it is being called by the ST SysTick interrupt handler,
rather than the FreeRTOS SysTick interrupt handler.

The FreeRTOS SysTick handler is as follows:

void xPortSysTickHandler( void )
{
     /* The SysTick runs at the lowest interrupt priority, so when this
     interrupt executes all interrupts must be unmasked.  There is 
     therefore no need to save and then restore the interrupt mask value 
     as its value is already known. */
     portDISABLE_INTERRUPTS();
     {
         /* Increment the RTOS tick. */
         if( xTaskIncrementTick() != pdFALSE )
         {
             /* A context switch is required.  Context switching is 
             performed in the PendSV interrupt.  Pend the PendSV interrupt. */
             portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
         }
     }
     portENABLE_INTERRUPTS();
}

xPortSysTickHandler() is a port specific handler that creates a critical
section, then calls the generic xTaskIncrementTick() C function
(xTaskIncrementTick() is used by all FreeRTOS ports). As can be seen in
the code comments, it is the port specific part that assumes it is
running at the lowest priority. However, the ST code has its own
SysTick handler, so that port specific part will [presumably] not be
executing anyway. If the ST SysTick handler just calls
xTaskIncrementTick() then it might be ok. Also, if the SysTick handler
was running at the priority defined by
configMAX_SYSCALL_INTERRUPT_PRIORITY then the critical section would not
be needed.

Can you please post the code for the SysTick handler you are using.

m-wagner wrote on Thursday, March 31, 2016:

No problem

SysTick ISR

void SysTick_Handler(void)
{
  HAL_IncTick();
  if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
    xPortSysTickHandler();
  }
}

HAL_IncTick()

__weak void HAL_IncTick(void)
{
  uwTick++;
}

xPortSysTickHandler() (gcc CM4F Port)

void xPortSysTickHandler( void )
{
	/* The SysTick runs at the lowest interrupt priority, so when this interrupt
	executes all interrupts must be unmasked.  There is therefore no need to
	save and then restore the interrupt mask value as its value is already
	known. */
	( void ) portSET_INTERRUPT_MASK_FROM_ISR();
	{
		/* Increment the RTOS tick. */
		if( xTaskIncrementTick() != pdFALSE )
		{
			/* A context switch is required.  Context switching is performed in
			the PendSV interrupt.  Pend the PendSV interrupt. */
			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 );
}

rtel wrote on Thursday, March 31, 2016:

So the SysTick_Handler() is calling the full FreeRTOS
xPortSysTickHandler(), rather than just xTaskIncrementTick(). However,
[thoughts evolving] I think this is still ok as the Cortex-M NVIC is an
intelligent thing and even though portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 )
is used (blindly setting to 0 rather than the previous value) the NVIC
won’t internally set the basepri below that of a pending
interrupt…so I think we have come a long way around in answering
your original question: yes, in fact it is probably fine after all at
the higher priority (although busy waiting in an ISR is still not a good
plan, it should not cause a crash).

m-wagner wrote on Monday, April 04, 2016:

Thank you for your answer!

Do you have a reference for this behaviour? I can’t find anyting useful in the Cortex processor manual…

m-wagner wrote on Wednesday, October 19, 2016:

In the end, we decided to drop ST drivers.
They use blocking wait inside interrupts (which with I could live, as they are just wait until hardware module confirmes some action)
They no not do proper error handling (at least in the version I tried in april '16). Try sending two bytes to the UART module while the driver isn’t in rx mode -> Hardware RX OV, but driver doesn’t detect that. And that’s just -one- example.