Volatile variables

Go To Last Post
24 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

Cliff, I often notice this, but it always puzzles me.  It may be good advice, but I don't think all ISR/main shared variables MUST be defined volatile. 

 

In fact I don't think my task switcher would work if that was true.  So I decided to think about it, but just for a short time.  smiley  Here's my theory:

 

If you want a function to repeatedly fetch the variable, then you need to make it volatile.  But if you want the function to fetch it only once, then it doesn't need to be volatile.

 

Anybody agree?

Last Edited: Fri. Mar 13, 2015 - 01:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anybody agree?

No.  At least not if GCC is your toolchain.

 

Post an example...

 

Without volatile, GCC's optimizer may very well look at an assignment and determine that it really isn't worth doing the store 'cause apparently the result isn't used anywhere.

 

First, read this Tutorial, and I'll be glad to look at a counter-example if you come up with one after reading that.

https://www.avrfreaks.net/forum/t...

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't  understand.  What assignment?  And what store?  Of course I use the variable I fetch.

 

Maybe I wasn't clear about the scenario.  I could have said it this way.  If a function repeatedly fetches the same variable before exiting, the variable needs to be volatile.  If the function fetches it once and then exits, it does not need to be volatile.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I said:

 

-- Read that article.  I think section 2) is the pertinent one.

-- Post a counter-example.  I'll be happy to look at it and discuss.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't use C but I'll try to come close.   I assume in the following, that flag is a global.  That is, it can be accessed by other code including interrupt handlers.


bool flag = false;

bool Fetch_flag()   {
       return flag;
}

What will Fetch_flag return?  I assume it will return the value of flag.  The value of flag could be set by an interrupt handler.  Does the C compiler know or care?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry Lee, but I believe Steve is correct. The scenario being discussed is main() calls a function that reads the variable once and only once. In that case, the value could not have been cached in a register since the function does not preserve local values between calls. Therefore the first access of that variable in that function must read the actual current value of that variable since there is no place else to get that value from. Only if there are more than one read of that variable could the compiler say "I already know that value, so I don't have to read it again". However, this argument only holds true if you can ensure that the function has not been inlined. If that is the case, then all bets are off.

Does the C compiler know or care?

In this example it is very possible that such a simple function might be inlined, so as stated above, this might not work as you expect. However if you put that function in a different file than where it is called from, it should be fine without the volatile. But the benefits of not using volatile in this case is exactly 0.

Without volatile, GCC's optimizer may very well look at an assignment and determine that it really isn't worth doing the store 'cause apparently the result isn't used anywhere.

That is an argument for writing to a variable. If a read value really isn't used, then there is no harm the compiler can do by eliminating the assignment. Where it would be a problem is when "variable" is really some hardware register that is affected (and possibly change its value) simply by accessing it.

 

So I do believe that there are some rather limited circumstances that volatile is not necessary, but those would be few and far between.

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Wed. Mar 11, 2015 - 08:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I do believe that there are some rather limited circumstances that volatile is not necessary, but those would be few and far between.

Not something I would risk unless you had some kind of code analyzer that said "yup, I've inspected that, you haven't made any mistakes, it's only accessed once, so on this occasion it is safe to drop volatile".

 

But what does it gain you anyway? The first (only) access is bound to be an LDS anyway (or similar), because it's a global, so does it make it more "efficient" by not having it "volatile"? If not why risk a mistake?

Last Edited: Wed. Mar 11, 2015 - 08:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did not say that there is any real reason to not make it volatile, just that there are some circumstances that it might not be strictly necessary. The only time I could see that it might make code more efficient is when some places in the code need the current value of the variable on every read, but other places you only want the current value on the first read. But the way to do that is to use volatile and then do the caching yourself by putting that first read into a local variable.

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Wed. Mar 11, 2015 - 08:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I do believe that there are some rather limited circumstances that volatile is not necessary, but those would be few and far between.

As Cliff said:  Why risk it?  [Now I have to test your fragment--as "flag" is global, the compiler might think it knows the value...just as in Cliff's Tutorial example with "count".]

 

The only time I could see that it might make code more efficient is when some places in the code need the current value of the variable on every read, but other places you only want the current value on the first read.

Yes, just like being aware of "volatile" being needed, the programmer needs to use it wisely.  (Wasn't there a link recently to a discussion of "the dangers of volatile"?)

 

Consider the below fragment, where "value" is a volatile unsigned char, and the if test is meant to be exhaustive.

 

if (value < LOW)
    {
        fill_valve = ON;
        overflow_valve = OFF;
    }
else if ((value >= LOW) && (value <= HIGH))
    {
        fill_valve = OFF;
        overflow_valve = OFF;
    }
else if (value > HIGH)
    {
        fill_valve = OFF;
        overflow_valve = ON;
    }

Contrived, yes, but if volatile "value" changes then it is possible that no path is taken.

 

Also, (and IME perhaps more subtle to find,) is atomicity considerations.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry Lee, but I believe Steve is correct. The scenario being discussed is main() calls a function that reads the variable once and only once. In that case, the value could not have been cached in a register since the function does not preserve local values between calls. Therefore the first access of that variable in that function must read the actual current value of that variable since there is no place else to get that value from. Only if there are more than one read of that variable could the compiler say "I already know that value, so I don't have to read it again". However, this argument only holds true if you can ensure that the function has not been inlined. If that is the case, then all bets are off.

The below test shows the compiler inlining the function, and only doing one read even though it is in a loop.  "volatile needed", right?

 

//---Includes
#include <avr/io.h>                    // Include AVR device-specific IO definitions
#include <avr/interrupt.h>

unsigned char flag;
unsigned char my_flag;


unsigned char Fetch_flag()   {
	return flag;
}
int main(void) 
{
for (unsigned char i = 0; i < 10; i++)	
	{
	my_flag = Fetch_flag();
	}

	PORTB = my_flag;
	while(1);
	return 0;
}

ISR(TIMER0_OVF_vect) {
	flag++;
}

00000048 <main>:
unsigned char flag;
unsigned char my_flag;


unsigned char Fetch_flag()   {
	return flag;
  48:	80 91 60 00 	lds	r24, 0x0060
  4c:	80 93 61 00 	sts	0x0061, r24
for (unsigned char i = 0; i < 10; i++)	
	{
	my_flag = Fetch_flag();
	}

	PORTB = my_flag;
  50:	88 bb       	out	0x18, r24	; 24
  52:	ff cf       	rjmp	.-2      	; 0x52 <main+0xa>

00000054 <__vector_6>:
	while(1);
	return 0;
}

ISR(TIMER0_OVF_vect) {
  54:	1f 92       	push	r1
  56:	0f 92       	push	r0
  58:	0f b6       	in	r0, 0x3f	; 63
  5a:	0f 92       	push	r0
  5c:	11 24       	eor	r1, r1
  5e:	8f 93       	push	r24
	flag++;
  60:	80 91 60 00 	lds	r24, 0x0060
  64:	8f 5f       	subi	r24, 0xFF	; 255
  66:	80 93 60 00 	sts	0x0060, r24
}
  6a:	8f 91       	pop	r24
  6c:	0f 90       	pop	r0
  6e:	0f be       	out	0x3f, r0	; 63
  70:	0f 90       	pop	r0
  72:	1f 90       	pop	r1
  74:	18 95       	reti

With "volatile unsigned char flag;", it is read each loop iteration.

 

So, is Steve still correct?

 

Now, with "noinline" you are safe; see below.  So is Steve right or not?  In the end it seems easier to follow Cliff's FAQ#2.

 

00000048 <Fetch_flag>:
unsigned char my_flag;


__attribute__ ((noinline)) unsigned char Fetch_flag()   {
	return flag;
}
  48:	80 91 60 00 	lds	r24, 0x0060
  4c:	08 95       	ret

0000004e <main>:
int main(void) 
{
  4e:	ca e0       	ldi	r28, 0x0A	; 10
for (unsigned char i = 0; i < 10; i++)	
	{
	my_flag = Fetch_flag();
  50:	fb df       	rcall	.-10     	; 0x48 <Fetch_flag>
  52:	80 93 61 00 	sts	0x0061, r24
  56:	c1 50       	subi	r28, 0x01	; 1
__attribute__ ((noinline)) unsigned char Fetch_flag()   {
	return flag;
}
int main(void) 
{
for (unsigned char i = 0; i < 10; i++)	
  58:	d9 f7       	brne	.-10     	; 0x50 <main+0x2>
	{
	my_flag = Fetch_flag();
	}

	PORTB = my_flag;
  5a:	88 bb       	out	0x18, r24	; 24
  5c:	ff cf       	rjmp	.-2      	; 0x5c <main+0xe>

0000005e <__vector_6>:
	while(1);
	return 0;
}

ISR(TIMER0_OVF_vect) {
  5e:	1f 92       	push	r1
  60:	0f 92       	push	r0
  62:	0f b6       	in	r0, 0x3f	; 63
  64:	0f 92       	push	r0
  66:	11 24       	eor	r1, r1
  68:	8f 93       	push	r24
	flag++;
  6a:	80 91 60 00 	lds	r24, 0x0060
  6e:	8f 5f       	subi	r24, 0xFF	; 255
  70:	80 93 60 00 	sts	0x0060, r24
}
  74:	8f 91       	pop	r24
  76:	0f 90       	pop	r0
  78:	0f be       	out	0x3f, r0	; 63
  7a:	0f 90       	pop	r0
  7c:	1f 90       	pop	r1
  7e:	18 95       	reti

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now I have to test your fragment--as "flag" is global, the compiler might think it knows the value...just as in Cliff's Tutorial example with "count".

But for that example, the value of count is compared every time through the while loop, not read just once, and it knew the value because the variable was written to within the same function with a constant known a compile time. In Steve's example there is no way that the compiler can assume what the value of "flag" is within the function (assuming, again, that the function is not inlined and that whole program optimization is not turned on).

So, is Steve still correct?

As I said, you have to ensure that the function is not inlined. Put Fetch_Flag into another .c file and see what happens.

In the end it seems easier to follow Cliff's FAQ#2.

As I said, I did not say that I would recommend it, just that there are circumstances where it is not technically necessary.

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Wed. Mar 11, 2015 - 09:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess it does need volatile in your above case.  The control never leaves main, if you ignore interrupts, which is what gcc does.  That's a pretty simple program, which is why Cliff's volatile rule is good.  But what about code not so simple.  I really don't know in this case.

 

Consider this code which could be called a task switcher that has been simplified to the point of absurdity.

 


   bool flag;

   while(true)   {
      Some_other_function();
      if(flag)   {
         .
         .
         .
         }
      else   {
         .
         .
         .
         }
      }

If Some_other_function() is in another compilation unit, would the code refetch the flag?  Does gcc find Some_other_function() in another compilation unit to see if it could change the flag?   I'm guessing no, so it would refetch the flag just in case.

 

If Some_other_function() is in the same compilation unit, would the code refetch the flag?  I wouldn't hazard a guess in this case.  It might look at Some_other_function() to see if it could change the flag.

Last Edited: Wed. Mar 11, 2015 - 09:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

*What have Posts #27 and all subsequent ones got to do with the original topic? I think they should be split off to their own thread. Agree?*

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kicking off another thread wander - "bool"? really?

[steve17 did say that he wasn't a C person, so I treated it as pseudocode.  But anyway...]

Get into the new millennium, Cliff...

 

 

2 An object declared as type _Bool is large enough to store the values 0 and 1
 

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Thu. Mar 12, 2015 - 02:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does gcc find Some_other_function() in another compilation unit to see if it could change the flag?

No.

If Some_other_function() is in another compilation unit, would the code refetch the flag?

Probably not. The purpose of 'volatile' is to tell the compiler that the value might change outside the current thread. Some_other_function() is inside the current thread, so "volatility" optimization considerations would likely not be applied.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

valusoft wrote:

*What have Posts #27 and all subsequent ones got to do with the original topic? I think they should be split off to their own thread. Agree?*

Splitting it off is okay with me.  Probably a good idea.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

And while I'm posting, @Steve17, in the example you just posted what do you think is gained by NOT making flag volatile?

Well it seems, probably nothing.  On the other hand, if I forget, it's nice to know it might work anyway.  smiley

 

clawson wrote:

Kicking off another thread wander - "bool"? really?

C++ lingo.

 

One last thing might be worth mentioning.  I think theusch touched on it.  If the variable is multi-byte, like an avr-gcc int, then you need to disable interrupts too, because 8 bit AVRs access one byte at a time.  This bug is a nasty one because it depends on precise timing.  Testing won't likely find it, but the customer probably will encounter it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On the other hand, if I forget, it's nice to know it might work anyway.

Say, what?!?  It only "works" in your contrived case, and then only if the noinline was added.  If you have taken that many precautions, how could you "forget?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

On the other hand, if I forget, it's nice to know it might work anyway.

Say, what?!?  It only "works" in your contrived case, and then only if the noinline was added.  If you have taken that many precautions, how could you "forget?

A contrived case is in the mind of the beholder.  Having a function repeatedly reading the variable seems to be the only case where it is guaranteed to fail.  The only place I do that is reading a peripheral register waiting for it to become ready.  It is a bug that is easy to find and fix.  I learned long ago to have my peripheral registers marked as volatile.  I guess you could say the peripherals run in their own threads.  The will set bits in their registers any damned time they feel like it.

 

I don't have a function that repeatedly reads a value set by an interrupt handler.  That is done in the task switcher.  The function that does it could be inlined though.  Maybe the reason it works without volatile is it calls a function not in the compilation unit.

Last Edited: Thu. Mar 12, 2015 - 07:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I learned long ago to have my peripheral registers marked as volatile. 

??? What "peripheral register" is this?  If indeed AVR8 and GCC, all the I/O registers are already volatile.

 

I suppose if you are a C++ person, everything is a function.  For my simpleminded C approach, I'd never have a one-line function to fetch/check a register.  But then again, I don't count on "inline".

 

Certainly a contrived example in the light of all of my AVR8 C work.  YMMV. 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

, I'd never have a one-line function to fetch/check a register.  But then again, I don't count on "inline".

Why do you think the function is one-line?  And *an inflamatory question that shouldn't be asked*

Last Edited: Fri. Mar 13, 2015 - 01:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for removing the inflammation.  I had a bad day at the office.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why do you think the function is one-line? 

Because of your posted example.  I inferred from that and the later C++ mention that it was a simple accessor function.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

Because of your posted example.  I inferred from that and the later C++ mention that it was a simple accessor function.

Yes I can see that.  The example was just to show a function that fetched the flag once.  I didn't want to add any extraneous code so it did indeed looked like an accessor function.  Accessor functions do have a good use.  It's the way a class can fetch data that is in another class.  The alternative would be to make the data public but that would allow any code to not only read it but to also change it.  That would defeat the purpose of classes.  Anyway the function in my task switcher doesn't need an accessor function here.

 

They tell me if the function is inlined, it is efficient.  I put these functions, and almost all of my functions, in the class declaration.  This makes them candidates for inlining, and avr-gcc seems to inline nearly all, if not all, of them.