前言
在嵌入式系统设计中,我们经常有实时数据采集的需求。低速率的数据一般处理难度不高,但是随着数据速率的提高,可能需要面对有限的硬件资源与系统性能的矛盾。这时候,就对软、硬件的设计要求有了更高的要求。
比如处理高速率的数据的时候,经常需要更大的硬件资源,比如片上 RAM,FIFO 来缓冲数据,以提高数据的吞吐率。但是一般 FPGA 内部的片上资源很有限。在这种情况下,如果提高硬件资源的利用率在工程上将是很大的挑战。
此教程介绍一种通过双通道 DMA,采用 PingPong 传输的方式,以提高实时数据的处理能力,减小对硬件资源的依赖。
简介
单通道 DMA 传输方式
如图1所示,只用一个 DMA 通道传输数据,DMA 的传输块大小为 FPGA 内部的缓冲区大小。
如果实时同步对 DMA 采集到的数据做处理的话,那么软件的抖动和延时可能会使 DMA 暂停超过缓冲区的缓冲时间,这将会带来严重的后果—数据溢出。在对数据完整性要求高的应用场合是致使的,尤其是在高速实时系统中。
双通道 PingPong DMA 传输方式
双通道 DMA 的示意图如图2所示,两个 DMA 同时开启,FPGA 在同一时间只会响应其中一个 DMA,当其传输完成后,由于另外一个 DMA 已经发出了请求,所以要 FPGA 内部以硬件的时序快速切换到另外一个 DMA。与此同时,之前的 DMA 可以同步做一个数据处理的工作。如此反复,两个 DMA 以 PingPong 的方式交替运行。
可以看出,在采用 PingPong DMA 方式后,可以明显减小对 FPGA 片上缓冲区的依赖,极大的提高系统的吞吐量。
系统设计框图
在我们的测试系统中,实例化了两个 AXI CDMA 的实例,分别负责两个 DMA 通道的传输。IP 方面,增加了DmaManager模块,负责对两个 DMA 的 AXI 请求做 PingPong 的管理,并输出 AXI 接口到 FPGA 内部的 BRAM 上。
DMA 工作在 100MHz,如果需要测试更高的速度,可以修改 PL 的时钟,或者通过 Clock Wizard 生成时间。
软件架构
我们在上位机软件中,启动了三个线程,包括:
两个 DMA 启动和关闭的线程和一个数据处理线程。
这三个线程之间通过 concurrent_queue 结构共享数据,即两个 DMA 线程通过 PingPong 的方式交替向 queue 里面填写数据,第三个线程通过异步的方式从 queue 里面取出数据块,并进行处理。
Concurrent_queue 的设计利用了 C++11 的特性,以简洁易读的方式实现了一个线程安全的队列模板。
示例工程
文末包括了完整的样例工程,目录的结构说明如下:
HW 文件夹里面包含了基于 ZC706 开发板的 AMD Vivado™ Design Suite 工程。
FW 文件夹是对应的 Petalinux 工程。
SW 是上柆机软件,可以运行在 PS 侧,也可以运行在 Windows 或者 Linux 平台,通过 CMake 工具生成项目文件。
测试结果
使用 Vivado 2022.2 打开 HW 目录下的 Fpga0.xpr 工程,综合,实现,并生成比特文件。
进行到 FW 文件夹,执行 petalinux-build 生成内核文件,然后执行:
petalinux-package --boot --u-boot --fpga xxx/hw/Fpga0.runs/impl_1/system_wrapper.bit –force
生成 BOOT.BIN 启动镜像。
拷贝 fw/images/linux/ 目录下的 BOOT.BIN,boot.scr 和 image.ub 至 SD 卡,插入 SD 卡至开发板,切换启动模式到 SD 卡,上电启动。
进行到 SW 目录下,新建 Build 目录,运行 CMake 工具,选择 Visual Studio 模板,生成工程后用 Visual Studio 打开,编译软件。
直接运行 app01。
观察控制台的输出,可以看到两个 DMA 交替输出。
在 Vivado 下 JTAG 连接开发板,选择合适的触发源,观察 DMA 以 PingPong 的方式运行。
AMD 自适应 SOC 及 FPGA 中文技术支持社区