我目前的工作是研究如何为任意嵌入式设备寄存器创建类。目前,我正在与许多具有不同寄存器大小的目标一起工作,并且我想标准化(或至少以某种逻辑方式)枚举用于内存映射io,寄存器等的对象。 当前的公司标准是在寄存器位置放置一个变量。
volatile uint16_t ®ister_obj = *reinterpret_cast<volatile uint16_t *>(0x01A02);
我阅读了一些有关嵌入式C ++主题的论文和讨论,最著名的是this blog post by Niklas Hauser,而这个overview提供了对其他几篇论文的比较和扩展。这些帖子进一步引用了representing and manipulating hardware with C and C++上的一篇出色论文,以及Ken Smith's C++ Hardware Register Redux中的一些模拟示例和替代性想法。
我进一步定义了一个类型别名,以表示寄存器宽度:
/* 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
然后,第一个代码段变为:
reg16_t ®ister_obj = *reinterpret_cast<reg16_t *>(0x01A02);
这让我不满意。我意识到,“新放置”可能非常适合我的应用程序,因为寄存器和内存映射的IO是有效的预分配内存部分,可以在上面覆盖对象。
我阅读了variadic templates,并花了数小时试图设计一个模板,该模板在指定的地址创建并放置了任意类型,最接近的是以下内容:
template <typename T>
T* placeObject(uintptr_t address) {
return new (reinterpret_cast<uintptr_t*>(address)) T;
}
,但是实现实际上是我刚开始时的较差版本。例如:
reg16_t& control_register = *submodule_new<reg16_t>(0x0160);
我尝试了一个结构模板并想到了:
template<typename T, uintptr_t address>
struct submodule
{
T &location = *reinterpret_cast<T *>(address);
// Also needs to implement mutability (access policy)
};
这看起来很有希望,因为它的声明正是我在寻找的东西
submodule<reg16_t, 0x0160> control_register_0;
但是struct-interface很尴尬:
uint8_t ENABLE = 0x4;
control_register_0.location &= ~ENABLE; // this probably doesn't work
我的实际问题
该项目的最终目标是使子模块包含访问策略以及要读取和写入的数据的位置。理想情况下,这些子模块将在可变参量类生成器中构建,以允许任意多,任意大小的内存接口提供类型安全和访问策略(只读,只写)。形式如下:
// 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);