By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,853 Members | 974 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,853 IT Pros & Developers. It's quick & easy.

Forcing compiler to access bitfields as 32-bits instead of bytes.

P: 66
In my code, I have bit fields in structures to access hw registers. I tested my compiler to see how it reads/writes to the registers and noticed that the compiler is trying to access the registers by bytes. The HW registers only allow 32-bit "words" to be written or read, otherwise it will give a "bus error".

Is there a way to force the compiler to produce WORD (32bit) accesses for bitfields instead of byte??
Dec 4 '08 #1
Share this Question
Share on Google+
5 Replies

Expert 100+
P: 2,396
Don't use bit fields; instead access each register as an unsigned long and do your own bit manipulations.

Actually, you don't have a firm guarantee that unsigned long will give you precisely 32-bit accesses. Ideally you would use uint32_t from C99. If that isn't an option for you, then create your own typedef. This typedef may need to be changed whenever you port your code to a different compiler.

You need to worry about endianness. The 32-bit words may be written out in a different bit order than the logical values you're manipulating in your program. My suggestion is to use access functions to do all reads and writes. These access functions can include conditionally-compiled code to make endian adjustments for you. You either need to provide your own endian-control macro or use one that your compiler already provides. To prevent anybody from cheating, make sure the register pointers are hidden from the rest of your software.

Don't forget that I/O registers are "volatile".
Dec 4 '08 #2

P: 66

Like this?? :

Expand|Select|Wrap|Line Numbers
  1. typedef struct {
  3.   uint32_t reg1 : 25;
  4.   uint32_t reg2 : 5;
  5.   uint32_t reg3 : 2;
  7. } volatile RegStruct;
Right now I have the same, but the reg types are 'unsigned' instead of 'uint32_t'
Will that make it access the registers by words not bytes?
Dec 5 '08 #3

Expert Mod 5K+
P: 8,916
No, Don means do not use bit fields at all.

Read your register into a 32 bit unsigned int and then manipulate that copy that exists in the RAM rather than attempting to access

Expand|Select|Wrap|Line Numbers
  1. uint32_t reg = *((volatile uint32_t *)REGISTER_ADDRESS);
  3. uint32_t reg3 = (reg & 0xC0000000) >> 30;
  5. // etc
Do not make bit accesses to registers, it can produce unpredictable results.

I fixed this exact problem in project I work on 2 weeks ago. Accessing an analogue-digital converter the original code ready the ready bit until it was set and then read the data bits. But these bits were all part of the same register and reading it clears the ready bit and starts the next conversion so by reading the ready bit the data bits become invalid.

I changed the code to read the register into a local variable, test the ready bit in that variable and if set extract the data bits from that variable.

The difference? Before the change for a given input the readings averaged 85 with a standard deviation of 110 after the change for the same input the reading averaged 137 with a standard deviation of 3.

Not reading the register correctly and in a single access really messed things up.
Dec 5 '08 #4

Expert 100+
P: 2,396
However, instead of sprinkling references to REGISTER_ADDRESS throughout your code, I suggest you provide two access functions:

unsigned long readRegister(void);
void writeRegister(unsigned long value);

These functions dereference the REGISTER_ADDRESS, but everybody else calls these functions. That way, any endian conversions that become necessary are localized to these two functions.

Notice that I'm suggesting the prototypes for these functions use 'unsigned long' rather than uint32_t. That's my own personal preference -- I like to minimize the scope of specific-width types. The access functions would have to cast to and from uint32_t. Unsigned long is guaranteed to be at least 32 bits wide, so it would work everywhere except possibly where the register accesses take place.

Suppose you want to set one bit in a register. The following snippet looks correct:
Expand|Select|Wrap|Line Numbers
  1. unsigned long value;
  2. value = readRegister();
  3. writeRegister(value | BIT_MASK);
A not uncommon headache for device driver writers is that some hardware provides write-only registers. If that were true here then the preceding code probably corrupts part of the register because readRegister() returned garbage.

The way to deal with that problem is to maintain a "ram image" of the register. The writeRegister() function writes its argument value to this local static variable and also to the register. The readRegister() function returns the value of the ram image instead of vainly trying to read the register itself. There are a couple of limitations to this strategy:

1. The ram image is not initialized until after you call writeRegister(), therefore you want to be sure you don't call readRegister() first.

2. writeRegister() isn't thread safe: if another thread calls readRegister() between the time the ram image is written and the time the register is written then it will not get the correct value for the contents of the register.
Dec 5 '08 #5

P: 66
Thanks guys!! We are convinced of moving away from bit fields - and I will work on your suggestions now...
Dec 5 '08 #6

Post your reply

Sign in to post your reply or Sign up for a free account.