mirror of
https://github.com/sendyne/cppreg.git
synced 2025-05-09 23:24:05 +00:00
ADD MIXED REGISTER SIZES SUPPORT TO REGISTER PACK
This relies on a memory map implementation based on std::array and specialized over the register size. PackedRegister can now be used with all supported register sizes (i.e., widths). This relates to #7 and the finalization of the API before performance testing.
This commit is contained in:
parent
ae6da9b7f5
commit
bbdf513156
7
cppreg.h
7
cppreg.h
@ -10,10 +10,11 @@
|
|||||||
#define CPPREG_CPPREG_H
|
#define CPPREG_CPPREG_H
|
||||||
|
|
||||||
|
|
||||||
#include "Register.h"
|
|
||||||
#include "Field.h"
|
|
||||||
#include "MergeWrite.h"
|
|
||||||
#include "Overflow.h"
|
#include "Overflow.h"
|
||||||
|
#include "MergeWrite.h"
|
||||||
|
#include "Register.h"
|
||||||
|
#include "RegisterPack.h"
|
||||||
|
#include "Field.h"
|
||||||
|
|
||||||
|
|
||||||
#endif // CPPREG_CPPREG_H
|
#endif // CPPREG_CPPREG_H
|
||||||
|
@ -23,10 +23,10 @@ namespace cppreg {
|
|||||||
|
|
||||||
//! Register data structure.
|
//! Register data structure.
|
||||||
/**
|
/**
|
||||||
* @tparam address Register address.
|
* @tparam RegAddress Register address.
|
||||||
* @tparam width Register total width (i.e., size).
|
* @tparam RegWidth Register total width (i.e., size).
|
||||||
* @tparam reset Register reset value (0x0 if unknown).
|
* @tparam ResetValue Register reset value (0x0 if unknown).
|
||||||
* @tparam shadow Boolean flag to enable shadow value (enabled if `true`).
|
* @tparam UseShadow shadow Boolean flag to enable shadow value.
|
||||||
*
|
*
|
||||||
* This data structure will act as a container for fields and is
|
* This data structure will act as a container for fields and is
|
||||||
* therefore limited to a strict minimum. It only carries information
|
* therefore limited to a strict minimum. It only carries information
|
||||||
|
@ -19,90 +19,108 @@
|
|||||||
namespace cppreg {
|
namespace cppreg {
|
||||||
|
|
||||||
|
|
||||||
|
//! Memory map implementation.
|
||||||
|
/**
|
||||||
|
* @tparam Address Memory base address.
|
||||||
|
* @tparam N Memory size in bytes.
|
||||||
|
* @tparam W Width in bits of the memory "elements".
|
||||||
|
*
|
||||||
|
* This structure is used to map an array structure onto a memory region.
|
||||||
|
* The width parameter will correspond to the register size.
|
||||||
|
*/
|
||||||
|
template <Address_t Address, std::uint32_t N, Width_t W>
|
||||||
|
struct memory_map {
|
||||||
|
|
||||||
|
//! Array type.
|
||||||
|
using mem_array_t = std::array<
|
||||||
|
volatile typename RegisterType<W>::type,
|
||||||
|
N / sizeof(typename RegisterType<W>::type)
|
||||||
|
>;
|
||||||
|
|
||||||
|
//! Static reference to memory array.
|
||||||
|
/**
|
||||||
|
* This is defined below.
|
||||||
|
*/
|
||||||
|
static mem_array_t& array;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Memory array reference definition.
|
||||||
|
template <Address_t Address, std::uint32_t N, Width_t W>
|
||||||
|
typename memory_map<Address, N, W>::mem_array_t&
|
||||||
|
memory_map<Address, N, W>::array = *(
|
||||||
|
reinterpret_cast<typename memory_map<Address, N, W>::mem_array_t*>(
|
||||||
|
Address
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
//! Register pack base implementation.
|
//! Register pack base implementation.
|
||||||
/**
|
/**
|
||||||
|
* @tparam PackBase Pack base address.
|
||||||
|
* @tparam PackByteSize Pack size in bytes.
|
||||||
*/
|
*/
|
||||||
template <
|
template <
|
||||||
Address_t PackBase,
|
Address_t PackBase,
|
||||||
Width_t RegWidth,
|
std::uint32_t PackByteSize
|
||||||
std::uint32_t N
|
|
||||||
> struct RegisterPack {
|
> struct RegisterPack {
|
||||||
|
|
||||||
//! Pack register type.
|
|
||||||
using register_type = typename RegisterType<RegWidth>::type;
|
|
||||||
|
|
||||||
//! Type alias for byte array.
|
|
||||||
using mem_array_t =std::array<volatile register_type, N>;
|
|
||||||
|
|
||||||
//! Base address.
|
//! Base address.
|
||||||
constexpr static const Address_t pack_base = PackBase;
|
constexpr static const Address_t pack_base = PackBase;
|
||||||
|
|
||||||
//! Pack size in bytes.
|
//! Pack size in bytes.
|
||||||
constexpr static const std::uint32_t size_in_bytes =
|
constexpr static const std::uint32_t size_in_bytes = PackByteSize;
|
||||||
N * sizeof(register_type);
|
|
||||||
|
|
||||||
//! Register width in the pack.
|
|
||||||
constexpr static const Width_t register_width = RegWidth;
|
|
||||||
|
|
||||||
//! Reference to the byte array.
|
|
||||||
/**
|
|
||||||
* This is explicitly defined below.
|
|
||||||
*/
|
|
||||||
static mem_array_t& _mem_array;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! RegisterPack byte array reference definition.
|
|
||||||
template <
|
|
||||||
Address_t PackBase,
|
|
||||||
Width_t RegWidth,
|
|
||||||
std::uint32_t N
|
|
||||||
>
|
|
||||||
typename RegisterPack<PackBase, RegWidth, N>::mem_array_t&
|
|
||||||
RegisterPack<PackBase, RegWidth, N>::_mem_array =
|
|
||||||
*(
|
|
||||||
reinterpret_cast<
|
|
||||||
RegisterPack<PackBase, RegWidth, N>::mem_array_t* const
|
|
||||||
>(
|
|
||||||
RegisterPack<PackBase, RegWidth, N>::pack_base
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
//! Packed register implementation.
|
//! Packed register implementation.
|
||||||
/**
|
/**
|
||||||
|
* @tparam RegisterPack Pack to which the register belongs.
|
||||||
|
* @tparam BitOffset Offset in bits for the register with respect to base.
|
||||||
|
* @tparam RegWidth Register width.
|
||||||
|
* @tparam ResetValue Register reset value (0x0 if unknown).
|
||||||
|
* @tparam UseShadow shadow Boolean flag to enable shadow value.
|
||||||
*
|
*
|
||||||
* This implementation is intended to be used when defining a register
|
* This implementation is intended to be used when defining a register
|
||||||
* that belongs to a type.
|
* that belongs to a peripheral group.
|
||||||
*/
|
*/
|
||||||
template <
|
template <
|
||||||
typename RegisterPack,
|
typename RegisterPack,
|
||||||
std::uint32_t OffsetInPack,
|
std::uint32_t BitOffset,
|
||||||
typename RegisterType<RegisterPack::register_width>::type ResetValue = 0x0,
|
Width_t RegWidth,
|
||||||
|
typename RegisterType<RegWidth>::type ResetValue = 0x0,
|
||||||
bool UseShadow = false
|
bool UseShadow = false
|
||||||
>
|
>
|
||||||
struct PackedRegister :
|
struct PackedRegister :
|
||||||
Register<
|
Register<
|
||||||
RegisterPack::pack_base + OffsetInPack,
|
RegisterPack::pack_base + (BitOffset / 8u),
|
||||||
RegisterPack::register_width,
|
RegWidth,
|
||||||
ResetValue,
|
ResetValue,
|
||||||
UseShadow
|
UseShadow
|
||||||
> {
|
> {
|
||||||
|
|
||||||
//! Register type.
|
//! Register type.
|
||||||
using base_reg = Register<
|
using base_reg = Register<
|
||||||
RegisterPack::pack_base + OffsetInPack,
|
RegisterPack::pack_base + (BitOffset / 8u),
|
||||||
RegisterPack::register_width,
|
RegWidth,
|
||||||
ResetValue,
|
ResetValue,
|
||||||
UseShadow
|
UseShadow
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
//! Memory map type.
|
||||||
|
using mem_map_t = memory_map<
|
||||||
|
RegisterPack::pack_base,
|
||||||
|
RegisterPack::size_in_bytes,
|
||||||
|
RegWidth
|
||||||
|
>;
|
||||||
|
|
||||||
//! 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_t& rw_mem_device() {
|
static typename base_reg::MMIO_t& rw_mem_device() {
|
||||||
return RegisterPack::_mem_array[OffsetInPack];
|
return mem_map_t::array[BitOffset / RegWidth];
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Memory accessor.
|
//! Memory accessor.
|
||||||
@ -110,14 +128,25 @@ namespace cppreg {
|
|||||||
* @return A reference to the read-only register memory.
|
* @return A reference to the read-only register memory.
|
||||||
*/
|
*/
|
||||||
static const typename base_reg::MMIO_t& ro_mem_device() {
|
static const typename base_reg::MMIO_t& ro_mem_device() {
|
||||||
return RegisterPack::_mem_array[OffsetInPack];
|
return mem_map_t::array[BitOffset / RegWidth];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safety check to detect if are overflowing the pack.
|
// Safety check to detect if are overflowing the pack.
|
||||||
static_assert((OffsetInPack + (RegisterPack::register_width / 8u)) <=
|
static_assert((BitOffset / 8u) <= RegisterPack::size_in_bytes,
|
||||||
RegisterPack::size_in_bytes,
|
|
||||||
"packed register is overflowing the pack");
|
"packed register is overflowing the pack");
|
||||||
|
|
||||||
|
// Safety check to detect if the register is mis-aligned.
|
||||||
|
// A register is mis-aligned if it its offset is not a multiple of
|
||||||
|
// its size and if the pack base address alignment is different from
|
||||||
|
// its type alignment.
|
||||||
|
static_assert((
|
||||||
|
(BitOffset % RegWidth) == 0
|
||||||
|
&&
|
||||||
|
(RegisterPack::pack_address % (RegWidth / 8u) == 0)
|
||||||
|
),
|
||||||
|
"register mis-alignment with respect to pack base");
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -137,22 +166,35 @@ namespace cppreg {
|
|||||||
|
|
||||||
|
|
||||||
//! Template for loop implementation.
|
//! Template for loop implementation.
|
||||||
|
/**
|
||||||
|
* @tparam start Start 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 {
|
||||||
|
|
||||||
|
//! Loop method.
|
||||||
|
/**
|
||||||
|
* @tparam Op Operation to be called at each iteration.
|
||||||
|
*
|
||||||
|
* This will call Op for the range [start, end).
|
||||||
|
*/
|
||||||
template <template <std::size_t> class Op, typename T = void>
|
template <template <std::size_t> class Op, typename T = void>
|
||||||
inline static void iterate(
|
inline static void loop(
|
||||||
typename std::enable_if<start < end, T>::type* = nullptr
|
typename std::enable_if<start < end, T>::type* = nullptr
|
||||||
) {
|
) {
|
||||||
Op<start>()();
|
Op<start>()();
|
||||||
if (start < end)
|
if (start < end)
|
||||||
for_loop<start + 1, end>::template iterate<Op>();
|
for_loop<start + 1, end>::template iterate<Op>();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Loop method closure.
|
||||||
template <template <std::size_t> class Op, typename T = void>
|
template <template <std::size_t> class Op, typename T = void>
|
||||||
inline static void iterate(
|
inline static void loop(
|
||||||
typename std::enable_if<start >= end, T>::type* = nullptr
|
typename std::enable_if<start >= end, T>::type* = nullptr
|
||||||
) {};
|
) {};
|
||||||
};
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -388,55 +388,67 @@ namespace cppreg {
|
|||||||
#ifndef CPPREG_REGISTERPACK_H
|
#ifndef CPPREG_REGISTERPACK_H
|
||||||
#define CPPREG_REGISTERPACK_H
|
#define CPPREG_REGISTERPACK_H
|
||||||
namespace cppreg {
|
namespace cppreg {
|
||||||
|
template <Address_t Address, std::uint32_t N, Width_t W>
|
||||||
|
struct memory_map {
|
||||||
|
using mem_array_t = std::array<
|
||||||
|
volatile typename RegisterType<W>::type,
|
||||||
|
N / sizeof(typename RegisterType<W>::type)
|
||||||
|
>;
|
||||||
|
static mem_array_t& array;
|
||||||
|
};
|
||||||
|
template <Address_t Address, std::uint32_t N, Width_t W>
|
||||||
|
typename memory_map<Address, N, W>::mem_array_t&
|
||||||
|
memory_map<Address, N, W>::array = *(
|
||||||
|
reinterpret_cast<typename memory_map<Address, N, W>::mem_array_t*>(
|
||||||
|
Address
|
||||||
|
)
|
||||||
|
);
|
||||||
template <
|
template <
|
||||||
Address_t PackBase,
|
Address_t PackBase,
|
||||||
std::uint32_t PackByteSize
|
std::uint32_t PackByteSize
|
||||||
> struct RegisterPack {
|
> struct RegisterPack {
|
||||||
using mem_array_t = std::array<volatile std::uint8_t, PackByteSize>;
|
|
||||||
constexpr static const Address_t pack_base = PackBase;
|
constexpr static const Address_t pack_base = PackBase;
|
||||||
constexpr static const std::uint32_t size_in_bytes = PackByteSize;
|
constexpr static const std::uint32_t size_in_bytes = PackByteSize;
|
||||||
static mem_array_t& _mem_array;
|
|
||||||
};
|
};
|
||||||
template <
|
|
||||||
Address_t PackBase,
|
|
||||||
std::uint32_t PackByteSize
|
|
||||||
>
|
|
||||||
typename RegisterPack<PackBase, PackByteSize>::mem_array_t&
|
|
||||||
RegisterPack<PackBase, PackByteSize>::_mem_array =
|
|
||||||
*(
|
|
||||||
reinterpret_cast<
|
|
||||||
RegisterPack<PackBase, PackByteSize>::mem_array_t* const
|
|
||||||
>(RegisterPack<PackBase, PackByteSize>::pack_base)
|
|
||||||
);
|
|
||||||
template <
|
template <
|
||||||
typename RegisterPack,
|
typename RegisterPack,
|
||||||
|
std::uint32_t BitOffset,
|
||||||
Width_t RegWidth,
|
Width_t RegWidth,
|
||||||
std::uint32_t OffsetInPack,
|
|
||||||
typename RegisterType<RegWidth>::type ResetValue = 0x0,
|
typename RegisterType<RegWidth>::type ResetValue = 0x0,
|
||||||
bool UseShadow = false
|
bool UseShadow = false
|
||||||
>
|
>
|
||||||
struct PackedRegister :
|
struct PackedRegister :
|
||||||
Register<
|
Register<
|
||||||
RegisterPack::pack_base + OffsetInPack * 8,
|
RegisterPack::pack_base + (BitOffset / 8u),
|
||||||
RegWidth,
|
RegWidth,
|
||||||
ResetValue,
|
ResetValue,
|
||||||
UseShadow
|
UseShadow
|
||||||
> {
|
> {
|
||||||
using base_reg = Register<
|
using base_reg = Register<
|
||||||
RegisterPack::pack_base + OffsetInPack * 8,
|
RegisterPack::pack_base + (BitOffset / 8u),
|
||||||
RegWidth,
|
RegWidth,
|
||||||
ResetValue,
|
ResetValue,
|
||||||
UseShadow
|
UseShadow
|
||||||
>;
|
>;
|
||||||
|
using mem_map_t = memory_map<
|
||||||
|
RegisterPack::pack_base,
|
||||||
|
RegisterPack::size_in_bytes,
|
||||||
|
RegWidth
|
||||||
|
>;
|
||||||
static typename base_reg::MMIO_t& rw_mem_device() {
|
static typename base_reg::MMIO_t& rw_mem_device() {
|
||||||
return RegisterPack::_mem_array[OffsetInPack];
|
return mem_map_t::array[BitOffset / RegWidth];
|
||||||
};
|
};
|
||||||
static const typename base_reg::MMIO_t& ro_mem_device() {
|
static const typename base_reg::MMIO_t& ro_mem_device() {
|
||||||
return RegisterPack::_mem_array[OffsetInPack];
|
return mem_map_t::array[BitOffset / RegWidth];
|
||||||
};
|
};
|
||||||
static_assert((OffsetInPack + (RegWidth / 8u)) <=
|
static_assert((BitOffset / 8u) <= RegisterPack::size_in_bytes,
|
||||||
RegisterPack::size_in_bytes,
|
|
||||||
"packed register is overflowing the pack");
|
"packed register is overflowing the pack");
|
||||||
|
static_assert((
|
||||||
|
(BitOffset % RegWidth) == 0
|
||||||
|
&&
|
||||||
|
(RegisterPack::pack_address % (RegWidth / 8u) == 0)
|
||||||
|
),
|
||||||
|
"register mis-alignment with respect to pack base");
|
||||||
};
|
};
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
struct PackIndexing {
|
struct PackIndexing {
|
||||||
@ -446,7 +458,7 @@ namespace cppreg {
|
|||||||
template <std::size_t start, std::size_t end>
|
template <std::size_t start, std::size_t end>
|
||||||
struct for_loop {
|
struct for_loop {
|
||||||
template <template <std::size_t> class Op, typename T = void>
|
template <template <std::size_t> class Op, typename T = void>
|
||||||
inline static void iterate(
|
inline static void loop(
|
||||||
typename std::enable_if<start < end, T>::type* = nullptr
|
typename std::enable_if<start < end, T>::type* = nullptr
|
||||||
) {
|
) {
|
||||||
Op<start>()();
|
Op<start>()();
|
||||||
@ -454,7 +466,7 @@ namespace cppreg {
|
|||||||
for_loop<start + 1, end>::template iterate<Op>();
|
for_loop<start + 1, end>::template iterate<Op>();
|
||||||
};
|
};
|
||||||
template <template <std::size_t> class Op, typename T = void>
|
template <template <std::size_t> class Op, typename T = void>
|
||||||
inline static void iterate(
|
inline static void loop(
|
||||||
typename std::enable_if<start >= end, T>::type* = nullptr
|
typename std::enable_if<start >= end, T>::type* = nullptr
|
||||||
) {};
|
) {};
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user