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

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • dissectcode
    New Member
    • Jul 2008
    • 66

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

    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??
  • donbock
    Recognized Expert Top Contributor
    • Mar 2008
    • 2427

    #2
    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".

    Comment

    • dissectcode
      New Member
      • Jul 2008
      • 66

      #3
      Originally posted by donbock
      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.
      .

      Like this?? :

      Code:
      typedef struct {
      
        uint32_t reg1 : 25;
        uint32_t reg2 : 5;
        uint32_t reg3 : 2;
      
      } 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?

      Comment

      • Banfa
        Recognized Expert Expert
        • Feb 2006
        • 9067

        #4
        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

        Code:
        uint32_t reg = *((volatile uint32_t *)REGISTER_ADDRESS);
        
        uint32_t reg3 = (reg & 0xC0000000) >> 30;
        
        // 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.

        Comment

        • donbock
          Recognized Expert Top Contributor
          • Mar 2008
          • 2427

          #5
          However, instead of sprinkling references to REGISTER_ADDRES S throughout your code, I suggest you provide two access functions:

          unsigned long readRegister(vo id);
          void writeRegister(u nsigned long value);

          These functions dereference the REGISTER_ADDRES S, 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:
          Code:
          unsigned long value;
          value = readRegister();
          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.

          Comment

          • dissectcode
            New Member
            • Jul 2008
            • 66

            #6
            Thanks guys!! We are convinced of moving away from bit fields - and I will work on your suggestions now...

            Comment

            Working...