diff --git a/funreg.hpp b/funreg.hpp index 00cd3a9..accdb9d 100644 --- a/funreg.hpp +++ b/funreg.hpp @@ -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 #ifdef FUNREG_ENABLE_EXTERNAL_IO #include #endif +#ifdef FUNREG_ENABLE_ERROR_CHECKS +#include +#endif + namespace fr { // A utility to measure a bit-mask's offset from bit zero. template -constexpr auto BitOffset = []() constexpr { +requires(std::unsigned_integral && + (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 +requires(std::unsigned_integral) struct MemoryIO { using type = T; constexpr static auto addr = Addr; @@ -60,6 +73,45 @@ struct MemoryIO { } }; +template +concept IsAccess = requires(typename T::type x) { + requires std::same_as; + {T::read()} -> std::same_as; + {T::write(x)} -> std::same_as; +}; + +#ifdef FUNREG_ENABLE_EXTERNAL_IO +template +requires IsAccess +struct Register; + +template +concept IsRegister = requires { + requires IsAccess; + requires std::same_as>; +}; +#else +template +requires(std::unsigned_integral) +class Register; + +template +concept IsRegister = requires { + requires std::unsigned_integral; + requires IsAccess; +}; +#endif + +template +requires IsRegister +struct RegisterMask; + +template +concept IsRegisterMask = requires { + requires IsRegister; + requires std::same_as>; +}; + /** * @struct Register * @brief Defines a register, given how to access it. @@ -70,16 +122,22 @@ struct MemoryIO { */ #ifdef FUNREG_ENABLE_EXTERNAL_IO template -struct Register { +requires IsAccess +class Register { +public: using access = Access; using T = typename Access::type; constexpr static auto Addr = Access::addr; #else template -struct Register { - using RegAccess = MemoryIO; +requires(std::unsigned_integral) +class Register { +public: + using Access = MemoryIO; #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 + requires(IsRegisterMask, ...) static void set() { apply([](auto r, auto m) { return r | m; }); } @@ -113,6 +172,7 @@ struct Register { * Clears register bits to '0' according to the given RegisterMasks. */ template + requires(IsRegisterMask, ...) static void clear() { apply([](auto r, auto m) { return r & ~m; }); } @@ -128,6 +188,7 @@ struct Register { * Toggles bits in the register according to the given RegisterMasks. */ template + requires(IsRegisterMask, ...) static void toggle() { apply([](auto r, auto m) { return r ^m; }); } @@ -145,6 +206,7 @@ struct Register { * If no masks are given, all register bits are returned. */ template + requires(IsRegisterMask, ...) static auto read() { if constexpr (sizeof...(Masks) > 0) return read() & mergeMasks(); @@ -157,6 +219,7 @@ struct Register { * If no masks are given, tests if the register has a non-zero value. */ template + requires(IsRegisterMask, ...) static bool test() { if constexpr (sizeof...(Masks) > 0) { auto mask = mergeMasks(); @@ -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 + requires(IsRegisterMask, ...) static void apply(auto fn) { if constexpr (sizeof...(Masks) > 0) { auto mask = mergeMasks(); @@ -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 + requires(IsRegisterMask, ...) static auto mergeMasks() { if constexpr (sizeof...(Masks) > 0) { if constexpr ((isThis | ...)) { @@ -224,20 +288,20 @@ struct Register { #ifdef FUNREG_ENABLE_EXTERNAL_IO // Determines if the given register matches this one. template + requires(IsRegister) constexpr static bool isThis = [] { return std::is_same_v && Addr == Reg::Addr; }(); #else // Determines if the given register matches this one. template + requires(IsRegister) 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; */ template +requires IsRegister 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 +requires(IsRegisterMask) struct RegisterMaskValue { /** @@ -406,6 +472,7 @@ struct RegisterMaskValue * each register. */ template +requires(IsRegister, ...) class RegisterGroup { public: @@ -415,6 +482,7 @@ public: * only written once. */ template + requires(IsRegisterMask, ...) static void set() { apply([](auto r, auto m) { return r | m; }); } @@ -425,6 +493,7 @@ public: * Only reads and writes each register once; see set(). */ template + requires(IsRegisterMask, ...) static void clear() { apply([](auto r, auto m) { return r & ~m; }); } @@ -435,6 +504,7 @@ public: * Only reads and writes each register once; see set(). */ template + requires(IsRegisterMask, ...) static void toggle() { apply([](auto r, auto m) { return r ^ m; }); } @@ -465,6 +535,7 @@ private: * register. */ template +requires(IsRegisterMask, ...) constexpr auto Masks = (RegMasks::mask | ...); #ifdef FUNREG_ENABLE_EXTERNAL_IO @@ -501,4 +572,3 @@ using MemRegister = Register; } // namespace fr #endif // FUNCTIONAL_REGISTER_IO_H -