我正在寻找一种使用gcc 4.8.4将嵌入式设备寄存器传递给C ++模板的方法。在描述嵌入式器件的数据手册中,寄存器的地址通常作为原始存储器位置给出(例如,0x40008000)。
当我测试软件时,我想使用静态整数作为寄存器来查看寄存器值是否设置正确。
所以基本上一个设备外围设备的包装器归结为一个类,其寄存器地址作为模板参数给出:
template < volatile std::uint32_t* Reg >
struct peripheral {};
测试工作正常:
std::uint32_t reg;
peripheral< ® > mocked;
但是当我想用固定的数据表给出地址时实例化模板:
peripheral< reinterpret_cast< std::uint32_t* >( 0x40008000 ) > mocked;
gcc抱怨:could not convert template argument '1073774592u' to 'volatile uint32_t* {aka volatile long unsigned int*}
。 clang并没有抱怨这个。
如果我使用给定为整数的地址作为模板参数,我在测试期间使用模拟寄存器的地址实例化模板时遇到问题:
template < std::intptr_t Reg >
struct peripheral {};
std::uint32_t reg;
peripheral< reinterpret_cast< std::intptr_t >( ® ) > mocked;
这会产生error: conversion from pointer type 'uint32_t* {aka long unsigned int*}' to arithmetic type 'intptr_t {aka int}' in a constant-expression
。
我可以想到两个解决方案:
1)使用指针作为模板参数,使用全局变量作为寄存器,并使用一些链接器脚本魔法来修复寄存器的地址。
2)使用特殊寄存器类型,这些寄存器类型具有与外围模板的通用接口,但有两种非常不同的实现用于测试和实际应用。
但我正在寻找一种更简单的方法来实现这一目标。任何想法,指针或评论?
答案 0 :(得分:2)
在常量表达式中不允许执行reinterpret_cast<>
(编译器也会告诉您);另见Constant expressions
。
我建议如下(另见C++11 constexpr function's argument passed in template argument):
#include <cstdint>
#include <cassert>
template<typename PtrT, PtrT Ptr>
struct peripheral
{
static void* const value;
template<typename T>
static T* as() noexcept
{ return static_cast<T*>(value); }
};
#define PERIPHERAL(addr) peripheral<decltype((addr)), (addr)>
std::uint32_t reg = 0;
int main() {
peripheral<std::uintptr_t, 0x42> mocked1;
peripheral<volatile std::uint32_t*, ®> mocked2;
PERIPHERAL(0x42) mocked3;
PERIPHERAL(®) mocked4;
assert((mocked3.value == PERIPHERAL(0x42)::as<void*>()));
return 0;
}
答案 1 :(得分:0)
我的解决方案如下:
template < class Name = int, typename T = std::uint32_t, T* Value = nullptr >
class mocked_register
{
public:
static void set( T v )
{
*address() = v;
}
static T get()
{
return *address();
}
static volatile T* address()
{
static T internal_;
return Value ? Value : &internal_;
}
};
其中Name应该是使实例化与其他实例化不同的任何类型。定义多个类型时,可以使用以前定义的类型作为名称:
typedef mocked_register<> START;
typedef mocked_register< START > STOP;
typedef mocked_register< STOP > COUNT;
对于测试,该类型保留一个“半”静态变量以保持寄存器值。对于arm架构,我有一些使用寄存器数组的情况。在这种情况下,Value
参数可用于提供外部数组:
std::uint32_t capture_regs[ 4 ];
typedef mocked_register< SHUTDOWN, std::uint32_t, capture_regs > CAPTURE;
对于生产部件,模板更容易:
template < std::uint32_t Register >
struct addressed_register
{
static void set( std::uint32_t value )
{
*reinterpret_cast< volatile std::uint32_t* >( Register ) = value;
}
static std::uint32_t get()
{
return *reinterpret_cast< volatile std::uint32_t* >( Register );
}
};
在这两种情况下(测试和生产),设备抽象采用一组模板参数并将它们用作寄存器:
template <
class OUTSET,
class OUTCLEAR,
class DIRSET,
class DIRCLR,
class PIN_CNF,
std::uint8_t Nr >
struct pin
{
static void init_as_input()
{
DIRCLR::set( 1 << Nr );
}
};
如果实现赋值和隐式转换为T,可以添加更多类似语法的寄存器(但我不是这个想法的忠实粉丝):
START start;
COUNT count;
start = 1;
std::uint32_t c = count;