本帖最后由 FPGA课程 于 2024-10-14 18:21 编辑
软件版本: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 可以完成很多数据读写存储类的应用,本文将展示通过 FDMA 读写 AXI-BRAM 本文实验目的: 1:掌握基于 uiFDMA3.0 的 FPGA 工程设计 2:利用 uiFDMA3.0 提供的接口,编写 BRAM 测试程序 3:对 AXI-BRAM 读写仿真和测试 2 搭建 FPGA 图形化工程
2.1 创建 Block Design 并且命名为 system
如下图所示,图形化 system 就是一个代码容器,接着我们要添加一些图像化的 IP
2.2 添加图形化 FPGA IP首先设置自定义 IP 的路径,这里读者可以把我们配套工程根路径下的 uisrc 文件夹复制到目前的工程根路径,单击 Settings 在弹出的 Settings 窗口选择 IP->Repository 设置如下路径
添加+号添加 IP
比如输入关键词 FDMA 就可以搜索到我们米联客 uiFDMA IP(注意最新版本是 3.0 版本)
用类似的方法添加必要的 IP 如下图所示:
2.3 完成 IP 之间的信号自动这种简单的工程可以先让软件先自动化线,然后根据结果手动一些调整
可以看到连线结果
2.4 调整 IP 参数
1:BRAM 参数设置首先把 IP 的配置参数修改下,双击需要设置的IP 可以进行参数设置 FDMA 设置数据位宽 128bit 可以访问内存地址位宽 32bit 其他默认
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 大 小,增加数据传输效率
2.5 引出 FPGA 接口信号
分别右击下图 2 个 IP,然后选择 Make External ,把需要引出到顶层的 FPGA 信号引出
为了引出时钟需先右击信号 PIN 脚断开连接,然后 Make External ,之后重新连接
修改后重新连接时钟
2.6 修改信号名字修改默认的时钟名字
2.7 连线最后的遗漏软件还在提示我们有线没有连接,这种提示是建立在 XILINX 自带的 IP,软件可以识别一部分比如复位时钟等, 不是可以识别所有的信号,一般再最后我们如果看到还有这种提示说明还有未完成的连线
这里是 ext_reset_in 没有连接,也是接到 Clocking Wizard 的 Locked
2.8 视图优化右击空白处,弹出菜单选择 Regenerate Layout 优化下视图
2.9 地址分配AXI 总线必须分配地址,设置 uiFDMA 的地址空间分配,起始地址可以任意设置,我们设置从 0x0000_0000 开始, 大小 64KB
2.10 自动校验保存工程
单击下图中的控件可以对图形化编程进行校验
2.11 自动产生顶层文件右击 system,在弹出的菜单中选择 Create HDL Wrapper
3 编写 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 [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;
- wire sysclk;
- //对差分时钟采用 IBUFGDS IP 核去转换
- IBUFGDS CLK_U(
- .I(sysclk_p),
- .IB(sysclk_n),
- .O(sysclk)
- );
- 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),
- .ui_clk(ui_clk)
- );
- endmodule
复制代码
4RTL 仿真
4.1 仿真 tb 文件编写仿真 tb 文件,fdma_bram_test_tb.v ,非常简单,只需要给一个时钟 - `timescale 1ns / 1ps
- module fdma_bram_test_tb();
- reg sysclk_p;
- reg sysclk_n;
- fdma_bram_test fdma_bram_test_inst
- (
- .sysclk_p(sysclk_p),
- .sysclk_n(sysclk_n),
- );
- initial begin
- sysclk_p <= 1'b0; //系统时钟设置初值
- sysclk_n <= 1'b1;
- #100;
- end
- always #5 sysclk_p = ~sysclk_p;
- always #5 sysclk_n = ~sysclk_n;
- endmodule
复制代码添加 fdma_bram_test_tb.v 到工程中
4.2 仿真测试
进行 RTL 行为仿真
放大后观察数据
我们也可以继续深入看 FDMA 源码里面的信号工作情况,这个读者可以自己区分析下,我们米联客计划出一 份专门讲解 AXI 总线部分的教程,里面详细分析 FDMA 的设计过程,以及信号仿真。本章节主要是侧重 FDMA 的 应用,只要会熟练应用可以了! 5 编译测试添加 ila IP CORE 和 fpga pin 定义
ila 的设置如下
编译产生 bit
下载程序到开发板测试,通过在线逻辑分析仪观察
6 程序分析
6.1 总流程图
如下图所示,本文的程序工作流程如下,包括请求写数据、FDMA收到写数据请求、写数据完成、请求读数据、FDMA收到读数据请求和读数据完成、校验。
6.2FDMA 的读写时序以下是 fdma_test.v 中读写 fdma ip 接口的状态机,由于读写代码对称,对 fdma 的读写操作可以分为 2 步完成,分别 是:1:发送读/写请求 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: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 总线协议的使用。
|