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.

volatile Keyword

Hello all,

I am trying to best understand use of volatile variables. To the best of my understanding, a variable should only be declared as volatile if its value will change outside the main program's flow (like in an ISR).

Let's assume we are reading from the UART by utilizing the UART_RX ISR and reading a keyboard input from the user.

Let's assume we have the following variables:
Value = the byte value in the UART_RX buffer register
Count = the number of bytes received

When the user presses a key, the program enters the UART_RX ISR, where count is incremented, and Value is changed to the user input.

From my understanding, since both variables are updated/changed inside the ISR, both of them should be declared as volatile. Is this true? Also, if Value were an array of characters, whose values would get incrementally updated each time the user types a key, this array should also be declared volatile.

Would there be another type of situation that a variable should be volatile? I have seen some posts on the forum mention the case of hardware registers (e.g. reading P1IN or writing P1OUT). But to my understanding, these are already declared in the header file (if using C), and in general one should not have to worry about it. If this is true, are volatiles used mainly used in conjunction with ISR's and other peripherals like the DMA module?

Thanks in advance.

Sincerely,

Mehdi

  • Mehdi Rahman said:
    a variable should only be declared as volatile if its value will change outside the main program's flow (like in an ISR).

    Correct.

    Mehdi Rahman said:
    Would there be another type of situation that a variable should be volatile?

    The one most commonly quoted in 'C' textbooks (most of which are not written for embedded microcontroller users) is variables shared between tasks in a multitasking system.

    Stuff in shared memory (eg, dual-port RAM) would be another.

    Mehdi Rahman said:
    I have seen some posts on the forum mention the case of hardware registers (e.g. reading P1IN or writing P1OUT). But to my understanding, these are already declared in the header file (if using C), and in general one should not have to worry about it.

    Of course, the whole point of a ready-made header is that it should do this for you. But if you were "rolling your own" then you would have to consider it.

    http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword

    Although it may seem odd at first sight, note that it does make sense to qualify some things as both 'volatile' and 'const'

    http://www.embedded.com/electronics-blogs/barr-code/4236917/Combining-C-s-volatile-and-const-keywords

  • I know very little about c. But I think your understanding of volatile is completely incorrect. You are talking about the scope of a variable. volatile has very little to do with scope. You may be also concerned about the life of a variable. That has very little to do with volatile either.

    Sorry, I am not qualified to give you an answer. However, I am certain that you are on the wrong track.

  • OCY said:
    You are talking about the scope of a variable

    That was not my reading of the original post but, reading it again, you may be right!

    OCY said:
    volatile has very little to do with scope. You may be also concerned about the life of a variable. That has very little to do with volatile either.

    Yes - 'volatile' has absolutely nothing to do with either scope or lifetime.

    volatile int some_global_variable;

    void some_ISR( void )
    {
       :
       some_global_variable = whatever;
       :
    }

    void some_function( void )
    {
       :
       some_other_variable = some_global_variable;
       :
    }

    Because 'some_global_variable' can be modified by the ISR outside of the "normal execution" of some_function(), it needs to be declared volatile;

    Because 'some_global_variable' needs to be accessible to both the ISR and some_function(), it needs to be global (or, at least, at file scope if both functions are in the same compilation unit).

  • Andy Neil said:
    ... Because 'some_global_variable' can be modified by the ISR outside of the "normal execution" of some_function(), it needs to be declared volatile; ...

    That is true. In most cases, memory mapped I/O need to be declared volatile for the same reason. This is (I think) so that the compiler will take your source code seriously and not to optimize the seemingly unnecessary read/write access out.

    However, depends on the intend of the code, some time you may want to defeat this by making a copy of to a local variable and use that copy the normal way. Or, occasionally, you may want to disable interrupt for a short period of time so that the volatile variable will not be accessed by the ISR during such critical period.

  • Mehdi Rahman said:
    I am trying to best understand use of volatile variables. To the best of my understanding, a variable should only be declared as volatile if its value will change outside the main program's flow (like in an ISR).


    Basically, 'volatile' means the content of this memory location cannot be considered stable. So you must not use a locak backup of it (e.g. in a register) to optimize code.
    The wider interpretation of 'volatile' means that the use of this memory location (AKA 'variable') has side-effects of unspecified type. So the compiler is forced to do each read or write access excatcly where and when and as often as it appears in the source code. This of course significantly lowers code efficiency.

    One possible use is for variables that are changed in an ISR. Without volatile, main would perhaps work with a copy of the value in a whiel loop and never notice that the 'real' variable has been altered from inside an ISR.
    But there are other appearances too: Take the IV registers. Reading them changes their value, writing to them changes their value to 0 and of courtse a pending interrupt changes the next reading too. If you write somethign to one of the IV registers (like SYSRSTIV), the compiler must not assume that reading SYSRSTIV right wiht the next isntruction will return the same value jyou just wrote.
    Or clearing an IFG bit doesn't mean it stays cleared when you read the register again.

    For this reason, all hardware registers are declared as volatile 'variables'. And you're right, this is already done in the header files.

    However, you might 'virtualize' hardware registers in your software, liek assigning the reference to an USCI register to a global pointer, so your code sets the pointer up once and then can work with USCI0 or USCI1 with the same code. The the pointer should be volatile too. "A volatile unsigned char *" (for a byte register) as it points to a volatile memory location.
    And perhaps even a "volatile unsigned char volatile *" if the pointer itself may change unexpectedly (e.g. because an pushbutton ISR switches it to point to another USCI)

    Inline assembly instrucitons are usually volatile too. (imagine the '__disable_interrupt()' (which is directly translated into an assembly instruction) would be moved around for optimization.

    However, the use of volatile on local variables is normally ignored. Local variables are generated by the compiler. And the compiler exactly knows whether anything can possibly know where the variable is and could change it. Only if you pass the reference of a local variable to a function (or a global pointer), it may be altered form outside the program flow and 'volatile' will be obeyed.
    So it is futile to declare a local counter variable as volatile to avoid the compiler optimizing away an empty delay-loop.

  • OCY said:
    In most cases, memory mapped I/O need to be declared volatile for the same reason. This is (I think) so that the compiler will take your source code seriously and not to optimize the seemingly unnecessary read/write access out

    Correct.

    OCY said:
    you may want to disable interrupt for a short period of time so that the volatile variable will not be accessed by the ISR during such critical period

    Now I think you're straying into the realms of so-called "atomic" access?

    Declaring a variable volatile does not affect whether or not accesses are atomic - if it matters, you have to take separate measures to fix that...

  • Andy Neil said:
    Declaring a variable volatile does not affect whether or not accesses are atomic - if it matters, you have to take separate measures to fix that...

    Right.

    On MSP430, access to short int (16 bit) is usually atomic. Howeve,r this is only true if the access doesn't involve any pointers.
    If you have a struct that contains an int, access i sonly guaranteed to be atomic, if the struct is a global variable and you access it though its global name. If you have a pointer to the struct and pass it to a funciton, the funciton doesn't know whether the struct is word aligned and has to access the INT member with two byte accesses.
    Same is true for packed structs.
    Also, only RMW instructions are atomic (e.g. x += y). If (and only if) the compiler correctly generates an RMW instruction (and possible only for +-|&,not for */%). Still a DMA may interrupt between the read and the write. If you use a formula like (x = x+y) then the generation of an RMW instruction is less likely.
    long int (32 bit) accesses are never atomic since the MSP needs to do two 16 bit reads/writes. If you have a 32 bit counter that is incremented inside an ISR, you must access it only while interrupts are disabled.

    There are other threads in this forum talking about atomic access.

  • Jens-Michael Gross said:
    If you have a pointer to the struct and pass it to a funciton, the funciton doesn't know whether the struct is word aligned and has to access the INT member with two byte accesses.

    That's not right for the IAR and CCS compilers.  Does gcc do that?  I would hope not.

    Jeff

  • Jeff Tenney said:
    That's not right for the IAR and CCS compilers.  Does gcc do that?  I would hope not.

    MSPGCC does - now. Originally, it didn't, trying 16 bit read on a misaligned member of the misaligned struct with desastrous results.
    I had an applicaiton where the struct is part of an incoming package with stramlined data. So the absolute position of the struct dependend on the data before it. I moved a pointer along the stream buffer to interpret the data. When I discovered teh struct, I did a pointer typecast to the struct type and read the content. However, when the struct wasn't aliogned, the result was wrong. a 50:50 chance. The next version of MSPGCC fixed this.

    In this specific case, the struct was packed - I'm not sure whether the fix would apply to an unpacked struct, I never tried.
    However, the MSP can only read aligned words (while other processors may be able to read word values even if misaligned) and will silently ignore the LSB of a target address on any word operation.
    To ensure that a word read from an unknown pointer succeeds, you'll have to read the LSB first with a byte read operation into the target, read the MSB into a register, do a byte swap on it and then OR the two into the target. Similar for writing to memory.

  • Jens-Michael Gross said:
    I'm not sure whether the fix would apply to an unpacked struct, I never tried.

    I don't think mspgcc would apply that fix to normal (not-packed) structs.  If you define a struct as packed (to byte level) then the compiler will use the slower, byte-oriented code for access to that data type.  However, if the struct is defined normally (packed to level native to the machine, 2-byte words for MSP430), then the compiler should use the faster, word-oriented code.  IAR and CCS do this.  I would assume gcc has it right too.

    Dealing with "packed" structures can be tricky.  For example, creating a pointer to a field inside a packed structure is allowed but is highly perilous.

    And now we are officially way off topic!  ;-)

    Jeff

  • Jeff Tenney said:
    And now we are officially way off topic!  ;-)

    It's still about compiler handling of 'special' cases of variables. But no longer about volatile. :)

    As I said, the problem with the MSP is that it will silently ignore the LSB on any word-size instruction. When using pointers to structs, these might be misaligned. The compiler doesn't know whether a passed pointer will be odd or even. You may be right and the byte-access fix only applies if the pointer points to a packed struct. It makes some sense.
    When I first encountered the problem, I tried the packed attribute and it didn't work, then I wrote access macros which manually do what the compiler should have done and defined all field ins the struct as arrays of char. With the advantage that the struct and the maros remove possible problems with endianess when the same code is compiled on different systems (which it does in my case).
    Later I discovered more by accident that the problem was fixed in a newer compiler revision.

    However, if you aren't aware of this problem of ignored LSBs on word operations, you will lose lots of hair when trying to debug a program where this occurs, beause the debugger will show you something happen that doesn't match the code and data.
    The very same thing that will happen if you debug code where the volatile keyword is missing on key variables.

**Attention** This is a public forum