aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2022-08-08 08:42:21 -0400
committerClyne Sullivan <clyne@bitgloo.com>2022-08-08 08:42:21 -0400
commitc0ca2d3a373e66f474d21ce34c25b38d953a66c7 (patch)
tree325ea827749f2ae62c783b0ac54e580f54b9615e
parent0a3a9188e70ca435f600dc1cceee614895468572 (diff)
external i/o implemented
-rw-r--r--funreg.hpp122
1 files changed, 88 insertions, 34 deletions
diff --git a/funreg.hpp b/funreg.hpp
index 24a34fe..6ca44ed 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,8 +7,19 @@
#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.
@@ -20,8 +31,18 @@ constexpr auto BitOffset = []() constexpr {
return BitOffset<(Mask >> 1), (N + 1)>;
}();
+/**
+ * @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.
+ *
+ * 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;
/**
@@ -39,48 +60,38 @@ struct MemoryIO {
}
};
-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
- * @brief Defines a memory-mapped register, given bit-size and address.
- * @tparam T The integer type that matches 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.
+ * @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.
*/
-template<typename T, typename RegAccess>
+#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 {
- constexpr static auto Addr = RegAccess::addr;
+ using RegAccess = MemoryIO<T, Addr>;
+#endif // FUNREG_ENABLE_EXTERNAL_IO
/**
* Gets a pointer to the register.
*/
constexpr static T read() {
- return RegAccess::read();
+ return Access::read();
}
/**
* Overwrites the register's value.
*/
constexpr static void write(const T& value) {
- RegAccess::write(value);
+ Access::write(value);
}
/**
@@ -164,10 +175,10 @@ struct Register {
*/
template<typename... Ops>
static void modify() {
- if (((Addr == Ops::reg::addr) | ...)) {
+ if constexpr ((isThis<typename Ops::reg> | ...)) {
auto mask = read();
([&mask] {
- if (Addr == Ops::reg::addr)
+ if constexpr (isThis<typename Ops::reg>)
mask = Ops(mask);
}(), ...);
write(mask);
@@ -184,20 +195,22 @@ struct Register {
static void apply(auto fn) {
if constexpr (sizeof...(Masks) > 0) {
auto mask = mergeMasks<Masks...>();
- if (mask)
+ 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 (((Addr == Masks::reg::addr) | ...)) {
+ if constexpr ((isThis<typename Masks::reg> | ...)) {
auto mask =
([] {
- return Addr == Masks::reg::addr ? Masks::mask : 0;
+ return isThis<typename Masks::reg> ? Masks::mask : 0;
}() | ...);
return mask;
} else {
@@ -208,10 +221,23 @@ struct Register {
}
}
+#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;
};
/**
@@ -441,8 +467,36 @@ 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, MemoryIO<T, Addr>>;
+using MemRegister = Register<T, Addr>;
+#endif // FUNREG_ENABLE_EXTERNAL_IO
} // namespace fr