1
0
mirror of https://github.com/sendyne/cppreg.git synced 2025-05-09 23:24:05 +00:00

ADD ENUMERATION TYPE FOR SUPPORTED REGISTER SIZES

Register sizes are now represented by an enumeration type. This limits the
possibility to typeset unsupported register sizes, and make the interface easier
to read.

See #12.
This commit is contained in:
Nicolas Clauvelin 2018-03-15 17:13:54 -04:00
parent c217f34e0c
commit 95288090d4
10 changed files with 278 additions and 178 deletions

View File

@ -28,13 +28,23 @@ namespace cppreg {
using Address_t = std::uintptr_t; using Address_t = std::uintptr_t;
//! Type alias for register and field widths. //! Enumeration type for register size in bits.
/** /**
* This limit the implementation to a maximum size of 256 bits for any * This is used to enforce the supported register sizes.
* register or field.
* This corresponds to the number of bits in the a register or field.
*/ */
using Width_t = std::uint8_t; enum class RegBitSize {
b8, //!< 8-bit register.
b16, //!< 16-bit register.
b32 //!< 32-bit register.
};
// //! Type alias for register and field widths.
// /**
// * This limit the implementation to a maximum size of 256 bits for any
// * register or field.
// * This corresponds to the number of bits in the a register or field.
// */
using FieldWidth_t = std::uint8_t;
//! Type alias for register and field offsets. //! Type alias for register and field offsets.
@ -44,7 +54,7 @@ namespace cppreg {
* Given that we consider 32 bits address space and 32 bits register this * Given that we consider 32 bits address space and 32 bits register this
* should be enough. * should be enough.
*/ */
using Offset_t = std::uint8_t; using FieldOffset_t = std::uint8_t;
//! Shorthand for max value as a mask. //! Shorthand for max value as a mask.

View File

@ -41,7 +41,7 @@ namespace cppreg {
* The mask and offset are used to define a specific field within the * The mask and offset are used to define a specific field within the
* register. * register.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
struct RegisterRead { struct RegisterRead {
//! Boolean flag for trivial implementation. //! Boolean flag for trivial implementation.
@ -94,7 +94,7 @@ namespace cppreg {
* This write implementation is used only when the value to be written is * This write implementation is used only when the value to be written is
* not a constant expression. * not a constant expression.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
struct RegisterWrite { struct RegisterWrite {
//! Boolean flag for trivial implementation. //! Boolean flag for trivial implementation.
@ -152,7 +152,9 @@ namespace cppreg {
* This write implementation is used only when the value to be written is * This write implementation is used only when the value to be written is
* a constant expression. * a constant expression.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value> template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
struct RegisterWriteConstant { struct RegisterWriteConstant {
//! Boolean flag for trivial implementation. //! Boolean flag for trivial implementation.
@ -204,7 +206,7 @@ namespace cppreg {
* @param mmio_device Pointer to register mapped memory. * @param mmio_device Pointer to register mapped memory.
* @return The value at the field location. * @return The value at the field location.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
inline static T read(const MMIO_t& mmio_device) noexcept { inline static T read(const MMIO_t& mmio_device) noexcept {
return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device); return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device);
}; };
@ -224,7 +226,7 @@ namespace cppreg {
* @param mmio_device Pointer to register mapped memory. * @param mmio_device Pointer to register mapped memory.
* @param value Value to be written at the field location. * @param value Value to be written at the field location.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
inline static void write(MMIO_t& mmio_device, inline static void write(MMIO_t& mmio_device,
const T value) noexcept { const T value) noexcept {
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value); RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
@ -239,7 +241,9 @@ namespace cppreg {
* @tparam value Value to be written at the field location. * @tparam value Value to be written at the field location.
* @param mmio_device Pointer to register mapped memory. * @param mmio_device Pointer to register mapped memory.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value> template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
inline static void write(MMIO_t& mmio_device) noexcept { inline static void write(MMIO_t& mmio_device) noexcept {
RegisterWriteConstant<MMIO_t, T, mask, offset, value> RegisterWriteConstant<MMIO_t, T, mask, offset, value>
::write(mmio_device); ::write(mmio_device);
@ -300,7 +304,7 @@ namespace cppreg {
* @param mmio_device Pointer to register mapped memory. * @param mmio_device Pointer to register mapped memory.
* @param value Value to be written at the field location. * @param value Value to be written at the field location.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
inline static void write(MMIO_t& mmio_device, inline static void write(MMIO_t& mmio_device,
const T value) noexcept { const T value) noexcept {
@ -319,7 +323,9 @@ namespace cppreg {
* @tparam value Value to be written at the field location. * @tparam value Value to be written at the field location.
* @param mmio_device Pointer to register mapped memory. * @param mmio_device Pointer to register mapped memory.
*/ */
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value> template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
inline static void write(MMIO_t& mmio_device) noexcept { inline static void write(MMIO_t& mmio_device) noexcept {
// For write-only fields we can only write to the whole register. // For write-only fields we can only write to the whole register.

View File

@ -39,8 +39,8 @@ namespace cppreg {
*/ */
template < template <
typename BaseRegister, typename BaseRegister,
Width_t FieldWidth, FieldWidth_t field_width,
Offset_t FieldOffset, FieldOffset_t field_offset,
typename AccessPolicy typename AccessPolicy
> >
struct Field { struct Field {
@ -58,10 +58,10 @@ namespace cppreg {
using policy = AccessPolicy; using policy = AccessPolicy;
//! Field width. //! Field width.
constexpr static const Width_t width = FieldWidth; constexpr static const FieldWidth_t width = field_width;
//! Field offset. //! Field offset.
constexpr static const Offset_t offset = FieldOffset; constexpr static const FieldOffset_t offset = field_offset;
//! Field mask. //! Field mask.
constexpr static const type mask = make_shifted_mask<type>(width, constexpr static const type mask = make_shifted_mask<type>(width,
@ -80,7 +80,7 @@ namespace cppreg {
*/ */
template <type value> template <type value>
struct check_overflow : internals::check_overflow< struct check_overflow : internals::check_overflow<
parent_register::size, type,
value, value,
(mask >> offset) (mask >> offset)
> {}; > {};
@ -226,7 +226,7 @@ namespace cppreg {
"field width is larger than parent register size"); "field width is larger than parent register size");
static_assert(parent_register::size >= width + offset, static_assert(parent_register::size >= width + offset,
"offset + width is larger than parent register size"); "offset + width is larger than parent register size");
static_assert(FieldWidth != 0u, static_assert(width != 0u,
"defining a Field type of width 0u is not allowed"); "defining a Field type of width 0u is not allowed");
}; };

View File

@ -28,12 +28,12 @@ namespace cppreg {
* @return The mask value. * @return The mask value.
*/ */
template <typename Mask_t> template <typename Mask_t>
constexpr Mask_t make_mask(const Width_t width) noexcept { constexpr Mask_t make_mask(const FieldWidth_t width) noexcept {
return width == 0 ? return width == 0 ?
0u 0u
: :
static_cast<Mask_t>( static_cast<Mask_t>(
(make_mask<Mask_t>(Width_t(width - 1)) << 1) | 1 (make_mask<Mask_t>(FieldWidth_t(width - 1)) << 1) | 1
); );
}; };
@ -46,8 +46,8 @@ namespace cppreg {
* @return The mask value. * @return The mask value.
*/ */
template <typename Mask_t> template <typename Mask_t>
constexpr Mask_t make_shifted_mask(const Width_t width, constexpr Mask_t make_shifted_mask(const FieldWidth_t width,
const Offset_t offset) noexcept { const FieldOffset_t offset) noexcept {
return static_cast<Mask_t>(make_mask<Mask_t>(width) << offset); return static_cast<Mask_t>(make_mask<Mask_t>(width) << offset);
}; };

View File

@ -46,7 +46,7 @@ namespace cppreg {
template < template <
typename Register, typename Register,
typename Register::type mask, typename Register::type mask,
Offset_t offset, FieldOffset_t offset,
typename Register::type value typename Register::type value
> class MergeWrite_tmpl { > class MergeWrite_tmpl {
@ -130,7 +130,7 @@ namespace cppreg {
inline inline
typename std::enable_if< typename std::enable_if<
(internals::check_overflow< (internals::check_overflow<
Register::size, new_value, (F::mask >> F::offset) typename Register::type, new_value, (F::mask >> F::offset)
>::value), >::value),
T T
>::type&& >::type&&

View File

@ -21,7 +21,7 @@ namespace internals {
//! Overflow check implementation. //! Overflow check implementation.
/** /**
* @tparam W Width of the register or field type. * @tparam T Data type.
* @tparam value Value to check. * @tparam value Value to check.
* @tparam limit Overflow limit value. * @tparam limit Overflow limit value.
* *
@ -30,9 +30,9 @@ namespace internals {
* There is overflow if value if strictly larger than limit. * There is overflow if value if strictly larger than limit.
*/ */
template < template <
Width_t W, typename T,
typename RegisterType<W>::type value, T value,
typename RegisterType<W>::type limit T limit
> >
struct check_overflow : std::integral_constant<bool, value <= limit> {}; struct check_overflow : std::integral_constant<bool, value <= limit> {};

View File

@ -23,8 +23,8 @@ namespace cppreg {
//! Register data structure. //! Register data structure.
/** /**
* @tparam RegAddress Register address. * @tparam reg_address Register address.
* @tparam RegWidth Register total width (i.e., size). * @tparam reg_size Register size enum value.
* @tparam ResetValue Register reset value (0x0 if unknown). * @tparam ResetValue Register reset value (0x0 if unknown).
* @tparam UseShadow shadow Boolean flag to enable shadow value. * @tparam UseShadow shadow Boolean flag to enable shadow value.
* *
@ -35,30 +35,31 @@ namespace cppreg {
* create custom types. * create custom types.
*/ */
template < template <
Address_t RegAddress, Address_t reg_address,
Width_t RegWidth, RegBitSize reg_size,
typename RegisterType<RegWidth>::type ResetValue = 0x0, typename TypeTraits<reg_size>::type reset_value = 0x0,
bool UseShadow = false bool use_shadow = false
> >
struct Register { struct Register {
//! Register base type. //! Register base type.
using type = typename RegisterType<RegWidth>::type; using type = typename TypeTraits<reg_size>::type;
//! MMIO pointer type. //! MMIO pointer type.
using MMIO_t = volatile type; using MMIO_t = volatile type;
//! Register base address. //! Boolean flag for shadow value management.
constexpr static const Address_t base_address = RegAddress; using shadow = Shadow<Register, use_shadow>;
//! Register total width. //! Register base address.
constexpr static const Width_t size = RegWidth; constexpr static const Address_t base_address = reg_address;
//! Register size in bits.
constexpr static const std::uint8_t size =
TypeTraits<reg_size>::bit_size;
//! Register reset value. //! Register reset value.
constexpr static const type reset = ResetValue; constexpr static const type reset = reset_value;
//! Boolean flag for shadow value management.
using shadow = Shadow<Register, UseShadow>;
//! Memory modifier. //! Memory modifier.
/** /**
@ -109,7 +110,7 @@ namespace cppreg {
inline static inline static
typename std::enable_if< typename std::enable_if<
internals::check_overflow< internals::check_overflow<
size, value, (F::mask >> F::offset) type, value, (F::mask >> F::offset)
>::value, >::value,
T T
>::type&& >::type&&
@ -118,8 +119,8 @@ namespace cppreg {
}; };
// Sanity check. // Sanity check.
static_assert(RegWidth != 0u, static_assert(size != 0u,
"defining a Register type of width 0u is not allowed"); "defining a Register type of zero size is not allowed");
}; };

View File

@ -24,20 +24,20 @@ namespace internals {
//! Memory map implementation. //! Memory map implementation.
/** /**
* @tparam Address Memory base address. * @tparam address Memory base address.
* @tparam N Memory size in bytes. * @tparam n Memory size in bytes.
* @tparam W Width in bits of the memory "elements". * @tparam reg_size Register bit size for the array elements.
* *
* This structure is used to map an array structure onto a memory region. * This structure is used to map an array structure onto a memory region.
* The width parameter will correspond to the register size. * The width parameter will correspond to the register size.
*/ */
template <Address_t Address, std::uint32_t N, Width_t W> template <Address_t address, std::uint32_t n, RegBitSize reg_size>
struct memory_map { struct memory_map {
//! Array type. //! Array type.
using mem_array_t = std::array< using mem_array_t = std::array<
volatile typename RegisterType<W>::type, volatile typename TypeTraits<reg_size>::type,
N / sizeof(typename RegisterType<W>::type) n / sizeof(typename TypeTraits<reg_size>::type)
>; >;
//! Static reference to memory array. //! Static reference to memory array.
@ -49,24 +49,25 @@ namespace internals {
}; };
//! Memory array reference definition. //! Memory array reference definition.
template <Address_t Address, std::uint32_t N, Width_t W> template <Address_t address, std::uint32_t n, RegBitSize reg_size>
typename memory_map<Address, N, W>::mem_array_t& typename memory_map<address, n, reg_size>::mem_array_t&
memory_map<Address, N, W>::array = *( memory_map<address, n, reg_size>::array = *(
reinterpret_cast<typename memory_map<Address, N, W>::mem_array_t*>( reinterpret_cast<typename memory_map<address, n, reg_size>
Address ::mem_array_t*>(
address
) )
); );
//! is_aligned implementation. //! is_aligned implementation.
/** /**
* @tparam Address Address to be checked for alignment. * @tparam address Address to be checked for alignment.
* @tparam alignment Alignment constraint. * @tparam alignment Alignment constraint.
*/ */
template <Address_t Address, std::size_t alignment> template <Address_t address, std::size_t alignment>
struct is_aligned : std::integral_constant< struct is_aligned : std::integral_constant<
bool, bool,
(Address & (alignment - 1)) == 0 (address & (alignment - 1)) == 0
> { > {
}; };
@ -76,19 +77,19 @@ namespace internals {
//! Register pack base implementation. //! Register pack base implementation.
/** /**
* @tparam PackBase Pack base address. * @tparam base_address Pack base address.
* @tparam PackByteSize Pack size in bytes. * @tparam pack_byte_size Pack size in bytes.
*/ */
template < template <
Address_t PackBase, Address_t base_address,
std::uint32_t PackByteSize std::uint32_t pack_byte_size
> struct RegisterPack { > struct RegisterPack {
//! Base address. //! Base address.
constexpr static const Address_t pack_base = PackBase; constexpr static const Address_t pack_base = base_address;
//! Pack size in bytes. //! Pack size in bytes.
constexpr static const std::uint32_t size_in_bytes = PackByteSize; constexpr static const std::uint32_t size_in_bytes = pack_byte_size;
}; };
@ -106,32 +107,32 @@ namespace internals {
*/ */
template < template <
typename RegisterPack, typename RegisterPack,
std::uint32_t BitOffset, std::uint32_t bit_offset,
Width_t RegWidth, RegBitSize reg_size,
typename RegisterType<RegWidth>::type ResetValue = 0x0, typename TypeTraits<reg_size>::type reset_value = 0x0,
bool UseShadow = false bool use_shadow = false
> >
struct PackedRegister : struct PackedRegister :
Register< Register<
RegisterPack::pack_base + (BitOffset / 8u), RegisterPack::pack_base + (bit_offset / 8u),
RegWidth, reg_size,
ResetValue, reset_value,
UseShadow use_shadow
> { > {
//! Register type. //! Register type.
using base_reg = Register< using base_reg = Register<
RegisterPack::pack_base + (BitOffset / 8u), RegisterPack::pack_base + (bit_offset / 8u),
RegWidth, reg_size,
ResetValue, reset_value,
UseShadow use_shadow
>; >;
//! Memory map type. //! Memory map type.
using mem_map_t = internals::memory_map< using mem_map_t = internals::memory_map<
RegisterPack::pack_base, RegisterPack::pack_base,
RegisterPack::size_in_bytes, RegisterPack::size_in_bytes,
RegWidth reg_size
>; >;
//! Memory modifier. //! Memory modifier.
@ -139,7 +140,8 @@ namespace internals {
* @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 mem_map_t::array[BitOffset / RegWidth]; return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
}; };
//! Memory accessor. //! Memory accessor.
@ -147,22 +149,26 @@ namespace internals {
* @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 mem_map_t::array[BitOffset / RegWidth]; return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
}; };
// Safety check to detect if are overflowing the pack. // Safety check to detect if are overflowing the pack.
static_assert((BitOffset / 8u) <= RegisterPack::size_in_bytes, static_assert((bit_offset / 8u) <= RegisterPack::size_in_bytes,
"packed register is overflowing the pack"); "packed register is overflowing the pack");
// A packed register of width N bits requires: // A packed register of width N bits requires:
// - the pack address to be N-bits aligned (N/8 aligned), // - the pack address to be N-bits aligned (N/8 aligned),
// - the pack address with offset to be N-bits aligned (N/8 aligned). // - the pack address with offset to be N-bits aligned (N/8 aligned).
static_assert( static_assert(
internals::is_aligned<RegisterPack::pack_base, (RegWidth / 8)> internals::is_aligned<
::value RegisterPack::pack_base,
TypeTraits<reg_size>::byte_size
>::value
&& &&
internals::is_aligned< internals::is_aligned<
RegisterPack::pack_base + (BitOffset / 8), (RegWidth / 8) RegisterPack::pack_base + (bit_offset / 8),
TypeTraits<reg_size>::byte_size
>::value, >::value,
"register is mis-aligned in the pack" "register is mis-aligned in the pack"
); );

View File

@ -19,20 +19,50 @@
namespace cppreg { namespace cppreg {
//! Register data type default implementation. // //! Register data type default implementation.
/** // /**
* @tparam Size Register size. // * @tparam Size Register size.
* // *
* This will fail to compile if the register size is not implemented. // * This will fail to compile if the register size is not implemented.
*/ // */
template <Width_t Size> // template <Width_t Size>
struct RegisterType; // struct RegisterType;
//
// //!@{ Specializations based on register size.
// template <> struct RegisterType<8u> { using type = std::uint8_t; };
// template <> struct RegisterType<16u> { using type = std::uint16_t; };
// template <> struct RegisterType<32u> { using type = std::uint32_t; };
// //!@}
//!@{ Specializations based on register size.
template <> struct RegisterType<8u> { using type = std::uint8_t; }; //! Register type traits based on size.
template <> struct RegisterType<16u> { using type = std::uint16_t; }; /**
template <> struct RegisterType<32u> { using type = std::uint32_t; }; * @tparam S Register size in bits.
//!@} */
template <RegBitSize S>
struct TypeTraits;
template <> struct TypeTraits<RegBitSize::b8> {
using type = std::uint8_t;
constexpr static const std::uint8_t bit_size = 8u;
constexpr static const std::uint8_t byte_size = 1u;
constexpr static const std::uint8_t max_field_width = 8u;
constexpr static const std::uint8_t max_field_offset = 8u;
};
template <> struct TypeTraits<RegBitSize::b16> {
using type = std::uint16_t;
constexpr static const std::uint8_t bit_size = 16u;
constexpr static const std::uint8_t byte_size = 2u;
constexpr static const std::uint8_t max_field_width = 16u;
constexpr static const std::uint8_t max_field_offset = 16u;
};
template <> struct TypeTraits<RegBitSize::b32> {
using type = std::uint32_t;
constexpr static const std::uint8_t bit_size = 32u;
constexpr static const std::uint8_t byte_size = 4u;
constexpr static const std::uint8_t max_field_width = 32u;
constexpr static const std::uint8_t max_field_offset = 32u;
};
} }

View File

@ -15,8 +15,13 @@
#define CPPREG_CPPREG_DEFINES_H #define CPPREG_CPPREG_DEFINES_H
namespace cppreg { namespace cppreg {
using Address_t = std::uintptr_t; using Address_t = std::uintptr_t;
using Width_t = std::uint8_t; enum class RegBitSize {
using Offset_t = std::uint8_t; b8,
b16,
b32
};
using FieldWidth_t = std::uint8_t;
using FieldOffset_t = std::uint8_t;
template <typename T> template <typename T>
struct type_mask { struct type_mask {
constexpr static const T value = std::numeric_limits<T>::max(); constexpr static const T value = std::numeric_limits<T>::max();
@ -28,7 +33,7 @@ namespace cppreg {
#ifndef CPPREG_ACCESSPOLICY_H #ifndef CPPREG_ACCESSPOLICY_H
#define CPPREG_ACCESSPOLICY_H #define CPPREG_ACCESSPOLICY_H
namespace cppreg { namespace cppreg {
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
struct RegisterRead { struct RegisterRead {
constexpr static const bool is_trivial = constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u); (mask == type_mask<T>::value) && (offset == 0u);
@ -47,7 +52,7 @@ namespace cppreg {
return static_cast<T>(mmio_device); return static_cast<T>(mmio_device);
}; };
}; };
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
struct RegisterWrite { struct RegisterWrite {
constexpr static const bool is_trivial = constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u); (mask == type_mask<T>::value) && (offset == 0u);
@ -70,7 +75,9 @@ namespace cppreg {
mmio_device = value; mmio_device = value;
}; };
}; };
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value> template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
struct RegisterWriteConstant { struct RegisterWriteConstant {
constexpr static const bool is_trivial = constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u); (mask == type_mask<T>::value) && (offset == 0u);
@ -92,18 +99,20 @@ namespace cppreg {
}; };
}; };
struct read_only { struct read_only {
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
inline static T read(const MMIO_t& mmio_device) noexcept { inline static T read(const MMIO_t& mmio_device) noexcept {
return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device); return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device);
}; };
}; };
struct read_write : read_only { struct read_write : read_only {
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
inline static void write(MMIO_t& mmio_device, inline static void write(MMIO_t& mmio_device,
const T value) noexcept { const T value) noexcept {
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value); RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
}; };
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value> template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
inline static void write(MMIO_t& mmio_device) noexcept { inline static void write(MMIO_t& mmio_device) noexcept {
RegisterWriteConstant<MMIO_t, T, mask, offset, value> RegisterWriteConstant<MMIO_t, T, mask, offset, value>
::write(mmio_device); ::write(mmio_device);
@ -127,14 +136,16 @@ namespace cppreg {
}; };
}; };
struct write_only { struct write_only {
template <typename MMIO_t, typename T, T mask, Offset_t offset> template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
inline static void write(MMIO_t& mmio_device, inline static void write(MMIO_t& mmio_device,
const T value) noexcept { const T value) noexcept {
RegisterWrite<MMIO_t, T, type_mask<T>::value, 0u>::write( RegisterWrite<MMIO_t, T, type_mask<T>::value, 0u>::write(
mmio_device, ((value << offset) & mask) mmio_device, ((value << offset) & mask)
); );
}; };
template <typename MMIO_t, typename T, T mask, Offset_t offset, T value> template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
inline static void write(MMIO_t& mmio_device) noexcept { inline static void write(MMIO_t& mmio_device) noexcept {
RegisterWriteConstant< RegisterWriteConstant<
MMIO_t, T, type_mask<T>::value, 0u, ((value << offset) & mask) MMIO_t, T, type_mask<T>::value, 0u, ((value << offset) & mask)
@ -149,11 +160,29 @@ namespace cppreg {
#ifndef CPPREG_TRAITS_H #ifndef CPPREG_TRAITS_H
#define CPPREG_TRAITS_H #define CPPREG_TRAITS_H
namespace cppreg { namespace cppreg {
template <Width_t Size> template <RegBitSize S>
struct RegisterType; struct TypeTraits;
template <> struct RegisterType<8u> { using type = std::uint8_t; }; template <> struct TypeTraits<RegBitSize::b8> {
template <> struct RegisterType<16u> { using type = std::uint16_t; }; using type = std::uint8_t;
template <> struct RegisterType<32u> { using type = std::uint32_t; }; constexpr static const std::uint8_t bit_size = 8u;
constexpr static const std::uint8_t byte_size = 1u;
constexpr static const std::uint8_t max_field_width = 8u;
constexpr static const std::uint8_t max_field_offset = 8u;
};
template <> struct TypeTraits<RegBitSize::b16> {
using type = std::uint16_t;
constexpr static const std::uint8_t bit_size = 16u;
constexpr static const std::uint8_t byte_size = 2u;
constexpr static const std::uint8_t max_field_width = 16u;
constexpr static const std::uint8_t max_field_offset = 16u;
};
template <> struct TypeTraits<RegBitSize::b32> {
using type = std::uint32_t;
constexpr static const std::uint8_t bit_size = 32u;
constexpr static const std::uint8_t byte_size = 4u;
constexpr static const std::uint8_t max_field_width = 32u;
constexpr static const std::uint8_t max_field_offset = 32u;
};
} }
#endif #endif
@ -163,9 +192,9 @@ namespace cppreg {
namespace cppreg { namespace cppreg {
namespace internals { namespace internals {
template < template <
Width_t W, typename T,
typename RegisterType<W>::type value, T value,
typename RegisterType<W>::type limit T limit
> >
struct check_overflow : std::integral_constant<bool, value <= limit> {}; struct check_overflow : std::integral_constant<bool, value <= limit> {};
} }
@ -177,17 +206,17 @@ namespace internals {
#define CPPREG_MASK_H #define CPPREG_MASK_H
namespace cppreg { namespace cppreg {
template <typename Mask_t> template <typename Mask_t>
constexpr Mask_t make_mask(const Width_t width) noexcept { constexpr Mask_t make_mask(const FieldWidth_t width) noexcept {
return width == 0 ? return width == 0 ?
0u 0u
: :
static_cast<Mask_t>( static_cast<Mask_t>(
(make_mask<Mask_t>(Width_t(width - 1)) << 1) | 1 (make_mask<Mask_t>(FieldWidth_t(width - 1)) << 1) | 1
); );
}; };
template <typename Mask_t> template <typename Mask_t>
constexpr Mask_t make_shifted_mask(const Width_t width, constexpr Mask_t make_shifted_mask(const FieldWidth_t width,
const Offset_t offset) noexcept { const FieldOffset_t offset) noexcept {
return static_cast<Mask_t>(make_mask<Mask_t>(width) << offset); return static_cast<Mask_t>(make_mask<Mask_t>(width) << offset);
}; };
} }
@ -215,7 +244,7 @@ namespace cppreg {
template < template <
typename Register, typename Register,
typename Register::type mask, typename Register::type mask,
Offset_t offset, FieldOffset_t offset,
typename Register::type value typename Register::type value
> class MergeWrite_tmpl { > class MergeWrite_tmpl {
public: public:
@ -259,7 +288,7 @@ namespace cppreg {
inline inline
typename std::enable_if< typename std::enable_if<
(internals::check_overflow< (internals::check_overflow<
Register::size, new_value, (F::mask >> F::offset) typename Register::type, new_value, (F::mask >> F::offset)
>::value), >::value),
T T
>::type&& >::type&&
@ -330,18 +359,19 @@ namespace cppreg {
#define CPPREG_REGISTER_H #define CPPREG_REGISTER_H
namespace cppreg { namespace cppreg {
template < template <
Address_t RegAddress, Address_t reg_address,
Width_t RegWidth, RegBitSize reg_size,
typename RegisterType<RegWidth>::type ResetValue = 0x0, typename TypeTraits<reg_size>::type reset_value = 0x0,
bool UseShadow = false bool use_shadow = false
> >
struct Register { struct Register {
using type = typename RegisterType<RegWidth>::type; using type = typename TypeTraits<reg_size>::type;
using MMIO_t = volatile type; using MMIO_t = volatile type;
constexpr static const Address_t base_address = RegAddress; using shadow = Shadow<Register, use_shadow>;
constexpr static const Width_t size = RegWidth; constexpr static const Address_t base_address = reg_address;
constexpr static const type reset = ResetValue; constexpr static const std::uint8_t size =
using shadow = Shadow<Register, UseShadow>; TypeTraits<reg_size>::bit_size;
constexpr static const type reset = reset_value;
static MMIO_t& rw_mem_device() { static MMIO_t& rw_mem_device() {
return *(reinterpret_cast<MMIO_t* const>(base_address)); return *(reinterpret_cast<MMIO_t* const>(base_address));
}; };
@ -368,15 +398,15 @@ namespace cppreg {
inline static inline static
typename std::enable_if< typename std::enable_if<
internals::check_overflow< internals::check_overflow<
size, value, (F::mask >> F::offset) type, value, (F::mask >> F::offset)
>::value, >::value,
T T
>::type&& >::type&&
merge_write() noexcept { merge_write() noexcept {
return std::move(T::make()); return std::move(T::make());
}; };
static_assert(RegWidth != 0u, static_assert(size != 0u,
"defining a Register type of width 0u is not allowed"); "defining a Register type of zero size is not allowed");
}; };
} }
#endif #endif
@ -385,67 +415,84 @@ 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> namespace internals {
template <Address_t address, std::uint32_t n, RegBitSize reg_size>
struct memory_map { struct memory_map {
using mem_array_t = std::array< using mem_array_t = std::array<
volatile typename RegisterType<W>::type, volatile typename TypeTraits<reg_size>::type,
N / sizeof(typename RegisterType<W>::type) n / sizeof(typename TypeTraits<reg_size>::type)
>; >;
static mem_array_t& array; static mem_array_t& array;
}; };
template <Address_t Address, std::uint32_t N, Width_t W> template <Address_t address, std::uint32_t n, RegBitSize reg_size>
typename memory_map<Address, N, W>::mem_array_t& typename memory_map<address, n, reg_size>::mem_array_t&
memory_map<Address, N, W>::array = *( memory_map<address, n, reg_size>::array = *(
reinterpret_cast<typename memory_map<Address, N, W>::mem_array_t*>( reinterpret_cast<typename memory_map<address, n, reg_size>
Address ::mem_array_t*>(
address
) )
); );
template <Address_t address, std::size_t alignment>
struct is_aligned : std::integral_constant<
bool,
(address & (alignment - 1)) == 0
> {
};
}
template < template <
Address_t PackBase, Address_t base_address,
std::uint32_t PackByteSize std::uint32_t pack_byte_size
> struct RegisterPack { > struct RegisterPack {
constexpr static const Address_t pack_base = PackBase; constexpr static const Address_t pack_base = base_address;
constexpr static const std::uint32_t size_in_bytes = PackByteSize; constexpr static const std::uint32_t size_in_bytes = pack_byte_size;
}; };
template < template <
typename RegisterPack, typename RegisterPack,
std::uint32_t BitOffset, std::uint32_t bit_offset,
Width_t RegWidth, RegBitSize reg_size,
typename RegisterType<RegWidth>::type ResetValue = 0x0, typename TypeTraits<reg_size>::type reset_value = 0x0,
bool UseShadow = false bool use_shadow = false
> >
struct PackedRegister : struct PackedRegister :
Register< Register<
RegisterPack::pack_base + (BitOffset / 8u), RegisterPack::pack_base + (bit_offset / 8u),
RegWidth, reg_size,
ResetValue, reset_value,
UseShadow use_shadow
> { > {
using base_reg = Register< using base_reg = Register<
RegisterPack::pack_base + (BitOffset / 8u), RegisterPack::pack_base + (bit_offset / 8u),
RegWidth, reg_size,
ResetValue, reset_value,
UseShadow use_shadow
>; >;
using mem_map_t = memory_map< using mem_map_t = internals::memory_map<
RegisterPack::pack_base, RegisterPack::pack_base,
RegisterPack::size_in_bytes, RegisterPack::size_in_bytes,
RegWidth reg_size
>; >;
static typename base_reg::MMIO_t& rw_mem_device() { static typename base_reg::MMIO_t& rw_mem_device() {
return mem_map_t::array[BitOffset / RegWidth]; return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
}; };
static const typename base_reg::MMIO_t& ro_mem_device() { static const typename base_reg::MMIO_t& ro_mem_device() {
return mem_map_t::array[BitOffset / RegWidth]; return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
}; };
static_assert((BitOffset / 8u) <= RegisterPack::size_in_bytes, static_assert((bit_offset / 8u) <= RegisterPack::size_in_bytes,
"packed register is overflowing the pack"); "packed register is overflowing the pack");
static_assert(( static_assert(
(BitOffset % RegWidth) == 0 internals::is_aligned<
&& RegisterPack::pack_base,
(RegisterPack::pack_base % (RegWidth / 8u) == 0) TypeTraits<reg_size>::byte_size
), >::value
"register mis-alignment with respect to pack base"); &&
internals::is_aligned<
RegisterPack::pack_base + (bit_offset / 8),
TypeTraits<reg_size>::byte_size
>::value,
"register is mis-aligned in the pack"
);
}; };
template <typename... T> template <typename... T>
struct PackIndexing { struct PackIndexing {
@ -493,8 +540,8 @@ namespace cppreg {
namespace cppreg { namespace cppreg {
template < template <
typename BaseRegister, typename BaseRegister,
Width_t FieldWidth, FieldWidth_t field_width,
Offset_t FieldOffset, FieldOffset_t field_offset,
typename AccessPolicy typename AccessPolicy
> >
struct Field { struct Field {
@ -502,15 +549,15 @@ namespace cppreg {
using type = typename parent_register::type; using type = typename parent_register::type;
using MMIO_t = typename parent_register::MMIO_t; using MMIO_t = typename parent_register::MMIO_t;
using policy = AccessPolicy; using policy = AccessPolicy;
constexpr static const Width_t width = FieldWidth; constexpr static const FieldWidth_t width = field_width;
constexpr static const Offset_t offset = FieldOffset; constexpr static const FieldOffset_t offset = field_offset;
constexpr static const type mask = make_shifted_mask<type>(width, constexpr static const type mask = make_shifted_mask<type>(width,
offset); offset);
constexpr static const bool has_shadow = constexpr static const bool has_shadow =
parent_register::shadow::value; parent_register::shadow::value;
template <type value> template <type value>
struct check_overflow : internals::check_overflow< struct check_overflow : internals::check_overflow<
parent_register::size, type,
value, value,
(mask >> offset) (mask >> offset)
> {}; > {};
@ -585,7 +632,7 @@ namespace cppreg {
"field width is larger than parent register size"); "field width is larger than parent register size");
static_assert(parent_register::size >= width + offset, static_assert(parent_register::size >= width + offset,
"offset + width is larger than parent register size"); "offset + width is larger than parent register size");
static_assert(FieldWidth != 0u, static_assert(width != 0u,
"defining a Field type of width 0u is not allowed"); "defining a Field type of width 0u is not allowed");
}; };
} }