Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C bitfield element with non-contiguous layout

I'm looking for input on the most elegant interface to put around a memory-mapped register interface where the target object is split in the register:

union __attribute__ ((__packed__)) epsr_t {
    uint32_t storage;
    struct {
        unsigned reserved0    : 10;
        unsigned ICI_IT_2to7  :  6; // TOP HALF
        unsigned reserved1    :  8;
        unsigned T            :  1;
        unsigned ICI_IT_0to1  :  2; // BOTTOM HALF
        unsigned reserved2    :  5;
    } bits;
};

In this case, accessing the single bit T or any of the reserved fields work fine, but to read or write the ICI_IT requires code more like:

union epsr_t epsr;
// Reading:
uint8_t ici_it = (epsr.bits.ICI_IT_2to7 << 2) | epsr.bits.ICI_IT_0to1;
// Writing:
epsr.bits.ICI_IT_2to7 = ici_it >> 2;
epsr.bits.ICI_IT_0to1 = ici_it & 0x3;

At this point I've lost a chunk of the simplicity / convenience that the bitfield abstraction is trying to provide. I considered the macro solution:

#define GET_ICI_IT(_e)      ((_e.bits.ICI_IT_2to7 << 2) | _e.bits.ICI_IT_0to1)
#define SET_ICI_IT(_e, _i)  do {\
    _e.bits.ICI_IT_2to7 = _i >> 2;\
    _e.bits.ICI_IT_0to1 = _i & 0x3;\
    while (0);

But I'm not a huge fan of macros like this as a general rule, I hate chasing them down when I'm reading someone else's code, and far be it from me to inflict such misery on others. I was hoping there was a creative trick involving structs / unions / what-have-you to hide the split nature of this object more elegantly (ideally as a simple member of an object).

like image 210
Pat Avatar asked Jan 14 '13 03:01

Pat


1 Answers

I don't think there's ever a 'nice' way, and actually I wouldn't rely on bitfields... Sometimes it's better to just have a bunch of exhaustive macros to do everything you'd want to do, document them well, and then rely on them having encapsulated your problem...

#define ICI_IT_HI_SHIFT   14
#define ICI_IT_HI_MASK    0xfc
#define ICI_IT_LO_SHIFT   5
#define ICI_IT_LO_MASK    0x02

// Bits containing the ICI_IT value split in the 32-bit EPSR
#define ICI_IT_PACKED_MASK  ((ICI_IT_HI_MASK << ICI_IT_HI_SHIFT) |     \
                             (ICI_IT_LO_MASK << ICI_IT_LO_SHIFT))

// Packs a single 8-bit ICI_IT value x into a 32-bit EPSR e
#define PACK_ICI_IT(e,x)  ((e & ~ICI_IT_PACKED_MASK) |                 \
                           ((x & ICI_IT_HI_MASK) << ICI_IT_HI_SHIFT) | \
                           ((x & ICI_IT_LO_MASK) << ICI_IT_LO_SHIFT)))

// Unpacks a split 8-bit ICI_IT value from a 32-bit EPSR e
#define UNPACK_ICI_IT(e)  (((e >> ICI_IT_HI_SHIFT) & ICI_IT_HI_MASK) | \
                           ((e >> ICI_IT_LO_SHIFT) & ICI_IT_LO_MASK)))

Note that I haven't put type casting and normal macro stuff in, for the sake of readability. Yes, I get the irony in mentioning readability...

like image 185
paddy Avatar answered Sep 29 '22 23:09

paddy