|
|
@ -11,7 +11,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
template<auto Mask, unsigned int N = 0>
|
|
|
|
template<auto Mask, unsigned int N = 0>
|
|
|
|
constexpr auto BitOffset = []() constexpr {
|
|
|
|
constexpr auto BitOffset = []() constexpr {
|
|
|
|
if constexpr (Mask & 1)
|
|
|
|
if constexpr (Mask & 1)
|
|
|
@ -20,6 +20,40 @@ constexpr auto BitOffset = []() constexpr {
|
|
|
|
return BitOffset<(Mask >> 1), (N + 1)>;
|
|
|
|
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
|
|
|
|
* @struct Register
|
|
|
|
* @brief Defines a memory-mapped register, given bit-size and address.
|
|
|
|
* @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<uint32_t, 0xA0004120>"
|
|
|
|
* Use only as a type, e.g. "using GPIO_OUT = Register<uint32_t, 0xA0004120>"
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
template<typename T, uintptr_t Addr>
|
|
|
|
template<typename T, typename RegAccess>
|
|
|
|
struct Register {
|
|
|
|
struct Register {
|
|
|
|
|
|
|
|
constexpr static auto Addr = RegAccess::addr;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Gets a pointer to the register.
|
|
|
|
* Gets a pointer to the register.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
constexpr static auto get() {
|
|
|
|
constexpr static T read() {
|
|
|
|
return reinterpret_cast<volatile T*>(Addr);
|
|
|
|
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<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static void set() {
|
|
|
|
static void set() {
|
|
|
|
apply<Masks...>([](auto r, auto m) { *r = *r | m; });
|
|
|
|
apply<Masks...>([](auto r, auto m) { return r | m; });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Sets register bits to '1' according to the given mask.
|
|
|
|
* Sets register bits to '1' according to the given mask.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void set(const T& mask) {
|
|
|
|
static void set(const T& mask) {
|
|
|
|
*get() = *get() | mask;
|
|
|
|
write(read() | mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -60,14 +103,14 @@ struct Register {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static void clear() {
|
|
|
|
static void clear() {
|
|
|
|
apply<Masks...>([](auto r, auto m) { *r = *r & ~m; });
|
|
|
|
apply<Masks...>([](auto r, auto m) { return r & ~m; });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Clears register bits to '0' according to the given mask.
|
|
|
|
* Clears register bits to '0' according to the given mask.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void clear(const T& mask) {
|
|
|
|
static void clear(const T& mask) {
|
|
|
|
*get() = *get() & ~mask;
|
|
|
|
write(read() & ~mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -75,14 +118,14 @@ struct Register {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static void toggle() {
|
|
|
|
static void toggle() {
|
|
|
|
apply<Masks...>([](auto r, auto m) { *r = *r ^ m; });
|
|
|
|
apply<Masks...>([](auto r, auto m) { return r ^m; });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Toggles bits in the register according to the given mask.
|
|
|
|
* Toggles bits in the register according to the given mask.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void toggle(const T& mask) {
|
|
|
|
static void toggle(const T& mask) {
|
|
|
|
*get() = *get() ^ mask;
|
|
|
|
write(read() ^ mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -92,26 +135,10 @@ struct Register {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static auto read() {
|
|
|
|
static auto read() {
|
|
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
|
|
if constexpr (sizeof...(Masks) > 0)
|
|
|
|
if (((Addr == Masks::reg::addr) | ...)) {
|
|
|
|
return read() & mergeMasks<Masks...>();
|
|
|
|
auto mask =
|
|
|
|
else
|
|
|
|
([] {
|
|
|
|
return read();
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -121,17 +148,10 @@ struct Register {
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static bool test() {
|
|
|
|
static bool test() {
|
|
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
|
|
if (((Addr == Masks::reg::addr) | ...)) {
|
|
|
|
auto mask = mergeMasks<Masks...>();
|
|
|
|
auto mask =
|
|
|
|
return (read() & mask) == mask;
|
|
|
|
([] {
|
|
|
|
|
|
|
|
return Addr == Masks::reg::addr ? Masks::mask : 0;
|
|
|
|
|
|
|
|
}() | ...);
|
|
|
|
|
|
|
|
return (*get() & mask) == mask;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
return *get() != 0;
|
|
|
|
return read() != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -145,16 +165,16 @@ struct Register {
|
|
|
|
template<typename... Ops>
|
|
|
|
template<typename... Ops>
|
|
|
|
static void modify() {
|
|
|
|
static void modify() {
|
|
|
|
if (((Addr == Ops::reg::addr) | ...)) {
|
|
|
|
if (((Addr == Ops::reg::addr) | ...)) {
|
|
|
|
auto mask = *get();
|
|
|
|
auto mask = read();
|
|
|
|
([&mask] {
|
|
|
|
([&mask] {
|
|
|
|
if (Addr == Ops::reg::addr)
|
|
|
|
if (Addr == Ops::reg::addr)
|
|
|
|
mask = Ops(mask);
|
|
|
|
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.
|
|
|
|
// Applies bit-masks to the register through the provided function.
|
|
|
|
// The provided function receives a pointer to the register's data and a
|
|
|
|
// The provided function receives a pointer to the register's data and a
|
|
|
@ -163,14 +183,28 @@ struct Register {
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static void apply(auto fn) {
|
|
|
|
static void apply(auto fn) {
|
|
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
|
|
if constexpr (sizeof...(Masks) > 0) {
|
|
|
|
auto mask =
|
|
|
|
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 Addr == Masks::reg::addr ? Masks::mask : 0;
|
|
|
|
}() | ...);
|
|
|
|
}() | ...);
|
|
|
|
if (mask)
|
|
|
|
return mask;
|
|
|
|
fn(get(), mask);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
fn(get(), T(0) - 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -203,7 +237,7 @@ struct RegisterMask
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
struct set {
|
|
|
|
struct set {
|
|
|
|
constexpr set() {
|
|
|
|
constexpr set() {
|
|
|
|
*Reg::get() = *Reg::get() | Mask;
|
|
|
|
Reg::write(Reg::read() | Mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// For internal use.
|
|
|
|
// For internal use.
|
|
|
@ -219,7 +253,7 @@ struct RegisterMask
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
struct clear {
|
|
|
|
struct clear {
|
|
|
|
constexpr clear() {
|
|
|
|
constexpr clear() {
|
|
|
|
*Reg::get() = *Reg::get() & ~Mask;
|
|
|
|
Reg::write(Reg::read() & ~Mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// For internal use.
|
|
|
|
// For internal use.
|
|
|
@ -235,7 +269,7 @@ struct RegisterMask
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
struct toggle {
|
|
|
|
struct toggle {
|
|
|
|
constexpr toggle() {
|
|
|
|
constexpr toggle() {
|
|
|
|
*Reg::get() = *Reg::get() ^ Mask;
|
|
|
|
Reg::write(Reg::read() ^ Mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// For internal use.
|
|
|
|
// For internal use.
|
|
|
@ -249,7 +283,7 @@ struct RegisterMask
|
|
|
|
* Reads from the paired register, applying the bit-mask.
|
|
|
|
* Reads from the paired register, applying the bit-mask.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static auto read() {
|
|
|
|
static auto read() {
|
|
|
|
return *Reg::get() & Mask;
|
|
|
|
return Reg::read() & Mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -272,10 +306,10 @@ struct RegisterMask
|
|
|
|
template<T value>
|
|
|
|
template<T value>
|
|
|
|
struct write {
|
|
|
|
struct write {
|
|
|
|
constexpr write() {
|
|
|
|
constexpr write() {
|
|
|
|
auto r = *Reg::get();
|
|
|
|
auto r = Reg::read();
|
|
|
|
r &= ~Mask;
|
|
|
|
r &= ~Mask;
|
|
|
|
r |= value << BitOffset<Mask>;
|
|
|
|
r |= value << BitOffset<Mask>;
|
|
|
|
*Reg::get() = r;
|
|
|
|
Reg::write(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// For internal use.
|
|
|
|
// For internal use.
|
|
|
@ -352,11 +386,11 @@ public:
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Sets bits throughout this group's registers according to the given masks.
|
|
|
|
* 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
|
|
|
|
* Bit-masks for the same register will be merged so that each register is
|
|
|
|
* only written once.
|
|
|
|
* only written once.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static void set() {
|
|
|
|
static void set() {
|
|
|
|
apply<Masks...>([](auto r, auto m) { *r = *r | m; });
|
|
|
|
apply<Masks...>([](auto r, auto m) { return r | m; });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -366,7 +400,7 @@ public:
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static void clear() {
|
|
|
|
static void clear() {
|
|
|
|
apply<Masks...>([](auto r, auto m) { *r = *r & ~m; });
|
|
|
|
apply<Masks...>([](auto r, auto m) { return r & ~m; });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -376,7 +410,7 @@ public:
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
template<typename... Masks>
|
|
|
|
template<typename... Masks>
|
|
|
|
static void toggle() {
|
|
|
|
static void toggle() {
|
|
|
|
apply<Masks...>([](auto r, auto m) { *r = *r ^ m; });
|
|
|
|
apply<Masks...>([](auto r, auto m) { return r ^ m; });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -407,6 +441,10 @@ private:
|
|
|
|
template<typename... RegMasks>
|
|
|
|
template<typename... RegMasks>
|
|
|
|
constexpr auto Masks = (RegMasks::mask | ...);
|
|
|
|
constexpr auto Masks = (RegMasks::mask | ...);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T, uintptr_t Addr>
|
|
|
|
|
|
|
|
using MemRegister = Register<T, MemoryIO<T, Addr>>;
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace fr
|
|
|
|
} // namespace fr
|
|
|
|
|
|
|
|
|
|
|
|
#endif // FUNCTIONAL_REGISTER_IO_H
|
|
|
|
#endif // FUNCTIONAL_REGISTER_IO_H
|
|
|
|
|
|
|
|
|
|
|
|