bit fields and data structure

M

ma740988

I often peruse source that use memory mapped registers as a
configuration and/or command and status interface between embedded
software and FPGA logic. In doing so you're given a register name
accompanied by descriptions and a location in memory for reading/
writing data.

For ex:.

Revision Register - Location 0x0000
[31...15] spare
[14...00] number


Control Register - Location 0x0001
[31..3] -- Spare
[2] -- Address Line 3
[1] -- Address Line 2
[0] -- Address Line 1

To describe these registers in source one could do:

struct RevisionRegister {
unsigned short number : 16;
unsigned short spare : 16;
};

struct ControlRegister {
unsigned int Spare : 29;
unsigned short AddressLine3 : 1;
unsigned short AddressLine2 : 1;
unsigned short AddressLine1 : 1 ;
} ;

The trouble with the composite type ControlRegister is sizeof
(ControlRegister) is not 32 bits. That said, its not possible to
receive ControlRegister in it's current form from an end user and
overlay it at the appropriate location. The fundamental issue
surrounds the use of bitfields and implementation defined nature of
POD types. The question: What are some of the - if you will - tips
for dealing with this issue?

Would it make sense to eliminate the fields ... so now:

struct ControlRegister {
unsigned int Spare ;
unsigned int AddressLine3 ;
unsigned int AddressLine2 ;
unsigned int AddressLine1 ;
} ;

Upon receipt of the type layout ContolRegister (something I have to do
anyways) according to the needs of the memory mapped location.

Trouble with this is you give the end user free reign to put in
values outside of the bit range.
 
M

Maxim Yegorushkin

I often peruse source that use memory mapped registers as a
configuration and/or command and status interface between embedded
software and FPGA logic.  In doing so you're given a register name
accompanied by descriptions and a location in memory for reading/
writing data.

For ex:.

Revision Register  - Location 0x0000
[31...15]    spare
[14...00]    number

Control Register - Location 0x0001
[31..3] -- Spare
[2]  -- Address Line 3
[1]  -- Address Line 2
[0]  -- Address Line 1

To describe these registers in source one could  do:

  struct RevisionRegister {
    unsigned short  number      :  16;
    unsigned short  spare         :  16;
  };

struct ControlRegister {
  unsigned int      Spare              :  29;
  unsigned short AddressLine3 :  1;
  unsigned short AddressLine2 :  1;
  unsigned short AddressLine1  : 1 ;

} ;

The trouble with the composite type ControlRegister is sizeof
(ControlRegister) is not 32 bits.

It is because bit-fields of different type can not be merged. Fix:

struct ControlRegister {
unsigned Spare : 29;
unsigned AddressLine3 : 1;
unsigned AddressLine2 : 1;
unsigned AddressLine1 : 1 ;
} ;
That said, its not possible to
receive ControlRegister in it's current form from an end user and
overlay it at the appropriate location.   The fundamental issue
surrounds the use of bitfields and implementation defined nature of
POD types.  The question:  What are some of the - if you will - tips
for dealing with this issue?

Another issue is that bit-field bit order can be little or big-endian
depending on the platform and the compiler. A portable way to read
mapped registers is to read the register, convert it from the byte
order of the hardware that exposes that register into the byte order
of the cpu (if byte orders are the same this is a noop), and then
apply mask by using bitwise and operator to extract the interesting
bits. This way you don't depend on the bit-field bit order.
 
M

ma740988

I often peruse source that use memory mapped registers as a
configuration and/or command and status interface between embedded
software and FPGA logic.  In doing so you're given a register name
accompanied by descriptions and a location in memory for reading/
writing data.
Revision Register  - Location 0x0000
[31...15]    spare
[14...00]    number
Control Register - Location 0x0001
[31..3] -- Spare
[2]  -- Address Line 3
[1]  -- Address Line 2
[0]  -- Address Line 1
To describe these registers in source one could  do:
  struct RevisionRegister {
    unsigned short  number      :  16;
    unsigned short  spare         :  16;
  };
struct ControlRegister {
  unsigned int      Spare              :  29;
  unsigned short AddressLine3 :  1;
  unsigned short AddressLine2 :  1;
  unsigned short AddressLine1  : 1 ;
The trouble with the composite type ControlRegister is sizeof
(ControlRegister) is not 32 bits.

It is because bit-fields of different type can not be merged. Fix:

    struct ControlRegister {
      unsigned Spare :  29;
      unsigned AddressLine3 : 1;
      unsigned AddressLine2 : 1;
      unsigned AddressLine1 : 1 ;
    } ;
That said, its not possible to
receive ControlRegister in it's current form from an end user and
overlay it at the appropriate location.   The fundamental issue
surrounds the use of bitfields and implementation defined nature of
POD types.  The question:  What are some of the - if you will - tips
for dealing with this issue?

Another issue is that bit-field bit order can be little or big-endian
depending on the platform and the compiler. A portable way to read
mapped registers is to read the register, convert it from the byte
order of the hardware that exposes that register into the byte order
of the cpu (if byte orders are the same this is a noop), and then
apply mask by using bitwise and operator to extract the interesting
bits. This way you don't depend on the bit-field bit order.
I think I understand what you're alluding to on reads (didn't complete
the sample code based on your description so i could post it), what
about write operations?
 
J

James Kanze

I often peruse source that use memory mapped registers as a
configuration and/or command and status interface between
embedded software and FPGA logic. In doing so you're given
a register name accompanied by descriptions and a location
in memory for reading/ writing data.
For ex:.
Revision Register - Location 0x0000
[31...15] spare
[14...00] number
Control Register - Location 0x0001
[31..3] -- Spare
[2] -- Address Line 3
[1] -- Address Line 2
[0] -- Address Line 1
To describe these registers in source one could do:
struct RevisionRegister {
unsigned short number : 16;
unsigned short spare : 16;
};
struct ControlRegister {
unsigned int Spare : 29;
unsigned short AddressLine3 : 1;
unsigned short AddressLine2 : 1;
unsigned short AddressLine1 : 1 ;
} ;

Maybe. How a compiler lays out bitfields is implementation
defined.

It is with my compilers. But it definitely depends on the
compiler; a compiler for a 16 bit machine is likely to
systematically put any bit field greater or equal to 16 bits in
a separate word.
It is because bit-fields of different type can not be merged.

Since when? Both g++ and Sun CC merge them, under Solaris.
struct ControlRegister {
unsigned Spare : 29;
unsigned AddressLine3 : 1;
unsigned AddressLine2 : 1;
unsigned AddressLine1 : 1 ;
} ;

It would be a pretty poor implementation which generated a
different layout for these two. I'm not even sure it's legal.
(The actual layout is implementation defined, and the standard
isn't really clear about the restrictions, if any. But laying
out these two differently certainly violates the spirit of the
standard, if not the actual wording.)
Another issue is that bit-field bit order can be little or
big-endian depending on the platform and the compiler.

The layout is implementation defined, with very few
restrictions. But if you're reading and writing memory mapped
registers, portability is presumably not a concern. And
implementation defined means documented, so you know what the
compiler does. For any one particular version of the compiler,
of course, but from a QoI point of view, no reasonable compiler
will change the basic principles from one version to the next.
(Although... I've seen changes more radical than that, like the
byte order in a long. From a QoI point of view, the quality
wasn't there, but it was still the compiler we had to use.)
A portable way to read mapped registers is to read the
register, convert it from the byte order of the hardware that
exposes that register into the byte order of the cpu (if byte
orders are the same this is a noop), and then apply mask by
using bitwise and operator to extract the interesting bits.
This way you don't depend on the bit-field bit order.

"A portable way to read mapped registers"? Sounds like an
oxymoron to me.:)

Seriously, of course, such problems don't affect just mapped
registers, and this is the way to solve them portably in other
cases (transmission protocols, etc.). The register is slightly
different, in that you can (maybe) read and write words, and not
just bytes, but other than that, the principles are the same.
Using bit fields results in less and cleaner code, but it only
works for mapped registers, and only then if your compiler does
something reasonable, and documents it. (If it doesn't, he
might want to consider changing compilers; under Windows, VC++
is more or less broken with regards to bit fields, but g++ works
fine.)
 
J

James Kanze

On Nov 17, 4:48 am, Maxim Yegorushkin
<[email protected]> wrote:

[...]
I think I understand what you're alluding to on reads (didn't
complete the sample code based on your description so i could
post it), what about write operations?

Same principle. Basically:

enum ControlRegisterMasks
{
spareMask = 0x1FFFFFFF,
addressLine3Mask = 0x20000000,
addressLine2Mask = 0x40000000,
addressLine1Mask = 0x80000000
} ;

enum ControlRegisterShiftCounts
{
spareShiftCount = 0,
addressLine3ShiftCount = 29,
addressLine2ShiftCount = 30,
addressLine1ShiftCount = 31
} ;

typedef unsigned int /* or uint32_t */ ControlRegister ;

int
getAddressLine1(
ControlRegister reg )
{
return (reg & addressLine1Mask) >> addressLine1ShiftCount ;
}

void
setAddressLine1(
ControlRegister& reg,
int value )
{
reg = (reg & ~addressLine1Mask)
| ((value << addressLine1ShiftCount) & addressLine1Mask) ;
}

// ...
 
M

ma740988

Same principle.  Basically:

    enum ControlRegisterMasks
    {
        spareMask        = 0x1FFFFFFF,
        addressLine3Mask = 0x20000000,
        addressLine2Mask = 0x40000000,
        addressLine1Mask = 0x80000000
    } ;

    enum ControlRegisterShiftCounts
    {
        spareShiftCount        =  0,
        addressLine3ShiftCount = 29,
        addressLine2ShiftCount = 30,
        addressLine1ShiftCount = 31
    } ;

    typedef unsigned int /* or uint32_t */ ControlRegister ;

    int
    getAddressLine1(
        ControlRegister     reg )
    {
        return (reg & addressLine1Mask) >> addressLine1ShiftCount ;
    }

    void
    setAddressLine1(
        ControlRegister&    reg,
        int                 value )
    {
        reg = (reg & ~addressLine1Mask)
            | ((value << addressLine1ShiftCount) & addressLine1Mask) ;
    }

    //  ...

Hows this link (http://www.open-std.org/jtc1/sc22/wg14/www/docs/
n929.pdf) for an idea. I like the concept in the document.

So given a register layout:
destination : 7 bits
source : 7 bits
enable_this : 1 bit
enable_that : 1 bit
spare : 16 bits

One option I'm experimenting with surrounds the use of access types
(borrowed from Ada's access types )

struct some_hw_register {
bool enable_that ; // : 1
bool enable_this ; // : 1
unsigned int source ; // : 7
unsigned int destination ; // : 7
};

class HW_Map : public Access_Register {
public :
Access_Hw_Reg < unsigned int, 0 > enable_that;
Access_HW_Reg < unsigned int, 1 > enable_that;
Access_HW_Reg < unsigned int, 2, 7 > source; // bit
position 2, 7 bits
Access_HW_Reg < unsigned int, 9, 7 > destination; // bit
position 9, 7 bits
HW_Map ( void* ptr ) : Access_Register ( ptr ) {}

inline void Read ( some_hw_register& hw ) {
hw.enable_that = Access_Register::Read ( enable_that ) ;
hw.enable_this = Access_Register::Read ( enable_this ) ;
hw.source = Access_Register::Read ( source) ;
hw.destination = Access_Register::Read ( destination ) ;
}
inline void Write ( some_hw_register& hw ) {
Access_Register::Write( enable_that, hw.enable_that ) ;
Access_Register::Write( enable_this, hw. enable_this ) ;
Access_Register::Write( source, hw.source ) ;
Access_Register::Write( destination, hw.destination ) ;
}
};

Option 2:
Now page 8 of the attached provides a cool idea. I suspect I could
do:
HW_MEMORY < read_method, write_method, addr_const, addr_range >
MEM1 ;
Later in read_method and write_method, I could access the individual
variables. Not sure how to structure this one. I'm lost :)

I understand that there's portability issues here and such there's no
clean way to deal with bit field manipulations except avoid them.
Thats nice, however, in the REAL world - a world that deals with
signal processing applications executing on DSPs (the world I live
in), a world that deals with mobile phones etc. etc. there's no way
around this.
 
J

James Kanze

Hows this link (http://www.open-std.org/jtc1/sc22/wg14/www/docs/ n929.pdf)
for an idea. I like the concept in the document.

I haven't studied it in detail, but it looks like a proposal for
something to add to the standard. Which I don't think was
accepted. In some ways, it is a solution looking for a
problem. It certainly provides something more directly readable
than the current situation, and it provides a portable syntax.
But hardware I/O registers are by their very nature unportable,
so the portable syntax doesn't buy you much. And it's not as if
bit shifting etc. cause any real problems.
 
M

ma740988

In some ways, it is a solution looking for a
problem.  It certainly provides something more directly readable
than the current situation, and it provides a portable syntax.
But hardware I/O registers are by their very nature unportable,
so the portable syntax doesn't buy you much.  And it's not as if
bit shifting etc. cause any real problems.
Fair enough! I was headed down the path with sets_this_variable and
gets_this_variable with the bit fiddling done within the methods. At
some point I thought there has to be a better way :) then I ran across
this article:

http://www.artima.com/cppsource/safelabels.html
A novel idea with regards to individual bits but it doesn't appear to
address a range of bits.

Oh well
Thanks
 

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,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top