Beyond volatile

How to save days of debugging time

When debugging embedded applications, intermittent problems are among the more difficult ones. This text discusses one source of such problems: variables that are accessed asynchronously, e.g., by interrupt routines or by code running in separate threads.

Such variables must be properly defined and have adequate protection. The definition must include the volatile keyword—this informs the compiler that the variable can be changed from other places than the currently executing thread. The compiler will then avoid certain optimizations on this variable, obeying the accesses to the variable as given in the source code and making sure the variable is stable at each "sequence point". Thus the compiler will not rely on knowledge of values from previous accesses to reuse the same register, move operations across sequence points and so on.

To protect a shared variable, each access to the variable must also be made atomic from other accesses. Reading from or writing to a variable in C is not guaranteed to be an atomic operation as such. Accessing a 32-bit variable on an 8-bit architecture would typically be non-atomic, as would any object read or write that requires more than one instruction. Even if you inspect the compiler generated code and find it to be OK, it is not certain that the next compilation will give the same result if you have made changes to the code or to the compiler settings.

C Example

A typical example of a shared variable is when an interrupt routine updates a time-keeping variable that is read from main code. If the Interrupt Service Routine (ISR) runs with interrupts disabled (which often is the case), the interrupt function is atomic. In other words, it will not be interrupted and thus this access should not need further protection.

Note that for the below ISR you will also need to enable and initialize the interrupt, but that is not of interest for this example.

volatile long tick_count = 0;
__interrupt void tick_timer ()
{
tick_count++;
}

The main code on the other hand must prevent the interrupt from happening while the variable is being accessed. A simplified unsafe example would be:

...
if (tick_count >= next_stop) /* Unsafe! */
{
next_stop += 100;
do_stuff();
}

This code is unsafe as reading tick_count is not guaranteed to be an atomic operation. A tick_timer interrupt may occur when we have read one or a few bytes of tick_count. Then tick_count is modified in the ISR and when we resume reading tick_count, we read the rest of tick_count from the updated value. The value we compare could be wrong; it would be a mix of the previous and the new value. In many cases mixing old and new values does no real harm, you get either the old or the new value, but in others you get something completely different altogether. For example, if the previous value of tick_count is 0x00FF FFFF and the updated value is 0x0100 0000 it is easy to see how the resulting value can be completely wrong.

To protect the access of tick_count, we can use a monitor function:

__monitor long get_tick_count () 
{
return tick_count;
}
... 
if (get_tick_count() >= next_stop)
{
next_stop += 100;
do_stuff();
}

A monitor function is a function that disables interrupts while it is executing, and then restores the previous interrupt state at function exit.

A shorter monitor or A taste of C++

A monitor function has the disadvantage that interrupts are turned off for the full duration of the function. Sometimes it would be nice to be able to turn off interrupts in the middle of a function. This can often be done using intrinsic functions; the minor disadvantage is that you will have to remember to put in a line of code to turn off interrupts, and one at the end of the section to enable it. In C++, you can embed this logic in a class—using IAR Embedded Workbench for AVR as an example, you can add the following class:

extern "C" 
{
#include <inavr.h>
}
class Mutex 
{
public:
Mutex ()
{
_state = __save_interrupt();
__disable_interrupt();
}
~Mutex ()
{
__restore_interrupt(_state);
}

private:
unsigned char _state;
};

To use this Mutex class in the previous example, you can use the below for main code:

long tick;
{
Mutex m;
tick = tick_count;
}
if (tick >= next_stop)
{
next_stop += 100;
do_stuff();
}

The constructor of m will be inserted first in the block, it will save previous interrupt state and disable interrupts. The body is then executed (reading tick_count) and finally the destructor will restore the previous state.

Comments

There are numerous variations on the details of implementation, but the general principles are the same.

For C, if you use a compiler that does not have a monitor keyword, you would need to implement equivalent functionality in some other way, possibly using intrinsic functions similar to what is done in the Mutex class above. 

With IAR Embedded Workbench for ARM you might use __get_CPSR() to find out the old state of the interrupts, with IAR Embedded Workbench for MSP430 you might use __get_interrupt_state().

In some cases you may assume the previous interrupt status is known, i.e. just do enable and disable.

If you are using an operating system, it will provide a number of services that minimizes the need for you to disable and enable interrupts as above. For one thing, operating systems typically provide some kind of system clock where you only should use the appropriate system calls, not manipulate implementation details. Another thing to note is that most operating systems provide semaphores to protect common resources.

Conclusion

The key topic of this text is to ensure that variables accessed asynchronously are protected properly, and that volatile is not always enough.

The general solution is to ensure that certain operations cannot be interrupted. Some ways to achieve this in C and in C++ are described, giving C and C++ code fragments for IAR Embedded Workbench for AVR as well as names of relevant intrinsic functions for IAR Embedded Workbench for ARM and IAR Embedded Workbench for MSP430.

This article is written by Bo Eriksson, Technical Support Engineer at IAR Systems.

© IAR Systems 1995-2016 - All rights reserved.