使用内存映射的 memcpy 和朋友 I/O

Using memcpy and friends with memory-mapped I/O

我正在从事一个嵌入式项目,其中涉及 I/O 内存映射 FPGA 寄存器。指向这些内存区域的指针需要标记为 volatile,这样编译器就不会 "optimize out" 通过在 CPU 寄存器中缓存值来读取和写入 FPGA。

在少数情况下,我们希望将一系列FPGA寄存器复制到缓冲区中以供进一步使用。由于寄存器映射到连续地址,memcpy 似乎是合适的,但是将我们的 volatile 指针作为源参数传递会给出关于丢弃 volatile 限定符的警告。

放弃指针的 volatile 特性以抑制此警告是否安全(且理智)?除非编译器做了一些神奇的事情,否则我无法想象调用 memcpy 将无法执行实际复制的场景。另一种方法是只使用 for 循环并逐字节复制,但是 memcpy 实现可以(并且确实)根据副本的大小、对齐等优化副本。

绝对不安全。无法保证 memcpy 将以何种顺序复制数据,以及一次复制多少字节。

作为 FPGA 和嵌入式软件两者的开发者,只有一个明确的答案:不要使用 memcpy 等。为此

一些原因:

  • 不能保证 memcpy 将以任何特定顺序工作。
  • 编译器很可能会用内联代码替换调用。
  • 此类访问通常需要一定的字数。 memcpy 不保证。
  • 寄存器映射中的间隙可能会导致未定义的行为。

但是,您可以使用简单的 for 循环并自己复制。这是安全的,如果寄存器是 volatile见下文)。

根据您的平台,仅 volatile 可能不够。内存区域也必须是 non-cachablestrictly ordered (并且 - 可能 - 非共享)。否则,系统总线可能(并且将对某些平台)重新排序访问。

此外,您可能需要 barriers/fences 才能 CPU 不重新排序访问。请仔细阅读您的硬件规格。

如果您需要更频繁地传输更大的块,请考虑使用 DMA。如果 FPGA 使用 PCI(e),您可以将 busmaster DMA 与 scatter/gather 一起使用(但是,这不容易实现;我自己做了,但可能值得付出努力)。

最好(也是最理智)的方法实际上取决于多种因素,例如平台、所需速度等。在所有可能的方法中,我认为使用 mempcy() 较不理智的方法之一 (1)最佳 (1):不确定这是否是正确的语法,但我希望你明白我的意思)。