How to check bit fields order.

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

    #1

    How to check bit fields order.

    Hi everyone - I was reading in the K&R C book (on page 150) that (Bit) Fields are assigned left to right on some machines and right to left on others. This means listing the bit fields from most significant to least, or vice versa.

    So I am creating a couple header files for drivers which contain structures of all the relevant bit fields....I need to "test" how my company's compiler orders the bit fields.

    I was thinking about creating a file with one of the structures which lists the bit fields...and testing that. But how do I call it, and check that the first or last bit was read (I am also new to C) Please help! thanks!
  • donbock
    Recognized Expert Top Contributor
    • Mar 2008
    • 2427

    #2
    Originally posted by dissectcode
    I need to "test" how my company's compiler orders the bit fields.
    Why do you feel it is necessary to know precisely how bit fields are ordered by your compiler? If you're careful to write portable code then it won't matter to you how bit fields are ordered.

    Exceptions I can think of:

    1. You might be using bit fields to overlay a memory-mapped I/O register. Obviously, you can't be certain this strategy works unless you know how the compiler orders the bit fields. That's the precise reason why I advise you not to use bit fields in this way -- use bit masks instead.

    2. You're writing a structure (that contains bit fields) to a file that might be read by another machine using a different compiler with different bit field ordering rules. You should define the format of portable data files carefully and completely without any reference to C/C++ keywords. Then write portable software that creates a data file conforming to that specifications.

    Comment

    • dissectcode
      New Member
      • Jul 2008
      • 66

      #3
      In all possibility I could be mistaken when I think that the "compiler" orders the bit fields. Anyway - they need to be in an order, since they are mapping the hardware registers...ple ase help. thanks!

      Comment

      • Banfa
        Recognized Expert Expert
        • Feb 2006
        • 9067

        #4
        Determining the Order of Bit Fields

        Method 1

        RTFM! Your platform will undoubtedly have a platform guide (or user guide) and this is the sort of information it contains as well as such things as endianess, default sign of a char, processor bit size and other important stuff (if you happen to be writing non-portable code)

        Method 2

        Create a structure with bit fields in them, write 0 to them all, then write 1 to one of them. Put a break point on the following line of code and run the program in the platforms debugger.

        Method 3

        Don't bother, do what donblock said in 1 above use bit masks to access your hardware registers it is portable.

        Comment

        • donbock
          Recognized Expert Top Contributor
          • Mar 2008
          • 2427

          #5
          Originally posted by dissectcode
          In all possibility I could be mistaken when I think that the "compiler" orders the bit fields. Anyway - they need to be in an order, since they are mapping the hardware registers...ple ase help. thanks!
          I say again ... reconsider this approach. Suppose you do confirm that your compiler's bit field order matches the hardware registers. That doesn't mean that a newer version of the same compiler won't break your code by ordering the bit fields differently -- let alone what problems you may face if you change compiler vendor.

          If you really do insist on this course of action then declare a union that overlays a char array with some bit fields. Repeatedly set the bit fields to different values and print the char array until you understand how your compiler organizes the bit fields. Then comment the operational bit field declaration like your life depends on it. Explain the bit field ordering that you expect; explain the hardware register layout that you're trying to match; explain how badly things will go wrong (not til run-time!) if a compiler upgrade changes the bit field ordering.

          I insist on maximum portability. I don't let any of the sw engineers working for me use bit fields for memory-mapped I/O.

          Comment

          • dissectcode
            New Member
            • Jul 2008
            • 66

            #6
            Originally posted by donbock
            I say again ... reconsider this approach. Suppose you do confirm that your compiler's bit field order matches the hardware registers. That doesn't mean that a newer version of the same compiler won't break your code by ordering the bit fields differently -- let alone what problems you may face if you change compiler vendor.
            Hello - I appreciate your feedback. What would be a better method, then, to "map" bit fields to registers? I would like to make sure I am doing the right thing for my company.
            thanks!

            Comment

            • drhowarddrfine
              Recognized Expert Expert
              • Sep 2006
              • 7434

              #7
              Hm. Maybe I'm not thinking of something but the compiler doesn't arrange the bit fields but that the processor it runs on does. iow, bit 0-x are the same in all computers but the processor may fetch them 'big-endian'/'little-endian' but it doesn't matter to the compiler or software. In networking, endian matters and bits/bytes are swapped around on both ends sometimes.

              Particularly if you are trying to interface directly with the processor in an embedded system, bit fields are set all the time.

              But maybe I'm thinking like the hardware engineer that I am.

              Comment

              • donbock
                Recognized Expert Top Contributor
                • Mar 2008
                • 2427

                #8
                Nonportable aspects of bit fields (from the C89 Standard)

                Undefined behavior:
                1. A bit-field is declared with a type other than int, signed int, or unsigned int.

                Implementation-defined behavior:
                2. Whether a 'plain' int bit-field is treated as a signed int bit-field or as an unsigned int bit-field.
                3. The order of allocation of bit-fields within a storage-unit.
                4. The size of bit-field storage-units.
                5. The maximum size of a bit field.
                6. Whether a bit-field can straddle a storage-unit boundary. If a bit-field would cross a forbidden boundary, the compiler may issue a warning or it may silently move the bit-field to the next storage-unit.
                7. An unnamed bit field of length 0 commands the compiler to pack no more bit fields in the current storage-unit. The storage-unit size is implementation dependent so the effect of this feature is also implementation dependent.

                Kernighan & Ritchie (section 6.9) says
                "Almost everything about [bit] fields is implementation-dependent."

                One final problem: bit-fields will almost certainly not work properly on a write-only output register.

                Comment

                • donbock
                  Recognized Expert Top Contributor
                  • Mar 2008
                  • 2427

                  #9
                  Originally posted by dissectcode
                  Hello - I appreciate your feedback. What would be a better method, then, to "map" bit fields to registers? I would like to make sure I am doing the right thing for my company.
                  thanks!
                  1. Not required, but I recommend you use specific-length typedefs for memory-mapped I/O words. That way you can simply redefine the typedef if a compiler upgrade changes the sizes of the built-in integer types.

                  2. Access the register through a pointer to the appropriate type. Use pointer-to-volatile. Assigning the address value to that pointer is inherently non-portable so don't worry about it.

                  3. For a register you intend to write to; determine if the software can read back the values that were written to it. If not, then you need to keep an image of the register in RAM. Don't forget to initialize the image and register. Notice that this creates a thread-safety risk: there is a timing window between when a new value goes into the image and when it goes into the register.

                  4. For read-only registers, declare the pointer as a pointer-to-volatile-const.

                  5. Use masking and shifting to set specific bits within a register; or extract specific bits from a register. It is easier if your fields are all unsigned. Here you're explicitly doing what the compiler hides from you if you use the bit-field feature. Avoid magic numbers -- use macros for the mask and shift values.

                  6. Perform the actual memory access through a function, one for reading and one for writing. Best if they are in their own file so the pointer-to-register is only useable by them, insuring that nobody breaks the rules by dereferencing the pointer themself. You can then include codec software as needed to handled endian conversions. This also limits the number of spots where you may need to single-thread to avoid race conditions.

                  For example:
                  Code:
                  typedef unsigned long HW_WORD;
                  #define THEREGISTER_INIT_VALUE 0x000000FC
                  static volatile HW_WORD * const theRegister = 0x8000400C;
                  static HW_WORD theRegisterImage;
                  
                  void writeRegister(HW_WORD value) {
                      theRegisterImage = value;
                      *theRegister = theRegisterImage;
                  }
                  
                  HW_WORD readRegister(void) {
                      return theRegisterImage;
                  }
                  
                  void initializeRegister(void) {
                     writeRegister(THEREGISTER_INIT_VALUE);
                  }

                  Comment

                  • Banfa
                    Recognized Expert Expert
                    • Feb 2006
                    • 9067

                    #10
                    Originally posted by drhowarddrfine
                    Hm. Maybe I'm not thinking of something but the compiler doesn't arrange the bit fields but that the processor it runs on does.
                    Not quite right.

                    The compiler doesn't arrange the bit order in a byte but that the processor it runs on does.

                    That is not the same as a bit field in a defined structure. When you declare a bit field the compiler reserves some space (at least 1 byte but it can be more depending on the compiler/platform). Then within that allocated byte(s) the compiler chooses which bit it will use to represent the bit field you declared. It has free reign to allocate any bit (from 3 in dons post).

                    Bit order in the byte is hardware dependent but bit field order in the data allocated to hold that bit field is compiler dependent.

                    Comment

                    • drhowarddrfine
                      Recognized Expert Expert
                      • Sep 2006
                      • 7434

                      #11
                      Yeah, I was forgetting about the distinction of bit "fields" vs bits in a register.

                      Comment

                      • Kevin H
                        New Member
                        • Oct 2008
                        • 2

                        #12
                        donblock:

                        "One final problem: bit-fields will almost certainly not work properly on a write-only output register."

                        Why is this so?

                        Comment

                        • donbock
                          Recognized Expert Top Contributor
                          • Mar 2008
                          • 2427

                          #13
                          Originally posted by Kevin H
                          donblock:

                          "One final problem: bit-fields will almost certainly not work properly on a write-only output register."

                          Why is this so?
                          By "write-only output register" I mean a memory-mapped output register that can be written to, but cannot be read.

                          Suppose you perform a bit-field operation that sets or clears one bit in regular memory. The compiler will typically implement the single bit-field assignment statement with the following sequence:
                          ... read memory location into processor register
                          ... set/clear appropriate bit in the processor register
                          ... write processor register back to memory location

                          Any time you write to a bit-field there will almost certainly be an implicit read operation. That's fine for normal memory. However, the implicit read will fetch a garbage value from a write-only register. As a result, bits outside the bit-field you're trying to change could be corrupted.

                          The best way to deal with write-only registers is to work with a RAM image of the register. First put the new value in the RAM image, then copy that value to the register. However, you must initialize the register in order to insure that the RAM image matches the last value written to the register.

                          Comment

                          • Kevin H
                            New Member
                            • Oct 2008
                            • 2

                            #14
                            donblock:

                            Thanks. This should really help with an argument I'm having at work.

                            Comment

                            • zhaobey
                              New Member
                              • Nov 2008
                              • 1

                              #15
                              #include<iostre am>
                              using namespace std;
                              union Foo
                              {
                              int i;
                              char c;
                              };
                              int main()
                              {
                              Foo foo;
                              foo.i=1;
                              if(foo.c==1)
                              cout<<"little_e ndian"<<endl;
                              else
                              cout<<"bigger_e ndain"<<endl;
                              }

                              Comment

                              Working...