aboutsummaryrefslogtreecommitdiffstats
path: root/funreg.hpp
diff options
context:
space:
mode:
authorClyne <clyne@bitgloo.com>2022-08-10 20:00:26 -0400
committerClyne <clyne@bitgloo.com>2022-08-10 20:00:26 -0400
commit2a5fe35aee6c561f991f954fb701c2f5749b51fe (patch)
treecb0d494fddb02d31a550dedd217330db1cfc528a /funreg.hpp
parent30fd7bb79fe88aad8740543dd29a56b8f7b55a01 (diff)
parenta35ded8145691d7f616a498da432dcbced8bb0ef (diff)
Merge pull request 'external-register' (#1) from external-register into master
Reviewed-on: https://code.bitgloo.com/clyne/funreg/pulls/1
Diffstat (limited to 'funreg.hpp')
-rw-r--r--funreg.hpp228
1 files changed, 160 insertions, 68 deletions
diff --git a/funreg.hpp b/funreg.hpp
index 156b6cf..00cd3a9 100644
--- a/funreg.hpp
+++ b/funreg.hpp
@@ -1,5 +1,5 @@
/**
- * funreg.hpp - Functional memory-mapped register I/O using modern C++.
+ * funreg.hpp - Functional register I/O using modern C++.
* Written by Clyne Sullivan.
* <https://github.com/tcsullivan/funreg>
*/
@@ -7,11 +7,22 @@
#ifndef FUNCTIONAL_REGISTER_IO_H
#define FUNCTIONAL_REGISTER_IO_H
+/**
+ * Comment to disable external/custom register access.
+ * When disabled, only memory-mapped register access is supported.
+ * fr::Register can then also be used instead of fr::MemRegister.
+ */
+#define FUNREG_ENABLE_EXTERNAL_IO
+
#include <stdint.h>
+#ifdef FUNREG_ENABLE_EXTERNAL_IO
+#include <type_traits>
+#endif
+
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>
constexpr auto BitOffset = []() constexpr {
if constexpr (Mask & 1)
@@ -21,23 +32,66 @@ constexpr auto BitOffset = []() constexpr {
}();
/**
- * @struct Register
- * @brief Defines a memory-mapped register, given bit-size and address.
- * @tparam T The integer type that matches the size of the register.
+ * @struct MemoryIO
+ * @brief Specifies how to access a memory-mapped register.
+ * @tparam T The size of the register.
* @tparam Addr The memory address of the register.
*
- * Defines a memory-mapped register that is usually accessed with a pointer of
- * type T*, and is located at address Addr.
+ * To create an I/O access type for external register access, use this
+ * structure as a template.
+ */
+template<typename T, uintptr_t Addr>
+struct MemoryIO {
+ using type = T;
+ constexpr static auto addr = Addr;
+
+ /**
+ * Reads the register's value.
+ */
+ 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;
+ }
+};
+
+/**
+ * @struct Register
+ * @brief Defines a register, given how to access it.
+ * @tparam Access Specifies register access. See MemoryIO for an example.
*
- * Use only as a type, e.g. "using GPIO_OUT = Register<uint32_t, 0xA0004120>"
+ * When FUNREG_ENABLE_EXTERNAL_IO is not defined, Register assumes MemoryIO
+ * access. The template parameters become that of MemoryIO.
*/
+#ifdef FUNREG_ENABLE_EXTERNAL_IO
+template<typename Access>
+struct Register {
+ 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>;
+#endif // FUNREG_ENABLE_EXTERNAL_IO
+
/**
* Gets a pointer to the register.
*/
- constexpr static auto get() {
- return reinterpret_cast<volatile T*>(Addr);
+ constexpr static T read() {
+ return Access::read();
+ }
+
+ /**
+ * Overwrites the register's value.
+ */
+ constexpr static void write(const T& value) {
+ Access::write(value);
}
/**
@@ -45,14 +99,14 @@ struct Register {
*/
template<typename... Masks>
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.
*/
static void set(const T& mask) {
- *get() = *get() | mask;
+ write(read() | mask);
}
/**
@@ -60,14 +114,14 @@ struct Register {
*/
template<typename... Masks>
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.
*/
static void clear(const T& mask) {
- *get() = *get() & ~mask;
+ write(read() & ~mask);
}
/**
@@ -75,14 +129,14 @@ struct Register {
*/
template<typename... Masks>
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.
*/
static void toggle(const T& mask) {
- *get() = *get() ^ mask;
+ write(read() ^ mask);
}
/**
@@ -92,26 +146,10 @@ struct Register {
*/
template<typename... Masks>
static auto read() {
- if constexpr (sizeof...(Masks) > 0) {
- if (((Addr == Masks::reg::addr) | ...)) {
- auto mask =
- ([] {
- 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;
+ if constexpr (sizeof...(Masks) > 0)
+ return read() & mergeMasks<Masks...>();
+ else
+ return read();
}
/**
@@ -121,17 +159,10 @@ struct Register {
template<typename... Masks>
static bool test() {
if constexpr (sizeof...(Masks) > 0) {
- if (((Addr == Masks::reg::addr) | ...)) {
- auto mask =
- ([] {
- return Addr == Masks::reg::addr ? Masks::mask : 0;
- }() | ...);
- return (*get() & mask) == mask;
- } else {
- return 0;
- }
+ auto mask = mergeMasks<Masks...>();
+ return (read() & mask) == mask;
} else {
- return *get() != 0;
+ return read() != 0;
}
}
@@ -144,17 +175,17 @@ struct Register {
*/
template<typename... Ops>
static void modify() {
- if (((Addr == Ops::reg::addr) | ...)) {
- auto mask = *get();
+ if constexpr ((isThis<typename Ops::reg> | ...)) {
+ auto mask = read();
([&mask] {
- if (Addr == Ops::reg::addr)
+ if constexpr (isThis<typename Ops::reg>)
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.
// The provided function receives a pointer to the register's data and a
@@ -163,21 +194,50 @@ struct Register {
template<typename... Masks>
static void apply(auto fn) {
if constexpr (sizeof...(Masks) > 0) {
- auto mask =
+ auto mask = mergeMasks<Masks...>();
+ if constexpr (mask)
+ write(fn(read(), mask));
+ } else {
+ write(fn(read(), T(0) - 1));
+ }
+ }
+
+ // Takes a list of bit-masks, and returns a merged mask of those which are
+ // meant for this register.
+ template<typename... Masks>
+ static auto mergeMasks() {
+ if constexpr (sizeof...(Masks) > 0) {
+ if constexpr ((isThis<typename Masks::reg> | ...)) {
+ auto mask =
([] {
- return Addr == Masks::reg::addr ? Masks::mask : 0;
+ return isThis<typename Masks::reg> ? Masks::mask : 0;
}() | ...);
- if (mask)
- fn(get(), mask);
+ return mask;
+ } else {
+ return 0;
+ }
} else {
- fn(get(), T(0) - 1);
+ return 0;
}
}
+#ifdef FUNREG_ENABLE_EXTERNAL_IO
+ // Determines if the given register matches this one.
+ template<typename 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>
+ constexpr static bool isThis = [] {
+ return Addr == Reg::Addr;
+ }();
+#endif // FUNREG_ENABLE_EXTERNAL_IO
+
Register() = delete;
using type = T;
- constexpr static auto addr = Addr;
};
/**
@@ -203,7 +263,7 @@ struct RegisterMask
*/
struct set {
constexpr set() {
- *Reg::get() = *Reg::get() | Mask;
+ Reg::write(Reg::read() | Mask);
}
// For internal use.
@@ -219,7 +279,7 @@ struct RegisterMask
*/
struct clear {
constexpr clear() {
- *Reg::get() = *Reg::get() & ~Mask;
+ Reg::write(Reg::read() & ~Mask);
}
// For internal use.
@@ -235,7 +295,7 @@ struct RegisterMask
*/
struct toggle {
constexpr toggle() {
- *Reg::get() = *Reg::get() ^ Mask;
+ Reg::write(Reg::read() ^ Mask);
}
// For internal use.
@@ -249,7 +309,7 @@ struct RegisterMask
* Reads from the paired register, applying the bit-mask.
*/
static auto read() {
- return *Reg::get() & Mask;
+ return Reg::read() & Mask;
}
/**
@@ -272,10 +332,10 @@ struct RegisterMask
template<T value>
struct write {
constexpr write() {
- auto r = *Reg::get();
+ auto r = Reg::read();
r &= ~Mask;
r |= value << BitOffset<Mask>;
- *Reg::get() = r;
+ Reg::write(r);
}
// For internal use.
@@ -332,7 +392,7 @@ struct RegisterMaskValue
* Tests if this value is currently set in the register.
*/
static bool test() {
- return Mask::read() & (value << BitOffset<Mask>);
+ return (Mask::read() & Mask::mask) == (value << BitOffset<Mask>);
}
};
@@ -352,11 +412,11 @@ public:
/**
* 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
- * only written once.
+ * only written once.
*/
template<typename... Masks>
static void set() {
- apply<Masks...>([](auto r, auto m) { *r = *r | m; });
+ apply<Masks...>([](auto r, auto m) { return r | m; });
}
/**
@@ -366,7 +426,7 @@ public:
*/
template<typename... Masks>
static void clear() {
- apply<Masks...>([](auto r, auto m) { *r = *r & ~m; });
+ apply<Masks...>([](auto r, auto m) { return r & ~m; });
}
/**
@@ -376,7 +436,7 @@ public:
*/
template<typename... Masks>
static void toggle() {
- apply<Masks...>([](auto r, auto m) { *r = *r ^ m; });
+ apply<Masks...>([](auto r, auto m) { return r ^ m; });
}
/**
@@ -407,6 +467,38 @@ private:
template<typename... RegMasks>
constexpr auto Masks = (RegMasks::mask | ...);
+#ifdef FUNREG_ENABLE_EXTERNAL_IO
+/**
+ * Defines a register that is accessed through memory, i.e. memory-mapped.
+ * @tparam T The variable type used to access the register (e.g. uint32_t).
+ * @tparam Addr The memory address of the register.
+ */
+template<typename T, uintptr_t Addr>
+using MemRegister = Register<MemoryIO<T, Addr>>;
+
+/**
+ * Defines a register that is accessed through external or custom means.
+ * @tparam ExtIO A type that provides access functionality (e.g. MemoryIO).
+ * @tparam T The variable type used to access the register (e.g. uint32_t).
+ * @tparam Addr The memory address of the register.
+ *
+ * Custom access types should be defined using MemoryIO as a template.
+ */
+template<template<typename, uintptr_t> typename ExtIO, typename T, uintptr_t Addr>
+using ExtRegister = Register<ExtIO<T, Addr>>;
+#else
+/**
+ * Defines a register that is accessed through memory, i.e. memory-mapped.
+ * @tparam T The variable type used to access the register (e.g. uint32_t).
+ * @tparam Addr The memory address of the register.
+ *
+ * With external I/O disabled, the Register type may be used directly instead.
+ */
+template<typename T, uintptr_t Addr>
+using MemRegister = Register<T, Addr>;
+#endif // FUNREG_ENABLE_EXTERNAL_IO
+
} // namespace fr
#endif // FUNCTIONAL_REGISTER_IO_H
+