diff options
-rw-r--r-- | funreg.hpp | 88 |
1 files changed, 79 insertions, 9 deletions
@@ -14,17 +14,29 @@ */ #define FUNREG_ENABLE_EXTERNAL_IO +/** + * Comment to disable compile-time error checks, which are mostly concepts + * to verify proper library usage. + */ +#define FUNREG_ENABLE_ERROR_CHECKS + #include <stdint.h> #ifdef FUNREG_ENABLE_EXTERNAL_IO #include <type_traits> #endif +#ifdef FUNREG_ENABLE_ERROR_CHECKS +#include <concepts> +#endif + namespace fr { // A utility to measure a bit-mask's offset from bit zero. template<auto Mask, unsigned int N = 0> -constexpr auto BitOffset = []() constexpr { +requires(std::unsigned_integral<decltype(Mask)> && + (N < 8 * sizeof(Mask))) +constexpr auto BitOffset = []() constexpr -> unsigned int { if constexpr (Mask & 1) return N; else @@ -41,6 +53,7 @@ constexpr auto BitOffset = []() constexpr { * structure as a template. */ template<typename T, uintptr_t Addr> +requires(std::unsigned_integral<T>) struct MemoryIO { using type = T; constexpr static auto addr = Addr; @@ -60,6 +73,45 @@ struct MemoryIO { } }; +template<typename T> +concept IsAccess = requires(typename T::type x) { + requires std::same_as<decltype(T::addr), const uintptr_t>; + {T::read()} -> std::same_as<typename T::type>; + {T::write(x)} -> std::same_as<void>; +}; + +#ifdef FUNREG_ENABLE_EXTERNAL_IO +template<typename Access> +requires IsAccess<Access> +struct Register; + +template<typename T> +concept IsRegister = requires { + requires IsAccess<typename T::access>; + requires std::same_as<T, Register<typename T::access>>; +}; +#else +template<typename T, uintptr_t Addr> +requires(std::unsigned_integral<T>) +class Register; + +template<typename T> +concept IsRegister = requires { + requires std::unsigned_integral<typename T::type>; + requires IsAccess<typename T::Access>; +}; +#endif + +template<typename Reg, typename Reg::type Mask> +requires IsRegister<Reg> +struct RegisterMask; + +template<typename T> +concept IsRegisterMask = requires { + requires IsRegister<typename T::reg>; + requires std::same_as<T, RegisterMask<typename T::reg, T::mask>>; +}; + /** * @struct Register * @brief Defines a register, given how to access it. @@ -70,16 +122,22 @@ struct MemoryIO { */ #ifdef FUNREG_ENABLE_EXTERNAL_IO template<typename Access> -struct Register { +requires IsAccess<Access> +class Register { +public: 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>; +requires(std::unsigned_integral<T>) +class Register { +public: + using Access = MemoryIO<T, Addr>; #endif // FUNREG_ENABLE_EXTERNAL_IO + using type = T; + /** * Gets a pointer to the register. */ @@ -98,6 +156,7 @@ struct Register { * Sets register bits to '1' according to the given RegisterMasks. */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static void set() { apply<Masks...>([](auto r, auto m) { return r | m; }); } @@ -113,6 +172,7 @@ struct Register { * Clears register bits to '0' according to the given RegisterMasks. */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static void clear() { apply<Masks...>([](auto r, auto m) { return r & ~m; }); } @@ -128,6 +188,7 @@ struct Register { * Toggles bits in the register according to the given RegisterMasks. */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static void toggle() { apply<Masks...>([](auto r, auto m) { return r ^m; }); } @@ -145,6 +206,7 @@ struct Register { * If no masks are given, all register bits are returned. */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static auto read() { if constexpr (sizeof...(Masks) > 0) return read() & mergeMasks<Masks...>(); @@ -157,6 +219,7 @@ struct Register { * If no masks are given, tests if the register has a non-zero value. */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static bool test() { if constexpr (sizeof...(Masks) > 0) { auto mask = mergeMasks<Masks...>(); @@ -185,13 +248,13 @@ struct Register { } } - // Below is meant for internal use only. - +private: // Applies bit-masks to the register through the provided function. // The provided function receives a pointer to the register's data and a // bit-mask created by merging all provided bit-masks. // If no masks are given, a mask selecting all bits is used. template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static void apply(auto fn) { if constexpr (sizeof...(Masks) > 0) { auto mask = mergeMasks<Masks...>(); @@ -205,6 +268,7 @@ struct Register { // Takes a list of bit-masks, and returns a merged mask of those which are // meant for this register. template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static auto mergeMasks() { if constexpr (sizeof...(Masks) > 0) { if constexpr ((isThis<typename Masks::reg> | ...)) { @@ -224,20 +288,20 @@ struct Register { #ifdef FUNREG_ENABLE_EXTERNAL_IO // Determines if the given register matches this one. template<typename Reg> + requires(IsRegister<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> + requires(IsRegister<Reg>) constexpr static bool isThis = [] { return Addr == Reg::Addr; }(); #endif // FUNREG_ENABLE_EXTERNAL_IO Register() = delete; - - using type = T; }; /** @@ -251,6 +315,7 @@ struct Register { * using LED_RED = RegisterMask<GPIO_OUT, (1 << 2)>; */ template<typename Reg, typename Reg::type Mask> +requires IsRegister<Reg> struct RegisterMask { using T = typename Reg::type; @@ -373,6 +438,7 @@ struct RegisterMask * @tparam value The value to be used for the given Mask. */ template<typename Mask, Mask::T value> +requires(IsRegisterMask<Mask>) struct RegisterMaskValue { /** @@ -406,6 +472,7 @@ struct RegisterMaskValue * each register. */ template<typename... Registers> +requires(IsRegister<Registers>, ...) class RegisterGroup { public: @@ -415,6 +482,7 @@ public: * only written once. */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static void set() { apply<Masks...>([](auto r, auto m) { return r | m; }); } @@ -425,6 +493,7 @@ public: * Only reads and writes each register once; see set(). */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static void clear() { apply<Masks...>([](auto r, auto m) { return r & ~m; }); } @@ -435,6 +504,7 @@ public: * Only reads and writes each register once; see set(). */ template<typename... Masks> + requires(IsRegisterMask<Masks>, ...) static void toggle() { apply<Masks...>([](auto r, auto m) { return r ^ m; }); } @@ -465,6 +535,7 @@ private: * register. */ template<typename... RegMasks> +requires(IsRegisterMask<RegMasks>, ...) constexpr auto Masks = (RegMasks::mask | ...); #ifdef FUNREG_ENABLE_EXTERNAL_IO @@ -501,4 +572,3 @@ using MemRegister = Register<T, Addr>; } // namespace fr #endif // FUNCTIONAL_REGISTER_IO_H - |