Hello All,
Today I am going to share with you an interesting story of an intelligent programmer who almost lost the hope on C programming and basically on everything.
The programmer had to implement a delay function using 32 bit Timer peripheral of Cortex M3 microcontroller. First it looked so simple. He was ignorant of what coming next to his way.
The tool/editor provided everything, from compiler to drivers of each and every peripheral of micro controller. It required just a foolish programmer to add A and B to get things done.
The tool also provided JTAG access to microcontroller to run the programs in Debug mode where user can add break points or user can program the inbuilt flash of microcontroller with final code, which is called Release mode. The microcontroller has an inbuilt SRAM where all the code runs. On power up, code from flash is shadowed to SRAM by the microcontroller. Don't ask me how.
Moving on to timer story. Initialize it with periodic or one shot mode. Load the count, enable the interrupt and enable the timer. It will begin to count down to zero and interrupts controller. If it is in periodic mode, it will reload itself and starts counting down to provide periodic interrupts.
Below is the implementation of delay function and how it was tested.
/****************************************************************************/
#define TIMER_DELAY_STOPPED (0U)
#define TIMER_DELAY_STARTED (1U)
#define TIMER_DELAY_ACHIEVED (2U)
UINT32 gu32TimerDelay = TIMER_DELAY_STARTED;
void timer1Init (void)
{
/*--------------------------------------------------------------------------
* Ensure the CMSIS-PAL provided g_FrequencyPCLK0 global variable contains
* the correct frequency of the APB bus connecting the MSS timer to the
* rest of the system.
*/
SystemCoreClockUpdate();
MSS_TIM1_init( MSS_TIMER_ONE_SHOT_MODE );
}
/* Delay function implemented by programmer */
void sDelay(UINT32 u32Sec)
{
UINT32 u32TimerLoadVal;
UINT32 u32TimerResolution;
/* For a 100Mhz system, this global variable will hold 100M as count which is equal to one second */
u32TimerResolution = (g_FrequencyPCLK0);
u32TimerLoadVal = (u32TimerResolution * u32Sec);
MSS_TIM1_load_immediate( u32TimerLoadVal );
gu32TimerDelay = TIMER_DELAY_STARTED;
MSS_TIM1_start();
MSS_TIM1_enable_irq();
while (gu32TimerDelay != TIMER_DELAY_ACHIEVED)
{
/* In tight loop till timer interrupts and sets the flag */
;
}
gu32TimerDelay = TIMER_DELAY_STOPPED;
MSS_TIM1_stop();
}
/* Linkage for Timer ISR function provided by compiler. User can add his logic inside this ISR.
__attribute__((__interrupt__)) void Timer1_IRQHandler( void )
{
gu32TimerDelay = TIMER_DELAY_ACHIEVED;
MSS_TIM1_clear_irq();
}
void main (void)
{
UINT32 count = 0;
while (1)
{
sDelay(2);
count = count + 2;
printf("\nTime elapsed in sec: %d", count);
}
}
Output:
Time elapsed in sec: 2
Time elapsed in sec: 4
Time elapsed in sec: 6
Time elapsed in sec: 8
.
.
.
/*****************************************************************************/
So the programmer wrote the above functions and tested it in Debug mode and it worked in one shot. He patted his own back and started using delay function everywhere in his final code and flashed it to microcontroller in Release mode.. Voila!!!! There lies the mystery. Not even one single print on the console after the first delay function was called.
To make sure the interrupt is working, programmer added print in ISR. Yes!! The print showed up on console. But no other prints after that. He added prints everywhere. Only prints till the timer interrupt ended, where showing up on the console. None after. Then commenced the long hours of debugging the issue and listening to various so called other intelligent programmers' suggestions.
One of the gossip was how the tool, in debug mode, adds delay to the execution of the code and in release mode, where there is no JTAG interaction with the microcontroller, causing the delay function to fail. Few morons agreed to this. Few experienced fellows said it was not the issue.
So our hero tried many foolish stuffs to debug the issue. Did he succeed? Was there a foolish mistake? Or was there a secret villain who added the mystery spice into the code?
To be continued.... (Only if any positive response to the story is observed.)
Today I am going to share with you an interesting story of an intelligent programmer who almost lost the hope on C programming and basically on everything.
The programmer had to implement a delay function using 32 bit Timer peripheral of Cortex M3 microcontroller. First it looked so simple. He was ignorant of what coming next to his way.
The tool/editor provided everything, from compiler to drivers of each and every peripheral of micro controller. It required just a foolish programmer to add A and B to get things done.
The tool also provided JTAG access to microcontroller to run the programs in Debug mode where user can add break points or user can program the inbuilt flash of microcontroller with final code, which is called Release mode. The microcontroller has an inbuilt SRAM where all the code runs. On power up, code from flash is shadowed to SRAM by the microcontroller. Don't ask me how.
Moving on to timer story. Initialize it with periodic or one shot mode. Load the count, enable the interrupt and enable the timer. It will begin to count down to zero and interrupts controller. If it is in periodic mode, it will reload itself and starts counting down to provide periodic interrupts.
Below is the implementation of delay function and how it was tested.
/****************************************************************************/
#define TIMER_DELAY_STOPPED (0U)
#define TIMER_DELAY_STARTED (1U)
#define TIMER_DELAY_ACHIEVED (2U)
UINT32 gu32TimerDelay = TIMER_DELAY_STARTED;
void timer1Init (void)
{
/*--------------------------------------------------------------------------
* Ensure the CMSIS-PAL provided g_FrequencyPCLK0 global variable contains
* the correct frequency of the APB bus connecting the MSS timer to the
* rest of the system.
*/
SystemCoreClockUpdate();
MSS_TIM1_init( MSS_TIMER_ONE_SHOT_MODE );
}
/* Delay function implemented by programmer */
void sDelay(UINT32 u32Sec)
{
UINT32 u32TimerLoadVal;
UINT32 u32TimerResolution;
/* For a 100Mhz system, this global variable will hold 100M as count which is equal to one second */
u32TimerResolution = (g_FrequencyPCLK0);
u32TimerLoadVal = (u32TimerResolution * u32Sec);
MSS_TIM1_load_immediate( u32TimerLoadVal );
gu32TimerDelay = TIMER_DELAY_STARTED;
MSS_TIM1_start();
MSS_TIM1_enable_irq();
while (gu32TimerDelay != TIMER_DELAY_ACHIEVED)
{
/* In tight loop till timer interrupts and sets the flag */
;
}
gu32TimerDelay = TIMER_DELAY_STOPPED;
MSS_TIM1_stop();
}
/* Linkage for Timer ISR function provided by compiler. User can add his logic inside this ISR.
__attribute__((__interrupt__)) void Timer1_IRQHandler( void )
{
gu32TimerDelay = TIMER_DELAY_ACHIEVED;
MSS_TIM1_clear_irq();
}
void main (void)
{
UINT32 count = 0;
while (1)
{
sDelay(2);
count = count + 2;
printf("\nTime elapsed in sec: %d", count);
}
}
Output:
Time elapsed in sec: 2
Time elapsed in sec: 4
Time elapsed in sec: 6
Time elapsed in sec: 8
.
.
.
/*****************************************************************************/
So the programmer wrote the above functions and tested it in Debug mode and it worked in one shot. He patted his own back and started using delay function everywhere in his final code and flashed it to microcontroller in Release mode.. Voila!!!! There lies the mystery. Not even one single print on the console after the first delay function was called.
To make sure the interrupt is working, programmer added print in ISR. Yes!! The print showed up on console. But no other prints after that. He added prints everywhere. Only prints till the timer interrupt ended, where showing up on the console. None after. Then commenced the long hours of debugging the issue and listening to various so called other intelligent programmers' suggestions.
One of the gossip was how the tool, in debug mode, adds delay to the execution of the code and in release mode, where there is no JTAG interaction with the microcontroller, causing the delay function to fail. Few morons agreed to this. Few experienced fellows said it was not the issue.
So our hero tried many foolish stuffs to debug the issue. Did he succeed? Was there a foolish mistake? Or was there a secret villain who added the mystery spice into the code?
To be continued.... (Only if any positive response to the story is observed.)
I wonder what this MSS_TIM1_init( MSS_TIMER_ONE_SHOT_MODE ) does.
ReplyDeleteSeems like some magic is going on in this part. Did this genius of a programmer try reinitializing timer on the isr.
PS. From a fellow who has not coded for months this may be a silly thing to suggest. Please bear with him.
This comment has been removed by the author.
ReplyDeleteMSS_TIM1_init() is fine. Its basically an inline function which programs the system registers of timer peripheral user chosen mode. And the ISR too is fine. I hope you give some more look into the body of the delay function.
ReplyDeleteCorrect me if I'm wrong, which is more likely.
ReplyDeleteBut shouldn't you enable the interrupts before you start the timer?
And do you need to enable the interrupt every time maybe its getting cleared in the stop timer function. Anyway this is what I could think of so far. Let me know if I still have hope of working near computers.
AK:
ReplyDeleteEnabling the interrupt before starting timer is a good thought. But I guess it doesn't matter unless you supply a Ghz clock to timer and and MHz clock to your microcontroller or you choose the lowest possible resolution (here, 10ns for 100MHz of clock (count = 1)) to get an interrupt. The resolution programmer chose is one second (count = 100M). So it takes 100M clock cycles to get an interrupt and either of the timer start function or interrupt enable function are non blocking (a simple memory right).
And calling interrupt enable function every time, seems redundant. But its just the over compensation the programmer chose, just to be on the safer side. So here are the explanations of most of the driver functions used above.
Timer init: Set the bit according to user chosen mode in the timer control register.
Timer load count: Load the 32 to bit count to timer counter register.
Timer start: Set the timer count down enable bit in control register.
Timer interrupt enable: Enable the interrupt for timer in control register and as well as enable the timer interrupt detection on the interrupt controller (called as NVIC) which detects and passes the peripheral interrupts to microcontroller.
Timer clear irq: Clear the pending interrupt in the timer interrupt status as well as clear the pending interrupt on the interrupt controller (NVIC)
Timer stop: Reset the timer count down enable bit in control register.
VK:
Yes u are right. timer1init() should be called before calling any other timer functions. But it is just an one time call. So it is called in usual peripheral init sections. Not required to be called in delay() function every time.
Guys,
Let me tell all the timer related driver functions were working fine. So just shift your views to rest of the delay() function. Actually I was hoping for one main suggestion, at least from the people who are preparing interviews. Lets see.. Take your time. And remember programmer had no trouble in running the code in debug code using JTAG....
Two words "Compiler optimization"
ReplyDeleteIn the debug mode its most likely that the tight while loop was not optimized.
Whereas in case of the release mode the compiler tried to be intelligent not aware that the programmer had already done intelligent stuff in the code. So the compiler just removed your entire while loop without asking the intelligent programmer leaving him and other scratching assorted body parts until they became sore... :D
The intelligent programmer must have declared the gu32TimerDelay flag as a volatile variable as its value can be changed by the ISR. If the compiler had knew this then it would have left the while loop alone and a few people could have had peaceful nights...
Finally the answer I was looking for. But the compiler was very much intelligent even for the volatile definition. It just ignored the volatile word as people ignore the word engineer associated with us. And it did not remove the while loop. If that was the case, at least the prints from the functions after delay function should have showed up on the console. Instead it just converted the tight loop into tighter while(1) loop and made the programmer's life miserable. So to summarize, debug mode, compiler flag was O0 and for release mode, the flag was set to O2.
ReplyDeleteNow the programmer is wondering how to answer for the definition of volatile, if asked in his next interview.
Sorry my bad your right it became a much tighter loop like the ones we used in college days at the end of the programs in microcontrollers to do nothing we used to put a while(1);
ReplyDeleteI wonder why volatile didn't work. But I'm not going to scratch myself for that I'll be happy with the "Its compiler dependent" answer... :P
So how did the intelligent programmer fix the issue?
Did he add a asm(NOP); in the while loop? Not sure if it would help with the intelligent compiler interfering.
Or did he just change the optimization flag while compiling in the release mode?
Anyway I wont take credit for figuring this one because I had a lot of help from you stressing that the problem is only in the delay function. Because of that I just spent 10 minutes googling to find the problem.
But I can imagine what a week it must have been for the intelligent engineer to figure this one out on his own... (bow)