DOC PREVIEW
UNCC ECGR 4101 - Volatile

This preview shows page 1 out of 2 pages.

Save
View full document
View full document
Premium Document
Do you want full access? Go Premium and unlock all 2 pages.
Access to all documents
Download any document
Ad free experience
Premium Document
Do you want full access? Go Premium and unlock all 2 pages.
Access to all documents
Download any document
Ad free experience

Unformatted text preview:

Have you experienced any of thefollowing in your C/C++ embeddedcode?•Code that works fine—until youturn optimization on•Code that works fine—as long asinterrupts are disabled•Flaky hardware drivers•Tasks that work fine in isolation—yet crash when another task isenabledIf you answered yes to any of theabove, it’s likely that you didn’t use theC keyword volatile. You aren’t alone.The use of volatile is poorly under-stood by many programmers. This isnot surprising, as most C texts dismissit in a sentence or two.volatile is a qualifier that isapplied to a variable when it isdeclared. It tells the compiler that thevalue of the variable may change atany time—without any action beingtaken by the code the compiler findsnearby. The implications of this arequite serious. However, before weexamine them, let’s take a look at thesyntax.SyntaxTo declare a variable volatile, includethe keyword volatile before or afterthe data type in the variable defini-tion. For instance both of these decla-rations will declare foo to be a volatileinteger:volatile int foo;int volatile foo;Now, it turns out that pointers tovolatile variables are very common.Both of these declarations declare footo be a pointer to a volatile integer:volatile int * foo; int volatile * foo; Volatile pointers to non-volatilevariables are very rare (I think I’veused them once), but I’d better goahead and give you the syntax:int * volatile foo;And just for completeness, if youreally must have a volatile pointer to avolatile variable, then:int volatile * volatile foo; Incidentally, for a great explana-tion of why you have a choice of whereto place volatile and why you shouldplace it after the data type (for exam-ple, int volatile * foo), consult DanSak’s column, “Top-Level cv-Qualifiersin Function Parameters” (February2000, p. 63).Finally, if you apply volatile to astruct or union, the entire contents ofthe struct/union are volatile. If youdon’t want this behavior, you can applythe volatile qualifier to the individualmembers of the struct/union.UseA variable should be declared volatilewhenever its value could change unex-pectedly. In practice, only three typesof variables could change: •Memory-mapped peripheral registers•Global variables modified by aninterrupt service routine•Global variables within a multi-threaded applicationPeripheral registersEmbedded systems contain real hard-ware, usually with sophisticatedperipherals. These peripherals con-tain registers whose values may changeasynchronously to the program flow.As a very simple example, consider an8-bit status register at address 0x1234.It is required that you poll the statusregister until it becomes non-zero.The naïve and incorrect implementa-tion is as follows:UINT1 * ptr = (UINT1 *) 0x1234;// Wait for register to become non-zero.while (*ptr == 0);// Do something else.This will almost certainly fail as soonas you turn the optimizer on, since thecompiler will generate assembly lan-guage that looks something like this:mov ptr, #0x1234mov a, @ptrloop bz loop The rationale of the optimizer isquite simple: having already read thevariable’s value into the accumulator(on the second line), there is no needto reread it, since the value will alwaysbe the same. Thus, in the third line,we end up with an infinite loop.Embedded Systems Programming JULY 2001 95BEGINNER’S CORNERby Nigel JonesVolatile✁ CUT HERE ✁RETURNTo force the compiler to do whatwe want, we modify the declaration to:UINT1 volatile * ptr = (UINT1 volatile *) 0x1234;The assembly language now lookslike this:mov ptr, #0x1234loop mov a, @ptrbz loopThe desired behavior is achieved. Subtler problems tend to arise withregisters that have special properties.For instance, a lot of peripherals con-tain registers that are cleared simply byreading them. Extra (or fewer) readsthan you are intending can cause quiteunexpected results in these cases.Interrupt service routinesInterrupt service routines often setvariables that are tested in main linecode. For example, a serial portinterrupt may test each receivedcharacter to see if it is an ETX char-acter (presumably signifying the endof a message). If the character is anETX, the ISR might set a global flag.An incorrect implementation of thismight be:int etx_rcvd = FALSE;void main(){...while (!ext_rcvd){// Wait}...}interrupt void rx_isr(void){...if (ETX == rx_char){etx_rcvd = TRUE;}...}With optimization turned off,this code might work. However, anyhalf decent optimizer will “break”the code. The problem is that thecompiler has no idea that etx_rcvdcan be changed within an ISR. As faras the compiler is concerned, theexpression !ext_rcvd is always true,and, therefore, you can never exitthe while loop. Consequently, all thecode after the while loop may simplybe removed by the optimizer. If youare lucky, your compiler will warnyou about this. If you are unlucky(or you haven’t yet learned to takecompiler warnings seriously), yourcode will fail miserably. Naturally,the blame will be placed on a “lousyoptimizer.”The solution is to declare the vari-able etx_rcvd to be volatile. Then allof your problems (well, some of themanyway) will disappear.Multi-threaded applicationsDespite the presence of queues, pipes,and other scheduler-aware communi-cations mechanisms in real-time oper-ating systems, it is still fairly commonfor two tasks to exchange informationvia a shared memory location (that is,a global).When you add a pre-emptive sched-uler to your code, your compiler stillhas no idea what a context switch is orwhen one might occur. Thus, anothertask modifying a shared global is con-ceptually identical to the problem ofinterrupt service routines discussedpreviously. So all shared global vari-ables should be declared volatile.For example:int cntr;void task1(void){cntr = 0;while (cntr == 0){sleep(1);}...}void task2(void){...cntr++;sleep(10);...}This code will likely fail once the com-piler’s optimizer is enabled. Declaringcntr to be volatile is the proper wayto solve the problem.Final thoughtsSome compilers allow you to implicitlydeclare all variables as volatile. Resistthis temptation, since it is essentially asubstitute for thought. It also leads topotentially less efficient code.Also, resist the temptation to blamethe optimizer or turn it off. Modernoptimizers are so good that I cannotremember the last time I came acrossan optimization bug. In contrast, Icome across failures to use volatilewith depressing frequency.If you are


View Full Document

UNCC ECGR 4101 - Volatile

Documents in this Course
Load more
Download Volatile
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view Volatile and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view Volatile 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?