cancel
Showing results for 
Search instead for 
Did you mean: 

TIM2 interrupt fires twice when optimize speed is set

Mr_M_from_G
Senior

Hello,

I just test some interrupt stuff with Nucleo F439ZI and found that simply setting optimize to -Ofast leads to a timer 2 interrupt (compare event when generating a PWM) firing two times, while -O0 shows the expected behaviour. I test this with an isr that just sets and resets a GPIO pin and clears the timers flag. Also SWV shows this double execution.

Whats wrong with optimize speed?

Any help is welcome - thanks

Martin

8 REPLIES 8

It takes some time (read: there are clock-domain synchronizers in the way) until the timer's interrupt signal (i.e. it's trailing edge after being cleared from program) propagates to NVIC. If the program succeeds in exiting the ISR faster, then NVIC sees a pending interrupt and starts the ISR again.

This is a recurring question here. One way to solve it is to place the clear far enough from the end of the ISR (but you need to find out how far exactly is far enough, and that depends on the APB divider of the bus where the timer resides); other ("bulletproof" but maybe a bit slower) is to check the timer's status register/bit at the beginning of the ISR and exit when none of the relevant status bits indicate interrupt.

JW

There is a race hazard with the pipeline, write buffers and the tail chaining. ie it doesn't clear through the TIM and NVIC before the process decides the IRQ is still pending, and calls again.

You don't check the source, and you don't clear the interrupt sufficiently early.

  1. void TIM2_IRQHandler (void)
  2. {
  3. //SetPortBit (port_Testpins, pin_Timer2_isr);
  4. GPIOA->BSRR=1U<<(6);
  5. __NOP ();
  6. //ClearPortBit (port_Testpins, pin_Timer2_isr);
  7. GPIOA->BSRR=(1U<<16)<<(6);
  8. ClearBitMask (TIM2->SR, TIM_SR_CC1IF);
  9. }

How does ClearBitMask work?

For the TIM you're explicitly not supposed to do

TIM2->SR &= ~TIM_SR_CC1IF;

but rather just write the mask

TIM2->SR = ~TIM_SR_CC1IF;

this is because the TIM runs several times faster than your back-to-back accesses, and using the RMW method will have the effect of clearing interrupts you never see.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Mr_M_from_G
Senior

Hello all,

and thanks for the answers.

I wasn't aware of the delay between the clear instruction for TIM_SR_CC1F and its taking effect. Jan suggested two solutions. I tried to move clearing the iF flag to the top and also needed another nop to have isr only once. Also ending isr with

while ((TIM2->SR & TIM_SR_CC1IF) != 0);

was succesful.

I also found that DSB followed by ISB at the end of isr is successful.

Finally here is my ClearBitMask macro:

#define ClearBitMask(Var,BitMask) (Var&=~(BitMask))

So it does what Clive says is not recommended. But doing TIM2->SR = ~TIM_SR_CC1IF; will set all others bits. I suppose that writing 1 to interrupt flags does not set them. Is it a general STM32 convention that interrupt flags can be cleared this way, ie they are always in words only with other flags of that kind? Or must I check every time in ref manual?

Thanks for any answer

Martin

> I also found that DSB followed by ISB at the end of isr is successful.

Don't be fooled, this is just a symptomatic solution. These instructions don't enforce any particular behaviour of the peripherals. You might've just as well used a few NOPs.

> But doing TIM2->SR = ~TIM_SR_CC1IF; will set all others bits.

Not - but ~

(this stupid forum does not allow to change fonts nor size)

As you've already noticed, the status register's bit's won't change upon writing 1 and are marked accordingly as rc_w0.

There are several strategies how to cope with the hazard of status bits being updated - rc_w0, rc_w1, implicit clear when read (rc_r), toggle by writing 1 (t), implicit clear by hardware through a certain action on other registers, and a separate clear register, and maybe there are more. In STM32, all of them are used - examples: rc_w0 in TIM, rc_w1 in OTG_USB in higher-end STM32, rc_r in some registers of ETH, t in the USB device in lower-end STM32, implicit clear in USART_SR.RXNE (by reading USART_DR) and a more intricate implicit clear process in several other USART_SR bits, separate clear register in DMA. This is the price we pay for these chips being thrown together from IP blocks of various sources (which is a relatively cheap process, as compared to thorough design from the ground up).

> Or must I check every time in ref manual?

If you don't want to rely on derived work such as the "libraries", thorough reading of the RM is a must.

JW

>>Or must I check every time in ref manual?

It might help, it will allow you to avoid latent and unexplained failure. At the very least assume that the peripheral register do not act like memory, they should be treated as volatile, may read/write different things, and are not atomic from the processors perspective. Also an understanding of the pipelined design and write-buffers will help avoid several other traps. Things are even more complicated in CM7 designs where cache coherency must be considered.

This issue has been discussed for over a decade.

Don't use bit-banding on TIM registers, and be very cautious about all peripheral registers.

ARM doesn't not commit gates to protecting you from potential hazards so you must be more aware of them.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

> Don't use bit-banding on TIM registers,

https://st-microelectronics.jiveon.com/community/stm32-forum/blog/2018/03/18/bit-banding-is-dangerous-when-used-on-hardware-set-status-registers

(hasn't been migrated yet (?) and the attached videos are already missing)

JW

[EDIT] migraged as https://community.st.com/s/feed/0D50X00009bLSAZSA4 but videos missing

Mr_M_from_G
Senior

Hello all,

thanks for the clear explanations, good help and hints to keep in mind.

I checked the manual of F439 and looked at many status registers and even though I found all the strategies Jan described, I found no register that was in danger of setting or clearing another bit along with the desired one. So it seems no danger on this side, but I keep this in mind to check the manual every time I do this.

Martin

henry.dick
Senior II

in many of those cases, the most likely cause is operator error: without code and any means to verify what you observed to a 3rd party, there is no way to rule out operator errors.

I would say to help others help you, post a minimalist piece of your code and some means to substantiate your conclusions so others can experiment on their own.

as what you observed is so out of this world.