Sign In
Forgot Password?
Sign In | | Create Account

volatile

I have often talked and written about my thoughts on programming languages. In particular the fact that there really is no language that was designed with embedded programming specifically in mind. Historically, there was assembly language, but that is rarely used for large scale coding nowadays. Intel promoted PL/M many years ago, but that has fallen by the wayside.

C and C++ are the key languages today. Neither was designed for embedded, but they do the job quite well. Ironically, I think that one of the precursors to C, BCPL, would have been well suited to embedded work, but that language has been consigned to the history books.

There is one feature of C/C++, that is often not well understood, but is a godsend to embedded programmers: the keyword volatile

When you declare a variable/object volatile, you are telling the compiler not to optimize access to the data. When your code writes a value to the variable, it should be written straight away and not saved in a register for use later. Likewise, when the code reads the value, it should not use a copy that was obtained earlier.
Broadly speaking, it must be assumed that a volatile variable can change at any time, independently of the current code. This implies two uses: variables that are shared between execution threads [between tasks in an RTOS or between the mainline code and an interrupt service routine]; I/O device registers.

Although volatile is a useful language facility, it is not without difficulties. Firstly, some research has shown that many compilers do not implement the keyword correctly in all circumstances. So care and attention to generated code is required. There are also the usual troubles with C syntax; consider these:

volatile int* pi1 = 0;          // pointer to volatile int
int* volatile pi2 = 0;          // volatile pointer to int
volatile int* volatile pi3 = 0; // volatile pointer to volatile int

Plenty of room for errors there! [The same syntactic issues arise with const.]

However, just declaring a variable volatile is not enough if you are sharing it between execution threads [even if there are texts that suggest that this is the case]. To illustrate the problem, consider this code:

volatile x = 1;
...
x++;

Syntactically this is fine, but what about the generated code? If the CPU instruction set allows a memory location to be incremented directly with a single, non-interruptible instruction, there is no problem [so long as the compiler uses those instructions!]. But many devices would require the data to be read, incremented and written back. This is all fine according to the language definition, but what if an interrupt occurs during this sequence of instructions?

You need to protect access to it so that the memory location always contains a valid value. You could do this by disabling and re-enabling interrupts or, with an RTOS, maybe a semaphore would be the answer.

[With acknowledgement to my friend Meador Inge, upon whom I can always rely when I get puzzled about compilers, languages etc.]

RTOS

More Blog Posts

About Colin Walls Follow on Twitter

Colin WallsI have over twenty-five years experience in the electronics industry, largely dedicated to embedded software. A frequent presenter at conferences and seminars and author of numerous technical articles and two books on embedded software, I am a member of the marketing team of the Mentor Graphics Embedded Systems Division, and am based in the UK. Away from work, I have a wide range of interests including photography and trying to point my two daughters in the right direction in life. Learn more about Colin, including his go-to karaoke song and the best parts of being British: http://go.mentor.com/3_acv Visit The Colin Walls Blog

More Posts by Colin Walls

Comments 11

Post a Comment
Colin, This is *exactly* the issue I encountered with a customer a few months back. The customer had a bunch of shared data, between tasks & interrupts. All the data was 32-bit unsigned ints (no structs) on a 32-bit architecture. Most of the data was marked volatile, but none of it was protected. When I told them they needed to *protect* the data to prevent corruption, their main guru said "Nope, wrong, everything is a 32-bit value or smaller, and the PowerPC is a 32-bit architecture, so there's no problem -- a 32-bit read or write can't be interrupted. There is no window for data corruption." A big smile broke out on my face.... ahhhh... this stuff really is harder than it looks (when you've been doing it a while, you realize that the things you take for granted aren't always so obvious...) I explained that the issue wasn't corrupted/aborted bus cycles, the issue was the need for a protected read-modify-write operation which is spread across multiple CPU instructions, any one of which can be interrupted by an ISR or higher priority task. As you said, if an uninterruptible RMW cycle is available & implemented, or if there is one writer and multiple readers, the issue fades... but in the typical case of shared data with multiple clients reading & writing, data protection is crucial. The "volatile" keyword tells the compiler how to behave, not the other developers :-)

Dan
3:33 PM Aug 31, 2009

Thanks for the affirmation Dan. Always good to have real world feedback.

Colin Walls
3:44 PM Aug 31, 2009

Colin, Jack Ganssle suggested that everyone writing embedded code read this article about volatiles: http://www.cs.utah.edu/~regehr/papers/emsoft08-preprint.pdf. Turns out most compilers have problems with the implementation of the volatile construct. Interesting reading. -russ -russ

Russell Klein
4:31 PM Sep 1, 2009

Thanks Russ. I actually forwarded that article to our compiler team a few months back. Our products aren't "damned", but this gives us a chance to check.

Colin Walls
4:36 PM Sep 1, 2009

Great blog post on an important subject. I am writing to provide a few helpful links for those readers may want to dig deeper. The proper use of C's volatile keyword, including where to place it in a declaration, is poorly understood by many programmers. This is not surprising, as most C texts dismiss it in a sentence or two. We have a helpful article about this at http://www.netrino.com/Embedded-Systems/How-To/C-Volatile-Keyword I've blogged about best practices for use of volatile in C (http://www.embeddedgurus.net/barr-code/2009/03/coding-standard-rule-4-use-volatile.html) and related compiler problems (http://www.embeddedgurus.net/barr-code/2007/12/compiler-quality-and-cs-volatile.html). Nigel Jones has a nice blog post about a surprising alternative use for the volatile keyword in embedded software at http://www.embeddedgurus.net/stack-overflow/2009/01/using-volatile-to-achieve-persistence.html And, last but not least, there's clarification about whether to use a semaphore or a mutex to prevent race conditions in the article Mutexes and Semaphores Demystified at http://www.netrino.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore I hope all this extra info helps engineers avoid the problem Dan described above.

Michael Barr
10:20 AM Sep 3, 2009

Thanks Michael. A very useful contribution.

Colin Walls
10:24 AM Sep 3, 2009

Highly placed persons like you, writing such articles about deep insights in programming basics, actually points towards the under-importance given to it in our institutions - this must be fixed!

Sumeet Gupta
7:48 AM Sep 13, 2009

Sumeet: Firstly, thank you for describing me as "highly placed". I agree with your points 100%.

Colin Walls
6:57 PM Sep 13, 2009

My understanding of "volatile" in C is that it implies that the compiler should not rely on the value remaining the same between operations in the local code, i.e. if you read the value and put it in a register, do some other stuff and see another read, you should not assume the register and the address location still match. It has little to do with atomicity: in the code - volatile int x=1; x++; - all I would expect is that the "x++" does a "read add 1, write", despite just having done a write, without the "volatile" optimized code should just right 2. If some other thread comes in and changes x during the "add 1" that's an entirely different issue.

Kevin Cameron
3:16 AM Jan 9, 2010

Kevin: That last example has, IMHO, a flaw in the logic. What if x is mapped onto an I/O device? It could change value asynchronously from the code. I suppose that a compiler ought to not allow ++ and -- on volatiles?

Colin Walls
1:46 PM Jan 11, 2010

Since the C language is not tied to specific hardware, the language itself cannot make assumptions about atomicity, but the compiler could (optionally) issue warnings/errors. Bear in mind that in C++ ++ and -- can be overloaded so it is in theory possible to build atomic operators if you need them and/or add checks.

Kevin Cameron
7:42 PM Jan 17, 2010

Add Your Comment

Please complete the following information to comment or sign in.

(Your email will not be published)

Archives

 
Online Chat