/* clang-format off */ //! cppreg library. /** * @file cppreg-all.h * @author Nicolas Clauvelin (nclauvelin@sendyne.com) * @copyright Copyright 2010-2022 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 = std::uintptr_t; enum class RegBitSize : std::uint8_t { b8, // NOLINT b16, // NOLINT b32, // NOLINT b64 // NOLINT }; using FieldWidth = std::uint8_t; using FieldOffset = std::uint8_t; template struct type_mask { // NOLINT constexpr static const T value = std::numeric_limits::max(); }; constexpr static const auto one_byte = std::size_t{8}; } #endif // Traits.h #ifndef CPPREG_TRAITS_H #define CPPREG_TRAITS_H namespace cppreg { template struct TypeTraits {}; template <> struct TypeTraits { using type = std::uint8_t; // NOLINT constexpr static auto bit_size = std::uint8_t{8}; constexpr static auto byte_size = std::uint8_t{bit_size / 8}; }; template <> struct TypeTraits { using type = std::uint16_t; // NOLINT constexpr static auto bit_size = std::uint8_t{16}; constexpr static auto byte_size = std::uint8_t{bit_size / 8}; }; template <> struct TypeTraits { using type = std::uint32_t; // NOLINT constexpr static auto bit_size = std::uint8_t{32}; constexpr static auto byte_size = std::uint8_t{bit_size / 8}; }; template <> struct TypeTraits { using type = std::uint64_t; // NOLINT constexpr static auto bit_size = std::uint8_t{64}; constexpr static auto byte_size = std::uint8_t{bit_size / 8}; }; } #endif // Internals.h #ifndef CPPREG_INTERNALS_H #define CPPREG_INTERNALS_H namespace cppreg { namespace internals { template struct check_overflow // NOLINT : std::integral_constant {}; template
struct is_aligned // NOLINT : std::integral_constant< bool, (static_cast(address) & (alignment - 1)) == 0> {}; } } #endif // Memory.h #ifndef CPPREG_DEV_MEMORY_H #define CPPREG_DEV_MEMORY_H namespace cppreg { template
struct RegisterPack { constexpr static const Address pack_base = base_address; constexpr static const std::uint32_t size_in_bytes = pack_byte_size; }; template
struct MemoryDevice { using MemStorage = std::array; static MemStorage& _mem_storage; // NOLINT template static const volatile typename TypeTraits::type& ro_memory() { static_assert( internals::is_aligned::type>::value>::value, "MemoryDevice:: ro request not aligned"); return *(reinterpret_cast::type*>( &_mem_storage[byte_offset])); } template static volatile typename TypeTraits::type& rw_memory() { static_assert( internals::is_aligned::type>::value>::value, "MemoryDevice:: rw request not aligned"); return *( // NOLINTNEXTLINE reinterpret_cast::type*>( &_mem_storage[byte_offset])); } }; template
// NOLINTNEXTLINE typename MemoryDevice::MemStorage& MemoryDevice::_mem_storage = // NOLINTNEXTLINE *(reinterpret_cast::MemStorage*>(a)); template struct RegisterMemoryDevice { using mem_device = // NOLINT MemoryDevice; }; } #endif // AccessPolicy.h #ifndef CPPREG_ACCESSPOLICY_H #define CPPREG_ACCESSPOLICY_H namespace cppreg { template struct is_trivial_rw // NOLINT : std::integral_constant::value) && (offset == FieldOffset{0})> {}; template using is_trivial = // NOLINT typename std::enable_if::value, U>::type; template using is_not_trivial = // NOLINT typename std::enable_if::value, U>::type; template struct RegisterRead { template static T read(const MMIO& mmio_device, // NOLINTNEXTLINE is_not_trivial* = nullptr) noexcept { const auto lhs = static_cast(mmio_device & mask); return static_cast(lhs >> offset); } template static T read(const MMIO& mmio_device, // NOLINTNEXTLINE is_trivial* = nullptr) noexcept { return static_cast(mmio_device); } }; template struct RegisterWrite { template static void write(MMIO& mmio_device, T value, // NOLINTNEXTLINE is_not_trivial* = nullptr) noexcept { const auto shifted_value = static_cast(value << offset); const auto lhs = static_cast(mmio_device & static_cast(~mask)); const auto rhs = static_cast(shifted_value & mask); mmio_device = static_cast(lhs | rhs); } template static void write(MMIO& mmio_device, T value, // NOLINTNEXTLINE is_trivial* = nullptr) noexcept { mmio_device = value; } }; template struct RegisterWriteConstant { template static void write(MMIO& mmio_device, // NOLINTNEXTLINE is_not_trivial* = nullptr) noexcept { constexpr auto shifted_value = static_cast(value << offset); const auto lhs = static_cast(mmio_device & static_cast(~mask)); constexpr auto rhs = static_cast(shifted_value & mask); mmio_device = static_cast(lhs | rhs); } template static void write(MMIO& mmio_device, // NOLINTNEXTLINE is_trivial* = nullptr) noexcept { mmio_device = value; } }; struct read_only { // NOLINT template static T read(const MMIO& mmio_device) noexcept { return RegisterRead::read(mmio_device); } }; struct read_write : read_only { // NOLINT template static void write(MMIO& mmio_device, const T value) noexcept { RegisterWrite::write(mmio_device, value); } template static void write(MMIO& mmio_device) noexcept { RegisterWriteConstant::write(mmio_device); } template static void set(MMIO& mmio_device) noexcept { RegisterWriteConstant::write( mmio_device); } template static void clear(MMIO& mmio_device) noexcept { RegisterWriteConstant(~mask)>::write(mmio_device); } template static void toggle(MMIO& mmio_device) noexcept { mmio_device = static_cast((mmio_device) ^ mask); } }; struct write_only { // NOLINT template static void write(MMIO& mmio_device, const T value) noexcept { RegisterWrite::value, FieldOffset{0}>::write( mmio_device, ((value << offset) & mask)); } template static void write(MMIO& mmio_device) noexcept { RegisterWriteConstant::value, FieldOffset{0}, ((value << offset) & mask)>::write(mmio_device); } }; } #endif // Mask.h #ifndef CPPREG_MASK_H #define CPPREG_MASK_H namespace cppreg { template constexpr Mask make_mask(const FieldWidth width) noexcept { return width == 0U ? static_cast(0U) : static_cast( static_cast(make_mask(FieldWidth( static_cast(width - 1U))) << 1U) | 1U); } template constexpr Mask make_shifted_mask(const FieldWidth width, const FieldOffset 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 class MergeWrite_tmpl { // NOLINT private: using base_type = typename Register::type; // NOLINT constexpr static auto _accumulated_value = // NOLINT base_type{(value << offset) & mask}; constexpr static auto _combined_mask = mask; // NOLINT template using propagated = // NOLINT MergeWrite_tmpl(~F::mask)) | ((new_value << F::offset) & F::mask)>; MergeWrite_tmpl() = default; static_assert(!Register::shadow::value, "merge write is not available for shadow value register"); public: static MergeWrite_tmpl create() noexcept { return {}; } ~MergeWrite_tmpl() = default; MergeWrite_tmpl(MergeWrite_tmpl&&) noexcept = default; 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; void done() const&& noexcept { typename Register::MMIO& mmio_device = Register::rw_mem_device(); RegisterWriteConstant::write(mmio_device); } template propagated with() const&& noexcept { static_assert( std::is_same::value, "MergeWrite_tmpl:: field is not from the same register"); constexpr auto no_overflow = internals::check_overflow> F::offset)>::value; static_assert(no_overflow, "MergeWrite_tmpl:: field overflow in with() call"); return propagated{}; } }; template class MergeWrite { private: using base_type = typename Register::type; // NOLINT base_type _accumulated_value; // NOLINT constexpr static auto _combined_mask = mask; // NOLINT template using propagated = // NOLINT MergeWrite; constexpr MergeWrite() : _accumulated_value{0} {}; constexpr explicit MergeWrite(const base_type v) : _accumulated_value{v} {}; static_assert(!Register::shadow::value, "merge write is not available for shadow value register"); public: constexpr static MergeWrite create(const base_type value) noexcept { return MergeWrite(value); } ~MergeWrite() = default; MergeWrite(MergeWrite&& mw) noexcept : _accumulated_value{mw._accumulated_value} {}; MergeWrite(const MergeWrite&) = delete; MergeWrite& operator=(const MergeWrite&) = delete; MergeWrite& operator=(MergeWrite&&) = delete; void done() const&& noexcept { typename Register::MMIO& mmio_device = Register::rw_mem_device(); RegisterWrite::write(mmio_device, _accumulated_value); } template propagated with(const base_type value) const&& noexcept { static_assert( std::is_same::value, "field is not from the same register in merge_write"); constexpr auto neg_mask = static_cast(~F::mask); const auto shifted_value = static_cast(value << F::offset); const auto lhs = static_cast(_accumulated_value & neg_mask); const auto rhs = static_cast(shifted_value & F::mask); return propagated::create(static_cast(lhs | rhs)); } }; } #endif // Register.h #ifndef CPPREG_REGISTER_H #define CPPREG_REGISTER_H namespace cppreg { template
::type reset_value = 0x0, bool use_shadow = false> struct Register { using type = typename TypeTraits::type; // NOLINT using MMIO = volatile type; using shadow = Shadow; // NOLINT constexpr static auto base_address = reg_address; constexpr static auto size = TypeTraits::bit_size; constexpr static auto reset = reset_value; using pack = RegisterPack; // NOLINT static MMIO& rw_mem_device() { using MemDevice = typename RegisterMemoryDevice::mem_device; return MemDevice::template rw_memory(); } static const MMIO& ro_mem_device() { using MemDevice = typename RegisterMemoryDevice::mem_device; return MemDevice::template ro_memory(); } template static MergeWrite merge_write( const typename F::type value) noexcept { const auto lhs = static_cast(value << F::offset); return MergeWrite::create( static_cast(lhs & F::mask)); } template > static T merge_write() noexcept { static_assert( internals::check_overflow> F::offset)>:: value, "Register::merge_write:: value too large for the field"); return T::create(); } static_assert(size != 0, "Register:: register definition with zero size"); static_assert(internals::is_aligned::byte_size>::value, "Register:: address is mis-aligned for register type"); }; } #endif // RegisterPack.h #ifndef CPPREG_REGISTERPACK_H #define CPPREG_REGISTERPACK_H namespace cppreg { template ::type reset_value = 0x0, bool use_shadow = false> struct PackedRegister : Register { using pack = RegisterPack; // NOLINT using base_reg = // NOLINT Register; static typename base_reg::MMIO& rw_mem_device() noexcept { using MemDevice = typename RegisterMemoryDevice::mem_device; return MemDevice::template rw_memory(); } static const typename base_reg::MMIO& ro_mem_device() noexcept { using MemDevice = typename RegisterMemoryDevice::mem_device; return MemDevice::template ro_memory(); } static_assert(TypeTraits::byte_size + (bit_offset / one_byte) <= RegisterPack::size_in_bytes, "PackRegister:: packed register is overflowing the pack"); static_assert( internals::is_aligned::byte_size>::value, "PackedRegister:: pack base address is mis-aligned for register type"); static_assert( internals::is_aligned::byte_size>::value, "PackedRegister:: offset address is mis-aligned for register type"); }; template struct PackIndexing { using tuple_t = typename std::tuple; // NOLINT constexpr static const std::size_t n_elems = std::tuple_size::value; template using elem = typename std::tuple_element::type; // NOLINT }; template struct for_loop { // NOLINT template static void apply() noexcept { Func().template operator()(); if (start < end) { for_loop::template apply(); } } #if __cplusplus >= 201402L template static void apply(Op&& f) noexcept { if (start < end) { f(std::integral_constant{}); for_loop::apply(std::forward(f)); }; } #endif }; template struct for_loop { template static void apply() noexcept {} #if __cplusplus >= 201402L template static void apply(Op&&) noexcept {} // NOLINT #endif }; template struct pack_loop : for_loop<0, IndexedPack::n_elems> {}; // NOLINT } #endif // Field.h #ifndef CPPREG_REGISTERFIELD_H #define CPPREG_REGISTERFIELD_H namespace cppreg { template struct Field { using parent_register = BaseRegister; // NOLINT using type = typename parent_register::type; // NOLINT using MMIO = typename parent_register::MMIO; // NOLINT using policy = AccessPolicy; // NOLINT constexpr static auto width = field_width; constexpr static auto offset = field_offset; constexpr static auto mask = make_shifted_mask(width, offset); template using if_no_shadow = // NOLINT typename std::enable_if::type; template using if_shadow = // NOLINT typename std::enable_if::type; static type read() noexcept { return policy::template read( parent_register::ro_mem_device()); } template static void write(const if_no_shadow value) noexcept { policy::template write( parent_register::rw_mem_device(), value); } template static void write(const if_shadow value) noexcept { RegisterWrite::write( parent_register::shadow::shadow_value, value); policy:: template write::value, FieldOffset{0}>( parent_register::rw_mem_device(), parent_register::shadow::shadow_value); } template static void write(if_no_shadow* = nullptr) noexcept { // NOLINT policy::template write( parent_register::rw_mem_device()); static_assert( internals::check_overflow> offset)>::value, "Field::write: value too large for the field"); } template static void write(if_shadow* = nullptr) noexcept { // NOLINT write(value); static_assert( internals::check_overflow> offset)>::value, "Field::write: value too large for the field"); } static void set() noexcept { policy::template set( parent_register::rw_mem_device()); } static void clear() noexcept { policy::template clear( parent_register::rw_mem_device()); } static void toggle() noexcept { policy::template toggle( parent_register::rw_mem_device()); } static bool is_set() noexcept { return (Field::read() == (mask >> offset)); } static bool is_clear() noexcept { return (Field::read() == type{0}); } static_assert(parent_register::size >= width, "Field:: field width is larger than parent register size"); static_assert(parent_register::size >= width + offset, "Field:: offset + width is larger than parent register size"); static_assert(width != FieldWidth{0}, "Field:: defining a Field type of zero width is not allowed"); }; } #endif /* clang-format on */