]> code.bitgloo.com Git - clyne/funreg.git/commitdiff
external i/o implemented
authorClyne Sullivan <clyne@bitgloo.com>
Mon, 8 Aug 2022 12:42:21 +0000 (08:42 -0400)
committerClyne Sullivan <clyne@bitgloo.com>
Mon, 8 Aug 2022 12:42:21 +0000 (08:42 -0400)
funreg.hpp

index 24a34feb948152775927570620f0a806f4883cc0..6ca44ed51acf86e399c023eaa781b89486b04798 100644 (file)
@@ -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