This is the mail archive of the
crossgcc@sources.redhat.com
mailing list for the crossgcc project.
See the CrossGCC FAQ for lots
more information.
Re: arm-elf-gcc : change default data alignement depending on ARM/THUMB
- From: Bill Gatliff <bgat at billgatliff dot com>
- To: Vincent Rubiolo <vincent dot rubiolo at st dot com>
- Cc: gcc-help at gcc dot gnu dot org, crossgcc at sources dot redhat dot com
- Date: Thu, 21 Aug 2003 09:48:47 -0500
- Subject: Re: arm-elf-gcc : change default data alignement depending on ARM/THUMB
- References: <3F43E014.3070800@st.com>
Vincent:
It indeed seems that ARM ADS 1.2 aligns on halfwords (16 bits) when in
THUMB mode (along with much more sophisticated rules). GCC different
behavior causes much more padding to be inserted in structs members
and, as the project I work on uses HW mapped registers, application
crashes.
For now, I worked around that using a compile time define to enable
the __attribute((packed))__ on the struct(s). I would nevertheless
like to know whether there is another solution.
Avoid using structures for hardware i/o. Seriously.
There are only minimal guarantees in ANSI regarding structure layout,
not nearly enough to make them good for associating reliably with
hardware. As you've seen, structure implementations can change in
response to changes in compiler settings and versions--- but the
hardware doesn't!
The "packed" attribute helps a lot, but really only provides a
suggestion to the compiler--- and hence, the compiler is free to ignore
you if it wants to. Alas, C doesn't provide a bulletproof way to do
hardware i/o with an abstract data type.
This is all especially the case with bitmapped fields. DON'T do
bitmapped fields for hardware i/o, especially in cases where the
register has "reserved, set to zero" bits!
Here's an example of why. I was working some time ago on a DMA
controller inside the Hitachi SH7045 (or maybe it was one of the other
peripherals, I can't really remember now). Anyway, a particular control
register had some reserved bits in them, and the datasheet clearly
stated that you _had_ to write zeros to those bits any time you touched
the register.
Well, I thought I was being clever, and came up with a data structure
that looked something like this (from memory):
typedef struct {
...
unsigned int rq1f3: 1;
unsigned int reserved: 29;
unsigned int r1f4: 2;
...
} the_register_s;
This structure allows you to set the rq1f3 and r1f4 bits, but doesn't
tell the compiler anything about the reserved bits. As a result, when I
did this:
the_register_s *r = (the_register_s*)THE_REGISTER_ADDRESS;
r->rq1f3 = 1;
r->r1f4 = 2;
When I looked at the assembly code, I found that the compiler was doing
basically this:
*r |= 0x80000000;
*r |= 0x00000002;
Which appears to be exactly what I was asking for.
BUT, see the problem? I didn't either--- at first. The reserved bits
in this register sometimes read back nonzero data, and that data gets
written back to the register due to the read-modify-write operation
implemented by the compiler. In violation of the instructions in the
datasheet. Net result was the CPU going into undocumented test modes
that made it do strange things, including locking up at unpredictable
times thereafter.
ANY time I've tried to be clever with data structures for hardware i/o,
I've lived to regret it.
Oh, and I left the "volatile" keyword out of all of the above, but you
absolutely, positively need it as well when you're touching hardware.
If you don't believe me, turn on -O2 and see what happens. :^) Where
the keyword goes in the above example is an exercise left to the
reader. :^)
Regards,
b.g.
--
Bill Gatliff
Embedded systems training and consulting
bgat@billgatliff.com
------
Want more information? See the CrossGCC FAQ, http://www.objsw.com/CrossGCC/
Want to unsubscribe? Send a note to crossgcc-unsubscribe@sources.redhat.com