On 23/09/15 21:55, Tim Wescott wrote:
> This question came up in the thread titled "Re: Ada, was: Re: Fundamental
> C question about "if" statements".
>
> I felt it deserved its own thread, because it's far off topic to what's
> become a very interesting, but far off topic thread.
>
Maybe I should have read this post before answering in the other thread...
> I, for one, would really like to see one of our resident compiler experts
> answer the question at the bottom.
Let me try (see below).
It is the case for many MCU's, including most ARM devices.
>
> I would REALLY like one of the compiler guys to weigh in on this, because
> I'm wondering if collecting all those unions into a struct and calling it
> volatile isn't forcing a 32-bit access. It would be nice to know if I'm
> just bamboozling the optimizer into doing what I want, or if I'm actually
> _telling_ it to do the right thing.
As long as you are using gcc 4.6 (or any other compiler that implements
volatile bitfield accesses with exactly the field size given in the
bitfield definition), you are fine. C does not specify what size such
accesses should have, but leaves it up to the implementation. So gcc
4.5 and earlier were technically correct to the C standards - the
implementation picked a size that gave smaller and faster code, but that
unfortunately did not work on many ARM microcontrollers.
>
> So, the final definitions (that I left out) is
>
> struct SPeripheralRegs
> {
> UPeriphRegister1 REG1;
> UPeriphRegister2 REG2;
> -- etc --
> };
>
> extern volatile SPeripheralRegs THIS_PERIPHERAL;
>
> Then I access things by
>
> THIS_PERIPHERAL.REGISTER.bits.whatever = something;
>
> Is (oh compiler guys!!!) this directing the compiler to act as if I wrote:
>
> UPeriphRegister A;
>
> A.all = static_cast<volatile unsigned int>THIS_PERIPHERAL.REGISTER.all;
>
> A.bits.whatever = something;
>
> static_cast<volatile unsigned int>THIS_PERIPHERAL.REGISTER.all = A.all;
>
That's pretty much what happens, yes. With each RMW set done
independently for each bitfield access.
> I could see how it might do this by the intent of the language, but I
> could see that I may just be bamboozling the optimizer, rather than
> intelligently directing it. So -- I'm curious, in a more-than-idly sort
> of way.
>
It is implementation dependent (defined by the compiler), but gcc 4.6
onwards defines it to work in the way you expect here.
I've pasted some bits from my replies on the other thread here, in case
people prefer to reply in this thread. But I will continue to read and
comment on both threads. Whether that will be helpful or confusing
remains to be seen...
On 23/09/15 21:19, Tim Wescott wrote:
>
> So it looks like:
>
> struct SPeriphRegisterBits
> {
> unsigned int bit0 : 1;
> unsigned int bits1_10 : 10;
> unsigned int bit11 : 1;
> unsigned int : 16;
> unsigned int bits28_31 : 4;
> };
>
> union UPerihRegister
> {
> struct SPeriphRegisterBits bits;
> unsigned int all;
> }
A few comments here.
Don't use "unsigned int" - use uint32_t. Make it all absolutely clear
and fully specified.
You don't need to do this in two parts. Put it altogether, using an
anonymous struct (this is a very common extension to C99, which has
existed in gcc since close to the dawn of time, and it is now part of C11).
I prefer to make typedefs of all my types, so that I don't need union or
struct tags later. But that is a matter of style. And I strongly
dislike systems Hungarian style, such as prefixing a type with U just
because it happens to be a union (especially if you haven't used
typedef, so that you have to have the "union" tag too).
Make your padding bits explicit with a name. The C++ memory model says
that unnamed fields may not be changed by any write operations (C is
more lenient), which can cause trouble for fields. It's better to make
all your padding explicit.
I also like to add a static assertion to check that there are no
mistakes when defining structures like these. Of preference I also use
"-Wpadded" in gcc to warn of any unexpected padding - but that does not
play well with most manufacturer-supplied headers.
typedef union {
struct {
uint32_t bit0 : 1;
uint32_t bits1_10 : 10;
uint32_t bit11 : 1;
uint32_t padding : 16;
uint32_t bits28_31 : 4;
};
uint32_t all;
} periphRegister_t;
static_assert(sizeof(periphRegister_t) == 4,
"Check bitfield definition for PeriphRegister");
Having said all that, your definition will also work correctly (assuming
you've got the volatile there somewhere, and are using gcc 4.6 or above
which fixes the volatile bitfield problem).
>
> All of the various register definitions then get collected into a
> structure for the peripheral, which gets declared as "extern const", and
> defined in the linker command file.
I presume you mean "extern volatile const", and that's only for the
read-only registers. It's common to define registers like this:
#define periphRegister (*((volatile periphRegister_t*) 0x12345678))
(Registers can, of course, be collected in larger structures first.)
The advantage of that is that everything is defined in a C header file -
you don't need to mess around with linker files too. And since the
compiler knows exactly what addresses are used, it can generate more
efficient code than if they have to be sorted out at link time.
If you write:
void test1(void) {
periphRegister.bit0 = 1;
periphRegister.bit11 = 0;
}
then the compiler will (correctly) generate two independent RMW
sequences, optimising only the calculations of the address to use.
If you want both assignments to be done in a single RMW operation, you
must do so explicitly - that's what the "all" field is for.
void test2(void) {
periphRegister_t cache;
cache.all = periphRegister.all;
cache.bit0 = 1;
cache.bit11 = 0;
periphRegister.all = cache.all;
}