draft; make reg access generic

pull/1/head
Clyne 2 years ago
parent 30fd7bb79f
commit 0a3a9188e7

@ -11,7 +11,7 @@
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 { constexpr auto BitOffset = []() constexpr {
if constexpr (Mask & 1) if constexpr (Mask & 1)
@ -20,6 +20,40 @@ constexpr auto BitOffset = []() constexpr {
return BitOffset<(Mask >> 1), (N + 1)>; return BitOffset<(Mask >> 1), (N + 1)>;
}(); }();
template<typename T, uintptr_t Addr>
struct MemoryIO {
constexpr static auto addr = Addr;
/**
* Gets a pointer to the register.
*/
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;
}
};
template<typename T, uintptr_t Addr>
struct ExternalIO {
constexpr static auto addr = Addr;
/**
* Gets a pointer to the register.
*/
static T (*read)();
/**
* Overwrites the register's value.
*/
static void (*write)(const T& value);
};
/** /**
* @struct Register * @struct Register
* @brief Defines a memory-mapped register, given bit-size and address. * @brief Defines a memory-mapped register, given bit-size and address.
@ -31,13 +65,22 @@ constexpr auto BitOffset = []() constexpr {
* *
* Use only as a type, e.g. "using GPIO_OUT = Register<uint32_t, 0xA0004120>" * Use only as a type, e.g. "using GPIO_OUT = Register<uint32_t, 0xA0004120>"
*/ */
template<typename T, uintptr_t Addr> template<typename T, typename RegAccess>
struct Register { struct Register {
constexpr static auto Addr = RegAccess::addr;
/** /**
* Gets a pointer to the register. * Gets a pointer to the register.
*/ */
constexpr static auto get() { constexpr static T read() {
return reinterpret_cast<volatile T*>(Addr); return RegAccess::read();
}
/**
* Overwrites the register's value.
*/
constexpr static void write(const T& value) {
RegAccess::write(value);
} }
/** /**
@ -45,14 +88,14 @@ struct Register {
*/ */
template<typename... Masks> template<typename... Masks>
static void set() { 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. * Sets register bits to '1' according to the given mask.
*/ */
static void set(const T& mask) { static void set(const T& mask) {
*get() = *get() | mask; write(read() | mask);
} }
/** /**
@ -60,14 +103,14 @@ struct Register {
*/ */
template<typename... Masks> template<typename... Masks>
static void clear() { 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. * Clears register bits to '0' according to the given mask.
*/ */
static void clear(const T& mask) { static void clear(const T& mask) {
*get() = *get() & ~mask; write(read() & ~mask);
} }
/** /**
@ -75,14 +118,14 @@ struct Register {
*/ */
template<typename... Masks> template<typename... Masks>
static void toggle() { 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. * Toggles bits in the register according to the given mask.
*/ */
static void toggle(const T& mask) { static void toggle(const T& mask) {
*get() = *get() ^ mask; write(read() ^ mask);
} }
/** /**
@ -92,26 +135,10 @@ struct Register {
*/ */
template<typename... Masks> template<typename... Masks>
static auto read() { static auto read() {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0)
if (((Addr == Masks::reg::addr) | ...)) { return read() & mergeMasks<Masks...>();
auto mask = else
([] { return read();
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;
} }
/** /**
@ -121,17 +148,10 @@ struct Register {
template<typename... Masks> template<typename... Masks>
static bool test() { static bool test() {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0) {
if (((Addr == Masks::reg::addr) | ...)) { auto mask = mergeMasks<Masks...>();
auto mask = return (read() & mask) == mask;
([] {
return Addr == Masks::reg::addr ? Masks::mask : 0;
}() | ...);
return (*get() & mask) == mask;
} else {
return 0;
}
} else { } else {
return *get() != 0; return read() != 0;
} }
} }
@ -145,16 +165,16 @@ struct Register {
template<typename... Ops> template<typename... Ops>
static void modify() { static void modify() {
if (((Addr == Ops::reg::addr) | ...)) { if (((Addr == Ops::reg::addr) | ...)) {
auto mask = *get(); auto mask = read();
([&mask] { ([&mask] {
if (Addr == Ops::reg::addr) if (Addr == Ops::reg::addr)
mask = Ops(mask); 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. // Applies bit-masks to the register through the provided function.
// The provided function receives a pointer to the register's data and a // The provided function receives a pointer to the register's data and a
@ -163,14 +183,28 @@ struct Register {
template<typename... Masks> template<typename... Masks>
static void apply(auto fn) { static void apply(auto fn) {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0) {
auto mask = auto mask = mergeMasks<Masks...>();
if (mask)
write(fn(read(), mask));
} else {
write(fn(read(), T(0) - 1));
}
}
template<typename... Masks>
static auto mergeMasks() {
if constexpr (sizeof...(Masks) > 0) {
if (((Addr == Masks::reg::addr) | ...)) {
auto mask =
([] { ([] {
return Addr == Masks::reg::addr ? Masks::mask : 0; return Addr == Masks::reg::addr ? Masks::mask : 0;
}() | ...); }() | ...);
if (mask) return mask;
fn(get(), mask); } else {
return 0;
}
} else { } else {
fn(get(), T(0) - 1); return 0;
} }
} }
@ -203,7 +237,7 @@ struct RegisterMask
*/ */
struct set { struct set {
constexpr set() { constexpr set() {
*Reg::get() = *Reg::get() | Mask; Reg::write(Reg::read() | Mask);
} }
// For internal use. // For internal use.
@ -219,7 +253,7 @@ struct RegisterMask
*/ */
struct clear { struct clear {
constexpr clear() { constexpr clear() {
*Reg::get() = *Reg::get() & ~Mask; Reg::write(Reg::read() & ~Mask);
} }
// For internal use. // For internal use.
@ -235,7 +269,7 @@ struct RegisterMask
*/ */
struct toggle { struct toggle {
constexpr toggle() { constexpr toggle() {
*Reg::get() = *Reg::get() ^ Mask; Reg::write(Reg::read() ^ Mask);
} }
// For internal use. // For internal use.
@ -249,7 +283,7 @@ struct RegisterMask
* Reads from the paired register, applying the bit-mask. * Reads from the paired register, applying the bit-mask.
*/ */
static auto read() { static auto read() {
return *Reg::get() & Mask; return Reg::read() & Mask;
} }
/** /**
@ -272,10 +306,10 @@ struct RegisterMask
template<T value> template<T value>
struct write { struct write {
constexpr write() { constexpr write() {
auto r = *Reg::get(); auto r = Reg::read();
r &= ~Mask; r &= ~Mask;
r |= value << BitOffset<Mask>; r |= value << BitOffset<Mask>;
*Reg::get() = r; Reg::write(r);
} }
// For internal use. // For internal use.
@ -352,11 +386,11 @@ public:
/** /**
* Sets bits throughout this group's registers according to the given masks. * 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 * Bit-masks for the same register will be merged so that each register is
* only written once. * only written once.
*/ */
template<typename... Masks> template<typename... Masks>
static void set() { static void set() {
apply<Masks...>([](auto r, auto m) { *r = *r | m; }); apply<Masks...>([](auto r, auto m) { return r | m; });
} }
/** /**
@ -366,7 +400,7 @@ public:
*/ */
template<typename... Masks> template<typename... Masks>
static void clear() { static void clear() {
apply<Masks...>([](auto r, auto m) { *r = *r & ~m; }); apply<Masks...>([](auto r, auto m) { return r & ~m; });
} }
/** /**
@ -376,7 +410,7 @@ public:
*/ */
template<typename... Masks> template<typename... Masks>
static void toggle() { static void toggle() {
apply<Masks...>([](auto r, auto m) { *r = *r ^ m; }); apply<Masks...>([](auto r, auto m) { return r ^ m; });
} }
/** /**
@ -407,6 +441,10 @@ private:
template<typename... RegMasks> template<typename... RegMasks>
constexpr auto Masks = (RegMasks::mask | ...); constexpr auto Masks = (RegMasks::mask | ...);
template<typename T, uintptr_t Addr>
using MemRegister = Register<T, MemoryIO<T, Addr>>;
} // namespace fr } // namespace fr
#endif // FUNCTIONAL_REGISTER_IO_H #endif // FUNCTIONAL_REGISTER_IO_H

Loading…
Cancel
Save