|
|
|
|
|||||||||||||||||||||||||||
![]() |
|
|
«
Previous Thread
|
Next Thread
»
|
Thread Tools | Search this Thread | Display Modes |
|
#1
|
|||
|
|||
|
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 |
|
#2
|
|||
|
|||
|
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? I don't believe that anything in the standard guarantees the right thing for memory-mapped I/ (the vague promises aren't good enough), but if the compiler for your system supports memory-mapped I/ and is used to actually compile drivers using memory-mapped I/, 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/ 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. |
|
#3
|
|||
|
|||
|
A basic question question about volatile use
Ark Khasin wrote, 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. 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. 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 |
|
#4
|
|||
|
|||
|
A basic question question about volatile use
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/ (the vague promises aren't good enough), What if it is not an I/ but a normal memory location with intended byte-size members? (BTW, why the difference?) Thanks, Ark |
|
#5
|
|||
|
|||
|
A basic question question about volatile use
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. 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 |
|
#6
|
|||
|
|||
|
A basic question question about volatile use
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> |
|
#7
|
|||
|
|||
|
A basic question question about volatile use
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. 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. TH, 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? |
|
#8
|
|||
|
|||
|
A basic question question about volatile use
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> |
|
#9
|
|||
|
|||
|
A basic question question about volatile use
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. > course struct {uint8_t a; uint32_t b; uint8_t c;} is likely to have padding somewhere. But uint8_t alone? |
|
#10
|
|||
|
|||
|
A basic question question about volatile use
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. >> 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. |
|
#11
|
|||
|
|||
|
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? >> >I don't believe that anything in the standard guarantees the right >thing for memory-mapped I/ (the vague promises aren't good enough), >What if it is not an I/ but a normal memory location with intended >byte-size members? (BTW, why the difference?) RAM does not care about the number of accesses or the width of accesses. What you write can be read back. 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/, 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/ width may cause malfunctions or access something entirely different. The contents of a memory-mapped I/ device register may change on its own (e.g. data ready or status flags, or a clock). |
|
#12
|
|||
|
|||
|
A basic question question about volatile use
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/ -- Joe Wright "Everything should be made as simple as possible, but not simpler." Albert Einstein |
|
#13
|
|||
|
|||
|
A basic question question about volatile use
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/ 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 |
![]() |
| Viewing: Web Development Archives > FAQs > C/C++ > A basic question question about volatile use |
| Thread Tools | Search this Thread |
| Display Modes | Rate This Thread |
|
|
|
|