|
|
|
@ -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
|
|
|
|
|
|
|
|
|
|