Hello,
I'm using Keil MDK 3.80a on STM32 and trying to use the bitband attribute to access individual bits in a 32bit register.
typedef struct { uint32_t a0: 1; uint32_t b0: 1; uint32_t c0: 1; uint32_t d0: 1; uint32_t e0: 1; uint32_t f0: 1; uint32_t g0: 1; uint32_t h0: 1; //8bit uint32_t a1: 1; uint32_t b1: 1; uint32_t c1: 1; uint32_t d1: 1; uint32_t e1: 1; uint32_t f1: 1; uint32_t g1: 1; uint32_t h1: 1; //8bit uint32_t a2: 1; uint32_t b2: 1; uint32_t c2: 1; uint32_t d2: 1; uint32_t e2: 1; uint32_t f2: 1; uint32_t g2: 1; uint32_t h2: 1; //8bit uint32_t a3: 1; uint32_t b3: 1; uint32_t c3: 1; uint32_t d3: 1; uint32_t e3: 1; uint32_t f3: 1; uint32_t g3: 1; uint32_t h3: 1; //8bit } BB __attribute__((bitband)); BB bb __attribute__((at(0x40010800)));
(0x40010800 is the STM32 GPIOA.CRL register)
When in main I set
bb.a0=1;
which should be bit 0, it compiles to
0x080001BE 4B23 LDR r3,[pc,#140] ; @0x0800024C 0x080001C0 F8C30800 STR r0,[r3,#0x800]
which is off by 0x1C from the correct bitbanding address (which should be 0x42210000) , so instead of setting bit0, it sets bit 7 in GPIOA.CRL
Is my definition of the bitfield wrong or where is the error?
Thank You!
B. Schmidt
R3 = 0x4220f81c after load from flash in example above... sorry, forgot that piece of information before.
My guess is that the toolchain does not pack bitfields in the way you expect. You'll need to check the documentation as this is one of those implementation defined things.
In fact, bitfields is probably the classic example of "those implementation defined things"
As Messrs Kernighan and Ritchie themselves wrote,
"Almost everything about fields is implementation-dependent"
I knew that bitfields are implemenation dependent and wouldn't usally use these.
But this Keil document shows this exact use togehter with the bitband attribute, I didn't even change the name of the structure ;)
http://www.keil.com/support/man/docs/armcc/armcc_cjaebife.htm
Now I'm not sure how to proceed
Regards
Benjamin
So it does!
Time to contact Keil Support, then...
As far as I can see that example doesn't rely on any assumption about how the bits in the bitfield structure map to the bits in the underlying 32 bit value, whereas your code does.
Unless you have a good reason to use bitfields it is usually better, and portable, to stick with shifting and masking.
I guess a "good reason" would be to take advantage of the underlying hardware's ability to directly access individual bits?
That feature can be used without using bitfields too, as long as all bitfields are exactly one bit large.
Then you just create a struct with 32 32-bit large fields and map to the correct address.
The attribute was added to let the compiler mix and/or for multi-bit fields with 32-bit reads/writes for 1-bit fields.
I was not able to reproduce this with either MDK (RVCT4.0 [Build 524]) nor RVDS (RVCT4.0 [Build 591]) and default optimization. Which compiler settings did you apply?
Apart from this,
Andy Neil wrote: > In fact, bitfields is probably the classic example of > "those implementation defined things"
However, the exact bit field layout is defined by the ARM ABI. You can safely assume that RVCT is ABI compliant.
Jack Sprat wrote: > As far as I can see that example doesn't rely on any > assumption about how the bits in the bitfield > structure map to the bits in the underlying 32 bit > value, whereas your code does.
And righly so! See above.
Further information here (cf. sec. 7.1.7): infocenter.arm.com/.../IHI0042C_aapcs.pdf
Regards Marcus http://www.doulos.com/arm/
However, the exact bit field layout is defined by the ARM ABI.
And you're sure that covers even bit-banded ones? I'm certainly no ARM expert, but this sounds to me like this might be a feature that, originating in a second-party implementation of the ARM core, might not be covered by the ARM ABI document. Not even to mention it was added a good deal later than the ABI was first written.
Bit banding is a different access path to bits, so I would be very surprised if not implemented according to the ABI.
When using the attribute to have the compiler automagically use bit banding, the compiler will switch between the bitbanding feature or standard and/or operations depending on if a specific field is one bit large or if it is wider. That will only work if both access methods have compatible bit ordering/align.
the problem is solved.
I spoke to Keil Tech support and the problem was that I had two structures pointing to the same address using the at attribute.
One struct for 32bit access and the second using a bitfield and bitbanding to read and write the STM32 registers.
I should have been given a linker error because these overlapping regions are not supported, but it compiled and linked free of errors and warnings.
Access using the structure which was later defined always had a wrong address. My advice at the moment is not to use the bitband attribute, it is pretty restricted and not worth the hassle in my opionion
As long as you have volatile-declared your variables, you should be able to have a pointer to the "raw" address of the register, allowing you to do direct 16-bit or 32-bit updates. This will not affect the linkers ability to place variables at absolute addresses.
Another alternative is to manuall set an unsigned variable "at" the register address, and a struct/array of 32-bit unsigned integers "at" the alias address for bit-addressing. Remember that the bit-banding does its magic by having the bitbanded variables aliased to two different addresses. The bitband attribute is a bit of black magic added by the compiler to try and automagically hide this aliasing.
"One struct for 32bit access and the second using a bitfield and bitbanding"
So would a union have fixed this?
A union would allow a direct write to the full register width, or and/or operations on individual bits.
But it wouldn't have allowed the more optimized accesses from making a 32-bit write to an aliased address to set or clear a single bit. In this case, the processor has a 128 byte array of 32 32-bit integers mapping that are aliased to a single 32-bit address in either device space or a limited range of the normal RAM. Making one 32-bit write takes less code than having to read a 32-bit address, and then set or clear a bit before writing back the result. The hardware still has to perform a read/modify/write, but you will not waste instructions for it.
This optimization is only possible for single-bit updates. Any multi-bit updates will require either multiple single-bit updates in the bitband-aliased address range, or a normal and/or performed on the unaliased address.