Issue
There was a lot said and written about volatile variables, and their use. In those articles, two slightly different ideas can be found:
1 - Volatile should be used when variable is getting changed outside of the compiled program.
2 - Volatile should be used when variable is getting changed outside the normal flow of the function.
First statement limits volatile usage to memory-mapped registers etc. and multi-threaded stuff, but the second one actually adds interrupts into the scope.
This article (http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword) for example explicitly states that volatile modifier should be used for globals changed during the interrupt, and provides this example:
int etx_rcvd = FALSE;
void main()
{
...
while (!ext_rcvd)
{
// Wait
}
...
}
interrupt void rx_isr(void)
{
...
if (ETX == rx_char)
{
etx_rcvd = TRUE;
}
...
}
Note how setting up a rx_isr() as a callback is conveniently omitted here. Hence I wrote my own example:
#include <stdio.h>
#include <time.h>
#include <signal.h>
void f();
int n = 0;
void main()
{
signal(2,f);
time_t tLastCalled = 0;
printf("Entering the loop\n");
while (n == 0)
{
if (time(NULL) - tLastCalled > 1)
{
printf("Still here...\n");
tLastCalled = time(NULL);
}
}
printf ("Done\n");
}
void f()
{
n = 1;
}
Compiled with gcc on linux with various levels of optimizations, every time loop exited and I saw "Done" when I pressed ctrl+c, which means that really gcc compiler is smart enough not to optimize variable n here.
That said, my question is:
If compiler can really optimize global variables modified by an interrupt service routine, then:
1. Why it has a right of optimizing a global variable in the first place when it can possibly be called from another file?
2. Why the example article and many others on the internet state that the compiler will not "notice" the interrupt callback function?
3. How do I modify my code to accomplish this?
Solution
If the compiler can really optimize global variables modified by an interrupt service routine, then:
- Why does it have the right of optimizing a global variable in the first place when it can possibly be called from another file?
The key here is that in a "normal", single-threaded program with no interrupts, the global variable cannot be modified at any time. All accesses to the variable are sequenced in a predictable manner, no matter which file makes the access.
And the optimizations may be subtle. It is not as simple as "ah ok this global doesn't seem to be used, let's remove it entirely". Rather, for some code like
while(global)
{
do_stuff(global);
}
the optimizer might create something behaving like:
register tmp = global;
loop:
do_stuff(tmp);
goto loop;
Which changes the meaning of the program completely. How such bugs caused by the lack of volatile manifest themselves is always different from case-to-case. They are very hard to find.
- Why the example article and many others on the internet state that the compiler will not "notice" the interrupt callback function?
Because embedded compilers are traditionally stupid when it comes to this aspect. Traditionally, when a compiler spots your non-standard interrupt keyword, it will just do 2 things:
- Generate the specific return code from that function, since interrupts usually have different calling conventions compared to regular function calls.
- Ensure that the function gets linked even though it is never called from the program. Possibly allocated in a separate memory segment. This is actually done by the linker and not the compiler.
There might nowadays be smarter compilers. PC/desktop compilers face the very same issue when dealing with callback functions/threads, but they are usually smart enough to realize that they shouldn't assume things about global variables shared with a callback.
Embedded compilers are traditionally far dumber than PC/desktop compilers when it comes to optimizations. They are generally of lower quality and worse at standard compliance. If you are one of just a few compiler vendors supporting a specific target, or perhaps the only vendor, then the lack of competition means that you don't have to worry much about quality. You can sell crap and charge a lot for it.
But even good compilers can struggle with such scenarios, especially multi-platform ones that don't know anything about how interrupts etc work specifically in "target x".
So you have the case where the good, multi-platform compiler is too generic to handle this bug. While at the same time, the bad, narrow compiler for "target x" is too poorly written to handle it, even though it supposedly knows all about how interrupts work on "target x".
- How do I modify my code to accomplish this?
Make such globals volatile
.
Answered By - Lundin Answer Checked By - Candace Johnson (WPSolving Volunteer)