| // 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 ®_value_; } |
| const ValueType* reg_value_ptr() const { return ®_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); |
| auto masked = static_cast<IntType>(*value_ptr_ & ~(mask_ << shift_)); |
| *value_ptr_ = static_cast<IntType>(masked | (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_ |