Bitfields in a heterogenous environment

G

GalenTX

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
 
X

xarax

GalenTX said:
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!
 
E

Eric Sosman

GalenTX said:
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.
 
G

GalenTX

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.
 
E

Eric Sosman

GalenTX said:
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!
 
G

GalenTX

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.
 
G

GalenTX

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,078
Latest member
MakersCBDBlood

Latest Threads

Top