XILINX XDMA pcie 使用

前段时间在公司项目中调试了PCIE,正好做一个总结,那些介绍XDMA、PCIE之类的多余的东西网上能搜到很多,我这里就不多说。我写的只是自己的一些想法,以及自己的设计思路。

同每一个刚开始调试PCIE的人一样,作为初学者大家都是先去网上搜集大量的资料学习,我也搜集了很多,看完之后其实还是有点泪崩,不知道怎么做。

在Vivado的IP Catalog中我们可以看到有三个可用于PCIE控制的IP核,其中第三个IP使用起来是最简单的,是将PCIE快速应用到项目中的最好的选择。

打开第三个IP核配置界面如下:

Mode: 配置模式, 选择 Advanced 高级配置

Lane Width:根据硬件选择支持 X1 、X2、X4

Max Link Speed: 选择 5.0GT/s 即 PCIE2.0

Reference Clock : 100MHZ, 参考时钟 100M

DMA Interface Option: 接口选择 AXI4 Stream或者AXI,因为我的项目中接口不是AXI4接口,所以我选择了AXI4 Stream

AXI Data Width: 128bit, 即 AXI4 数据总线宽度为 128bit

AXI Clock : 125M, 即 AXI4 接口时钟为 125MHZ(如果有125M和250M供选择的话,在带宽允许的情况下,建议选择125M的,可以避免综合实现后出现IP核内部时序不收敛的情况出现),我在项目调试的初期选用的250Mhz,整个工程搭好综合实现就出现了IP核内部时序问题,无赖只好降低时钟到125Mhz)

AXI-Lite Slave Interface:我们不勾选,因为XDMA的所有寄存器都由PS端配置,fpga用户逻辑这边不做任何的操作

PCIE ID配置里面的参数我们选择默认参数即可

PCIE BAR 配置,这个配置比较重要!!!

PCIE to AXI Lite Master Interface:不勾选 , 我们不使用这个接口来访问用户逻辑,如果有人看过《PCIE_xdma教程_LINUX.pdf》这个pdf文档的话,应该知道这个文档里讲的是使用这个接口来进行访问用户逻辑,而不使用DMA Bypass 接口,我刚开始也是参照这个文档使用这个接口,但是后面在使用的时候发现了问题,这个接口的数据输出是经过XDMA内部过滤的,也就是说这个接口出来的地址并不是所有的都能给用户使用,有部分地址是用来对XDMA的寄存器配置使用的,容易造成用户地址和配置地址混合,如果用户地址只有几个十几个还好,要是有几十上百个的话,这个接口并不实用。

PCIE to DMA Bypass Interface:勾选上,我们用这个接口来访问用户逻辑,映射空间选择 1M基本够用, 当然用户也可以根据实际需要来自定义大小。

PCIE to AXI Translation: 这个设置比较重要, 通常情况下, 主机侧 PCIE BAR 地址与用户逻辑侧地址是不一样的, 这个设置就是进行 BAR 地址到 AXI 地址的转换, 比如主机一侧 BAR地址为 0, IP 里面转换设置为 0x80000000, 则主机访问 BAR 地址 0 转换到 AXI LIte 总线地址就是 0x80000000

在Ubuntu中对Bypass配置的命令是:./xdma_bypass 地址 类型  数据  

如果没有数据即为读操作,如果有数据则为写操作。地址根据类型来决定,为什么这么说呢,请看下面:

./xdma_bypass  0x0 w 0x12345678   (当类型为w时,表示是以word传输数据,32bit,地址只能是4的倍数)

./xdma_bypass  0x0 h 0x1234   (当类型为w时,表示是以半个word传输数据,16bit,地址只能是2的倍数)

./xdma_bypass  0x0 b 0x12   (当类型为w时,表示是以半个字节传输数据,8bit)

又因为数据是128bit,地址0~15,每个地址其实只有一个字节的数据:

./xdma_bypass   0   w 0x12345678,fpga用户收到的数据为:128'h00000000_00000000_00000000_12345678,地址为0

./xdma_bypass   4   w 0x23456789,fpga用户收到的数据为:128'h00000000_00000000_23456789_00000000,地址为4

./xdma_bypass   8   w 0x3456789a,fpga用户收到的数据为:128'h00000000_3456789a_00000000_00000000,地址为8

./xdma_bypass  12  w 0x456789ab,fpga用户收到的数据为:128'h456789ab_00000000_00000000_00000000,地址为12

./xdma_bypass  16  w 0x56789abc,fpga用户收到的数据为:128'h00000000_00000000_00000000_56789abc,地址为16

./xdma_bypass  3  b 0x56,fpga用户收到的数据为:128'h00000000_00000000_00000000_56000000,地址为3

./xdma_bypass  6  b 0x1256,fpga用户收到的数据为:128'h00000000_00000000_12560000_00000000,地址为6

上面这种数据传输方式可以自己去研究一下,我也是自己调试的时候看出来的现象。

PCIE 中断设置

User Interrupts: 用户中断, XDMA 提供 16 条中断线给用户逻辑, 这里面可以配置使用几条中断线。

Legacy Interrupt: XDMA 支持 Legacy 中断

选择 MSI 中断:注意: MSI 中断和 MSI-X 中断只能选择一个, 否则会报错, 如果选择了 MSI 中断, 则可以选择 Legacy 中断, 如果选择了 MSI-X 中断, 那么 MSI 必须取消选择, 同时 Legacy 也必须选择 None。 此 IP 对于 7 系列设置有这么个问题, 如果使用 Ultrascale 系列, 则可以全部选择
 

配置 DMA 相关内容

Number of DMA Read Channel(H2C) 和 Number of DMA Write Channel(C2H) 通道数:对于 PCIE2.0 来说最大只能选择 2, 也就是 XDMA 可以提供最多两个独立的写通道和两个独立的读通道, 独立的通道对于实际应用中有很大的作用, 在带宽允许的前提下, 一个 PCIE可以实现多种不同的传输功能, 并且互不影响。

Number of Request IDs for Read (Write) channel : 这个是每个通道设置允许最大的outstanding 数量, 按照默认即可

打开xdma_0的源文件树,可以看到其内部有一个xdma0_pcie2_ip,双击打开这个xdma0_pcie2_ip可以看到就是7 Series Integrated Block for PCI Express IP,也就是说XDMA其实是将7 Series Integrated Block for PCI Express IP进行封装而产生的。如果使用7 Series Integrated Block for PCI Express IP进行PCIE开发我们就得自己编写剩下得其它模块。

一般来讲对于一个自己不太了解的IP核生成后,刚开始可能不太会使用,我一般产用的方法就是生成一个Example Design,

从example的工程中我们可以看到顶层就例化了两个模块,一个XDMA  IP核和一个xdma_app模块,xdma_app模块内部就是一个AXI4接口的RAM,用来存放DMA Bypass Interface接口用户寄存器的数据,通道0和通道1的数据h2c出来后直接回环到c2h,

我在第一次使用这个IP核的时候把IP核手册看完,都还是没有看明白h2c通道出来的数据流到底包含哪些信息,相信不止我一个人有这样的感觉,后来经过上板测试,才知道h2c通道出来的是只是纯数据流,所有的数据都是我们所需要的数据,往c2h通道灌输数据的时候也是直接灌数据就可以了,不需要组帧啥的。 // Ref clock buffer IBUFDS_GTE2 refclk_ibuf (.O(sys_clk), .ODIV2(), .I(sys_clk_p), .CEB(1'b0), .IB(sys_clk_n)); // Reset buffer IBUF sys_reset_n_ibuf (.O(sys_rst_n_c), .I(sys_rst_n));

// Core Top Level Wrapper
xdma_0 xdma_0_i
(
//---------------------------------------------------------------------------------------//
// PCI Express (pci_exp) Interface //
//---------------------------------------------------------------------------------------//
.sys_clk ( sys_clk ),
.sys_rst_n ( sys_rst_n_c ),

  // Tx
  .pci_exp_txn     ( pci_exp_txn ),
  .pci_exp_txp     ( pci_exp_txp ),
  
  // Rx
  .pci_exp_rxn     ( pci_exp_rxn ),
  .pci_exp_rxp     ( pci_exp_rxp ),


   // CQ Bypass ports
  .m_axib_awid      (m_axib_awid),
  .m_axib_awaddr    (m_axib_awaddr),
  .m_axib_awlen     (m_axib_awlen),
  .m_axib_awsize    (m_axib_awsize),
  .m_axib_awburst   (m_axib_awburst),
  .m_axib_awprot    (m_axib_awprot),
  .m_axib_awvalid   (m_axib_awvalid),
  .m_axib_awready   (m_axib_awready),
  .m_axib_awlock    (m_axib_awlock),
  .m_axib_awcache   (m_axib_awcache),
  .m_axib_wdata     (m_axib_wdata),
  .m_axib_wstrb     (m_axib_wstrb),
  .m_axib_wlast     (m_axib_wlast),
  .m_axib_wvalid    (m_axib_wvalid),
  .m_axib_wready    (m_axib_wready),
  .m_axib_bid       (m_axib_bid),
  .m_axib_bresp     (m_axib_bresp),
  .m_axib_bvalid    (m_axib_bvalid),
  .m_axib_bready    (m_axib_bready),
  .m_axib_arid      (m_axib_arid),
  .m_axib_araddr    (m_axib_araddr),
  .m_axib_arlen     (m_axib_arlen),
  .m_axib_arsize    (m_axib_arsize),
  .m_axib_arburst   (m_axib_arburst),
  .m_axib_arprot    (m_axib_arprot),
  .m_axib_arvalid   (m_axib_arvalid),
  .m_axib_arready   (m_axib_arready),
  .m_axib_arlock    (m_axib_arlock),
  .m_axib_arcache   (m_axib_arcache),
  .m_axib_rid       (m_axib_rid),
  .m_axib_rdata     (m_axib_rdata),
  .m_axib_rresp     (m_axib_rresp),
  .m_axib_rlast     (m_axib_rlast),
  .m_axib_rvalid    (m_axib_rvalid),
  .m_axib_rready    (m_axib_rready),

  // AXI streaming ports
  .s_axis_c2h_tdata_0   (m_axis_h2c_tdata_0),
  .s_axis_c2h_tlast_0   (m_axis_h2c_tlast_0),
  .s_axis_c2h_tvalid_0  (m_axis_h2c_tvalid_0),
  .s_axis_c2h_tready_0  (m_axis_h2c_tready_0),
  .s_axis_c2h_tkeep_0   (m_axis_h2c_tkeep_0),
  .s_axis_c2h_tdata_1   (m_axis_h2c_tdata_1),
  .s_axis_c2h_tlast_1   (m_axis_h2c_tlast_1),
  .s_axis_c2h_tvalid_1  (m_axis_h2c_tvalid_1),
  .s_axis_c2h_tready_1  (m_axis_h2c_tready_1),
  .s_axis_c2h_tkeep_1   (m_axis_h2c_tkeep_1),
  .m_axis_h2c_tdata_0   (m_axis_h2c_tdata_0),
  .m_axis_h2c_tlast_0   (m_axis_h2c_tlast_0),
  .m_axis_h2c_tvalid_0  (m_axis_h2c_tvalid_0),
  .m_axis_h2c_tready_0  (m_axis_h2c_tready_0),
  .m_axis_h2c_tkeep_0   (m_axis_h2c_tkeep_0),
  .m_axis_h2c_tdata_1   (m_axis_h2c_tdata_1),
  .m_axis_h2c_tlast_1   (m_axis_h2c_tlast_1),
  .m_axis_h2c_tvalid_1  (m_axis_h2c_tvalid_1),
  .m_axis_h2c_tready_1  (m_axis_h2c_tready_1),
  .m_axis_h2c_tkeep_1   (m_axis_h2c_tkeep_1),



  .usr_irq_req       (usr_irq_req),
  .usr_irq_ack       (usr_irq_ack),
  .msi_enable        (msi_enable),
  .msi_vector_width  (msi_vector_width),




  //-- AXI Global
  .axi_aclk        ( user_clk ),
  .axi_aresetn     ( user_resetn ),
  .user_lnk_up     ( user_lnk_up )
);

example工程自带有仿真,我们可以使用Vivado自带的仿真工程仿真。

如果仔细分析仿真工程我们可以看到在pci_exp_usrapp_tx模块中用task封装了很多类型的PCIE数据包,有TSK_TX_TYPE0_CONFIGURATION_READ、TSK_TX_TYPE1_CONFIGURATION_READ、TSK_TX_TYPE0_CONFIGURATION_WRITE、TSK_TX_TYPE1_CONFIGURATION_WRITE、TSK_TX_MEMORY_READ_64、TSK_TX_MEMORY_WRITE_32。。。。。等等,基本上所有的PCIE数据包都在这里面,想了解的自己可以去细看,仿真的切入点是在sample_tests.vh文件中,在该文件中有很多testcase,在不同的testcase中调用相应的TSK发送相应的PCIE数据包,默认仿真testcase是dma_test0,如果想看懂仿真平台,这部分代码得去细看,这里我就不做详细描述了,,,,

看懂上面得仿真平台基本上也就知道在Ubuntu中是如何配置XDMA,关于寄存器的配置,等有时间再补充。。。。。

另外:

经过测试DMA目前最大只能传输8Mbytes数据,如果要传输大于8Mbytes数据则需要多次传输

  • 48
    点赞
  • 289
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值