Volatile in Peripheral registers
Embedded systems contain real hardware, usually with sophisticated peripherals. These peripherals contain registers whose values may change asynchronously to the program flow. As a very simple example, consider an 8-bit status register at address 0x1234. It is required that you poll the status register until it becomes non-zero. The nave and incorrect implementation is as follows:
UINT1 * ptr = (UINT1 *) 0x1234;
// Wait for register to become non-zero.
while (*ptr == 0);
// Do something else.
This will almost certainly fail as soon as you turn the optimizer on, since the compiler will generate assembly language that looks something like this:
mov ptr, #0x1234 mov a, @ptr loop bz loop
The rationale of the optimizer is quite simple: having already read the variable's value into the accumulator (on the second line), there is no need to reread it, since the value will always be the same. Thus, in the third line, we end up with an infinite loop. To force the compiler to do what we want, we modify the declaration to:
UINT1 volatile * ptr =
(UINT1 volatile *) 0x1234;
The assembly language now looks like this:
mov ptr, #0x1234
loop mov a, @ptr
bz loop
The desired behavior is achieved.
Subtler problems tend to arise with registers that have special properties. For instance, a lot of peripherals contain registers that are cleared simply by reading them. Extra (or fewer) reads than you are intending can cause quite unexpected results in these cases.
We provide Volatile in C - hardware registers for the educational purposes
only. We do not responsiable for the correctness of its contents. the risk of
using it lies entirelywith the user.