Interrupts, atomic variables, and the “volatile” keyword
I have been trying to track down a particularly tricky problem on my Wireless Debug Probe over the last few days. When embedded systems begin to perform in strange, apparently illogical, ways the culprit is usually related to interrupts disrupting the logic of the non-interrupt, or background tasks.
While this turned out not to be the issue I was facing with the project, the first things checked were the potential impacts of interrupts. The first area of interest was to check if there were any variables or code lines that the compiler may have been optimizing out. In general ‘C/C++’ compilers these days have really good optimizers that remove unnecessary code or reduce it to a minimal number of instructions. The compiler does this by analyzing code in the context of the surrounding code. This can be problematic when for example a variable is being examined in a background task and modified in an interrupt task. A typical use of this may be a boolean flag that gets asserted by an interrupt function and is tested in a background function. As the compiler examines the code around the background testing it may find no other references to the variable and decide it can optimize the test out, or change it in some other way. The result of this is that the background task may never detect the boolean being asserted. Fortunately modern ‘C/C++’ compilers have the “volatile” keyword that allows us to inform the compiler that the code should not be optimized when the variable is accessed.
For a good discussion of the volatile keyword see this article.
A second potential issue is reading and writing of variables being interrupted mid-way through the process by a function that also modifies the variable. When a microcontroller needs to modify a memory-based variable it needs to perform a read, modify, write sequence. This sequence may take several instructions and an interrupt may occur at any point during the sequence. Should the interrupt function modify the variable the value partially read or written by the background function may be corrupted. To avoid such non-atomic operations the background function should temporarily disable any interrupt that may affect the variable to be manipulated, restoring the interrupt state once the operation is completed.
Now back to finding my real problem.