1
0
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:
Nicolas Clauvelin 2018-03-13 08:47:47 -04:00
parent ae6da9b7f5
commit bbdf513156
4 changed files with 139 additions and 84 deletions

View File

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

View File

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

View File

@ -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>();
}; };
template <template <std::size_t> class Op, typename T = void>
inline static void iterate(
typename std::enable_if<start >= end, T>::type* = nullptr
) {};
};
//! Loop method closure.
template <template <std::size_t> class Op, typename T = void>
inline static void loop(
typename std::enable_if<start >= end, T>::type* = nullptr
) {};
};
} }

View File

@ -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,17 +458,17 @@ 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>()();
if (start < end) if (start < end)
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
) {}; ) {};
}; };
} }
#endif #endif