volatile attribute

Hello, All!

Could you please explain me the meaning of 'volatile' keyword when defining variables? I don't quite understand this :(

With best regards, Roman Mashak. E-mail: snipped-for-privacy@tusur.ru

Reply to
Roman Mashak
Loading thread data ...

formatting link

formatting link

Reply to
Guy Macon

Hi,

Please read this:

formatting link

Al

Reply to
Al Borowski

defining

Use volatile when you don't want the compiler to make assumptions about a variable. The compiler will always fetch the variable rather than keeping a copy in a register. It's used for monitoring I/O ports or variables that might be changed by other processes.

Peter

--
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.779 / Virus Database: 526 - Release Date: 19/10/04
Reply to
moocowmoo

defining

It tells the compiler that the variable can change outside of the predictable program flow - it cannot therefore make any assumptions as to its value when optimising. The variable must be loaded from memory each time it is used.

Examples: A variable mapped to a register (where hardware can change the register value), a variable that can get altered from an interrupt service routine, a variable that can get altered by more than one task in a multitasking system.

Regards, Richard.

formatting link

Reply to
Richard

Hi,

the volatile attribute is an indication that the variable can change for example on account of an interrupt.

With this indication the compiler cannot use a register to store the variable, or do any type of optimization.

For example if a volatile variable is not used, the compiler do not remove it, as it could be used by an interrupt

bye giammy

Reply to
Gianluca Moro

In simplest terms, it tells the compiler that you know something about the variable that it doesn't, so it should do exactly what you tell it to do with it, no more, and no less. More specifically, it tells the compiler that access to the variable may have side effects it's not aware of, and that the value could change by means not visible to the compiler.

Example: at a PPOE I worked with the Zilog 8530 UART, which has two addressable registers, the (transmit or receive) data register and the control register. Internally it has (IIRC) 16 writable and something like 8 readable registers. You access the internal registers by writing their address to the control register, then reading or writing the control register a second time.

For example, to write 0 to register 1 and then read register 2, you would do something like

CR = 1; CR = 0; CR = 2; x = CR;

If CR is not declared volatile, the compiler's optimizer is free to reduce this sequence to the equivalent of

CR = 2; x = 2;

Which is unlikely to be what you want. The compiler sees the consecutive writes to CR as redundant and unnecessary, and since it just wrote a 2 into CR, that must what is stored there, so there's no need to read it.

The volatile keyword forces the compiler to generate code that performs all 4 accesses to the control register.

HTH,

-=Dave

--
Change is inevitable, progress is not.
Reply to
Dave Hansen

snipped-for-privacy@yahoo.com (Gianluca Moro) wrote in news: snipped-for-privacy@posting.google.com:

Volatile is a keyword in C.

Or when you create a pointer to a volatile "thing" that is, in fact, memory mapped hardware. If the hardware updates the "thing" and the compiler knows the variable is volatile then things should work as you expect. This is not a requirement of ISO C which specifies the behavior of volatile variables in a very restricted way.

No, it can use a register but it must reload the register with the source location every time you access the volatile variable.

That's a consequence of volatile.

--
- Mark ->
--
Reply to
Mark A. Odell

Is this "fetch the variable" somehow obligatory? Quite many architectures have opcodes for directly modifying a memory place, for instance clearing/setting a bit in a memory place which really is a I/O-port....

What the GNU C compiler does in the 'volatile' case :

  1. load the data from the memory place into a CPU register 2. modify it 3. write it back

instead of doing the same thing as with a non-volatile variable:

a. modify the data directly in the memory place

looks being simply wrong. If an interrupt happens between the steps 1 and 3 and the ISR for it changes the same memory address, for instance changing some other bit there, the write back will replace that bit to its original value... Whether this kind of thing ever would be needed, is of course purely theoretical....

If knowing this "feature" in GCC, one can always try to disable interrupts during the steps 1-3, but using the opcode 'a' could be assumed to be 'atomic' and not being possible to interrupt when executed... That handling volatiles isn't atomic may be a surprise for newbies, and a nuisance for those wanting as small code as possible, three opcodes generally takes more code memory than that one opcode.

Reply to
Kai Ruottu

Does it? On what architecture, in what version of GCC? I'd like to see an actual example of this behaviour.

Not necessarily. You're mixing up two things here: volatility and atomicity.

It may go contrary to common wisdom, but since C has no concept of multithreading in its language definition, flagging an object as volatile does *not* guarantee thread-safety through atomic accesses. That's not its intended usage, nor is the above difference actually the one 'volatile' is supposed to cause. What volatile is supposed to do, in this particular context, is forbid the compiler from *keeping* the value in a register across any sequence point. I.e. for normal objects, in the above nomenclature, a sequence of

  1. 2. 2. 2. 2. 2. 3. 2. 2. 2. 3.

would be allowed, but for volatiles, that's expressly forbidden.

In fact, the only way C even tries to handle atomicity and thread-safety is with and the type sigatomic_t defined in there.

--
Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.
Reply to
Hans-Bernhard Broeker

Don't you mean "What the GNU C compiler does on the one particular processor architecture that I happen to be using"?

formatting link
formatting link
formatting link

--
Guy Macon
http://www.guymacon.com
Reply to
Guy Macon

No, all 'volatile' means is that the compiler should read it afresh each time it uses it, instead of reading it once and holding it in a register if it's accessed several times.

No language standard could call for atomicity in specific places such as IO, simply because you can't guarantee that the feature exists in the target architecture. And even if you could, the behaviour can be wildly different- for some ports the read reflects the actual value on the IO pin, for others the value of the output register, for yet more the port might have no read facility at all and return rubbish.

Paul Burke

Reply to
Paul Burke

H8/300, H8/500, m68k, v850,... all these have opcodes to "directly set/clear a bit in a memory place whose address is given in the opcode or in a CPU register". And GCC really handles these operations with these expected opcodes when handling non-volatile objects... How these opcodes work internally, maybe copying the data into some invisible register, then modifying it and writing back, isn't important, only that none of the visible CPU registers are used.

All GCCs since 1995 or so behave this way... Maybe also earlier ones but in 1995 RMS & Co. seem to have last done something with the 'volatile_ok' variable in the GCC code... Quite many have reported, at least unofficially, this bug but some kind of MS-like attitude, "it is not a bug, it is a feature" has been the normal reaction. Or people start to talk about the C/C++ standards and that 'volatile' should somehow trigger the memory handling from direct to load-store...

Anyone can get an example with a code doing bit set/clear in a memory place... But here is one from the H8/300 world showing how the produced code should be :

------------------- clip ----------------------------- #define SFR __attribute__((eightbit_data))

extern volatile unsigned char SFR SCI_SSR0; extern volatile unsigned char SFR SCI_TDR0;

/* #define SCI_TDR0 (*(volatile unsigned char *) (0xffffdb)) #define SCI_SSR0 (*(volatile unsigned char *) (0xffffdc))

*/

/* Single char out to the serial port */

void put_ser(unsigned char c) { while ((SCI_SSR0 & 0x80) != 0x80) ; SCI_TDR0 = c; SCI_SSR0 &= 0x7F; }

------------------- clip ----------------------------- ; GCC For the Hitachi H8/300[HS] ; By Hitachi America Ltd and Cygnus Support ; Modified by Kai Ruottu ; release F-1.0.1 ; -O2

.file "put_ser.c" .section .text .align 1 .global _put_ser _put_ser: .L5: mov.b @_SCI_SSR0:8,r2l and #128,r2l beq .L5 mov.b r0l,@_SCI_TDR0:8 bclr #7,@_SCI_SSR0:8 rts .end

------------------- clip -----------------------------

and here are some clips from a diff between the outputs of a non-modified GCC and a by-me modified gcc-3.3.5 for m68k/cpu32 :

------------------- clip -----------------------------

*** quicc_335-1.s 2004-10-27 18:46:34.000000000 +0300

--- quicc_335-2.s 2004-10-27 14:33:46.000000000 +0300

*************** *** 6,13 **** init_channel0: move.l quicc,%a1 lea (3072,%a1),%a0 ! lea (5632,%a1),%a1 ! clr.l (%a1) clr.w (%a0) move.w #400,2(%a0) move.b #24,4(%a0)

--- 6,12 ---- init_channel0: move.l quicc,%a1 lea (3072,%a1),%a0 ! clr.l 5632(%a1) clr.w (%a0) move.w #400,2(%a0) move.b #24,4(%a0)

*************** *** 132,141 **** move.w #2570,8(%a0) move.l #384,4(%a0) move.l #277348364,(%a0) ! move.l (%a0),%d0 ! moveq.l #48,%d1 ! or.l %d1,%d0 ! move.l %d0,(%a0) rts .size ethernet_up, .-ethernet_up .align 2

--- 130,137 ---- move.w #2570,8(%a0) move.l #384,4(%a0) move.l #277348364,(%a0) ! moveq.l #48,%d0 ! or.l %d0,(%a0) rts .size ethernet_up, .-ethernet_up .align 2

*************** *** 265,276 **** extb.l %d0 moveq.l #32,%d1 and.l %d1,%d0 ! add.l quicc,%d0 ! move.l %d0,%a0 ! lea (5632,%a0),%a0 ! move.w 20(%a0),%d0 ! or.w #8,%d0 ! move.w %d0,20(%a0) rts .size quicc_finish, .-quicc_finish .align 2

--- 261,269 ---- extb.l %d0 moveq.l #32,%d1 and.l %d1,%d0 ! move.l quicc,%a0 ! add.l %d0,%a0 ! or.w #8,5652(%a0) rts .size quicc_finish, .-quicc_finish .align 2

*************** *** 529,548 **** lea RQB+6,%a0 move.w %a6,%d0 move.b %d0,(%a0,%a1.l) ! move.w (%a2),%d0 ! and.w #8192,%d0 ! move.w %d0,(%a2) tst.b %d7 jbne .L42 ! move.w (%a2),%d0 ! or.w #-32768,%d0 ! move.w %d0,(%a2) jbra .L43 .align 2 .L42: move.l %a2,%a4 clr.b %d7 .L43: move.w (%a2),%d0 and.l #8192,%d0 addq.l #1,%d5

--- 525,541 ---- lea RQB+6,%a0 move.w %a6,%d0 move.b %d0,(%a0,%a1.l) ! and.w #8192,(%a2) tst.b %d7 jbne .L42 ! or.w #-32768,(%a2) jbra .L43 .align 2 .L42: move.l %a2,%a4 clr.b %d7 .L43:

  • clr.l %d0 move.w (%a2),%d0 and.l #8192,%d0 addq.l #1,%d5
*************** *** 739,748 **** addq.l #6,%a3 addq.l #8,%a2 jbpl .L67 ! subq.l #8,%a2 ! move.w (%a2),%d0 ! or.w #8192,%d0 ! move.w %d0,(%a2) movm.l (%sp)+,#0xc0c rts .size create_tx_pool, .-create_tx_pool

--- 726,732 ---- addq.l #6,%a3 addq.l #8,%a2 jbpl .L67 ! or.w #8192,-8(%a2) movm.l (%sp)+,#0xc0c rts .size create_tx_pool, .-create_tx_pool

------------------- clip -----------------------------

In the H8/300 case compiling the:

SCI_SSR0 &= 0x7F;

to be:

bclr #7,@_SCI_SSR0:8 ('Clear bit #7 in address SCI_SSR0')

is expected but replacing it with 'mov.b' / 'and' / 'mov.b' opcodes would be very unexpected.... As the example shows, the C code translated to machine code is a quite direct 1:1 translation...

As the m68k clips show, replacing a "clear the data in the given memory address" is converted to "put the memory address into a register and use this register as a pointer when clearing the data", is another bug type besides that load-store seen also in the last clip....

My local H8/300, H8/500, m68k etc. tools use a hack which seems to fix this bug but at least one bug caused by the fix, as the example shows

.L43:

  • clr.l %d0 move.w (%a2),%d0

appears then... Clearing the target register before moving something from memory into it, is vain and without the hack this doesn't happen.

Using a hack without fully knowing what it could cause somewhere else, has however been the only way this far... Of course I have tried to understand how GCC works but it could be much simpler to get someone of the GCC-gurus to try to fix this.

Quite many 'current' CPU archs are now RISC-like, even those 8/16 bit ones (like AVR) and don't know this kind of opcodes, then there aren't any differences between non-volatiles and volatiles with them...

Maybe, I'm far from being a specialist in the C or C++ language or its standards... My 'atomicity' simply meaned something which cannot be interrupted when started... In the good old Z80 architecture there were those LDIR and LDDR 'block move' opcodes, seemingly 'atomic' but these could be interrupted during their execution, but I would be quite sure that the 'bclr', 'bset' etc. in the H8/300 architecture cannot be interrupted...

Not changing the way GCC handles I/O ports in memory places in the non-volatile and volatile cases is what ordinary people would expect, not that the handling will switch to those RISC-like load-store operations because the object is volatile....

Yes, I will understand this, but cannot understand why toggling a bit in a memory place in the H8/300 example case cannot happen as :

bset #7,@_SCI_SSR0:8 ('Set bit#7 in SCI_SSR0') bclr #7,@_SCI_SSR0:8 ('Clear bit#7 in SCI_SSR0')

but it would maybe require 6 opcodes to do the same thing.... If this would be allowed, someone of the GCC gurus could fix this current feature. So there must be something which disables handling memory places just like the CPU registers, directly, or via a pointer without first moving the data into a CPU register and then back again after modifying it.

I became aware of this feature more than 5 years ago and have nagged since that, always when there is some kind of chance... Haven't yet met an embedded person who hasn't thought this "feature" in GCC being a bug... The GCC developers must have some better knowledge.

People usually nag about GCC producing much bigger code than those old commercial C compilers and in the m68k case this "volatile bug" feature is one reason for that. The old commercial C compilers didn't switch from direct handling to load-store with volatile objects...

If enough embedded people will nag about this, maybe some day we will see it being fixed... Before all the people will stop using the H8/300, m68k, v850 etc. old (partially) CISC-type architectures, and the fixing then being too late... With RISC-type CPUs the "load-store" is the only and the standard way to do these things...

Reply to
Kai Ruottu

Let's get one thing straight, though: the above effect of 'volatile' is silly, and I'm quite sure it's also unnecessary. But

1) This is not a bug of GCC. Suboptimal code is being generated for no truly good reason, but both versions are equally correct. In other words, it's a quality-of-implementation issue. 2) This has nothing to do with atomicity or thread-safety. Even if *all* objects referred to by an expression statement were volatiles or constants, it's still not guaranteed by C standards or anything else that an interrupt somewhere during the execution of such an expression won't lead to unexpected results. Volatile does nothing towards protecting your values from modification between sequence points.

Not having the actual reports and replies to them makes it essentially impossible to judge which side was right, in that discussion. The GCC advocates you quote are right in at least one point, though: this is not a bug, because, as I pointed out, both versions of that code are exactly equally correct. It really is a (bad) feature.

The example might be more convincing if you could keep it to the realm of standard C. I.e. no __attribute__s. Arguing about the effect of standard C modifiers on manifestly non-standard objects is futile.

[...]

Unexpected and suboptimal: yes. But not strictly incorrect.

Have you considered that they may have to refuse doing such a modification because they cannot find a way to do it without introducing secondary errors like the ones you observed with your hacked version?

[...]

Yes. And volatility is something else.

It would be nice if runtime systems or processors knew about what C calls "sequence points" --- i.e. if there were any level above the single machine instructions at which interruption could be automatically forbidden. In such a dream system, the C compiler could ensure a consistent state of all variables in memory at each sequence point (for volatiles, it already does that), and the processor would take care of the intervals between them. But since that isn't the case, atomicity and C language programming can't, generally, be had at the same time.

And even if they could, they'ld at least be no worse at that than the long version of the read-modify-write triple-jump.

Is the typical piece of 68k code really so wrought with volatile objects that this problem alone would make the code "much bigger", in total? I somehow find that hard to believe.

--
Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.
Reply to
Hans-Bernhard Broeker

I don't have the data sheet in front of me and it's not a part I use, but if I remember correctly the AVR Microcontroller can't do that, and GCC supports it.

Well now you have. There is some question as to whether I fit the class "ordinary people", but I am an "embedded person", and I expect my compiler to hehave as specified in the language definition, as opposed to hehaving the way ordinary people would expect or behaving the way that an embedded person doesn't like.

Very few aspects of C or C++ are "what ordinary people would expect." You can get used to almost anything.

Take a look at these webpages:

formatting link
formatting link
formatting link
formatting link
formatting link
formatting link

Reply to
Guy Macon

It's not a bug - you are assuming "volatile" means "atomic". C does not have any way to express "atomic", and even if it did, many processors have no way to implement it. The generated code might be sub-optimal in space or speed, but the generated code is correct.

There are lots of reasons why C is an apalling language for embedded development (and for virtually everything else), but unfortunately we're stuck with it for most practical purposes.

Reply to
David

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.