高性能裸机抽象

High Performance Bare Metal Abstraction

最近我们有了一个想法,即使用模板为裸机开发创建通用的高性能抽象。

通常每个芯片制造商都提供 C header 这样的:

//Following structure is POD so we can rely on its memory layout
struct Periphery{
  volatile uint32_t reg1;
  volatile uint32_t reg2;
};

#define PERIPHERY0BASE 0x000000ab //address where does registers of periphery start
#define PERIPHERY1BASE 0x000000cd
static Periphery* PERIPHERY0 = (Periphery*)(PERIPHERY0BASE ); 
//or  
#define PERIPHERY1 (Periphery*)(PERIPHERY1BASE )

我们的想法是创建 drivers,它们是平台特定的,但对于给定的外围设备类型是通用的:

template<int addr>
PeripheryDriver{
  static inline void doSomething(int foo){
    (PERIPHERY0*)(addr)->reg1 = foo;
  }
}
 //typedefs for different peripheries of the same type 
typedef Periphery<PERIPHERY0BASE> Periphery0;
typedef Periphery<PERIPHERY1BASE> Periphery1;

然后将像这样在平台独立模块中使用:

template<class P> 
class DriverUser{ 
  DriverUser(){
    P::doSomething(0x00);
  }
};

所有这一切的要点是,我们可以从一个平台上的单个外设中抽象出来,从而为具有相同结构的所有外设创建通用 driver,例如计时器、Uart 等一个处理器系列。此外,它允许我们创建高性能平台独立模块,例如,我们可以创建高性能引脚访问,它与用汇编程序编写的一样高效,但同时具有高度可重用性:

//normally would be in PCB specific header
typedef Pin<Port0, Pin0> Pin0;
typedef Pin<Port1, Pin7> Pin1;

//application specific code
typedef Pin0 TxPin;
typedef Pin1 RxPin;

void main(){
  SoftwareUart<TxPin,RxPin> my_uart(115200);
  my_uart.send("hello world");
}

然后可以实现完全独立于平台的 SoftwareUart,但将 High 写入 TxPin 将与在汇编程序中一样高效,所有这些都无需使用宏。

我们的问题是在某些平台上制造商的 headers 不包含 定义地址名称的宏,但只有地址已经转换为指针的宏,因此我们不能将它们用作模板参数。 例如 PERIPHERY0BASE 仅 PERIHPERY0

不可用

我的问题是是否有任何解决方法可以保持效率?(重写寄存器定义除外) 在 C++11 中,我会考虑使用 constexpr 创建函数,该函数将获取静态结构的地址,然后将其用作模板参数。不幸的是,我们不能指望 C++11 的可用性。有任何想法吗?我们需要 modify/write 我们自己的寄存器定义吗?

抱歉,很难理解您的实际需求。如果我对你的理解正确,你需要一种通用的方法来从第三方提供的指针中获取特定结构的偏移量 headers (假设你知道结构的对齐方式)。 如果您声称可以使用 C++11 constexpr 函数实现您的目标,请尝试使用 C++03 的模板。

我想你需要引入一个更高级别的包装器来将指针转换为偏移量:

template <typename T, T ptr, unsigned TAlignmentMask>
struct AddrRetriever
{
    static const int value = (int)ptr & TAlignmentMask;
}

然后使用:

typedef Periphery<
    AddrRetriever<
        volatile void*, // use the type of the pointer vendor provides
        PTR_FROM_VENDOR, 
        KNOWN_ALIGNMENT_MASK>::value
 > Periphery0;

作为旁注,我想推荐阅读 Practical Guide to Bare Metal C++。它将为您提供一些实现通用异步定时器、uart 和其他外围设备的想法。

您应该始终围绕硬件外围设备创建一个硬件抽象层。这意味着调用者不需要知道或关心寄存器的位和字节。换句话说,为给定的 MCU 上的给定外围设备制作标准硬件驱动程序。

在同一个芯片上处理多个同类型的硬件外设,通常会把第一个寄存器的地址作为参数来区分。看来这就是您的代码正在做的事情。

为了进一步提高抽象级别,您可以创建一个抽象基 class "UART",它包含所有 UART 的通用函数,例如设置波特率和通信格式,如果设置硬件握手需要,发送,接收等。然后,您所有的 UART 驱动程序都必须继承基础 class 函数接口。

那么应用程序的调用者不需要知道或关心特定硬件外设如何在给定的 MCU 上工作。调用者应用程序将是完全可移植的。

这是进行专业固件设计的标准方法。通常它是在 C 中完成的,但也可以在 C++ 中小心地完成,而不会产生太多的负担(避免使用模板)。