Bitfields in Managed Code

What are Bitfields?

If you’re asking this question, then you probably should stay away, but I’ll answer anyway. A bitfield is a method in C/C++ to map sub-components of a structure that is stored in a primitive type. For example, the LTC6946 synthesizer has several registers each of which is contained in a single byte of data.
ADDR MSB [6] [5] [4] [3] [2] [1] LSB R/W DEFAULT
h08 BST FILT RFO OD R/W hF9
The common method to deal with this register structure is to use bit ANDs and shifts. Ultimately the code is easier to read if we could use something like:
register.bst = true;
C/C++ has a way to make this happen, and at the same time it ensures that limits are set, that is I can’t set register.filt to more than 3.
 
struct LTC6946_ReferenceOutput
{
unsigned out_div : 3; // Default 0x1 | Output divider value
unsigned rf_out_pwr : 2; // Default 0x3 | Output Power
unsigned ref_buff_filt : 2; // Default 0x3 | Reference input buffer filter
bool ref_buff_boost : 1; // Default true | Reference input boost current
};
Now, endianess matters here, so structure the code for the endianess of the system this will run on. Typically managed code will run on a little endian machine so we start with the low order parts of the register.

Adding in a Union

This part is not strictly necessary, but it makes reading and writing to the device much simpler. A union in C/C++ allows two datatypes to occupy the same memory space. In the case of a register it allows us to get access to the underlying value without a bunch of pointer work.
template<typename regType>
union Reg32
{
UInt32 val;
regType reg;
};

template<typename regType>
union Reg16
{
UInt16 val;
regType reg;
};

template<typename regType>
union Reg8
{
Byte val;
regType reg;

};

To use this with the LTC6946_ReferenceOutputStructure we declare a variable of the type:
Reg8<LTC6946_ReferenceOutput>

Managed Code

If you try to use this structure in managed code it won’t work. In managed C++ you’ll get an error like “error C4368: cannot define ‘register’ as a member of managed…”. The trick is in managed C++ you can use a pointer to the structure. Now we can make this work, but it takes a bit more code than straight C/C++.

Creating the Basics

public ref class LTC6946RefOut
{
public:
LTC6946RefOut() 
{
m_fields = new Reg8<LTC6946_ReferenceOutput>();
}

property unsigned out_div
{
unsigned get() { return m_fields->reg.out_div; }
void set(unsigned value) { m_fields->reg.out_div = value; }
};

property unsigned rf_out_pwr
{
unsigned get() { return m_fields->reg.rf_out_pwr; }
void set(unsigned value) { m_fields->reg.rf_out_pwr = value; }
};

property unsigned ref_buff_filt
{
unsigned get() { return m_fields->reg.ref_buff_filt; }
void set(unsigned value) { m_fields->reg.ref_buff_filt = value; }
};

property bool ref_buff_boost
{
bool get() { return m_fields->reg.ref_buff_boost; }
void set(bool value) { m_fields->reg.ref_buff_boost = value; }
};

property Byte val
{
Byte get() { return m_fields->val; }
void set(Byte value) { m_fields->val = value; }
}

 virtual String^ ToString() override
 {
  return m_fields->val.ToString();
 }

private:
Reg8<LTC6946_ReferenceOutput> *m_fields;
};
This will allow the bitfield to be used in managed code including C# and Visual Basic. There are a few clean up tasks yet to do before the code is complete.

Garbage Collection

To get garbage collection to work properly we need to add the destructor and finalizer methods. Each method should delete the m_fields pointer.
!LTC6946RefOut() 
{
delete m_fields;
}
~LTC6946RefOut() 
{
!LTC6946RefOut();
}

By adding these to the class the managed garbage collection will properly clean up the unmanaged code.

Abstraction

There is a fair amount of code here that can be tedious to re-type. The individual properties that map to fields in the bitfield cannot be abstracted away, but the rest can.
template<typename regType>
public ref class CBitField8 abstract
{
public:
CBitField8()
{
m_fields = new Reg8<regType>();
}

!CBitField8()
{
delete m_fields;
}

~CBitField8()
{
!CBitField8();
}

property Byte val
{
Byte get() { return m_fields->val; }
void set(Byte value) { m_fields->val = value; }
}

virtual String^ ToString() override
{
return m_fields->val.ToString();
}

protected:
Reg8<regType> *m_fields;
};

Repeat for 16 and 32 bit bitfields.

Final Form

After the abstraction is in place our initial class is simplified to:
public ref class LTC6946RefOut : CBitField8<LTC6946_ReferenceOutput>
{
public:
LTC6946RefOut() : CBitField8()
{
}

property unsigned out_div
{
unsigned get() { return m_fields->reg.out_div; }
void set(unsigned value) { m_fields->reg.out_div = value; }
};

property unsigned rf_out_pwr
{
unsigned get() { return m_fields->reg.rf_out_pwr; }
void set(unsigned value) { m_fields->reg.rf_out_pwr = value; }
};

property unsigned ref_buff_filt
{
unsigned get() { return m_fields->reg.ref_buff_filt; }
void set(unsigned value) { m_fields->reg.ref_buff_filt = value; }
};

property bool ref_buff_boost
{
bool get() { return m_fields->reg.ref_buff_boost; }
void set(bool value) { m_fields->reg.ref_buff_boost = value; }
};
};
This entry was posted in Mike and tagged .