2.4.8. Use of volatile

2.4.8.1. Shared Data

  • Any global variable that is read/written by main() and one or more ISRs must be annotated volatile

  • Volatile indicates to the compiler that the variable might be modified by something external to the obvious flow of the program such as an ISR

  • This ensures the compiler preserves the number of volatile reads and writes to the global variable exactly as written in C/C++ code. The compiler will not:

    • Eliminate redundant reads or writes

    • Re-order accesses

Table 2.13 illustrates the need for volatile when optimizations are enabled. Without the volatile qualifier on flag, the compiler will remove the if block in main() because its analysis indicates that flag is always 0 and the if condition is always false. volatile indicates to the compiler that something outside of main(), in this case the ISR, can update flag.

Table 2.13 Use of volatile

Main application

Interrupt Service Routine

volatile int flag;
int x;

int main()
{
    flag = 0;
    ...

    if (flag == 1)
        x++;

    ...
}
extern int flag;
interrupt void ISR(void)
{
    ...
    flag = 1;
    ...
}

2.4.8.2. Peripheral access

  • The volatile keyword must be used when accessing memory locations that represent memory mapped peripherals.

  • Such memory locations might change value in ways that the compiler cannot predict.

  • This ensures the compiler preserves reads and writes to memory exactly as written in the C code.

  • A missing volatile qualifier can result in the compiler incorrectly optimizing away or reordering reads/writes.

Listing 2.2 Using volatile for peripheral register access
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    static inline void
    GPIO_writePin(uint32_t pin, uint32_t outVal)
    {
        volatile uint32_t *gpioDataReg;
        uint32_t pinMask;

        //
        // Check the arguments.
        //
        ASSERT(GPIO_isPinValid(pin));

        gpioDataReg = (uint32_t *)GPIODATA_BASE +
                      ((pin / 32U) * GPIO_DATA_REGS_STEP);

        pinMask = (uint32_t)1U << (pin % 32U);

        if(outVal == 0U)
        {
            gpioDataReg[GPIO_GPxCLEAR_INDEX] = pinMask;
        }
        else
        {
            gpioDataReg[GPIO_GPxSET_INDEX] = pinMask;
        }
    }