9

Im writing embedded firmware, and find it sometimes hard to decide when I need volatile or not.

When I have a function that waits for some boolean flag to be changed by an interrupt, it's obvious that the flag needs to be volatile, because else the function would wait forever, since the compiler doesn't realise the value can be changed by the interrupt.

But when I have a short function that just checks a flag in the first line, I would expect the flag doesnt need to be volatile, because its value will be read every time I enter the function? So when an interrupt modifies its value between the first time I call the function, and the second time, I will get the fresh value. Or is it not guaranteed that every time I enter the function all caching registers are cleared?

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Maestro
  • 9,046
  • 15
  • 83
  • 116
  • when in doubt, disassemble and see what the compiler did. doesnt mean it always will or wont if you change any of the code, have to re-check every time you compile and/or just make it volatile. – old_timer Feb 01 '13 at 04:00
  • If `volatile` is too inefficient consider memory barriers. – starblue Feb 02 '13 at 16:07

5 Answers5

8

You would still need to mark your variable volatile: since the optimizer is free to inline your functions, especially the short ones, calling your function in a loop without a volatile mark for accessing hardware-modified memory would place you in danger of not reading the memory after the initial iteration.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • But when I would mark my function as 'not-inlineable' is it guaranteed by C that it gets a fresh copy? – Maestro Feb 01 '13 at 01:45
  • @Joshua I doubt that there is a guarantee like that in the standard, yet I cannot think of a way around it: if the function is not inlinable and there is a reference of an external variable, loading of the value needs to happen somehow, so there will be a read. – Sergey Kalinichenko Feb 01 '13 at 01:51
  • Maybe the compiler would recycle some register/stack parts when it notices the function is called within a loop, but I'm happy their not that smart ;) – Maestro Feb 01 '13 at 01:58
  • Whether the function gets inlined or not doesn't matter, because the "missing volatile" bug is more fundamental than that: the optimizer may change the meaning of the whole code. So this doesn't answer the question. – Lundin Feb 01 '13 at 07:59
  • 1
    @Lundin Why, it most certainly does answer the "may I skip volatile" question negatively, giving a perfectly good reason (inlining) for it. – Sergey Kalinichenko Feb 01 '13 at 11:19
  • @dasblinkenlight The answer doesn't address the root problem, inlining is just one of many optimization issues that could be dangerous. If the missing volatile bug appears it will have far more drastic concequences, as illustrated in my own example. Inlining or not can only be a concern if the compiler performs no other optimization than the inlining itself. -> – Lundin Feb 01 '13 at 11:45
  • And in that case, inlining or not only matters if the function uses CPU registers or local stack to store the results; it may as well store them on the stack in the caller, or in a register that is preserved between calls: that is completely platform and calling convention-dependent. – Lundin Feb 01 '13 at 11:46
  • @Lundin There is a big difference between not answering a question, and answering the question 100% right, but providing an explanation that is perfectly valid but perhaps somewhat less complete than you would have liked. Inlining is only one example when things may break, but it is a solid, self-explanatory example. You provide a different example, and it is also a good one. I'm sure there are more examples, it does not matter. What matters, though, is that a reader of any of the answers on this page will get a solid advise to always use `volatile` everywhere a read must be forced. – Sergey Kalinichenko Feb 01 '13 at 13:23
5

...because its value will be read every time I enter the function?

No, there is no guarantee for this. The problem with the "lack of volatile bug" is that the compiler's optimizer, unaware of that a certain variable can get changed from an external source, changes the whole meaning of the code.

So if you have this:

static int x=0;

int func (void)
{
  if(x == 0)
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

interrupt void isr (void)
{
  x = SOMETHING;
}

Then the compiler will ponder: "Hmm, x is never modified anywhere, since "isr" is never called from the program. So x is always 0, I'll optimize the code to this:"

int func (void)
{
  return 1;
}

And then, perhaps, it will inline the whole function. Whether that happens or not is of no consequence, since the meaning of the code is already destroyed in the previous optimization step.

Any variable shared with an interrupt (or a thread, or DMA, or a hardware register, or a callback function) must be declared as volatile, always.

Lundin
  • 195,001
  • 40
  • 254
  • 396
2

ANY access to a hardware register is best to be marked volatile. The compiler doesn't know that it will be changed via interrupt or DMA from the hardware and the compiler can and will assume it doesn't so it can and will cache certain values.

Essentially if it's hardware mapped or can be changed via interrupt (from hardware) mark it volatile.

Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88
2

In addition to marking the variable volatile to force a load (as @dasblinkenlight suggests), you should also take steps to ensure that the variable is read (and written) atomically. On some platforms for certain sized objects (like 32-bit values on recent x86 processors), this happens automatically. In general, you might need to put a synchronization lock around the variable, like a mutex or a semaphore. This is relatively easy to do when the asynchronous code is a thread. I'm not sure what to do when there is a true interrupt involved, as some synchronization techniques might not be possible. Your platform documentation should provide some insight here.

Randall Cook
  • 6,728
  • 6
  • 33
  • 68
  • It's a 32-bit ARM processor, and in the documentation I read that all 32-bit acceses are automaticly atomic. So I'm in luck, because I don't know how I would implement mutexes on this simple MCU. – Maestro Feb 01 '13 at 01:54
  • Cool. Sounds like you're all set. Good luck. – Randall Cook Feb 01 '13 at 02:00
  • Mutexes, and other OS calls that can block, cannot be used in interrupt handlers. If an RTOS is being used, the usual way of notifying a waiting thread from an interupt handler is by a semaphore signal, not some volatile flag. – Martin James Feb 01 '13 at 11:23
0

All shared memory should be declared volatile.

You may or may not be right that your particular compiler will not optimise out a read in a particular example, but in that case the volatile keyword adds zero overhead (i.e. there is no overhead in specifying an explicit read where it was going to happen in any case), so why risk undefined or non-portable behaviour?

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • How should one treat memory which will be shared at certain discrete points during code execution but at no other times? For example, on a platform where DMA can output data faster than a CPU-driven loop, the most efficient way to write an fread/fwrite-style function may be to perform DMA directly to/from the caller's buffer, but it would be very awkward for such functions to require that the caller supply a `volatile` object. While code could copy the data to a `volatile` buffer before using DMA, that would negate the advantage of using DMA in the first place. – supercat Feb 26 '17 at 17:37
  • @superat : That is a very different and far more complex question. You should post a question of your own rather than add a comment to an old and barely relevant answer to someone else's question.. – Clifford Feb 27 '17 at 07:43