aboutsummaryrefslogtreecommitdiff
path: root/bitfield.h
diff options
context:
space:
mode:
Diffstat (limited to 'bitfield.h')
-rw-r--r--bitfield.h193
1 files changed, 193 insertions, 0 deletions
diff --git a/bitfield.h b/bitfield.h
new file mode 100644
index 0000000..833a722
--- /dev/null
+++ b/bitfield.h
@@ -0,0 +1,193 @@
+#ifndef UTILS_BITFIELD_H
+#define UTILS_BITFIELD_H
+
+#include <cstdint>
+#include <type_traits>
+
+namespace bitfield {
+namespace impl {
+/**
+ * Constant check for supported underlying BITFILED types.
+ */
+template <typename ValueType>
+static constexpr bool is_bitfield_type =
+ std::is_integral<ValueType>::value && std::is_unsigned<ValueType>::value &&
+ !std::is_same<ValueType, bool>::value;
+
+/**
+ * Compute a BITMASK based on the HIGHBIT and LOWBIT template parameters.
+ * The HIGHBIT and LOWBIT are inclusive.
+ *
+ * # Example
+ * ComputeMask<4, 7, uint32_t>() -> 0xf0
+ */
+template <std::uint8_t LowBit, std::uint8_t HighBit, typename ValueType>
+static constexpr inline ValueType compute_mask() {
+ using unsigned_t = typename std::make_unsigned<ValueType>::type;
+
+ constexpr unsigned_t kMaxBits = sizeof(unsigned_t) * 8;
+ static_assert(HighBit < kMaxBits, "HighBit exceeds bits of ValueType");
+ static_assert(LowBit <= HighBit, "HighBit must not be larger than LowBit");
+
+ constexpr unsigned_t kLen = HighBit - LowBit;
+ return kLen == (kMaxBits - 1)
+ ? ~static_cast<unsigned_t>(0)
+ : ((static_cast<unsigned_t>(1) << (HighBit - LowBit + 1)) - 1)
+ << LowBit;
+}
+} // namespace impl
+
+// -- FIELD REF ----------------------------------------------------------------
+
+/**
+ * A mutable reference to a single FIELD in a BITFIELD.
+ */
+template <std::uint8_t LowBit, std::uint8_t HighBit, typename ValueType>
+struct field_ref {
+ constexpr explicit field_ref(ValueType& val) : m_val{val} {}
+
+ constexpr operator ValueType() const {
+ return (m_val & kMask) >> LowBit;
+ }
+
+ constexpr ValueType val() const {
+ return static_cast<ValueType>(*this);
+ }
+
+ constexpr field_ref& operator=(ValueType val) {
+ m_val &= ~kMask;
+ return operator|=(val);
+ }
+
+ constexpr field_ref& operator|=(ValueType val) {
+ m_val |= (val << LowBit) & kMask;
+ return *this;
+ }
+
+ constexpr field_ref& operator&=(ValueType val) {
+ m_val &= (val << LowBit) & kMask;
+ return *this;
+ }
+
+ private:
+ ValueType& m_val;
+ enum : ValueType {
+ kMask = impl::compute_mask<LowBit, HighBit, ValueType>(),
+ };
+};
+
+// -- CONST FIELD REF ----------------------------------------------------------
+
+/**
+ * A constant reference to a single FIELD in a BITFIELD.
+ */
+template <std::uint8_t LowBit, std::uint8_t HighBit, typename ValueType>
+struct const_field_ref {
+ constexpr explicit const_field_ref(const ValueType& val) : m_val{val} {}
+
+ constexpr operator ValueType() const {
+ return (m_val & kMask) >> LowBit;
+ }
+
+ constexpr ValueType val() const {
+ return static_cast<ValueType>(*this);
+ }
+
+ private:
+ const ValueType& m_val;
+ enum : ValueType {
+ kMask = impl::compute_mask<LowBit, HighBit, ValueType>(),
+ };
+};
+
+// -- BITFIELD -----------------------------------------------------------------
+
+/**
+ * The BITFIELD base class.
+ */
+template <typename ValueType = std::uint32_t>
+struct bitfield {
+ static_assert(impl::is_bitfield_type<ValueType>,
+ "bitfield instantiated with incorrect type");
+
+ constexpr explicit bitfield(ValueType val) : m_val{val} {}
+
+ constexpr operator ValueType() const {
+ return m_val;
+ }
+
+ constexpr ValueType val() const {
+ return static_cast<ValueType>(*this);
+ }
+
+#define OPERATOR(OP) \
+ constexpr bitfield<ValueType>& operator OP(ValueType val) { \
+ m_val OP val; \
+ return *this; \
+ }
+ OPERATOR(=)
+ OPERATOR(|=)
+ OPERATOR(&=)
+#undef OPERATOR
+
+ protected:
+ ValueType m_val;
+};
+
+// -- MACROS -------------------------------------------------------------------
+//
+// Code generator macros to conveniently define BITFIELDs with multiple FIELDs.
+//
+// # Example
+//
+// BITFIELD_START(status_reg, std::uint32_t)
+// FIELD(N, 0, 0)
+// FIELD(V, 1, 1)
+// FIELD(C, 2, 2)
+// FIELD(Z, 3, 3)
+// FIELD(MODE, 4, 7)
+// BITFIELD_END()
+//
+// status_ref r(0);
+//
+// std::uint32_t n = r.V();
+// r.V() = 0xfff;
+// assert(r.V() == 0x1);
+// assert(r == 0x2);
+
+#define BITFIELD_START(NAME, TYPE) \
+ struct NAME : public bitfield::bitfield<TYPE> { \
+ using ValueType = TYPE; \
+ using bitfield<ValueType>::operator=; \
+ \
+ constexpr explicit NAME(ValueType val) : bitfield<ValueType>{val} {}
+
+#define BITFIELD_END() \
+ } \
+ ;
+
+#define FIELD(NAME, L, H) \
+ constexpr ::bitfield::field_ref<L, H, ValueType> NAME() { \
+ return ::bitfield::field_ref<L, H, ValueType>(m_val); \
+ } \
+ \
+ constexpr ::bitfield::const_field_ref<L, H, ValueType> NAME() const { \
+ return ::bitfield::const_field_ref<L, H, ValueType>(m_val); \
+ }
+
+// -- TESTS --------------------------------------------------------------------
+
+static_assert(impl::is_bitfield_type<std::uint32_t>, "");
+static_assert(!impl::is_bitfield_type<bool>, "");
+static_assert(!impl::is_bitfield_type<std::int32_t>, "");
+static_assert(!impl::is_bitfield_type<std::uint32_t*>, "");
+static_assert(!impl::is_bitfield_type<float>, "");
+
+static_assert(impl::compute_mask<0, 0, std::uint32_t>() == 0x1, "");
+static_assert(impl::compute_mask<4, 7, std::uint32_t>() == 0xf0, "");
+static_assert(impl::compute_mask<12, 12, std::uint32_t>() == 0x1000, "");
+static_assert(impl::compute_mask<15, 16, std::uint32_t>() == 0x18000, "");
+static_assert(impl::compute_mask<0, 31, std::uint32_t>() == 0xffffffff, "");
+
+} // namespace bitfield
+#endif