external i/o implemented

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

@ -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. * Written by Clyne Sullivan.
* <https://github.com/tcsullivan/funreg> * <https://github.com/tcsullivan/funreg>
*/ */
@ -7,8 +7,19 @@
#ifndef FUNCTIONAL_REGISTER_IO_H #ifndef FUNCTIONAL_REGISTER_IO_H
#define 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> #include <stdint.h>
#ifdef FUNREG_ENABLE_EXTERNAL_IO
#include <type_traits>
#endif
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.
@ -20,8 +31,18 @@ constexpr auto BitOffset = []() constexpr {
return BitOffset<(Mask >> 1), (N + 1)>; 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> template<typename T, uintptr_t Addr>
struct MemoryIO { struct MemoryIO {
using type = T;
constexpr static auto addr = Addr; 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 * @struct Register
* @brief Defines a memory-mapped register, given bit-size and address. * @brief Defines a register, given how to access it.
* @tparam T The integer type that matches the size of the register. * @tparam Access Specifies register access. See MemoryIO for an example.
* @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.
* *
* 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 { struct Register {
constexpr static auto Addr = RegAccess::addr; using RegAccess = MemoryIO<T, Addr>;
#endif // FUNREG_ENABLE_EXTERNAL_IO
/** /**
* Gets a pointer to the register. * Gets a pointer to the register.
*/ */
constexpr static T read() { constexpr static T read() {
return RegAccess::read(); return Access::read();
} }
/** /**
* Overwrites the register's value. * Overwrites the register's value.
*/ */
constexpr static void write(const T& value) { constexpr static void write(const T& value) {
RegAccess::write(value); Access::write(value);
} }
/** /**
@ -164,10 +175,10 @@ struct Register {
*/ */
template<typename... Ops> template<typename... Ops>
static void modify() { static void modify() {
if (((Addr == Ops::reg::addr) | ...)) { if constexpr ((isThis<typename Ops::reg> | ...)) {
auto mask = read(); auto mask = read();
([&mask] { ([&mask] {
if (Addr == Ops::reg::addr) if constexpr (isThis<typename Ops::reg>)
mask = Ops(mask); mask = Ops(mask);
}(), ...); }(), ...);
write(mask); write(mask);
@ -184,20 +195,22 @@ struct Register {
static void apply(auto fn) { static void apply(auto fn) {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0) {
auto mask = mergeMasks<Masks...>(); auto mask = mergeMasks<Masks...>();
if (mask) if constexpr (mask)
write(fn(read(), mask)); write(fn(read(), mask));
} else { } else {
write(fn(read(), T(0) - 1)); 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> template<typename... Masks>
static auto mergeMasks() { static auto mergeMasks() {
if constexpr (sizeof...(Masks) > 0) { if constexpr (sizeof...(Masks) > 0) {
if (((Addr == Masks::reg::addr) | ...)) { if constexpr ((isThis<typename Masks::reg> | ...)) {
auto mask = auto mask =
([] { ([] {
return Addr == Masks::reg::addr ? Masks::mask : 0; return isThis<typename Masks::reg> ? Masks::mask : 0;
}() | ...); }() | ...);
return mask; return mask;
} else { } 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; Register() = delete;
using type = T; using type = T;
constexpr static auto addr = Addr;
}; };
/** /**
@ -441,8 +467,36 @@ private:
template<typename... RegMasks> template<typename... RegMasks>
constexpr auto Masks = (RegMasks::mask | ...); 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> 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 } // namespace fr

Loading…
Cancel
Save