diff options
Diffstat (limited to 'funreg.hpp')
-rw-r--r-- | funreg.hpp | 228 |
1 files changed, 160 insertions, 68 deletions
@@ -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. * <https://github.com/tcsullivan/funreg> */ @@ -7,11 +7,22 @@ #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 <stdint.h> +#ifdef FUNREG_ENABLE_EXTERNAL_IO +#include <type_traits> +#endif + namespace fr { -// A utility to measure a bit-mask's offset from bit zero. +// A utility to measure a bit-mask's offset from bit zero. template<auto Mask, unsigned int N = 0> constexpr auto BitOffset = []() constexpr { if constexpr (Mask & 1) @@ -21,23 +32,66 @@ constexpr auto BitOffset = []() constexpr { }(); /** - * @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. + * @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. * - * Defines a memory-mapped register that is usually accessed with a pointer of - * type T*, and is located at address Addr. + * To create an I/O access type for external register access, use this + * structure as a template. + */ +template<typename T, uintptr_t Addr> +struct MemoryIO { + using type = T; + constexpr static auto addr = Addr; + + /** + * Reads the register's value. + */ + constexpr static T read() { + return *reinterpret_cast<volatile T*>(Addr); + } + + /** + * Overwrites the register's value. + */ + constexpr static void write(const T& value) { + *reinterpret_cast<volatile T*>(Addr) = value; + } +}; + +/** + * @struct Register + * @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<uint32_t, 0xA0004120>" + * When FUNREG_ENABLE_EXTERNAL_IO is not defined, Register assumes MemoryIO + * access. The template parameters become that of MemoryIO. */ +#ifdef FUNREG_ENABLE_EXTERNAL_IO +template<typename Access> +struct Register { + using access = Access; + using T = typename Access::type; + constexpr static auto Addr = Access::addr; +#else template<typename T, uintptr_t Addr> struct Register { + using RegAccess = MemoryIO<T, Addr>; +#endif // FUNREG_ENABLE_EXTERNAL_IO + /** * Gets a pointer to the register. */ - constexpr static auto get() { - return reinterpret_cast<volatile T*>(Addr); + constexpr static T read() { + return Access::read(); + } + + /** + * Overwrites the register's value. + */ + constexpr static void write(const T& value) { + Access::write(value); } /** @@ -45,14 +99,14 @@ struct Register { */ template<typename... Masks> static void set() { - apply<Masks...>([](auto r, auto m) { *r = *r | m; }); + apply<Masks...>([](auto r, auto m) { return r | m; }); } /** * Sets register bits to '1' according to the given mask. */ static void set(const T& mask) { - *get() = *get() | mask; + write(read() | mask); } /** @@ -60,14 +114,14 @@ struct Register { */ template<typename... Masks> static void clear() { - apply<Masks...>([](auto r, auto m) { *r = *r & ~m; }); + apply<Masks...>([](auto r, auto m) { return r & ~m; }); } /** * Clears register bits to '0' according to the given mask. */ static void clear(const T& mask) { - *get() = *get() & ~mask; + write(read() & ~mask); } /** @@ -75,14 +129,14 @@ struct Register { */ template<typename... Masks> static void toggle() { - apply<Masks...>([](auto r, auto m) { *r = *r ^ m; }); + apply<Masks...>([](auto r, auto m) { return r ^m; }); } /** * Toggles bits in the register according to the given mask. */ static void toggle(const T& mask) { - *get() = *get() ^ mask; + write(read() ^ mask); } /** @@ -92,26 +146,10 @@ struct Register { */ template<typename... Masks> static auto read() { - if constexpr (sizeof...(Masks) > 0) { - if (((Addr == Masks::reg::addr) | ...)) { - auto mask = - ([] { - return Addr == Masks::reg::addr ? Masks::mask : 0; - }() | ...); - return *get() & mask; - } else { - return 0; - } - } else { - return *get(); - } - } - - /** - * Overwrites the entire register with the given value. - */ - static void write(const T& value) { - *get() = value; + if constexpr (sizeof...(Masks) > 0) + return read() & mergeMasks<Masks...>(); + else + return read(); } /** @@ -121,17 +159,10 @@ struct Register { template<typename... Masks> static bool test() { if constexpr (sizeof...(Masks) > 0) { - if (((Addr == Masks::reg::addr) | ...)) { - auto mask = - ([] { - return Addr == Masks::reg::addr ? Masks::mask : 0; - }() | ...); - return (*get() & mask) == mask; - } else { - return 0; - } + auto mask = mergeMasks<Masks...>(); + return (read() & mask) == mask; } else { - return *get() != 0; + return read() != 0; } } @@ -144,17 +175,17 @@ struct Register { */ template<typename... Ops> static void modify() { - if (((Addr == Ops::reg::addr) | ...)) { - auto mask = *get(); + if constexpr ((isThis<typename Ops::reg> | ...)) { + auto mask = read(); ([&mask] { - if (Addr == Ops::reg::addr) + if constexpr (isThis<typename Ops::reg>) mask = Ops(mask); }(), ...); - *get() = mask; + write(mask); } } - // Below is meant for internal use only. + // Below is meant for internal use only. // Applies bit-masks to the register through the provided function. // The provided function receives a pointer to the register's data and a @@ -163,21 +194,50 @@ struct Register { template<typename... Masks> static void apply(auto fn) { if constexpr (sizeof...(Masks) > 0) { - auto mask = + auto mask = mergeMasks<Masks...>(); + 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<typename... Masks> + static auto mergeMasks() { + if constexpr (sizeof...(Masks) > 0) { + if constexpr ((isThis<typename Masks::reg> | ...)) { + auto mask = ([] { - return Addr == Masks::reg::addr ? Masks::mask : 0; + return isThis<typename Masks::reg> ? Masks::mask : 0; }() | ...); - if (mask) - fn(get(), mask); + return mask; + } else { + return 0; + } } else { - fn(get(), T(0) - 1); + return 0; } } +#ifdef FUNREG_ENABLE_EXTERNAL_IO + // Determines if the given register matches this one. + template<typename Reg> + constexpr static bool isThis = [] { + return std::is_same_v<typename Reg::access, access> && Addr == Reg::Addr; + }(); +#else + // Determines if the given register matches this one. + template<typename Reg> + constexpr static bool isThis = [] { + return Addr == Reg::Addr; + }(); +#endif // FUNREG_ENABLE_EXTERNAL_IO + Register() = delete; using type = T; - constexpr static auto addr = Addr; }; /** @@ -203,7 +263,7 @@ struct RegisterMask */ struct set { constexpr set() { - *Reg::get() = *Reg::get() | Mask; + Reg::write(Reg::read() | Mask); } // For internal use. @@ -219,7 +279,7 @@ struct RegisterMask */ struct clear { constexpr clear() { - *Reg::get() = *Reg::get() & ~Mask; + Reg::write(Reg::read() & ~Mask); } // For internal use. @@ -235,7 +295,7 @@ struct RegisterMask */ struct toggle { constexpr toggle() { - *Reg::get() = *Reg::get() ^ Mask; + Reg::write(Reg::read() ^ Mask); } // For internal use. @@ -249,7 +309,7 @@ struct RegisterMask * Reads from the paired register, applying the bit-mask. */ static auto read() { - return *Reg::get() & Mask; + return Reg::read() & Mask; } /** @@ -272,10 +332,10 @@ struct RegisterMask template<T value> struct write { constexpr write() { - auto r = *Reg::get(); + auto r = Reg::read(); r &= ~Mask; r |= value << BitOffset<Mask>; - *Reg::get() = r; + Reg::write(r); } // For internal use. @@ -332,7 +392,7 @@ struct RegisterMaskValue * Tests if this value is currently set in the register. */ static bool test() { - return Mask::read() & (value << BitOffset<Mask>); + return (Mask::read() & Mask::mask) == (value << BitOffset<Mask>); } }; @@ -352,11 +412,11 @@ public: /** * Sets bits throughout this group's registers according to the given masks. * Bit-masks for the same register will be merged so that each register is - * only written once. + * only written once. */ template<typename... Masks> static void set() { - apply<Masks...>([](auto r, auto m) { *r = *r | m; }); + apply<Masks...>([](auto r, auto m) { return r | m; }); } /** @@ -366,7 +426,7 @@ public: */ template<typename... Masks> static void clear() { - apply<Masks...>([](auto r, auto m) { *r = *r & ~m; }); + apply<Masks...>([](auto r, auto m) { return r & ~m; }); } /** @@ -376,7 +436,7 @@ public: */ template<typename... Masks> static void toggle() { - apply<Masks...>([](auto r, auto m) { *r = *r ^ m; }); + apply<Masks...>([](auto r, auto m) { return r ^ m; }); } /** @@ -407,6 +467,38 @@ private: template<typename... RegMasks> 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<typename T, uintptr_t Addr> +using MemRegister = Register<MemoryIO<T, Addr>>; + +/** + * 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<template<typename, uintptr_t> typename ExtIO, typename T, uintptr_t Addr> +using ExtRegister = Register<ExtIO<T, Addr>>; +#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<typename T, uintptr_t Addr> +using MemRegister = Register<T, Addr>; +#endif // FUNREG_ENABLE_EXTERNAL_IO + } // namespace fr #endif // FUNCTIONAL_REGISTER_IO_H + |