Compare commits

..

2 Commits

@ -14,17 +14,29 @@
*/ */
#define FUNREG_ENABLE_EXTERNAL_IO #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> #include <stdint.h>
#ifdef FUNREG_ENABLE_EXTERNAL_IO #ifdef FUNREG_ENABLE_EXTERNAL_IO
#include <type_traits> #include <type_traits>
#endif #endif
#ifdef FUNREG_ENABLE_ERROR_CHECKS
#include <concepts>
#endif
namespace fr { 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> 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) if constexpr (Mask & 1)
return N; return N;
else else
@ -41,6 +53,7 @@ constexpr auto BitOffset = []() constexpr {
* structure as a template. * structure as a template.
*/ */
template<typename T, uintptr_t Addr> template<typename T, uintptr_t Addr>
requires(std::unsigned_integral<T>)
struct MemoryIO { struct MemoryIO {
using type = T; using type = T;
constexpr static auto addr = Addr; constexpr static auto addr = Addr;
@ -60,6 +73,54 @@ 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 =
IsAccess<typename T::access> &&
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 =
std::unsigned_integral<typename T::type> &&
IsAccess<typename T::Access>;
#endif
template<typename Reg, typename Reg::type Mask>
requires IsRegister<Reg>
struct RegisterMask;
template<typename T>
concept IsRegisterMask =
IsRegister<typename T::reg> &&
std::same_as<T, RegisterMask<typename T::reg, T::mask>>;
template<typename M, typename R>
concept UsesRegister =
IsRegisterMask<M> &&
IsRegister<R> &&
std::same_as<typename M::reg, R>;
template<typename M, typename... Rs>
concept UsesSomeRegister =
IsRegisterMask<M> &&
(IsRegister<Rs>, ...) &&
(std::same_as<typename M::reg, Rs> || ...);
/** /**
* @struct Register * @struct Register
* @brief Defines a register, given how to access it. * @brief Defines a register, given how to access it.
@ -70,16 +131,22 @@ struct MemoryIO {
*/ */
#ifdef FUNREG_ENABLE_EXTERNAL_IO #ifdef FUNREG_ENABLE_EXTERNAL_IO
template<typename Access> template<typename Access>
struct Register { requires IsAccess<Access>
class Register {
public:
using access = Access; using access = Access;
using T = typename Access::type; using T = typename Access::type;
constexpr static auto Addr = Access::addr; constexpr static auto Addr = Access::addr;
#else #else
template<typename T, uintptr_t Addr> template<typename T, uintptr_t Addr>
struct Register { requires(std::unsigned_integral<T>)
using RegAccess = MemoryIO<T, Addr>; class Register {
public:
using Access = MemoryIO<T, Addr>;
#endif // FUNREG_ENABLE_EXTERNAL_IO #endif // FUNREG_ENABLE_EXTERNAL_IO
using type = T;
/** /**
* Gets a pointer to the register. * Gets a pointer to the register.
*/ */
@ -98,6 +165,8 @@ struct Register {
* Sets register bits to '1' according to the given RegisterMasks. * Sets register bits to '1' according to the given RegisterMasks.
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesRegister<Masks, Register<Access>>, ...))
static void set() { static void set() {
apply<Masks...>([](auto r, auto m) { return r | m; }); apply<Masks...>([](auto r, auto m) { return r | m; });
} }
@ -113,6 +182,8 @@ struct Register {
* Clears register bits to '0' according to the given RegisterMasks. * Clears register bits to '0' according to the given RegisterMasks.
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesRegister<Masks, Register<Access>>, ...))
static void clear() { static void clear() {
apply<Masks...>([](auto r, auto m) { return r & ~m; }); apply<Masks...>([](auto r, auto m) { return r & ~m; });
} }
@ -128,6 +199,8 @@ struct Register {
* Toggles bits in the register according to the given RegisterMasks. * Toggles bits in the register according to the given RegisterMasks.
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesRegister<Masks, Register<Access>>, ...))
static void toggle() { static void toggle() {
apply<Masks...>([](auto r, auto m) { return r ^m; }); apply<Masks...>([](auto r, auto m) { return r ^m; });
} }
@ -145,6 +218,8 @@ struct Register {
* If no masks are given, all register bits are returned. * If no masks are given, all register bits are returned.
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesRegister<Masks, Register<Access>>, ...))
static auto read() { static auto read() {
if constexpr (sizeof...(Masks) > 0) if constexpr (sizeof...(Masks) > 0)
return read() & mergeMasks<Masks...>(); return read() & mergeMasks<Masks...>();
@ -157,6 +232,8 @@ struct Register {
* If no masks are given, tests if the register has a non-zero value. * If no masks are given, tests if the register has a non-zero value.
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesRegister<Masks, Register<Access>>, ...))
static bool test() { static bool test() {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0) {
auto mask = mergeMasks<Masks...>(); auto mask = mergeMasks<Masks...>();
@ -185,16 +262,16 @@ struct Register {
} }
} }
// Below is meant for internal use only. // Internal use only.
// Applies bit-masks to the register through the provided function. // Applies bit-masks to the register through the provided function.
// The provided function receives a pointer to the register's data and a // All masks meant for this register are merged together; then, `fn`
// bit-mask created by merging all provided bit-masks. // will receive the register's current value and the mask to carry out
// the operation. The result is written back to the register.
// If no masks are given, a mask selecting all bits is used. // If no masks are given, a mask selecting all bits is used.
template<typename... Masks> template<typename... Masks>
static void apply(auto fn) { constexpr static void apply(auto fn) {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0) {
auto mask = mergeMasks<Masks...>(); constexpr auto mask = mergeMasks<Masks...>();
if constexpr (mask) if constexpr (mask)
write(fn(read(), mask)); write(fn(read(), mask));
} else { } else {
@ -202,10 +279,11 @@ struct Register {
} }
} }
private:
// Takes a list of bit-masks, and returns a merged mask of those which are // Takes a list of bit-masks, and returns a merged mask of those which are
// meant for this register. // meant for this register.
template<typename... Masks> template<typename... Masks>
static auto mergeMasks() { constexpr static auto mergeMasks() {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0) {
if constexpr ((isThis<typename Masks::reg> | ...)) { if constexpr ((isThis<typename Masks::reg> | ...)) {
auto mask = auto mask =
@ -224,20 +302,20 @@ struct Register {
#ifdef FUNREG_ENABLE_EXTERNAL_IO #ifdef FUNREG_ENABLE_EXTERNAL_IO
// Determines if the given register matches this one. // Determines if the given register matches this one.
template<typename Reg> template<typename Reg>
requires(IsRegister<Reg>)
constexpr static bool isThis = [] { constexpr static bool isThis = [] {
return std::is_same_v<typename Reg::access, access> && Addr == Reg::Addr; return std::is_same_v<typename Reg::access, access> && Addr == Reg::Addr;
}(); }();
#else #else
// Determines if the given register matches this one. // Determines if the given register matches this one.
template<typename Reg> template<typename Reg>
requires(IsRegister<Reg>)
constexpr static bool isThis = [] { constexpr static bool isThis = [] {
return Addr == Reg::Addr; return Addr == Reg::Addr;
}(); }();
#endif // FUNREG_ENABLE_EXTERNAL_IO #endif // FUNREG_ENABLE_EXTERNAL_IO
Register() = delete; Register() = delete;
using type = T;
}; };
/** /**
@ -251,6 +329,7 @@ struct Register {
* using LED_RED = RegisterMask<GPIO_OUT, (1 << 2)>; * using LED_RED = RegisterMask<GPIO_OUT, (1 << 2)>;
*/ */
template<typename Reg, typename Reg::type Mask> template<typename Reg, typename Reg::type Mask>
requires IsRegister<Reg>
struct RegisterMask struct RegisterMask
{ {
using T = typename Reg::type; using T = typename Reg::type;
@ -373,6 +452,7 @@ struct RegisterMask
* @tparam value The value to be used for the given Mask. * @tparam value The value to be used for the given Mask.
*/ */
template<typename Mask, Mask::T value> template<typename Mask, Mask::T value>
requires(IsRegisterMask<Mask>)
struct RegisterMaskValue struct RegisterMaskValue
{ {
/** /**
@ -406,6 +486,7 @@ struct RegisterMaskValue
* each register. * each register.
*/ */
template<typename... Registers> template<typename... Registers>
requires(IsRegister<Registers>, ...)
class RegisterGroup class RegisterGroup
{ {
public: public:
@ -415,6 +496,8 @@ public:
* only written once. * only written once.
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesSomeRegister<Masks, Registers...>, ...))
static void set() { static void set() {
apply<Masks...>([](auto r, auto m) { return r | m; }); apply<Masks...>([](auto r, auto m) { return r | m; });
} }
@ -425,6 +508,8 @@ public:
* Only reads and writes each register once; see set(). * Only reads and writes each register once; see set().
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesSomeRegister<Masks, Registers...>, ...))
static void clear() { static void clear() {
apply<Masks...>([](auto r, auto m) { return r & ~m; }); apply<Masks...>([](auto r, auto m) { return r & ~m; });
} }
@ -435,6 +520,8 @@ public:
* Only reads and writes each register once; see set(). * Only reads and writes each register once; see set().
*/ */
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesSomeRegister<Masks, Registers...>, ...))
static void toggle() { static void toggle() {
apply<Masks...>([](auto r, auto m) { return r ^ m; }); apply<Masks...>([](auto r, auto m) { return r ^ m; });
} }
@ -453,6 +540,8 @@ public:
private: private:
template<typename... Masks> template<typename... Masks>
requires((IsRegisterMask<Masks>, ...) &&
(UsesSomeRegister<Masks, Registers...>, ...))
static void apply(auto fn) { static void apply(auto fn) {
(Registers::template apply<Masks...>(fn), ...); (Registers::template apply<Masks...>(fn), ...);
} }
@ -465,6 +554,7 @@ private:
* register. * register.
*/ */
template<typename... RegMasks> template<typename... RegMasks>
requires(IsRegisterMask<RegMasks>, ...)
constexpr auto Masks = (RegMasks::mask | ...); constexpr auto Masks = (RegMasks::mask | ...);
#ifdef FUNREG_ENABLE_EXTERNAL_IO #ifdef FUNREG_ENABLE_EXTERNAL_IO
@ -501,4 +591,3 @@ using MemRegister = Register<T, Addr>;
} // namespace fr } // namespace fr
#endif // FUNCTIONAL_REGISTER_IO_H #endif // FUNCTIONAL_REGISTER_IO_H

Loading…
Cancel
Save