Hi guys:
here is my code ,it works in AC5, but when I change to AC6, it is dead in delay_1ms(), I have no idea why it happened:
struct bsl_data_t{ uint32_t tick_1ms;};static struct bsl_data_t g_bsl;
void TIMER16_IRQHandler(void){ if(SET == timer_interrupt_flag_get(TIMER16, TIMER_INT_UP)){ /* clear channel 0 interrupt bit */ timer_interrupt_flag_clear(TIMER16, TIMER_INT_UP); /* toggle selected led */ g_bsl.tick_1ms++; }}uint32_t get_timer_tick(void){ return g_bsl.tick_1ms;}
/* x1ms */#define GET_DIFF_TICK(last_tick) ( (0x100000+get_timer_tick()-last_tick)&0xFFFFF )#define SET_TICK(var1) var1=get_timer_tick()void delay_1ms(uint32_t xms){ volatile uint32_t tick,diff=xms; SET_TICK(tick); while(GET_DIFF_TICK(tick) < diff){} // dead in here, never come out in AC6.}
here some picture maybe help:
AC5:
AC6:
donot know why It removed line 311:
At the very least you're lacking a "volatile" keyword in the definition of the primary counter variable shared between the ISR and the main code.
Come on , it is a global variable, and it is inside a function ,which is called by other procedure, and that means complier definitely can not remove it. who will write a complier not considering the global variable and remove the whole function? AC5 is working fine, AC5 has the correct consideration.
Get off your high horse and try it before pontificating about why it cannot work.
Current use of volatile is completely wrong headed.
Compiler likely recognizing it can fold or inline.
struct bsl_data_t{ volatile uint32_t tick_1ms; }; static struct bsl_data_t g_bsl; void TIMER16_IRQHandler(void) { if(SET == timer_interrupt_flag_get(TIMER16, TIMER_INT_UP)){ /* clear channel 0 interrupt bit */ timer_interrupt_flag_clear(TIMER16, TIMER_INT_UP); /* toggle selected led */ g_bsl.tick_1ms++; } } uint32_t get_timer_tick(void) { return g_bsl.tick_1ms; } /* x1ms */ void delay_1ms(uint32_t diff) { uint32_t tick = get_timer_tick(); while((get_timer_tick() - tick) < diff){} }
Math overly complex on the diff, let the 32-bit unsigned number space work for you.
Thanks, it works. Now i am confused. when the variable should be added a volatile? I have so many variables in one struct.
Anything that changes outside of the normal execution flow should be volatile. You should be able to flag specific records in the structure individually, the whole structure does not need to be volatile. You'll need to make a judgement based on what you're doing with things.
Structures describing peripheral registers will often be volatile as the hardware can change state internally outside the purview of the CPU. ie reading a UART status register to see a byte was received, or a counter register in a TIMER.
Your specific use case has a tight loop checking the same memory, the compiler assumes this memory location won't be changing unless explicitly flagged that it can change. The volatile directive forces the memory to be reloaded into a register at each iteration. The directive may also change how a variable is allocated/held, ie register vs memory/stack
Marking local variables as volatile only makes sense if you've passed them as a pointer for something else to write into, but again this tends to be dangerous as the variable is temporary so may belong to some other scope by the time an IRQ or DMA operation acts upon it.
It has been used as a trick to force a loop iteration not to be folded/removed by the optimizer, but these thing tend to be non-portable, and subject to variable behaviour from compilation to compilation, or optimization settings, etc.
Appreciate for reply.
Thank you for a so patient explanation.
In my case ,what we can find out is: AC6 assumed the memory location "uint32_t tick_1ms" does not changed unless explicitly flagged. But interesting , should not it be a const or macro which define a unchange value? for example:
const uint32_t tick_1ms; #define tick_1ms 1
the complier assume it unchange, but does not know it is changed in a ISR.
Let's say "uint32_t tick_1ms" truely not change, but the complier should execute and assign a "unchange value" to a new variable:
cur = get_timer_tick();
But it should not remobe the whole line , so why it removed?
We define a lot of global variables , that is to say we will use them in many place, we may increase or decrease them in some place. If the complier cannot detect the change correctly, it should not assume the memory location not change. Right?
lanmanck said:AC6 assumed the memory location "uint32_t tick_1ms" does not changed unless explicitly flagged. But interesting , should not it be a const or macro which define a unchange value?
You really need to cut down on jumping to conclusions so violently.
No, the compiler did not expect any explicit "flagging", nor did it assume the variable would be constant. The only thing it assumes is that the value of a variable does not change all by itself, i.e. in ways not covered by the langauge itself. One of those ways is that the C code's executaion is interrupted by the CPU doing something else entirely. Which is exactly what ISRs always do.