diff --git a/funreg.hpp b/funreg.hpp index 156b6cf..24a34fe 100644 --- a/funreg.hpp +++ b/funreg.hpp @@ -11,7 +11,7 @@ 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 constexpr auto BitOffset = []() constexpr { if constexpr (Mask & 1) @@ -20,6 +20,40 @@ constexpr auto BitOffset = []() constexpr { return BitOffset<(Mask >> 1), (N + 1)>; }(); +template +struct MemoryIO { + constexpr static auto addr = Addr; + + /** + * Gets a pointer to the register. + */ + constexpr static T read() { + return *reinterpret_cast(Addr); + } + + /** + * Overwrites the register's value. + */ + constexpr static void write(const T& value) { + *reinterpret_cast(Addr) = value; + } +}; + +template +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. @@ -31,13 +65,22 @@ constexpr auto BitOffset = []() constexpr { * * Use only as a type, e.g. "using GPIO_OUT = Register" */ -template +template struct Register { + constexpr static auto Addr = RegAccess::addr; + /** * Gets a pointer to the register. */ - constexpr static auto get() { - return reinterpret_cast(Addr); + constexpr static T read() { + 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 static void set() { - apply([](auto r, auto m) { *r = *r | m; }); + apply([](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 +103,14 @@ struct Register { */ template static void clear() { - apply([](auto r, auto m) { *r = *r & ~m; }); + apply([](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 +118,14 @@ struct Register { */ template static void toggle() { - apply([](auto r, auto m) { *r = *r ^ m; }); + apply([](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 +135,10 @@ struct Register { */ template 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(); + else + return read(); } /** @@ -121,17 +148,10 @@ struct Register { template 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(); + return (read() & mask) == mask; } else { - return *get() != 0; + return read() != 0; } } @@ -145,16 +165,16 @@ struct Register { template static void modify() { if (((Addr == Ops::reg::addr) | ...)) { - auto mask = *get(); + auto mask = read(); ([&mask] { if (Addr == Ops::reg::addr) 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,14 +183,28 @@ struct Register { template static void apply(auto fn) { if constexpr (sizeof...(Masks) > 0) { - auto mask = + auto mask = mergeMasks(); + if (mask) + write(fn(read(), mask)); + } else { + write(fn(read(), T(0) - 1)); + } + } + + template + static auto mergeMasks() { + if constexpr (sizeof...(Masks) > 0) { + if (((Addr == Masks::reg::addr) | ...)) { + auto mask = ([] { return Addr == Masks::reg::addr ? Masks::mask : 0; }() | ...); - if (mask) - fn(get(), mask); + return mask; + } else { + return 0; + } } else { - fn(get(), T(0) - 1); + return 0; } } @@ -203,7 +237,7 @@ struct RegisterMask */ struct set { constexpr set() { - *Reg::get() = *Reg::get() | Mask; + Reg::write(Reg::read() | Mask); } // For internal use. @@ -219,7 +253,7 @@ struct RegisterMask */ struct clear { constexpr clear() { - *Reg::get() = *Reg::get() & ~Mask; + Reg::write(Reg::read() & ~Mask); } // For internal use. @@ -235,7 +269,7 @@ struct RegisterMask */ struct toggle { constexpr toggle() { - *Reg::get() = *Reg::get() ^ Mask; + Reg::write(Reg::read() ^ Mask); } // For internal use. @@ -249,7 +283,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 +306,10 @@ struct RegisterMask template struct write { constexpr write() { - auto r = *Reg::get(); + auto r = Reg::read(); r &= ~Mask; r |= value << BitOffset; - *Reg::get() = r; + Reg::write(r); } // For internal use. @@ -352,11 +386,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 static void set() { - apply([](auto r, auto m) { *r = *r | m; }); + apply([](auto r, auto m) { return r | m; }); } /** @@ -366,7 +400,7 @@ public: */ template static void clear() { - apply([](auto r, auto m) { *r = *r & ~m; }); + apply([](auto r, auto m) { return r & ~m; }); } /** @@ -376,7 +410,7 @@ public: */ template static void toggle() { - apply([](auto r, auto m) { *r = *r ^ m; }); + apply([](auto r, auto m) { return r ^ m; }); } /** @@ -407,6 +441,10 @@ private: template constexpr auto Masks = (RegMasks::mask | ...); +template +using MemRegister = Register>; + } // namespace fr #endif // FUNCTIONAL_REGISTER_IO_H +