blob: 7cfecf0160bcf27ad71361df3560a53276fe733e [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef HWREG_BITFIELDS_H_
#define HWREG_BITFIELDS_H_
#include <limits.h>
#include <stdint.h>
#include <zircon/assert.h>
#include <type_traits>
#include "internal.h"
#include "mmio.h"
// This file provides some helpers for accessing bitfields in registers.
//
// Example usage:
// ```
//
// // Define bitfields for an "AuxControl" 32-bit register.
// class AuxControl : public hwreg::RegisterBase<AuxControl, uint32_t> {
// public:
// // Returns an object representing the register's type and address.
// static hwreg::RegisterAddr<AuxControl> Get() { return {0x64010}; }
//
// // Bits [30:25] and [19:0] are automatically preserved across RMW cycles.
//
// // Define a single-bit field.
// DEF_BIT(31, enabled);
// // Define a 5-bit field, from bits 20-24 (inclusive).
// DEF_FIELD(24, 20, message_size);
// };
//
// void Example1(hwreg::RegisterIo* reg_io) {
// // Read the register's value from MMIO. "reg" is a snapshot of the
// // register's value which also knows the register's address.
// auto reg = AuxControl::Get().ReadFrom(reg_io);
//
// // Read this register's "message_size" field.
// uint32_t size = reg.message_size();
//
// // Change this field's value. This modifies the snapshot.
// reg.set_message_size(1234);
//
// // Write the modified register value to MMIO.
// reg.WriteTo(reg_io);
// }
//
// // Fields may also be set in a fluent style
// void Example2(hwreg::RegisterIo* reg_io) {
// // Read the register's value from MMIO, updates the message size and
// // enabled bit, then writes the value back to MMIO
// AuxControl::Get().ReadFrom(reg_io).set_message_size(1234).set_enabled(1).WriteTo(reg_io);
// }
//
// // It is also possible to write a register without having to read it
// // first:
// void Example3(hwreg::RegisterIo* reg_io) {
// // Start off with a value that is initialized to zero.
// auto reg = AuxControl::Get().FromValue(0);
// // Fill out fields.
// reg.set_message_size(2345);
// // Write the register value to MMIO.
// reg.WriteTo(reg_io);
// }
// ```
//
// Note that this produces efficient code with GCC and Clang, which are
// capable of optimizing away the intermediate objects.
//
// The arguments to DEF_FIELD() are organized to match up with Intel's
// documentation for their graphics hardware. For example, if the docs
// specify a field as:
// 23:0 Data M value
// then that translates to:
// DEF_FIELD(23, 0, data_m_value)
// To match up, we put the upper bit first and use an inclusive bit range.
//
// For fields whose value is relative to their position in the register you can
// use DEF_UNSHIFTED_FIELD(high, low, name). This ensures that when you
// read/write full values they will be applied to the field with the bits
// overlapping other fields masked off, without shifting anything.
// Enums: Enums can be used in register field definitions using the
// DEF_ENUM_FIELD macro as shown below
// ```
// enum LinkState {
// Connected = 0,
// Disconnected = 1,
// Disabled = 2,
// ...
// };
// ...
// DEF_ENUM_FIELD(LinkState, 8, 5, PLS);
// ```
//
// Register types may be templated, but in order to do so `SelfType` must be
// explicitly defined:
// ```
// template <unsigned int N>
// struct TemplatedReg : public hwreg::RegisterBase<TemplatedReg<N>, uint32_t> {
// using SelfType = TemplatedReg<N>;
//
// DEF_FIELD(N, 0, lower);
// ...
// };
// ```
// The DEF_*_(FIELD|BIT) macros rely on SelfType being unambiguously defined
// within the scope at which they are used .
// The DEF_*_(BIT|FIELD) macros rely on SelfType being unambiguously defined
// within the scope at which they are used (ditto for the *conditional* flavors
// of the DEF_*_SUB(FIELD|BIT) (see next paragraph)). In a non-templated case,
// we inherit this for free from the RegisterBase base class; otherwise, it
// needs to be explicitly redeclared (as above).
//
// In contexts where the layout of the register depends on a static condition
// (e.g., relating to a template parameter), there are conditional versions of
// the DEF_FOO(...) macros, DEF_COND_FOO(..., COND), that compile away when the
// condition does not hold. So long as each layout variant is valid in its own
// right, there are no overlap constraints across different variants. Further,
// the same field name may be reused across layouts that are defined by the
// DEF_COND*_(FIELD|BIT) macros.
//
namespace hwreg {
// Tag that can be passed as the third template parameter for RegisterBase to enable
// the pretty-printing interfaces on a register.
struct EnablePrinter;
// An instance of RegisterBase represents a staging copy of a register,
// which can be written to the register itself. It knows the register's
// address and stores a value for the register.
//
// Normal usage is to create classes that derive from RegisterBase and
// provide methods for accessing bitfields of the register. RegisterBase
// does not provide a constructor because constructors are not inherited by
// derived classes by default, and we don't want the derived classes to
// have to declare constructors.
//
// Any bits not declared using DEF_FIELD/DEF_BIT/DEF_RSVDZ_FIELD/DEF_RSVDZ_BIT
// will be automatically preserved across RMW operations.
template <class DerivedType, class IntType, class PrinterState = void>
class RegisterBase {
static_assert(internal::IsSupportedInt<IntType>::value, "unsupported register access width");
static_assert(std::is_same_v<PrinterState, void> || std::is_same_v<PrinterState, EnablePrinter>,
"unsupported printer state");
public:
using SelfType = DerivedType;
using ValueType = IntType;
using PrinterEnabled = std::is_same<PrinterState, EnablePrinter>;
uint32_t reg_addr() const { return reg_addr_; }
void set_reg_addr(uint32_t addr) { reg_addr_ = addr; }
constexpr ValueType reg_value() const { return reg_value_; }
ValueType* reg_value_ptr() { return &reg_value_; }
const ValueType* reg_value_ptr() const { return &reg_value_; }
constexpr SelfType& set_reg_value(IntType value) {
reg_value_ = value;
return *static_cast<SelfType*>(this);
}
template <typename T>
SelfType& ReadFrom(T* reg_io) {
internal::Visit([this](auto&& io) { reg_value_ = io.template Read<ValueType>(reg_addr_); },
*reg_io);
return *static_cast<SelfType*>(this);
}
template <typename T>
SelfType& WriteTo(T* reg_io) {
internal::Visit(
[this](auto&& io) {
io.template Write(static_cast<IntType>(reg_value_ & ~params_.rsvdz_mask), reg_addr_);
},
*reg_io);
return *static_cast<SelfType*>(this);
}
// Invokes print_fn(const char* buf) once for each field, including each
// RsvdZ field, and one extra time if there are any undefined bits set.
// The callback argument must not be accessed after the callback
// returns. The callback will be called once for each field with a
// null-terminated string describing the name and contents of the field.
//
// Printed fields will look like: "field_name[26:8]: 0x00123 (291)"
// The undefined bits message will look like: "unknown set bits: 0x00301000"
//
// WARNING: This will substantially increase code size and stack usage at the
// call site. https://fxbug.dev/42147424 tracks investigation to improve this.
//
// Example use:
// reg.Print([](const char* arg) { printf("%s\n", arg); });
template <typename F>
void Print(F print_fn) {
static_assert(PrinterEnabled::value, "Pass hwreg::EnablePrinter to RegisterBase to enable");
internal::PrintRegister(print_fn, params_.printer.fields.data(), params_.printer.num_fields,
reg_value_, params_.fields_mask, sizeof(ValueType));
}
// Equivalent to Print([](const char* arg) { printf("%s\n", arg); });
void Print() {
static_assert(PrinterEnabled::value, "Pass hwreg::EnablePrinter to RegisterBase to enable");
internal::PrintRegisterPrintf(params_.printer.fields.data(), params_.printer.num_fields,
reg_value_, params_.fields_mask, sizeof(ValueType));
}
template <typename FieldCallback>
constexpr void ForEachField(FieldCallback&& callback) const {
static_assert(PrinterEnabled::value, "Pass hwreg::EnablePrinter to RegisterBase to enable");
static_assert(std::is_invocable_v<FieldCallback, const char*, ValueType, uint32_t, uint32_t>);
for (unsigned i = 0; i < params_.printer.num_fields; ++i) {
ValueType mask = internal::ComputeMask<ValueType>(params_.printer.fields[i].bit_high_incl() -
params_.printer.fields[i].bit_low() + 1)
<< params_.printer.fields[i].bit_low();
ValueType value = (reg_value_ & mask) >> params_.printer.fields[i].bit_low();
callback((mask & rsvdz_mask()) == mask ? nullptr : params_.printer.fields[i].name(), value,
params_.printer.fields[i].bit_high_incl(), params_.printer.fields[i].bit_low());
}
}
constexpr IntType fields_mask() const { return params_.fields_mask; }
constexpr IntType rsvdz_mask() const { return params_.rsvdz_mask; }
protected:
internal::FieldParameters<PrinterEnabled::value, ValueType>& params() { return params_; }
private:
internal::FieldParameters<PrinterEnabled::value, ValueType> params_;
ValueType reg_value_ = 0;
uint32_t reg_addr_ = 0;
};
// An instance of RegisterAddr represents a typed register address: It
// knows the address of the register (within the MMIO address space) and
// the type of its contents, RegType. RegType represents the register's
// bitfields. RegType should be a subclass of RegisterBase.
template <class RegType>
class RegisterAddr {
public:
RegisterAddr(uint32_t reg_addr) : reg_addr_(reg_addr) {}
static_assert(std::is_base_of_v<RegisterBase<RegType, typename RegType::ValueType>, RegType> ||
std::is_base_of_v<
RegisterBase<RegType, typename RegType::ValueType, EnablePrinter>, RegType>,
"Parameter of RegisterAddr<> should derive from RegisterBase");
// Instantiate a RegisterBase using the value of the register read from
// MMIO.
template <typename T>
RegType ReadFrom(T* reg_io) const {
RegType reg;
reg.set_reg_addr(reg_addr_);
reg.ReadFrom(reg_io);
return reg;
}
// Instantiate a RegisterBase using the given value for the register.
RegType FromValue(typename RegType::ValueType value) const {
RegType reg;
reg.set_reg_addr(reg_addr_);
reg.set_reg_value(value);
return reg;
}
uint32_t addr() const { return reg_addr_; }
private:
const uint32_t reg_addr_;
};
template <class IntType>
class BitfieldRef {
public:
constexpr BitfieldRef(IntType* value_ptr, uint32_t bit_high_incl, uint32_t bit_low)
: value_ptr_(value_ptr),
shift_(bit_low),
mask_(internal::ComputeMask<IntType>(bit_high_incl - bit_low + 1)) {}
constexpr BitfieldRef(IntType* value_ptr, uint32_t bit_high_incl, uint32_t bit_low,
bool unshifted)
: value_ptr_(value_ptr),
shift_(0),
mask_(static_cast<IntType>(internal::ComputeMask<IntType>(bit_high_incl - bit_low + 1)
<< bit_low)) {}
constexpr IntType get() const { return static_cast<IntType>((*value_ptr_ >> shift_) & mask_); }
constexpr void set(IntType field_val) {
static_assert(!std::is_const_v<IntType>);
ZX_DEBUG_ASSERT((field_val & ~mask_) == 0);
*value_ptr_ = static_cast<IntType>(*value_ptr_ & ~(mask_ << shift_));
*value_ptr_ = static_cast<IntType>(*value_ptr_ | (field_val << shift_));
}
private:
IntType* const value_ptr_;
const uint32_t shift_;
const IntType mask_;
};
//
// We take care to use `typename SelfType::ValueType` over `ValueType` below,
// since the defined contract above is that `SelfType` alone must be
// (unambiguously) defined within the applied scope. Similarly, we explicitly
// access `params()` and `reg_value_ptr()` via `this` so as to sidestep any
// possible ambiguity (e.g., in the case of a templated register type).
//
// Declares multi-bit fields in a derived class of RegisterBase<D, T>. This
// produces functions "T NAME() const" and "D& set_NAME(T)". Both bit indices
// are inclusive.
#define DEF_FIELD(BIT_HIGH, BIT_LOW, NAME) DEF_COND_FIELD(BIT_HIGH, BIT_LOW, NAME, true)
#define DEF_COND_FIELD(BIT_HIGH, BIT_LOW, NAME, COND) \
static_assert((BIT_HIGH) >= (BIT_LOW), "Upper bit goes before lower bit"); \
static_assert((BIT_HIGH) < sizeof(typename SelfType::ValueType) * CHAR_BIT, \
"Upper bit is out of range"); \
/* NOLINTBEGIN(misc-non-private-member-variables-in-classes) */ \
__NO_UNIQUE_ADDRESS struct { \
struct NAME##Marker {}; \
__NO_UNIQUE_ADDRESS hwreg::internal::Field<SelfType, NAME##Marker, (COND)> field; \
} HWREG_INTERNAL_MEMBER_NAME(NAME) = {{&this->params(), #NAME, (BIT_HIGH), (BIT_LOW)}}; \
/* NOLINTEND(misc-non-private-member-variables-in-classes) */ \
template <bool Cond = (COND)> \
hwreg::internal::enable_if_t<Cond, typename SelfType::ValueType, (BIT_HIGH), (BIT_LOW)> NAME() \
const { \
return hwreg::BitfieldRef<const typename SelfType::ValueType>(this->reg_value_ptr(), \
(BIT_HIGH), (BIT_LOW)) \
.get(); \
} \
template <bool Cond = (COND)> \
hwreg::internal::enable_if_t<Cond, SelfType&, (BIT_HIGH), (BIT_LOW)> set_##NAME( \
typename SelfType::ValueType val) { \
hwreg::BitfieldRef<typename SelfType::ValueType>(this->reg_value_ptr(), (BIT_HIGH), (BIT_LOW)) \
.set(val); \
return *this; \
} \
static_assert(true) // eat a ;
#define DEF_UNSHIFTED_FIELD(BIT_HIGH, BIT_LOW, NAME) \
DEF_COND_UNSHIFTED_FIELD(BIT_HIGH, BIT_LOW, NAME, true)
#define DEF_COND_UNSHIFTED_FIELD(BIT_HIGH, BIT_LOW, NAME, COND) \
static_assert((BIT_HIGH) >= (BIT_LOW), "Upper bit goes before lower bit"); \
static_assert((BIT_HIGH) < sizeof(typename SelfType::ValueType) * CHAR_BIT, \
"Upper bit is out of range"); \
/* NOLINTBEGIN(misc-non-private-member-variables-in-classes) */ \
__NO_UNIQUE_ADDRESS struct { \
struct NAME##Marker {}; \
__NO_UNIQUE_ADDRESS hwreg::internal::Field<SelfType, NAME##Marker, (COND)> field; \
} HWREG_INTERNAL_MEMBER_NAME(NAME) = {{&this->params(), #NAME, (BIT_HIGH), (BIT_LOW)}}; \
/* NOLINTEND(misc-non-private-member-variables-in-classes) */ \
template <bool Cond = (COND)> \
hwreg::internal::enable_if_t<Cond, typename SelfType::ValueType, (BIT_HIGH), (BIT_LOW)> NAME() \
const { \
return hwreg::BitfieldRef<const typename SelfType::ValueType>(this->reg_value_ptr(), \
(BIT_HIGH), (BIT_LOW), true) \
.get(); \
} \
template <bool Cond = (COND)> \
hwreg::internal::enable_if_t<Cond, SelfType&, (BIT_HIGH), (BIT_LOW)> set_##NAME( \
typename SelfType::ValueType val) { \
hwreg::BitfieldRef<typename SelfType::ValueType>(this->reg_value_ptr(), (BIT_HIGH), (BIT_LOW), \
true) \
.set(val); \
return *this; \
} \
static_assert(true) // eat a ;
#define DEF_ENUM_FIELD(ENUM_TYPE, BIT_HIGH, BIT_LOW, NAME) \
DEF_COND_ENUM_FIELD(ENUM_TYPE, BIT_HIGH, BIT_LOW, NAME, true)
#define DEF_COND_ENUM_FIELD(ENUM_TYPE, BIT_HIGH, BIT_LOW, NAME, COND) \
static_assert((BIT_HIGH) >= (BIT_LOW), "Upper bit goes before lower bit"); \
static_assert((BIT_HIGH) < sizeof(typename SelfType::ValueType) * CHAR_BIT, \
"Upper bit is out of range"); \
static_assert(std::is_enum_v<ENUM_TYPE>, "Field type is not an enum"); \
/* NOLINTBEGIN(misc-non-private-member-variables-in-classes) */ \
__NO_UNIQUE_ADDRESS struct { \
struct NAME##Marker {}; \
__NO_UNIQUE_ADDRESS hwreg::internal::Field<SelfType, NAME##Marker, (COND)> field; \
} HWREG_INTERNAL_MEMBER_NAME(NAME) = {{&this->params(), #NAME, (BIT_HIGH), (BIT_LOW)}}; \
/* NOLINTEND(misc-non-private-member-variables-in-classes) */ \
template <bool Cond = (COND)> \
hwreg::internal::enable_if_t<Cond, ENUM_TYPE, (BIT_HIGH), (BIT_LOW)> NAME() const { \
return static_cast<ENUM_TYPE>(hwreg::BitfieldRef<const typename SelfType::ValueType>( \
this->reg_value_ptr(), (BIT_HIGH), (BIT_LOW)) \
.get()); \
} \
template <bool Cond = (COND)> \
hwreg::internal::enable_if_t<Cond, SelfType&, (BIT_HIGH), (BIT_LOW)> set_##NAME(ENUM_TYPE val) { \
hwreg::BitfieldRef<typename SelfType::ValueType>(this->reg_value_ptr(), (BIT_HIGH), (BIT_LOW)) \
.set(static_cast<typename SelfType::ValueType>(val)); \
return *this; \
} \
static_assert(true) // eat a ;
// Declares single-bit fields in a derived class of RegisterBase<D, T>. This
// produces functions "T NAME() const" and "void set_NAME(T)".
#define DEF_BIT(BIT, NAME) DEF_COND_BIT(BIT, NAME, true)
#define DEF_COND_BIT(BIT, NAME, COND) DEF_COND_FIELD(BIT, BIT, NAME, COND)
// Declares multi-bit reserved-zero fields in a derived class of RegisterBase<D, T>.
// This will ensure that on RegisterBase<T>::WriteTo(), reserved-zero bits are
// automatically zeroed. Both bit indices are inclusive.
#define DEF_RSVDZ_FIELD(BIT_HIGH, BIT_LOW) DEF_COND_RSVDZ_FIELD(BIT_HIGH, BIT_LOW, true)
#define DEF_COND_RSVDZ_FIELD(BIT_HIGH, BIT_LOW, COND) \
static_assert((BIT_HIGH) >= (BIT_LOW), "Upper bit goes before lower bit"); \
static_assert((BIT_HIGH) < sizeof(typename SelfType::ValueType) * CHAR_BIT, \
"Upper bit is out of range"); \
/* NOLINTBEGIN(misc-non-private-member-variables-in-classes) */ \
__NO_UNIQUE_ADDRESS struct { \
struct RsvdZMarker {}; \
__NO_UNIQUE_ADDRESS hwreg::internal::RsvdZField<SelfType, RsvdZMarker, (COND)> field; \
} HWREG_INTERNAL_MEMBER_NAME(RsvdZ) = { \
{&this->params(), (BIT_HIGH), \
(BIT_LOW)}} /* NOLINTEND(misc-non-private-member-variables-in-classes) */
// Declares single-bit reserved-zero fields in a derived class of RegisterBase<D, T>.
// This will ensure that on RegisterBase<T>::WriteTo(), reserved-zero bits are
// automatically zeroed.
#define DEF_RSVDZ_BIT(BIT) DEF_COND_RSVDZ_BIT(BIT, true)
#define DEF_COND_RSVDZ_BIT(BIT, COND) DEF_COND_RSVDZ_FIELD(BIT, BIT, COND)
// Declares "decltype(FIELD) NAME() const" and "void set_NAME(decltype(FIELD))" that
// reads/modifies the declared bitrange. Both bit indices are inclusive.
#define DEF_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW, NAME) \
HWREG_INTERNAL_CHECK_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW) \
constexpr typename std::remove_reference_t<decltype(FIELD)> NAME() const { \
return hwreg::BitfieldRef<const typename std::remove_reference_t<decltype(FIELD)>>( \
&FIELD, (BIT_HIGH), (BIT_LOW)) \
.get(); \
} \
constexpr auto& set_##NAME(typename std::remove_reference_t<decltype(FIELD)> val) { \
hwreg::BitfieldRef<typename std::remove_reference_t<decltype(FIELD)>>(&FIELD, (BIT_HIGH), \
(BIT_LOW)) \
.set(val); \
return *this; \
} \
static_assert(true) // eat a ;
#define DEF_COND_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW, NAME, COND) \
HWREG_INTERNAL_CHECK_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW) \
template <bool Cond = (COND)> \
constexpr hwreg::internal::enable_if_t<Cond, std::remove_reference_t<decltype(FIELD)>, \
(BIT_HIGH), (BIT_LOW)> \
NAME() const { \
return hwreg::BitfieldRef<const std::remove_reference_t<decltype(FIELD)>>(&FIELD, (BIT_HIGH), \
(BIT_LOW)) \
.get(); \
} \
template <bool Cond = (COND), typename = std::enable_if_t<Cond>> \
constexpr hwreg::internal::enable_if_t<Cond, SelfType&, (BIT_HIGH), (BIT_LOW)> set_##NAME( \
std::remove_reference_t<decltype(FIELD)> val) { \
hwreg::BitfieldRef<std::remove_reference_t<decltype(FIELD)>>(&FIELD, (BIT_HIGH), (BIT_LOW)) \
.set(val); \
return *this; \
} \
static_assert(true) // eat a ;
// Declares "decltype(FIELD) NAME() const" and "void set_NAME(decltype(FIELD))" that
// reads/modifies the declared bit.
#define DEF_SUBBIT(FIELD, BIT, NAME) DEF_SUBFIELD(FIELD, BIT, BIT, NAME)
#define DEF_COND_SUBBIT(FIELD, BIT, NAME, COND) DEF_COND_SUBFIELD(FIELD, BIT, BIT, NAME, COND)
// Declares "decltype(FIELD) NAME() const" and "void set_NAME(decltype(FIELD))" that
// reads/modifies the declared bitrange, without shifting the input / output value.
// Both bit indices are inclusive.
//
// For example, given the definition:
//
// struct PackedAddress {
// DEF_UNSHIFTED_SUBFIELD(data, 32, 1, address);
// DEF_SUBBIT(data, 0, low_bit);
// };
//
// will allow 32-bit addresses to be directly read/written to `address`. If
// `DEF_SUBFIELD` read/written values would need to be shifted left/right by
// 1 bit each time.
#define DEF_UNSHIFTED_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW, NAME) \
HWREG_INTERNAL_CHECK_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW) \
constexpr typename std::remove_reference_t<decltype(FIELD)> NAME() const { \
return hwreg::BitfieldRef<const typename std::remove_reference_t<decltype(FIELD)>>( \
&FIELD, (BIT_HIGH), (BIT_LOW), /*unshifted=*/true) \
.get(); \
} \
constexpr auto& set_##NAME(typename std::remove_reference_t<decltype(FIELD)> val) { \
hwreg::BitfieldRef<typename std::remove_reference_t<decltype(FIELD)>>( \
&FIELD, (BIT_HIGH), (BIT_LOW), /*unshifted=*/true) \
.set(val); \
return *this; \
} \
static_assert(true) // eat a ;
#define DEF_COND_UNSHIFTED_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW, NAME, COND) \
HWREG_INTERNAL_CHECK_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW) \
template <bool Cond = (COND), typename = std::enable_if_t<Cond>> \
constexpr hwreg::internal::enable_if_t<Cond, std::remove_reference_t<decltype(FIELD)>, \
(BIT_HIGH), (BIT_LOW)> \
NAME() const { \
return hwreg::BitfieldRef<const std::remove_reference_t<decltype(FIELD)>>( \
&FIELD, (BIT_HIGH), (BIT_LOW), /*unshifted=*/true) \
.get(); \
} \
template <bool Cond = (COND), typename = std::enable_if_t<Cond>> \
constexpr hwreg::internal::enable_if_t<Cond, SelfType&, (BIT_HIGH), (BIT_LOW)> set_##NAME( \
std::remove_reference_t<decltype(FIELD)> val) { \
hwreg::BitfieldRef<std::remove_reference_t<decltype(FIELD)>>(&FIELD, (BIT_HIGH), (BIT_LOW), \
/*unshifted=*/true) \
.set(val); \
return *this; \
} \
static_assert(true) // eat a ;
// Declares "TYPE NAME() const" and "void set_NAME(TYPE)" that
// reads/modifies the declared bitrange. Both bit indices are inclusive.
#define DEF_ENUM_SUBFIELD(FIELD, ENUM_TYPE, BIT_HIGH, BIT_LOW, NAME) \
HWREG_INTERNAL_CHECK_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW) \
static_assert(std::is_enum_v<ENUM_TYPE>, "ENUM_TYPE is not an enum"); \
constexpr ENUM_TYPE NAME() const { \
return static_cast<ENUM_TYPE>( \
hwreg::BitfieldRef<const typename std::remove_reference_t<decltype(FIELD)>>( \
&FIELD, (BIT_HIGH), (BIT_LOW)) \
.get()); \
} \
constexpr auto& set_##NAME(ENUM_TYPE val) { \
hwreg::BitfieldRef<typename std::remove_reference_t<decltype(FIELD)>>(&FIELD, (BIT_HIGH), \
(BIT_LOW)) \
.set(static_cast<typename std::remove_reference_t<decltype(FIELD)>>(val)); \
return *this; \
} \
static_assert(true) // eat a ;
#define DEF_COND_ENUM_SUBFIELD(FIELD, ENUM_TYPE, BIT_HIGH, BIT_LOW, NAME, COND) \
HWREG_INTERNAL_CHECK_SUBFIELD(FIELD, BIT_HIGH, BIT_LOW) \
static_assert(std::is_enum_v<ENUM_TYPE>, "ENUM_TYPE is not an enum"); \
template <bool Cond = (COND), typename = std::enable_if_t<Cond>> \
constexpr hwreg::internal::enable_if_t<Cond, ENUM_TYPE, (BIT_HIGH), (BIT_LOW)> NAME() const { \
return static_cast<ENUM_TYPE>( \
hwreg::BitfieldRef<const std::remove_reference_t<decltype(FIELD)>>(&FIELD, (BIT_HIGH), \
(BIT_LOW)) \
.get()); \
} \
template <bool Cond = (COND), typename = std::enable_if_t<Cond>> \
constexpr hwreg::internal::enable_if_t<Cond, SelfType&, (BIT_HIGH), (BIT_LOW)> set_##NAME( \
ENUM_TYPE val) { \
hwreg::BitfieldRef<std::remove_reference_t<decltype(FIELD)>>(&FIELD, (BIT_HIGH), (BIT_LOW)) \
.set(static_cast<std::remove_reference_t<decltype(FIELD)>>(val)); \
return *this; \
} \
static_assert(true) // eat a ;
} // namespace hwreg
#endif // HWREG_BITFIELDS_H_