cancel
Showing results for 
Search instead for 
Did you mean: 

Dealing with compiler problems (over-optimizing to nonsense)

Linas L
Senior II

Hello.

Very frequently I end up chasing problems that not suppose to be in first place.

Here is example ( making 32b counter of 16b by counting overflows)

volatile void Delay_ms(uint32_t ms) // delay in ms
{
  time = 0;
   time_start = LPTIM2->CNT;
   overflow =  TIM2_IRQ;
  while(time<(ms*33))
  {
    over = TIM2_IRQ-overflow;
    over = over * 32768;
    time = over+LPTIM2->CNT-time_start;
  }
}

What is strange that all variabes are global volatile 32b unsigned integeres. And variable over just does not exist after compilation, so my code will hang if I ask more than 65535 counter tiks. (TIM2_IRQ is incremented by 1 inside reload interrupt elswhere each second)

Setting optimizing to zero solve this problem, but variable over is used and should not be optimized !

How do you deal with problem like this ?

(IAR ARM)

12 REPLIES 12

Should be able to do this a lot simpler for delays less than a minute (1ms tick)..

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

Yes, I spend way to long debugging this simple code, will use second LPTIM with prescaler just to do delay_ms function. Since other counter is needed for wake up and other time related work.

TDK
Guru

This is a coding problem, not a compiler problem. You need a proper delay_ms function.

Also, "volatile void" is not what you want here:

https://stackoverflow.com/questions/14288603/what-does-or-did-volatile-void-function-do#:~:text=According%20to%20the%20gcc%20documentation,that%20the%20function%20never%20returns.

If you feel a post has answered your question, please click "Accept as Solution".

Which STM32?

Show all variables definition (copy/paste, don't edit). Or, better, post minimal but complete compilable example exhibiting the problem.

Show disasm (best mixed with src).

> will hang if I ask more than 65535 counter ticks

How do you ask that? What is "counter tick"?

JW

OK, so maybe someone can fix/show problem with my code and i can run it 🙂

I am using STM32L552 Nucleo board with internal LSI enabled, and clock selected for LPTIM2 with no prescaler. I am getting overflow interrupt each second since it is counting to 32768. This parks woks well.

uint32_t LPTIM2_IRQ  = 0;
 
void LPTIM2_IRQHandler(void)
{
  if(LL_LPTIM_IsActiveFlag_ARRM(LPTIM2) == 1)
  {
    LPTIM2_IRQ++;
    LL_LPTIM_ClearFLAG_ARRM(LPTIM2); 
  }
}

This part works great ! I can count time in seconds no problem.

Now in Delay_ms is simply track what state is LPTIM2_IRQ variable, and if it overflow, I add to the time 32768. I am also subtracting current timer value at the beginning and adding timer value when checking elapsing time. This should work ( or maybe i am missing something ? )

uint32_t LPTIM2_IRQ  = 0;
uint32_t time = 0;
uint32_t time_start = 0;
uint32_t overflow =0;
uint32_t over = 0;
 
uint32_t  Delay_ms(uint32_t ms) // delay in ms
{
  uint32_t time_delay =0;
  
  time_start = LPTIM2->CNT;
  overflow   = LPTIM2_IRQ;
  
  while(time_delay<ms*33)
  {
    time_delay = ((LPTIM2_IRQ - overflow) * 32768) - time_start+LPTIM2->CNT;
    time = time_delay; // Tack in debugger what it does
  }
  return time_delay;
}

So whats wrong with my program ? It does work up to the point where i need to track multiple overflows, but below that it kinda works, with some glithes. But with high optimization, it just brakes down...

Goal is to use as little hardware as possible, since i am counting each uA in my project. But if i can't do simple thing like that i will use TIM2/5 with 32b register and reset to zero to track my time and don't have to deal with all of this math.

If where is another way to have precise delay i would love to hear it ( power optimized and easy to use) . IF this project goes well in the end i will have to go to sleep instead of doing that to save energy.

LPTIM2_IRQ needs to be qualified as volatile.

Extending timers is always a pain, because of atomicity issues. The common solution is to read twice, compare and repeat.

JW

Endup using TIM2 with 32b sized counter. It gives me 42s in with 100MHz clock, and I don't need more. If i do, just scale clock and that is it.

Changing system clock only need to update systemCoreClock variable to correct value and timer stays precise to us.

extern uint32_t SystemCoreClock;
 
void Delay_ms(uint32_t ms)
{
  uint32_t F = SystemCoreClock / 1000;
  TIM2->CNT = 0;
  while(TIM2->CNT<ms*F)
  {
  }
}

Trying to set it as volatile, did not help, nothing helped. Guess I need to lear how to write programs

As I've said,

> Extending timers is always a pain, because of atomicity issues.

  uint32_t GetExtendedLpTime(void) {
    uint32_t cnt1, ovf, cnt2;
    
    do {
      cnt1 = cnt2;
      ovf = LPTIM2_IRQ;
      cnt2 = LPTIM2->CNT;  
    } while(cnt2 < cnt1);  // repeat if overflow happened
                           // LPTIM is upcounter only
    return (ovf * 32768 + cnt2);                       
  }

JW

Linas L
Senior II

This is latest example that i can't fix without using global volatile variable. cleaning u8 array by using 32b access to get 4x performance (with union)

  uint32_t i = 0;
  uint32_t pointer = 0;
  pointer = (BUFFER_SIZE_SARA- *(__IO uint32_t *)0x4002000C )/4;
  while(i<pointer)
  {
    SARA_DATA.u32[i]=0;
    i++;
  }

pointer is always 1024 (buffer size is 4096) and memory location is DMA transfer count, so i don't have to clean all aray, just data DMA send to this array.

But by reading first to volatile global variable, it works as it should. But I don't want to keep global variables just to go around compiler or my programming quirks