|
|
|
@ -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
|
|
|
|
|
|
|
|
|
|