473,322 Members | 1,614 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,322 software developers and data experts.

A basic question question about volatile use

I have a memory-mapped peripheral with a mapping like, say,
struct T {uint8_t read, write, status, forkicks;};

If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?

Is slapping a volatile on each member of the struct definition any
different? better? worse?

Thank you,
Ark
Jul 31 '08 #1
16 1795
>I have a memory-mapped peripheral with a mapping like, say,
>struct T {uint8_t read, write, status, forkicks;};

If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?
I don't believe that anything in the standard guarantees the right
thing for memory-mapped I/O (the vague promises aren't good enough),
but if the compiler for your system supports memory-mapped I/O and
is used to actually compile drivers using memory-mapped I/O, chances
are much better that it will work using volatile than not. Read
the specific documentation that should come with your compiler.

If your system actually has to deal with getting the width of I/O
correct, chances are the compiler will also.
>Is slapping a volatile on each member of the struct definition any
different? better? worse?
It depends entirely on your compiler.

Jul 31 '08 #2
Ark Khasin wrote, On 31/07/08 06:23:
I have a memory-mapped peripheral with a mapping like, say,
struct T {uint8_t read, write, status, forkicks;};
Be aware that the compiler is allowed to put in padding. Since this is
obviously for non-portable code it might be sufficient to just put in a
comment in the code stating that you are relying on the compiler not
putting in any padding.
If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?
The requirements on the compiler for volatile are quite lax and even
leave is up to the implementation to define what counts as accessing a
variable. So you should read your compiler documentation.

Imagine a processor which can physically only read/write in chunks of 64
bits but which nevertheless has a compiler with a "normal" 8 bit byte.
On such an implementation if the structure did not have any padding the
compiler would not be able to prevent all bytes being added.

Having said that, I would *expect* the compiler to "do the right
thing"(tm) in your case. I would expect this because by not doing so on
what I'm guessing is a compiler targeting a small embedded device would
make it hard to use.
Is slapping a volatile on each member of the struct definition any
different? better? worse?
It could be inconvenient. One thing I've sometimes wanted to do was use
a structure of that general type both for some in-normal-memory stuff
(without the volatile qualifier) and for the memory-mapped device.
volatile-qualifying the members would make this awkward.

There is also an argument that you should not use a struct for this
purpose *because* it could have padding, but if your code for
interfacing to the HW is nicely isolated and the compiler documentation
says it will work, then as it is highly system specific anyway I don't
see a real problem.

Sorry that the best advise is to read the compiler documentation, but it
really is your best chance to ensure it works correctly. The next best
advice would be asking in a group dedicated to your specific
implementation or in comp.arch.embedded where there are a lot more
people who are regularly doing this kind of thing than there are in this
group.

Note your question *is* topical here since volatile *is* part of the
standard language, it's just that to achieve your aims you need to go
beyond the guarantees of the language to things guaranteed by your
specific implementation.
--
Flash Gordon
Jul 31 '08 #3
Gordon Burditt wrote:
>I have a memory-mapped peripheral with a mapping like, say,
struct T {uint8_t read, write, status, forkicks;};

If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?

I don't believe that anything in the standard guarantees the right
thing for memory-mapped I/O (the vague promises aren't good enough),
What if it is not an I/O but a normal memory location with intended
byte-size members? (BTW, why the difference?)

Thanks,
Ark

Jul 31 '08 #4
Flash Gordon wrote:
Be aware that the compiler is allowed to put in padding. Since this is
obviously for non-portable code it might be sufficient to just put in a
comment in the code stating that you are relying on the compiler not
putting in any padding.
I was under the impression that if a struct contains only uint8_t and
arrays thereof, there is no padding. Is it wrong?
>
Imagine a processor which can physically only read/write in chunks of 64
bits but which nevertheless has a compiler with a "normal" 8 bit byte.
On such an implementation if the structure did not have any padding the
compiler would not be able to prevent all bytes being added.
Is such a compiler allowed to define uint8_t? Hmm... I guess it is...

Thanks,
Ark
Jul 31 '08 #5
Ark Khasin wrote:
Flash Gordon wrote:
>Be aware that the compiler is allowed to put in padding. Since this
is obviously for non-portable code it might be sufficient to just put
in a comment in the code stating that you are relying on the compiler
not putting in any padding.
I was under the impression that if a struct contains only uint8_t and
arrays thereof, there is no padding. Is it wrong?
The [u]intN_t types are specified to contain any padding bits, but
padding between structure members of these types in still allowed.

<snip>

Jul 31 '08 #6
Flash Gordon wrote:
Imagine a processor which can physically only read/write in chunks
of 64
bits but which nevertheless has a compiler with a "normal" 8 bit byte.
On such an implementation if the structure did not have any padding the
compiler would not be able to prevent all bytes being added.
This is true for any implementation of any language; the peripherals
must be designed to be accessible via CPU instructions, so my
struct T {uint8_t read, write, status, forkicks;};
doesn't describe a peripheral on such a platform.
OTOH, it can be a normal memory object (+ consider a "normal" platform
with a volatile bitfield member of a struct). The closest thing to "the
right thing" would be to disable the interrupts while reading/writing.
Will a(ny) compiler do this?
Jul 31 '08 #7
santosh wrote:
Ark Khasin wrote:
>Flash Gordon wrote:
>>Be aware that the compiler is allowed to put in padding. Since this
is obviously for non-portable code it might be sufficient to just
put in a comment in the code stating that you are relying on the
compiler not putting in any padding.
I was under the impression that if a struct contains only uint8_t and
arrays thereof, there is no padding. Is it wrong?

The [u]intN_t types are specified to contain any padding bits, but
^
add a "not" here.
padding between structure members of these types in still allowed.

<snip>
Jul 31 '08 #8
santosh wrote:
Ark Khasin wrote:
>I was under the impression that if a struct contains only uint8_t and
arrays thereof, there is no padding. Is it wrong?

The [u]intN_t types are specified to contain any padding bits, but
padding between structure members of these types in still allowed.
Of course struct {uint8_t a; uint32_t b; uint8_t c;} is likely to have
padding somewhere. But uint8_t alone?..
Jul 31 '08 #9
Ark Khasin wrote:
santosh wrote:
>Ark Khasin wrote:
>>I was under the impression that if a struct contains only uint8_t
and arrays thereof, there is no padding. Is it wrong?

The [u]intN_t types are specified to contain any padding bits, but
padding between structure members of these types in still allowed.
Of course struct {uint8_t a; uint32_t b; uint8_t c;} is likely to have
padding somewhere. But uint8_t alone?..
I think you are conflating padding bits (which the [u]intN_t types
cannot have) with padding bytes, which are allowed between any two
structure members. It is not allowed between elements of an array.

Jul 31 '08 #10
>Flash Gordon wrote:
>>Imagine a processor which can physically only read/write in
chunks of 64 bits but which nevertheless has a compiler with
a "normal" 8 bit byte.
For instance, the original DEC Alpha (though later versions of the
chip acquired byte load and store via the so-called "BWX" option)
Current MIPS chips, which are found in equipment being made and
sold today (many readers probably have them in their own homes),
still do 32-bit accesses as a minimum.
>>On such an implementation if the structure did not have any padding the
compiler would not be able to prevent all bytes being a[ccess]ed.
In article <cOdkk.483$Ht4.306@trnddc01>,
Ark Khasin <ak*****@macroexpressions.comwrote:
>This is true for any implementation of any language; the peripherals
must be designed to be accessible via CPU instructions, so my
struct T {uint8_t read, write, status, forkicks;};
doesn't describe a peripheral on such a platform.
Indeed. But it shows some of the limitations the C Standard has
in setting limitations on implementations: if Standard C required
implementations to do only single-byte load and store for volatile
"uint8_t" accesses, Standard C would be impossible on old DEC Alpha
chips, and on current MIPS chips.
>OTOH, it can be a normal memory object (+ consider a "normal" platform
with a volatile bitfield member of a struct). The closest thing to "the
right thing" would be to disable the interrupts while reading/writing.
Will a(ny) compiler do this?
One might. I have not seen any that would, though.

The *intent* of the volatile qualifier is clear enough. It means,
in effect: "Hey Mr Compiler, this object does not behave like
ordinary RAM, so do not assume that anything you put in it stays
there, nor that anything you read from it is still there even
nanoseconds later." What the compiler *does* with that extra bit
of information is up to the implementation. In general, if you
are accessing memory-mapped I/O hardware, using the "volatile"
qualifier is necessary, but sometimes not sufficient.

Memory-mapped I/O is of course not required, and there are systems
that do not have it (systems where "I/O" operations are separate
instructions like "inb" and "outb", for instance; of course no one
would buy a chip with such instructions today :-) ). So in "100%
Standard" C, where you would not even *think* of accessing I/O
hardware :-) , there are only two situations in which you need
"volatile":

- when working with a sig_atomic_t variable used in a signal
handler, and

- when using setjmp() and longjmp(), where you should qualify
all local variables as "volatile". (You can get away without
it for some local variables, but figuring out which ones can
be somewhat painful, and it is easier and safer to start by
marking them all volatile. Later, when doing optimization, if
profiling shows that some of them are causing performance
problems, you can work out which one(s) are safe.)

Personally, I wish that Standard C did not require "volatile" for
either of these situations (and I think C99 does not for the first
one, having smuggled the "volatile" into sig_atomic_t). But C is
what the Standard says it is, and as C programmers, we must either
go with that, or be very certain *why* we are not doing so. (That
is, you can abandon the Standard any time you like, but you should
*know* that you are doing this, and what you stand both to gain
and to lose in the process.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out) http://web.torek.net/torek/index.html
Jul 31 '08 #11
On 31 Jul, 08:31, Ark Khasin <akha...@macroexpressions.comwrote:
santosh wrote:
Ark Khasin wrote:
I was under the impression that if a struct contains only uint8_t and
arrays thereof, there is no padding. Is it wrong?
The [u]intN_t types are specified to contain any padding bits, but
padding between structure members of these types in still allowed.

Of course struct {uint8_t a; uint32_t b; uint8_t c;} is likely to have
padding somewhere. But uint8_t alone?..
your declaration looked like this:-

struct T {uint8_t read, write, status, forkicks;};

So you don't have a lone uint8_t you have 4 of them!
I must admit I'd consider it an odd compiler that padded
between uint8_ts like this.

<muse: "how can I annoy jacob?">

I suppose a machine with 24-bit words packing 3 chars
into a word might insert padding.

ccc00c

--
Nick Keighley

Jul 31 '08 #12
On Thu, 31 Jul 2008 05:23:23 GMT, Ark Khasin
<ak*****@macroexpressions.comwrote:
>I have a memory-mapped peripheral with a mapping like, say,
struct T {uint8_t read, write, status, forkicks;};
If you want to insure that there is no padding between the four
members, change it to {uint8_t x[4];}. If you like, you can add
macros of the form
#define READ x[0]
#define WRITE x[1]
so that references to the members are mnemonically meaningful.
>
If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?
This is really a system specific issue. I doubt if volatile makes a
difference. A machine may perform all memory accesses in "word" sized
blocks and extract the relevant bytes from internal registers.

I would expect that since this peripheral works on your system that
your system supports byte accesses. I would further expect that
anyone writing a compiler for this system would do so also.

But you asked for a guarantee. The best we can offer is probably. If
your compiler has a newsgroup or a tech support site, that would be
the place to ask.
>
Is slapping a volatile on each member of the struct definition any
different? better? worse?
I don't see how it could hurt and it does emphasize your intent.

--
Remove del for email
Jul 31 '08 #13
>>I have a memory-mapped peripheral with a mapping like, say,
>>struct T {uint8_t read, write, status, forkicks;};

If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?

I don't believe that anything in the standard guarantees the right
thing for memory-mapped I/O (the vague promises aren't good enough),
What if it is not an I/O but a normal memory location with intended
byte-size members? (BTW, why the difference?)
Ordinary RAM does not care about the number of accesses or the width
of accesses. What you write can be read back. Ordinary RAM doesn't
usually NEED volatile unless there is some asynchronous code (signal
handler, interrupt routine, etc.) that can change the contents
without the compiler knowing about it.

With memory-mapped I/O, reading or writing a register may trigger
an action (clearing a "data ready" flag, fetching the next set of
data, launching a missile, etc. What you read (the "status" register)
may have nothing to do with what you write (the "command" register)
except they share the same address (so turning a byte write into a
word fetch, replace a byte, write a word may be a disaster, even
though it's not an issue with ordinary RAM). Accessing the register
with the wrong I/O width may cause malfunctions or access something
entirely different. The contents of a memory-mapped I/O device
register may change on its own (e.g. data ready or status flags,
or a clock).

Jul 31 '08 #14
Ark Khasin wrote:
I have a memory-mapped peripheral with a mapping like, say,
struct T {uint8_t read, write, status, forkicks;};

If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?

Is slapping a volatile on each member of the struct definition any
different? better? worse?
I guess you don't have a decent C book. The point of 'volatile' is to
tell the compiler that this variable might be modified by forces outside
this program's scope. Think memory-mapped I/O.

--
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Jul 31 '08 #15
Joe Wright wrote:
Ark Khasin wrote:
>I have a memory-mapped peripheral with a mapping like, say,
struct T {uint8_t read, write, status, forkicks;};

If I slap a volatile on an object of type struct T, does it guarantee
that all accesses to the members are byte-wide, or is the compiler free
to read or read-modify-write in any data width it chooses?

Is slapping a volatile on each member of the struct definition any
different? better? worse?

I guess you don't have a decent C book. The point of 'volatile' is to
tell the compiler that this variable might be modified by forces outside
this program's scope. Think memory-mapped I/O.
I think you've missed the point of Ark's question, which is not
about what `volatile' is used for, but about whether it guarantees
any particular type of access. The answer to that question, as others
have pointed out, is "No."

It might appear that by qualifying each element

struct T {
volatile uint8_t read;
volatile uint8_t write;
...
};

.... one could tell the compiler not to meddle with the `read' element
while accessing the adjacent `write'. The argument might hold that
`volatile' warns the compiler that each access to the variable is a
side-effect, so that touching `read' while fiddling with `write'
would cause a side-effect in the real machine that is not present in
the abstract machine. The argument sounds pretty good, but the last
sentence of 6.7.3p6 torpedoes it: "What constitutes an access to an
object that has volatile-qualified type is implementation-defined."
This sentence -- which might as well be named the Mack Truck Clause,
because it's a hole wide enough to drive a Mack truck through -- is
the Standard's concession that C is implemented on real machines that
may not always be capable of following the abstract machine's rules
quite that scrupulously. (For example, others have mentioned real
machines that are unable to access bytes in isolation, but always
deal with larger assemblages.)

Ark's case is not hopeless, though. The MTC says "implementation-
*defined*," which means that the implementation is required to define
what it means to access a volatile object. Somewhere in his compiler's
documentation there should be a description of how volatile accesses
are carried out, and perhaps the compiler's promises -- which are
specific to that particular compiler, of course -- will suffice for
Ark's purposes. In fact, there's an excellent chance that they will,
because if the machine has arranged its I/O registers in this manner,
it's highly likely that the hardware is able to generate the accesses
they need, and there's a reasonable chance that the C compiler can
create code that generates those accesses. It's not certain -- on
some machines you may need to resort to assembly -- but the odds are
fairly good.

--
Er*********@sun.com
Jul 31 '08 #16
Eric Sosman wrote:
It might appear that by qualifying each element

struct T {
volatile uint8_t read;
volatile uint8_t write;
...
};

... one could tell the compiler not to meddle with the `read' element
while accessing the adjacent `write'. The argument might hold that
`volatile' warns the compiler that each access to the variable is a
side-effect, so that touching `read' while fiddling with `write'
would cause a side-effect in the real machine that is not present in
the abstract machine. The argument sounds pretty good, but the last
sentence of 6.7.3p6 torpedoes it: "What constitutes an access to an
object that has volatile-qualified type is implementation-defined."
This sentence -- which might as well be named the Mack Truck Clause,
because it's a hole wide enough to drive a Mack truck through -- is
the Standard's concession that C is implemented on real machines that
may not always be capable of following the abstract machine's rules
quite that scrupulously. (For example, others have mentioned real
machines that are unable to access bytes in isolation, but always
deal with larger assemblages.)

Ark's case is not hopeless, though. The MTC says "implementation-
*defined*," which means that the implementation is required to define
what it means to access a volatile object. Somewhere in his compiler's
documentation there should be a description of how volatile accesses
are carried out, and perhaps the compiler's promises -- which are
specific to that particular compiler, of course -- will suffice for
Ark's purposes. In fact, there's an excellent chance that they will,
because if the machine has arranged its I/O registers in this manner,
it's highly likely that the hardware is able to generate the accesses
they need, and there's a reasonable chance that the C compiler can
create code that generates those accesses. It's not certain -- on
some machines you may need to resort to assembly -- but the odds are
fairly good.
Thank you, Eric and all.
The code is working fine; my inquiry was about how much RTFM I need to
do if I am to replace the compiler. Turns out the whole volatile
business is implementation-dependent (perhaps actually -defined with any
luck). It would be nice at least to have a machine-defined set of atomic
types and a guarantee that an atomic volatile type is accessed
atomically and without molesting its neighbors. Alas, even that isn't
true: ARM has an instruction to access (volatile) 64-bit (and wider)
objects atomically but my compiler (IAR) doesn't seem to always bother.

- Ark
Jul 31 '08 #17

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
by: Matthew Polder | last post by:
Hi, When a class Apple is written and the assignment operator is not explicitly declared, the operator Apple& operator=(const Apple&) is created by the compiler. Is there any difference...
2
by: LBJ | last post by:
I have the following C code: //pHeadLoc contains an address that can be modified externally, //therefore it is a volatile pointer to a pointer volatile int** const pHeadLoc = (int**)0x456000; ...
9
by: Tim Rentsch | last post by:
I have a question about what ANSI C allows/requires in a particular context related to 'volatile'. Consider the following: volatile int x; int x_remainder_arg( int y ){ return x % y; }
8
by: Tim Rentsch | last post by:
Here's another question related to 'volatile'. Consider the following: int x; void foo(){ int y; y = (volatile int) x;
56
by: spibou | last post by:
In the statement "a *= expression" is expression assumed to be parenthesized ? For example if I write "a *= b+c" is this the same as "a = a * (b+c)" or "a = a * b+c" ?
7
by: Chris Newby | last post by:
I'm trying to undertand some details on the C# "lock" statement. I came across some code during a review and thought I spotted a deadlock scenario, but the programmer said he'd taken it from a...
94
by: Samuel R. Neff | last post by:
When is it appropriate to use "volatile" keyword? The docs simply state: " The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock...
8
by: JackC | last post by:
Hi, I am trying to get posix threads working from within an object, heres some code: int NConnection::TheadControl() { int thread_id; pthread_t new_connection;...
16
by: jackie | last post by:
i know that in c plus plus,++x returns l-value while x++ returns r- value,but what is the situation in c,are both ++x and x++ return r- value? i don't know how C99 defines it,thx.
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.