mirror of
https://github.com/sendyne/cppreg.git
synced 2025-05-11 05:33:52 +00:00
API REVISED FOR BETTER READ/WRITE PERFORMANCE
* For trivial read/write operations (over whole register) simpler implementations are now used to avoid unnecessary load/store instructions. * Merge write implementation has been revised to provide better performance when manipulating constant values. * `is_set` and `is_clear` are now available for all Field-based types. This closes #8.
This commit is contained in:
parent
1c27f907a5
commit
eb9226fe5d
@ -47,6 +47,18 @@ namespace cppreg {
|
|||||||
using Offset_t = std::uint8_t;
|
using Offset_t = std::uint8_t;
|
||||||
|
|
||||||
|
|
||||||
|
//! Shorthand for max value as a mask.
|
||||||
|
/**
|
||||||
|
* @tparam T Data type.
|
||||||
|
*
|
||||||
|
* This is used to define register masks.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
struct type_mask {
|
||||||
|
constexpr static const T value = std::numeric_limits<T>::max();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
|
||||||
#endif // CPPREG_CPPREG_INCLUDES_H
|
#endif // CPPREG_CPPREG_INCLUDES_H
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Access policy abstract implementation.
|
//! Access policy implementation.
|
||||||
/**
|
/**
|
||||||
* @file AccessPolicy.h
|
* @file AccessPolicy.h
|
||||||
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
|
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
|
||||||
@ -6,6 +6,17 @@
|
|||||||
*
|
*
|
||||||
* Access policies are used to describe if register fields are read-write,
|
* Access policies are used to describe if register fields are read-write,
|
||||||
* read-only or write-only.
|
* read-only or write-only.
|
||||||
|
*
|
||||||
|
* - The read and write implementations distinguish between trivial and
|
||||||
|
* non-trivial operations. A trivial operation corresponds to a read or
|
||||||
|
* write over a whole register; in such a case no masking of shifting is
|
||||||
|
* necessary. On the other hand, if the operation is only over a region of
|
||||||
|
* the register proper masking and shifting is required. The switch between
|
||||||
|
* trivial and non-trivial implementations is done automatically.
|
||||||
|
* - The write implementation also distinguishes between writing constant
|
||||||
|
* values (i.e., known at compile time) and non-constant value. This was
|
||||||
|
* intended to make it possible to simplify operations at compile time and
|
||||||
|
* minimize overhead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -20,6 +31,167 @@
|
|||||||
namespace cppreg {
|
namespace cppreg {
|
||||||
|
|
||||||
|
|
||||||
|
//! Register read implementation.
|
||||||
|
/**
|
||||||
|
* @tparam MMIO_t Memory device type.
|
||||||
|
* @tparam T Register data type.
|
||||||
|
* @tparam mask Mask for the read operation.
|
||||||
|
* @tparam offset Offset for the read operation.
|
||||||
|
*
|
||||||
|
* The mask and offset are used to define a specific field within the
|
||||||
|
* register.
|
||||||
|
*/
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
|
struct RegisterRead {
|
||||||
|
|
||||||
|
//! Boolean flag for trivial implementation.
|
||||||
|
/**
|
||||||
|
* The trivial corresponds to reading the whole register, that is,
|
||||||
|
* the mask is identity and the offset is zero.
|
||||||
|
*/
|
||||||
|
constexpr static const bool is_trivial =
|
||||||
|
(mask == type_mask<T>::value) && (offset == 0u);
|
||||||
|
|
||||||
|
//! Non-trivial read implementation.
|
||||||
|
/**
|
||||||
|
* @param mmio_device Pointer to the register memory device.
|
||||||
|
* @return The content of the register field.
|
||||||
|
*/
|
||||||
|
template <typename U = void>
|
||||||
|
inline static T read(
|
||||||
|
const MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<!is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
return static_cast<T>((*mmio_device & mask) >> offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Trivial read implementation.
|
||||||
|
/**
|
||||||
|
* @param mmio_device Pointer to the register memory device.
|
||||||
|
* @return The content of the register field.
|
||||||
|
*/
|
||||||
|
template <typename U = void>
|
||||||
|
inline static T read(
|
||||||
|
const MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
return static_cast<T>(*mmio_device);
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! Register write implementation.
|
||||||
|
/**
|
||||||
|
* @tparam MMIO_t Memory device type.
|
||||||
|
* @tparam T Register data type.
|
||||||
|
* @tparam mask Mask for the read operation.
|
||||||
|
* @tparam offset Offset for the read operation.
|
||||||
|
*
|
||||||
|
* The mask and offset are used to define a specific field within the
|
||||||
|
* register.
|
||||||
|
*
|
||||||
|
* This write implementation is used only when the value to be written is
|
||||||
|
* not a constant expression.
|
||||||
|
*/
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
|
struct RegisterWrite {
|
||||||
|
|
||||||
|
//! Boolean flag for trivial implementation.
|
||||||
|
/**
|
||||||
|
* The trivial corresponds to writing to the whole register, that is,
|
||||||
|
* the mask is identity and the offset is zero.
|
||||||
|
*/
|
||||||
|
constexpr static const bool is_trivial =
|
||||||
|
(mask == type_mask<T>::value) && (offset == 0u);
|
||||||
|
|
||||||
|
//! Non-trivial write implementation.
|
||||||
|
/**
|
||||||
|
* @param mmio_device Pointer to the register memory device.
|
||||||
|
* @param value Value to be written to the register field.
|
||||||
|
*/
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
T value,
|
||||||
|
typename std::enable_if<!is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = static_cast<T>(
|
||||||
|
(*mmio_device & ~mask) | ((value << offset) & mask)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Trivial write implementation.
|
||||||
|
/**
|
||||||
|
* @param mmio_device Pointer to the register memory device.
|
||||||
|
* @param value Value to be written to the register field.
|
||||||
|
*/
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
T value,
|
||||||
|
typename std::enable_if<is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! Register write constant implementation.
|
||||||
|
/**
|
||||||
|
* @tparam MMIO_t Memory device type.
|
||||||
|
* @tparam T Register data type.
|
||||||
|
* @tparam mask Mask for the read operation.
|
||||||
|
* @tparam offset Offset for the read operation.
|
||||||
|
* @tparam value Value to be written to the register field.
|
||||||
|
*
|
||||||
|
* The mask and offset are used to define a specific field within the
|
||||||
|
* register.
|
||||||
|
*
|
||||||
|
* This write implementation is used only when the value to be written is
|
||||||
|
* a constant expression.
|
||||||
|
*/
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value>
|
||||||
|
struct RegisterWriteConstant {
|
||||||
|
|
||||||
|
//! Boolean flag for trivial implementation.
|
||||||
|
/**
|
||||||
|
* The trivial corresponds to writing to the whole register, that is,
|
||||||
|
* the mask is identity and the offset is zero.
|
||||||
|
*/
|
||||||
|
constexpr static const bool is_trivial =
|
||||||
|
(mask == type_mask<T>::value) && (offset == 0u);
|
||||||
|
|
||||||
|
//! Non-trivial write implementation.
|
||||||
|
/**
|
||||||
|
* @param mmio_device Pointer to the register memory device.
|
||||||
|
*/
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<!is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = static_cast<T>(
|
||||||
|
(*mmio_device & ~mask) | ((value << offset) & mask)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Trivial write implementation.
|
||||||
|
/**
|
||||||
|
* @param mmio_device Pointer to the register memory device.
|
||||||
|
*/
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
//! Read-only access policy.
|
//! Read-only access policy.
|
||||||
struct read_only {
|
struct read_only {
|
||||||
|
|
||||||
@ -27,16 +199,14 @@ namespace cppreg {
|
|||||||
/**
|
/**
|
||||||
* @tparam MMIO_t Register memory device type.
|
* @tparam MMIO_t Register memory device type.
|
||||||
* @tparam T Field data type.
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
|
* @tparam offset Field offset.
|
||||||
* @param mmio_device Pointer to register mapped memory.
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
* @param mask Field mask.
|
|
||||||
* @param offset Field offset.
|
|
||||||
* @return The value at the field location.
|
* @return The value at the field location.
|
||||||
*/
|
*/
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
inline static T read(const MMIO_t* const mmio_device,
|
inline static T read(const MMIO_t* const mmio_device) noexcept {
|
||||||
const T mask,
|
return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device);
|
||||||
const Offset_t offset) noexcept {
|
|
||||||
return static_cast<T>((*mmio_device & mask) >> offset);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -49,54 +219,68 @@ namespace cppreg {
|
|||||||
/**
|
/**
|
||||||
* @tparam MMIO_t Register memory device type.
|
* @tparam MMIO_t Register memory device type.
|
||||||
* @tparam T Field data type.
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
|
* @tparam offset Field offset.
|
||||||
* @param mmio_device Pointer to register mapped memory.
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
* @param mask Field mask.
|
|
||||||
* @param offset Field offset.
|
|
||||||
* @param value Value to be written at the field location.
|
* @param value Value to be written at the field location.
|
||||||
*/
|
*/
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
inline static void write(MMIO_t* const mmio_device,
|
inline static void write(MMIO_t* const mmio_device,
|
||||||
const T mask,
|
|
||||||
const Offset_t offset,
|
|
||||||
const T value) noexcept {
|
const T value) noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device & ~mask) |
|
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
|
||||||
((value << offset) & mask));
|
};
|
||||||
|
|
||||||
|
//! Write access implementation for constant value.
|
||||||
|
/**
|
||||||
|
* @tparam MMIO_t Register memory device type.
|
||||||
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
|
* @tparam offset Field offset.
|
||||||
|
* @tparam value Value to be written at the field location.
|
||||||
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
|
*/
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value>
|
||||||
|
inline static void write(MMIO_t* const mmio_device) noexcept {
|
||||||
|
RegisterWriteConstant<MMIO_t, T, mask, offset, value>
|
||||||
|
::write(mmio_device);
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Set field implementation.
|
//! Set field implementation.
|
||||||
/**
|
/**
|
||||||
* @tparam T Field data type.
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
* @param mmio_device Pointer to register mapped memory.
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
* @param mask Field mask.
|
|
||||||
*/
|
*/
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask>
|
||||||
inline static void set(MMIO_t* const mmio_device, const T mask)
|
inline static void set(MMIO_t* const mmio_device)
|
||||||
noexcept {
|
noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device) | mask);
|
RegisterWriteConstant<MMIO_t, T, mask, 0u, mask>
|
||||||
|
::write(mmio_device);
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Clear field implementation.
|
//! Clear field implementation.
|
||||||
/**
|
/**
|
||||||
* @tparam MMIO_t Register memory device type.
|
* @tparam MMIO_t Register memory device type.
|
||||||
* @tparam T Field data type.
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
* @param mmio_device Pointer to register mapped memory.
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
* @param mask Field mask.
|
|
||||||
*/
|
*/
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask>
|
||||||
inline static void clear(MMIO_t* const mmio_device, const T mask)
|
inline static void clear(MMIO_t* const mmio_device)
|
||||||
noexcept {
|
noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device) & ~mask);
|
RegisterWriteConstant<MMIO_t, T, mask, 0u, ~mask>
|
||||||
|
::write(mmio_device);
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Toggle field implementation.
|
//! Toggle field implementation.
|
||||||
/**
|
/**
|
||||||
* @tparam MMIO_t Register memory device type.
|
* @tparam MMIO_t Register memory device type.
|
||||||
* @tparam T Field data type.
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
* @param mmio_device Pointer to register mapped memory.
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
* @param mask Field mask.
|
|
||||||
*/
|
*/
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask>
|
||||||
inline static void toggle(MMIO_t* const mmio_device, const T mask)
|
inline static void toggle(MMIO_t* const mmio_device)
|
||||||
noexcept {
|
noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device) ^ mask);
|
*mmio_device = static_cast<T>((*mmio_device) ^ mask);
|
||||||
};
|
};
|
||||||
@ -111,19 +295,38 @@ namespace cppreg {
|
|||||||
/**
|
/**
|
||||||
* @tparam MMIO_t Register memory device type.
|
* @tparam MMIO_t Register memory device type.
|
||||||
* @tparam T Field data type.
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
|
* @tparam offset Field offset
|
||||||
* @param mmio_device Pointer to register mapped memory.
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
* @param mask Field mask.
|
|
||||||
* @param offset Field offset
|
|
||||||
* @param value Value to be written at the field location.
|
* @param value Value to be written at the field location.
|
||||||
*/
|
*/
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
inline static void write(MMIO_t* const mmio_device,
|
inline static void write(MMIO_t* const mmio_device,
|
||||||
const T mask,
|
|
||||||
const Offset_t offset,
|
|
||||||
const T value) noexcept {
|
const T value) noexcept {
|
||||||
|
|
||||||
// We cannot read the current value so we simply fully write to it.
|
// For write-only fields we can only write to the whole register.
|
||||||
*mmio_device = ((value << offset) & mask);
|
RegisterWrite<MMIO_t, T, type_mask<T>::value, 0u>::write(
|
||||||
|
mmio_device, ((value << offset) & mask)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Write access implementation for constant value.
|
||||||
|
/**
|
||||||
|
* @tparam MMIO_t Register memory device type.
|
||||||
|
* @tparam T Field data type.
|
||||||
|
* @tparam mask Field mask.
|
||||||
|
* @tparam offset Field offset
|
||||||
|
* @tparam value Value to be written at the field location.
|
||||||
|
* @param mmio_device Pointer to register mapped memory.
|
||||||
|
*/
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value>
|
||||||
|
inline static void write(MMIO_t* const mmio_device) noexcept {
|
||||||
|
|
||||||
|
// For write-only fields we can only write to the whole register.
|
||||||
|
RegisterWriteConstant<
|
||||||
|
MMIO_t, T, type_mask<T>::value, 0u, ((value << offset) & mask)
|
||||||
|
>
|
||||||
|
::write(mmio_device);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
135
register/Field.h
135
register/Field.h
@ -6,6 +6,7 @@
|
|||||||
*
|
*
|
||||||
* This header provides the definitions related to register field
|
* This header provides the definitions related to register field
|
||||||
* implementation.
|
* implementation.
|
||||||
|
* Strictly speaking a field is defined as a region of a register.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ namespace cppreg {
|
|||||||
* @tparam BaseRegister Parent register.
|
* @tparam BaseRegister Parent register.
|
||||||
* @tparam width Field width.
|
* @tparam width Field width.
|
||||||
* @tparam offset Field offset.
|
* @tparam offset Field offset.
|
||||||
* @tparam P Access policy type (rw, ro, wo).
|
* @tparam P Access policy type.
|
||||||
*
|
*
|
||||||
* This data structure provides static methods to deal with field access
|
* This data structure provides static methods to deal with field access
|
||||||
* (read, write, set, clear, and toggle). These methods availability depends
|
* (read, write, set, clear, and toggle). These methods availability depends
|
||||||
@ -53,26 +54,29 @@ namespace cppreg {
|
|||||||
//! MMIO type.
|
//! MMIO type.
|
||||||
using MMIO_t = typename parent_register::MMIO_t;
|
using MMIO_t = typename parent_register::MMIO_t;
|
||||||
|
|
||||||
|
//! Field policy.
|
||||||
|
using policy = AccessPolicy;
|
||||||
|
|
||||||
//! Field width.
|
//! Field width.
|
||||||
constexpr static const Width_t width = FieldWidth;
|
constexpr static const Width_t width = FieldWidth;
|
||||||
|
|
||||||
//! Field offset.
|
//! Field offset.
|
||||||
constexpr static const Offset_t offset = FieldOffset;
|
constexpr static const Offset_t offset = FieldOffset;
|
||||||
|
|
||||||
//! Field policy.
|
|
||||||
using policy = AccessPolicy;
|
|
||||||
|
|
||||||
//! Field mask.
|
//! Field mask.
|
||||||
/**
|
|
||||||
* The field mask is computed at compile time.
|
|
||||||
*/
|
|
||||||
constexpr static const type mask = make_shifted_mask<type>(width,
|
constexpr static const type mask = make_shifted_mask<type>(width,
|
||||||
offset);
|
offset);
|
||||||
|
|
||||||
|
//! Boolean flag indicating if a shadow value is used.
|
||||||
|
constexpr static const bool has_shadow =
|
||||||
|
parent_register::shadow::use_shadow;
|
||||||
|
|
||||||
//! Customized overflow check implementation for Field types.
|
//! Customized overflow check implementation for Field types.
|
||||||
/**
|
/**
|
||||||
* @tparam value Value to be checked for overflow.
|
* @tparam value Value to be checked for overflow.
|
||||||
* @return `true` if no overflow, `false` otherwise.
|
* @return `true` if no overflow, `false` otherwise.
|
||||||
|
*
|
||||||
|
* This is only used for the template form of the write method.
|
||||||
*/
|
*/
|
||||||
template <type value>
|
template <type value>
|
||||||
struct check_overflow {
|
struct check_overflow {
|
||||||
@ -84,125 +88,104 @@ namespace cppreg {
|
|||||||
>::result::value;
|
>::result::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Field read method.
|
//!@ Field read method.
|
||||||
/**
|
/**
|
||||||
* @return Field value.
|
* @return Field value.
|
||||||
*/
|
*/
|
||||||
inline static type read() noexcept {
|
inline static type read() noexcept {
|
||||||
return
|
return policy::template read<MMIO_t, type, mask, offset>(
|
||||||
AccessPolicy::read(parent_register::ro_mem_pointer(),
|
parent_register::ro_mem_pointer()
|
||||||
mask,
|
);
|
||||||
offset);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Field write method (shadow value disabled).
|
//! Field write method (no shadow value).
|
||||||
/**
|
/**
|
||||||
* @param value Value to be written to the field.
|
* @param value Value to be written to the field.
|
||||||
*
|
|
||||||
* We use SFINAE to discriminate for registers with shadow value
|
|
||||||
* enabled.
|
|
||||||
*
|
|
||||||
* This method does not perform any check on the input value. If the
|
|
||||||
* input value is too large for the field size it will not overflow
|
|
||||||
* but only the part that fits in the field will be written.
|
|
||||||
* For safe write see
|
|
||||||
*/
|
*/
|
||||||
template <typename T = type>
|
template <typename T = type>
|
||||||
inline static void
|
inline static void
|
||||||
write(const typename std::enable_if<
|
write(const typename std::enable_if<!has_shadow, T>::type value)
|
||||||
!parent_register::shadow::use_shadow, T
|
noexcept {
|
||||||
>::type value) noexcept {
|
policy::template write<MMIO_t, type, mask, offset>(
|
||||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
parent_register::rw_mem_pointer(),
|
||||||
mask,
|
value
|
||||||
offset,
|
);
|
||||||
value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Field write method (shadow value enabled).
|
//! Field write method (w/ shadow value).
|
||||||
/**
|
/**
|
||||||
* @param value Value to be written to the field.
|
* @param value Value to be written to the field.
|
||||||
*
|
|
||||||
* We use SFINAE to discriminate for registers with shadow value
|
|
||||||
* enabled.
|
|
||||||
*
|
|
||||||
* This method does not perform any check on the input value. If the
|
|
||||||
* input value is too large for the field size it will not overflow
|
|
||||||
* but only the part that fits in the field will be written.
|
|
||||||
* For safe write see
|
|
||||||
*/
|
*/
|
||||||
template <typename T = type>
|
template <typename T = type>
|
||||||
inline static void
|
inline static void
|
||||||
write(const typename std::enable_if<
|
write(const typename std::enable_if<has_shadow, T>::type value)
|
||||||
parent_register::shadow::use_shadow, T
|
noexcept {
|
||||||
>::type value) noexcept {
|
|
||||||
|
|
||||||
// Update shadow value.
|
// Update shadow value.
|
||||||
// Fetch the whole register content.
|
|
||||||
// This assumes that reading a write-only fields return some value.
|
// This assumes that reading a write-only fields return some value.
|
||||||
parent_register::shadow::value =
|
RegisterWrite<type, type, mask, offset>
|
||||||
(parent_register::shadow::value & ~mask) |
|
::write(&parent_register::shadow::value, value);
|
||||||
((value << offset) & mask);
|
|
||||||
|
|
||||||
// Write as a block to the register, that is, we do not use the
|
// Write as a block to the register, that is, we do not use the
|
||||||
// mask and offset.
|
// mask and offset.
|
||||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
policy::template write<MMIO_t, type, type_mask<type>::value, 0u>(
|
||||||
~(0u),
|
parent_register::rw_mem_pointer(),
|
||||||
0u,
|
parent_register::shadow::value
|
||||||
parent_register::shadow::value);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Field write method with compile-time check (shadow value disabled).
|
//! Field write method with overflow check (no shadow value).
|
||||||
/**
|
/**
|
||||||
* @tparam value Value to be written to the field
|
* @tparam value Value to be written to the field
|
||||||
*
|
*
|
||||||
* We use SFINAE to discriminate for registers with shadow value
|
|
||||||
* enabled.
|
|
||||||
*
|
|
||||||
* This method performs a compile-time check to avoid overflowing the
|
* This method performs a compile-time check to avoid overflowing the
|
||||||
* field.
|
* field and uses the constant write implementation.
|
||||||
*/
|
*/
|
||||||
template <type value, typename T = void>
|
template <type value, typename T = void>
|
||||||
inline static
|
inline static
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
(!parent_register::shadow::use_shadow)
|
!has_shadow
|
||||||
&&
|
&&
|
||||||
check_overflow<value>::result,
|
check_overflow<value>::result,
|
||||||
T
|
T
|
||||||
>::type
|
>::type
|
||||||
write() noexcept {
|
write() noexcept {
|
||||||
write(value);
|
policy::template write<MMIO_t, type, mask, offset, value>(
|
||||||
|
parent_register::rw_mem_pointer()
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Field write method with compile-time check (shadow value enabled).
|
//! Field write method with overflow check (w/ shadow value).
|
||||||
/**
|
/**
|
||||||
* @tparam value Value to be written to the field
|
* @tparam value Value to be written to the field
|
||||||
*
|
*
|
||||||
* We use SFINAE to discriminate for registers with shadow value
|
|
||||||
* enabled.
|
|
||||||
*
|
|
||||||
* This method performs a compile-time check to avoid overflowing the
|
* This method performs a compile-time check to avoid overflowing the
|
||||||
* field.
|
* field and uses the constant write implementation.
|
||||||
*/
|
*/
|
||||||
template <type value, typename T = void>
|
template <type value, typename T = void>
|
||||||
inline static
|
inline static
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
parent_register::shadow::use_shadow
|
has_shadow
|
||||||
&&
|
&&
|
||||||
check_overflow<value>::result,
|
check_overflow<value>::result,
|
||||||
T
|
T
|
||||||
>::type
|
>::type
|
||||||
write() noexcept {
|
write() noexcept {
|
||||||
write(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// For this particular we can simply forward to the non-constant
|
||||||
|
// implementation.
|
||||||
|
write(value);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
//! Field set method.
|
//! Field set method.
|
||||||
/**
|
/**
|
||||||
* This method will set all bits in the field.
|
* This method will set all bits in the field.
|
||||||
*/
|
*/
|
||||||
inline static void set() noexcept {
|
inline static void set() noexcept {
|
||||||
AccessPolicy::set(parent_register::rw_mem_pointer(), mask);
|
policy::template
|
||||||
|
set<MMIO_t, type, mask>(parent_register::rw_mem_pointer());
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Field clear method.
|
//! Field clear method.
|
||||||
@ -210,7 +193,8 @@ namespace cppreg {
|
|||||||
* This method will clear all bits in the field.
|
* This method will clear all bits in the field.
|
||||||
*/
|
*/
|
||||||
inline static void clear() noexcept {
|
inline static void clear() noexcept {
|
||||||
AccessPolicy::clear(parent_register::rw_mem_pointer(), mask);
|
policy::template
|
||||||
|
clear<MMIO_t, type, mask>(parent_register::rw_mem_pointer());
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Field toggle method.
|
//! Field toggle method.
|
||||||
@ -218,30 +202,23 @@ namespace cppreg {
|
|||||||
* This method will toggle all bits in the field.
|
* This method will toggle all bits in the field.
|
||||||
*/
|
*/
|
||||||
inline static void toggle() noexcept {
|
inline static void toggle() noexcept {
|
||||||
AccessPolicy::toggle(parent_register::rw_mem_pointer(), mask);
|
policy::template
|
||||||
|
toggle<MMIO_t, type, mask>(parent_register::rw_mem_pointer());
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Is field set bool method.
|
//! Is field set bool method.
|
||||||
/**
|
/**
|
||||||
* @return `true` if the 1-bit field is set to 1, `false` otherwise.
|
* @return `true` if all the bits are set to 1, `false` otherwise.
|
||||||
*
|
|
||||||
* This is only available if the field is 1 bit wide.
|
|
||||||
*/
|
*/
|
||||||
template <typename T = bool>
|
inline static bool is_set() noexcept {
|
||||||
inline static typename std::enable_if<FieldWidth == 1, T>::type
|
return (Field::read() == (mask >> offset));
|
||||||
is_set() noexcept {
|
|
||||||
return (Field::read() == 1u);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Is field clear bool method.
|
//! Is field clear bool method.
|
||||||
/**
|
/**
|
||||||
* @return `true` if the 1-bit field is set to 0, `false` otherwise.
|
* @return `true` if all the bits are set to 0, `false` otherwise.
|
||||||
*
|
|
||||||
* This is only available if the field is 1 bit wide.
|
|
||||||
*/
|
*/
|
||||||
template <typename T = bool>
|
inline static bool is_clear() noexcept {
|
||||||
inline static typename std::enable_if<FieldWidth == 1, T>::type
|
|
||||||
is_clear() noexcept {
|
|
||||||
return (Field::read() == 0u);
|
return (Field::read() == 0u);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
*
|
*
|
||||||
* The "merge write" implementation is designed to make it possible to merge
|
* The "merge write" implementation is designed to make it possible to merge
|
||||||
* write operations on different fields into a single one.
|
* write operations on different fields into a single one.
|
||||||
|
* The implementation distinguishes between values known at compile time and
|
||||||
|
* value known at run time.
|
||||||
|
*
|
||||||
|
* By design the merge write implementation forces the caller to chain and
|
||||||
|
* finalize all write operations in a single pass.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -22,12 +27,28 @@
|
|||||||
namespace cppreg {
|
namespace cppreg {
|
||||||
|
|
||||||
|
|
||||||
//! Write operation holding structure.
|
//! Merge write constant implementation.
|
||||||
/**
|
/**
|
||||||
* @tparam Register Underlying register for the final write operation.
|
* @tparam Register Register on which the merged write will be performed.
|
||||||
|
* @tparam mask Initial mask.
|
||||||
|
* @tparam offset Initial offset.
|
||||||
|
* @tparam value Initial value.
|
||||||
|
*
|
||||||
|
* The initial data will come from the field on which the merge write
|
||||||
|
* will be initiated.
|
||||||
|
*
|
||||||
|
* This implementation is designed for operations in which all data is
|
||||||
|
* available at compile time. This makes it possible to leverage a
|
||||||
|
* template implementation and simplify most of the operations (based on
|
||||||
|
* the access policies implementation that detects trivial operations). In
|
||||||
|
* addition, this will also perform overflow checking.
|
||||||
*/
|
*/
|
||||||
template <typename Register>
|
template <
|
||||||
class MergeWrite {
|
typename Register,
|
||||||
|
typename Register::type mask,
|
||||||
|
Offset_t offset,
|
||||||
|
typename Register::type value
|
||||||
|
> class MergeWrite_tmpl {
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -35,31 +56,42 @@ namespace cppreg {
|
|||||||
//! Type alias to register base type.
|
//! Type alias to register base type.
|
||||||
using base_type = typename Register::type;
|
using base_type = typename Register::type;
|
||||||
|
|
||||||
//! Static instantiation method.
|
|
||||||
static MergeWrite create_instance(const base_type value,
|
|
||||||
const base_type mask) noexcept {
|
|
||||||
MergeWrite mw;
|
|
||||||
mw._accumulated_value = value;
|
|
||||||
mw._combined_mask = mask;
|
|
||||||
return mw;
|
|
||||||
};
|
|
||||||
|
|
||||||
//!@ Move constructor.
|
private:
|
||||||
MergeWrite(MergeWrite&& mw) noexcept
|
|
||||||
: _accumulated_value(mw._accumulated_value),
|
|
||||||
_combined_mask(mw._combined_mask) {
|
|
||||||
};
|
|
||||||
|
|
||||||
//!@{ Non-copyable.
|
// Disabled for shadow value register.
|
||||||
MergeWrite(const MergeWrite&) = delete;
|
static_assert(!Register::shadow::use_shadow,
|
||||||
MergeWrite& operator=(const MergeWrite&) = delete;
|
"merge write is not available for shadow value register");
|
||||||
|
|
||||||
|
// Accumulated value.
|
||||||
|
constexpr static const base_type _accumulated_value =
|
||||||
|
((value << offset) & mask);
|
||||||
|
|
||||||
|
// Combined mask.
|
||||||
|
constexpr static const base_type _combined_mask = mask;
|
||||||
|
|
||||||
|
// Default constructor.
|
||||||
|
MergeWrite_tmpl() {};
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Instantiation method.
|
||||||
|
inline static MergeWrite_tmpl make() noexcept { return {}; };
|
||||||
|
|
||||||
|
//!@{ Non-copyable and non-moveable.
|
||||||
|
MergeWrite_tmpl(const MergeWrite_tmpl&) = delete;
|
||||||
|
MergeWrite_tmpl& operator=(const MergeWrite_tmpl&) = delete;
|
||||||
|
MergeWrite_tmpl& operator=(MergeWrite_tmpl&&) = delete;
|
||||||
|
MergeWrite_tmpl operator=(MergeWrite_tmpl) = delete;
|
||||||
|
MergeWrite_tmpl(MergeWrite_tmpl&&) = delete;
|
||||||
//!@}
|
//!@}
|
||||||
|
|
||||||
//! Destructor.
|
//! Closure method.
|
||||||
/**
|
/**
|
||||||
* This is where the write operation is performed.
|
* This is where the write happens.
|
||||||
*/
|
*/
|
||||||
~MergeWrite() {
|
inline void done() const && noexcept {
|
||||||
|
|
||||||
// Get memory pointer.
|
// Get memory pointer.
|
||||||
typename Register::MMIO_t* const mmio_device =
|
typename Register::MMIO_t* const mmio_device =
|
||||||
@ -68,10 +100,111 @@ namespace cppreg {
|
|||||||
// Write to the whole register using the current accumulated value
|
// Write to the whole register using the current accumulated value
|
||||||
// and combined mask.
|
// and combined mask.
|
||||||
// No offset needed because we write to the whole register.
|
// No offset needed because we write to the whole register.
|
||||||
*mmio_device = static_cast<base_type>(
|
RegisterWriteConstant<
|
||||||
(*mmio_device & ~_combined_mask) |
|
typename Register::MMIO_t,
|
||||||
((_accumulated_value) & _combined_mask)
|
typename Register::type,
|
||||||
);
|
_combined_mask,
|
||||||
|
0u,
|
||||||
|
_accumulated_value
|
||||||
|
>::write(mmio_device);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//! With method for constant value.
|
||||||
|
/**
|
||||||
|
* @tparam F Field to be written
|
||||||
|
* @tparam new_value Value to write to the field.
|
||||||
|
* @return A new instance for chaining other write operations.
|
||||||
|
*/
|
||||||
|
template <
|
||||||
|
typename F,
|
||||||
|
base_type new_value,
|
||||||
|
typename T = MergeWrite_tmpl<
|
||||||
|
Register,
|
||||||
|
(_combined_mask | F::mask),
|
||||||
|
0u,
|
||||||
|
(_accumulated_value & ~F::mask) | ((new_value << F::offset) &
|
||||||
|
F::mask)
|
||||||
|
>
|
||||||
|
>
|
||||||
|
inline
|
||||||
|
typename std::enable_if<
|
||||||
|
(internals::check_overflow<
|
||||||
|
Register::size, new_value, (F::mask >> F::offset)
|
||||||
|
>::result::value),
|
||||||
|
T
|
||||||
|
>::type&&
|
||||||
|
with() const && noexcept {
|
||||||
|
return std::move(T::make());
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! Merge write implementation.
|
||||||
|
/**
|
||||||
|
* @tparam Register Register on which the merged write will be performed.
|
||||||
|
* @tparam mask Initial mask.
|
||||||
|
*
|
||||||
|
* The initial mask will come from the field on which the merge write
|
||||||
|
* will be initiated.
|
||||||
|
*/
|
||||||
|
template <
|
||||||
|
typename Register,
|
||||||
|
typename Register::type mask
|
||||||
|
>
|
||||||
|
class MergeWrite {
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Type alias to register base type.
|
||||||
|
using base_type = typename Register::type;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Combined mask.
|
||||||
|
constexpr static const base_type _combined_mask = mask;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Static instantiation method.
|
||||||
|
constexpr static MergeWrite make(const base_type value) noexcept {
|
||||||
|
return MergeWrite(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
//!@ Move constructor.
|
||||||
|
MergeWrite(MergeWrite&& mw) noexcept
|
||||||
|
: _accumulated_value(mw._accumulated_value) {};
|
||||||
|
|
||||||
|
//!@{ Non-copyable.
|
||||||
|
MergeWrite(const MergeWrite&) = delete;
|
||||||
|
MergeWrite& operator=(const MergeWrite&) = delete;
|
||||||
|
MergeWrite& operator=(MergeWrite&&) = delete;
|
||||||
|
//!@}
|
||||||
|
|
||||||
|
//! Closure method.
|
||||||
|
/**
|
||||||
|
* This is where the write happens.
|
||||||
|
*/
|
||||||
|
inline void done() const && noexcept {
|
||||||
|
|
||||||
|
// Get memory pointer.
|
||||||
|
typename Register::MMIO_t* const mmio_device =
|
||||||
|
Register::rw_mem_pointer();
|
||||||
|
|
||||||
|
// Write to the whole register using the current accumulated value
|
||||||
|
// and combined mask.
|
||||||
|
// No offset needed because we write to the whole register.
|
||||||
|
RegisterWrite<
|
||||||
|
typename Register::MMIO_t,
|
||||||
|
base_type,
|
||||||
|
_combined_mask,
|
||||||
|
0u
|
||||||
|
>::write(mmio_device, _accumulated_value);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -80,12 +213,10 @@ namespace cppreg {
|
|||||||
* @tparam F Field type describing where to write in the register.
|
* @tparam F Field type describing where to write in the register.
|
||||||
* @param value Value to write to the register.
|
* @param value Value to write to the register.
|
||||||
* @return A reference to the current merge write data.
|
* @return A reference to the current merge write data.
|
||||||
*
|
|
||||||
* This method is used to add another operation to the final merged
|
|
||||||
* write.
|
|
||||||
*/
|
*/
|
||||||
template <typename F>
|
template <typename F>
|
||||||
MergeWrite&& with(const base_type value) && noexcept {
|
inline MergeWrite<Register, _combined_mask | F::mask> with
|
||||||
|
(const base_type value) && noexcept {
|
||||||
|
|
||||||
// Check that the field belongs to the register.
|
// Check that the field belongs to the register.
|
||||||
static_assert(std::is_same<
|
static_assert(std::is_same<
|
||||||
@ -95,43 +226,19 @@ namespace cppreg {
|
|||||||
"field is not from the same register in merge_write");
|
"field is not from the same register in merge_write");
|
||||||
|
|
||||||
// Update accumulated value.
|
// Update accumulated value.
|
||||||
F::policy::write(&_accumulated_value,
|
F::policy::template write<
|
||||||
F::mask,
|
base_type,
|
||||||
F::offset,
|
base_type,
|
||||||
value);
|
F::mask,
|
||||||
|
F::offset
|
||||||
|
>(&_accumulated_value, value);
|
||||||
|
|
||||||
// Update combine mask.
|
return
|
||||||
_combined_mask = _combined_mask | F::mask;
|
std::move(
|
||||||
|
MergeWrite<Register, (_combined_mask | F::mask)>
|
||||||
|
::make(_accumulated_value)
|
||||||
|
);
|
||||||
|
|
||||||
return std::move(*this);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//! With method with compile-time check.
|
|
||||||
/**
|
|
||||||
* @tparam F Field type describing where to write in the register.
|
|
||||||
* @param value Value to write to the register.
|
|
||||||
* @return A reference to the current merge write data.
|
|
||||||
*
|
|
||||||
* This method is used to add another operation to the final merged
|
|
||||||
* write.
|
|
||||||
*
|
|
||||||
* This method performs a compile-time check to avoid overflowing the
|
|
||||||
* field.
|
|
||||||
*/
|
|
||||||
template <
|
|
||||||
typename F,
|
|
||||||
base_type value,
|
|
||||||
typename T = MergeWrite
|
|
||||||
>
|
|
||||||
typename std::enable_if<
|
|
||||||
(internals::check_overflow<
|
|
||||||
Register::size, value, (F::mask >> F::offset)
|
|
||||||
>::result::value),
|
|
||||||
T
|
|
||||||
>::type&&
|
|
||||||
with() && noexcept {
|
|
||||||
return std::move(*this).template with<F>(value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -142,15 +249,12 @@ namespace cppreg {
|
|||||||
"merge write is not available for shadow value register");
|
"merge write is not available for shadow value register");
|
||||||
|
|
||||||
// Private default constructor.
|
// Private default constructor.
|
||||||
MergeWrite() : _accumulated_value(0u),
|
constexpr MergeWrite() : _accumulated_value(0u) {};
|
||||||
_combined_mask(0u) {};
|
constexpr MergeWrite(const base_type v) : _accumulated_value(v) {};
|
||||||
|
|
||||||
// Accumulated value.
|
// Accumulated value.
|
||||||
base_type _accumulated_value;
|
base_type _accumulated_value;
|
||||||
|
|
||||||
// Combined mask.
|
|
||||||
base_type _combined_mask;
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,30 +76,35 @@ namespace cppreg {
|
|||||||
return reinterpret_cast<const MMIO_t* const>(base_address);
|
return reinterpret_cast<const MMIO_t* const>(base_address);
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Merge write function.
|
//! Merge write start function.
|
||||||
/**
|
/**
|
||||||
* @tparam F Field on which to perform the write operation.
|
* @tparam F Field on which to perform the first write operation.
|
||||||
* @param value Value to write to the field.
|
* @param value Value to be written to the field.
|
||||||
* @return A merge write data structure.
|
* @return A merge write data structure to chain further writes.
|
||||||
*/
|
*/
|
||||||
template <typename F>
|
template <typename F>
|
||||||
inline static MergeWrite<typename F::parent_register>
|
inline static MergeWrite<typename F::parent_register, F::mask>
|
||||||
merge_write(const typename F::type value) noexcept {
|
merge_write(const typename F::type value) noexcept {
|
||||||
return
|
return
|
||||||
MergeWrite<typename F::parent_register>
|
MergeWrite<typename F::parent_register, F::mask>
|
||||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
::make(((value << F::offset) & F::mask));
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Merge write function.
|
//! Merge write start function for constant value.
|
||||||
/**
|
/**
|
||||||
* @tparam F Field on which to perform the write operation.
|
* @tparam F Field on which to perform the first write operation.
|
||||||
* @param value Value to write to the field.
|
* @tparam value Value to be written to the field.
|
||||||
* @return A merge write data structure.
|
* @return A merge write data structure to chain further writes.
|
||||||
*/
|
*/
|
||||||
template <
|
template <
|
||||||
typename F,
|
typename F,
|
||||||
type value,
|
type value,
|
||||||
typename T = MergeWrite<typename F::parent_register>
|
typename T = MergeWrite_tmpl<
|
||||||
|
typename F::parent_register,
|
||||||
|
F::mask,
|
||||||
|
F::offset,
|
||||||
|
value
|
||||||
|
>
|
||||||
>
|
>
|
||||||
inline static
|
inline static
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
@ -107,11 +112,9 @@ namespace cppreg {
|
|||||||
size, value, (F::mask >> F::offset)
|
size, value, (F::mask >> F::offset)
|
||||||
>::result::value,
|
>::result::value,
|
||||||
T
|
T
|
||||||
>::type
|
>::type&&
|
||||||
merge_write() noexcept {
|
merge_write() noexcept {
|
||||||
return
|
return std::move(T::make());
|
||||||
MergeWrite<typename F::parent_register>
|
|
||||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
|
@ -49,6 +49,7 @@ namespace cppreg {
|
|||||||
template <typename Register>
|
template <typename Register>
|
||||||
constexpr const bool Shadow<Register, true>::use_shadow;
|
constexpr const bool Shadow<Register, true>::use_shadow;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
// cppreg_Defines.h
|
// cppreg_Defines.h
|
||||||
#ifndef CPPREG_CPPREG_DEFINES_H
|
#ifndef CPPREG_CPPREG_DEFINES_H
|
||||||
@ -16,6 +17,10 @@ namespace cppreg {
|
|||||||
using Address_t = std::uintptr_t;
|
using Address_t = std::uintptr_t;
|
||||||
using Width_t = std::uint8_t;
|
using Width_t = std::uint8_t;
|
||||||
using Offset_t = std::uint8_t;
|
using Offset_t = std::uint8_t;
|
||||||
|
template <typename T>
|
||||||
|
struct type_mask {
|
||||||
|
constexpr static const T value = std::numeric_limits<T>::max();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -23,46 +28,118 @@ namespace cppreg {
|
|||||||
#ifndef CPPREG_ACCESSPOLICY_H
|
#ifndef CPPREG_ACCESSPOLICY_H
|
||||||
#define CPPREG_ACCESSPOLICY_H
|
#define CPPREG_ACCESSPOLICY_H
|
||||||
namespace cppreg {
|
namespace cppreg {
|
||||||
struct read_only {
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
template <typename MMIO_t, typename T>
|
struct RegisterRead {
|
||||||
inline static T read(const MMIO_t* const mmio_device,
|
constexpr static const bool is_trivial =
|
||||||
const T mask,
|
(mask == type_mask<T>::value) && (offset == 0u);
|
||||||
const Offset_t offset) noexcept {
|
template <typename U = void>
|
||||||
|
inline static T read(
|
||||||
|
const MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<!is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
return static_cast<T>((*mmio_device & mask) >> offset);
|
return static_cast<T>((*mmio_device & mask) >> offset);
|
||||||
};
|
};
|
||||||
|
template <typename U = void>
|
||||||
|
inline static T read(
|
||||||
|
const MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
return static_cast<T>(*mmio_device);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
|
struct RegisterWrite {
|
||||||
|
constexpr static const bool is_trivial =
|
||||||
|
(mask == type_mask<T>::value) && (offset == 0u);
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
T value,
|
||||||
|
typename std::enable_if<!is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = static_cast<T>(
|
||||||
|
(*mmio_device & ~mask) | ((value << offset) & mask)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
T value,
|
||||||
|
typename std::enable_if<is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value>
|
||||||
|
struct RegisterWriteConstant {
|
||||||
|
constexpr static const bool is_trivial =
|
||||||
|
(mask == type_mask<T>::value) && (offset == 0u);
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<!is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = static_cast<T>(
|
||||||
|
(*mmio_device & ~mask) | ((value << offset) & mask)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
template <typename U = void>
|
||||||
|
inline static void write(
|
||||||
|
MMIO_t* const mmio_device,
|
||||||
|
typename std::enable_if<is_trivial, U*>::type = nullptr
|
||||||
|
) noexcept {
|
||||||
|
*mmio_device = value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
struct read_only {
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
|
inline static T read(const MMIO_t* const mmio_device) noexcept {
|
||||||
|
return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
struct read_write : read_only {
|
struct read_write : read_only {
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
inline static void write(MMIO_t* const mmio_device,
|
inline static void write(MMIO_t* const mmio_device,
|
||||||
const T mask,
|
|
||||||
const Offset_t offset,
|
|
||||||
const T value) noexcept {
|
const T value) noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device & ~mask) |
|
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
|
||||||
((value << offset) & mask));
|
|
||||||
};
|
};
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value>
|
||||||
inline static void set(MMIO_t* const mmio_device, const T mask)
|
inline static void write(MMIO_t* const mmio_device) noexcept {
|
||||||
|
RegisterWriteConstant<MMIO_t, T, mask, offset, value>
|
||||||
|
::write(mmio_device);
|
||||||
|
};
|
||||||
|
template <typename MMIO_t, typename T, T mask>
|
||||||
|
inline static void set(MMIO_t* const mmio_device)
|
||||||
noexcept {
|
noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device) | mask);
|
RegisterWriteConstant<MMIO_t, T, mask, 0u, mask>
|
||||||
|
::write(mmio_device);
|
||||||
};
|
};
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask>
|
||||||
inline static void clear(MMIO_t* const mmio_device, const T mask)
|
inline static void clear(MMIO_t* const mmio_device)
|
||||||
noexcept {
|
noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device) & ~mask);
|
RegisterWriteConstant<MMIO_t, T, mask, 0u, ~mask>
|
||||||
|
::write(mmio_device);
|
||||||
};
|
};
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask>
|
||||||
inline static void toggle(MMIO_t* const mmio_device, const T mask)
|
inline static void toggle(MMIO_t* const mmio_device)
|
||||||
noexcept {
|
noexcept {
|
||||||
*mmio_device = static_cast<T>((*mmio_device) ^ mask);
|
*mmio_device = static_cast<T>((*mmio_device) ^ mask);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
struct write_only {
|
struct write_only {
|
||||||
template <typename MMIO_t, typename T>
|
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||||
inline static void write(MMIO_t* const mmio_device,
|
inline static void write(MMIO_t* const mmio_device,
|
||||||
const T mask,
|
|
||||||
const Offset_t offset,
|
|
||||||
const T value) noexcept {
|
const T value) noexcept {
|
||||||
*mmio_device = ((value << offset) & mask);
|
RegisterWrite<MMIO_t, T, type_mask<T>::value, 0u>::write(
|
||||||
|
mmio_device, ((value << offset) & mask)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value>
|
||||||
|
inline static void write(MMIO_t* const mmio_device) noexcept {
|
||||||
|
RegisterWriteConstant<
|
||||||
|
MMIO_t, T, type_mask<T>::value, 0u, ((value << offset) & mask)
|
||||||
|
>
|
||||||
|
::write(mmio_device);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -144,66 +221,115 @@ namespace cppreg {
|
|||||||
#ifndef CPPREG_MERGEWRITE_H
|
#ifndef CPPREG_MERGEWRITE_H
|
||||||
#define CPPREG_MERGEWRITE_H
|
#define CPPREG_MERGEWRITE_H
|
||||||
namespace cppreg {
|
namespace cppreg {
|
||||||
template <typename Register>
|
template <
|
||||||
|
typename Register,
|
||||||
|
typename Register::type mask,
|
||||||
|
Offset_t offset,
|
||||||
|
typename Register::type value
|
||||||
|
> class MergeWrite_tmpl {
|
||||||
|
public:
|
||||||
|
using base_type = typename Register::type;
|
||||||
|
private:
|
||||||
|
static_assert(!Register::shadow::use_shadow,
|
||||||
|
"merge write is not available for shadow value register");
|
||||||
|
constexpr static const base_type _accumulated_value =
|
||||||
|
((value << offset) & mask);
|
||||||
|
constexpr static const base_type _combined_mask = mask;
|
||||||
|
MergeWrite_tmpl() {};
|
||||||
|
public:
|
||||||
|
inline static MergeWrite_tmpl make() noexcept { return {}; };
|
||||||
|
MergeWrite_tmpl(const MergeWrite_tmpl&) = delete;
|
||||||
|
MergeWrite_tmpl& operator=(const MergeWrite_tmpl&) = delete;
|
||||||
|
MergeWrite_tmpl& operator=(MergeWrite_tmpl&&) = delete;
|
||||||
|
MergeWrite_tmpl operator=(MergeWrite_tmpl) = delete;
|
||||||
|
MergeWrite_tmpl(MergeWrite_tmpl&&) = delete;
|
||||||
|
inline void done() const && noexcept {
|
||||||
|
typename Register::MMIO_t* const mmio_device =
|
||||||
|
Register::rw_mem_pointer();
|
||||||
|
RegisterWriteConstant<
|
||||||
|
typename Register::MMIO_t,
|
||||||
|
typename Register::type,
|
||||||
|
_combined_mask,
|
||||||
|
0u,
|
||||||
|
_accumulated_value
|
||||||
|
>::write(mmio_device);
|
||||||
|
};
|
||||||
|
template <
|
||||||
|
typename F,
|
||||||
|
base_type new_value,
|
||||||
|
typename T = MergeWrite_tmpl<
|
||||||
|
Register,
|
||||||
|
(_combined_mask | F::mask),
|
||||||
|
0u,
|
||||||
|
(_accumulated_value & ~F::mask) | ((new_value << F::offset) &
|
||||||
|
F::mask)
|
||||||
|
>
|
||||||
|
>
|
||||||
|
inline
|
||||||
|
typename std::enable_if<
|
||||||
|
(internals::check_overflow<
|
||||||
|
Register::size, new_value, (F::mask >> F::offset)
|
||||||
|
>::result::value),
|
||||||
|
T
|
||||||
|
>::type&&
|
||||||
|
with() const && noexcept {
|
||||||
|
return std::move(T::make());
|
||||||
|
};
|
||||||
|
};
|
||||||
|
template <
|
||||||
|
typename Register,
|
||||||
|
typename Register::type mask
|
||||||
|
>
|
||||||
class MergeWrite {
|
class MergeWrite {
|
||||||
public:
|
public:
|
||||||
using base_type = typename Register::type;
|
using base_type = typename Register::type;
|
||||||
static MergeWrite create_instance(const base_type value,
|
private:
|
||||||
const base_type mask) noexcept {
|
constexpr static const base_type _combined_mask = mask;
|
||||||
MergeWrite mw;
|
public:
|
||||||
mw._accumulated_value = value;
|
constexpr static MergeWrite make(const base_type value) noexcept {
|
||||||
mw._combined_mask = mask;
|
return MergeWrite(value);
|
||||||
return mw;
|
|
||||||
};
|
};
|
||||||
MergeWrite(MergeWrite&& mw) noexcept
|
MergeWrite(MergeWrite&& mw) noexcept
|
||||||
: _accumulated_value(mw._accumulated_value),
|
: _accumulated_value(mw._accumulated_value) {};
|
||||||
_combined_mask(mw._combined_mask) {
|
|
||||||
};
|
|
||||||
MergeWrite(const MergeWrite&) = delete;
|
MergeWrite(const MergeWrite&) = delete;
|
||||||
MergeWrite& operator=(const MergeWrite&) = delete;
|
MergeWrite& operator=(const MergeWrite&) = delete;
|
||||||
~MergeWrite() {
|
MergeWrite& operator=(MergeWrite&&) = delete;
|
||||||
|
inline void done() const && noexcept {
|
||||||
typename Register::MMIO_t* const mmio_device =
|
typename Register::MMIO_t* const mmio_device =
|
||||||
Register::rw_mem_pointer();
|
Register::rw_mem_pointer();
|
||||||
*mmio_device = static_cast<base_type>(
|
RegisterWrite<
|
||||||
(*mmio_device & ~_combined_mask) |
|
typename Register::MMIO_t,
|
||||||
((_accumulated_value) & _combined_mask)
|
base_type,
|
||||||
);
|
_combined_mask,
|
||||||
|
0u
|
||||||
|
>::write(mmio_device, _accumulated_value);
|
||||||
};
|
};
|
||||||
template <typename F>
|
template <typename F>
|
||||||
MergeWrite&& with(const base_type value) && noexcept {
|
inline MergeWrite<Register, _combined_mask | F::mask> with
|
||||||
|
(const base_type value) && noexcept {
|
||||||
static_assert(std::is_same<
|
static_assert(std::is_same<
|
||||||
typename F::parent_register,
|
typename F::parent_register,
|
||||||
Register
|
Register
|
||||||
>::value,
|
>::value,
|
||||||
"field is not from the same register in merge_write");
|
"field is not from the same register in merge_write");
|
||||||
F::policy::write(&_accumulated_value,
|
F::policy::template write<
|
||||||
F::mask,
|
base_type,
|
||||||
F::offset,
|
base_type,
|
||||||
value);
|
F::mask,
|
||||||
_combined_mask = _combined_mask | F::mask;
|
F::offset
|
||||||
return std::move(*this);
|
>(&_accumulated_value, value);
|
||||||
};
|
return
|
||||||
template <
|
std::move(
|
||||||
typename F,
|
MergeWrite<Register, (_combined_mask | F::mask)>
|
||||||
base_type value,
|
::make(_accumulated_value)
|
||||||
typename T = MergeWrite
|
);
|
||||||
>
|
|
||||||
typename std::enable_if<
|
|
||||||
(internals::check_overflow<
|
|
||||||
Register::size, value, (F::mask >> F::offset)
|
|
||||||
>::result::value),
|
|
||||||
T
|
|
||||||
>::type&&
|
|
||||||
with() && noexcept {
|
|
||||||
return std::move(*this).template with<F>(value);
|
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
static_assert(!Register::shadow::use_shadow,
|
static_assert(!Register::shadow::use_shadow,
|
||||||
"merge write is not available for shadow value register");
|
"merge write is not available for shadow value register");
|
||||||
MergeWrite() : _accumulated_value(0u),
|
constexpr MergeWrite() : _accumulated_value(0u) {};
|
||||||
_combined_mask(0u) {};
|
constexpr MergeWrite(const base_type v) : _accumulated_value(v) {};
|
||||||
base_type _accumulated_value;
|
base_type _accumulated_value;
|
||||||
base_type _combined_mask;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -232,16 +358,21 @@ namespace cppreg {
|
|||||||
return reinterpret_cast<const MMIO_t* const>(base_address);
|
return reinterpret_cast<const MMIO_t* const>(base_address);
|
||||||
};
|
};
|
||||||
template <typename F>
|
template <typename F>
|
||||||
inline static MergeWrite<typename F::parent_register>
|
inline static MergeWrite<typename F::parent_register, F::mask>
|
||||||
merge_write(const typename F::type value) noexcept {
|
merge_write(const typename F::type value) noexcept {
|
||||||
return
|
return
|
||||||
MergeWrite<typename F::parent_register>
|
MergeWrite<typename F::parent_register, F::mask>
|
||||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
::make(((value << F::offset) & F::mask));
|
||||||
};
|
};
|
||||||
template <
|
template <
|
||||||
typename F,
|
typename F,
|
||||||
type value,
|
type value,
|
||||||
typename T = MergeWrite<typename F::parent_register>
|
typename T = MergeWrite_tmpl<
|
||||||
|
typename F::parent_register,
|
||||||
|
F::mask,
|
||||||
|
F::offset,
|
||||||
|
value
|
||||||
|
>
|
||||||
>
|
>
|
||||||
inline static
|
inline static
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
@ -249,11 +380,9 @@ namespace cppreg {
|
|||||||
size, value, (F::mask >> F::offset)
|
size, value, (F::mask >> F::offset)
|
||||||
>::result::value,
|
>::result::value,
|
||||||
T
|
T
|
||||||
>::type
|
>::type&&
|
||||||
merge_write() noexcept {
|
merge_write() noexcept {
|
||||||
return
|
return std::move(T::make());
|
||||||
MergeWrite<typename F::parent_register>
|
|
||||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
|
||||||
};
|
};
|
||||||
static_assert(RegWidth != 0u,
|
static_assert(RegWidth != 0u,
|
||||||
"defining a Register type of width 0u is not allowed");
|
"defining a Register type of width 0u is not allowed");
|
||||||
@ -275,11 +404,13 @@ namespace cppreg {
|
|||||||
using parent_register = BaseRegister;
|
using parent_register = BaseRegister;
|
||||||
using type = typename parent_register::type;
|
using type = typename parent_register::type;
|
||||||
using MMIO_t = typename parent_register::MMIO_t;
|
using MMIO_t = typename parent_register::MMIO_t;
|
||||||
|
using policy = AccessPolicy;
|
||||||
constexpr static const Width_t width = FieldWidth;
|
constexpr static const Width_t width = FieldWidth;
|
||||||
constexpr static const Offset_t offset = FieldOffset;
|
constexpr static const Offset_t offset = FieldOffset;
|
||||||
using policy = AccessPolicy;
|
|
||||||
constexpr static const type mask = make_shifted_mask<type>(width,
|
constexpr static const type mask = make_shifted_mask<type>(width,
|
||||||
offset);
|
offset);
|
||||||
|
constexpr static const bool has_shadow =
|
||||||
|
parent_register::shadow::use_shadow;
|
||||||
template <type value>
|
template <type value>
|
||||||
struct check_overflow {
|
struct check_overflow {
|
||||||
constexpr static const bool result =
|
constexpr static const bool result =
|
||||||
@ -290,49 +421,47 @@ namespace cppreg {
|
|||||||
>::result::value;
|
>::result::value;
|
||||||
};
|
};
|
||||||
inline static type read() noexcept {
|
inline static type read() noexcept {
|
||||||
return
|
return policy::template read<MMIO_t, type, mask, offset>(
|
||||||
AccessPolicy::read(parent_register::ro_mem_pointer(),
|
parent_register::ro_mem_pointer()
|
||||||
mask,
|
);
|
||||||
offset);
|
|
||||||
};
|
};
|
||||||
template <typename T = type>
|
template <typename T = type>
|
||||||
inline static void
|
inline static void
|
||||||
write(const typename std::enable_if<
|
write(const typename std::enable_if<!has_shadow, T>::type value)
|
||||||
!parent_register::shadow::use_shadow, T
|
noexcept {
|
||||||
>::type value) noexcept {
|
policy::template write<MMIO_t, type, mask, offset>(
|
||||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
parent_register::rw_mem_pointer(),
|
||||||
mask,
|
value
|
||||||
offset,
|
);
|
||||||
value);
|
|
||||||
};
|
};
|
||||||
template <typename T = type>
|
template <typename T = type>
|
||||||
inline static void
|
inline static void
|
||||||
write(const typename std::enable_if<
|
write(const typename std::enable_if<has_shadow, T>::type value)
|
||||||
parent_register::shadow::use_shadow, T
|
noexcept {
|
||||||
>::type value) noexcept {
|
RegisterWrite<type, type, mask, offset>
|
||||||
parent_register::shadow::value =
|
::write(&parent_register::shadow::value, value);
|
||||||
(parent_register::shadow::value & ~mask) |
|
policy::template write<MMIO_t, type, type_mask<type>::value, 0u>(
|
||||||
((value << offset) & mask);
|
parent_register::rw_mem_pointer(),
|
||||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
parent_register::shadow::value
|
||||||
~(0u),
|
);
|
||||||
0u,
|
|
||||||
parent_register::shadow::value);
|
|
||||||
};
|
};
|
||||||
template <type value, typename T = void>
|
template <type value, typename T = void>
|
||||||
inline static
|
inline static
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
(!parent_register::shadow::use_shadow)
|
!has_shadow
|
||||||
&&
|
&&
|
||||||
check_overflow<value>::result,
|
check_overflow<value>::result,
|
||||||
T
|
T
|
||||||
>::type
|
>::type
|
||||||
write() noexcept {
|
write() noexcept {
|
||||||
write(value);
|
policy::template write<MMIO_t, type, mask, offset, value>(
|
||||||
|
parent_register::rw_mem_pointer()
|
||||||
|
);
|
||||||
};
|
};
|
||||||
template <type value, typename T = void>
|
template <type value, typename T = void>
|
||||||
inline static
|
inline static
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
parent_register::shadow::use_shadow
|
has_shadow
|
||||||
&&
|
&&
|
||||||
check_overflow<value>::result,
|
check_overflow<value>::result,
|
||||||
T
|
T
|
||||||
@ -341,22 +470,21 @@ namespace cppreg {
|
|||||||
write(value);
|
write(value);
|
||||||
};
|
};
|
||||||
inline static void set() noexcept {
|
inline static void set() noexcept {
|
||||||
AccessPolicy::set(parent_register::rw_mem_pointer(), mask);
|
policy::template
|
||||||
|
set<MMIO_t, type, mask>(parent_register::rw_mem_pointer());
|
||||||
};
|
};
|
||||||
inline static void clear() noexcept {
|
inline static void clear() noexcept {
|
||||||
AccessPolicy::clear(parent_register::rw_mem_pointer(), mask);
|
policy::template
|
||||||
|
clear<MMIO_t, type, mask>(parent_register::rw_mem_pointer());
|
||||||
};
|
};
|
||||||
inline static void toggle() noexcept {
|
inline static void toggle() noexcept {
|
||||||
AccessPolicy::toggle(parent_register::rw_mem_pointer(), mask);
|
policy::template
|
||||||
|
toggle<MMIO_t, type, mask>(parent_register::rw_mem_pointer());
|
||||||
};
|
};
|
||||||
template <typename T = bool>
|
inline static bool is_set() noexcept {
|
||||||
inline static typename std::enable_if<FieldWidth == 1, T>::type
|
return (Field::read() == (mask >> offset));
|
||||||
is_set() noexcept {
|
|
||||||
return (Field::read() == 1u);
|
|
||||||
};
|
};
|
||||||
template <typename T = bool>
|
inline static bool is_clear() noexcept {
|
||||||
inline static typename std::enable_if<FieldWidth == 1, T>::type
|
|
||||||
is_clear() noexcept {
|
|
||||||
return (Field::read() == 0u);
|
return (Field::read() == 0u);
|
||||||
};
|
};
|
||||||
static_assert(parent_register::size >= width,
|
static_assert(parent_register::size >= width,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user