Is is necessary to use volatile when writing to hardware in C or C++?
Asked Answered
T

4

16

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.

Thoer answered 15/10, 2020 at 15:4 Comment(9)
When you say "writing to hardware", you mean something like MMIO? I.E. something that is always at the same address? Is this why you are concerned that consecutive writes could be optimized to only the last one?Splenic
Yes it is necessary. Otherwise the compiler may optimize out everything and not even keep the last write.Midships
@MarcoBonelli: I do indeed mean writing to a single address in an FPGA which is a letterbox. I write successive values to this address, which the FPGA interprets and acts on. Many other microprocessor connected I/O chips work in the same way, and lots on peripheral devices integrated into modern processors and DSPsThoer
@Midships I mean... not necessarily that. MMIO and PMIO are a thing.Splenic
@12431234123412341234123: Can you quote a source for your statment? I checked K&R, Modern C, Practical C, C in a Nutshell, Misra 2004 and probably others with no success.Thoer
Did you test it? I assume gcc will, when you use -O3 and no volatile 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
C11 5.1.2.3 Program execution - 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 The compiler assumes there will be no side effect when you do not use volatile.Midships
if other code on other targets happens to work without volatile that is either how you are building the code or dumb luck. I prefer to use an abstraction, call a function, you can if you want then inline and volatile pointer that function but you have the option and can also build with without volatile, no guessing as to when volatile is going to work and how (it doesnt work as you would like all the time).Parthen
C is not C++, this are 2 different languages. Please do not ask such questions for 2 different languages at the same time since they can be different. (They are not in this case, but if you had known that you wouldn't had ask this question).Midships
P
13

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.

Parthen answered 15/10, 2020 at 15:52 Comment(0)
B
5

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:

  1. Accesses through volatile glvalues are evaluated strictly according to the rules of the abstract machine.
  2. 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.
  3. 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.

Brittain answered 15/10, 2020 at 15:33 Comment(0)
M
3

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.

Midships answered 15/10, 2020 at 15:39 Comment(0)
S
1

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

Splenic answered 15/10, 2020 at 15:44 Comment(1)
Note that notes are not normative, and contradicting them technically does not make a language implementation non-conforming.Brittain

© 2022 - 2024 — McMap. All rights reserved.