4

Following is some code to flash LED's on a Pandaboard when running the Barrelfish operating system. My question is that why don't the LED's flash if the 'volatile' keyword is removed from the definitions of gpio_oe and gpio_dataout.

static volatile uint32_t *gpio_oe = (uint32_t *)(GPIO_BASE + 0x0134);
static volatile uint32_t *gpio_dataout = (uint32_t *)(GPIO_BASE + 0x013C);

void led_flash
{
    // Enable output
    *gpio_oe &= (~(1 << 8));

    // Toggle LED on and off till eternity
    while(true)
    {
      *gpio_dataout ^= (1 << 8);  // Set means LED on; Clear means LED off
       time_delay();  // To give blinking effect
    }
}

I know that volatile needs to be used if the value of a variable can change spontaneously through a source outside the program. But I can't see such a case here. What optimization does the compiler perform that renders the whole while loop for flashing LED's meaningless? And what is the logic behind such optimization, ie. a legit case where such an optimization would make sense?

gjain
  • 4,468
  • 5
  • 39
  • 47
  • What compiler? If we could see the generated assembly, we might have some insight into exactly why the compiler chose to do what it did. – Jonathon Reinhart Feb 05 '13 at 14:55
  • @JonathonReinhart - compiler does not really matter, the C specification allows a compiler to optimize out access to a variable which is not volatile, replace it with a register, etc. That doesn't mean a given compiler will, but it means that a programmer must assume that it might - to do otherwise and depend on details gleaned from the assembly output is to open yourself to it breaking with even a slightly different version of the compilter, or different options or even code details fed to the same compiler. – Chris Stratton Feb 05 '13 at 14:57
  • 2
    I understand that, I was mostly curious. I, and the OP all understand that volatile is necessary here, because he's talking to hardware. His title says "why is volatile needed", but he's really asking why (when it was omitted) the compiler optimized in the way it did. So I was trying to see exactly what the compiler did. – Jonathon Reinhart Feb 05 '13 at 15:03

4 Answers4

9

You also need volatile to force a memory write and order in which generated code would access volatile variables. With regular variables the compiler may decide that the writes are unnecessary and either throw them away or only keep the last one.

Moved from the comments: The compiler may write nothing at all if it sees no reads of the variable, it may even remove the variable.

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • But how could the compiler possibly optimize _away_ the while loop, since in every iteration the value stored at gpio_dataout toggles (0 or 1), and since it's an infinite loop - there is no _last value_ that the compiler can assign to *gpio_dataout. To me, it appears that we can't avoid the while loop with reads/writes in each iteration. But the compiler is still being clever here. Why and how? – gjain Feb 05 '13 at 15:13
  • 3
    The compiler may write nothing at all if it sees no reads of the variable, it may even remove the variable. – Alexey Frunze Feb 05 '13 at 15:33
  • @netcoder: Sorry, but I still don't get it. You say that the compiler may "assume that the same value is always written and subsequent writes are therefore useless". But that is exactly what is _not_ the case, since in every iteration a 0 is written if there was 1 and vice-versa. It's not the same value that is written everytime. – gjain Feb 05 '13 at 15:36
  • @AlexeyFrunze: Your "comment" above directly answers the question asked in a way that your "answer" entirely fails to do. Edit the answer rather than leave people reading the comments to find the useful content. – Clifford Feb 05 '13 at 20:56
  • @AlexeyFrunze: To be fair I see now that the title of the question was changed *after* you wrote the answer, so the answer was probably valid previously. +1 because a) you fixed it, and b) in the context of the unedited title my comment was over critical. – Clifford Feb 05 '13 at 21:08
5

As far as the compiler can tell, the values *gpio_oe and *gpio_dataout are written but never read. For normal data memory such an access pattern is entirely redundant so can be optimised out. Similarly for locations that are read but never written.

For memory mapped I/O however access to the "memory" location has side effects that the compiler is not aware of. Declaring the location volatile tells the compiler that the location must be explicitly accessed exactly as described by the code.

As well as memory mapped I/O, a similar issue occurs with memory shared between separate threads (RTOS tasks or interrupt handlers for example) since the language is similarly unaware of these contexts.

Embedded.com covers the subject in a number of articles:

Clifford
  • 88,407
  • 13
  • 85
  • 165
4

You are trying to access hardware registers directly so you want every access to go to the memory bus and not stay in registers like a normal variable. Volatile will tell the compiler to force all uses of that variable to go to or come from the memory bus. You still have the problem of data caching but that is a separate topic.

EDIT:

What would happen is in your infinite loop the compiler could optimize that variable to be in a register meaning never go to the memory bus meaning never change the gpio meaning the led would not blink. This should be easy to see if you remove the volatile, compile then disassemble (or compile to asm, I find it much easier to read by disassembling the binary).

old_timer
  • 69,149
  • 8
  • 89
  • 168
1

volatile prevents the compiler from optimizing reads and writes to variable, without it the compiler assumes the value never changes and could replace a loop in which a flag is read with one call or remove a write if the variable is not used later, See this question:

Why is volatile needed in C?

Community
  • 1
  • 1
iabdalkader
  • 17,009
  • 4
  • 47
  • 74
  • Right general idea, but in this case its preventing the compiler from optimizing out *writes* to a variable which is seemingly never used *by anything the compiler would be aware of*. – Chris Stratton Feb 05 '13 at 14:59
  • @ChrisStratton right, I expanded the answer further to cover both cases. – iabdalkader Feb 05 '13 at 15:02
  • "without it the compiler assumes the value never changes" - I guess compiler can't assume that here since the value stored at gpio_dataout does _change_ in _every_ iteration. How can the compiler possibly ignore that? Also, it doesn't make much sense to replace while loop with a single call, since this is an infinite loop and the compiler can't possibly tell what the _last_ value would be; There is no _last_ value. – gjain Feb 05 '13 at 15:17
  • 1
    @gjain - without volatile to inform it that someone might be watching, it can simply conclude that running the loop has no effect, and skip it entirely (if non-infinite) or make the contents empty. – Chris Stratton Feb 05 '13 at 15:22
  • @gjain That's in the case of reading a variable not writing, when polling a flag for example. And in the case of writing, if you repeatedly write to a variable the *same value* it makes sense to replace that with just one write. – iabdalkader Feb 05 '13 at 15:23
  • @mux: I guess it's not the same value being written every time. It's 0 if it is 1 already and vice-versa. Chris' argument makes more sense to me here. – gjain Feb 05 '13 at 15:45