STM32 occasionally misses interrupts

rousea wrote on Monday, September 07, 2015:

I have an STM32 system that works correctly most of the time but occasionally fails to service interrupts. I wonder if there is something I am missing in my understanding of the NVIC:

The system is built around FreeRTOS and uses, among other things, three USART’s and a real time clock. The RTC produces a one second tick on GPIO input E1 which is configured to generate the EXTI1 interrupt. I have proved that the system sometimes misses this interrupt, and I suspect that it sometimes also misses USART interrupts:

The EXTI1 interrupt service routine simply gives an RTOS semaphore:
**
xSemaphoreGiveFromISR(System.Semaphore.SecondTick, &xHigherPriorityTaskWoken);
**
One of the FreeRTOS tasks waits for this semaphore, but times out if it is not received within 1.02 seconds:

**if (xSemaphoreTake(System.Semaphore.SecondTick, 1020) == pdFAIL){
.
.
} else {
.
.
}
**
This should obviously never fail, but occasionally it does.

If I run the system through a debugger (OpenOCD) and pause it at random times I often see that the EXTIPR and the NVIC ISPR registers both indicate that the EXTI1 interrupt is pending. I would expect any pending interrupt to be handled immediately and cleared by the ISR. The only reason I can imagine why this is not happening is if the interrupt is disabled.

As far as I can see the only mechanism for disabling interrupts is to set BASEPRI to a value numerically lower than the priority of the interrupt. SCB AIRCR PRIGROUP is set to 3 (i.e. 16 group priority levels, no sub-priorities) and the NVIC IPR for EXTI is set to 0x50. In FreeRTOSconfig.h the value for configMAX_SYSCALL_INTERRUPT_PRIORITY is set to 0x30.

According to my understanding FreeRTOS sets BASEPRI to configMAX_SYSCALL_INTERRUPT_PRIORITY when it is executing a task that can’t be interrupted and 0 at all other times. I don’t believe that I can monitor BASEPRI directly. However, I think FreeRTOS copies it to pxCurrentTCB->uxBasePriority, and whenever I pause the debugger this reads 0.

Can anyone give any suggestions why the interrupts are failing?

rtel wrote on Monday, September 07, 2015:

If configMAX_SYSCALL_INTERRUPT_PRIORITY is 0x30, and the interrupt’s priority is 0x50, then the interrupt will be masked while you are inside a critical section, but BASEPRI should enable all interrupts while you are outside of a critical section (as you say, it sets BASEPRI to 0, which enables all interrupts). What is BASEPRI actually set to when the problem occurs? You should be able to read it using OpenOCD - and it is not copied to pxCurrentTCB->uxBasePriority. uxBasePriority holds a task priority, which is not related to an interrupt priority.

For the interrupt to execute five things must be done - the peripheral must be configured to generate interrupts, the interrupt must be enabled in the NVIC, the BASEPRI register must be set to allow the interrupt’s priority, the priority group must be set as you expect, and interrupts must be globally enabled in the CPU itself - did you check all four things? Maybe one of them got disabled and not re-enabled along the way.

Are you calling any FreeRTOS API functions from a critical section, or when the scheduler is suspended - or are you calling any non-interrupt safe API functions from an interrupt?

As you are using an STM32, did you call NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ) anywhere, or did you set the PRIGROUP register yourself? These things can get quite confusing.

Are you using STM32 Cube, or have you created your own drivers?

Do you have configASSERT() defined - if not, please define it as it may highlight the problem.

As an aside, consider using a direct to task notification in place of the semaphore - it is faster and uses less RAM.

Regards.

rousea wrote on Monday, September 07, 2015:

Thanks for your prompt response. I added a call to get_BASEPRI() within the fail code above (to copy BASEPRI to a variable) followed by a breakpoint. When this breakpoint was hit I checked various registers to follow your points:

  1. EXT>IMR = 0xFF22, showing that EXTI1 interrupt is enabled. Furthermore EXTI>PR = 0x02, showing that EXTI1 has a pending interrupt

  2. NVIC>PR byte 7 = 0x50, showing that EXTI1 is enabled at priority level 0x50. NVIC>ISER = 0xE000E100, showing that EXTI1 interrupt is enabled. NVIC ISPR = 0xE000E200, showing that EXTI1 interrupt is pending.

  3. BASEPRI = 0

  4. SCB>AIRCR = 0xFA050300, showing that PRIGROUP = 3 (i.e. 16 group levels, no sub-groups)

  5. I may be missing something but I can’t find any way to globally enable/disable interrupts. The nearest I can find is SCB>ICSR, which reads:

    ICSR {…}
    Word 0xc35000
    {…}
    VECTACTIVEL 0x0
    VECTACTIVEH 0x0
    RETTOBASE 0x0
    VECTPENDING_0_3 0x5
    VECTPENDING_4_7 0x3
    ISRPENDING 0x1
    ISRPREEMPT 0x1
    PENDSTCLR 0x0
    PENDSTSET 0x0
    PENDSVCLR 0x0
    PENDSVSET 0x0
    NMIPENDSET 0x0

This confirms that there is a pending ISR but confuses me by saying the vector is 0x35 = dec 53 = USART5. I am not using USART5!

I checked through all the ISR’s and none of them call non interrupt safe API’s.

I am not impressed by the drivers in STM32 Cube. They are not intuitive, are not flexible and don’t allow register contents to be easily analysed. I have therefore created my own drivers, hence the report shown above for SCB>ICSR. It is not therefore practical for me to use ConfigASSERT().

I am currently using FreeRTOS 8.1.2. RTOS task notifications weren’t introduced until V8.2.0. When I resolve this problem I will consider upgrading.

I am therefore no further forward

rtel wrote on Monday, September 07, 2015:

The CPSR register in the Cortex-M core itself (not the NVIC) has an ‘i’
bit that is used for globally enabling and disabling interrupts. This
is not a ‘mask’, in that it doesn’t just disable some priorities, it
just bluntly disables all priorities.

However, it sounds like your issue might be due to the unexpected vector
address. Is that correct?

rousea wrote on Tuesday, September 08, 2015:

The vector shown in the ICSR register is baffling, because it points to the USART5 interrupt, which is not enabled or configured in any way, so I can’t explain it. I am therefore assuming it is a “red herring”. I am still left with the problem that there is a pending interrupt, but no ISR is invoked to handle it.

I can’t find any reference to a CPSR register. The Eclipse/OpenOCD combination I use for development can display the processor registers, including the xPSR register. The STM32 documentation (PM0056 Programming Manual) says this combines the APSR, IPSR and EPSR but none of these has an interrupt enable flag.

Although I can’t prove conclusively that the system is missing USART interrupts, I can prove it misses the EXTI1 interrupt (which is hard-wired through GPIO E1 to an RTC 1 second clock pulse). The problem seems to only occur when unsynchronised data is being received on one or more USARTs. I am exploring the possibility that if a USART and an EXTI interrupt occur simultaneously one is ignored.

Do you think there is any benefit in upgrading to FreeRTOS 8.2.0 regarding this problem?

rousea wrote on Tuesday, September 08, 2015:

I have found where the ICSR Vector Pending address is set to 0x35: It is when FreeRTOS executes vPortYield():

void vPortYield( void )
{
/* Set a PendSV to request a context switch. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;

/* Barriers are normally not required but do ensure the code is completely
within the specified behaviour for the architecture. */
__asm volatile( "dsb" );
__asm volatile( "isb" );

}

portNVIC_INT_CTRL_REG points to the SCB ICSR register. Writing portNVIC_PENDSVSET_BIT to it sets the PENDSVSET bit and simultaneously changes VECTPENDING to 0x35.

rtel wrote on Tuesday, September 08, 2015:

My apologies sending you down the wrong path looking for an i bit - I
was getting my ARM architectures muddled up (I use a lot of different
ones). I believe The equivalent on the Cortex-M is the PRIMASK register.

I would not think updating the version would make a difference to the issue.

I know you said it was not practical to defined configASSERT() - but I
can’t remember why. It may still provide the answer.

Take care with the vector numbering as it might be that the 0 vector is
taken as the first user definable vector, rather than the first vector
in the table (the first few vectors being used for the system interrupts
such as PendSV).

Regards.

alainm3 wrote on Friday, September 18, 2015:

Hi, I am following this issue, it could hurt me too…

Has this been identified and fixed? How?

Thanks,
Alain
(Yes I did read the sourceforge discussion and ther is nothing there)

Em 08-09-2015 08:28, Alan escreveu:

I have found where the ICSR Vector Pending address is set to 0x35: It
is when FreeRTOS executes vPortYield():

void vPortYield( void )
{
//Set a PendSV to request a context switch. //
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;

/* Barriers are normally not required but do ensure the code is completely
within the specified behaviour for the architecture. */
__asm volatile( “dsb” );
__asm volatile( “isb” );

}

portNVIC_INT_CTRL_REG points to the SCB ICSR register. Writing
portNVIC_PENDSVSET_BIT to it sets the PENDSVSET bit and simultaneously
changes VECTPENDING to 0x35.


STM32 occasionally misses interrupts
https://sourceforge.net/p/freertos/discussion/382005/thread/9900d264/?limit=25#fe23


Sent from sourceforge.net because you indicated interest in
https://sourceforge.net/p/freertos/discussion/382005/

To unsubscribe from further messages, please visit
https://sourceforge.net/auth/subscriptions/

rtel wrote on Monday, September 21, 2015:

Hi, I am following this issue, it could hurt me too…

Has this been identified and fixed? How?

I didn’t realise there was anything that needed fixing - the setting the
PendSV bit makes an interrupt pending, that is its intended behaviour.

If you think there is something wrong please start another thread that
explains what.

Regards.