how to use volatile key word?

how to use volatile key word?

Assume I have a global variable shared by two or more tasks. Do I need to declare it as volatile?

When will a non-volatile global variable be written back to memory from a register copy? Before a function call? Or before the return statement?

Reply to
伏虎
Loading thread data ...

o declare it as volatile?

That is probably a good idea. Usually volatile referes to the possibility t= hat hardware may change the value, such as an I/O port.

register copy? Before a function call? Or before the return statement?

I don't know. It could depend on the compiler.

ed

Reply to
Ed Prochak

declare it as volatile?

hardware may change the value, such as an I/O port.

register copy? Before a function call? Or before the return statement?

All writes to variables are written at the time they are written.

If the value has not been modified, no write may be done, depends on compiler.

Variables are read and stored into registers. If the same variable is use a second time in a function, the register variable will be used.

If the variable is "volatile", it will read from the original source location (memory or I/O port)

If a variable is used in two locations, be sure one place only sets and the other only clears that location.

Such as an interrupt flag.

hope this helps.

hamilton

Reply to
hamilton

declare it as volatile?

You need to be precise in your terminology when you're discussing language issues if you want to avoid the language lawyers who might point out that there's no mention of which programming language you're referring to and, if you mean the C language, such a person might object that there are no such things as "global variables" in C, so the entire question is moot.

Whether an object with external linkage needs the volatile type-qualifier would depend on how you're sharing the abstract execution between the two "tasks" (C doesn't define a task, as such). If there's a signal or interrupt involved then yes, the volatile type qualifier would be necessary to ensure that the value of the variable agrees with the value produced by the abstract machine. If the tasks are multiple legs in a state machine, for example, then the type qualifier shouldn't be needed.

register copy? Before a function call? Or before the return statement?

You don't know. All that you can ask is that the program behave as described by the standard for the abstract machine defined in the semantics. If the actual implementation can determine that a particular object without a type qualifier is never accessed again then it can discard it and never write it back to memory.

--
Rich Webb     Norfolk, VA
Reply to
Rich Webb

need to

from a

statement?

Ideally the write to memory will be as soon as absolutely possible. In an example I saw, the code

int a, b; // ... a = b = 0;

generated (in pseudo-assembly)

load #0 store b store a

then changing to volatile

volatile int a, b; // ... a = b = 0;

generated

load #0 store b load b store a

`volatile` keeps the code generator from making any assumptions about the state of a variable based on the code it's generating locally. This is useful for locations that are changed by hardware, and locations that are changed by interrupt handlers.

Mel.

Reply to
Mel Wilson

Yes. But if multi-tasking is pre-emptive, this is sufficient *only* when 1a) the CPU is single core or 1b) the value is word atomic - i.e. it cannot lie across the boundary of two cache lines

and

2) the reader(s) care only for the current value.

The issue of word atomic is extremely important for multi-core CPUs, because most CPUs now have wide registers - FP, SIMD, MAC, etc. - which take multiple bus cycles to read or write. Each core will lock it's own bus during an access, but the cores have separate L1 caches which are kept synchronized by a write snooping mechanism. When a value lies partially in two cache lines, the snoop mechanism requires multiple line copies to update it in another core's cache, and so the asynchronously running core may perceive a partial update if it reads at the wrong time.

If the reader and writer need to interlock (writer needs to know the value was delivered), or if the reader needs to perceive every value in a sequence rather than simply the latest, then you must use some separate synchronization mechanism (mutex, semaphore, etc.).

The register value will be written back to memory when it needs to be ... i.e. when the register is needed for some other purpose. And then it likely will be written only if the value has changed (else the compiler sucks). The location of the write back may *not* coincide with a visible function call ... if the called function does not use that particular register, an optimizing compiler likely will not bother to save it. Of course, if the called function in turn calls other functions, any of those further calls might cause the register to be saved.

George

Reply to
George Neuner

Yes, but this is not necessarily sufficient.

That's not specified by the C standard. If you're lucky, the compiler will document when "store" instructions are generated and the CPU's data sheet will document when "store" instructions are actually executed.

There are two issues here: the compiler and the CPU.

The compiler can re-organise code according to the "as if" rule described in section 5.1.2.3p10 of the standard:

[#10] Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across translation unit boundaries. In such an implementation, at the time of each function entry and function return where the calling function and the called function are in different translation units, the values of all externally linked objects and of all objects accessible via pointers therein would agree with the abstract semantics. Furthermore, at the time of each such function entry the values of the parameters of the called function and of all objects accessible via pointers therein would agree with the abstract semantics. In this type of implementation, objects referred to by interrupt service routines activated by the signal function would require explicit specification of volatile storage, as well as other implementation-defined restrictions.

IOW, it can read a location as soon as it knows that the location should have the correct value according to the rules of the language, and it can defer writing to a location so long as this will not affect the behaviour of external functions.

The C standard doesn't really say very much concrete about "volatile". Apart from the specifications of longjmp() and signal(), the rest is either implementation-defined or non-normative.

And the CPU may re-order load/store operations, so just because one store instruction occurs before another, it doesn't necessarily follow that the values will be written to memory in that order. You may need to use a memory barrier (fence) when reading or writing memory which may be accessed by threads running on different cores if those cores each have their own cache.

Reply to
Nobody

eed to declare it as volatile?

That may be neither necessary nor sufficient. To be sure that a global is dealt with between two tasks you need to consult with your RTOS manual. In the case of a simple cooperative task environment simple globals are sufficient. On the other extreme with shared memory among multiple cores you need to take other actions. The only sure way in the end is to use the mechanisms defined by the RTOS. Such mechanisms will also take care of anything need to ensure that communications are atomic. Volatile is not, in general, a good mechanism for this.

register copy? Before a function call? Or before the return statement?

Both of the above and more. Indeed even a regular variable will be written back to memory in that case since the compiler cannot know what the called routine will do with a global variable. In the case where the compiler can optimize over all routines though it is allowed to optimize even that case.

For volatile variables the compiler must (as in is required to) read and write volatile variable exactly as many times as the expression calls for in the order called for. There are all sorts of subtle points that can affect this and not all compilers get it "right" and not everyone can agree on what "right" is so relying on it for more than the simple cases can be dangerous.

Robert

Reply to
Robert Adsett

Close, but no cigar :-(

First off, even for a single core CPU there may be other masters such as DMA - you may need to consider them when making sure all data is consistent.

Secondly, "atomic" access is important, and cache lines (for processors with cache) are important, but the two concepts are almost completely orthogonal.

"Atomic" access is concerned with reading or writing an item of data as a single unit. You have to take care that all data passed between different contexts (such as tasks, interrupt routines, main functions, dma, other processors, etc.) is accessed atomically. In most cases, this means the data is accessed as a single unit whose size is at most the bit-width of the processor (i.e., 8-bit, 16-bit or 32-bit for a

32-bit cpu), and which is naturally aligned (misaligned accesses are often handled as two smaller accesses).

If your data is larger than this, then you need to simulate atomic access in some way - disabling interrupts, guarding with synchronising atomic-sized data, using semaphores, using an RTOS message passing system, etc. This applies on /all/ processors, regardless of the existence of cache.

Regarding cache, you need to be sure that any alternative masters involved (such as DMA or a second cpu) see consistent data. This will mean you need cache snooping, or explicit cache flushes, or non-cached memory regions (note that write-through cache policies are /not/ sufficient). Your data will naturally be aligned within a cache line if it is atomic. And with processors with advanced caches, you probably also have additional instructions such as "msync" to flush the processor's write queues - these things quickly get complicated!

If the compiler doesn't know the source of a new function call (or the function being returned to), then it must also write out to memory as required before doing the function call or return. If it knows the source, it may be able to keep the writes in memory for longer.

Usually there is some mechanism to force the writes out, but it is compiler dependent. For many compilers, the gcc assembly style memory barrier works well - asm volatile("" ::: "memory"), and is often combined with processor-specific synchronisation instructions.

(Clarification) the value will be written back if it /might/ have changed - i.e., there was a write to the value in the source code. The compiler will not generate code to check if the value /actually/ changed. It is also possible that the compiler will generate code to write the value even if there is no write executed in the path through the source code, if that gives better code (for example if the source code write was conditional).

Of course, for volatile writes, you will get exactly as many writes as given in the source code.

Note also that there is no guarantee of the ordering of non-volatile writes or reads. For volatile accesses, you are guaranteed that they will occur in the order given - but you know nothing about when in the program the accesses will occur. And the compiler may re-order non-volatile accesses freely amongst the volatile ones - only the volatile accesses are specifically ordered.

Reply to
David Brown

It may be good to point out why this is so. The c language defines some programming abstraction like integers and procedure calls.

volatile kind of specifies that data falls outside those abstractions. So by definition you're on thin ice, and have to make sure the program does what is intended. You're in the realm of c-as-a-portable-assembler; you have to get involved in the actual hardware you use, to a higher or lesser extent.

Groetjes Albert

--

--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
Reply to
Albert van der Horst

And if the terms "cross posting" and "multiple posting" mean nothing to you, check out this Wiki article:

formatting link

I had been unaware that the question had been posted to multiple groups: yes, you will get wildly different answers, because what's appropriate and applicable for an itty bitty processor with code running on bare metal is considerably different -- and often diametrically opposed to -- what's appropriate and applicable in an application running on a desktop.

Consider a guy in camo pants with a pack full of freeze-dried food, armed with a long knife and a shotgun. Put him in Times Square -- is his gear appropriate and applicable? How about a lawyer in a shiny suit with $300 shoes, armed with a Visa Platinum? How's he doing? Now put those two guys out in the sticks in Alaska -- who's gear makes sense now?

Ditto software practices in (deep) embedded vs. desktop.

--
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?

Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com
Reply to
Tim Wescott

Op Tue, 26 Jun 2012 18:09:00 +0200 schreef =E4=BC=8F=E8=99=8E :

d =

No, you need to let the tasks communicate with each-other about who is t= he =

owner of the data. For example by using message passing.

m =

t?

Generally whenever the value to be written has been calculated.

--

Gemaakt met Opera's revolutionaire e-mailprogramma:  =

http://www.opera.com/mail/
(Remove the obvious prefix to reply privately.)
Reply to
Boudewijn Dijkstra

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.