一:前言

这篇博客是我应一位网友之约写的,他想要学习基于FPGA的PCIe DMA控制器设计,但是手上没有合适的Xilinx开发板,而且xapp1052又没有提供仿真代码,让他的学习陷入了困境。所以我想了想,还是用EDK搭建一个微小系统,然后用modelsim来仿真xapp1052的DMA收发控制,这样应该是最全面的理解PCIe_DMA了,希望对大家都有帮助。

二:前期准备

1、Xapp1052 Demo(http://download.csdn.net/download/yuzeren48/7723795)

2、ISE14.1套件

3、基本会使用EDK(主要是Xilinx Platform Studio,XPS 和 Software Development Kit,SDK)

三:操作步骤

1、编译库文件,将D:\Xilinx\14.1\ISE_DS\ISE\verilog\mti_se\10.1b\nt\modelsim.ini中选中部分复制粘贴到D:\modeltech_10.1b\modelsim.ini中

2、打开XPS,新建一个最小系统,使用microblaze和PLB总线,总线上挂载的硬件IP如图2所示,硬件的总线地址如图3所示。

图2 硬件IP

图3 硬件IP总线地址

3、完成硬件系统搭建后,导出到SDK

4、打开SDK后,先新建一个BSP包,步骤请参考《Xilinx FPGA开发实用教程》

然后新建一个空的Xilinx C Project,命名为example。

在E:\xapp1052\dma_performance_demo\win32_sw\win32_driver\source下找到ioctrl.h,复制粘贴到E:\pcie_edk\EDK\workspace\example\src

在src中添加C文件,命名为RC_example.c。再将

D:\Xilinx\14.1\ISE_DS\EDK\sw\XilinxProcessorIPLib\drivers\pcie_v4_01_a\examples\xpcie_rc_enumerate_example.c中的代码复制到RC_example.c中

对RC_example.c做如下修改:

//#define PCIE_CFG_BAR_0_ADDR         0x11110000
#define PCIE_EP_CFG_BAR_0_ADDR 0x0000FFFF // Remote EP BAR0
#define PCIE_RC_CFG_BAR_0_ADDR 0x0000EEEE // RC BAR0

添加:

//-------------------BMD Mrd Test
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + DCR_OFFSET) = Xil_EndianSwap32(0x00000001); //1. DMA assert reset
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + DCR_OFFSET) = Xil_EndianSwap32(0x00000000); //
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_ADDR_OFFSET) = PCIE_RC_CFG_BAR_0_ADDR; //2. Read DMA TLP Address Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_SIZE_OFFSET) = Xil_EndianSwap32(0x0000050/); //3. Read DMA TLP Size Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_COUNT_OFFSET) = Xil_EndianSwap32(0x00000100); //4. Read DMA TLP Count Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_PATTERN_OFFSET) = Xil_EndianSwap32(0xA3A2A1A0); //5. Read DMA Data Pattern Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + DCSR_OFFSET) = Xil_EndianSwap32(0x00010000); //7. MWr start

或者

//-------------------BMD Mrd Test
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + DCR_OFFSET) = Xil_EndianSwap32(0x00000001); //1. DMA assert reset
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + DCR_OFFSET) = Xil_EndianSwap32(0x00000000); //
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_ADDR_OFFSET) = PCIE_RC_CFG_BAR_0_ADDR; //2. Read DMA TLP Address Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_SIZE_OFFSET) = Xil_EndianSwap32(0x0000050/); //3. Read DMA TLP Size Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_COUNT_OFFSET) = Xil_EndianSwap32(0x00000100); //4. Read DMA TLP Count Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + READ_PATTERN_OFFSET) = Xil_EndianSwap32(0xA3A2A1A0); //5. Read DMA Data Pattern Register
*(unsigned int*)(XPAR_PLBV46_PCIE_0_IPIFBAR_0 + DCSR_OFFSET) = Xil_EndianSwap32(0x00010000); //7. MWr start

在初始化RC端配置寄存器时,添加代码:

//---------------------------------------------------- Configure RC
//Write Address to PCIe BAR0
HeaderData = PCIE_RC_CFG_BAR_0_ADDR;
XPcie_WriteLocalConfigSpace(XlnxRootComplexPtr, PCIE_CFG_BAR_0_REG, HeaderData);

将RC端pcie_bar0设为0x0000EEEE (有大小端,故实际地址为0xEEEE0000)。

在枚举使需要初始化远端EP,修改代码,使能master enable bit:

ConfigData |= (PCIE_CFG_CMD_BUSM_EN | PCIE_CFG_CMD_MEM_EN | 0x80000000    //master enable bit ); 

修改EP端pcie_bar0为0x0000FFFF(有大小端,故实际地址为0xFFFF0000):

/* Write Address to PCIe BAR0 */
ConfigData = (PCIE_EP_CFG_BAR_0_ADDR | PCIeBusNum | PCIeDevNum | PCIeFunNum);

最后,去掉所有printf函数,打印太慢了,影响仿真。

编译后生成elf文件,打开XPS,设置sim executable:

在Edit中设置preference

然后点击Generate HDL Files,再Launch Simulator

在打开的modelsim脚本栏中输入c;

完成RC编译,再输入s; 开始对RC端仿真。

5、若要仿真整个PCIE DMA,则需要修改..\EDK\simulation\behavioral文件夹下的system_tb.v,在system_tb.v中加入EP端用户逻辑,修改如下:

// START USER CODE (Do not remove this line)

  // User: Put your stimulus here. Code in this
// section will not be overwritten.
initial
begin
pcie_sysclk_p = 'b1;
forever #(fpga_0_clk_1_sys_clk_p_pin_PERIOD)
pcie_sysclk_p = ~pcie_sysclk_p; //100MHz
end initial
begin
pcie_sysclk_n = 'b0;
forever #(fpga_0_clk_1_sys_clk_p_pin_PERIOD)
pcie_sysclk_n = ~pcie_sysclk_n; //100MHz
end reg EP_pcie_sysclk_p;
reg EP_pcie_sysclk_n; initial
begin
EP_pcie_sysclk_p = 'b1;
forever #()
EP_pcie_sysclk_p = ~EP_pcie_sysclk_p; //125 MHz
end initial
begin
EP_pcie_sysclk_n = 'b0;
forever #()
EP_pcie_sysclk_n = ~EP_pcie_sysclk_n; //125 MHz
end wire ep_pci_exp_txp;
wire ep_pci_exp_txn; always@* begin
plbv46_pcie_0_RXP_pin = ep_pci_exp_txp;
plbv46_pcie_0_RXN_pin = ep_pci_exp_txn;
end xilinx_pcie_2_0_ep_v6 # ( .PL_FAST_TRAIN("TRUE") )
EP ( // SYS Inteface
.sys_clk_p(EP_pcie_sysclk_p),
.sys_clk_n(EP_pcie_sysclk_n),
.sys_reset(fpga_0_rst_1_sys_rst_pin), `ifdef ENABLE_LEDS
// Misc signals
.led_0(led_0),
.led_1(led_1),
.led_2(led_2),
`endif // PCI-Express Interface
.pci_exp_txn(ep_pci_exp_txn),
.pci_exp_txp(ep_pci_exp_txp),
.pci_exp_rxn(plbv46_pcie_0_TXN_pin),
.pci_exp_rxp(plbv46_pcie_0_TXP_pin) );
// END USER CODE (Do not remove this line)

为了仿真方便,修改system_tb.v后,在E:\pcie_edk\EDK\simulation下新建bmd_sim文件夹,将behavioral文件夹下的system_tb.v拷贝到bmd_sim文件夹中,编写simulate_mti.do文件,将需要编译的ep端文件(主要包括pcie硬核和xapp1052DMA)写成.f文件。

vlog -work work +incdir+E:/pcie_edk/Coregen/EP_1_7/v6_pcie_v1_7/example_design \
+define+SIMULATION \
+define+PCIE2_0 \
$env(XILINX)/verilog/src/glbl.v \
-f ../bmd_sim/ep_v6.f
vlog -work work ../bmd_sim/system_tb.v

6、打开modelsim,输入以下脚本

cd E:/pcie_edk/EDK/simulation/behavioral

do system_setup.do

c;

do ../bmd_sim/simulate_mti.do

s;

do ../bmd_sim/wave.do

run all

四:仿真结果

这里只贴出DMA写操作的仿真结果

Ep端DMA发送

RC端接收

写入BRAM

五:总结

从搭建系统到最后仿真,工作量还是比较大的。一篇博客很难把每一步都讲得仔仔细细,小编把里面最重要的几个点都给出来了,希望能够帮到有需要的朋友。

对于初学者,可能看了这篇博客觉得不够详细,没法真正理解PCIe DMA的使用,所以小编在最后留下一个支付宝账号【账户:bubble_fish@yeah.net  姓名:俞则人】,10元/份出售以上全部EDK工程文件以及modelsim仿真文件,这样做的目的一是为了激发您学习的动力(花钱买来的东西终究比免费得来的更加珍惜),二是为了赞助我的泡泡鱼团队基金,尊重每一位码农的劳动成果!

最新文章

  1. js中常用数组方法concat join push pop slice splice shift
  2. HTTP相关知识 --转载
  3. MySQL 5.5主从同步设置教程
  4. RHCA442学习笔记-Unit13网络性能调整
  5. Bzoj 2763: [JLOI2011]飞行路线 dijkstra,堆,最短路,分层图
  6. ORACLE多表关联UPDATE 语句
  7. oracle批量导入数据
  8. Linux下修改键盘映射
  9. 浏览器事件window.onload、o…
  10. 如何取得ChipmunkConstraint实例对象的私有属性
  11. 【English】20190321
  12. PS中如何提高修改psd图片的效率(自动选择工具)
  13. Windows和Frames之间的切换
  14. Redis(4)---主从复制
  15. swagger 自动生成接口测试用例
  16. mysql5.6.13通用二进制格式安装并使用amoeba实现对mysql5.6数据库读写分离
  17. windows8安装msi或exe软件提示2503错误的解决办法
  18. dataTable表头未对其解决方法
  19. Java中sleep()与wait()区别(涉及类锁相关概念)
  20. JavaScript——引用类型之数组

热门文章

  1. eclipse导入spring aop xml约束
  2. swift如何打印对象的地址
  3. 阿里云CentOS 7服务器挂载数据盘
  4. Vmware Horizon 服务器替换 ssl 证书
  5. memcache知识梳理
  6. C++ 读书笔记1
  7. 线性规划 Matlab
  8. Language-Directed Hardware Design for Network Performance Monitoring——Marple
  9. 微信公众平台OAuth2.0授权
  10. PHP设计模式系列 - 外观模式