本帖最后由 FPGA课程 于 2024-9-10 09:02 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述FDMA是米联客基于AXI4总线协议定制的一个DMA控制器。有了这个IP我们可以统一实现用FPGA代码直接读写PL的DDR或者ZYNQ/ZYNQMP SOC PS的DDR或者BRAM。在米联客的数据交互方案中,FDMA IP CORE 已经广泛应用于ZYNQ SOC/Artix7/Kintex7/ultrascale/ultrascale+系列FPGA/SOC。 如果用过ZYNQ/ZYNQMP SOC的都知道,要直接操作PS的DDR 通常是DMA 或者VDMA,然而用过XILINX 的DMA IP 和VDMA IP,总有一种遗憾,那就是不够灵活,还需要对寄存器配置,真是麻烦。XILINX 的总线接口是AXI4总线,自定义AXI4 IP挂到总线上就能实现对内存地址空间的读写访问。因此,我们只要掌握AXI4协议就能完成不管是PS还是PL DDR的读写操作。 本文实验目的: 1:掌握基于uiFDMA3.2的FPGA工程设计 2:利用uiFDMA3.2提供的接口,编写BRAM测试程序 3:对AXI-BRAM读写仿真和测试 我们第一入门的demo选择对BRAM仿真测试,是因为不管是仿真还是编译测试,对AXI-BRAM速度都非常快,我们在下面一个DEMO中会给出对DDR的读写仿真测试。 2系统框图本系统中先将测试数据通过Milianke UiFDMA写入Bram,再通过Milianke UiFDMA将Bram中数据读出。将读写数据进行对比。通过在线逻辑分析仪抓取读写数据测试读写正确性。
3创建图形化逻辑工程1:创建工程命名为fpga_prj双击VIVADO软件图标启动VIVADO
设置工程路径,并且命名工程名为fpga_prj
以下设置FPGA或者ZYNQ或者ZYNQ-MPSOC芯片型号,必须和开发板保持一致,如果不清楚的请查阅自己开发板的硬件手册或者根据选型手册上参数确认
2:创建Block Design并且命名为system
如下图所示,图形化system就是一个代码容器,接着我们要添加一些图像化的IP
3:添加图形化FPGA IP首先设置自定义IP的路径,这里读者可以把我们配套工程根路径下的uisrc文件夹复制到目前的工程根路径,单击Settings在弹出的Settings窗口选择IP->Repository 设置如下路径
添加+号添加IP
比如输入关键词FDMA就可以搜索到我们米联客uiFDMA IP
用类似的方法添加必要的IP如下图所示:
4:完成IP之间的信号自动这种简单的工程可以先让软件先自动化线,然后根据结果手动一些调整
可以看到连线结果
5:调整IP参数
5-1:BRAM参数设置首先把IP的配置参数修改下,双击需要设置的IP可以进行参数设置 FDMA设置数据位宽128bit 可以访问内存地址位宽32bit,最大AXI 最大burst的程度为256
BRAM设置,使用BRAM Controller 为真双口RAM
5-2:BRAM Controller参数设置AXI BRAM Controller设置axi4协议,数据位宽128bit 读延迟1个时钟
5-3:Clocking Wizard参数设置
5-4:AXI Interconnect IP设置双击AXI Interconnect IP 进行设置
设置AXI Interconnect IP的性能参数,其中Enable Register Slice 用于改善时序,Enable Data FIFO用于增加FIFO大小,增加数据传输效率
6:引出FPGA接口信号分别右击下图2个IP,然后选择Make External,把需要引出到顶层的FPGA信号引出
为了引出时钟需先右击信号PIN脚断开连接,然后Make External,之后重新连接
修改后重新连接时钟
7:修改复位和信号名字修改默认的时钟名字,以相同的方法修改其他信号
信号名修改后,修改复位脚的接线
修改完成后
8:视图优化右击空白处,弹出菜单选择Regenerate Layout优化下视图
9:地址分配AXI总线必须分配地址,设置uiFDMA的地址空间分配,起始地址可以任意设置,我们设置从0x0000_0000开始,大小64KB
10:自动校验保存工程
11:校验图形逻辑工程单击下图中的控件可以对图形化编程进行校验
12:自动产生顶层文件右击system,在弹出的菜单中选择Create HDL Wrapper
4编写FDMA的BRAM测试代码刚刚以上自动产生的顶层文件,只是引出的信号接口,并不能完成对FDMA的控制,所以我们需要自定义一个顶层文件,可以复制刚才产生的文件,修改,这样可以省一些编写调用接口的时间。 为了方便文件的管理,我们新建一个fdma_bram_test.v文件,并且复制以上代码,到这个文件夹。
右击删除刚刚自动产生的文件
添加fdma_bram_test.v文件
fdma_bram_test.v文件
- `timescale 1ns / 1ps
- module fdma_bram_test(
- input sysclk_p,
- input sysclk_n
- );
- wire [31:0] fdma_raddr;
- reg fdma_rareq;
- wire fdma_rbusy;
- wire [127:0] fdma_rdata;
- wire [15:0] fdma_rsize;
- wire fdma_rvalid;
- wire [31:0] fdma_waddr;
- reg fdma_wareq;
- wire fdma_wbusy;
- wire [127:0] fdma_wdata;
- wire [15:0] fdma_wsize;
- wire fdma_wvalid;
- wire sysclk;
- wire ui_clk;
- wire test_error;
- reg test_error1=1'b0;
- parameter TEST_MEM_SIZE = 32'd64*1024;//64KB
- parameter FDMA_BURST_LEN = 16'd512;
- parameter ADDR_MEM_OFFSET = 0;
- parameter ADDR_INC = FDMA_BURST_LEN * 16;
-
- parameter WRITE1 = 0;
- parameter WRITE2 = 1;
- parameter WAIT = 2;
- parameter READ1 = 3;
- parameter READ2 = 4;
- reg [31: 0] t_data;
- reg [31: 0] fdma_waddr_r;
- reg [2 :0] T_S = 0;
- assign fdma_waddr = fdma_waddr_r + ADDR_MEM_OFFSET;
- assign fdma_raddr = fdma_waddr;
- assign fdma_wsize = FDMA_BURST_LEN;
- assign fdma_rsize = FDMA_BURST_LEN;
- assign fdma_wdata ={t_data,t_data,t_data,t_data};
-
- //delay reset
- reg [8:0] rst_cnt = 0;
- always @(posedge ui_clk)
- if(rst_cnt[8] == 1'b0)
- rst_cnt <= rst_cnt + 1'b1;
- else
- rst_cnt <= rst_cnt;
- always @(posedge ui_clk)begin
- if(rst_cnt[8] == 1'b0)begin
- T_S <=0;
- fdma_wareq <= 1'b0;
- fdma_rareq <= 1'b0;
- t_data<=0;
- fdma_waddr_r <=0;
- end
- else begin
- case(T_S)
- WRITE1:begin
- if(fdma_waddr_r==TEST_MEM_SIZE) fdma_waddr_r<=0;
- if(!fdma_wbusy)begin
- fdma_wareq <= 1'b1;
- t_data <= 0;
- end
- if(fdma_wareq&&fdma_wbusy)begin
- fdma_wareq <= 1'b0;
- T_S <= WRITE2;
- end
- end
- WRITE2:begin
- if(!fdma_wbusy) begin
- T_S <= WAIT;
- t_data <= 32'd0;
- end
- else if(fdma_wvalid) begin
- t_data <= t_data + 1'b1;
- end
- end
- WAIT:begin//not needed
- T_S <= READ1;
- end
- READ1:begin
- if(!fdma_rbusy)begin
- fdma_rareq <= 1'b1;
- t_data <= 0;
- end
- if(fdma_rareq&&fdma_rbusy)begin
- fdma_rareq <= 1'b0;
- T_S <= READ2;
- end
- end
- READ2:begin
- if(!fdma_rbusy) begin
- T_S <= WRITE1;
- t_data <= 32'd0;
- fdma_waddr_r <= fdma_waddr_r + ADDR_INC;//128/8=16
- end
- else if(fdma_rvalid) begin
- t_data <= t_data + 1'b1;
- end
- end
- default:
- T_S <= WRITE1;
- endcase
- end
- end
-
- assign test_error = (fdma_rvalid && (t_data[15:0] != fdma_rdata[15:0]));
- always @(posedge ui_clk)
- test_error1 <= test_error;
- ila_0 ila_dbg (
- .clk(ui_clk),
- .probe0({fdma_wdata[15:0],fdma_wareq,fdma_wvalid,fdma_wbusy}),
- .probe1({fdma_rdata[15:0],t_data[15:0],fdma_rvalid,fdma_rbusy,T_S,test_error1})
- );
- IBUFDS #(
- .DIFF_TERM("FALSE"), // Differential Termination
- .IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
- .IOSTANDARD("DEFAULT") // Specify the input I/O standard
- ) IBUFDS_inst (
- .O(sysclk), // Buffer output
- .I(sysclk_p), // Diff_p buffer input (connect directly to top-level port)
- .IB(sysclk_n) // Diff_n buffer input (connect directly to top-level port)
- );
- system system_i
- (.FDMA_S_0_i_fdma_raddr(fdma_raddr),
- .FDMA_S_0_i_fdma_rareq(fdma_rareq),
- .FDMA_S_0_o_fdma_rbusy(fdma_rbusy),
- .FDMA_S_0_o_fdma_rdata(fdma_rdata),
- .FDMA_S_0_i_fdma_rready(1'b1),
- .FDMA_S_0_i_fdma_rsize(fdma_rsize),
- .FDMA_S_0_o_fdma_rvalid(fdma_rvalid),
- .FDMA_S_0_i_fdma_waddr(fdma_waddr),
- .FDMA_S_0_i_fdma_wareq(fdma_wareq),
- .FDMA_S_0_o_fdma_wbusy(fdma_wbusy),
- .FDMA_S_0_i_fdma_wdata(fdma_wdata),
- .FDMA_S_0_i_fdma_wready(1'b1),
- .FDMA_S_0_i_fdma_wsize(fdma_wsize),
- .FDMA_S_0_o_fdma_wvalid(fdma_wvalid),
- .sysclk(sysclk),
- .ui_clk(ui_clk)
- );
- endmodule
复制代码
5程序分析
1:总流程图如下图所示,本文的程序工作流程如下,包括请求写数据、FDMA收到写数据请求、写数据完成、请求读数据、FDMA收到读数据请求和读数据完成、校验。
2:FDMA的读写时序以下是fdma_test.v中读写fdma ip接口的状态机,由于读写代码对称,对fdma的读写操作可以分为2步完成,分别是:1:发送读/写请求 2:读/写有效数据。
2-1:FDMA的写时序
fdma_wready设置为1,当fdma_wbusy=0的时候代表FDMA的总线非忙,可以进行一次新的FDMA传输,这个时候可以设置fdma_wreq=1,同时设置fdma burst的起始地址和fdma_wsize本次需要传输的数据大小(以bytes为单位)。当fdma_wvalid=1的时候需要给出有效的数据,写入AXI总线。当最后一个数写完后,fdma_wvalid和fdma_wbusy变为0。 AXI4总线最大的burst lenth是256,而经过封装后,用户接口的fdma_size可以任意大小的,fdma ip内部代码控制每次AXI4总线的Burst长度,这样极大简化了AXI4总线协议的使用。 2-2:FDMA的读时序
fdma_rready设置为1,当fdma_rbusy=0的时候代表FDMA的总线非忙,可以进行一次新的FDMA传输,这个时候可以设置fdma_rreq=1,同时设置fdma burst的起始地址和fdma_rsize本次需要传输的数据大小(以bytes为单位)。当fdma_rvalid=1的时候需要给出有效的数据,写入AXI总线。当最后一个数写完后,fdma_rvalid和fdma_rbusy变为0。 同样对于AXI4总线的读操作,AXI4总线最大的burst lenth是256,而经过封装后,用户接口的fdma_size可以任意大小的,fdma ip内部代码控制每次AXI4总线的Burst长度,这样极大简化了AXI4总线协议的使用。 6RTL仿真
1:仿真tb文件编写仿真tb文件,fdma_bram_test_tb.v,非常简单,只需要给一个时钟 - `timescale 1ns / 1ps
- module fdma_bram_test_tb();
- reg sysclk_p;
- fdma_bram_test fdma_bram_test_inst
- (
- .sysclk_p(sysclk_p),
- .sysclk_n(~sysclk_p)
- );
-
- initial begin
- sysclk_p = 0;
- #100;
- end
- always #5 sysclk_p = ~sysclk_p;
- endmodule
复制代码
添加fdma_bram_test_tb.v到工程中
2:仿真测试进行RTL行为仿真
放大后观察数据,我们可以看到test_error有毛刺,但是test_error打拍后,毛刺就没有了。
FDMA 源码部分的分析,在前面课程中已经分析过,这里不再重复。 7编译测试添加ila IP CORE 和fpga pin定义
ila的设置如下
编译产生bit
下载程序到开发板测试,通过在线逻辑分析仪观察
|