From eb9226fe5daab6bc729538d6ff9c10a1826d2665 Mon Sep 17 00:00:00 2001 From: Nicolas Clauvelin Date: Fri, 9 Mar 2018 10:32:40 -0500 Subject: [PATCH] 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. --- cppreg_Defines.h | 12 ++ cppreg_Includes.h | 1 + policies/AccessPolicy.h | 269 ++++++++++++++++++++++++++++---- register/Field.h | 135 +++++++--------- register/MergeWrite.h | 246 ++++++++++++++++++++--------- register/Register.h | 35 +++-- register/ShadowValue.h | 1 + single/cppreg-all.h | 336 +++++++++++++++++++++++++++------------- 8 files changed, 732 insertions(+), 303 deletions(-) diff --git a/cppreg_Defines.h b/cppreg_Defines.h index 557efb8..f71d301 100644 --- a/cppreg_Defines.h +++ b/cppreg_Defines.h @@ -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 + struct type_mask { + constexpr static const T value = std::numeric_limits::max(); + }; + + } diff --git a/cppreg_Includes.h b/cppreg_Includes.h index 3c3fbb5..24e644a 100644 --- a/cppreg_Includes.h +++ b/cppreg_Includes.h @@ -11,6 +11,7 @@ #include +#include #endif // CPPREG_CPPREG_INCLUDES_H diff --git a/policies/AccessPolicy.h b/policies/AccessPolicy.h index b3c20cd..a5b011a 100644 --- a/policies/AccessPolicy.h +++ b/policies/AccessPolicy.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 + 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::value) && (offset == 0u); + + //! Non-trivial read implementation. + /** + * @param mmio_device Pointer to the register memory device. + * @return The content of the register field. + */ + template + inline static T read( + const MMIO_t* const mmio_device, + typename std::enable_if::type = nullptr + ) noexcept { + return static_cast((*mmio_device & mask) >> offset); + }; + + //! Trivial read implementation. + /** + * @param mmio_device Pointer to the register memory device. + * @return The content of the register field. + */ + template + inline static T read( + const MMIO_t* const mmio_device, + typename std::enable_if::type = nullptr + ) noexcept { + return static_cast(*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 + 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::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 + inline static void write( + MMIO_t* const mmio_device, + T value, + typename std::enable_if::type = nullptr + ) noexcept { + *mmio_device = static_cast( + (*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 + inline static void write( + MMIO_t* const mmio_device, + T value, + typename std::enable_if::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 + 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::value) && (offset == 0u); + + //! Non-trivial write implementation. + /** + * @param mmio_device Pointer to the register memory device. + */ + template + inline static void write( + MMIO_t* const mmio_device, + typename std::enable_if::type = nullptr + ) noexcept { + *mmio_device = static_cast( + (*mmio_device & ~mask) | ((value << offset) & mask) + ); + }; + + //! Trivial write implementation. + /** + * @param mmio_device Pointer to the register memory device. + */ + template + inline static void write( + MMIO_t* const mmio_device, + typename std::enable_if::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 - inline static T read(const MMIO_t* const mmio_device, - const T mask, - const Offset_t offset) noexcept { - return static_cast((*mmio_device & mask) >> offset); + template + inline static T read(const MMIO_t* const mmio_device) noexcept { + return RegisterRead::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 + template inline static void write(MMIO_t* const mmio_device, - const T mask, - const Offset_t offset, const T value) noexcept { - *mmio_device = static_cast((*mmio_device & ~mask) | - ((value << offset) & mask)); + RegisterWrite::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 + inline static void write(MMIO_t* const mmio_device) noexcept { + RegisterWriteConstant + ::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 - inline static void set(MMIO_t* const mmio_device, const T mask) + template + inline static void set(MMIO_t* const mmio_device) noexcept { - *mmio_device = static_cast((*mmio_device) | mask); + RegisterWriteConstant + ::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 - inline static void clear(MMIO_t* const mmio_device, const T mask) + template + inline static void clear(MMIO_t* const mmio_device) noexcept { - *mmio_device = static_cast((*mmio_device) & ~mask); + RegisterWriteConstant + ::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 - inline static void toggle(MMIO_t* const mmio_device, const T mask) + template + inline static void toggle(MMIO_t* const mmio_device) noexcept { *mmio_device = static_cast((*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 + template 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::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 + 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::value, 0u, ((value << offset) & mask) + > + ::write(mmio_device); }; diff --git a/register/Field.h b/register/Field.h index 86874c7..44082ce 100644 --- a/register/Field.h +++ b/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(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 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( + 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 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::type value) + noexcept { + policy::template write( + 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 inline static void - write(const typename std::enable_if< - parent_register::shadow::use_shadow, T - >::type value) noexcept { + write(const typename std::enable_if::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 + ::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::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 inline static typename std::enable_if< - (!parent_register::shadow::use_shadow) + !has_shadow && check_overflow::result, T >::type write() noexcept { - write(value); + policy::template write( + 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 inline static typename std::enable_if< - parent_register::shadow::use_shadow + has_shadow && check_overflow::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(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(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(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 - inline static typename std::enable_if::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 - inline static typename std::enable_if::type - is_clear() noexcept { + inline static bool is_clear() noexcept { return (Field::read() == 0u); }; diff --git a/register/MergeWrite.h b/register/MergeWrite.h index 0d1e8eb..64171ca 100644 --- a/register/MergeWrite.h +++ b/register/MergeWrite.h @@ -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 - 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( - (*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 - MergeWrite&& with(const base_type value) && noexcept { + inline MergeWrite 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 + ::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(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; - }; diff --git a/register/Register.h b/register/Register.h index f87bad5..216b7ca 100644 --- a/register/Register.h +++ b/register/Register.h @@ -76,30 +76,35 @@ namespace cppreg { return reinterpret_cast(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 - inline static MergeWrite + inline static MergeWrite merge_write(const typename F::type value) noexcept { return - MergeWrite - ::create_instance(((value << F::offset) & F::mask), F::mask); + MergeWrite + ::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 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 - ::create_instance(((value << F::offset) & F::mask), F::mask); + return std::move(T::make()); }; // Sanity check. diff --git a/register/ShadowValue.h b/register/ShadowValue.h index e4a82be..1328671 100644 --- a/register/ShadowValue.h +++ b/register/ShadowValue.h @@ -49,6 +49,7 @@ namespace cppreg { template constexpr const bool Shadow::use_shadow; + } diff --git a/single/cppreg-all.h b/single/cppreg-all.h index 445f0a5..4b23096 100644 --- a/single/cppreg-all.h +++ b/single/cppreg-all.h @@ -8,6 +8,7 @@ #include #include #include +#include // 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 + struct type_mask { + constexpr static const T value = std::numeric_limits::max(); + }; } #endif @@ -23,46 +28,118 @@ namespace cppreg { #ifndef CPPREG_ACCESSPOLICY_H #define CPPREG_ACCESSPOLICY_H namespace cppreg { - struct read_only { - template - inline static T read(const MMIO_t* const mmio_device, - const T mask, - const Offset_t offset) noexcept { + template + struct RegisterRead { + constexpr static const bool is_trivial = + (mask == type_mask::value) && (offset == 0u); + template + inline static T read( + const MMIO_t* const mmio_device, + typename std::enable_if::type = nullptr + ) noexcept { return static_cast((*mmio_device & mask) >> offset); }; + template + inline static T read( + const MMIO_t* const mmio_device, + typename std::enable_if::type = nullptr + ) noexcept { + return static_cast(*mmio_device); + }; + }; + template + struct RegisterWrite { + constexpr static const bool is_trivial = + (mask == type_mask::value) && (offset == 0u); + template + inline static void write( + MMIO_t* const mmio_device, + T value, + typename std::enable_if::type = nullptr + ) noexcept { + *mmio_device = static_cast( + (*mmio_device & ~mask) | ((value << offset) & mask) + ); + }; + template + inline static void write( + MMIO_t* const mmio_device, + T value, + typename std::enable_if::type = nullptr + ) noexcept { + *mmio_device = value; + }; + }; + template + struct RegisterWriteConstant { + constexpr static const bool is_trivial = + (mask == type_mask::value) && (offset == 0u); + template + inline static void write( + MMIO_t* const mmio_device, + typename std::enable_if::type = nullptr + ) noexcept { + *mmio_device = static_cast( + (*mmio_device & ~mask) | ((value << offset) & mask) + ); + }; + template + inline static void write( + MMIO_t* const mmio_device, + typename std::enable_if::type = nullptr + ) noexcept { + *mmio_device = value; + }; + }; + struct read_only { + template + inline static T read(const MMIO_t* const mmio_device) noexcept { + return RegisterRead::read(mmio_device); + }; }; struct read_write : read_only { - template + template inline static void write(MMIO_t* const mmio_device, - const T mask, - const Offset_t offset, const T value) noexcept { - *mmio_device = static_cast((*mmio_device & ~mask) | - ((value << offset) & mask)); + RegisterWrite::write(mmio_device, value); }; - template - inline static void set(MMIO_t* const mmio_device, const T mask) + template + inline static void write(MMIO_t* const mmio_device) noexcept { + RegisterWriteConstant + ::write(mmio_device); + }; + template + inline static void set(MMIO_t* const mmio_device) noexcept { - *mmio_device = static_cast((*mmio_device) | mask); + RegisterWriteConstant + ::write(mmio_device); }; - template - inline static void clear(MMIO_t* const mmio_device, const T mask) + template + inline static void clear(MMIO_t* const mmio_device) noexcept { - *mmio_device = static_cast((*mmio_device) & ~mask); + RegisterWriteConstant + ::write(mmio_device); }; - template - inline static void toggle(MMIO_t* const mmio_device, const T mask) + template + inline static void toggle(MMIO_t* const mmio_device) noexcept { *mmio_device = static_cast((*mmio_device) ^ mask); }; }; struct write_only { - template + template 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::value, 0u>::write( + mmio_device, ((value << offset) & mask) + ); + }; + template + inline static void write(MMIO_t* const mmio_device) noexcept { + RegisterWriteConstant< + MMIO_t, T, type_mask::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 + 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( - (*mmio_device & ~_combined_mask) | - ((_accumulated_value) & _combined_mask) - ); + RegisterWrite< + typename Register::MMIO_t, + base_type, + _combined_mask, + 0u + >::write(mmio_device, _accumulated_value); }; template - MergeWrite&& with(const base_type value) && noexcept { + inline MergeWrite 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(value); + F::policy::template write< + base_type, + base_type, + F::mask, + F::offset + >(&_accumulated_value, value); + return + std::move( + MergeWrite + ::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(base_address); }; template - inline static MergeWrite + inline static MergeWrite merge_write(const typename F::type value) noexcept { return - MergeWrite - ::create_instance(((value << F::offset) & F::mask), F::mask); + MergeWrite + ::make(((value << F::offset) & F::mask)); }; template < typename F, type value, - typename T = MergeWrite + 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 - ::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(width, offset); + constexpr static const bool has_shadow = + parent_register::shadow::use_shadow; template 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( + parent_register::ro_mem_pointer() + ); }; template 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::type value) + noexcept { + policy::template write( + parent_register::rw_mem_pointer(), + value + ); }; template 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::type value) + noexcept { + RegisterWrite + ::write(&parent_register::shadow::value, value); + policy::template write::value, 0u>( + parent_register::rw_mem_pointer(), + parent_register::shadow::value + ); }; template inline static typename std::enable_if< - (!parent_register::shadow::use_shadow) + !has_shadow && check_overflow::result, T >::type write() noexcept { - write(value); + policy::template write( + parent_register::rw_mem_pointer() + ); }; template inline static typename std::enable_if< - parent_register::shadow::use_shadow + has_shadow && check_overflow::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(parent_register::rw_mem_pointer()); }; inline static void clear() noexcept { - AccessPolicy::clear(parent_register::rw_mem_pointer(), mask); + policy::template + clear(parent_register::rw_mem_pointer()); }; inline static void toggle() noexcept { - AccessPolicy::toggle(parent_register::rw_mem_pointer(), mask); + policy::template + toggle(parent_register::rw_mem_pointer()); }; - template - inline static typename std::enable_if::type - is_set() noexcept { - return (Field::read() == 1u); + inline static bool is_set() noexcept { + return (Field::read() == (mask >> offset)); }; - template - inline static typename std::enable_if::type - is_clear() noexcept { + inline static bool is_clear() noexcept { return (Field::read() == 0u); }; static_assert(parent_register::size >= width,