Skip to content

Instantly share code, notes, and snippets.

@willeccles
Created March 3, 2022 15:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willeccles/bc0c2828602a2ff7b8e7bae56b448f9e to your computer and use it in GitHub Desktop.
Save willeccles/bc0c2828602a2ff7b8e7bae56b448f9e to your computer and use it in GitHub Desktop.
C++ register mapping
/*
* Basic register map implementation in C++. Makes reading/writing registers
* safer, should be just as efficient as doing things the old fashioned way.
* The setup is a little ugly, but such is life. It was never going to be pretty
* anyway.
*
* Requires C++20, though that could be changed with a little SFINAE trickery.
*
* Example code uses GPIO peripherals on an AM3358.
*
* This is obviously pointless in Linux userspace (you'd have to modify it to
* use an offset from mmap rather than an absolute register address, and you
* should be using drivers anyway), but on bare metal this should achieve its
* goal.
*
* https://godbolt.org/z/3a4YM7j7d
*/
#include <cstdint>
template <class T, unsigned off, unsigned len>
requires(off<(sizeof(T) * 8) && len <= (sizeof(T) * 8) && len> 0 &&
(len + off) <= (sizeof(T) * 8))
inline constexpr T mask() {
if constexpr (len == sizeof(T) * 8) {
return ~(T)0;
} else {
return (T)(((1ULL << len) - 1ULL) << off);
}
}
template <uintptr_t reg, unsigned off, unsigned len>
struct RegField {
static constexpr auto Mask = mask<uint32_t, off, len>();
uint32_t get() const volatile {
return (*((volatile uint32_t*)(reg)) & Mask) >> off;
}
operator uint32_t() const volatile { return get(); }
auto& set(uint32_t val) volatile {
*((volatile uint32_t*)(reg)) =
((*((volatile uint32_t*)(reg))) & ~Mask) | (((val << off) & Mask));
return *this;
}
auto& operator=(uint32_t val) volatile { return set(val); }
};
// GPIO_REVISION
template <uintptr_t addr>
struct GPIORevision {
GPIORevision(){};
volatile const RegField<addr, 0, 6> minor;
volatile const RegField<addr, 6, 2> custom;
volatile const RegField<addr, 8, 3> major;
volatile const RegField<addr, 11, 5> rtl;
volatile const RegField<addr, 16, 12> func;
volatile const RegField<addr, 30, 2> scheme;
};
// GPIO_SYSCONFIG
template <uintptr_t addr>
struct GPIOSysConfig {
GPIOSysConfig(){};
volatile RegField<addr, 0, 1> autoidle;
volatile RegField<addr, 1, 1> softreset;
volatile RegField<addr, 2, 1> enawakeup;
volatile RegField<addr, 3, 2> idlemode;
};
// GPIO_DATAOUT
template <uintptr_t addr>
struct GPIODataOut {
GPIODataOut(){};
// TODO: an indexable array of fields, or a "multi-field" type thing (there
// is one field here, but each of its 32 bits corresponds to a different pin)
volatile RegField<addr, 0, 1> dataout0;
volatile RegField<addr, 1, 1> dataout1;
volatile RegField<addr, 2, 1> dataout2;
};
// GPIO peripheral register map
template <uintptr_t base>
struct GPIO {
// these offsets might need to be divided by 4, can't remember, don't care
GPIORevision<base + 0x0> revision;
GPIOSysConfig<base + 0x10> sysconfig;
GPIODataOut<base + 0x13C> dataout;
};
// GPIO0 and GPIO1
static GPIO<0x44E07000> gpio0;
static GPIO<0x4804C000> gpio1;
int main(int argc, char** argv) {
gpio0.dataout.dataout0 = 1;
gpio1.dataout.dataout0 = 1;
uint32_t m = gpio0.revision.major;
gpio0.dataout.dataout1 = 1;
gpio0.dataout.dataout2 = 1;
return m;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment