From c0ca2d3a373e66f474d21ce34c25b38d953a66c7 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Mon, 8 Aug 2022 08:42:21 -0400 Subject: [PATCH] external i/o implemented --- funreg.hpp | 122 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/funreg.hpp b/funreg.hpp index 24a34fe..6ca44ed 100644 --- a/funreg.hpp +++ b/funreg.hpp @@ -1,5 +1,5 @@ /** - * funreg.hpp - Functional memory-mapped register I/O using modern C++. + * funreg.hpp - Functional register I/O using modern C++. * Written by Clyne Sullivan. * */ @@ -7,8 +7,19 @@ #ifndef FUNCTIONAL_REGISTER_IO_H #define FUNCTIONAL_REGISTER_IO_H +/** + * Comment to disable external/custom register access. + * When disabled, only memory-mapped register access is supported. + * fr::Register can then also be used instead of fr::MemRegister. + */ +#define FUNREG_ENABLE_EXTERNAL_IO + #include +#ifdef FUNREG_ENABLE_EXTERNAL_IO +#include +#endif + namespace fr { // A utility to measure a bit-mask's offset from bit zero. @@ -20,8 +31,18 @@ constexpr auto BitOffset = []() constexpr { return BitOffset<(Mask >> 1), (N + 1)>; }(); +/** + * @struct MemoryIO + * @brief Specifies how to access a memory-mapped register. + * @tparam T The size of the register. + * @tparam Addr The memory address of the register. + * + * To create an I/O access type for external register access, use this + * structure as a template. + */ template struct MemoryIO { + using type = T; constexpr static auto addr = Addr; /** @@ -39,48 +60,38 @@ struct MemoryIO { } }; -template -struct ExternalIO { - constexpr static auto addr = Addr; - - /** - * Gets a pointer to the register. - */ - static T (*read)(); - - /** - * Overwrites the register's value. - */ - static void (*write)(const T& value); -}; - /** * @struct Register - * @brief Defines a memory-mapped register, given bit-size and address. - * @tparam T The integer type that matches the size of the register. - * @tparam Addr The memory address of the register. - * - * Defines a memory-mapped register that is usually accessed with a pointer of - * type T*, and is located at address Addr. + * @brief Defines a register, given how to access it. + * @tparam Access Specifies register access. See MemoryIO for an example. * - * Use only as a type, e.g. "using GPIO_OUT = Register" + * When FUNREG_ENABLE_EXTERNAL_IO is not defined, Register assumes MemoryIO + * access. The template parameters become that of MemoryIO. */ -template +#ifdef FUNREG_ENABLE_EXTERNAL_IO +template +struct Register { + using access = Access; + using T = typename Access::type; + constexpr static auto Addr = Access::addr; +#else +template struct Register { - constexpr static auto Addr = RegAccess::addr; + using RegAccess = MemoryIO; +#endif // FUNREG_ENABLE_EXTERNAL_IO /** * Gets a pointer to the register. */ constexpr static T read() { - return RegAccess::read(); + return Access::read(); } /** * Overwrites the register's value. */ constexpr static void write(const T& value) { - RegAccess::write(value); + Access::write(value); } /** @@ -164,10 +175,10 @@ struct Register { */ template static void modify() { - if (((Addr == Ops::reg::addr) | ...)) { + if constexpr ((isThis | ...)) { auto mask = read(); ([&mask] { - if (Addr == Ops::reg::addr) + if constexpr (isThis) mask = Ops(mask); }(), ...); write(mask); @@ -184,20 +195,22 @@ struct Register { static void apply(auto fn) { if constexpr (sizeof...(Masks) > 0) { auto mask = mergeMasks(); - if (mask) + if constexpr (mask) write(fn(read(), mask)); } else { write(fn(read(), T(0) - 1)); } } + // Takes a list of bit-masks, and returns a merged mask of those which are + // meant for this register. template static auto mergeMasks() { if constexpr (sizeof...(Masks) > 0) { - if (((Addr == Masks::reg::addr) | ...)) { + if constexpr ((isThis | ...)) { auto mask = ([] { - return Addr == Masks::reg::addr ? Masks::mask : 0; + return isThis ? Masks::mask : 0; }() | ...); return mask; } else { @@ -208,10 +221,23 @@ struct Register { } } +#ifdef FUNREG_ENABLE_EXTERNAL_IO + // Determines if the given register matches this one. + template + constexpr static bool isThis = [] { + return std::is_same_v && Addr == Reg::Addr; + }(); +#else + // Determines if the given register matches this one. + template + constexpr static bool isThis = [] { + return Addr == Reg::Addr; + }(); +#endif // FUNREG_ENABLE_EXTERNAL_IO + Register() = delete; using type = T; - constexpr static auto addr = Addr; }; /** @@ -441,8 +467,36 @@ private: template constexpr auto Masks = (RegMasks::mask | ...); +#ifdef FUNREG_ENABLE_EXTERNAL_IO +/** + * Defines a register that is accessed through memory, i.e. memory-mapped. + * @tparam T The variable type used to access the register (e.g. uint32_t). + * @tparam Addr The memory address of the register. + */ +template +using MemRegister = Register>; + +/** + * Defines a register that is accessed through external or custom means. + * @tparam ExtIO A type that provides access functionality (e.g. MemoryIO). + * @tparam T The variable type used to access the register (e.g. uint32_t). + * @tparam Addr The memory address of the register. + * + * Custom access types should be defined using MemoryIO as a template. + */ +template typename ExtIO, typename T, uintptr_t Addr> +using ExtRegister = Register>; +#else +/** + * Defines a register that is accessed through memory, i.e. memory-mapped. + * @tparam T The variable type used to access the register (e.g. uint32_t). + * @tparam Addr The memory address of the register. + * + * With external I/O disabled, the Register type may be used directly instead. + */ template -using MemRegister = Register>; +using MemRegister = Register; +#endif // FUNREG_ENABLE_EXTERNAL_IO } // namespace fr