2

My question is targeted towards the embedded development, specifically STM32.

I am well aware of the fact that the use of volatile qualifier for a variable is crucial when dealing with a program with interrupt service routines (ISR) in order to prevent the compiler optimising out a variable that is used in both the ISR and the main thread.

In Atollic TrueSTUDIO one can turn off the GCC optimisations with the -O0 flag before the compilation. The question is, whether it is absolutely necessary to use the volatile qualifier for variables that are used inside and outside the ISR, even when the optimisations are turned off like this.

ManoriN
  • 56
  • 6
  • 1
    Answer is not required.But good practice to use volatile for required variable – Babajan Jul 07 '20 at 10:25
  • 1
    One day you'll turn optimizations back on and then you'll be glad you used it. – user253751 Jul 07 '20 at 10:29
  • Why oh why would you rely on the compiler optimization level for your code to work properly? This just doesn't make any sense. Do you think `-O0` without `volatile` will produce faster code? (hint: no, even `-O1` or `-Og` will be noticeably faster). What you need to do is turn on absolutely all warnings and set them to be reported as errors (`-Wall -Wextra -Werror`, possibly any additional warning not covered with these like `-Winit-self -Wshadow -Wstrict-aliasing`), and then simply write correct, portable code. – vgru Jul 07 '20 at 12:28

3 Answers3

4

With optimizations disabled it seems unlikely that you'd need volatile. However, the compiler can do trivial optimizations even at O0. For example it might remove parts of the code that it can deduct won't be used. So not using volatile will be a gamble. I see no reason why you shouldn't be using volatile, particularly not if you run with no optimizations on anyway.

Also, regardless of optimization level, variables may be pre-fetch cached on high end MCUs with data cache. Whether volatile solves/should solve this is debatable, however.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks for your answer. That's what exactly I was looking for. – ManoriN Jul 07 '20 at 11:27
  • this STM32 where caching is the least important problem unless you use DMA access and concurrently core is accessing the same data itself – 0___________ Jul 07 '20 at 13:03
  • @P__J__ If you say so - there's a whole lot of different STM32s with different cores up to at least Cortex M7 iirc. – Lundin Jul 07 '20 at 13:34
1

“Programs must be written for people to read, and only incidentally for machines to execute.”

I think here We can use this quote. Imagine a situation (as user253751 mentioned) you remove keyword volatile from every variable because there is optimization enabled. Then few months later you have to turn optimization on. Do you imagine what a disaster happened?

In addition, I work with code where there is an abstraction layer above bare-metal firmware and there we use volatile keyword when variable share memory space between those layers to be sure that we use exact proper value. So there this another usage of volatile not only in ISRs, that means there is not easy to change this back and be sure that everything works ok.

Debugging code where variable should be volatile is not so hard but bugs like this looks like something magic happened and you don't know why because for example something happened one in 10k execution of that part of code.

Summary: There is no strict "ban" for removing volatile keyword when optimization is turned off but for me is VERY bad programming practice.

dunajski
  • 381
  • 3
  • 15
0

I am well aware of the fact that the use of volatile qualifier for a variable is crucial when dealing with a program with interrupt service routines (ISR) in order to prevent the compiler optimising out a variable that is used in both the ISR and the main thread.

You should actually keep in mind that volatile is not a synchronization construct.

It does not force any barriers, and does not prevent reordering with other non-volatile variables. It only tells the compiler not to reorder the specific access relative to other volatile variables -- and even then gives no guarantees that the variables won't be reordered by the CPU.

That's why GCC will happily compile this:

volatile int value = 0; 
int old_value = 0;     

void swap(void)
{
    old_value = value;
    value = 100;
}

to something like:

// value will be changed before old_value
mov eax, value
mov value, 100
mov old_value, eax

So if your function uses a volatile to signal something like "everything else has been written up to this point", keep in mind that it might not be enough.

Additionally, if you are writing for a multi-core microcontroller, reordering of instructions done by the CPU will render the volatile keyword completely useless.

In other words, the correct approach for dealing with synchronization is to use what the C standard is telling you to use, i.e. the <stdatomic.h> library. Constructs provided by this library are guaranteed to be portable and emit all necessary compiler and CPU barriers.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • "It does not force any barriers" This isn't really clear, see https://stackoverflow.com/questions/58695320/does-volatile-guarantee-anything-at-all-in-portable-c-code-for-multi-core-syst/58697222#58697222. In particular, nothing in the C standard indicates that a conforming implementation may re-order non-volatile variables across a volatile access. Rather the opposite: "In the abstract machine, all expressions are evaluated as specified by the semantics." /--/ "Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine." – Lundin Jul 07 '20 at 13:39
  • Particularly given the old, clear C99 definition of sequence points: "Evaluation of an expression may produce side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place." A semicolon is a sequence point, for example. – Lundin Jul 07 '20 at 13:42
  • Additionally, it seems quite clear that the OP isn't using volatile for the sake of achieving atomic access, or even for memory barriers, but to prevent incorrect optimizations (see [this](https://electronics.stackexchange.com/a/409570/6102), 2nd bullet), so a large portion of this answer is irrelevant. – Lundin Jul 07 '20 at 13:46
  • @Lundin: thanks for the remarks, I believe you already wrote something like this on a similar answer I posted. Yes, GCC is likely not conforming and their interpretation hardly makes sense, but since OP specifically tagged the question GCC I thought I might mention it. – vgru Jul 07 '20 at 13:59
  • However, I didn't find it "quite clear" that OP isn't using volatile for synchronization; usage of the same variable inside two different interrupts implies some sort of synchronization. Since they are asking if `volatile` is needed with `-O0`, I understand the underlying question as "will the order of my operations remain in program order if I remove `volatile` at `-O0`". That's why I wanted to point out that with GCC it might not be in order even *with* the `volatile` keyword. – vgru Jul 07 '20 at 14:00
  • Since this is tagged embedded, it's most likely the reason mentioned in my 2nd link, because at least historically, you can't safely interact with ISRs without using volatile. Memory barrier/prefetch protection may or may not be an issue as well. – Lundin Jul 07 '20 at 14:05
  • @Groo Thank your for your remarks. The intent of the question was to determine whether `arm-none-eabi` GCC optimises out a volatile variable inside an ISR in case the compilation is done with `-O0`. – ManoriN Jul 08 '20 at 10:17
  • @Lundin: On MSVC or compilers that attempt to be compatible with it, a `volatile` object can be used as a mutex to guard non-qualified objects, without need for non-standard syntax, at least when all accesses to an object will be performed via cache-consistent cores. I find bizarre the notion that the authors of the Standard intended that quality implementations designed for low-level programming should require the use of special syntax to create even the most basic mutex on a single-core machine. – supercat Jul 20 '20 at 22:51