mirror of
https://github.com/sendyne/cppreg.git
synced 2026-01-19 17:02:10 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cb2714b4b | ||
|
|
42bd134067 | ||
|
|
eb166f16e2 | ||
|
|
263bd8913d |
12
API.md
12
API.md
@@ -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:
|
||||||
|
|||||||
@@ -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::)
|
||||||
|
|||||||
2
cppreg.h
2
cppreg.h
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user