高性能裸机抽象

High Performance Bare Metal Abstraction

本文关键字:抽象 裸机 高性能      更新时间:2023-10-16

最近我们想到了使用模板为裸机开发创建通用的高性能抽象。

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

//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 )

我们的想法是创建特定于平台但针对给定外围设备的通用驱动程序:

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);
  }
};

所有这些的要点是,我们可以从一个平台上的单个外设中抽象出来,从而为具有相同结构的所有外设创建通用驱动程序,例如一个处理器系列上的计时器、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将与汇编程序一样高效,而无需使用宏。

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

可用

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

抱歉,很难理解您实际需要什么。如果我理解正确,您需要一种通用方法来从第三方标头提供的指针中获取特定结构中的偏移量(假设您知道结构的对齐方式(。如果您声称可以通过 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;

作为旁注,我建议阅读裸机C++实用指南。它将为您提供一些有关实现通用异步计时器、uart 和其他外围设备的想法,就像您想要的那样。

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

为了在同一芯片上处理同一类型的多个硬件外设,通常将第一个寄存器的地址作为参数,以使它们分开。似乎这就是您的代码正在做的事情。

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

然后,应用程序的调用方不需要知道或关心特定硬件外设在给定MCU上的工作方式。调用方应用程序将是完全可移植的。

这是进行专业固件设计的标准方法。通常它是用 C 语言完成的,但只要小心一点,也可以在 C++ 中完成,而不会产生太多的自重(避免使用模板(。