在古老的嵌入式世界中,人们将硬件(-配置)-寄存器映射作为结构编写,这是一个非常简单的32位硬件示例:

#define hw_baseaddr ((uintptr_t) 0x10000000)

struct regs {
     uint32_t reg1;
     uint32_t reg2;
};

#define hw_reg ((volatile struct regs *) hw_baseaddr)

void f(void)
{
    hw_reg->reg1 = 0xdeadcafe;
    hw_reg->reg2 = 0xc0fefe;
}

效果很好,编译器(至少在我们的平台上为gcc)意识到hw_reg引用了相同的地址(在编译时是已知且恒定的),并且ld仅对其引用了一次。第二个st(存储)是通过单指令的4字节偏移量完成的-同样在我们的平台上。

如何在不使用#defines的情况下使用现代C++(在C++ 11之后)重现此行为?

我们尝试了很多事情:类内外的static constconstexpr。他们都不喜欢reinterprest_cast<>的(隐式)。

回应有关更改原因的评论:恐怕它主要是成名和荣耀。但不仅如此。使用此C代码调试可能很困难。假设您要记录所有的写访问,这种方法将要求您重写所有内容。但是,这里我不是在寻找可以简化特定情况的解决方案,而是在寻找灵感。

编辑只是为了澄清一些评论:我问这个问题是不要更改任何有效的代码(写于1990年代)。我正在寻找 future 项目的解决方案,因为我对define实现并不完全满意,并且在问自己现代C++是否有更好的可能性。

最佳答案

我认为变量模板为此处提供了一种优雅的解决方案。

// Include this in some common header
template <class Impl>
volatile Impl& regs = *reinterpret_cast<volatile Impl*>(Impl::base_address);

template <std::uintptr_t BaseAddress>
struct HardwareAt {
    static const std::uintptr_t base_address = BaseAddress;

    // can't be instantiated
    ~HardwareAt() = delete;
};

// This goes in a certain HW module's header
struct MyHW : HardwareAt<0x10000000> {
    std::uint32_t in;
    std::uint32_t out;
};

// Example usage
int main()
{
    std::printf("%p\n%p\n", &regs<MyHW>.in, &regs<MyHW>.out);

    // or with alias for backward compatibility:
    auto hw_reg = &regs<MyHW>;
    std::printf("%p\n%p\n", &hw_reg->in, &hw_reg->out);
}

像这样使用它而不是与宏一起使用的好处之一是键入安全,并且实际上您可以引用同一源文件中不同硬件模块的寄存器,而无需混合使用。

10-08 18:59