平台:vivado2017.4
芯片:xc7k325tfbg676-2 (active)
创建BD工程。
更新TCL文件
Write_bd_tcl E/CODE1/VIVADO/tcl/xdma_bd.tcl
方便下次搭建BD工程的时候可以直接使用当前这一套。
使用方法:
soure E/CODE1/VIVADO/tcl/xdma_bd.tcl
这里我们直接新建了XDMA工程,使用xdma,在m_axi上挂了一个主设备,两个从机,他们通过m_axi的地址映射。在M_AXI_LITE上挂了三个设备,他们的地址映射关系为。
接下来生成顶层的HDL模块
对模块介绍一下。使用了XDMA的IP核,IP的相关配置可以可以参考pg195。具体的配置根据硬件来选择不同的配置。
我开发板使用的是pciex4,5.0GT/S。输入时钟100MHZ,AXI参考时钟为125MHZ,使用AXI Memory Mapped。PCIE ID型号我更改了,bar空间使用AXI_LITE,PCIE to bar translation地址是0x44a00000,bar大小1M。中断未使用,DMA通道H2C和C2H都选择2个通道。其他默认。
搭建如上所示的BD原理图。
分配M_AXI地址到BRAM0和BRAM1。
0x0000_0000_0000_0000对应BRAM0.
0x0000_0000_0001_0000对应BRAM1.
分配M_AXI_LITE地址到BRAM2,BRAM3,BRAM4.
0x44A0_0000对应BRAM2.
0x44A1_0000对应BRAM3.
0x44A2_0000对应BRAM4.
这里我将BRAM0对应的PORTA部分引出去,接入自己需要输入的信号。
对于ram的端口应该都很熟悉了。
这里具体的操作模式是,PCIE通过AXI访问BRAM,我们将BRAM的PORTA引出后在上面不同的地址上放入自己存储的值,或通过AXI写入的值。部分代码如下:
//---------------------------------------
//register read/write opteration interface
//---------------------------------------
input wire bram_clk ,//寄存器写时钟同bram一致125mhz
input wire bram_rst ,//寄存器复位同bram一致
input wire[15:0] bram_addr ,//寄存器地址
input wire[31:0] bram_wr_data ,//寄存器写入数据
input wire[3:0] bram_we ,//寄存器字节写使能
input wire bram_en ,//寄存器写使能bram_en
output reg [31:0] bram_rd_data //寄存器读出数据
//写入数据
else if(bram_en == 1'b1 && bram_wr_dly == 1'b0 && bram_we == 4'b1111)
begin
case(bram_addr)
//----------------------------------------
// pci bus test
//----------------------------------------
16'h0000_0000:reg_bus_test <= #U_DLY bram_wr_data;
//vpx led控制
16'h0000_0058:reg_vpx_led <= #U_DLY bram_wr_data[0];
.....
//读取部分。
case(bram_addr)
//----------------------------------------
// pci bus test
//----------------------------------------
16'h0000_0000: bram_rd_data <= #U_DLY ~reg_bus_test;
这里我们为了验证bram的读写,将地址0的写入值和读取值值取反。方便验证。
准备工作完成。
添加SDC约束文件。根据原理图,将PCIEx4的引脚分配完善。
添加SPI约束设置SPI加载速度50m,总线位宽4位。
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
生成bit流文件后,下载程序后会出现找不到模块的情况。情况的现象为通过设备管理器能发现模块。但是进入不了bar空间。又会出现设备管理器发现不了设备的情况。
经过分析对比别的开发板。在xdma时钟上,我们选择了外部输入的125mhz差分时钟。就会出现有时找得到模块。但是找到了模块也没有bar空间。这里观察开发板上有两个差分时钟。我们将另外一对差分100mhz接入XDMA的IP。这里就能找到当前模块。读写bar空间读写正常。
正常读写。
下面测试XDMA驱动。准备安装一个XDMA驱动测试XDMA的传输。
前面说到我们把模块的名字改了,即vendor id。之后发现安装不了下载的驱动。在驱动文件部分找到了驱动安装文件inf,发现了不能安装的问题的原因。
使用gvim读取inf文件。发现文件中包含的设备ID是固定的。
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7011
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7012
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7014
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7018
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7021
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7022
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7024
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7028
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7031
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7032
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7034
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_7038
大致包含这些设备ID,这里是xilinx官方自己设置的设备ID型号。在创建XDMA IP的时候,将ID改为这些值就可以正常安装驱动了。这里先尝试了官方xdma_driver_win_bin_x64_12052020文件下的驱动文件。发现安装后出现黄色感叹号,即驱动不能正常使用。测试模式下还是一致。在xilinx论坛上找到说需要更换一个版本的驱动就可以了。这里找到Xilinx_Answer_65444_Windows_Files文件下的驱动。安装,打开,发现能正常使用。对于设定不同的设备ID,可以由VS2015中更改inf文件后编译生成驱动,也可以直接在inf文件中修改为你自己需要的设备ID号。
%XDMA.DRVDESC%= XDMA_Inst, PCI\VEN_10ee&DEV_xxxx(自定义设置的ID)
另外,设备的名字也是可以更改的。
;======================= String Definitions ===================================
[Strings]
Companyname = "Xilinx"
ClassName = "Xilinx Drivers"
XDMA.SVCDESC = "Xilinx DMA Driver Service"
XDMA.DRVDESC = "Xilinx DMA"
DeviceDescription = "Xilinx FPGA device"
以上部分在XDMA.DRVDESC = "Xilinx DMA"改为XDMA.DRVDESC = "你的设备名字"就可以更改了。
保存存后重新安装驱动。就可以发现能正常找到驱动了。
在正常读写bar0,控制我的bram存储器。这里实验了一下,使用WINDRIVER打开pcie硬件设备发现有两个bar。使用bar0能正常读写,这里控制bar0地址0,0读取为我们设置值A5A5A5A5,向地址0在写入值,读回与写入值相反,验证成功。
下面我们验证XDMA的通道的读写。
我们设计的程序,使用M_AXI上挂了两个bram分别用来存储。首先使用驱动里面自带的exe来验证xdma的功能的正确性。
这里我们直接给XDMA使用两个bram来验证此IP。通过M_AXI总线连接的的BRAM,使用其中一个来当做内存,即可以启动XDMA的H2C和C2H来向BRAM里面写入数据和读出数据。使用以上的exe文件写入的数据放在此bram里面。之后在从里面读出。
这里访问windriver里面的偏移地址是,pcie的基地址,通过BAR0来访问pcie是,这里的地址会映射到M_AXI_LITE地址上。比如说,向pcie的bar0的0x0地址写入一个数据,这里pcie创建TLP包发送,解析后转换到AXI_LITE总线上的地址0x44A0_0000,写入数据。AXI总线更新地址,突发长度,数据位数,等待和BRAM握手成功后,开启此次传输。AXI写入地址0x44A0_0000,转换到bram地址为bram_addr_a地址0。写入数据配置的数据。
读取的过程正好相反。从BRAM地址0上读出数据,放入AXI总线的地址0x44A0_0000上,pcie将AXI总线地址上的数据读出打包为TLP文件从bar0的地址0传输出去。
使用BD创建XDMA的IP核的时候,新建BRAM和XDMA自动连接时会使用AXI_Interconnect来连接M_AXI和BRAM以及M_AXI_LITE连接的BRAM。关于此IP的作用可以查阅PG_059_AXI_Interconnect。
XILINX的XDMA IP 使用了AXI总线作为数据和指令的传输。当涉及到多个AXI协议之间的互联的时候就需要使用此IP。
其中最主要的就是AXI Crossbar。实现了多个主设备和多个从设备直接的连接。IP还实现了AXI地址位宽之间的转换。不同AXI时钟直接的转换。不同AXI协议之间的转换,以及设置有数据缓存FIFO等等。
在M_AXI控制的BRAM这一部分,我们使用windriver打开硬件,读取bar1空间的0x0地址上的数据。这里可以看到和XDMA4.1文档上描述的寄存器一致。
Bar1地址0,可以查看pg195XDMA4.1手册里面的描述。
寄存器空间,通过读取请求或者写入请求映射到基址寄存器bar既可以从主机访问DMA和用户内部配置的寄存器和状态寄存器。
关于PCIE的bar空间的分配,这里参考pg195XDMA4.1手册里面的描述的目标桥接器。
目标桥接器用于接收来自主机的请求,根据bar,请求将通过AXI4_LITE主接口转发到内部目标用户或CQ旁路端口。在下游用户逻辑返回非转发请求的数据后,目标桥接器就会生成读取完成TLP,通过CC总线将其发送至PCIE IP。具体的关系如下。
32位bar
IP定义期间PCIE BAR选择 |
BAR0(32位) |
BAR1(32位) |
BAR2(32位) |
默认值 |
DMA |
||
启用‘PCIE to AXI LITE MASTER’ |
PCIE到AXI4_LITE主接口 |
DMA |
|
选择‘PCIE to AXI LITE MASTER’和‘PCIE to DMA BYPASS’ |
PCIE到AXI4_LITE主接口 |
DMA |
PCIE到DMA旁路 |
启用‘PCIE to DMA BYPASS’ |
DMA |
PCIE到DMA旁路 |
如果是64位bar
IP定义期间PCIE BAR选择 |
BAR0(64位) |
BAR1(64位) |
BAR2(64位) |
默认值 |
DMA |
||
启用‘PCIE to AXI LITE MASTER’ |
PCIE到AXI4_LITE主接口 |
DMA |
|
选择‘PCIE to AXI LITE MASTER’和‘PCIE to DMA BYPASS’ |
PCIE到AXI4_LITE主接口 |
DMA |
PCIE到DMA旁路 |
启用‘PCIE to DMA BYPASS’ |
DMA |
PCIE到DMA旁路 |
用户可以根据需求自由选择BAR。
下面分析我们读取出的BAR1的地址0的数据。
读取值为0x1FC00006
查看H2C通道寄存器(0x0)H2C Channel Identifier (0x00)
和读取值一样。
下面我们来验证测试XDMA驱动里面自带的exe文件。
打开安装驱动所在的文件夹,在文件夹下打开命令窗口。
这些exe文件的作用为可以查看驱动源码里面的介绍。
/
|__ build/ - Generated directory containing build output binaries.
|__ exe/ - Contains sample client application source code.
| |__ user_events/ - Sample code demonstrating access to user event interrupts.
| |__ xdma_info/ - Utility application which prints out the XDMA core ip
| | configuration.
| |__ xdma_rw/ - Utility for reading/writing to/from xdma device nodes such
| | as control, user, bypass, h2c_0, c2h_0 etc.
| |__ xdma_test/ - Basic test application which performs H2C/C2H transfers on
| all present channels.
|__ inc/ - Contains public API header file for XDMA driver.
|__ sys/ - Contains the XDMA driver source code.
|__ README.md - This file.
|__ XDMA.sln - Visual Studio Solution.
首先输入
xdma_test.exe
这里的xdma_test.exe的作用是,通道0的h2c从主机内存中向c2h写入4096个数据,在从c2h读出数据,之后做了对比,对比成功则测试通道1。
下面测试读写文件xdma_rw.exe首先对xdma_rw.exe的操作说明。
Control |
用于访问 DMA/Bridge Subsystem for PCI Express® 寄存器。 |
User |
操作你设置的AXI_LITE上的数据,比如你上面挂的barm。 |
Event |
是用来操作中断相关的。 |
H2c |
是启动dma传输方向为主机到fpga |
C2h |
是启动dma传输方向为fpga到主机。 |
比如使用user来读取我工程的用户寄存器。即AXI_LITE控制的bram上的数据。
xdma_rw.exe user(操作模式) read(读or写) 0x00(操作地址) -l(读出的形式文件或者字节)4(读出的字节数)
操作一下。直接读写文件。
写入4k的二进制文件
xdma_rw.exe h2c_0 write 0x00000000 -b -f datafile4K.bin -l 4096
读出4K的二进制文件到dataflie4k_rd.bin
xdma_rw.exe c2h_0 read 0x00000000 -b -f datafile4K_rd.bin -l 4096
结果如下。
使用为了直观的感受,我把二进制文件的第一行全部替换为第二行的内容,从XDMA读出的文件对比情况如下。
到这里就完整的验证的XDMA的传输功能。
注:simple_dma.exe程序的功能,是一次向XDMA写入8M的数据,在读出。这里我们所创建的BRAM大小很小达不到存储的要求,所以这里不验证。如果板子上带有DDR3这里可以尝试。可以把工程的BRAM替换为DDR3来测试这个程序的功能。
这里为了更加直观的验证读取功能。继续使用xdma_rw读出数据这里不保存为二进制文件,直接显示出来。
使用xdma_rw.exe启动dma传输从地址0读取4096byte数据。输入:
xdma_rw.exe c2h_0 read 0x00000000 -l 4096
下面是启动c2h后通过ila采集的m_axi上的数据。
改成 Debug 下载,并打开 ILA 集成逻辑分析仪,单步调试,设置触发条件。
对于写事务,设置检测 WVALID 和 WREADY 均有效;
对于读事务,设置检测 RVALID 和 RREADY 都有效。
输入后,驱动启动DMA传输,在M_AXI上从地址0开始读取数据,首先需要在M_AXI上读地址通道握手。IP AXI_Interconnect会根据当前映射的地址选择不同BRAM。这里我们的地址为0,在M00_AXI读地址通道握手成功。开始传输数据数据,从机的数据是32bit,主机M_AXI的数据是128bit,通过下图可以看见,数据在从机的上的axi读数据通道握手传输4次后,主机M_AXI读数据通道握手传输一次。
这里是从m00_axi上读取得32bit数据。
接下来操作M_AXI_LITE。使用user来控制向bar0写入和读出数据。
具体的操作如下:
使用xdma_rw.exe向地址0写入4byte数据0x01020304
xdma_rw.exe user write 0x00 -l 4 0x01 0x02 0x03 0x04
启动xdma_rw.exe 使用user从AXI_LITE读取地址0上的4byte数据。
xdma_rw.exe user read 0x00 -l 4
其结果如下。
读出值与写入值取反,验证成功。
工程在M_AXI上挂了两个bram,M_AXIL_LITE上挂了三个BRAM,如何访问不同的bram呢。PCIE的XDMA驱动会根据你上位机里面设定的地址不同从而将你访问的请求转换到不同的AXI地址上。这里参考了米联客的上位机部分的代码。感兴趣的可以自己使用QT改写。
首先使用user来访问0x10000,即访问M_AXI_LITE。
在vivado上使用ila采集可以看到,在访问地址0x10000+0x00上写了数据12,在读出数据,ila采样如下。
通过地址转换,这里访问了bram3。信号通过AXI_LITE后转换为到SLOT_5_AXI。
经过测试,访问0x0000和0x20000地址后,进过M_AXI_LITE转换后,分别访问了bram2和bram4,部分截图如下。
访问0x20000
从0x20000读出数据。
接下来访问M_AXI。还是使用xdma_rw.exe。
写入:xdma_rw.exe h2c_0 write 0x10000 -b -f datafile4K.bin -l 4096
读出:xdma_rw.exe c2h_0 read 0x10000 -l 4096
使用C2H_0访问M_AXI的0x10000
通过ILA采集部分截图如下。
首先M_AXI上地址解析出0x0000_0000_0001_0000通过AXI_Interconnect后访问了bram1。同bram1在读数据通道上握手成功。