FPGA PCIE接口的Linux DMA Engine驱动

这篇具有很好参考价值的文章主要介绍了FPGA PCIE接口的Linux DMA Engine驱动。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要

英创嵌入式主板,如ESM7000系列、ESM8000系列等,均可配置标准的PCIE×1高速接口。连接NVMe模块作高速大容量数据存储、连接多通道高速网络接口模块都是PCIE接口的典型应用。此外,对于工控领域中的高速数据采集,还可采用FPGA的PCIE IP核实现PCIE EP端点,与英创嵌入式主板构成高效低成本的应用方案。本文简要介绍方案硬件配置,以及PCIE在Linux平台上的驱动程序实现。

硬件设计要点

Xilinx公司为它的FPGA设计有多种PCIE EP端点的IP核,针对本文的应用需求,选择DMA/Bridge Subsystem for PCI Express v4.1(简称PCIE/XDMA)。PCIE/XDMA在硬件上把PCIE接口转换为AXI-Stream高速并行接口(简称AXIS),工控前端逻辑只需把采集数据转换成AXI-Stream格式提供给AXIS通道。IP核会采用PCIE总线的DMA机制,把AXIS通道数据按数据块的形式直接传送至Linux的内存中,这样在Linux的应用程序就可直接处理采集数据了。Xilinx Artix 7系的低成本芯片XC7A35T、XC7A50T均可容纳PCIE/XDMA IP核,这样可保证应用方案的成本处于合理的范围。

pcie驱动,fpga开发,linux,arm开发,c语言,嵌入式硬件
图1 ETA750 Block Design Diagram

 

图1中的实例xdma_0是Xilinx公司的PCIE/DMA IP模块,作为PCIE端点设备(PCIE Endpoint Device)。Dtaker1_5_0是应用相关的前端逻辑。对PCIE的主要配置如下图所示:

pcie驱动,fpga开发,linux,arm开发,c语言,嵌入式硬件
图2 PCIE接口配置

 

上述配置定义的AXIS总线为64-bit数据宽度、总线时钟62.5MHz(ACLK)。

AXIS总线典型的握手时序如图3所示,一个数据传输周期最快需要3个ACLK,T3上升沿为数据锁存时刻:

pcie驱动,fpga开发,linux,arm开发,c语言,嵌入式硬件
图3  AXI同步握手时序

 

若前端逻辑每4个ACLK产生一个dword数据,则对应的数据速率就是125MB/s。

pcie驱动,fpga开发,linux,arm开发,c语言,嵌入式硬件
图4 PCIE中断配置

 

基于XC7A50T的PCIE/DMA IP可支持最多4路DMA通道,分别为2路发送(H2C通道)和2路接收(C2H通道),加上用户前端逻辑中断,共有至少5个中断源。采用PCIE的MSI中断机制是解决多中断源的最好方式,所以配置8个中断矢量,实际使用5个。

DMA Engine驱动

目前Xilinx公司为其IP核DMA/Bridge Subsystem for PCI Express v4.1,仅提供基于x86体系的驱动,而没有在Linux DMA Engine架构上做工作。而事实上,DMA Engine架构已成为ARM嵌入式Linux平台的DMA应用的事实标准(de facto),为此本方案首先构建了标准的DMA Engine架构驱动程序,包括通用DMA Controller驱动和面向应用的DMA Client驱动,应用程序通过标准的字符型设备节点,操作DMA Client驱动,从而实现所需的数据采集。图5是从软件开发角度来看的总体功能框图。

pcie驱动,fpga开发,linux,arm开发,c语言,嵌入式硬件
图5 方案总体功能框图

 

DMA Engine架构为不同的DMA模式提供不同的API函数,其中最主要的是单次DMA和周期DMA两种,其API函数分别为:

struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(

           struct dma_chan *chan, struct scatterlist *sgl,

           unsigned int sg_len, enum dma_data_direction direction,

           unsigned long flags);



struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(

           struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,

           size_t period_len, enum dma_data_direction direction);

DMA Controller驱动要求DMA支持Scatter-gather结构的非连续数据Buffer,但在本方案的应用中,对单次DMA情形,采用单个Buffer是最常见的应用方式,这时可采用DMAEngine的简化函数:

struct dma_async_tx_descriptor *dmaengine_prep_slave_singl(

           struct dma_chan *chan, dma_addr_t buf, size_t len,

           enum dma_data_direction direction, unsigned long flags);

Cyclic DMA模式,是把多个DMA Buffer通过其描述符(dma descriptor)表连接成环状,当一个buffer的DMA传送结束后,驱动程序的中断线程将自动启动面向下一个描述符的DMA Buffer。由DMA descriptor表描述的逻辑流程如图2所示:

pcie驱动,fpga开发,linux,arm开发,c语言,嵌入式硬件
图6 Cyclic DMA逻辑流程

 

本方案的DMA Controller驱动实现了上述两种DMA传输方式,即单次DMA传输和周期DMA传输。DMA Controller驱动本质上讲,是一种通用的DMA服务器,如何使用DMA的传输功能,实现具体的数据传输任务,则是由DMA Client来决定的。Linux把DMA服务与具体应用分成两个部分,有利于DMA Controller驱动面向不同的应用场景。

DMA Client驱动

DMA Client驱动是一个面向应用的驱动,如图5所示,它需要与User Space的上层应用程序配合运行,来完成所需的数据采集与处理。

单次DMA的操作如下所示。

/* prepare a single buffer dma */
desc = dmaengine_prep_slave_single(dchan, edev->dma_phys,
              edev->total_len, DMA_DEV_TO_MEM,
              DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
    dev_err(edev->dev, "dmaengine_prep_slave_single(..) failed\n");
    ret = -ENODEV;
    goto error_out;
}

/* setup dtaker hardware */
eta750_dtaker_setup(edev);

/* put callback, and submit dma */
desc->callback = dma_callback;
desc->callback_param = edev;
edev->cookie = dmaengine_submit(desc);
ret = dma_submit_error(edev->cookie);
if (ret) {
    dev_err(edev->dev, "DMA submit failed %d\n", ret);
    goto error_submit;
}

/* init complete, and fire */
reinit_completion(&edev->xdma_chan_complete);
dma_async_issue_pending(dchan);

/* simulate input data */
eta750_dtaker_run(edev);

/* wait dma complete */
count = wait_for_completion_timeout(&edev->xdma_chan_complete, msecs_to_jiffies(DMA_TIMEOUT));
if (count == 0) {
    dev_err(edev->dev, "wait_for_completion_timeout timeout\n");
    ret = -ETIMEDOUT;
    eta750_dtaker_end(edev);
    goto error_submit;
}

/* error processing */
eta750_dtaker_error_pro(edev);

/* stop front-end daq unit */
count = eta750_dtaker_end(edev);

/* dump data */
eta750_dtaker_dump_data(edev);
return edev->total_len;

error_submit:
dmaengine_terminate_all(dchan);

error_out:
return ret;

只有周期DMA方式才能实现连续数据采集,在DMA Client中采用双DMA Buffer的乒乓结构来实现连续采集,应用程序处理0# Buffer数据时,DMA传输数据至1# Buffer,传输结束时,进行切换,应用程序处理1# Buffer数据,DMA传输新数据至0# Buffer。周期DMA需要指定每个buffer的长度period_len,同时需指定由2个buffer构成的ping-pong buffer的总长度total_len。其DMA流程如下所示。

/* prepare cyclic buffer dma */
desc = dmaengine_prep_dma_cyclic(dchan, edev->dma_phys, edev->total_len,
edev->period_len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (!desc) {
    dev_err(edev->dev, "%s: prep dma cyclic failed!\n", __func__);
    ret = -EINVAL;
    goto error_out;
}

/* in cyclic mode */
edev->cyclic = true;

/* setup dtaker hardware */
eta750_dtaker_setup(edev);

/* put callback, and submit dma */
desc->callback = dma_callback;
desc->callback_param = edev;
edev->cookie = dmaengine_submit(desc);
ret = dma_submit_error(edev->cookie);
if (ret) {
    dev_err(edev->dev, "cyclic dma submit failed %d\n", ret);
    goto error_submit;
}

/* init complete, and fire */
reinit_completion(&edev->xdma_chan_complete);
dma_async_issue_pending(dchan);
edev->running = true;

/* simulate input data */
eta750_dtaker_run(edev);
edev->data_seed += ((edev->period_len / sizeof(u16)) * edev->data_incr);

while(!kthread_should_stop()) {
    /* wait dma complete */
    count = wait_for_completion_timeout(&edev->xdma_chan_complete, 
    msecs_to_jiffies(DMA_TIMEOUT));
    if (count == 0) {
        dev_err(edev->dev, "wait_for_completion timeout, transfer %d\n", edev->transfer_count);
        ret = -ETIMEDOUT;
        break;
    }

    /* data processing */
    eta750_dtaker_error_pro(edev);
    edev->transfer_count++;
    reinit_completion(&edev->xdma_chan_complete);

    /* fill more data */
    eta750_dtaker_run(edev);
    edev->data_seed += ((edev->period_len / sizeof(u16)) * edev->data_incr);
}

/* stop front-end daq unit */
count = eta750_dtaker_end(edev);
edev->running = false;

error_submit:
dmaengine_terminate_all(dchan);
edev->cyclic = false;
dev_info(edev->dev, "%s: dma stopped, cyclic %d, running %d\n", __func__, edev->cyclic, edev->running);

error_out:
return ret;

从上面代码可见,传送过程是一个无限循环,DMA Controller驱动会自动进行ping-pong buffer的切换。并通过回调函数通知上层应用程序,新数据已准备就绪。应用程序可通过命令来终止采集传输过程。文章来源地址https://www.toymoban.com/news/detail-609591.html

到了这里,关于FPGA PCIE接口的Linux DMA Engine驱动的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA优质开源项目 – PCIE通信

    本文介绍一个FPGA开源项目:PCIE通信。该工程围绕Vivado软件中提供的PCIE通信IP核XDMA IP建立。Xilinx提供了XDMA的开源驱动程序,可在Windows系统或者Linux系统下使用,因此采用XDMA IP进行PCIE通信是比较简单直接的。 本文主要介绍一下XDMA IP核的使用和Vivado工程的源代码结构。文章末

    2024年02月14日
    浏览(27)
  • 【PCIE】基于PCIE4C的数据传输(一)——PC访问FPGA

    PCIE4C   PCIE4C是Ultrascale+系列开始引入的硬核,它是PCIE4硬核的延续,在功能上增加了对PCIe4.0协议的支持,由于PCIe报文采用高速串行传输,到达FPGA后首先经过GT转换为低速并行数据,之后由PCIE4C进行进一步处理,得到便于用户使用的AXI-Stream形式的报文。 为了便于使用,Xil

    2024年04月08日
    浏览(42)
  • FPGA PCIe 软核的实现——扒一扒复旦微、国微PCIe软核的实现

    近来复旦微、国微等厂家相继推出了可以兼容XILINX PCIe硬核的PCIe软核,销售也到所里来推广了一下,领导交代让抽自己的时间试用研究一下,看项目中用不用的起来。 读研的时候就接触过,PCIE协议非常非常复杂,要实现非常非常困难,稍微看过一些协议,看球不懂,真给这

    2024年02月16日
    浏览(77)
  • 基于 FPGA 的 PCIe IP 核简介

                      在某些高速数据存储场景下,单个NVMe IP已无法满足带宽需求,常需要多个NVMe IP并行工作以提升写入带宽,由于NVMe底层使用PCIe,而Xilinx/Altera等厂家FPGA芯片自带的PCIe硬核往往有限,比如Kintex-7芯片一般只有一个PCIe2.0硬核,此时,基于GTX等高速收发器

    2024年02月14日
    浏览(25)
  • FPGA实现的PCIe2.0与PCIe1.0 NVMe IP性能测试

    NVMe协议固态硬盘的速度随所使用的PCIe协议不同而不同。NVMe固态硬盘在PCIe Gen2、Gen3、Gen4条件下,理论有效带宽分别为2GB/s、3.938GB/s、7.877GB/s。目前,基于PCIe Gen5的NVMe SSD在2023年已发布,其理论有效带宽为17.754GB/s。 在之前的帖子中,我们介绍了一个在PCIe3.0协议下的基于FPGA

    2024年04月14日
    浏览(44)
  • PCIE调试技巧 FPGA:解决问题的有效方法

    PCIE调试技巧 FPGA:解决问题的有效方法 在FPGA开发中,PCI Express(PCIE)接口已经成为了高性能数据传输的首选标准。然而,由于其复杂性,PCIE调试常常成为开发者面临的一项艰巨任务。本文将分享一些PCIE调试心路和一些有效的技巧,帮助开发者识别、定位和解决PCIE相关的问

    2024年03月21日
    浏览(52)
  • 基于PCIe的NVMe协议在FPGA中实现方法

            NVMe协议是工作在PCIE的最上层协议层的,故需要先搞清楚PCIE。本文基于Xilinx的UltraScale+,开发工具为Vivado2021.2。学习中以spec为主,其它资料辅助参考(重点介绍学习方法及资料,有时间再加细节)。 请勿转载!      主要参考的文章是《老男孩读PCIe》,同时参考《

    2024年02月02日
    浏览(27)
  • FPGA通过PCIe读写DDR4仿真IP核

    环境:Vivado 17.4 根据个人所需选择器件库,创建好空的工程文件夹。 添加第一个IP:utility buffer 双击模块进入配置,选择差分时钟; 第二个IP,直接搜索DMA ,双击添加; 添加之后同样双击模块,进入配置:  配置完成。  第三个IP:AXI Interconnect,双击模块进入配置,将主从接

    2023年04月19日
    浏览(34)
  • PCIe 总线基础 驱动接口 和 BAR空间详解

    PCIe 总线基础 PCIe扫盲系列 原版PDF PCI总线是一种树型结构,并且独立于CPU总线,可以和CPU总线并行操作。PCI总线上可以挂接PCI设备和PCI桥,PCI总线上只允许有一个PCI主设备(同一时刻),其他的均为PCI 从设备,而且读写操作只能在主从设备之间进行,从设备之间的数据交换需

    2023年04月09日
    浏览(20)
  • 关于xilinx使用PCIE实现FPGA的部分重配置实现(MCAP)

    平台:vivado21018.3 芯片:xcku115-flva1517-2-i (active) 本文官方文档:Xilinx_Answer_64761_Ultrascale_Devices 本文驱动下载地址:64761 - Bitstream Loading across the PCI Express Link in UltraScale and UltraScale+ Devices for Tandem PCIe and Partial Reconfiguration (xilinx.com) 本文参考:Xilinx基于PCIE的部分重配置实现(一)

    2024年02月01日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包