4 Commits
v0.4 ... master

Author SHA1 Message Date
Nicolas Clauvelin
8cb2714b4b Fix typo in API documentation
This commit fixes the register type used in the "Register pack goodies" example.

Closes #16
2024-02-21 20:56:54 -05:00
Nicolas Clauvelin
42bd134067 Update CMake install directives 2022-01-27 14:59:12 -05:00
Nicolas Clauvelin
eb166f16e2 Update documentation for register memory access
This commit adds a mention of the `rw_mem_device` and `ro_mem_device`
that provides access to a register memory.

Closes #15
2022-01-27 13:16:20 -05:00
Nicolas Clauvelin
263bd8913d Project maintenance and review
This commit is a collection of changes related to:

- Code style fixes (including nolint) and formatting.
- Suppression of compiler warnings in bitwise operations.
- Minor edits (e.g. copyright dates).
2022-01-27 12:26:40 -05:00
16 changed files with 306 additions and 229 deletions

12
API.md
View File

@@ -155,13 +155,13 @@ struct Peripheral {
using Data = Field<Channel0, 8u, 0u, read_write>; using Data = Field<Channel0, 8u, 0u, read_write>;
}; };
struct Channel1 : PackedRegister<Pack, RegBistSize::b8, 8 * 1> { struct Channel1 : PackedRegister<Pack, RegBistSize::b8, 8 * 1> {
using Data = Field<Channel0, 8u, 0u, read_write>; using Data = Field<Channel1, 8u, 0u, read_write>;
}; };
struct Channel2 : PackedRegister<Pack, RegBistSize::b8, 8 * 2> { struct Channel2 : PackedRegister<Pack, RegBistSize::b8, 8 * 2> {
using Data = Field<Channel0, 8u, 0u, read_write>; using Data = Field<Channel2, 8u, 0u, read_write>;
}; };
struct Channel3 : PackedRegister<Pack, RegBistSize::b8, 8 * 3> { struct Channel3 : PackedRegister<Pack, RegBistSize::b8, 8 * 3> {
using Data = Field<Channel0, 8u, 0u, read_write>; using Data = Field<Channel3, 8u, 0u, read_write>;
}; };
} }
@@ -232,6 +232,12 @@ pack_loop<Channels>::apply([](auto index) {
}); });
``` ```
### Accessing registers memory ###
The register memory can be accessed directly using the static methods:
- `rw_mem_device()` for read/write access.
- `ro_mem_devices` for read-only access.
## Field interface ## ## Field interface ##
The `Field` template type provided by `cppreg` (see [Field.h](register/Field.h)) contains the added value of the library in terms of type safety, efficiency and expression of intent. The interface is: The `Field` template type provided by `cppreg` (see [Field.h](register/Field.h)) contains the added value of the library in terms of type safety, efficiency and expression of intent. The interface is:

View File

@@ -2,7 +2,7 @@
# cppreg library CMake script # cppreg library CMake script
# #
# Nicolas Clauvelin (nclauvelin@sendyne.com) # Nicolas Clauvelin (nclauvelin@sendyne.com)
# Sendyne Corp., 2017 # Sendyne Corp., 2022
# #
# -------------------------------------------------------------------------- # # -------------------------------------------------------------------------- #
@@ -51,6 +51,10 @@ set_property(TARGET cppreg
# --- Install directives --- # --- Install directives ---
install(FILES ${cppreg_headers} DESTINATION include/cppreg) set(include_dest "include/cppreg")
install(TARGETS cppreg DESTINATION lib EXPORT cppreg-target)
install(EXPORT cppreg-target DESTINATION include/cppreg) install(FILES ${cppreg_headers} DESTINATION ${include_dest})
install(TARGETS cppreg DESTINATION lib EXPORT cppreg-cmake)
install(EXPORT cppreg-cmake
DESTINATION ${include_dest}
NAMESPACE cppreg::)

View File

@@ -2,7 +2,7 @@
/** /**
* @file cppreg.h * @file cppreg.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
*/ */

View File

@@ -2,7 +2,7 @@
/** /**
* @file cppreg_Defines.h * @file cppreg_Defines.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* This header mostly defines data types used across the project. These data * This header mostly defines data types used across the project. These data
* types have been defined with 32-bits address space hardware in mind. It * types have been defined with 32-bits address space hardware in mind. It
@@ -32,10 +32,14 @@ using Address = std::uintptr_t;
* This is used to enforce the supported register sizes. * This is used to enforce the supported register sizes.
*/ */
enum class RegBitSize : std::uint8_t { enum class RegBitSize : std::uint8_t {
b8, //!< 8-bit register. //! 8-bit register.
b16, //!< 16-bit register. b8, // NOLINT
b32, //!< 32-bit register. //! 16-bit register.
b64 //!< 64-bit register. b16, // NOLINT
//! 32-bit register.
b32, // NOLINT
//! 64-bit register.
b64 // NOLINT
}; };
@@ -54,11 +58,15 @@ using FieldOffset = std::uint8_t;
* This is used to define register masks. * This is used to define register masks.
*/ */
template <typename T> template <typename T>
struct type_mask { struct type_mask { // NOLINT
constexpr static const T value = std::numeric_limits<T>::max(); constexpr static const T value = std::numeric_limits<T>::max();
}; };
//! Global constant to convert bits to bytes.
constexpr static const auto one_byte = std::size_t{8};
} // namespace cppreg } // namespace cppreg

View File

@@ -2,7 +2,7 @@
/** /**
* @file cppreg_Defines.h * @file cppreg_Defines.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
*/ */

View File

@@ -2,7 +2,7 @@
/** /**
* @file AccessPolicy.h * @file AccessPolicy.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* Access policies are used to describe if register fields are read-write, * Access policies are used to describe if register fields are read-write,
* read-only or write-only. * read-only or write-only.
@@ -39,7 +39,7 @@ namespace cppreg {
* @tparam offset Offset for the read operation. * @tparam offset Offset for the read operation.
*/ */
template <typename T, T mask, FieldOffset offset> template <typename T, T mask, FieldOffset offset>
struct is_trivial_rw struct is_trivial_rw // NOLINT
: std::integral_constant<bool, : std::integral_constant<bool,
(mask == type_mask<T>::value) (mask == type_mask<T>::value)
&& (offset == FieldOffset{0})> {}; && (offset == FieldOffset{0})> {};
@@ -52,7 +52,7 @@ struct is_trivial_rw
* @tparam U Enabled type if trivial. * @tparam U Enabled type if trivial.
*/ */
template <typename T, T mask, FieldOffset offset, typename U> template <typename T, T mask, FieldOffset offset, typename U>
using is_trivial = using is_trivial = // NOLINT
typename std::enable_if<is_trivial_rw<T, mask, offset>::value, U>::type; typename std::enable_if<is_trivial_rw<T, mask, offset>::value, U>::type;
//! Non-trivial read/write enable_if helper. //! Non-trivial read/write enable_if helper.
@@ -63,7 +63,7 @@ using is_trivial =
* @tparam U Enabled type if non-trivial. * @tparam U Enabled type if non-trivial.
*/ */
template <typename T, T mask, FieldOffset offset, typename U> template <typename T, T mask, FieldOffset offset, typename U>
using is_not_trivial = using is_not_trivial = // NOLINT
typename std::enable_if<!is_trivial_rw<T, mask, offset>::value, U>::type; typename std::enable_if<!is_trivial_rw<T, mask, offset>::value, U>::type;
//!@} //!@}
@@ -85,9 +85,10 @@ struct RegisterRead {
* @return The content of the register field. * @return The content of the register field.
*/ */
template <typename U = void> template <typename U = void>
static T read(const MMIO& mmio_device, static T read(const MMIO& mmio_device, // NOLINTNEXTLINE
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept { is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>((mmio_device & mask) >> offset); const auto lhs = static_cast<T>(mmio_device & mask);
return static_cast<T>(lhs >> offset);
} }
//! Trivial read implementation. //! Trivial read implementation.
@@ -96,7 +97,7 @@ struct RegisterRead {
* @return The content of the register field. * @return The content of the register field.
*/ */
template <typename U = void> template <typename U = void>
static T read(const MMIO& mmio_device, static T read(const MMIO& mmio_device, // NOLINTNEXTLINE
is_trivial<T, mask, offset, U>* = nullptr) noexcept { is_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>(mmio_device); return static_cast<T>(mmio_device);
} }
@@ -120,10 +121,12 @@ struct RegisterWrite {
*/ */
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device,
T value, T value, // NOLINTNEXTLINE
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept { is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = const auto shifted_value = static_cast<T>(value << offset);
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask)); const auto lhs = static_cast<T>(mmio_device & static_cast<T>(~mask));
const auto rhs = static_cast<T>(shifted_value & mask);
mmio_device = static_cast<T>(lhs | rhs);
} }
//! Trivial write implementation. //! Trivial write implementation.
@@ -133,7 +136,7 @@ struct RegisterWrite {
*/ */
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device,
T value, T value, // NOLINTNEXTLINE
is_trivial<T, mask, offset, U>* = nullptr) noexcept { is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value; mmio_device = value;
} }
@@ -156,10 +159,12 @@ struct RegisterWriteConstant {
* @param mmio_device Pointer to the register memory device. * @param mmio_device Pointer to the register memory device.
*/ */
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device, // NOLINTNEXTLINE
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept { is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = constexpr auto shifted_value = static_cast<T>(value << offset);
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask)); const auto lhs = static_cast<T>(mmio_device & static_cast<T>(~mask));
constexpr auto rhs = static_cast<T>(shifted_value & mask);
mmio_device = static_cast<T>(lhs | rhs);
} }
//! Trivial write implementation. //! Trivial write implementation.
@@ -167,7 +172,7 @@ struct RegisterWriteConstant {
* @param mmio_device Pointer to the register memory device. * @param mmio_device Pointer to the register memory device.
*/ */
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device, // NOLINTNEXTLINE
is_trivial<T, mask, offset, U>* = nullptr) noexcept { is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value; mmio_device = value;
} }
@@ -175,7 +180,7 @@ struct RegisterWriteConstant {
//! Read-only access policy. //! Read-only access policy.
struct read_only { struct read_only { // NOLINT
//! Read access implementation. //! Read access implementation.
/** /**
@@ -194,7 +199,7 @@ struct read_only {
//! Read-write access policy. //! Read-write access policy.
struct read_write : read_only { struct read_write : read_only { // NOLINT
//! Write access implementation. //! Write access implementation.
/** /**
@@ -267,7 +272,7 @@ struct read_write : read_only {
//! Write-only access policy. //! Write-only access policy.
struct write_only { struct write_only { // NOLINT
//! Write access implementation. //! Write access implementation.
/** /**

View File

@@ -2,7 +2,7 @@
/** /**
* @file Field.h * @file Field.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* This header provides the definitions related to register field * This header provides the definitions related to register field
* implementation. Strictly speaking a field is defined as a region of a * implementation. Strictly speaking a field is defined as a region of a
@@ -43,16 +43,16 @@ template <typename BaseRegister,
struct Field { struct Field {
//! Parent register for the field. //! Parent register for the field.
using parent_register = BaseRegister; using parent_register = BaseRegister; // NOLINT
//! Field data type derived from register data type. //! Field data type derived from register data type.
using type = typename parent_register::type; using type = typename parent_register::type; // NOLINT
//! MMIO type. //! MMIO type.
using MMIO = typename parent_register::MMIO; using MMIO = typename parent_register::MMIO; // NOLINT
//! Field policy. //! Field policy.
using policy = AccessPolicy; using policy = AccessPolicy; // NOLINT
//! Field width. //! Field width.
constexpr static auto width = field_width; constexpr static auto width = field_width;
@@ -65,10 +65,10 @@ struct Field {
//!@{ Helpers for write method selection based on shadow value. //!@{ Helpers for write method selection based on shadow value.
template <type value, typename T> template <type value, typename T>
using if_no_shadow = using if_no_shadow = // NOLINT
typename std::enable_if<!parent_register::shadow::value, T>::type; typename std::enable_if<!parent_register::shadow::value, T>::type;
template <type value, typename T> template <type value, typename T>
using if_shadow = using if_shadow = // NOLINT
typename std::enable_if<parent_register::shadow::value, T>::type; typename std::enable_if<parent_register::shadow::value, T>::type;
//!@} //!@}
@@ -118,7 +118,7 @@ struct Field {
* field and uses the constant write implementation. * field and uses the constant write implementation.
*/ */
template <type value, typename T = void> template <type value, typename T = void>
static void write(if_no_shadow<value, T>* = nullptr) noexcept { static void write(if_no_shadow<value, T>* = nullptr) noexcept { // NOLINT
policy::template write<MMIO, type, mask, offset, value>( policy::template write<MMIO, type, mask, offset, value>(
parent_register::rw_mem_device()); parent_register::rw_mem_device());
@@ -136,7 +136,7 @@ struct Field {
* field and uses the constant write implementation. * field and uses the constant write implementation.
*/ */
template <type value, typename T = void> template <type value, typename T = void>
static void write(if_shadow<value, T>* = nullptr) noexcept { static void write(if_shadow<value, T>* = nullptr) noexcept { // NOLINT
// For this one we simply forward to the non-constant // For this one we simply forward to the non-constant
// implementation because the shadow value needs to be updated. // implementation because the shadow value needs to be updated.
write(value); write(value);

View File

@@ -2,7 +2,7 @@
/** /**
* @file Internals.h * @file Internals.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* This header collects various implementations which are required for cppreg * This header collects various implementations which are required for cppreg
* implementation but not intended to be fully exposed to the user. * implementation but not intended to be fully exposed to the user.
@@ -31,7 +31,8 @@ namespace internals {
* There is overflow if value if strictly larger than limit. * There is overflow if value if strictly larger than limit.
*/ */
template <typename T, T value, T limit> template <typename T, T value, T limit>
struct check_overflow : std::integral_constant<bool, value <= limit> {}; struct check_overflow // NOLINT
: std::integral_constant<bool, value <= limit> {};
//! is_aligned implementation. //! is_aligned implementation.
@@ -42,8 +43,10 @@ struct check_overflow : std::integral_constant<bool, value <= limit> {};
* This will only derived from std::true_type if the address is aligned. * This will only derived from std::true_type if the address is aligned.
*/ */
template <Address address, std::size_t alignment> template <Address address, std::size_t alignment>
struct is_aligned struct is_aligned // NOLINT
: std::integral_constant<bool, (address & (alignment - 1)) == 0> {}; : std::integral_constant<
bool,
(static_cast<std::size_t>(address) & (alignment - 1)) == 0> {};
} // namespace internals } // namespace internals

View File

@@ -2,7 +2,7 @@
/** /**
* @file Mask.h * @file Mask.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* The implementation is designed to compute masks prior to runtime by * The implementation is designed to compute masks prior to runtime by
* relying on constexpr function. This will work as intended if the function * relying on constexpr function. This will work as intended if the function
@@ -28,9 +28,13 @@ namespace cppreg {
*/ */
template <typename Mask> template <typename Mask>
constexpr Mask make_mask(const FieldWidth width) noexcept { constexpr Mask make_mask(const FieldWidth width) noexcept {
return width == 0 ? static_cast<Mask>(0u) return width == 0U
: static_cast<Mask>( ? static_cast<Mask>(0U)
(make_mask<Mask>(FieldWidth(width - 1)) << 1) | 1); : static_cast<Mask>(
static_cast<Mask>(make_mask<Mask>(FieldWidth(
static_cast<FieldWidth>(width - 1U)))
<< 1U)
| 1U);
} }

View File

@@ -2,7 +2,7 @@
/** /**
* @file Memory.h * @file Memory.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
*/ */
@@ -10,6 +10,7 @@
#define CPPREG_DEV_MEMORY_H #define CPPREG_DEV_MEMORY_H
#include "Internals.h"
#include "Traits.h" #include "Traits.h"
#include <array> #include <array>
@@ -43,10 +44,10 @@ template <Address mem_address, std::size_t mem_byte_size>
struct MemoryDevice { struct MemoryDevice {
//! Storage type. //! Storage type.
using memory_storage = std::array<volatile std::uint8_t, mem_byte_size>; using MemStorage = std::array<volatile std::uint8_t, mem_byte_size>;
//! Memory device storage. //! Memory device storage.
static memory_storage& _mem_storage; static MemStorage& _mem_storage; // NOLINT
//! Accessor. //! Accessor.
template <RegBitSize reg_size, std::size_t byte_offset> template <RegBitSize reg_size, std::size_t byte_offset>
@@ -59,7 +60,7 @@ struct MemoryDevice {
reg_size>::type>::value>::value, reg_size>::type>::value>::value,
"MemoryDevice:: ro request not aligned"); "MemoryDevice:: ro request not aligned");
return *(reinterpret_cast<const volatile return *(reinterpret_cast<const volatile // NOLINT
typename TypeTraits<reg_size>::type*>( typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset])); &_mem_storage[byte_offset]));
} }
@@ -75,16 +76,17 @@ struct MemoryDevice {
reg_size>::type>::value>::value, reg_size>::type>::value>::value,
"MemoryDevice:: rw request not aligned"); "MemoryDevice:: rw request not aligned");
return *( return *( // NOLINTNEXTLINE
reinterpret_cast<volatile typename TypeTraits<reg_size>::type*>( reinterpret_cast<volatile typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset])); &_mem_storage[byte_offset]));
} }
}; };
//! Static reference definition. //! Static reference definition.
template <Address a, std::size_t s> template <Address a, std::size_t s> // NOLINTNEXTLINE
typename MemoryDevice<a, s>::memory_storage& MemoryDevice<a, s>::_mem_storage = typename MemoryDevice<a, s>::MemStorage& MemoryDevice<a, s>::_mem_storage =
*(reinterpret_cast<typename MemoryDevice<a, s>::memory_storage*>(a)); // NOLINTNEXTLINE
*(reinterpret_cast<typename MemoryDevice<a, s>::MemStorage*>(a));
//! Register memory device for register pack. //! Register memory device for register pack.
@@ -95,7 +97,7 @@ typename MemoryDevice<a, s>::memory_storage& MemoryDevice<a, s>::_mem_storage =
*/ */
template <typename RegisterPack> template <typename RegisterPack>
struct RegisterMemoryDevice { struct RegisterMemoryDevice {
using mem_device = using mem_device = // NOLINT
MemoryDevice<RegisterPack::pack_base, RegisterPack::size_in_bytes>; MemoryDevice<RegisterPack::pack_base, RegisterPack::size_in_bytes>;
}; };

View File

@@ -2,7 +2,7 @@
/** /**
* @file MergeWrite.h * @file MergeWrite.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* The "merge write" implementation is designed to make it possible to merge * The "merge write" implementation is designed to make it possible to merge
* write operations on different fields into a single one. * write operations on different fields into a single one.
@@ -45,26 +45,27 @@ template <typename Register,
typename Register::type mask, typename Register::type mask,
FieldOffset offset, FieldOffset offset,
typename Register::type value> typename Register::type value>
class MergeWrite_tmpl { class MergeWrite_tmpl { // NOLINT
private: private:
// Type alias to register base type. // Type alias to register base type.
using base_type = typename Register::type; using base_type = typename Register::type; // NOLINT
// Accumulated value. // Accumulated value.
constexpr static auto _accumulated_value = constexpr static auto _accumulated_value = // NOLINT
base_type{(value << offset) & mask}; base_type{(value << offset) & mask};
// Combined mask. // Combined mask.
constexpr static auto _combined_mask = mask; constexpr static auto _combined_mask = mask; // NOLINT
// Type helper. // Type helper.
template <typename F, base_type new_value> template <typename F, base_type new_value>
using propagated = using propagated = // NOLINT
MergeWrite_tmpl<Register, MergeWrite_tmpl<Register,
(_combined_mask | F::mask), (_combined_mask | F::mask),
FieldOffset{0}, FieldOffset{0},
(_accumulated_value & ~F::mask) (_accumulated_value
& static_cast<typename Register::type>(~F::mask))
| ((new_value << F::offset) & F::mask)>; | ((new_value << F::offset) & F::mask)>;
// Default constructor. // Default constructor.
@@ -80,12 +81,17 @@ public:
return {}; return {};
} }
//!@{ Non-copyable and non-moveable. //! Destructor.
~MergeWrite_tmpl() = default;
//! Move constructor.
MergeWrite_tmpl(MergeWrite_tmpl&&) noexcept = default;
//!@{ Non-copyable and non-assignable.
MergeWrite_tmpl(const MergeWrite_tmpl&) = delete; MergeWrite_tmpl(const MergeWrite_tmpl&) = delete;
MergeWrite_tmpl& operator=(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 operator=(MergeWrite_tmpl) = delete; MergeWrite_tmpl operator=(MergeWrite_tmpl) = delete;
MergeWrite_tmpl(MergeWrite_tmpl&&) = delete;
//!@} //!@}
//! Closure method. //! Closure method.
@@ -114,7 +120,7 @@ public:
* @return A merge write instance with accumulated data. * @return A merge write instance with accumulated data.
*/ */
template <typename F, base_type field_value> template <typename F, base_type field_value>
propagated<F, field_value>&& with() const&& noexcept { propagated<F, field_value> with() const&& noexcept {
// Check that the field belongs to the register. // Check that the field belongs to the register.
static_assert( static_assert(
@@ -129,7 +135,7 @@ public:
static_assert(no_overflow, static_assert(no_overflow,
"MergeWrite_tmpl:: field overflow in with() call"); "MergeWrite_tmpl:: field overflow in with() call");
return std::move(propagated<F, field_value>{}); return propagated<F, field_value>{};
} }
}; };
@@ -147,17 +153,18 @@ class MergeWrite {
private: private:
// Type alias to register base type. // Type alias to register base type.
using base_type = typename Register::type; using base_type = typename Register::type; // NOLINT
// Accumulated value. // Accumulated value.
base_type _accumulated_value; base_type _accumulated_value; // NOLINT
// Combined mask. // Combined mask.
constexpr static auto _combined_mask = mask; constexpr static auto _combined_mask = mask; // NOLINT
// Type helper. // Type helper.
template <typename F> template <typename F>
using propagated = MergeWrite<Register, _combined_mask | F::mask>; using propagated = // NOLINT
MergeWrite<Register, _combined_mask | F::mask>;
// Private default constructor. // Private default constructor.
constexpr MergeWrite() : _accumulated_value{0} {}; constexpr MergeWrite() : _accumulated_value{0} {};
@@ -173,11 +180,14 @@ public:
return MergeWrite(value); return MergeWrite(value);
} }
//! Destructor.
~MergeWrite() = default;
//!@ Move constructor. //!@ Move constructor.
MergeWrite(MergeWrite&& mw) noexcept MergeWrite(MergeWrite&& mw) noexcept
: _accumulated_value{mw._accumulated_value} {}; : _accumulated_value{mw._accumulated_value} {};
//!@{ Non-copyable. //!@{ Non-copyable and non-assignable.
MergeWrite(const MergeWrite&) = delete; MergeWrite(const MergeWrite&) = delete;
MergeWrite& operator=(const MergeWrite&) = delete; MergeWrite& operator=(const MergeWrite&) = delete;
MergeWrite& operator=(MergeWrite&&) = delete; MergeWrite& operator=(MergeWrite&&) = delete;
@@ -216,10 +226,11 @@ public:
"field is not from the same register in merge_write"); "field is not from the same register in merge_write");
// Update accumulated value. // Update accumulated value.
const auto new_value = static_cast<base_type>( constexpr auto neg_mask = static_cast<base_type>(~F::mask);
(_accumulated_value & ~F::mask) | ((value << F::offset) & F::mask)); const auto shifted_value = static_cast<base_type>(value << F::offset);
const auto lhs = static_cast<base_type>(_accumulated_value & neg_mask);
return std::move(propagated<F>::create(new_value)); const auto rhs = static_cast<base_type>(shifted_value & F::mask);
return propagated<F>::create(static_cast<base_type>(lhs | rhs));
} }
}; };

View File

@@ -2,7 +2,7 @@
/** /**
* @file Register.h * @file Register.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* This header provides the definitions related to register implementation. * This header provides the definitions related to register implementation.
*/ */
@@ -40,13 +40,13 @@ template <Address reg_address,
struct Register { struct Register {
//! Register base type. //! Register base type.
using type = typename TypeTraits<reg_size>::type; using type = typename TypeTraits<reg_size>::type; // NOLINT
//! MMIO pointer type. //! MMIO pointer type.
using MMIO = volatile type; using MMIO = volatile type;
//! Boolean flag for shadow value management. //! Boolean flag for shadow value management.
using shadow = Shadow<Register, use_shadow>; using shadow = Shadow<Register, use_shadow>; // NOLINT
//! Register base address. //! Register base address.
constexpr static auto base_address = reg_address; constexpr static auto base_address = reg_address;
@@ -58,15 +58,15 @@ struct Register {
constexpr static auto reset = reset_value; constexpr static auto reset = reset_value;
//! Register pack for memory device. //! Register pack for memory device.
using pack = RegisterPack<base_address, size / 8u>; using pack = RegisterPack<base_address, size / one_byte>; // NOLINT
//! Memory modifier. //! Memory modifier.
/** /**
* @return A reference to the writable register memory. * @return A reference to the writable register memory.
*/ */
static MMIO& rw_mem_device() { static MMIO& rw_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device; using MemDevice = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template rw_memory<reg_size, 0>(); return MemDevice::template rw_memory<reg_size, 0>();
} }
//! Memory accessor. //! Memory accessor.
@@ -74,8 +74,8 @@ struct Register {
* @return A reference to the read-only register memory. * @return A reference to the read-only register memory.
*/ */
static const MMIO& ro_mem_device() { static const MMIO& ro_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device; using MemDevice = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template ro_memory<reg_size, 0>(); return MemDevice::template ro_memory<reg_size, 0>();
} }
//! Merge write start function. //! Merge write start function.
@@ -87,8 +87,9 @@ struct Register {
template <typename F> template <typename F>
static MergeWrite<typename F::parent_register, F::mask> merge_write( static MergeWrite<typename F::parent_register, F::mask> merge_write(
const typename F::type value) noexcept { const typename F::type value) noexcept {
const auto lhs = static_cast<type>(value << F::offset);
return MergeWrite<typename F::parent_register, F::mask>::create( return MergeWrite<typename F::parent_register, F::mask>::create(
static_cast<type>((value << F::offset) & F::mask)); static_cast<type>(lhs & F::mask));
} }
//! Merge write start function for constant value. //! Merge write start function for constant value.
@@ -103,7 +104,7 @@ struct Register {
F::mask, F::mask,
F::offset, F::offset,
value>> value>>
static T&& merge_write() noexcept { static T merge_write() noexcept {
// Check overflow. // Check overflow.
static_assert( static_assert(
@@ -111,11 +112,11 @@ struct Register {
value, value,
"Register::merge_write<value>:: value too large for the field"); "Register::merge_write<value>:: value too large for the field");
return std::move(T::create()); return T::create();
} }
// Sanity check. // Sanity check.
static_assert(size != 0u, "Register:: register definition with zero size"); static_assert(size != 0, "Register:: register definition with zero size");
// Enforce alignment. // Enforce alignment.
static_assert(internals::is_aligned<reg_address, static_assert(internals::is_aligned<reg_address,

View File

@@ -2,7 +2,7 @@
/** /**
* @file RegisterPack.h * @file RegisterPack.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* This header provides the definitions related to packed register * This header provides the definitions related to packed register
* implementation. * implementation.
@@ -37,28 +37,31 @@ template <typename RegisterPack,
std::uint32_t bit_offset, std::uint32_t bit_offset,
typename TypeTraits<reg_size>::type reset_value = 0x0, typename TypeTraits<reg_size>::type reset_value = 0x0,
bool use_shadow = false> bool use_shadow = false>
struct PackedRegister : Register<RegisterPack::pack_base + (bit_offset / 8u), struct PackedRegister
reg_size, : Register<RegisterPack::pack_base + (bit_offset / one_byte),
reset_value, reg_size,
use_shadow> { reset_value,
use_shadow> {
//! Register pack. //! Register pack.
using pack = RegisterPack; using pack = RegisterPack; // NOLINT
//! Register type. //! Register type.
using base_reg = Register<RegisterPack::pack_base + (bit_offset / 8u), using base_reg = // NOLINT
reg_size, Register<RegisterPack::pack_base + (bit_offset / one_byte),
reset_value, reg_size,
use_shadow>; reset_value,
use_shadow>;
//! Memory modifier. //! Memory modifier.
/** /**
* @return A reference to the writable register memory. * @return A reference to the writable register memory.
*/ */
static typename base_reg::MMIO& rw_mem_device() noexcept { static typename base_reg::MMIO& rw_mem_device() noexcept {
using mem_device = using MemDevice =
typename RegisterMemoryDevice<RegisterPack>::mem_device; typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template rw_memory<reg_size, (bit_offset / 8u)>(); return MemDevice::template rw_memory<reg_size,
(bit_offset / one_byte)>();
} }
//! Memory accessor. //! Memory accessor.
@@ -66,13 +69,14 @@ struct PackedRegister : Register<RegisterPack::pack_base + (bit_offset / 8u),
* @return A reference to the read-only register memory. * @return A reference to the read-only register memory.
*/ */
static const typename base_reg::MMIO& ro_mem_device() noexcept { static const typename base_reg::MMIO& ro_mem_device() noexcept {
using mem_device = using MemDevice =
typename RegisterMemoryDevice<RegisterPack>::mem_device; typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template ro_memory<reg_size, (bit_offset / 8u)>(); return MemDevice::template ro_memory<reg_size,
(bit_offset / one_byte)>();
} }
// Safety check to detect if are overflowing the pack. // Safety check to detect if are overflowing the pack.
static_assert(TypeTraits<reg_size>::byte_size + (bit_offset / 8u) static_assert(TypeTraits<reg_size>::byte_size + (bit_offset / one_byte)
<= RegisterPack::size_in_bytes, <= RegisterPack::size_in_bytes,
"PackRegister:: packed register is overflowing the pack"); "PackRegister:: packed register is overflowing the pack");
@@ -84,7 +88,7 @@ struct PackedRegister : Register<RegisterPack::pack_base + (bit_offset / 8u),
TypeTraits<reg_size>::byte_size>::value, TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: pack base address is mis-aligned for register type"); "PackedRegister:: pack base address is mis-aligned for register type");
static_assert( static_assert(
internals::is_aligned<RegisterPack::pack_base + (bit_offset / 8u), internals::is_aligned<RegisterPack::pack_base + (bit_offset / one_byte),
TypeTraits<reg_size>::byte_size>::value, TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: offset address is mis-aligned for register type"); "PackedRegister:: offset address is mis-aligned for register type");
}; };
@@ -102,15 +106,15 @@ template <typename... T>
struct PackIndexing { struct PackIndexing {
//! Tuple type. //! Tuple type.
using tuple_t = typename std::tuple<T...>; using tuple_t = typename std::tuple<T...>; // NOLINT
//! Number of elements. //! Number of elements.
constexpr static const std::size_t n_elems = constexpr static const std::size_t n_elems =
std::tuple_size<tuple_t>::value; std::tuple_size<tuple_t>::value;
//! Element accessor. //! Element accessor.
template <std::size_t N> template <std::size_t n>
using elem = typename std::tuple_element<N, tuple_t>::type; using elem = typename std::tuple_element<n, tuple_t>::type; // NOLINT
}; };
@@ -120,7 +124,7 @@ struct PackIndexing {
* @tparam end End index value. * @tparam end End index value.
*/ */
template <std::size_t start, std::size_t end> template <std::size_t start, std::size_t end>
struct for_loop { struct for_loop { // NOLINT
//! Loop method. //! Loop method.
/** /**
@@ -131,8 +135,9 @@ struct for_loop {
template <typename Func> template <typename Func>
static void apply() noexcept { static void apply() noexcept {
Func().template operator()<start>(); Func().template operator()<start>();
if (start < end) if (start < end) {
for_loop<start + 1ul, end>::template apply<Func>(); for_loop<start + 1, end>::template apply<Func>();
}
} }
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
@@ -150,7 +155,7 @@ struct for_loop {
static void apply(Op&& f) noexcept { static void apply(Op&& f) noexcept {
if (start < end) { if (start < end) {
f(std::integral_constant<std::size_t, start>{}); f(std::integral_constant<std::size_t, start>{});
for_loop<start + 1ul, end>::apply(std::forward<Op>(f)); for_loop<start + 1, end>::apply(std::forward<Op>(f));
}; };
} }
#endif // __cplusplus 201402L #endif // __cplusplus 201402L
@@ -161,8 +166,8 @@ struct for_loop<end, end> {
static void apply() noexcept {} static void apply() noexcept {}
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
template <typename Op> template <typename Op>
static void apply(Op&& f) noexcept {} static void apply(Op&&) noexcept {} // NOLINT
#endif // __cplusplus 201402L #endif // __cplusplus 201402L
}; };
@@ -171,7 +176,7 @@ struct for_loop<end, end> {
* @tparam IndexedPack Indexed pack type. * @tparam IndexedPack Indexed pack type.
*/ */
template <typename IndexedPack> template <typename IndexedPack>
struct pack_loop : for_loop<0, IndexedPack::n_elems> {}; struct pack_loop : for_loop<0, IndexedPack::n_elems> {}; // NOLINT
} // namespace cppreg } // namespace cppreg

View File

@@ -2,7 +2,7 @@
/** /**
* @file ShadowValue.h * @file ShadowValue.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
*/ */

View File

@@ -2,7 +2,7 @@
/** /**
* @file Traits.h * @file Traits.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
* *
* This header provides some traits for register type instantiation. * This header provides some traits for register type instantiation.
*/ */
@@ -20,9 +20,9 @@ namespace cppreg {
//! Register type traits based on size. //! Register type traits based on size.
/** /**
* @tparam S Register size in bits. * @tparam size Register size in bits.
*/ */
template <RegBitSize S> template <RegBitSize size>
struct TypeTraits {}; struct TypeTraits {};
@@ -31,7 +31,7 @@ struct TypeTraits {};
//! 8-bit specialization. //! 8-bit specialization.
template <> template <>
struct TypeTraits<RegBitSize::b8> { struct TypeTraits<RegBitSize::b8> {
using type = std::uint8_t; using type = std::uint8_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{8}; constexpr static auto bit_size = std::uint8_t{8};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };
@@ -39,7 +39,7 @@ struct TypeTraits<RegBitSize::b8> {
//! 16-bit specialization. //! 16-bit specialization.
template <> template <>
struct TypeTraits<RegBitSize::b16> { struct TypeTraits<RegBitSize::b16> {
using type = std::uint16_t; using type = std::uint16_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{16}; constexpr static auto bit_size = std::uint8_t{16};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };
@@ -47,7 +47,7 @@ struct TypeTraits<RegBitSize::b16> {
//! 32-bit specialization. //! 32-bit specialization.
template <> template <>
struct TypeTraits<RegBitSize::b32> { struct TypeTraits<RegBitSize::b32> {
using type = std::uint32_t; using type = std::uint32_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{32}; constexpr static auto bit_size = std::uint8_t{32};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };
@@ -55,7 +55,7 @@ struct TypeTraits<RegBitSize::b32> {
//! 64-bit specialization. //! 64-bit specialization.
template <> template <>
struct TypeTraits<RegBitSize::b64> { struct TypeTraits<RegBitSize::b64> {
using type = std::uint64_t; using type = std::uint64_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{64}; constexpr static auto bit_size = std::uint8_t{64};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };

View File

@@ -1,14 +1,15 @@
/* clang-format off */
//! cppreg library. //! cppreg library.
/** /**
* @file cppreg-all.h * @file cppreg-all.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved. * @copyright Copyright 2010-2022 Sendyne Corp. All rights reserved.
*/ */
#include <cstdint> #include <cstdint>
#include <type_traits>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <type_traits>
// cppreg_Defines.h // cppreg_Defines.h
#ifndef CPPREG_CPPREG_DEFINES_H #ifndef CPPREG_CPPREG_DEFINES_H
@@ -16,17 +17,18 @@
namespace cppreg { namespace cppreg {
using Address = std::uintptr_t; using Address = std::uintptr_t;
enum class RegBitSize : std::uint8_t { enum class RegBitSize : std::uint8_t {
b8, b8, // NOLINT
b16, b16, // NOLINT
b32, b32, // NOLINT
b64 b64 // NOLINT
}; };
using FieldWidth = std::uint8_t; using FieldWidth = std::uint8_t;
using FieldOffset = std::uint8_t; using FieldOffset = std::uint8_t;
template <typename T> template <typename T>
struct type_mask { struct type_mask { // NOLINT
constexpr static const T value = std::numeric_limits<T>::max(); constexpr static const T value = std::numeric_limits<T>::max();
}; };
constexpr static const auto one_byte = std::size_t{8};
} }
#endif #endif
@@ -34,29 +36,29 @@ struct type_mask {
#ifndef CPPREG_TRAITS_H #ifndef CPPREG_TRAITS_H
#define CPPREG_TRAITS_H #define CPPREG_TRAITS_H
namespace cppreg { namespace cppreg {
template <RegBitSize S> template <RegBitSize size>
struct TypeTraits {}; struct TypeTraits {};
template <> template <>
struct TypeTraits<RegBitSize::b8> { struct TypeTraits<RegBitSize::b8> {
using type = std::uint8_t; using type = std::uint8_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{8}; constexpr static auto bit_size = std::uint8_t{8};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };
template <> template <>
struct TypeTraits<RegBitSize::b16> { struct TypeTraits<RegBitSize::b16> {
using type = std::uint16_t; using type = std::uint16_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{16}; constexpr static auto bit_size = std::uint8_t{16};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };
template <> template <>
struct TypeTraits<RegBitSize::b32> { struct TypeTraits<RegBitSize::b32> {
using type = std::uint32_t; using type = std::uint32_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{32}; constexpr static auto bit_size = std::uint8_t{32};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };
template <> template <>
struct TypeTraits<RegBitSize::b64> { struct TypeTraits<RegBitSize::b64> {
using type = std::uint64_t; using type = std::uint64_t; // NOLINT
constexpr static auto bit_size = std::uint8_t{64}; constexpr static auto bit_size = std::uint8_t{64};
constexpr static auto byte_size = std::uint8_t{bit_size / 8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8};
}; };
@@ -69,10 +71,13 @@ struct TypeTraits<RegBitSize::b64> {
namespace cppreg { namespace cppreg {
namespace internals { namespace internals {
template <typename T, T value, T limit> template <typename T, T value, T limit>
struct check_overflow : std::integral_constant<bool, value <= limit> {}; struct check_overflow // NOLINT
: std::integral_constant<bool, value <= limit> {};
template <Address address, std::size_t alignment> template <Address address, std::size_t alignment>
struct is_aligned struct is_aligned // NOLINT
: std::integral_constant<bool, (address & (alignment - 1)) == 0> {}; : std::integral_constant<
bool,
(static_cast<std::size_t>(address) & (alignment - 1)) == 0> {};
} }
} }
#endif #endif
@@ -88,8 +93,8 @@ struct RegisterPack {
}; };
template <Address mem_address, std::size_t mem_byte_size> template <Address mem_address, std::size_t mem_byte_size>
struct MemoryDevice { struct MemoryDevice {
using memory_storage = std::array<volatile std::uint8_t, mem_byte_size>; using MemStorage = std::array<volatile std::uint8_t, mem_byte_size>;
static memory_storage& _mem_storage; static MemStorage& _mem_storage; // NOLINT
template <RegBitSize reg_size, std::size_t byte_offset> template <RegBitSize reg_size, std::size_t byte_offset>
static const volatile typename TypeTraits<reg_size>::type& ro_memory() { static const volatile typename TypeTraits<reg_size>::type& ro_memory() {
static_assert( static_assert(
@@ -97,7 +102,7 @@ struct MemoryDevice {
std::alignment_of<typename TypeTraits< std::alignment_of<typename TypeTraits<
reg_size>::type>::value>::value, reg_size>::type>::value>::value,
"MemoryDevice:: ro request not aligned"); "MemoryDevice:: ro request not aligned");
return *(reinterpret_cast<const volatile return *(reinterpret_cast<const volatile // NOLINT
typename TypeTraits<reg_size>::type*>( typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset])); &_mem_storage[byte_offset]));
} }
@@ -108,17 +113,18 @@ struct MemoryDevice {
std::alignment_of<typename TypeTraits< std::alignment_of<typename TypeTraits<
reg_size>::type>::value>::value, reg_size>::type>::value>::value,
"MemoryDevice:: rw request not aligned"); "MemoryDevice:: rw request not aligned");
return *( return *( // NOLINTNEXTLINE
reinterpret_cast<volatile typename TypeTraits<reg_size>::type*>( reinterpret_cast<volatile typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset])); &_mem_storage[byte_offset]));
} }
}; };
template <Address a, std::size_t s> template <Address a, std::size_t s> // NOLINTNEXTLINE
typename MemoryDevice<a, s>::memory_storage& MemoryDevice<a, s>::_mem_storage = typename MemoryDevice<a, s>::MemStorage& MemoryDevice<a, s>::_mem_storage =
*(reinterpret_cast<typename MemoryDevice<a, s>::memory_storage*>(a)); // NOLINTNEXTLINE
*(reinterpret_cast<typename MemoryDevice<a, s>::MemStorage*>(a));
template <typename RegisterPack> template <typename RegisterPack>
struct RegisterMemoryDevice { struct RegisterMemoryDevice {
using mem_device = using mem_device = // NOLINT
MemoryDevice<RegisterPack::pack_base, RegisterPack::size_in_bytes>; MemoryDevice<RegisterPack::pack_base, RegisterPack::size_in_bytes>;
}; };
} }
@@ -129,25 +135,26 @@ struct RegisterMemoryDevice {
#define CPPREG_ACCESSPOLICY_H #define CPPREG_ACCESSPOLICY_H
namespace cppreg { namespace cppreg {
template <typename T, T mask, FieldOffset offset> template <typename T, T mask, FieldOffset offset>
struct is_trivial_rw struct is_trivial_rw // NOLINT
: std::integral_constant<bool, : std::integral_constant<bool,
(mask == type_mask<T>::value) (mask == type_mask<T>::value)
&& (offset == FieldOffset{0})> {}; && (offset == FieldOffset{0})> {};
template <typename T, T mask, FieldOffset offset, typename U> template <typename T, T mask, FieldOffset offset, typename U>
using is_trivial = using is_trivial = // NOLINT
typename std::enable_if<is_trivial_rw<T, mask, offset>::value, U>::type; typename std::enable_if<is_trivial_rw<T, mask, offset>::value, U>::type;
template <typename T, T mask, FieldOffset offset, typename U> template <typename T, T mask, FieldOffset offset, typename U>
using is_not_trivial = using is_not_trivial = // NOLINT
typename std::enable_if<!is_trivial_rw<T, mask, offset>::value, U>::type; typename std::enable_if<!is_trivial_rw<T, mask, offset>::value, U>::type;
template <typename MMIO, typename T, T mask, FieldOffset offset> template <typename MMIO, typename T, T mask, FieldOffset offset>
struct RegisterRead { struct RegisterRead {
template <typename U = void> template <typename U = void>
static T read(const MMIO& mmio_device, static T read(const MMIO& mmio_device, // NOLINTNEXTLINE
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept { is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>((mmio_device & mask) >> offset); const auto lhs = static_cast<T>(mmio_device & mask);
return static_cast<T>(lhs >> offset);
} }
template <typename U = void> template <typename U = void>
static T read(const MMIO& mmio_device, static T read(const MMIO& mmio_device, // NOLINTNEXTLINE
is_trivial<T, mask, offset, U>* = nullptr) noexcept { is_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>(mmio_device); return static_cast<T>(mmio_device);
} }
@@ -156,14 +163,16 @@ template <typename MMIO, typename T, T mask, FieldOffset offset>
struct RegisterWrite { struct RegisterWrite {
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device,
T value, T value, // NOLINTNEXTLINE
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept { is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = const auto shifted_value = static_cast<T>(value << offset);
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask)); const auto lhs = static_cast<T>(mmio_device & static_cast<T>(~mask));
const auto rhs = static_cast<T>(shifted_value & mask);
mmio_device = static_cast<T>(lhs | rhs);
} }
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device,
T value, T value, // NOLINTNEXTLINE
is_trivial<T, mask, offset, U>* = nullptr) noexcept { is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value; mmio_device = value;
} }
@@ -171,24 +180,26 @@ struct RegisterWrite {
template <typename MMIO, typename T, T mask, FieldOffset offset, T value> template <typename MMIO, typename T, T mask, FieldOffset offset, T value>
struct RegisterWriteConstant { struct RegisterWriteConstant {
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device, // NOLINTNEXTLINE
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept { is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = constexpr auto shifted_value = static_cast<T>(value << offset);
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask)); const auto lhs = static_cast<T>(mmio_device & static_cast<T>(~mask));
constexpr auto rhs = static_cast<T>(shifted_value & mask);
mmio_device = static_cast<T>(lhs | rhs);
} }
template <typename U = void> template <typename U = void>
static void write(MMIO& mmio_device, static void write(MMIO& mmio_device, // NOLINTNEXTLINE
is_trivial<T, mask, offset, U>* = nullptr) noexcept { is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value; mmio_device = value;
} }
}; };
struct read_only { struct read_only { // NOLINT
template <typename MMIO, typename T, T mask, FieldOffset offset> template <typename MMIO, typename T, T mask, FieldOffset offset>
static T read(const MMIO& mmio_device) noexcept { static T read(const MMIO& mmio_device) noexcept {
return RegisterRead<MMIO, T, mask, offset>::read(mmio_device); return RegisterRead<MMIO, T, mask, offset>::read(mmio_device);
} }
}; };
struct read_write : read_only { struct read_write : read_only { // NOLINT
template <typename MMIO, typename T, T mask, FieldOffset offset> template <typename MMIO, typename T, T mask, FieldOffset offset>
static void write(MMIO& mmio_device, const T value) noexcept { static void write(MMIO& mmio_device, const T value) noexcept {
RegisterWrite<MMIO, T, mask, offset>::write(mmio_device, value); RegisterWrite<MMIO, T, mask, offset>::write(mmio_device, value);
@@ -215,7 +226,7 @@ struct read_write : read_only {
mmio_device = static_cast<T>((mmio_device) ^ mask); mmio_device = static_cast<T>((mmio_device) ^ mask);
} }
}; };
struct write_only { struct write_only { // NOLINT
template <typename MMIO, typename T, T mask, FieldOffset offset> template <typename MMIO, typename T, T mask, FieldOffset offset>
static void write(MMIO& mmio_device, const T value) noexcept { static void write(MMIO& mmio_device, const T value) noexcept {
RegisterWrite<MMIO, T, type_mask<T>::value, FieldOffset{0}>::write( RegisterWrite<MMIO, T, type_mask<T>::value, FieldOffset{0}>::write(
@@ -239,9 +250,13 @@ struct write_only {
namespace cppreg { namespace cppreg {
template <typename Mask> template <typename Mask>
constexpr Mask make_mask(const FieldWidth width) noexcept { constexpr Mask make_mask(const FieldWidth width) noexcept {
return width == 0 ? static_cast<Mask>(0u) return width == 0U
: static_cast<Mask>( ? static_cast<Mask>(0U)
(make_mask<Mask>(FieldWidth(width - 1)) << 1) | 1); : static_cast<Mask>(
static_cast<Mask>(make_mask<Mask>(FieldWidth(
static_cast<FieldWidth>(width - 1U)))
<< 1U)
| 1U);
} }
template <typename Mask> template <typename Mask>
constexpr Mask make_shifted_mask(const FieldWidth width, constexpr Mask make_shifted_mask(const FieldWidth width,
@@ -274,18 +289,19 @@ template <typename Register,
typename Register::type mask, typename Register::type mask,
FieldOffset offset, FieldOffset offset,
typename Register::type value> typename Register::type value>
class MergeWrite_tmpl { class MergeWrite_tmpl { // NOLINT
private: private:
using base_type = typename Register::type; using base_type = typename Register::type; // NOLINT
constexpr static auto _accumulated_value = constexpr static auto _accumulated_value = // NOLINT
base_type{(value << offset) & mask}; base_type{(value << offset) & mask};
constexpr static auto _combined_mask = mask; constexpr static auto _combined_mask = mask; // NOLINT
template <typename F, base_type new_value> template <typename F, base_type new_value>
using propagated = using propagated = // NOLINT
MergeWrite_tmpl<Register, MergeWrite_tmpl<Register,
(_combined_mask | F::mask), (_combined_mask | F::mask),
FieldOffset{0}, FieldOffset{0},
(_accumulated_value & ~F::mask) (_accumulated_value
& static_cast<typename Register::type>(~F::mask))
| ((new_value << F::offset) & F::mask)>; | ((new_value << F::offset) & F::mask)>;
MergeWrite_tmpl() = default; MergeWrite_tmpl() = default;
static_assert(!Register::shadow::value, static_assert(!Register::shadow::value,
@@ -294,11 +310,12 @@ public:
static MergeWrite_tmpl create() noexcept { static MergeWrite_tmpl create() noexcept {
return {}; return {};
} }
~MergeWrite_tmpl() = default;
MergeWrite_tmpl(MergeWrite_tmpl&&) noexcept = default;
MergeWrite_tmpl(const MergeWrite_tmpl&) = delete; MergeWrite_tmpl(const MergeWrite_tmpl&) = delete;
MergeWrite_tmpl& operator=(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 operator=(MergeWrite_tmpl) = delete; MergeWrite_tmpl operator=(MergeWrite_tmpl) = delete;
MergeWrite_tmpl(MergeWrite_tmpl&&) = delete;
void done() const&& noexcept { void done() const&& noexcept {
typename Register::MMIO& mmio_device = Register::rw_mem_device(); typename Register::MMIO& mmio_device = Register::rw_mem_device();
RegisterWriteConstant<typename Register::MMIO, RegisterWriteConstant<typename Register::MMIO,
@@ -308,7 +325,7 @@ public:
_accumulated_value>::write(mmio_device); _accumulated_value>::write(mmio_device);
} }
template <typename F, base_type field_value> template <typename F, base_type field_value>
propagated<F, field_value>&& with() const&& noexcept { propagated<F, field_value> with() const&& noexcept {
static_assert( static_assert(
std::is_same<typename F::parent_register, Register>::value, std::is_same<typename F::parent_register, Register>::value,
"MergeWrite_tmpl:: field is not from the same register"); "MergeWrite_tmpl:: field is not from the same register");
@@ -318,17 +335,18 @@ public:
(F::mask >> F::offset)>::value; (F::mask >> F::offset)>::value;
static_assert(no_overflow, static_assert(no_overflow,
"MergeWrite_tmpl:: field overflow in with() call"); "MergeWrite_tmpl:: field overflow in with() call");
return std::move(propagated<F, field_value>{}); return propagated<F, field_value>{};
} }
}; };
template <typename Register, typename Register::type mask> template <typename Register, typename Register::type mask>
class MergeWrite { class MergeWrite {
private: private:
using base_type = typename Register::type; using base_type = typename Register::type; // NOLINT
base_type _accumulated_value; base_type _accumulated_value; // NOLINT
constexpr static auto _combined_mask = mask; constexpr static auto _combined_mask = mask; // NOLINT
template <typename F> template <typename F>
using propagated = MergeWrite<Register, _combined_mask | F::mask>; using propagated = // NOLINT
MergeWrite<Register, _combined_mask | F::mask>;
constexpr MergeWrite() : _accumulated_value{0} {}; constexpr MergeWrite() : _accumulated_value{0} {};
constexpr explicit MergeWrite(const base_type v) : _accumulated_value{v} {}; constexpr explicit MergeWrite(const base_type v) : _accumulated_value{v} {};
static_assert(!Register::shadow::value, static_assert(!Register::shadow::value,
@@ -337,6 +355,7 @@ public:
constexpr static MergeWrite create(const base_type value) noexcept { constexpr static MergeWrite create(const base_type value) noexcept {
return MergeWrite(value); return MergeWrite(value);
} }
~MergeWrite() = default;
MergeWrite(MergeWrite&& mw) noexcept MergeWrite(MergeWrite&& mw) noexcept
: _accumulated_value{mw._accumulated_value} {}; : _accumulated_value{mw._accumulated_value} {};
MergeWrite(const MergeWrite&) = delete; MergeWrite(const MergeWrite&) = delete;
@@ -354,9 +373,11 @@ public:
static_assert( static_assert(
std::is_same<typename F::parent_register, Register>::value, std::is_same<typename F::parent_register, Register>::value,
"field is not from the same register in merge_write"); "field is not from the same register in merge_write");
const auto new_value = static_cast<base_type>( constexpr auto neg_mask = static_cast<base_type>(~F::mask);
(_accumulated_value & ~F::mask) | ((value << F::offset) & F::mask)); const auto shifted_value = static_cast<base_type>(value << F::offset);
return std::move(propagated<F>::create(new_value)); const auto lhs = static_cast<base_type>(_accumulated_value & neg_mask);
const auto rhs = static_cast<base_type>(shifted_value & F::mask);
return propagated<F>::create(static_cast<base_type>(lhs | rhs));
} }
}; };
} }
@@ -371,26 +392,27 @@ template <Address reg_address,
typename TypeTraits<reg_size>::type reset_value = 0x0, typename TypeTraits<reg_size>::type reset_value = 0x0,
bool use_shadow = false> bool use_shadow = false>
struct Register { struct Register {
using type = typename TypeTraits<reg_size>::type; using type = typename TypeTraits<reg_size>::type; // NOLINT
using MMIO = volatile type; using MMIO = volatile type;
using shadow = Shadow<Register, use_shadow>; using shadow = Shadow<Register, use_shadow>; // NOLINT
constexpr static auto base_address = reg_address; constexpr static auto base_address = reg_address;
constexpr static auto size = TypeTraits<reg_size>::bit_size; constexpr static auto size = TypeTraits<reg_size>::bit_size;
constexpr static auto reset = reset_value; constexpr static auto reset = reset_value;
using pack = RegisterPack<base_address, size / 8u>; using pack = RegisterPack<base_address, size / one_byte>; // NOLINT
static MMIO& rw_mem_device() { static MMIO& rw_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device; using MemDevice = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template rw_memory<reg_size, 0>(); return MemDevice::template rw_memory<reg_size, 0>();
} }
static const MMIO& ro_mem_device() { static const MMIO& ro_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device; using MemDevice = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template ro_memory<reg_size, 0>(); return MemDevice::template ro_memory<reg_size, 0>();
} }
template <typename F> template <typename F>
static MergeWrite<typename F::parent_register, F::mask> merge_write( static MergeWrite<typename F::parent_register, F::mask> merge_write(
const typename F::type value) noexcept { const typename F::type value) noexcept {
const auto lhs = static_cast<type>(value << F::offset);
return MergeWrite<typename F::parent_register, F::mask>::create( return MergeWrite<typename F::parent_register, F::mask>::create(
static_cast<type>((value << F::offset) & F::mask)); static_cast<type>(lhs & F::mask));
} }
template <typename F, template <typename F,
type value, type value,
@@ -398,14 +420,14 @@ struct Register {
F::mask, F::mask,
F::offset, F::offset,
value>> value>>
static T&& merge_write() noexcept { static T merge_write() noexcept {
static_assert( static_assert(
internals::check_overflow<type, value, (F::mask >> F::offset)>:: internals::check_overflow<type, value, (F::mask >> F::offset)>::
value, value,
"Register::merge_write<value>:: value too large for the field"); "Register::merge_write<value>:: value too large for the field");
return std::move(T::create()); return T::create();
} }
static_assert(size != 0u, "Register:: register definition with zero size"); static_assert(size != 0, "Register:: register definition with zero size");
static_assert(internals::is_aligned<reg_address, static_assert(internals::is_aligned<reg_address,
TypeTraits<reg_size>::byte_size>::value, TypeTraits<reg_size>::byte_size>::value,
"Register:: address is mis-aligned for register type"); "Register:: address is mis-aligned for register type");
@@ -422,26 +444,30 @@ template <typename RegisterPack,
std::uint32_t bit_offset, std::uint32_t bit_offset,
typename TypeTraits<reg_size>::type reset_value = 0x0, typename TypeTraits<reg_size>::type reset_value = 0x0,
bool use_shadow = false> bool use_shadow = false>
struct PackedRegister : Register<RegisterPack::pack_base + (bit_offset / 8u), struct PackedRegister
reg_size, : Register<RegisterPack::pack_base + (bit_offset / one_byte),
reset_value, reg_size,
use_shadow> { reset_value,
using pack = RegisterPack; use_shadow> {
using base_reg = Register<RegisterPack::pack_base + (bit_offset / 8u), using pack = RegisterPack; // NOLINT
reg_size, using base_reg = // NOLINT
reset_value, Register<RegisterPack::pack_base + (bit_offset / one_byte),
use_shadow>; reg_size,
reset_value,
use_shadow>;
static typename base_reg::MMIO& rw_mem_device() noexcept { static typename base_reg::MMIO& rw_mem_device() noexcept {
using mem_device = using MemDevice =
typename RegisterMemoryDevice<RegisterPack>::mem_device; typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template rw_memory<reg_size, (bit_offset / 8u)>(); return MemDevice::template rw_memory<reg_size,
(bit_offset / one_byte)>();
} }
static const typename base_reg::MMIO& ro_mem_device() noexcept { static const typename base_reg::MMIO& ro_mem_device() noexcept {
using mem_device = using MemDevice =
typename RegisterMemoryDevice<RegisterPack>::mem_device; typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template ro_memory<reg_size, (bit_offset / 8u)>(); return MemDevice::template ro_memory<reg_size,
(bit_offset / one_byte)>();
} }
static_assert(TypeTraits<reg_size>::byte_size + (bit_offset / 8u) static_assert(TypeTraits<reg_size>::byte_size + (bit_offset / one_byte)
<= RegisterPack::size_in_bytes, <= RegisterPack::size_in_bytes,
"PackRegister:: packed register is overflowing the pack"); "PackRegister:: packed register is overflowing the pack");
static_assert( static_assert(
@@ -449,32 +475,33 @@ struct PackedRegister : Register<RegisterPack::pack_base + (bit_offset / 8u),
TypeTraits<reg_size>::byte_size>::value, TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: pack base address is mis-aligned for register type"); "PackedRegister:: pack base address is mis-aligned for register type");
static_assert( static_assert(
internals::is_aligned<RegisterPack::pack_base + (bit_offset / 8u), internals::is_aligned<RegisterPack::pack_base + (bit_offset / one_byte),
TypeTraits<reg_size>::byte_size>::value, TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: offset address is mis-aligned for register type"); "PackedRegister:: offset address is mis-aligned for register type");
}; };
template <typename... T> template <typename... T>
struct PackIndexing { struct PackIndexing {
using tuple_t = typename std::tuple<T...>; using tuple_t = typename std::tuple<T...>; // NOLINT
constexpr static const std::size_t n_elems = constexpr static const std::size_t n_elems =
std::tuple_size<tuple_t>::value; std::tuple_size<tuple_t>::value;
template <std::size_t N> template <std::size_t n>
using elem = typename std::tuple_element<N, tuple_t>::type; using elem = typename std::tuple_element<n, tuple_t>::type; // NOLINT
}; };
template <std::size_t start, std::size_t end> template <std::size_t start, std::size_t end>
struct for_loop { struct for_loop { // NOLINT
template <typename Func> template <typename Func>
static void apply() noexcept { static void apply() noexcept {
Func().template operator()<start>(); Func().template operator()<start>();
if (start < end) if (start < end) {
for_loop<start + 1ul, end>::template apply<Func>(); for_loop<start + 1, end>::template apply<Func>();
}
} }
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
template <typename Op> template <typename Op>
static void apply(Op&& f) noexcept { static void apply(Op&& f) noexcept {
if (start < end) { if (start < end) {
f(std::integral_constant<std::size_t, start>{}); f(std::integral_constant<std::size_t, start>{});
for_loop<start + 1ul, end>::apply(std::forward<Op>(f)); for_loop<start + 1, end>::apply(std::forward<Op>(f));
}; };
} }
#endif #endif
@@ -485,11 +512,11 @@ struct for_loop<end, end> {
static void apply() noexcept {} static void apply() noexcept {}
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
template <typename Op> template <typename Op>
static void apply(Op&& f) noexcept {} static void apply(Op&&) noexcept {} // NOLINT
#endif #endif
}; };
template <typename IndexedPack> template <typename IndexedPack>
struct pack_loop : for_loop<0, IndexedPack::n_elems> {}; struct pack_loop : for_loop<0, IndexedPack::n_elems> {}; // NOLINT
} }
#endif #endif
@@ -502,18 +529,18 @@ template <typename BaseRegister,
FieldOffset field_offset, FieldOffset field_offset,
typename AccessPolicy> typename AccessPolicy>
struct Field { struct Field {
using parent_register = BaseRegister; using parent_register = BaseRegister; // NOLINT
using type = typename parent_register::type; using type = typename parent_register::type; // NOLINT
using MMIO = typename parent_register::MMIO; using MMIO = typename parent_register::MMIO; // NOLINT
using policy = AccessPolicy; using policy = AccessPolicy; // NOLINT
constexpr static auto width = field_width; constexpr static auto width = field_width;
constexpr static auto offset = field_offset; constexpr static auto offset = field_offset;
constexpr static auto mask = make_shifted_mask<type>(width, offset); constexpr static auto mask = make_shifted_mask<type>(width, offset);
template <type value, typename T> template <type value, typename T>
using if_no_shadow = using if_no_shadow = // NOLINT
typename std::enable_if<!parent_register::shadow::value, T>::type; typename std::enable_if<!parent_register::shadow::value, T>::type;
template <type value, typename T> template <type value, typename T>
using if_shadow = using if_shadow = // NOLINT
typename std::enable_if<parent_register::shadow::value, T>::type; typename std::enable_if<parent_register::shadow::value, T>::type;
static type read() noexcept { static type read() noexcept {
return policy::template read<MMIO, type, mask, offset>( return policy::template read<MMIO, type, mask, offset>(
@@ -534,7 +561,7 @@ struct Field {
parent_register::shadow::shadow_value); parent_register::shadow::shadow_value);
} }
template <type value, typename T = void> template <type value, typename T = void>
static void write(if_no_shadow<value, T>* = nullptr) noexcept { static void write(if_no_shadow<value, T>* = nullptr) noexcept { // NOLINT
policy::template write<MMIO, type, mask, offset, value>( policy::template write<MMIO, type, mask, offset, value>(
parent_register::rw_mem_device()); parent_register::rw_mem_device());
static_assert( static_assert(
@@ -542,7 +569,7 @@ struct Field {
"Field::write<value>: value too large for the field"); "Field::write<value>: value too large for the field");
} }
template <type value, typename T = void> template <type value, typename T = void>
static void write(if_shadow<value, T>* = nullptr) noexcept { static void write(if_shadow<value, T>* = nullptr) noexcept { // NOLINT
write(value); write(value);
static_assert( static_assert(
internals::check_overflow<type, value, (mask >> offset)>::value, internals::check_overflow<type, value, (mask >> offset)>::value,
@@ -574,5 +601,6 @@ struct Field {
"Field:: defining a Field type of zero width is not allowed"); "Field:: defining a Field type of zero width is not allowed");
}; };
} }
#endif #endif
/* clang-format on */