注册为模板参数

Tor*_*zki 6 c++ embedded gcc

我正在寻找一种使用gcc 4.8.4将嵌入式设备寄存器传递给C++模板的方法.在描述嵌入式器件的数据手册中,寄存器的地址通常作为原始存储器位置给出(例如,0x40008000).

当我测试软件时,我想使用静态整数作为寄存器来查看寄存器值是否设置正确.

所以基本上一个设备外围设备的包装器归结为一个类,其寄存器地址作为模板参数给出:

template < volatile std::uint32_t* Reg >
struct peripheral {};
Run Code Online (Sandbox Code Playgroud)

测试工作正常:

std::uint32_t reg;
peripheral< &reg > mocked;
Run Code Online (Sandbox Code Playgroud)

但是当我想用固定的数据表给出地址时,实例化模板:

peripheral< reinterpret_cast< std::uint32_t* >( 0x40008000 ) > mocked;
Run Code Online (Sandbox Code Playgroud)

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 >( &reg ) > mocked;
Run Code Online (Sandbox Code Playgroud)

这导致了error: conversion from pointer type 'uint32_t* {aka long unsigned int*}' to arithmetic type 'intptr_t {aka int}' in a constant-expression.

我可以想到两个解决方案:

1)使用指针作为模板参数,使用全局变量作为寄存器,并使用某些链接器脚本魔法修复寄存器的地址.

2)使用特殊寄存器类型,这些寄存器类型具有与外围模板的公共接口,但是两个非常不同的实现用于测试和实际应用.

但我正在寻找一种更简单的方法来实现这一目标.任何想法,指针或评论?

Tor*_*zki 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_;
    }
};
Run Code Online (Sandbox Code Playgroud)

其中 Name 应该是使实例化不同于其他实例化的任何类型。当定义多个类型时,可以使用以前定义的类型作为名称:

typedef mocked_register<>             START;
typedef mocked_register< START >      STOP;
typedef mocked_register< STOP >       COUNT;
Run Code Online (Sandbox Code Playgroud)

为了测试,该类型保留一个“半”静态变量来保存寄存器值。对于 ARM 架构,我有一些情况,使用寄存器数组会很有帮助。在这种情况下,该Value参数可用于提供外部数组:

std::uint32_t capture_regs[ 4 ];
typedef mocked_register< SHUTDOWN, std::uint32_t, capture_regs > CAPTURE;
Run Code Online (Sandbox Code Playgroud)

对于生产部分,模板要简单得多:

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 );
    }
};
Run Code Online (Sandbox Code Playgroud)

在这两种情况(测试和生产)中,设备抽象采用一组模板参数并将它们用作寄存器:

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 );
    }
};
Run Code Online (Sandbox Code Playgroud)

如果要实现对 T 的赋值和隐式转换,可以添加更多类似寄存器的语法(但我不太喜欢这个想法):

START start;
COUNT count;
start = 1;
std::uint32_t c = count;
Run Code Online (Sandbox Code Playgroud)