Is it necessary to use volatile
when writing to hardware (say a FIFO) in C or C++. It is easy to confirm from online documentation that volatile
is necessary when reading hardware, but what about writing? I am concerned that an optimiser could eliminate a loop to write an array of values to a FIFO, and just write the last entry.
Just try it.
#define MYFIFOV (*((volatile unsigned char *)0x1000000))
#define MYFIFO (*((unsigned char *)0x1000000))
void funv ( void )
{
MYFIFOV=0;
MYFIFOV=0;
}
void fun ( void )
{
MYFIFO=0;
MYFIFO=0;
}
00000000 <funv>:
0: e3a03401 mov r3, #16777216 ; 0x1000000
4: e3a02000 mov r2, #0
8: e5c32000 strb r2, [r3]
c: e5c32000 strb r2, [r3]
10: e12fff1e bx lr
00000014 <fun>:
14: e3a03401 mov r3, #16777216 ; 0x1000000
18: e3a02000 mov r2, #0
1c: e5c32000 strb r2, [r3]
20: e12fff1e bx lr
strb means store byte. Without the volatile one of the writes was optimized out. So yes without volatile, writes can be optimized out. How and when the compiler decides to do it can vary. But assume it can happen and as a result cause you problems.
Is it necessary to use volatile when writing to hardware
Typically, yes.
I am concerned that an optimiser could eliminate a loop to write an array of values to a FIFO, and just write the last entry.
Your concern is valid. An optimiser may indeed perform such elimination given a non-volatile object. In fact, if it can prove that the written value will never be read, then it might eliminate all writes entirely.
Here is quote from C++ standard (latest draft):
[intro.abstract]
The semantic descriptions in this document define a parameterized nondeterministic abstract machine. This document places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.
A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine with the same program and the same input. ...
The least requirements on a conforming implementation are:
- Accesses through volatile glvalues are evaluated strictly according to the rules of the abstract machine.
- At program termination, all data written into files shall be identical to one of the possible results that execution of the program according to the abstract semantics would have produced.
- The input and output dynamics of interactive devices shall take place in such a fashion that prompting output is actually delivered before a program waits for input.
What constitutes an interactive device is implementation-defined.
These collectively are referred to as the observable behavior of the program.
Yes, you have to use volatile
.
From the C11 Standard, 5.1.2.3 Program execution - paragraph 4:
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced ....
When you do not use volatile
, the compiler may assume there will be no useful side effects and remove the writes.
According to the C standard (C99 §6.7.3 footnote 106, page 109 here):
A
volatile
declaration may be used to describe an object corresponding to a memory-mapped input/output port or an object accessed by an asynchronously interrupting function. Actions on objects so declared shall not be ‘‘optimized out’’ by an implementation or reordered except as permitted by the rules for evaluating expressions.
If you are working with a compliant C compiler then you are right in assuming that it would be necessary to use volatile
when writing to memory-mapped hardware.
Depending on the specific machine and compiler you are working on, volatile
the usage of volatile
might, at most, be redundant:
An implementation might define a one-to-one correspondence between abstract and actual semantics: at every sequence point, the values of the actual objects would agree with those specified by the abstract semantics. The keyword
volatile
would then be redundant.
As per C++, according to the latest draft:
Note 5:
volatile
is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note
© 2022 - 2024 — McMap. All rights reserved.
-O3
and novolatile
remove all writes except the last one when you write to the same address in a loop and don't read from it. The compiler is allowed to do that, but i am not 100% sure gcc does that. – Midships