软件版本: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 IP进行仿真验证。 2saxi_full_mem IP介绍这个IP的源码可以基于XILINX提供的axi-full-slave的模板简单修改就可以实现,如果读者想要更加详细的学习AXI总线想内容,可以阅读或者观看米联客“米联客2024版AXI4总线专题篇”相关课程内容。本文实验使用我们已经修改好的代码来完成验证。
这个IP后面可以用于AXI4总线的仿真验证 3创建FPGA逻辑工程
设置IP路径
添加已经创建好的IP
输入关键词fdma,在最后可以看到,双击添加Ip
可以看到本文的FDMA版本升级到3.2版
完成连线 继续添加剩余IP
设置IP参数
完成连线
设置地址分配:
4添加FDMA接口控制代码
添加完成后如下图:
fdma_axi_slave_test.v源码如下 - `timescale 1ns / 1ps
- module fdma_axi_slave_test(
- input I_sysclk
- );
- wire [31:0] fdma_raddr;
- reg fdma_rareq;
- wire fdma_rbusy;
- wire [31:0] fdma_rdata;
- wire [15:0] fdma_rsize;
- wire fdma_rvalid;
- wire [31:0] fdma_waddr;
- reg fdma_wareq;
- wire fdma_wbusy;
- wire [31:0] fdma_wdata;
- wire [15:0] fdma_wsize;
- wire fdma_wvalid;
- wire ui_clk;
- parameter TEST_MEM_SIZE = 32'd20000; //测试内存的地址范围
- parameter FDMA_BURST_LEN = 16'd500; //测试一次的长度
- parameter ADDR_MEM_OFFSET = 0; //地址偏移量
- parameter ADDR_INC = FDMA_BURST_LEN*4; //下一次FDMA burst的地址增加
-
- 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; //设置FDMA控制器一次写burst的数据长度
- assign fdma_rsize = FDMA_BURST_LEN; //设置FDMA控制器一次读burst的数据长度
- assign fdma_wdata ={t_data,t_data,t_data,t_data};
-
-
- ////延迟复位
- 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;
- //FDMA 读写控制器,每次先写后读,读出后对比数据正确性
- 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进入空闲,fdma_wbusy=0,请求写
- fdma_wareq <= 1'b1; //设置写请求
- t_data <= 0; //设置初值
- end
- if(fdma_wareq&&fdma_wbusy)begin//当fdma响应请求后,fdma_wbusy=1,进入下一个状态
- fdma_wareq <= 1'b0;
- T_S <= WRITE2;
- end
- end
- WRITE2:begin
- if(!fdma_wbusy) begin//当fdma完成请求后,fdma_wbusy=0,进入下一个状态
- T_S <= WAIT;
- t_data <= 32'd0;
- end
- else if(fdma_wvalid) begin//当fdma_wvalid有效期间必须写入有效数据
- t_data <= t_data + 1'b1;
- end
- end
- WAIT:begin//not needed
- T_S <= READ1;
- end
- READ1:begin
- if(!fdma_rbusy)begin//当fdma进入空闲,fdma_rbusy=0,请求读
- fdma_rareq <= 1'b1; //设置读请求
- t_data <= 0; //设置初值
- end
- if(fdma_rareq&&fdma_rbusy)begin//当fdma响应请求后,fdma_rbusy=1,进入下一个状态
- fdma_rareq <= 1'b0; //清除读请求
- T_S <= READ2;
- end
- end
- READ2:begin
- if(!fdma_rbusy) begin//当fdma完成请求后,fdma_rbusy=0,进入下一个状态
- T_S <= WRITE1;
- t_data <= 32'd0;
- fdma_waddr_r <= fdma_waddr_r + ADDR_INC; //当本次读写周期完成增加地址,地址以BYTE计算
- end
- else if(fdma_rvalid) begin//当fdma_rvalid有效期间读出的数据有效
- 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_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),
- .I_sysclk(I_sysclk),
- .ui_clk(ui_clk)
- );
- endmodule
复制代码
以上代码中调用的system.bd的图形代码接口。在状态机中,每次写500个长度32bit的数据,再读出来判断数据是否正确,因此传输20000字节的数据需要传输10次,每次FDMA传输的地址递增2000。
5仿真文件添加仿真文件
添加完成后:
仿真文件非常简单,只要提供时钟激励就可以。 - `timescale 1ns / 1ps
- module fdma_axi_slave_test_tb();
- reg I_sysclk;
- fdma_axi_slave_test fdma_axi_slave_test_inst
- (
- .I_sysclk(I_sysclk)
- );
-
- initial begin
- I_sysclk = 0;
- #100;
- end
- always #5 I_sysclk = ~I_sysclk;
- endmodule
复制代码
6实验结果下图中红色框内分别代表了FDMA一次burst的写操作和一次burst的读操作。FDMA 控制器会根据用户代码设置的fdma_wsize和fdma_rsize以及设置的FDMA IP参数中,AXI4总线支持的最大burst 长度,来决定进行多少次的AXI4 burst。
本次写传输中,wburst_len_req自动发起计算AXI4总线需要发起的传输长度,本次传输中,FDMA的用户代码每次发起传输长度为500,因此FDMA部分会自动控制AXI部分的传输长度,第一次axi burst长度为256,第二次axi burst长度为244。
一次FDMA写传输的起始时序
连续burst,自动管理burst长度,以及一次FDMA写传输结束时序
本次读传输中,rburst_len_req自动发起计算AXI4总线需要发起的传输长度,本次传输中,FDMA的用户代码每次发起传输长度为500,因此FDMA部分会自动控制AXI部分的传输长度,第一次axi burst长度为256,第二次axi burst长度为244。
一次FDMA读传输的起始时序
连续burst,自动管理burst长度,以及一次FDMA读传输结束时序
另外放大后可以看到rvalid不是连续的,这个读者也可以自己去优化saxi_ful_mem ip让这IP支持连续的rvalid
|