Bitfields in a heterogenous environment

Discussion in 'C Programming' started by GalenTX, Feb 4, 2005.

  1. GalenTX

    GalenTX Guest

    I am looking for opinions on a possible approach to coping with
    bitfields in a heterogenous environment.

    We've got a bunch of legacy code which maps to hardware using
    bitfields. The code was developed for a PowerPC/VxWorks/Tornado
    enviromant, with the bits packed in a big-endian manner: msb first. We
    now have to develop an Intel/Windows/MSVC++ version (lsb assigned
    first) which must maintain compatibility with the hardware interface
    and use the same source for manipulating the related bitfields. So
    discussion of better approaches than using bitfields is moot.

    I am considering the following approach to minimize impact to the
    legacy code. It uses nested macro calls to define the bitfields in
    forward or reverse order according to the environment:

    #ifdef BITFIELDS_LITTLE_ENDIAN
    #define DEFINE_BITFIELDS_MSB_FIRST(A,B) B A
    #else
    #define DEFINE_BITFIELDS_MSB_FIRST(A,B) A B
    #endif

    struct bitfield_struct
    {
    DEFINE_BITFIELDS_MSB_FIRST( int bit31 : 1; ,
    DEFINE_BITFIELDS_MSB_FIRST( int bit20_30 : 11; ,
    DEFINE_BITFIELDS_MSB_FIRST( int bit10_19 : 10; ,
    DEFINE_BITFIELDS_MSB_FIRST( int bit1_9 : 9; ,
    int bit0 : 1;
    ))))
    };

    The preprocessor generates the following if BITFIELDS_LITTLE_ENDIAN is
    defined:

    struct bitfield_struct
    {

    int bit0 : 1; int bit1_9 : 9; int bit10_19 : 10; int bit20_30 : 11; int
    bit31 : 1;
    };

    or otherwise,

    struct bitfield_struct
    {

    int bit31 : 1; int bit20_30 : 11; int bit10_19 : 10; int bit1_9 : 9;
    int bit0 : 1;
    };

    Of course, we will have to be careful to fill every bit position. We
    will nest the calls to define exactly 32 bits worth at a time, so the
    maximum nesting of the macro calls would be 32. We will also have to
    put up with the collapse of multiple lines of code into a single line
    for compiler error reporting and during debug.

    If we are to avoid touching bitfield-processing code, the only other
    option we have identified is to define the structures twice:

    #ifdef BITFIELDS_LITTLE_ENDIAN
    struct bitfield_struct
    {
    int bit0 : 1;
    int bit1_9 : 9;
    int bit10_19 : 10;
    int bit20_30 : 11;
    int bit31 : 1;
    };
    #else
    struct bitfield_struct
    {
    int bit31 : 1;
    int bit20_30 : 11;
    int bit10_19 : 10;
    int bit1_9 : 9;
    int bit0 : 1;
    };
    #endif

    I like the nested-macro approach because we only code the idea of the
    hardware bit map once and inserting "DEFINE_BITFIELDS_MSB_FIRST(" in
    front of the existing structures seems less error prone than trying to
    manually invert the order of definition. I was surprised to not find
    anything similar in previous discussions here and am wondering if it
    has any drawbacks I have not considered. If it helps to focus the
    discussion, we have no plans to go beyond the current 32-bit PowerPC
    and Intel architectures, though we might migrate the Intel platform
    from Windows/MSVC++ to Linux/gcc some day. We have 2000+ bitfield
    definitions to cope with.

    I look forward to your (constructive!) comments.

    -Galen
    GalenTX, Feb 4, 2005
    #1
    1. Advertising

  2. GalenTX

    xarax Guest

    "GalenTX" <> wrote in message
    news:...
    >I am looking for opinions on a possible approach to coping with
    > bitfields in a heterogenous environment.
    >
    > We've got a bunch of legacy code which maps to hardware using
    > bitfields. The code was developed for a PowerPC/VxWorks/Tornado
    > enviromant, with the bits packed in a big-endian manner: msb first. We
    > now have to develop an Intel/Windows/MSVC++ version (lsb assigned
    > first) which must maintain compatibility with the hardware interface
    > and use the same source for manipulating the related bitfields. So
    > discussion of better approaches than using bitfields is moot.


    Well then, you're stuck with poorly implemented legacy code.
    You should've used bit masking at the byte level to have a chance
    at portability to other hardware (at least where CHAR_BIT is
    same).

    > I am considering the following approach to minimize impact to the
    > legacy code. It uses nested macro calls to define the bitfields in
    > forward or reverse order according to the environment:


    Since you're planning on editing every header file to
    insert the macros, maybe you should take the next step
    and just convert the code to use masking. Masking is
    easy to understand, easy to maintain.

    > #ifdef BITFIELDS_LITTLE_ENDIAN
    > #define DEFINE_BITFIELDS_MSB_FIRST(A,B) B A
    > #else
    > #define DEFINE_BITFIELDS_MSB_FIRST(A,B) A B
    > #endif
    >
    > struct bitfield_struct
    > {
    > DEFINE_BITFIELDS_MSB_FIRST( int bit31 : 1; ,
    > DEFINE_BITFIELDS_MSB_FIRST( int bit20_30 : 11; ,
    > DEFINE_BITFIELDS_MSB_FIRST( int bit10_19 : 10; ,
    > DEFINE_BITFIELDS_MSB_FIRST( int bit1_9 : 9; ,
    > int bit0 : 1;
    > ))))
    > };
    >
    > The preprocessor generates the following if BITFIELDS_LITTLE_ENDIAN is
    > defined:
    >
    > struct bitfield_struct
    > {
    >
    > int bit0 : 1; int bit1_9 : 9; int bit10_19 : 10; int bit20_30 : 11; int
    > bit31 : 1;
    > };
    >
    > or otherwise,
    >
    > struct bitfield_struct
    > {
    >
    > int bit31 : 1; int bit20_30 : 11; int bit10_19 : 10; int bit1_9 : 9;
    > int bit0 : 1;
    > };
    >
    > Of course, we will have to be careful to fill every bit position. We
    > will nest the calls to define exactly 32 bits worth at a time, so the
    > maximum nesting of the macro calls would be 32. We will also have to
    > put up with the collapse of multiple lines of code into a single line
    > for compiler error reporting and during debug.


    Yup, you're asking for even more headaches, than if you
    had done it right the first time.

    > If we are to avoid touching bitfield-processing code, the only other
    > option we have identified is to define the structures twice:


    Bite the bullet NOW. Fix the broken code.

    > #ifdef BITFIELDS_LITTLE_ENDIAN
    > struct bitfield_struct
    > {
    > int bit0 : 1;
    > int bit1_9 : 9;
    > int bit10_19 : 10;
    > int bit20_30 : 11;
    > int bit31 : 1;
    > };
    > #else
    > struct bitfield_struct
    > {
    > int bit31 : 1;
    > int bit20_30 : 11;
    > int bit10_19 : 10;
    > int bit1_9 : 9;
    > int bit0 : 1;
    > };
    > #endif


    Hugely worse; a double-maintenance headache. Your junior
    programmers will certainly screw this up.

    > I like the nested-macro approach because we only code the idea of the
    > hardware bit map once and inserting "DEFINE_BITFIELDS_MSB_FIRST(" in
    > front of the existing structures seems less error prone than trying to
    > manually invert the order of definition. I was surprised to not find
    > anything similar in previous discussions here and am wondering if it
    > has any drawbacks I have not considered. If it helps to focus the
    > discussion, we have no plans to go beyond the current 32-bit PowerPC
    > and Intel architectures, though we might migrate the Intel platform
    > from Windows/MSVC++ to Linux/gcc some day. We have 2000+ bitfield
    > definitions to cope with.
    >
    > I look forward to your (constructive!) comments.


    Good luck, you'll need it!
    xarax, Feb 4, 2005
    #2
    1. Advertising

  3. GalenTX

    Eric Sosman Guest

    GalenTX wrote:
    > I am looking for opinions on a possible approach to coping with
    > bitfields in a heterogenous environment.
    >
    > We've got a bunch of legacy code which maps to hardware using
    > bitfields. The code was developed for a PowerPC/VxWorks/Tornado
    > enviromant, with the bits packed in a big-endian manner: msb first. We
    > now have to develop an Intel/Windows/MSVC++ version (lsb assigned
    > first) which must maintain compatibility with the hardware interface
    > and use the same source for manipulating the related bitfields. So
    > discussion of better approaches than using bitfields is moot.
    >
    > I am considering the following approach to minimize impact to the
    > legacy code. It uses nested macro calls to define the bitfields in
    > forward or reverse order according to the environment:
    >
    > #ifdef BITFIELDS_LITTLE_ENDIAN
    > #define DEFINE_BITFIELDS_MSB_FIRST(A,B) B A
    > #else
    > #define DEFINE_BITFIELDS_MSB_FIRST(A,B) A B
    > #endif
    >
    > struct bitfield_struct
    > {
    > DEFINE_BITFIELDS_MSB_FIRST( int bit31 : 1; ,
    > DEFINE_BITFIELDS_MSB_FIRST( int bit20_30 : 11; ,
    > DEFINE_BITFIELDS_MSB_FIRST( int bit10_19 : 10; ,
    > DEFINE_BITFIELDS_MSB_FIRST( int bit1_9 : 9; ,
    > int bit0 : 1;
    > ))))
    > };
    > [...]


    You seem to have taken care of arranging the bit fields
    in the desired order, but what about the individual bits in
    multi-bit fields like bit1_9? I'm using "order" rather
    loosely here because the bits probably aren't addressable.
    What I'm getting at is that if you do `foo.bit1_9 = 5', how
    do you know which two bits of the 32-bit (probably) struct
    are set?

    There's no portable way to be sure of getting the layout
    you want, so whatever solution you adopt will be compiler-
    (or compilers-) dependent. If you're stuck with bit fields,
    all I can suggest is to hack away and hope for the best; the
    C language as such is of little help in your predicament.

    It occurs to me that there might be a way out of the mess,
    depending on how the code is structured. If (*if*) the bulk
    of the code deals with bits already read from the hardware or
    prepares batches of bits to be written to the hardware, but
    the actual reading and writing takes place in relatively few
    places, you could just let the structs line up any old way and
    provide get() and put() functions that swizzled the bits as
    needed, on the fly. That, in fact, is a pretty good model for
    all such format-matching tasks: Put the format knowledge in a
    few isolated functions and use a "CPU-friendly" representation
    elsewhere. Whether your existing code base is organized so as
    to make this approach attractive is something you'll need to
    judge for yourself.

    --
    Eric Sosman, Feb 4, 2005
    #3
  4. GalenTX

    GalenTX Guest

    I don't think the bit-order within individually named bitfields is an
    issue. Once accessed by name, they should be converted to an int with
    the proper bit order. The issue just occurs with the way the names are
    allocated within the comprising int.

    We contemplated some bit-swizzling, as you say, but decided that was
    going to be much more work and leaves places in your program where the
    data in memory is not organized according to the structures you've
    defined--always an opportunity for errors. The VxWorks system is an
    embedded one, so the hardware interfaces permeate the code.

    In defense of the original programmers, some of the code is 10 years
    old and it was never envisioned that it would be used in any other
    environment. Given the constraint of a static and well-understood
    environment, bit-fields make for the clearest code at the point of use.
    Bit masking and shifting is absolutely more portable, but even with
    macros or functions to hide the complexity, it takes up more visual
    space in the application code, which makes it harder to see the real
    task at hand.
    GalenTX, Feb 4, 2005
    #4
  5. GalenTX

    Eric Sosman Guest

    GalenTX wrote:
    > I don't think the bit-order within individually named bitfields is an
    > issue. Once accessed by name, they should be converted to an int with
    > the proper bit order. The issue just occurs with the way the names are
    > allocated within the comprising int.


    You said the purpose was to match the fields to a
    hardware device, which usually means that the order of
    all the non-ignored bits is important. If you've got
    a bit-order mismatch between the CPU and the device,
    you might store 19 = 10011 in a five-bit field and find
    that the device understood it as 11001 = 25 instead.
    That's a good way to turn a READ SECTOR command into
    LOW-LEVEL FORMAT ...

    But then, perhaps you're not facing such issues.
    If not, fine -- but you're still forced to rely on the
    behaviors of the particular compilers you're using, and
    not on anything guaranteed by the C language. Good luck!

    --
    Eric Sosman, Feb 4, 2005
    #5
  6. GalenTX

    GalenTX Guest

    You're absolutely right, the C language guarantees me nothing here.
    But it would seem odd to assign an int to a bitfield and have the
    compiler place the bits into the int structure containing the bitfield
    in the opposite order that they appear in the int. That would require
    bit reversal, which would seem expensive.

    I'm not talking about the way the hardware is mapped on the processor
    bus. But even there, the hardware guy who wires more significant bits
    on the processor to less significant ones on the device is going to be
    in trouble. I'm also not talking about byte order, either.
    Fortunately, everything we are working with is a uniform byte width and
    is read and written in one place in the code, so we have only one place
    to verify that byte order is correct.

    Again, I think it is a pretty safe bet that bit significance will be
    preserved within a bitfield, regardless of the order in which multiple
    bitfields are allocated space in an int. At least for the limited set
    of compilers I am contemplating, I think I am okay.
    GalenTX, Feb 6, 2005
    #6
  7. GalenTX

    GalenTX Guest

    Trying to pull the discussion back to the point of my original post, I
    need to mention that we am constrained to edit header files only. So,
    for better or worse, we're going to have bitfields, the only question
    is how we go about defining the structure.

    Does anybody see any problem with my preferred approach, the nested
    macro calls? The alternative, to define the structures twice while
    inverting the order manually, seems more error prone in initial
    implementation seems to pose a higher maintenance cost.

    By the way, can anyone suggest a better name for the macro? I'm not
    entirely happy with DEFINE_BITFIELDS_MSB_FIRST.

    -Galen
    GalenTX, Feb 6, 2005
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Juha Kettunen
    Replies:
    9
    Views:
    353
    Leor Zolman
    Apr 10, 2004
  2. Vikas
    Replies:
    2
    Views:
    379
    David Hilsee
    Sep 11, 2004
  3. Régis Troadec

    About bitfields

    Régis Troadec, Feb 14, 2004, in forum: C Programming
    Replies:
    8
    Views:
    388
    Dan Pop
    Feb 17, 2004
  4. rohit
    Replies:
    23
    Views:
    1,719
    Dave Thompson
    Jun 10, 2004
  5. A
    Replies:
    12
    Views:
    452
Loading...

Share This Page