309 lines
11 KiB
C++
309 lines
11 KiB
C++
// vi:set ft=cpp: -*- Mode: C++ -*-
|
|
/*
|
|
* (c) 2012 Alexander Warg <warg@os.inf.tu-dresden.de>,
|
|
* economic rights: Technische Universität Dresden (Germany)
|
|
*
|
|
* License: see LICENSE.spdx (in this directory or the directories above)
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "type_list"
|
|
|
|
/** Our C++ library. */
|
|
namespace cxx {
|
|
|
|
/**
|
|
* Definition for a member (part) of a bit field.
|
|
*
|
|
* \param T The underlying type of the bit field.
|
|
* \param LSB The least significant bit of our bits.
|
|
* \param MSB The most significant bit of our bits.
|
|
*/
|
|
template<typename T, unsigned LSB, unsigned MSB>
|
|
class Bitfield
|
|
{
|
|
private:
|
|
typedef remove_reference_t<T> Base_type;
|
|
|
|
static_assert(MSB >= LSB, "boundary mismatch in bit-field definition");
|
|
static_assert(MSB < sizeof(Base_type) * 8, "MSB outside of bit-field type");
|
|
static_assert(LSB < sizeof(Base_type) * 8, "LSB outside of bit-field type");
|
|
|
|
/**
|
|
* Get the best unsigned type for `bits`.
|
|
*
|
|
* \param BITS Number of bits to cover.
|
|
*/
|
|
template<unsigned BITS> struct Best_type
|
|
{
|
|
template< typename TY > struct Cmp { enum { value = (BITS <= sizeof(TY)*8) }; };
|
|
typedef cxx::type_list<
|
|
unsigned char,
|
|
unsigned short,
|
|
unsigned int,
|
|
unsigned long,
|
|
unsigned long long
|
|
> Unsigned_types;
|
|
typedef cxx::find_type_t<Unsigned_types, Cmp> Type;
|
|
};
|
|
|
|
public:
|
|
enum
|
|
{
|
|
Bits = MSB + 1 - LSB, ///< Number of bits
|
|
Lsb = LSB, ///< index of the LSB
|
|
Msb = MSB, ///< index of the MSB
|
|
};
|
|
|
|
/// Masks for bitswise operation on internal parts of a bitfield
|
|
enum Masks : Base_type
|
|
{
|
|
/** Mask value to get #Bits bits. */
|
|
Low_mask = static_cast<Base_type>(~0ULL) >> (sizeof(Base_type)*8 - Bits),
|
|
/** Mask value to the bits out of a `T`. */
|
|
Mask = Low_mask << Lsb,
|
|
};
|
|
|
|
/**
|
|
* Type to hold at least #Bits bits.
|
|
*
|
|
* This type can handle all values that can be stored in this part of the bit
|
|
* field.
|
|
*/
|
|
typedef typename Best_type<Bits>::Type Bits_type;
|
|
|
|
/**
|
|
* Type to hold at least #Bits + #Lsb bits.
|
|
*
|
|
* This type can handle all values that can be stored in this part of the bit
|
|
* field when they are at the target location (#Lsb bits shifted to the left).
|
|
*/
|
|
typedef typename Best_type<Bits + Lsb>::Type Shift_type;
|
|
|
|
private:
|
|
static_assert(sizeof(Bits_type)*8 >= Bits, "error finding the type to store the bits");
|
|
static_assert(sizeof(Shift_type)*8 >= Bits + Lsb, "error finding the type to keep the shifted bits");
|
|
static_assert(sizeof(Bits_type) <= sizeof(Base_type), "size mismatch for Bits_type");
|
|
static_assert(sizeof(Shift_type) <= sizeof(Base_type), "size mismatch for Shift_type");
|
|
static_assert(sizeof(Bits_type) <= sizeof(Shift_type), "size mismatch for Shift_type and Bits_type");
|
|
|
|
public:
|
|
/**
|
|
* Get the bits out of `val`.
|
|
*
|
|
* \param val The raw value of the whole bit field.
|
|
*
|
|
* \return The bits form #Lsb to #Msb shifted to the right.
|
|
*/
|
|
static constexpr Bits_type get(Shift_type val)
|
|
{ return (val >> Lsb) & Low_mask; }
|
|
|
|
/**
|
|
* Get the bits in place out of `val`.
|
|
*
|
|
* \param val The raw value of the whole bit field.
|
|
*
|
|
* \return The bits from #Lsb to #Msb (unshifted).
|
|
*
|
|
* This means other bits are masked out, however the result is not shifted to
|
|
* the right.
|
|
*/
|
|
static constexpr Base_type get_unshifted(Shift_type val)
|
|
{ return val & Mask; }
|
|
|
|
/**
|
|
* Set the bits corresponding to `val`.
|
|
*
|
|
* \param dest The current value of the whole bit field.
|
|
* \param val The value to set into the bits.
|
|
*
|
|
* \return The new value of the whole bit field.
|
|
*
|
|
* \pre `val` must not contain more than #Bits bits.
|
|
*
|
|
* \note This function does not mask `val` to the right number of bits.
|
|
*/
|
|
static constexpr Base_type set_dirty(Base_type dest, Shift_type val)
|
|
{
|
|
//assert (!(val & ~Low_mask));
|
|
return (dest & ~Mask) | (val << Lsb);
|
|
}
|
|
|
|
/**
|
|
* Set the bits corresponding to `val`.
|
|
*
|
|
* \param dest The current value of the whole bit field.
|
|
* \param val The value shifted #Lsb bits to the left that shall be set into
|
|
* the bits.
|
|
*
|
|
* \return The new value of the whole bit field.
|
|
*
|
|
* \pre `val` must not contain more than #Bits bits shifted #Lsb bits to the
|
|
* left.
|
|
*
|
|
* \note This function does not mask `val` to the right number of bits.
|
|
*/
|
|
static constexpr Base_type set_unshifted_dirty(Base_type dest, Shift_type val)
|
|
{
|
|
//assert (!(val & ~Mask));
|
|
return (dest & ~Mask) | val;
|
|
}
|
|
|
|
/**
|
|
* Set the bits corresponding to `val`.
|
|
*
|
|
* \param dest The current value of the whole bit field.
|
|
* \param val The value to set into the bits.
|
|
*
|
|
* \return The new value of the whole bit field.
|
|
*/
|
|
static Base_type set(Base_type dest, Bits_type val)
|
|
{ return set_dirty(dest, val & Low_mask); }
|
|
|
|
/**
|
|
* Set the bits corresponding to `val`.
|
|
*
|
|
* \param dest The current value of the whole bit field.
|
|
* \param val The value shifted #Lsb bits to the left that shall be set into
|
|
* the bit field.
|
|
*
|
|
* \return the new value of the whole bit field.
|
|
*/
|
|
static Base_type set_unshifted(Base_type dest, Shift_type val)
|
|
{ return set_unshifted_dirty(dest, val & Mask); }
|
|
|
|
/**
|
|
* Get the shifted bits for `val`.
|
|
*
|
|
* \param val The value to set into the bits.
|
|
*
|
|
* \return The raw bit field value.
|
|
*
|
|
* \pre `val` must not contain more than #Bits bits.
|
|
*
|
|
* \note This function does not mask `val` to the right number of bits.
|
|
*/
|
|
static constexpr Base_type val_dirty(Shift_type val) { return val << Lsb; }
|
|
|
|
/**
|
|
* Get the shifted bits for `val`.
|
|
*
|
|
* \param val The value to set into the bits.
|
|
*
|
|
* \return The raw bit field value.
|
|
*/
|
|
static constexpr Base_type val(Bits_type val) { return val_dirty(val & Low_mask); }
|
|
|
|
/**
|
|
* Get the shifted bits for `val`.
|
|
*
|
|
* \param val The value shifted #Lsb bits to the left that shall be set into
|
|
* the bits.
|
|
*
|
|
* \return The raw bit field value.
|
|
*/
|
|
static constexpr Base_type val_unshifted(Shift_type val) { return val & Mask; }
|
|
|
|
/** Internal helper type */
|
|
template< typename TT >
|
|
class Value_base
|
|
{
|
|
private:
|
|
TT v;
|
|
|
|
public:
|
|
constexpr Value_base(TT t) : v(t) {}
|
|
constexpr Bits_type get() const { return Bitfield::get(v); }
|
|
constexpr Base_type get_unshifted() const { return Bitfield::get_unshifted(v); }
|
|
|
|
void set(Bits_type val) { v = Bitfield::set(v, val); }
|
|
void set_dirty(Bits_type val) { v = Bitfield::set_dirty(v, val); }
|
|
void set_unshifted(Shift_type val) { v = Bitfield::set_unshifted(v, val); }
|
|
void set_unshifted_dirty(Shift_type val) { v = Bitfield::set_unshifted_dirty(v, val); }
|
|
};
|
|
|
|
/** Internal helper type */
|
|
template< typename TT >
|
|
class Value : public Value_base<TT>
|
|
{
|
|
public:
|
|
constexpr Value(TT t) : Value_base<TT>(t) {}
|
|
constexpr operator Bits_type () const { return this->get(); }
|
|
constexpr Value &operator = (Bits_type val) { this->set(val); return *this; }
|
|
constexpr Value &operator = (Value const &val)
|
|
{ this->set(val.get()); return *this; }
|
|
Value(Value const &) = default;
|
|
};
|
|
|
|
/** Internal helper type */
|
|
template< typename TT >
|
|
class Value_unshifted : public Value_base<TT>
|
|
{
|
|
public:
|
|
constexpr Value_unshifted(TT t) : Value_base<TT>(t) {}
|
|
constexpr operator Shift_type () const { return this->get_unshifted(); }
|
|
constexpr Value_unshifted &operator = (Shift_type val) { this->set_unshifted(val); return *this; }
|
|
constexpr Value_unshifted &operator = (Value_unshifted const &val)
|
|
{ this->set_unshifted(val.get_unshifted()); return *this; }
|
|
Value_unshifted(Value_unshifted const &) = default;
|
|
};
|
|
|
|
/** Reference type to access the bits inside a raw bit field. */
|
|
typedef Value<Base_type &> Ref;
|
|
/** Volatile reference type to access the bits inside a raw bit field. */
|
|
typedef Value<Base_type volatile &> Ref_volatile;
|
|
/** Value type to access the bits inside a raw bit field. */
|
|
typedef Value<Base_type const> Val;
|
|
|
|
/** Reference type to access the bits inside a raw bit field (in place). */
|
|
typedef Value_unshifted<Base_type &> Ref_unshifted;
|
|
/** Volatile reference type to access the bits inside a raw bit field (in place). */
|
|
typedef Value_unshifted<Base_type volatile &> Ref_unshifted_volatile;
|
|
/** Value type to access the bits inside a raw bit field (in place). */
|
|
typedef Value_unshifted<Base_type const> Val_unshifted;
|
|
};
|
|
|
|
#define CXX_BITFIELD_MEMBER(LSB, MSB, name, data_member) \
|
|
/** @{ */ \
|
|
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
|
|
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
constexpr typename name ## _bfm_t::Val name() const { return data_member; } \
|
|
typename name ## _bfm_t::Val name() const volatile { return data_member; } \
|
|
/** Get a reference to the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
constexpr typename name ## _bfm_t::Ref name() { return data_member; } \
|
|
typename name ## _bfm_t::Ref_volatile name() volatile { return data_member; } \
|
|
/** @} */
|
|
|
|
#define CXX_BITFIELD_MEMBER_RO(LSB, MSB, name, data_member) \
|
|
/** @{ */ \
|
|
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
|
|
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
constexpr typename name ## _bfm_t::Val name() const { return data_member; } \
|
|
typename name ## _bfm_t::Val name() const volatile { return data_member; } \
|
|
/** @} */
|
|
|
|
#define CXX_BITFIELD_MEMBER_UNSHIFTED(LSB, MSB, name, data_member) \
|
|
/** @{ */ \
|
|
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
|
|
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
constexpr typename name ## _bfm_t::Val_unshifted name() const { return data_member; } \
|
|
typename name ## _bfm_t::Val_unshifted name() const volatile { return data_member; } \
|
|
/** Get a reference to the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
constexpr typename name ## _bfm_t::Ref_unshifted name() { return data_member; } \
|
|
typename name ## _bfm_t::Ref_unshifted_volatile name() volatile { return data_member; } \
|
|
/** @} */
|
|
|
|
#define CXX_BITFIELD_MEMBER_UNSHIFTED_RO(LSB, MSB, name, data_member) \
|
|
/** @{ */ \
|
|
/** Type to access the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
typedef cxx::Bitfield<decltype(data_member), LSB, MSB> name ## _bfm_t; \
|
|
/** Get the `name` bits (LSB to MSB) of `data_member`. */ \
|
|
constexpr typename name ## _bfm_t::Val_unshifted name() const { return data_member; } \
|
|
typename name ## _bfm_t::Val_unshifted name() const volatile { return data_member; } \
|
|
/** @} */
|
|
}
|