This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

C2000 (v6.2.11 compiler): volatile and optimization

Guru 20045 points

Hello,

Some background info:

Previously, I had been calling a function in the main loop.  It should be called approximately every 10ms and it was being called approximately every 10ms, i.e. 10ms+0.7ms. The function has many counters that are used to keep track of time.  One of these counter counts up to 24Hrs, which is 8640000 counts.  The 0.7ms error caused the 24Hrs to be off by 1.68hrs, i.e. 8640000*10.7ms = 92448 seconds = 25.68hrs.

Therefore, I decided to change the function to an ISR, whose interrupt is a reserved interrupt (C2000)  and which allows nesting of all other interrupts.  This interrupt's flag (IFR) is set inside a timer isr.  Once the timer isr is completed, the interrupt occurs immediately if no other higher interrupts are pending.  Otherwise, it runs after all other higher interrupts have completed. This allows the function to act like a task in a real time OS.

Actual Issue:

The function (now ISR) accesses many variables (mainly structure variables) and contains five to six function calls.  A significant amount of the variables are used by functions outside of the ISR.  I thought these structure variable could be optimized out by the compiler, so I changed all them to volatile types.

Am I correct in thinking that the variables could be optimized out?  I haven't yet determine the impact the change has made on runtime performance.

I am thinking this same sort of issue would have occurred if I would have used a RTOS, since each task is a thread of execution that is separate from the main thread of execution.  Is that correct?

I could have changed the timer structure, but that would have required a significant amount of work.

Stephen

  • Any global variable which can be modified by an ISR must be declared volatile to let the rest of the program know that there is an ISR out there which could change it. The compiler tends not to optimize out global variables except under special circumstances, but you should still use volatile to be sure.
    There are many ways to arrange the sharing (or privacy) of objects between threads, just as there are many kinds of threads. The compiler's idea of an ISR is not really a thread, but it does share a lot of the same characteristics.
  • "Any global variable which can be modified by an ISR must be declared volatile to let the rest of the program know that there is an ISR out there which could change it."

    I thought the purpose of volatile was to make sure the compiler doesn't perform optimization on the volatile variable.

    "The compiler tends not to optimize out global variables except under special circumstances, but you should still use volatile to be sure."

    What are the special circumstances?

    "There are many ways to arrange the sharing (or privacy) of objects between threads, just as there are many kinds of threads."

    What are the ways to a variable between RTOS and the main thread without it being optimized out?   How are the RTOS thread and ISR different in terms of optimization?

  • I recommend you have a look at http://processors.wiki.ti.com/index.php/Volatile

    The volatile keyword has many uses, but they all boil down to telling the compiler that an object may affect or be affected by the system in ways the compiler cannot know about.  In the case of an ISR, the compiler can't know whether there exists an ISR which might at any time modify that global variable.  You need to declare that global variable as volatile; by doing so, you let the compiler know that not only can't it eliminate the variable entirely, but it must also read and write the variable at exactly the times that it appears to on the code.  That is, it can't perform certain optimizations like keeping the value in a register.

    The compiler usually won't optimize away global variables, but it will if it thinks it can see the entire program and that the variable is never used.  This might happen in whole-program optimization.  It might also happen if the global variable is static.

    The main difference between a thread (of any kind) and an ISR is that threads might run simultaneously (or time sliced), whereas an ISR will suspend the thread it is interrupting while the ISR is active.

    In any case, I think there's really only one thing you should be doing.  You have an ISR which accesses global variables, so those variables should be declared volatile.  Whether or not there are also threads in the system makes no difference.

  • "In the case of an ISR, the compiler can't know whether there exists an ISR which might at any time modify that global variable."

    I have always wondered about this. If both an ISR and a function called by main use the same global variable, why wouldn't the compiler know not know to update the variable each time it is used?

    "The main difference between a thread (of any kind) and an ISR is that threads might run simultaneously (or time sliced), whereas an ISR will suspend the thread it is interrupting while the ISR is active."

    I would like to add that two ISRs can run almost simultaneously if nesting is allowed.

    "In any case, I think there's really only one thing you should be doing.  You have an ISR which accesses global variables, so those variables should be declared volatile.  Whether or not there are also threads in the system makes no difference."

    I agree.  However, it would be nice to have some tool which would output where variables are used.  Is there any tool that will do that?

    Thanks,

    Stephen

  • The compiler can't be sure it sees the entire program. You might have an ISR written in assembly tucked away somewhere.
  • The compiler has to compile the entire program (ISRs, functions and etc.). How can it not see the entire program?

    Ok, I thought about it a little bit. This is not exactly an issue with ISRs. It's an issue in general with any variable that can be changed outside the normal program flow (i.e. thread and etc.). If the compiler is optimizing, the first access will most likely be from memory and any subsequent access will possibly use a register. That's why when the ISR updates the global variable the main thread (or any other part of the program that is waiting for it to be set) can't see it being set because it using a cache value (located in a register).
  • We distinguish between the compiler, which only sees the C code, and the linker, which sees all of the object files for the entire program. If you have an ISR written in assembly code, the compiler has no chance to see what's going on.
  • You have been very helpful.

    Designating a variable as volatile will most likely slow down code execution because the processor is required to access memory more often.  There will also be more instructions to read from flash since the memory's address has to be specified.  This further slows down code execution.

    Therefore, I would like to not to have to designate a variable as global.

    I am thinking that the only potential problem area is where a piece of code is waiting for a variable to be set,i.e. 

    While(var1){}

    For this situation, var1 is loaded from memory into a register the first time the while loop is executed.  From that point onward the processor will use the value in the register.  

    Other areas of code where the variable is used more than once in a section of code shouldn't be an issue, i.e.

    code:

    var2 = a*b+2;  // the time var2 is referenced in this function.  var2 is loaded from memory into a register

    ... (small number of lines of code)

    var3 = x +  var2; // the second time var2 is referenced in this function.  var2 is in a register.

    An ISR updating  var2 between the first and last line of code is not an issue since the above code will be called again to compute a new var3. 

    Other than the while(var1){} scenario, can you think of any other scenarios that can cause an issue?

     

  • Yes, declaring an object volatile inhibits optimization.  However, you should still use volatile in this case.  Any reference to the volatile object could potentially be a problem.

    In your example where volatile "shouldn't be an issue," if you find that the extra load from memory is a bottleneck, you should read from var2 into a temporary variable:

    var2_copy = var2;
    var2 = var2_copy = a*b + 2;
    ..
    var3 = x + var2_copy;

    However, I doubt this is going to be a bottleneck.  You should write the code in a natural fashion, evaluate its performance, and only if it is a bottleneck, then tweak the code to avoid the extra load.  Please see the following: http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize

  • My ISR accesses 351 global variables (all of them in structures).  That might sound a lot for an ISR, but the ISR does alot.  The ISR is more like a RTOS task than an ISR since it allows nesting of all other ISRs in use.

    "Any reference to the volatile object could potentially be a problem"

    Some examples would be nice, however, I think it would be difficult to come up with one.  The only issue I see in the last example I gave would be atomic reads and writes, however, the volatile keyword doesn't guarantee atomic reads/writes.