//! cppreg library. /** * @file cppreg-all.h * @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved. */ #include #include #include #include // cppreg_Defines.h #ifndef CPPREG_CPPREG_DEFINES_H #define CPPREG_CPPREG_DEFINES_H namespace cppreg { using Address_t = std::uintptr_t; enum class RegBitSize { b8, b16, b32, b64 }; using FieldWidth_t = std::uint8_t; using FieldOffset_t = std::uint8_t; template struct type_mask { constexpr static const T value = std::numeric_limits::max(); }; } #endif // AccessPolicy.h #ifndef CPPREG_ACCESSPOLICY_H #define CPPREG_ACCESSPOLICY_H namespace cppreg { template struct RegisterRead { constexpr static const bool is_trivial = (mask == type_mask::value) && (offset == 0u); template inline static T read( const MMIO_t& mmio_device, typename std::enable_if::type = nullptr ) noexcept { return static_cast((mmio_device & mask) >> offset); }; template inline static T read( const MMIO_t& mmio_device, typename std::enable_if::type = nullptr ) noexcept { return static_cast(mmio_device); }; }; template struct RegisterWrite { constexpr static const bool is_trivial = (mask == type_mask::value) && (offset == 0u); template inline static void write( MMIO_t& mmio_device, T value, typename std::enable_if::type = nullptr ) noexcept { mmio_device = static_cast( (mmio_device & ~mask) | ((value << offset) & mask) ); }; template inline static void write( MMIO_t& mmio_device, T value, typename std::enable_if::type = nullptr ) noexcept { mmio_device = value; }; }; template < typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value > struct RegisterWriteConstant { constexpr static const bool is_trivial = (mask == type_mask::value) && (offset == 0u); template inline static void write( MMIO_t& mmio_device, typename std::enable_if::type = nullptr ) noexcept { mmio_device = static_cast( (mmio_device & ~mask) | ((value << offset) & mask) ); }; template inline static void write( MMIO_t& mmio_device, typename std::enable_if::type = nullptr ) noexcept { mmio_device = value; }; }; struct read_only { template inline static T read(const MMIO_t& mmio_device) noexcept { return RegisterRead::read(mmio_device); }; }; struct read_write : read_only { template inline static void write(MMIO_t& mmio_device, const T value) noexcept { RegisterWrite::write(mmio_device, value); }; template < typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value > inline static void write(MMIO_t& mmio_device) noexcept { RegisterWriteConstant ::write(mmio_device); }; template inline static void set(MMIO_t& mmio_device) noexcept { RegisterWriteConstant ::write(mmio_device); }; template inline static void clear(MMIO_t& mmio_device) noexcept { RegisterWriteConstant ::write(mmio_device); }; template inline static void toggle(MMIO_t& mmio_device) noexcept { mmio_device = static_cast((mmio_device) ^ mask); }; }; struct write_only { template inline static void write(MMIO_t& mmio_device, const T value) noexcept { RegisterWrite::value, 0u>::write( mmio_device, ((value << offset) & mask) ); }; template < typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value > inline static void write(MMIO_t& mmio_device) noexcept { RegisterWriteConstant< MMIO_t, T, type_mask::value, 0u, ((value << offset) & mask) > ::write(mmio_device); }; }; } #endif // Traits.h #ifndef CPPREG_TRAITS_H #define CPPREG_TRAITS_H namespace cppreg { template struct TypeTraits; template <> struct TypeTraits { 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 { 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 { 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; }; template <> struct TypeTraits { using type = std::uint64_t; constexpr static const std::uint8_t bit_size = 64u; constexpr static const std::uint8_t byte_size = 8u; constexpr static const std::uint8_t max_field_width = 64u; constexpr static const std::uint8_t max_field_offset = 64u; }; } #endif // Overflow.h #ifndef CPPREG_OVERFLOW_H #define CPPREG_OVERFLOW_H namespace cppreg { namespace internals { template < typename T, T value, T limit > struct check_overflow : std::integral_constant {}; } } #endif // Mask.h #ifndef CPPREG_MASK_H #define CPPREG_MASK_H namespace cppreg { template constexpr Mask_t make_mask(const FieldWidth_t width) noexcept { return width == 0 ? 0u : static_cast( (make_mask(FieldWidth_t(width - 1)) << 1) | 1 ); }; template constexpr Mask_t make_shifted_mask(const FieldWidth_t width, const FieldOffset_t offset) noexcept { return static_cast(make_mask(width) << offset); }; } #endif // ShadowValue.h #ifndef CPPREG_SHADOWVALUE_H #define CPPREG_SHADOWVALUE_H namespace cppreg { template struct Shadow : std::false_type {}; template struct Shadow : std::true_type { static typename Register::type shadow_value; }; template typename Register::type Shadow::shadow_value = Register::reset; } #endif // MergeWrite.h #ifndef CPPREG_MERGEWRITE_H #define CPPREG_MERGEWRITE_H namespace cppreg { template < typename Register, typename Register::type mask, FieldOffset_t offset, typename Register::type value > class MergeWrite_tmpl { private: using base_type = typename Register::type; static_assert(!Register::shadow::value, "merge write is not available for shadow value register"); constexpr static const base_type _accumulated_value = ((value << offset) & mask); constexpr static const base_type _combined_mask = mask; MergeWrite_tmpl() {}; public: inline static MergeWrite_tmpl make() noexcept { return {}; }; MergeWrite_tmpl(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(MergeWrite_tmpl&&) = delete; inline void done() const && noexcept { typename Register::MMIO_t& mmio_device = Register::rw_mem_device(); RegisterWriteConstant< typename Register::MMIO_t, typename Register::type, _combined_mask, 0u, _accumulated_value >::write(mmio_device); }; template < typename F, base_type new_value, typename T = MergeWrite_tmpl< Register, (_combined_mask | F::mask), 0u, (_accumulated_value & ~F::mask) | ((new_value << F::offset) & F::mask) > > inline typename std::enable_if< (internals::check_overflow< typename Register::type, new_value, (F::mask >> F::offset) >::value), T >::type&& with() const && noexcept { static_assert(std::is_same< typename F::parent_register, Register >::value, "field is not from the same register in merge_write"); return std::move(T::make()); }; }; template < typename Register, typename Register::type mask > class MergeWrite { public: using base_type = typename Register::type; private: constexpr static const base_type _combined_mask = mask; public: constexpr static MergeWrite make(const base_type value) noexcept { return MergeWrite(value); }; MergeWrite(MergeWrite&& mw) noexcept : _accumulated_value(mw._accumulated_value) {}; MergeWrite(const MergeWrite&) = delete; MergeWrite& operator=(const MergeWrite&) = delete; MergeWrite& operator=(MergeWrite&&) = delete; inline void done() const && noexcept { typename Register::MMIO_t& mmio_device = Register::rw_mem_device(); RegisterWrite< typename Register::MMIO_t, base_type, _combined_mask, 0u >::write(mmio_device, _accumulated_value); }; template inline MergeWrite with (const base_type value) && noexcept { static_assert(std::is_same< typename F::parent_register, Register >::value, "field is not from the same register in merge_write"); F::policy::template write< base_type, base_type, F::mask, F::offset >(_accumulated_value, value); return std::move( MergeWrite ::make(_accumulated_value) ); }; private: static_assert(!Register::shadow::value, "merge write is not available for shadow value register"); constexpr MergeWrite() : _accumulated_value(0u) {}; constexpr MergeWrite(const base_type v) : _accumulated_value(v) {}; base_type _accumulated_value; }; } #endif // Register.h #ifndef CPPREG_REGISTER_H #define CPPREG_REGISTER_H namespace cppreg { template < Address_t reg_address, RegBitSize reg_size, typename TypeTraits::type reset_value = 0x0, bool use_shadow = false > struct Register { using type = typename TypeTraits::type; using MMIO_t = volatile type; using shadow = Shadow; constexpr static const Address_t base_address = reg_address; constexpr static const std::uint8_t size = TypeTraits::bit_size; constexpr static const type reset = reset_value; static MMIO_t& rw_mem_device() { return *(reinterpret_cast(base_address)); }; static const MMIO_t& ro_mem_device() { return *(reinterpret_cast(base_address)); }; template inline static MergeWrite merge_write(const typename F::type value) noexcept { return MergeWrite ::make(((value << F::offset) & F::mask)); }; template < typename F, type value, typename T = MergeWrite_tmpl< typename F::parent_register, F::mask, F::offset, value > > inline static typename std::enable_if< internals::check_overflow< type, value, (F::mask >> F::offset) >::value, T >::type&& merge_write() noexcept { return std::move(T::make()); }; static_assert(size != 0u, "defining a Register type of zero size is not allowed"); }; } #endif // RegisterPack.h #ifndef CPPREG_REGISTERPACK_H #define CPPREG_REGISTERPACK_H namespace cppreg { namespace internals { template struct memory_map { using mem_array_t = std::array< volatile typename TypeTraits::type, n / sizeof(typename TypeTraits::type) >; static mem_array_t& array; }; template typename memory_map::mem_array_t& memory_map::array = *( reinterpret_cast ::mem_array_t*>( address ) ); template struct is_aligned : std::integral_constant< bool, (address & (alignment - 1)) == 0 > { }; } template < Address_t base_address, std::uint32_t pack_byte_size > struct RegisterPack { constexpr static const Address_t pack_base = base_address; constexpr static const std::uint32_t size_in_bytes = pack_byte_size; }; template < typename RegisterPack, RegBitSize reg_size, std::uint32_t bit_offset, typename TypeTraits::type reset_value = 0x0, bool use_shadow = false > struct PackedRegister : Register< RegisterPack::pack_base + (bit_offset / 8u), reg_size, reset_value, use_shadow > { using base_reg = Register< RegisterPack::pack_base + (bit_offset / 8u), reg_size, reset_value, use_shadow >; using mem_map_t = internals::memory_map< RegisterPack::pack_base, RegisterPack::size_in_bytes, reg_size >; static typename base_reg::MMIO_t& rw_mem_device() { return mem_map_t::array[bit_offset / TypeTraits::bit_size]; }; static const typename base_reg::MMIO_t& ro_mem_device() { return mem_map_t::array[bit_offset / TypeTraits::bit_size]; }; static_assert((bit_offset / 8u) <= RegisterPack::size_in_bytes, "packed register is overflowing the pack"); static_assert( internals::is_aligned< RegisterPack::pack_base, TypeTraits::byte_size >::value && internals::is_aligned< RegisterPack::pack_base + (bit_offset / 8), TypeTraits::byte_size >::value, "register is mis-aligned in the pack" ); }; template struct PackIndexing { using tuple_t = typename std::tuple; template using regs = typename std::tuple_element::type; }; template struct for_loop { template inline static void loop() { Func().template operator()(); if (start < end) for_loop::template loop(); }; #if __cplusplus >= 201402L template inline static void apply(Op& f) { if (start < end) { f(std::integral_constant{}); for_loop::apply(f); }; }; #endif }; template struct for_loop { template inline static void loop() {}; #if __cplusplus >= 201402L template inline static void apply(Op& f) {}; #endif }; template struct range_loop : for_loop< 0, std::tuple_size::value > {}; } #endif // Field.h #ifndef CPPREG_REGISTERFIELD_H #define CPPREG_REGISTERFIELD_H namespace cppreg { template < typename BaseRegister, FieldWidth_t field_width, FieldOffset_t field_offset, typename AccessPolicy > struct Field { using parent_register = BaseRegister; using type = typename parent_register::type; using MMIO_t = typename parent_register::MMIO_t; using policy = AccessPolicy; constexpr static const FieldWidth_t width = field_width; constexpr static const FieldOffset_t offset = field_offset; constexpr static const type mask = make_shifted_mask(width, offset); constexpr static const bool has_shadow = parent_register::shadow::value; template struct check_overflow : internals::check_overflow< type, value, (mask >> offset) > {}; inline static type read() noexcept { return policy::template read( parent_register::ro_mem_device() ); }; template inline static void write(const typename std::enable_if::type value) noexcept { policy::template write( parent_register::rw_mem_device(), value ); }; template inline static void write(const typename std::enable_if::type value) noexcept { RegisterWrite ::write(parent_register::shadow::shadow_value, value); policy::template write::value, 0u>( parent_register::rw_mem_device(), parent_register::shadow::shadow_value ); }; template inline static typename std::enable_if< !has_shadow && check_overflow::value, T >::type write() noexcept { policy::template write( parent_register::rw_mem_device() ); }; template inline static typename std::enable_if< has_shadow && check_overflow::value, T >::type write() noexcept { write(value); }; inline static void set() noexcept { policy::template set(parent_register::rw_mem_device()); }; inline static void clear() noexcept { policy::template clear(parent_register::rw_mem_device()); }; inline static void toggle() noexcept { policy::template toggle(parent_register::rw_mem_device()); }; inline static bool is_set() noexcept { return (Field::read() == (mask >> offset)); }; inline static bool is_clear() noexcept { return (Field::read() == 0u); }; static_assert(parent_register::size >= width, "field width is larger than parent register size"); static_assert(parent_register::size >= width + offset, "offset + width is larger than parent register size"); static_assert(width != 0u, "defining a Field type of width 0u is not allowed"); }; } #endif