Search code examples
cvolatilemisra

Read volatile variable has persistent effect? Misra C


Reading the Misra C guideline, I bumped into the following example:

extern volatile int v1, v2;
int t;

t = v1 + v2;

Per Misra C reading the variables v1 and v2 has a persistent side effect. I was wondering, why is that? Why reading a volatile variable may affect the execution state at that point? Note: Not assigning the sum to variable t, but just the reading itself.

Definition of persistent side effect: "A side effect is said to be persistent at a particular point in execution if it might have an effect on the execution state at that point." [Reference: Misra C, Appendix J]


Solution

  • This has nothing to do with MISRA-C as such, but the C language itself. The definition is found in the C standard (C11 5.1.2.3):

    Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

    It is somewhat common to have hardware peripheral flag registers that change their value upon read. For example: reading a status register followed by reading a data register is a common way to clear flags in UART or SPI hardware.

    For this reason, MISRA-C wants you to isolate volatile accesses into simple expressions, so that you don't get unexpected results such as in x && my_volatile, where my_volatile may or may not be evaluated and updated. The correct approach is to isolate the volatile access to a line of its own:

    if(x) 
    { 
      int tmp = my_volatile;
      // do stuff with tmp
    }
    

    These rules also prevents you from writing dysfunctional code such as:

    REGISTER |= FLAG1;
    REGISTER |= FLAG2;
    ...
    

    resulting in multiple writes to the register which causes execution overhead and possibly unwanted side effects. The above bad code scenario is sadly far more common in embedded firmware than you'd expect. Again, it is easiest to store the results in a temporary variable and isolate the write to a single line:

    uint32_t reg = FLAG1 | FLAG2 | ...;
    REGISTER = reg;