Created
March 3, 2022 15:41
-
-
Save willeccles/bc0c2828602a2ff7b8e7bae56b448f9e to your computer and use it in GitHub Desktop.
C++ register mapping
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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