[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_PL-DDR篇连载-02AXI4-FULL-uiFDMA IP仿真验证

文档创建者:FPGA课程
浏览次数:331
最后更新:2024-09-09
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 2-FPGA PL DDR使用
​软件版本: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总线专题篇”相关课程内容。本文实验使用我们已经修改好的代码来完成验证。
56b6d3d7795d449eb2f9c46b498697c7.jpg

这个IP后面可以用于AXI4总线的仿真验证
3创建FPGA逻辑工程
78588ae576ee4a029339fac25b088ebb.jpg
设置IP路径
51dcc14110b84f5ebd2c49ac16fc713e.jpg

3ef7880d69d449d49a690b32130fb9d8.jpg

添加已经创建好的IP
9437622708fe48a5af2e12894b71de48.jpg
输入关键词fdma,在最后可以看到,双击添加Ip
99ab61dd9b0e440d91df90edc7fdb8ea.jpg
可以看到本文的FDMA版本升级到3.2版
2d06e18e8fbe4cc58f05a6e9a2d1a686.jpg
完成连线
继续添加剩余IP
3acee26ab2f04152a83881c0b82d8a3a.jpg
94a4e98168a244aeaa12ddde614936ec.jpg
fb72b7dadf1447ba82879f1be7fede53.jpg

设置IP参数
0cb7fb6ce06b44bf84144da5d2d08a70.jpg
b41dfd8237054237894e049687a7830c.jpg
dab0a404184d498aab24704317ee8d30.jpg
ad8710db845f40ea81620cbc4cbd1130.jpg
完成连线
83b74bf85e914882aec47c9511aede12.jpg

设置地址分配:
b6a2bab3483647e4a15f323dbd4f9d62.jpg
4添加FDMA接口控制代码
40f598b3ae5740e49b553ebbff88fe23.jpg

3975896e892249c3b4afc990541b9e71.jpg
7ec1e2bdd68646548d69d58831352542.jpg
添加完成后如下图:
986a26cd341c4dbd894ff12427d32be8.jpg
fdma_axi_slave_test.v源码如下
  1. `timescale 1ns / 1ps
  2. module fdma_axi_slave_test(
  3. input I_sysclk
  4. );

  5. wire [31:0]   fdma_raddr;
  6. reg           fdma_rareq;
  7. wire          fdma_rbusy;
  8. wire [31:0]   fdma_rdata;
  9. wire [15:0]   fdma_rsize;
  10. wire          fdma_rvalid;
  11. wire [31:0]   fdma_waddr;
  12. reg           fdma_wareq;
  13. wire          fdma_wbusy;
  14. wire [31:0]   fdma_wdata;
  15. wire [15:0]   fdma_wsize;
  16. wire          fdma_wvalid;
  17. wire          ui_clk;

  18. parameter TEST_MEM_SIZE   = 32'd20000; //测试内存的地址范围
  19. parameter FDMA_BURST_LEN  = 16'd500; //测试一次的长度
  20. parameter ADDR_MEM_OFFSET = 0; //地址偏移量
  21. parameter ADDR_INC = FDMA_BURST_LEN*4; //下一次FDMA burst的地址增加

  22. parameter WRITE1 = 0;
  23. parameter WRITE2 = 1;
  24. parameter WAIT   = 2;
  25. parameter READ1  = 3;
  26. parameter READ2  = 4;

  27. reg [31: 0] t_data;
  28. reg [31: 0] fdma_waddr_r;
  29. reg [2  :0] T_S = 0;

  30. assign fdma_waddr = fdma_waddr_r + ADDR_MEM_OFFSET; //设置偏移地址
  31. assign fdma_raddr = fdma_waddr; //读写地址相同

  32. assign fdma_wsize = FDMA_BURST_LEN; //设置FDMA控制器一次写burst的数据长度
  33. assign fdma_rsize = FDMA_BURST_LEN; //设置FDMA控制器一次读burst的数据长度
  34. assign fdma_wdata ={t_data,t_data,t_data,t_data};
  35.   
  36.   
  37. ////延迟复位
  38. reg [8:0] rst_cnt = 0;
  39. always @(posedge ui_clk)
  40.     if(rst_cnt[8] == 1'b0)
  41.          rst_cnt <= rst_cnt + 1'b1;
  42.      else
  43.          rst_cnt <= rst_cnt;

  44. //FDMA 读写控制器,每次先写后读,读出后对比数据正确性
  45. always @(posedge ui_clk)begin
  46.     if(rst_cnt[8] == 1'b0)begin
  47.         T_S <=0;   
  48.         fdma_wareq  <= 1'b0;
  49.         fdma_rareq  <= 1'b0;
  50.         t_data<=0;
  51.         fdma_waddr_r <=0;      
  52.     end
  53.     else begin
  54.         case(T_S)      
  55.         WRITE1:begin
  56.             if(fdma_waddr_r==TEST_MEM_SIZE) fdma_waddr_r<=0; //超出测试内存范围,重新测试
  57.                 if(!fdma_wbusy)begin//当fdma进入空闲,fdma_wbusy=0,请求写
  58.                     fdma_wareq  <= 1'b1; //设置写请求
  59.                     t_data  <= 0; //设置初值
  60.                 end
  61.                 if(fdma_wareq&&fdma_wbusy)begin//当fdma响应请求后,fdma_wbusy=1,进入下一个状态
  62.                     fdma_wareq  <= 1'b0;
  63.                     T_S         <= WRITE2;
  64.                 end
  65.         end
  66.         WRITE2:begin
  67.             if(!fdma_wbusy) begin//当fdma完成请求后,fdma_wbusy=0,进入下一个状态
  68.                  T_S <= WAIT;
  69.                  t_data  <= 32'd0;
  70.             end
  71.             else if(fdma_wvalid) begin//当fdma_wvalid有效期间必须写入有效数据
  72.                 t_data <= t_data + 1'b1;
  73.             end
  74.         end
  75.         WAIT:begin//not needed
  76.             T_S <= READ1;
  77.         end
  78.         READ1:begin
  79.             if(!fdma_rbusy)begin//当fdma进入空闲,fdma_rbusy=0,请求读
  80.                 fdma_rareq  <= 1'b1; //设置读请求
  81.                 t_data   <= 0; //设置初值
  82.             end
  83.             if(fdma_rareq&&fdma_rbusy)begin//当fdma响应请求后,fdma_rbusy=1,进入下一个状态
  84.                  fdma_rareq  <= 1'b0; //清除读请求
  85.                  T_S         <= READ2;
  86.             end
  87.         end
  88.         READ2:begin
  89.             if(!fdma_rbusy) begin//当fdma完成请求后,fdma_rbusy=0,进入下一个状态
  90.                  T_S <= WRITE1;
  91.                  t_data  <= 32'd0;
  92.                  fdma_waddr_r  <= fdma_waddr_r + ADDR_INC; //当本次读写周期完成增加地址,地址以BYTE计算
  93.             end
  94.             else if(fdma_rvalid) begin//当fdma_rvalid有效期间读出的数据有效
  95.                 t_data <= t_data + 1'b1;
  96.             end
  97.         end   
  98.         default:
  99.             T_S <= WRITE1;     
  100.         endcase
  101.     end
  102.   end
  103. //对比是否有错误数据
  104. wire test_error = (fdma_rvalid && (t_data[15:0] != fdma_rdata[15:0]));

  105. //ila_0 ila_dbg (
  106. //  .clk(ui_clk),
  107. //  .probe0({fdma_wdata[15:0],fdma_wareq,fdma_wvalid,fdma_wbusy}),
  108. //  .probe1({fdma_rdata[15:0],t_data[15:0],fdma_rvalid,fdma_rbusy,T_S,test_error})
  109. //);

  110.   system system_i
  111.        (.FDMA_S_0_i_fdma_raddr(fdma_raddr),
  112.         .FDMA_S_0_i_fdma_rareq(fdma_rareq),
  113.         .FDMA_S_0_o_fdma_rbusy(fdma_rbusy),
  114.         .FDMA_S_0_o_fdma_rdata(fdma_rdata),
  115.         .FDMA_S_0_i_fdma_rready(1'b1),
  116.         .FDMA_S_0_i_fdma_rsize(fdma_rsize),
  117.         .FDMA_S_0_o_fdma_rvalid(fdma_rvalid),
  118.         .FDMA_S_0_i_fdma_waddr(fdma_waddr),
  119.         .FDMA_S_0_i_fdma_wareq(fdma_wareq),
  120.         .FDMA_S_0_o_fdma_wbusy(fdma_wbusy),
  121.         .FDMA_S_0_i_fdma_wdata(fdma_wdata),
  122.         .FDMA_S_0_i_fdma_wready(1'b1),
  123.         .FDMA_S_0_i_fdma_wsize(fdma_wsize),
  124.         .FDMA_S_0_o_fdma_wvalid(fdma_wvalid),
  125.         .I_sysclk(I_sysclk),
  126.         .ui_clk(ui_clk)
  127.         );        

  128. endmodule
复制代码

以上代码中调用的system.bd的图形代码接口。在状态机中,每次写500个长度32bit的数据,再读出来判断数据是否正确,因此传输20000字节的数据需要传输10次,每次FDMA传输的地址递增2000。

5仿真文件
添加仿真文件
e07ade3a356a4d6ea79ca28b0212897f.jpg
988619edc4a340f9b750121e3a0c5dd0.jpg
添加完成后:
f9079df59cd847e689e767f5118feb74.jpg

仿真文件非常简单,只要提供时钟激励就可以。
  1. `timescale 1ns / 1ps
  2. module fdma_axi_slave_test_tb();

  3. reg I_sysclk;

  4. fdma_axi_slave_test fdma_axi_slave_test_inst
  5. (
  6.     .I_sysclk(I_sysclk)

  7. );

  8. initial begin
  9.      I_sysclk  = 0;
  10.      #100;
  11. end

  12.     always #5 I_sysclk = ~I_sysclk;   

  13. endmodule
复制代码

6实验结果
下图中红色框内分别代表了FDMA一次burst的写操作和一次burst的读操作。FDMA 控制器会根据用户代码设置的fdma_wsize和fdma_rsize以及设置的FDMA IP参数中,AXI4总线支持的最大burst 长度,来决定进行多少次的AXI4 burst。
c7438873f8fa4c22a6e7260c658b9e5e.jpg
本次写传输中,wburst_len_req自动发起计算AXI4总线需要发起的传输长度,本次传输中,FDMA的用户代码每次发起传输长度为500,因此FDMA部分会自动控制AXI部分的传输长度,第一次axi burst长度为256,第二次axi burst长度为244。
3d7a9be0e6be4dfebe7bebfd92df1a52.jpg
一次FDMA写传输的起始时序
d6773f0939754276a984b8430e0b414e.jpg
连续burst,自动管理burst长度,以及一次FDMA写传输结束时序
955d3da7e1c84b878dbe49a39feaef21.jpg
本次读传输中,rburst_len_req自动发起计算AXI4总线需要发起的传输长度,本次传输中,FDMA的用户代码每次发起传输长度为500,因此FDMA部分会自动控制AXI部分的传输长度,第一次axi burst长度为256,第二次axi burst长度为244。
f180af491270468a8476f28ade677713.jpg
一次FDMA读传输的起始时序
6e2b798e24a44acdaaabf415a2d7d534.jpg
连续burst,自动管理burst长度,以及一次FDMA读传输结束时序
d5b29b252b854d27b53ffabd0fe031ba.jpg
另外放大后可以看到rvalid不是连续的,这个读者也可以自己去优化saxi_ful_mem ip让这IP支持连续的rvalid


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则