My current job has had me doing research into creating classes for arbitrary embedded device registers. Currently I am working with a number of targets with varying register sizes and I would like to standardize (or at least come up with some logical way) of enumerating objects for memory-mapped io, registers, etc. The current company standard is to place a variable at the register location.
volatile uint16_t ®ister_obj = *reinterpret_cast<volatile uint16_t *>(0x01A02);
I read a few papers and discussions on the subject of embedded C++, most notably this blog post by Niklas Hauser and this overview providing a comparison and extension of a few other papers. These posts contain further references to a great paper on representing and manipulating hardware with C and C++ as well as some mock examples and alternative ideas in Ken Smith's C++ Hardware Register Redux.
I further defined a type alias to represent the register width with:
/* These define an alias for the largest register that can exist in a
* sub-module. Where a module (IE: UART) contains an arbitrary number of
* sub-modules (IE UART_CTL0, UART_CTL1, UART_INTERRUPTS...) as defined in the
* vendor's datasheet.
*/
#if defined(__MSP430__)
using reg16_t = volatile uint16_t;
#elif defined(__ARM__)
using reg32_t = volatile uint32_t;
#endif
The first code snippet then becomes:
reg16_t ®ister_obj = *reinterpret_cast<reg16_t *>(0x01A02);
This didn't satisfy me. I realized that "placement new" might be a good fit for my applications as registers and memory-mapped IO are effectively pre-allocated memory sections that I could overlay objects over.
I read up on variadic templates and spent hours trying to design a template that created and placed an arbitrary type at a specified address, the closest was the following:
template <typename T>
T* placeObject(uintptr_t address) {
return new (reinterpret_cast<uintptr_t*>(address)) T;
}
but the implementation is effectively a worse version of what I started with. example:
reg16_t& control_register = *submodule_new<reg16_t>(0x0160);
I tried a struct template and came up with:
template<typename T, uintptr_t address>
struct submodule
{
T &location = *reinterpret_cast<T *>(address);
// Also needs to implement mutability (access policy)
};
this looked promising as it's declaration is exactly what I'm looking for:
submodule<reg16_t, 0x0160> control_register_0;
but the struct-interfacing is rather awkward:
uint8_t ENABLE = 0x4;
control_register_0.location &= ~ENABLE; // this probably doesn't work
My actual questions
- Are there any good examples of "pure C++" class-based register templates?
- If not, how should I structure the inheritance and construction notation so that it provides a clear intention of my goals?
The end goal for this project is to have sub-modules contain access-policies and the location of data to read and write. These sub-modules would ideally be constructed in a variadic class generator to allow for arbitrarily-many, arbitrarily-sized, interfaces to memory that provide type-safety and access policies (read-only, write-only). Something of the form:
// pseudo-code
template<typename base_type, class ...submodules>
class module
{
// somehow assign submodules to this class
};
module<submodule<reg16_t> UARTControl0, submodule<reg16_t> UARTStatus> UART();
UART.UARTControl0.Write(0x0001);
bool uart_enabled = UART.UARTStatus.read(0x);
Aucun commentaire:
Enregistrer un commentaire