You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
451 lines
12 KiB
C++
451 lines
12 KiB
C++
/**
|
|
* funreg.hpp - Functional memory-mapped register I/O using modern C++.
|
|
* Written by Clyne Sullivan.
|
|
* <https://github.com/tcsullivan/funreg>
|
|
*/
|
|
|
|
#ifndef FUNCTIONAL_REGISTER_IO_H
|
|
#define FUNCTIONAL_REGISTER_IO_H
|
|
|
|
#include <stdint.h>
|
|
|
|
namespace fr {
|
|
|
|
// A utility to measure a bit-mask's offset from bit zero.
|
|
template<auto Mask, unsigned int N = 0>
|
|
constexpr auto BitOffset = []() constexpr {
|
|
if constexpr (Mask & 1)
|
|
return N;
|
|
else
|
|
return BitOffset<(Mask >> 1), (N + 1)>;
|
|
}();
|
|
|
|
template<typename T, uintptr_t Addr>
|
|
struct MemoryIO {
|
|
constexpr static auto addr = Addr;
|
|
|
|
/**
|
|
* Gets a pointer to the register.
|
|
*/
|
|
constexpr static T read() {
|
|
return *reinterpret_cast<volatile T*>(Addr);
|
|
}
|
|
|
|
/**
|
|
* Overwrites the register's value.
|
|
*/
|
|
constexpr static void write(const T& value) {
|
|
*reinterpret_cast<volatile T*>(Addr) = value;
|
|
}
|
|
};
|
|
|
|
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.
|
|
*
|
|
* Use only as a type, e.g. "using GPIO_OUT = Register<uint32_t, 0xA0004120>"
|
|
*/
|
|
template<typename T, typename RegAccess>
|
|
struct Register {
|
|
constexpr static auto Addr = RegAccess::addr;
|
|
|
|
/**
|
|
* Gets a pointer to the register.
|
|
*/
|
|
constexpr static T read() {
|
|
return RegAccess::read();
|
|
}
|
|
|
|
/**
|
|
* Overwrites the register's value.
|
|
*/
|
|
constexpr static void write(const T& value) {
|
|
RegAccess::write(value);
|
|
}
|
|
|
|
/**
|
|
* Sets register bits to '1' according to the given RegisterMasks.
|
|
*/
|
|
template<typename... Masks>
|
|
static void set() {
|
|
apply<Masks...>([](auto r, auto m) { return r | m; });
|
|
}
|
|
|
|
/**
|
|
* Sets register bits to '1' according to the given mask.
|
|
*/
|
|
static void set(const T& mask) {
|
|
write(read() | mask);
|
|
}
|
|
|
|
/**
|
|
* Clears register bits to '0' according to the given RegisterMasks.
|
|
*/
|
|
template<typename... Masks>
|
|
static void clear() {
|
|
apply<Masks...>([](auto r, auto m) { return r & ~m; });
|
|
}
|
|
|
|
/**
|
|
* Clears register bits to '0' according to the given mask.
|
|
*/
|
|
static void clear(const T& mask) {
|
|
write(read() & ~mask);
|
|
}
|
|
|
|
/**
|
|
* Toggles bits in the register according to the given RegisterMasks.
|
|
*/
|
|
template<typename... Masks>
|
|
static void toggle() {
|
|
apply<Masks...>([](auto r, auto m) { return r ^m; });
|
|
}
|
|
|
|
/**
|
|
* Toggles bits in the register according to the given mask.
|
|
*/
|
|
static void toggle(const T& mask) {
|
|
write(read() ^ mask);
|
|
}
|
|
|
|
/**
|
|
* Reads the current value stored in the register, masking bits according to
|
|
* the given RegisterMasks.
|
|
* If no masks are given, all register bits are returned.
|
|
*/
|
|
template<typename... Masks>
|
|
static auto read() {
|
|
if constexpr (sizeof...(Masks) > 0)
|
|
return read() & mergeMasks<Masks...>();
|
|
else
|
|
return read();
|
|
}
|
|
|
|
/**
|
|
* Reads the register, and tests if all of the given bits are set.
|
|
* If no masks are given, tests if the register has a non-zero value.
|
|
*/
|
|
template<typename... Masks>
|
|
static bool test() {
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
auto mask = mergeMasks<Masks...>();
|
|
return (read() & mask) == mask;
|
|
} else {
|
|
return read() != 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Modifies the register's contents according to the given operations.
|
|
* The register will only be read and written once.
|
|
* Possible operations include RegisterMask::set, RegisterMask::clear,
|
|
* RegisterMask::toggle, RegisterMask::write<>, RegisterMaskValue::set,
|
|
* and RegisterMaskValue::clear.
|
|
*/
|
|
template<typename... Ops>
|
|
static void modify() {
|
|
if (((Addr == Ops::reg::addr) | ...)) {
|
|
auto mask = read();
|
|
([&mask] {
|
|
if (Addr == Ops::reg::addr)
|
|
mask = Ops(mask);
|
|
}(), ...);
|
|
write(mask);
|
|
}
|
|
}
|
|
|
|
// 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
|
|
// bit-mask created by merging all provided bit-masks.
|
|
// If no masks are given, a mask selecting all bits is used.
|
|
template<typename... Masks>
|
|
static void apply(auto fn) {
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
auto mask = mergeMasks<Masks...>();
|
|
if (mask)
|
|
write(fn(read(), mask));
|
|
} else {
|
|
write(fn(read(), T(0) - 1));
|
|
}
|
|
}
|
|
|
|
template<typename... Masks>
|
|
static auto mergeMasks() {
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
if (((Addr == Masks::reg::addr) | ...)) {
|
|
auto mask =
|
|
([] {
|
|
return Addr == Masks::reg::addr ? Masks::mask : 0;
|
|
}() | ...);
|
|
return mask;
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Register() = delete;
|
|
|
|
using type = T;
|
|
constexpr static auto addr = Addr;
|
|
};
|
|
|
|
/**
|
|
* @struct RegisterMask
|
|
* @brief Defines a bit mask that can be used with the specified register.
|
|
* @tparam Reg The Register that this mask belongs to.
|
|
* @tparam Mask A mask selecting the bits that the RegisterMask can modify.
|
|
*
|
|
* Pairs together a bit mask and the register the mask is meant for.
|
|
* For example, a single LED is controlled by bit 2 on the GPIO_OUT register:
|
|
* using LED_RED = RegisterMask<GPIO_OUT, (1 << 2)>;
|
|
*/
|
|
template<typename Reg, typename Reg::type Mask>
|
|
struct RegisterMask
|
|
{
|
|
using T = typename Reg::type;
|
|
|
|
/**
|
|
* Sets all bits in the bit-mask to "1".
|
|
* Call with no arguments (LED_REG::set()) to affect the paired register.
|
|
* Calling with an argument sets bits in the argument as if it were a
|
|
* register, returning the resulting value.
|
|
*/
|
|
struct set {
|
|
constexpr set() {
|
|
Reg::write(Reg::read() | Mask);
|
|
}
|
|
|
|
// For internal use.
|
|
using reg = Reg;
|
|
T modmask;
|
|
constexpr set(auto r): modmask(r | Mask) {}
|
|
constexpr operator T() const { return modmask; }
|
|
};
|
|
|
|
/**
|
|
* Clears all bits in the bit-mask to "0".
|
|
* See RegisterMask::set for calling conventions.
|
|
*/
|
|
struct clear {
|
|
constexpr clear() {
|
|
Reg::write(Reg::read() & ~Mask);
|
|
}
|
|
|
|
// For internal use.
|
|
using reg = Reg;
|
|
T modmask;
|
|
constexpr clear(auto r): modmask(r & ~Mask) {}
|
|
constexpr operator T() const { return modmask; }
|
|
};
|
|
|
|
/**
|
|
* Toggles all bits in the bit-mask.
|
|
* See RegisterMask::set for calling conventions.
|
|
*/
|
|
struct toggle {
|
|
constexpr toggle() {
|
|
Reg::write(Reg::read() ^ Mask);
|
|
}
|
|
|
|
// For internal use.
|
|
using reg = Reg;
|
|
T modmask;
|
|
constexpr toggle(auto r): modmask(r ^ Mask) {}
|
|
constexpr operator T() const { return modmask; }
|
|
};
|
|
|
|
/**
|
|
* Reads from the paired register, applying the bit-mask.
|
|
*/
|
|
static auto read() {
|
|
return Reg::read() & Mask;
|
|
}
|
|
|
|
/**
|
|
* Applies the bit-mask to the given register value, returning the result.
|
|
* This is useful in case the register's value has already been read; or, if
|
|
* the mask needs to be applied to a different value or register.
|
|
* @see Mask<>
|
|
*/
|
|
static auto read(const T& regval) {
|
|
return regval & Mask;
|
|
}
|
|
|
|
/**
|
|
* Writes the given value to the register.
|
|
* Writing is accomplished by clearing the bit-mask, then OR-ing the value
|
|
* to the bit-mask's offset.
|
|
* See RegisterMask::set for calling conventions, but note the additional
|
|
* template parameter "value".
|
|
*/
|
|
template<T value>
|
|
struct write {
|
|
constexpr write() {
|
|
auto r = Reg::read();
|
|
r &= ~Mask;
|
|
r |= value << BitOffset<Mask>;
|
|
Reg::write(r);
|
|
}
|
|
|
|
// For internal use.
|
|
using reg = Reg;
|
|
T modmask;
|
|
constexpr write(auto r):
|
|
modmask((r & ~Mask) | (value << BitOffset<Mask>)) {}
|
|
constexpr operator T() const { return modmask; }
|
|
};
|
|
|
|
/**
|
|
* Tests if all masked bits are set in the register.
|
|
*/
|
|
static bool test() {
|
|
return read() == Mask;
|
|
}
|
|
|
|
/**
|
|
* Tests if all masked bits are set in the given register value.
|
|
*/
|
|
static bool test(const T& regval) {
|
|
return read(regval) == Mask;
|
|
}
|
|
|
|
RegisterMask() = delete;
|
|
|
|
using reg = Reg;
|
|
constexpr static auto mask = Mask;
|
|
};
|
|
|
|
/**
|
|
* @struct RegisterMaskValue
|
|
* @brief Used to name the possible values of a multi-bit bit-mask.
|
|
* @tparam Mask The RegisterMask this value is associated with.
|
|
* @tparam value The value to be used for the given Mask.
|
|
*/
|
|
template<typename Mask, Mask::T value>
|
|
struct RegisterMaskValue
|
|
{
|
|
/**
|
|
* Call this directly to write the value into the register.
|
|
* Can also be used in modify() chains.
|
|
* @see RegisterMask::write()
|
|
* @see Register::modify()
|
|
*/
|
|
using set = typename Mask::write<value>;
|
|
|
|
/**
|
|
* Call this to clear the value from the register.
|
|
*/
|
|
using clear = typename Mask::clear;
|
|
|
|
/**
|
|
* Tests if this value is currently set in the register.
|
|
*/
|
|
static bool test() {
|
|
return Mask::read() & (value << BitOffset<Mask>);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @class RegisterGroup
|
|
* @brief Groups registers together for unified operations.
|
|
* @tparam Registers The registers to be included in this group.
|
|
*
|
|
* Allows for single operations to be carried out on multiple registers.
|
|
* Masks for the same register are merged, resulting in single load/stores for
|
|
* each register.
|
|
*/
|
|
template<typename... Registers>
|
|
class RegisterGroup
|
|
{
|
|
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.
|
|
*/
|
|
template<typename... Masks>
|
|
static void set() {
|
|
apply<Masks...>([](auto r, auto m) { return r | m; });
|
|
}
|
|
|
|
/**
|
|
* Clears bits throughout this group's registers according to the given
|
|
* masks.
|
|
* Only reads and writes each register once; see set().
|
|
*/
|
|
template<typename... Masks>
|
|
static void clear() {
|
|
apply<Masks...>([](auto r, auto m) { return r & ~m; });
|
|
}
|
|
|
|
/**
|
|
* Toggles bits throughout this group's registers according to the given
|
|
* masks.
|
|
* Only reads and writes each register once; see set().
|
|
*/
|
|
template<typename... Masks>
|
|
static void toggle() {
|
|
apply<Masks...>([](auto r, auto m) { return r ^ m; });
|
|
}
|
|
|
|
/**
|
|
* Modifies registers in this group according to the given operations.
|
|
* Each register will only be read and written once.
|
|
* Possible operations include RegisterMask::set, RegisterMask::clear,
|
|
* RegisterMask::toggle, RegisterMask::write<>, RegisterMaskValue::set,
|
|
* and RegisterMaskValue::clear.
|
|
*/
|
|
template<typename... Ops>
|
|
static void modify() {
|
|
(Registers::template modify<Ops...>(), ...);
|
|
}
|
|
|
|
private:
|
|
template<typename... Masks>
|
|
static void apply(auto fn) {
|
|
(Registers::template apply<Masks...>(fn), ...);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Merges the bit-masks of the given RegisterMasks, ignoring register
|
|
* assignment.
|
|
* Useful if the bit-masks are needed for something besides the assigned
|
|
* register.
|
|
*/
|
|
template<typename... RegMasks>
|
|
constexpr auto Masks = (RegMasks::mask | ...);
|
|
|
|
template<typename T, uintptr_t Addr>
|
|
using MemRegister = Register<T, MemoryIO<T, Addr>>;
|
|
|
|
} // namespace fr
|
|
|
|
#endif // FUNCTIONAL_REGISTER_IO_H
|
|
|