mirror of
https://github.com/sendyne/cppreg.git
synced 2025-05-09 15:14:05 +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;
|
||||
|
||||
|
||||
//! 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 <limits>
|
||||
|
||||
|
||||
#endif // CPPREG_CPPREG_INCLUDES_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Access policy abstract implementation.
|
||||
//! Access policy implementation.
|
||||
/**
|
||||
* @file AccessPolicy.h
|
||||
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
|
||||
@ -6,6 +6,17 @@
|
||||
*
|
||||
* Access policies are used to describe if register fields are read-write,
|
||||
* 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 {
|
||||
|
||||
|
||||
//! 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.
|
||||
struct read_only {
|
||||
|
||||
@ -27,16 +199,14 @@ namespace cppreg {
|
||||
/**
|
||||
* @tparam MMIO_t Register memory device type.
|
||||
* @tparam T Field data type.
|
||||
* @tparam mask Field mask.
|
||||
* @tparam offset Field offset.
|
||||
* @param mmio_device Pointer to register mapped memory.
|
||||
* @param mask Field mask.
|
||||
* @param offset Field offset.
|
||||
* @return The value at the field location.
|
||||
*/
|
||||
template <typename MMIO_t, typename T>
|
||||
inline static T read(const MMIO_t* const mmio_device,
|
||||
const T mask,
|
||||
const Offset_t offset) noexcept {
|
||||
return static_cast<T>((*mmio_device & mask) >> offset);
|
||||
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);
|
||||
};
|
||||
|
||||
};
|
||||
@ -49,54 +219,68 @@ namespace cppreg {
|
||||
/**
|
||||
* @tparam MMIO_t Register memory device type.
|
||||
* @tparam T Field data type.
|
||||
* @tparam mask Field mask.
|
||||
* @tparam offset Field offset.
|
||||
* @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.
|
||||
*/
|
||||
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,
|
||||
const T mask,
|
||||
const Offset_t offset,
|
||||
const T value) noexcept {
|
||||
*mmio_device = static_cast<T>((*mmio_device & ~mask) |
|
||||
((value << offset) & mask));
|
||||
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
|
||||
};
|
||||
|
||||
//! 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.
|
||||
/**
|
||||
* @tparam T Field data type.
|
||||
* @tparam mask Field mask.
|
||||
* @param mmio_device Pointer to register mapped memory.
|
||||
* @param mask Field mask.
|
||||
*/
|
||||
template <typename MMIO_t, typename T>
|
||||
inline static void set(MMIO_t* const mmio_device, const T mask)
|
||||
template <typename MMIO_t, typename T, T mask>
|
||||
inline static void set(MMIO_t* const mmio_device)
|
||||
noexcept {
|
||||
*mmio_device = static_cast<T>((*mmio_device) | mask);
|
||||
RegisterWriteConstant<MMIO_t, T, mask, 0u, mask>
|
||||
::write(mmio_device);
|
||||
};
|
||||
|
||||
//! Clear field implementation.
|
||||
/**
|
||||
* @tparam MMIO_t Register memory device type.
|
||||
* @tparam T Field data type.
|
||||
* @tparam mask Field mask.
|
||||
* @param mmio_device Pointer to register mapped memory.
|
||||
* @param mask Field mask.
|
||||
*/
|
||||
template <typename MMIO_t, typename T>
|
||||
inline static void clear(MMIO_t* const mmio_device, const T mask)
|
||||
template <typename MMIO_t, typename T, T mask>
|
||||
inline static void clear(MMIO_t* const mmio_device)
|
||||
noexcept {
|
||||
*mmio_device = static_cast<T>((*mmio_device) & ~mask);
|
||||
RegisterWriteConstant<MMIO_t, T, mask, 0u, ~mask>
|
||||
::write(mmio_device);
|
||||
};
|
||||
|
||||
//! Toggle field implementation.
|
||||
/**
|
||||
* @tparam MMIO_t Register memory device type.
|
||||
* @tparam T Field data type.
|
||||
* @tparam mask Field mask.
|
||||
* @param mmio_device Pointer to register mapped memory.
|
||||
* @param mask Field mask.
|
||||
*/
|
||||
template <typename MMIO_t, typename T>
|
||||
inline static void toggle(MMIO_t* const mmio_device, const T mask)
|
||||
template <typename MMIO_t, typename T, T mask>
|
||||
inline static void toggle(MMIO_t* const mmio_device)
|
||||
noexcept {
|
||||
*mmio_device = static_cast<T>((*mmio_device) ^ mask);
|
||||
};
|
||||
@ -111,19 +295,38 @@ namespace cppreg {
|
||||
/**
|
||||
* @tparam MMIO_t Register memory device type.
|
||||
* @tparam T Field data type.
|
||||
* @tparam mask Field mask.
|
||||
* @tparam offset Field offset
|
||||
* @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.
|
||||
*/
|
||||
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,
|
||||
const T mask,
|
||||
const Offset_t offset,
|
||||
const T value) noexcept {
|
||||
|
||||
// We cannot read the current value so we simply fully write to it.
|
||||
*mmio_device = ((value << offset) & mask);
|
||||
// For write-only fields we can only write to the whole register.
|
||||
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
|
||||
* implementation.
|
||||
* Strictly speaking a field is defined as a region of a register.
|
||||
*/
|
||||
|
||||
|
||||
@ -27,7 +28,7 @@ namespace cppreg {
|
||||
* @tparam BaseRegister Parent register.
|
||||
* @tparam width Field width.
|
||||
* @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
|
||||
* (read, write, set, clear, and toggle). These methods availability depends
|
||||
@ -53,26 +54,29 @@ namespace cppreg {
|
||||
//! MMIO type.
|
||||
using MMIO_t = typename parent_register::MMIO_t;
|
||||
|
||||
//! Field policy.
|
||||
using policy = AccessPolicy;
|
||||
|
||||
//! Field width.
|
||||
constexpr static const Width_t width = FieldWidth;
|
||||
|
||||
//! Field offset.
|
||||
constexpr static const Offset_t offset = FieldOffset;
|
||||
|
||||
//! Field policy.
|
||||
using policy = AccessPolicy;
|
||||
|
||||
//! Field mask.
|
||||
/**
|
||||
* The field mask is computed at compile time.
|
||||
*/
|
||||
constexpr static const type mask = make_shifted_mask<type>(width,
|
||||
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.
|
||||
/**
|
||||
* @tparam value Value to be checked for overflow.
|
||||
* @return `true` if no overflow, `false` otherwise.
|
||||
*
|
||||
* This is only used for the template form of the write method.
|
||||
*/
|
||||
template <type value>
|
||||
struct check_overflow {
|
||||
@ -84,125 +88,104 @@ namespace cppreg {
|
||||
>::result::value;
|
||||
};
|
||||
|
||||
//! Field read method.
|
||||
//!@ Field read method.
|
||||
/**
|
||||
* @return Field value.
|
||||
*/
|
||||
inline static type read() noexcept {
|
||||
return
|
||||
AccessPolicy::read(parent_register::ro_mem_pointer(),
|
||||
mask,
|
||||
offset);
|
||||
return policy::template read<MMIO_t, type, mask, offset>(
|
||||
parent_register::ro_mem_pointer()
|
||||
);
|
||||
};
|
||||
|
||||
//! Field write method (shadow value disabled).
|
||||
//! Field write method (no shadow value).
|
||||
/**
|
||||
* @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>
|
||||
inline static void
|
||||
write(const typename std::enable_if<
|
||||
!parent_register::shadow::use_shadow, T
|
||||
>::type value) noexcept {
|
||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
||||
mask,
|
||||
offset,
|
||||
value);
|
||||
write(const typename std::enable_if<!has_shadow, T>::type value)
|
||||
noexcept {
|
||||
policy::template write<MMIO_t, type, mask, offset>(
|
||||
parent_register::rw_mem_pointer(),
|
||||
value
|
||||
);
|
||||
};
|
||||
|
||||
//! Field write method (shadow value enabled).
|
||||
//! Field write method (w/ shadow value).
|
||||
/**
|
||||
* @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>
|
||||
inline static void
|
||||
write(const typename std::enable_if<
|
||||
parent_register::shadow::use_shadow, T
|
||||
>::type value) noexcept {
|
||||
write(const typename std::enable_if<has_shadow, T>::type value)
|
||||
noexcept {
|
||||
|
||||
// Update shadow value.
|
||||
// Fetch the whole register content.
|
||||
// This assumes that reading a write-only fields return some value.
|
||||
parent_register::shadow::value =
|
||||
(parent_register::shadow::value & ~mask) |
|
||||
((value << offset) & mask);
|
||||
RegisterWrite<type, type, mask, offset>
|
||||
::write(&parent_register::shadow::value, value);
|
||||
|
||||
// Write as a block to the register, that is, we do not use the
|
||||
// mask and offset.
|
||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
||||
~(0u),
|
||||
0u,
|
||||
parent_register::shadow::value);
|
||||
policy::template write<MMIO_t, type, type_mask<type>::value, 0u>(
|
||||
parent_register::rw_mem_pointer(),
|
||||
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
|
||||
*
|
||||
* We use SFINAE to discriminate for registers with shadow value
|
||||
* enabled.
|
||||
*
|
||||
* 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>
|
||||
inline static
|
||||
typename std::enable_if<
|
||||
(!parent_register::shadow::use_shadow)
|
||||
!has_shadow
|
||||
&&
|
||||
check_overflow<value>::result,
|
||||
T
|
||||
>::type
|
||||
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
|
||||
*
|
||||
* We use SFINAE to discriminate for registers with shadow value
|
||||
* enabled.
|
||||
*
|
||||
* 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>
|
||||
inline static
|
||||
typename std::enable_if<
|
||||
parent_register::shadow::use_shadow
|
||||
has_shadow
|
||||
&&
|
||||
check_overflow<value>::result,
|
||||
T
|
||||
>::type
|
||||
write() noexcept {
|
||||
write(value);
|
||||
};
|
||||
|
||||
// For this particular we can simply forward to the non-constant
|
||||
// implementation.
|
||||
write(value);
|
||||
|
||||
};
|
||||
|
||||
//! Field set method.
|
||||
/**
|
||||
* This method will set all bits in the field.
|
||||
*/
|
||||
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.
|
||||
@ -210,7 +193,8 @@ namespace cppreg {
|
||||
* This method will clear all bits in the field.
|
||||
*/
|
||||
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.
|
||||
@ -218,30 +202,23 @@ namespace cppreg {
|
||||
* This method will toggle all bits in the field.
|
||||
*/
|
||||
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.
|
||||
/**
|
||||
* @return `true` if the 1-bit field is set to 1, `false` otherwise.
|
||||
*
|
||||
* This is only available if the field is 1 bit wide.
|
||||
* @return `true` if all the bits are set to 1, `false` otherwise.
|
||||
*/
|
||||
template <typename T = bool>
|
||||
inline static typename std::enable_if<FieldWidth == 1, T>::type
|
||||
is_set() noexcept {
|
||||
return (Field::read() == 1u);
|
||||
inline static bool is_set() noexcept {
|
||||
return (Field::read() == (mask >> offset));
|
||||
};
|
||||
|
||||
//! Is field clear bool method.
|
||||
/**
|
||||
* @return `true` if the 1-bit field is set to 0, `false` otherwise.
|
||||
*
|
||||
* This is only available if the field is 1 bit wide.
|
||||
* @return `true` if all the bits are set to 0, `false` otherwise.
|
||||
*/
|
||||
template <typename T = bool>
|
||||
inline static typename std::enable_if<FieldWidth == 1, T>::type
|
||||
is_clear() noexcept {
|
||||
inline static bool is_clear() noexcept {
|
||||
return (Field::read() == 0u);
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,11 @@
|
||||
*
|
||||
* The "merge write" implementation is designed to make it possible to merge
|
||||
* 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 {
|
||||
|
||||
|
||||
//! 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>
|
||||
class MergeWrite {
|
||||
template <
|
||||
typename Register,
|
||||
typename Register::type mask,
|
||||
Offset_t offset,
|
||||
typename Register::type value
|
||||
> class MergeWrite_tmpl {
|
||||
|
||||
|
||||
public:
|
||||
@ -35,31 +56,42 @@ namespace cppreg {
|
||||
//! Type alias to register base 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.
|
||||
MergeWrite(MergeWrite&& mw) noexcept
|
||||
: _accumulated_value(mw._accumulated_value),
|
||||
_combined_mask(mw._combined_mask) {
|
||||
};
|
||||
private:
|
||||
|
||||
//!@{ Non-copyable.
|
||||
MergeWrite(const MergeWrite&) = delete;
|
||||
MergeWrite& operator=(const MergeWrite&) = delete;
|
||||
// Disabled for shadow value register.
|
||||
static_assert(!Register::shadow::use_shadow,
|
||||
"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.
|
||||
typename Register::MMIO_t* const mmio_device =
|
||||
@ -68,10 +100,111 @@ namespace cppreg {
|
||||
// Write to the whole register using the current accumulated value
|
||||
// and combined mask.
|
||||
// No offset needed because we write to the whole register.
|
||||
*mmio_device = static_cast<base_type>(
|
||||
(*mmio_device & ~_combined_mask) |
|
||||
((_accumulated_value) & _combined_mask)
|
||||
);
|
||||
RegisterWriteConstant<
|
||||
typename Register::MMIO_t,
|
||||
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.
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
static_assert(std::is_same<
|
||||
@ -95,43 +226,19 @@ namespace cppreg {
|
||||
"field is not from the same register in merge_write");
|
||||
|
||||
// Update accumulated value.
|
||||
F::policy::write(&_accumulated_value,
|
||||
F::mask,
|
||||
F::offset,
|
||||
value);
|
||||
F::policy::template write<
|
||||
base_type,
|
||||
base_type,
|
||||
F::mask,
|
||||
F::offset
|
||||
>(&_accumulated_value, value);
|
||||
|
||||
// Update combine mask.
|
||||
_combined_mask = _combined_mask | F::mask;
|
||||
return
|
||||
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");
|
||||
|
||||
// Private default constructor.
|
||||
MergeWrite() : _accumulated_value(0u),
|
||||
_combined_mask(0u) {};
|
||||
constexpr MergeWrite() : _accumulated_value(0u) {};
|
||||
constexpr MergeWrite(const base_type v) : _accumulated_value(v) {};
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
//! Merge write function.
|
||||
//! Merge write start function.
|
||||
/**
|
||||
* @tparam F Field on which to perform the write operation.
|
||||
* @param value Value to write to the field.
|
||||
* @return A merge write data structure.
|
||||
* @tparam F Field on which to perform the first write operation.
|
||||
* @param value Value to be written to the field.
|
||||
* @return A merge write data structure to chain further writes.
|
||||
*/
|
||||
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 {
|
||||
return
|
||||
MergeWrite<typename F::parent_register>
|
||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
||||
MergeWrite<typename F::parent_register, 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.
|
||||
* @param value Value to write to the field.
|
||||
* @return A merge write data structure.
|
||||
* @tparam F Field on which to perform the first write operation.
|
||||
* @tparam value Value to be written to the field.
|
||||
* @return A merge write data structure to chain further writes.
|
||||
*/
|
||||
template <
|
||||
typename F,
|
||||
type value,
|
||||
typename T = MergeWrite<typename F::parent_register>
|
||||
typename T = MergeWrite_tmpl<
|
||||
typename F::parent_register,
|
||||
F::mask,
|
||||
F::offset,
|
||||
value
|
||||
>
|
||||
>
|
||||
inline static
|
||||
typename std::enable_if<
|
||||
@ -107,11 +112,9 @@ namespace cppreg {
|
||||
size, value, (F::mask >> F::offset)
|
||||
>::result::value,
|
||||
T
|
||||
>::type
|
||||
>::type&&
|
||||
merge_write() noexcept {
|
||||
return
|
||||
MergeWrite<typename F::parent_register>
|
||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
||||
return std::move(T::make());
|
||||
};
|
||||
|
||||
// Sanity check.
|
||||
|
@ -49,6 +49,7 @@ namespace cppreg {
|
||||
template <typename Register>
|
||||
constexpr const bool Shadow<Register, true>::use_shadow;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
// cppreg_Defines.h
|
||||
#ifndef CPPREG_CPPREG_DEFINES_H
|
||||
@ -16,6 +17,10 @@ namespace cppreg {
|
||||
using Address_t = std::uintptr_t;
|
||||
using Width_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
|
||||
|
||||
@ -23,46 +28,118 @@ namespace cppreg {
|
||||
#ifndef CPPREG_ACCESSPOLICY_H
|
||||
#define CPPREG_ACCESSPOLICY_H
|
||||
namespace cppreg {
|
||||
struct read_only {
|
||||
template <typename MMIO_t, typename T>
|
||||
inline static T read(const MMIO_t* const mmio_device,
|
||||
const T mask,
|
||||
const Offset_t offset) noexcept {
|
||||
template <typename MMIO_t, typename T, T mask, Offset_t offset>
|
||||
struct RegisterRead {
|
||||
constexpr static const bool is_trivial =
|
||||
(mask == type_mask<T>::value) && (offset == 0u);
|
||||
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);
|
||||
};
|
||||
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 {
|
||||
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,
|
||||
const T mask,
|
||||
const Offset_t offset,
|
||||
const T value) noexcept {
|
||||
*mmio_device = static_cast<T>((*mmio_device & ~mask) |
|
||||
((value << offset) & mask));
|
||||
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
|
||||
};
|
||||
template <typename MMIO_t, typename T>
|
||||
inline static void set(MMIO_t* const mmio_device, const T 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, mask, offset, value>
|
||||
::write(mmio_device);
|
||||
};
|
||||
template <typename MMIO_t, typename T, T mask>
|
||||
inline static void set(MMIO_t* const mmio_device)
|
||||
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>
|
||||
inline static void clear(MMIO_t* const mmio_device, const T mask)
|
||||
template <typename MMIO_t, typename T, T mask>
|
||||
inline static void clear(MMIO_t* const mmio_device)
|
||||
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>
|
||||
inline static void toggle(MMIO_t* const mmio_device, const T mask)
|
||||
template <typename MMIO_t, typename T, T mask>
|
||||
inline static void toggle(MMIO_t* const mmio_device)
|
||||
noexcept {
|
||||
*mmio_device = static_cast<T>((*mmio_device) ^ mask);
|
||||
};
|
||||
};
|
||||
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,
|
||||
const T mask,
|
||||
const Offset_t offset,
|
||||
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
|
||||
#define CPPREG_MERGEWRITE_H
|
||||
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 {
|
||||
public:
|
||||
using base_type = typename Register::type;
|
||||
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;
|
||||
private:
|
||||
constexpr static const base_type _combined_mask = mask;
|
||||
public:
|
||||
constexpr static MergeWrite make(const base_type value) noexcept {
|
||||
return MergeWrite(value);
|
||||
};
|
||||
MergeWrite(MergeWrite&& mw) noexcept
|
||||
: _accumulated_value(mw._accumulated_value),
|
||||
_combined_mask(mw._combined_mask) {
|
||||
};
|
||||
: _accumulated_value(mw._accumulated_value) {};
|
||||
MergeWrite(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 =
|
||||
Register::rw_mem_pointer();
|
||||
*mmio_device = static_cast<base_type>(
|
||||
(*mmio_device & ~_combined_mask) |
|
||||
((_accumulated_value) & _combined_mask)
|
||||
);
|
||||
RegisterWrite<
|
||||
typename Register::MMIO_t,
|
||||
base_type,
|
||||
_combined_mask,
|
||||
0u
|
||||
>::write(mmio_device, _accumulated_value);
|
||||
};
|
||||
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<
|
||||
typename F::parent_register,
|
||||
Register
|
||||
>::value,
|
||||
"field is not from the same register in merge_write");
|
||||
F::policy::write(&_accumulated_value,
|
||||
F::mask,
|
||||
F::offset,
|
||||
value);
|
||||
_combined_mask = _combined_mask | F::mask;
|
||||
return std::move(*this);
|
||||
};
|
||||
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);
|
||||
F::policy::template write<
|
||||
base_type,
|
||||
base_type,
|
||||
F::mask,
|
||||
F::offset
|
||||
>(&_accumulated_value, value);
|
||||
return
|
||||
std::move(
|
||||
MergeWrite<Register, (_combined_mask | F::mask)>
|
||||
::make(_accumulated_value)
|
||||
);
|
||||
};
|
||||
private:
|
||||
static_assert(!Register::shadow::use_shadow,
|
||||
"merge write is not available for shadow value register");
|
||||
MergeWrite() : _accumulated_value(0u),
|
||||
_combined_mask(0u) {};
|
||||
constexpr MergeWrite() : _accumulated_value(0u) {};
|
||||
constexpr MergeWrite(const base_type v) : _accumulated_value(v) {};
|
||||
base_type _accumulated_value;
|
||||
base_type _combined_mask;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@ -232,16 +358,21 @@ namespace cppreg {
|
||||
return reinterpret_cast<const MMIO_t* const>(base_address);
|
||||
};
|
||||
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 {
|
||||
return
|
||||
MergeWrite<typename F::parent_register>
|
||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
||||
MergeWrite<typename F::parent_register, F::mask>
|
||||
::make(((value << F::offset) & F::mask));
|
||||
};
|
||||
template <
|
||||
typename F,
|
||||
type value,
|
||||
typename T = MergeWrite<typename F::parent_register>
|
||||
typename T = MergeWrite_tmpl<
|
||||
typename F::parent_register,
|
||||
F::mask,
|
||||
F::offset,
|
||||
value
|
||||
>
|
||||
>
|
||||
inline static
|
||||
typename std::enable_if<
|
||||
@ -249,11 +380,9 @@ namespace cppreg {
|
||||
size, value, (F::mask >> F::offset)
|
||||
>::result::value,
|
||||
T
|
||||
>::type
|
||||
>::type&&
|
||||
merge_write() noexcept {
|
||||
return
|
||||
MergeWrite<typename F::parent_register>
|
||||
::create_instance(((value << F::offset) & F::mask), F::mask);
|
||||
return std::move(T::make());
|
||||
};
|
||||
static_assert(RegWidth != 0u,
|
||||
"defining a Register type of width 0u is not allowed");
|
||||
@ -275,11 +404,13 @@ namespace cppreg {
|
||||
using parent_register = BaseRegister;
|
||||
using type = typename parent_register::type;
|
||||
using MMIO_t = typename parent_register::MMIO_t;
|
||||
using policy = AccessPolicy;
|
||||
constexpr static const Width_t width = FieldWidth;
|
||||
constexpr static const Offset_t offset = FieldOffset;
|
||||
using policy = AccessPolicy;
|
||||
constexpr static const type mask = make_shifted_mask<type>(width,
|
||||
offset);
|
||||
constexpr static const bool has_shadow =
|
||||
parent_register::shadow::use_shadow;
|
||||
template <type value>
|
||||
struct check_overflow {
|
||||
constexpr static const bool result =
|
||||
@ -290,49 +421,47 @@ namespace cppreg {
|
||||
>::result::value;
|
||||
};
|
||||
inline static type read() noexcept {
|
||||
return
|
||||
AccessPolicy::read(parent_register::ro_mem_pointer(),
|
||||
mask,
|
||||
offset);
|
||||
return policy::template read<MMIO_t, type, mask, offset>(
|
||||
parent_register::ro_mem_pointer()
|
||||
);
|
||||
};
|
||||
template <typename T = type>
|
||||
inline static void
|
||||
write(const typename std::enable_if<
|
||||
!parent_register::shadow::use_shadow, T
|
||||
>::type value) noexcept {
|
||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
||||
mask,
|
||||
offset,
|
||||
value);
|
||||
write(const typename std::enable_if<!has_shadow, T>::type value)
|
||||
noexcept {
|
||||
policy::template write<MMIO_t, type, mask, offset>(
|
||||
parent_register::rw_mem_pointer(),
|
||||
value
|
||||
);
|
||||
};
|
||||
template <typename T = type>
|
||||
inline static void
|
||||
write(const typename std::enable_if<
|
||||
parent_register::shadow::use_shadow, T
|
||||
>::type value) noexcept {
|
||||
parent_register::shadow::value =
|
||||
(parent_register::shadow::value & ~mask) |
|
||||
((value << offset) & mask);
|
||||
AccessPolicy::write(parent_register::rw_mem_pointer(),
|
||||
~(0u),
|
||||
0u,
|
||||
parent_register::shadow::value);
|
||||
write(const typename std::enable_if<has_shadow, T>::type value)
|
||||
noexcept {
|
||||
RegisterWrite<type, type, mask, offset>
|
||||
::write(&parent_register::shadow::value, value);
|
||||
policy::template write<MMIO_t, type, type_mask<type>::value, 0u>(
|
||||
parent_register::rw_mem_pointer(),
|
||||
parent_register::shadow::value
|
||||
);
|
||||
};
|
||||
template <type value, typename T = void>
|
||||
inline static
|
||||
typename std::enable_if<
|
||||
(!parent_register::shadow::use_shadow)
|
||||
!has_shadow
|
||||
&&
|
||||
check_overflow<value>::result,
|
||||
T
|
||||
>::type
|
||||
write() noexcept {
|
||||
write(value);
|
||||
policy::template write<MMIO_t, type, mask, offset, value>(
|
||||
parent_register::rw_mem_pointer()
|
||||
);
|
||||
};
|
||||
template <type value, typename T = void>
|
||||
inline static
|
||||
typename std::enable_if<
|
||||
parent_register::shadow::use_shadow
|
||||
has_shadow
|
||||
&&
|
||||
check_overflow<value>::result,
|
||||
T
|
||||
@ -341,22 +470,21 @@ namespace cppreg {
|
||||
write(value);
|
||||
};
|
||||
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 {
|
||||
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 {
|
||||
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 typename std::enable_if<FieldWidth == 1, T>::type
|
||||
is_set() noexcept {
|
||||
return (Field::read() == 1u);
|
||||
inline static bool is_set() noexcept {
|
||||
return (Field::read() == (mask >> offset));
|
||||
};
|
||||
template <typename T = bool>
|
||||
inline static typename std::enable_if<FieldWidth == 1, T>::type
|
||||
is_clear() noexcept {
|
||||
inline static bool is_clear() noexcept {
|
||||
return (Field::read() == 0u);
|
||||
};
|
||||
static_assert(parent_register::size >= width,
|
||||
|
Loading…
x
Reference in New Issue
Block a user