1
0
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:
Nicolas Clauvelin 2018-03-09 10:32:40 -05:00 committed by Nicolas Clauvelin
parent 1c27f907a5
commit eb9226fe5d
8 changed files with 732 additions and 303 deletions

View File

@ -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();
};
}

View File

@ -11,6 +11,7 @@
#include <cstdint>
#include <limits>
#endif // CPPREG_CPPREG_INCLUDES_H

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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;
};

View File

@ -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.

View File

@ -49,6 +49,7 @@ namespace cppreg {
template <typename Register>
constexpr const bool Shadow<Register, true>::use_shadow;
}

View File

@ -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,