Many C programmers get confused about Volatile keyword. The actual definition and applicability of the volatile keyword is often misconstrued in the context of the C language. Most of the tutorials don’t elaborate it to extend. This article will first briefly explain volatile and its history and then, through a series of examples about how not to use it, how to most effectively create correct systems software using volatile.
A common misconception is to imagine that somehow
const
is the opposite of volatile
and vice versa. They are unrelated and you should remember the fact.
Why Use Volatile?
By declaring a variable volatile you are effectively asking the compiler to be as inefficient as possible when it comes to reading or writing that variable. Specifically, the compiler should generate object code to perform each and every read from a volatile variable and each and every write to a volatile variable–even if you write it twice in a row or read it and ignore the result. No read or write can be skipped. Effectively no optimizations are allowed with respect to volatile variables.
Proper use of volatile
A variable should be declared volatile whenever its value could change unexpectedly. In practice, only following types of variables could be changed
- Memory-mapped peripheral registers
- Global variables modified by an interrupt service routine
- Global variables accessed by multiple tasks within a multi-threaded application
How to Use C’s volatile Keyword:
Now that the effect of the the “volatile” keyword has been briefly covered, we need to know exactly how to use it. In definition indicate that a variable is volatile, by including the keyword before or after the type indicator in a variable definition.
An example of declaring a simple volatile int type variable would be:
1
2
| volatile int x; int volatile x; |
If you had a pointer variable where the memory pointed to was volatile you could indicate that using:
1
2
| volatile int * x; int volatile * x; |
If you have a pointer variable where the address itself was volatile but the memory pointed to was not then:
1
| int * volatile x; |
If there was a pointer variable where both the pointer address and the memory pointed to were both volatile then you could do:
1
2
3
| volatile int * volatile x; int volatile * volatile x; |
Suppose our hardware is mapped into RAM somewhere and that has two addresses: a command port and a data port:
1
2
3
4
5
6
| typedef struct { int command; int data ; int isbusy; } MyGadget; |
Ok send some command now
1
2
3
4
5
6
7
8
9
10
11
12
| void SendCommand ( MyGadget * gadget , int command , int data ) { / / wait while the gadget is busy : while ( gadget - > isbusy ) { / / do nothing here. } / / set data first : gadget - > data = data ; / / writing the command starts the action : gadget - > command = command; } |
It is certain to fail because the compiler is free to change the order in which data and commands are written. This would cause gadget to issue commands with the previous data-value. Also take a look at the wait while busy loop, which will be optimized out. The compiler will try to be clever, read the value of isbusy just once and then go into an infinite loop.
No comments:
Post a Comment
commnet here