01使用fdma读写axi-bram(AXI4 FDMA数据缓存篇)


软件版本:vitis2019.2(vivado2019.2)

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA(本文使用米联客(milianke))MK7160FA开发板

登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!

1.1概述    

FDMA是米联客的基于AXI4总线协议定制的一个DMA控制器。有了这个IP我们可以统一实现用FPGA代码直接读写PL的DDR或者ZYNQ/ZYNQMP SOC PS的DDR或者BRAM。FDMA IP CORE 已经广泛应用于ZYNQ SOC/Artix7/Kintex7 FPGA,同样适用于ultrascale/ultrascale+系列FPGA/SOC。

如果用过ZYNQ/ZYNQMP的都知道,要直接操作PS的DDR 通常是DMA 或者VDMA,然而用过XILINX 的DMA IP 和VDMA IP,总有一种遗憾,那就是不够灵活,还需要对寄存器配置,真是麻烦。XILINX 的总线接口是AXI4总线,自定义AXI4 IP挂到总线上就能实现对内存地址空间的读写访问。因此,我们只要掌握AXI4协议就能完成不管是PS还是PL DDR的读写操作。

米联客封装的AXI4总线协议命名位uiFDMA,自动2018年第一版本发布后,就引起了很多FPGA工程师的兴趣,并且得到了广大FPGA工程师的好评,但是FDMA1.0版本还是有一些局限和BUG,再实际的应用中被FPGA工程师发现,因此给了我们很多宝贵意见。借此2021版本教程更新发布之际,我们也对FDMA1.0版本升级到FDMA3.0版本。

uiFDMA3.0新增特性:

1:支持多个FDMA IP同时挂带AXI-interconnect总线,同时工作

2:支持自动计算AXI-Burst长度,使用起来非常简单,只需要给出需要burst的长度。

从本文开始,我们从多个应用方案来演示FDMA的用途。

 

本文实验目的:

1:掌握基于uiFDMA3.0的FPGA工程设计

2:利用uiFDMA3.0提供的接口,编写BRAM测试程序

3:对AXI-BRAM读写仿真和测试

 

我们第一入门的demo选择对BRAM仿真测试,是因为不管是仿真还是编译测试,对AXI-BRAM速度都非常快,我们在下面一个DEMO中会给出对DDR的读写仿真测试。

1.2硬件接线

1.3搭建FPGA图形化工程

1.3.1创建工程命名为fpga_prj

双击VIVADO软件图标启动VIVADO

设置工程路径,并且命名工程名为fpga_prj

以下设置FPGA或者ZYNQ或者ZYNQ-MPSOC芯片型号,必须和开发板保持一致,如果不清楚的请查阅自己开发板的硬件手册或者根据选型手册上参数确认

 

1.3.2创建Block Design并且命名为system

如下图所示,图形化system就是一个代码容器,接着我们要添加一些图像化的IP

 

1.3.3添加图形化FPGA IP

首先设置自定义IP的路径,这里读者可以把我们配套工程根路径下的uisrc文件夹复制到目前的工程根路径,单击Settings在弹出的Settings窗口选择IP->Repository 设置如下路径

添加+号添加IP

比如输入关键词FDMA就可以搜索到我们米联客uiFDMA IP

用类似的方法添加必要的IP如下图所示:

 

1.3.4完成IP之间的信号自动

这种简单的工程可以先让软件先自动化线,然后根据结果手动一些调整

可以看到连线结果

1.3.5调整IP参数

1:BRAM参数设置

首先把IP的配置参数修改下,双击需要设置的IP可以进行参数设置

FDMA设置数据位宽128bit 可以访问内存地址位宽16bit(BRAM大小有限这里设置64KB)

BRAM设置,使用BRAM Controller 为真双口RAM

2:BRAM Controller参数设置

AXI BRAM Controller设置axi4协议,数据位宽128bit 读延迟1个时钟

3:Clocking Wizard参数设置

4:AXI Interconnect IP设置

双击AXI Interconnect IP 进行设置

设置AXI Interconnect IP的性能参数,其中Enable Register Slice 用于改善时序,Enable Data FIFO用于增加FIFO大小,增加数据传输效率

1.3.6引出FPGA接口信号

分别右击下图uiFDMA IP,然后选择Make External,把需要引出到顶层的FPGA信号引出

 

为了引出时钟需先右击信号PIN脚断开连接,然后Make External,之后重新连接

 

右击pin脚,弹出菜单选择Make External

修改后重新连接时钟

1.3.7修改信号名字

默认的时钟名字不是很好,可以自己修改下

修改完成如下

1.3.8最后的优化

我们知道之类外部复位可以优化掉不使用,接到clk_wiz_0时钟的locked脚

1.3.9视图优化

右击空白处,弹出菜单选择Regenerate Layout优化下视图

1.3.10地址分配

 

AXI总线必须分配地址,设置uiFDMA的地址空间分配,起始地址可以任意设置,我们设置从0x0000_0000开始,大小64KB

1.3.11自动校验

保存工程

单击下图中的控件可以对图形化编程进行校验

1.3.12自动产生顶层文件

右击system,在弹出的菜单中选择Create HDL Wrapper

1.4编写FDMA的BRAM测试代码

刚刚以上自动产生的顶层文件,只是引出的信号接口,并不能完成对FDMA的控制,所以我们需要自定义一个顶层文件,可以复制刚才产生的文件,修改,这样可以省一些编写调用接口的时间。

为了方便文件的管理,我们新建一个fdma_bram_test.v文件,并且复制以上代码,到这个文件夹。

右击删除刚刚自动产生的文件

添加fdma_bram_test.v文件

fdma_bram_test.v文件

 

`timescale 1ns / 1ps

//////////////////////////////////////////////////////////////////////////////////

/*

Company : Liyang Milian Electronic Technology Co., Ltd.

Brand: (milianke)

Technical forum:uisrc.com

taobao: https://milianke.taobao.com https://osrc.taobao.com

jd:https://milianke.jd.com

Create Date: 2021/04/25

Module Name: fdma_bram_test

Description:

Copyright: Copyright (c) milianke

Revision: 1.0

Signal description:

1) _i input

2) _o output

3) _n activ lowpai

4) _dg debug signal

5) _r delay or register

6) _s state mechine

*/

//////////////////////////////////////////////////////////////////////////////////

 

module fdma_bram_test(

input sysclk_p

);

 

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 [0:0] fdma_rstn;

wire ui_clk;

 

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

 

wire test_error = (fdma_rvalid && (t_data[15:0] != fdma_rdata[15:0]));

 

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_error})

);

 

system system_i

(.FDMA_S_0_fdma_raddr(fdma_raddr),

.FDMA_S_0_fdma_rareq(fdma_rareq),

.FDMA_S_0_fdma_rbusy(fdma_rbusy),

.FDMA_S_0_fdma_rdata(fdma_rdata),

.FDMA_S_0_fdma_rready(1'b1),

.FDMA_S_0_fdma_rsize(fdma_rsize),

.FDMA_S_0_fdma_rvalid(fdma_rvalid),

.FDMA_S_0_fdma_waddr(fdma_waddr),

.FDMA_S_0_fdma_wareq(fdma_wareq),

.FDMA_S_0_fdma_wbusy(fdma_wbusy),

.FDMA_S_0_fdma_wdata(fdma_wdata),

.FDMA_S_0_fdma_wready(1'b1),

.FDMA_S_0_fdma_wsize(fdma_wsize),

.FDMA_S_0_fdma_wvalid(fdma_wvalid),

.sysclk(sysclk_p),

.ui_clk(ui_clk)

);

 

endmodule

1.5RTL仿真

1.5.1仿真tb文件

编写仿真tb文件,fdma_bram_test_tb.v,非常简单,只需要给一个时钟

`timescale 1ns / 1ps

//////////////////////////////////////////////////////////////////////////////////

/*

Company : Liyang Milian Electronic Technology Co., Ltd.

Brand: 米联客(milianke)

Technical forumuisrc.com

taobao: osrc.taobao.com

Create Date: 2021/04/25

Module Name: fdma_bram_test_tb

Description:

Copyright: Copyright (c) msxbo

Revision: 1.0

Signal description

1) _i input

2) _o output

3) _n activ low

4) _dg debug signal

5) _r delay or register

6) _s state mechine

*/

////////////////////////////////////////////////////////////////////////////////

module fdma_bram_test_tb();

 

 

reg sysclk_p;

 

fdma_bram_test fdma_bram_test_inst

(

.sysclk_p(sysclk_p)

);

 

initial begin

sysclk_p = 0;

#100;

end

 

always #5 sysclk_p = ~sysclk_p;

 

endmodule

添加fdma_bram_test_tb.v到工程中

1.5.2仿真测试

进行RTL行为仿真

放大后观察数据

我们也可以继续深入看FDMA源码里面的信号工作情况,这个读者可以自己区分析下,我们米联客计划出一份专门讲解AXI总线部分的教程,里面详细分析FDMA的设计过程,以及信号仿真。本章节主要是侧重FDMA的应用,只要会熟练应用可以了!

1.6编译测试

添加ila IP CORE 和fpga pin定义

ila的设置如下

 

编译产生bit

下载程序到开发板测试,通过在线逻辑分析仪观察

 

1.7程序分析

1.7.1FDMA的写时序

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。

1.7.2FDMA的读时序

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。

1.7.3读写状态机

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM