[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_PL-DDR篇连载-04使用fdma读写DDR

文档创建者:FPGA课程
浏览次数:337
最后更新:2024-09-10
文档课程分类-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的使用方法, 前面一节课演示了FDMA读写AXI-BRAM,本文我们继续使用FDMA实现对AXI-MIG的读写,以此读写DDR。由于FDMA的读写操作都是基于AXI总线,所以用户代码部分一致性也非常好,我们的状态机都不需要做修改,基本上只要把前文的BRAM IP换成MIG IP即可。
本文实验目的:
1:利用uiFDMA3.2提供的接口,编写DDR测试程序
2:对MIG接口读写仿真和测试
本系统中先将测试数据通过AXI-DMA写入DDR,再通过AXI-DMA将DDR3中数据读出。将读写数据进行对比。通过在线逻辑分析仪抓取读写数据测试读写正确性。
dbcf5aec0d294ce982eb3e6d14a62eda.jpg
2基于图形化逻辑设计
搭建过程我们不再详细描述,不清楚的可以参考前“使用FDMA读写AXI-BRAM”,搭建好的工程如下
dee26d15adb34217b2536244ff850301.jpg

我们主要看下MIG的配置
1:MIG配置
3a94ebd07cab4ccab780839e6999c844.jpg
bfabcbaa848849ce91b4f598e486da40.jpg
d8c2a2366c604c6d88c00737411f0f24.jpg
1c76d7690b8e4247bc9d1ae15f3801f0.jpg

bbd7ddc9a01941b486cf409e94c4c486.jpg
fe11d7a108974845827d94fe89744942.jpg
64b6f68b9a204efdb4741870a53b39e3.jpg

df59a71a734f4a19bda7bbdecb3f1d9d.jpg
6a4eb58b144347ad8bd5ef1eefdfced5.jpg
f6d1609d53244af8b6533bc03a667d75.jpg
fbe7d3111c344a9489ee77a463a0d2ec.jpg
4557d805d8f74681a3f383ca2081daad.jpg


继续NEXT,知道下页
ecd41a5e16fa4e5b8cdf6f02446aacfc.jpg
2:编写FDMA的DDR测试代码
以下程序中是fdma读写操作的具体实现,先写入一定数据到DDR中,然后再读出,对比是否有错误,几个关键参数:
TEST_MEM_SIZE:定义了测试内从空间的大小,以byte为单位,是整数倍的FDMA_BURST_LEN *(fdma_wdata/8)。
FDMA_BURST_LEN:定义每次FDMA传输的长度,这个长度是整数倍的fdma_wdata或者fdma_rdata。
ADDR_MEM_OFFSET:代码了内从访问的起始地址。

  1. `timescale 1ns / 1ps

  2. module fdma_ddr_test(
  3. output [14:0]DDR3_0_addr,
  4. output [2 :0]DDR3_0_ba,
  5. output DDR3_0_cas_n,
  6. output [0 :0]DDR3_0_ck_n,
  7. output [0 :0]DDR3_0_ck_p,
  8. output [0 :0]DDR3_0_cke,
  9. output [0 :0]DDR3_0_cs_n,
  10. output [7 :0]DDR3_0_dm,
  11. inout  [63:0]DDR3_0_dq,
  12. inout  [7 :0]DDR3_0_dqs_n,
  13. inout  [7 :0]DDR3_0_dqs_p,
  14. output [0 :0]DDR3_0_odt,
  15. output DDR3_0_ras_n,
  16. output DDR3_0_reset_n,
  17. output DDR3_0_we_n,
  18. input sysclk_p,
  19. input sysclk_n
  20. );

  21. wire [31:0]   fdma_raddr;
  22. reg           fdma_rareq;
  23. wire          fdma_rbusy;
  24. wire [127:0]  fdma_rdata;
  25. wire [15:0]   fdma_rsize;
  26. wire          fdma_rvalid;
  27. wire [31:0]   fdma_waddr;
  28. reg           fdma_wareq;
  29. wire          fdma_wbusy;
  30. wire [127:0]  fdma_wdata;
  31. wire [15:0]   fdma_wsize;
  32. wire          fdma_wvalid;
  33. wire [0:0]    fdma_rstn;
  34. wire          ui_clk;
  35. wire          sysclk;

  36. parameter TEST_MEM_SIZE   = 32'd1024*1024*1024*2;
  37. parameter FDMA_BURST_LEN  = 16'd512;
  38. parameter ADDR_MEM_OFFSET = 0;
  39. parameter ADDR_INC = FDMA_BURST_LEN * 16;

  40. parameter WRITE1 = 0;
  41. parameter WRITE2 = 1;
  42. parameter WAIT   = 2;
  43. parameter READ1  = 3;
  44. parameter READ2  = 4;

  45. reg [31: 0] t_data;
  46. reg [31: 0] fdma_waddr_r;
  47. reg [2  :0] T_S = 0;

  48. assign fdma_waddr = fdma_waddr_r + ADDR_MEM_OFFSET;
  49. assign fdma_raddr = fdma_waddr;

  50. assign fdma_wsize = FDMA_BURST_LEN;
  51. assign fdma_rsize = FDMA_BURST_LEN;
  52. assign fdma_wdata ={t_data,t_data,t_data,t_data};
  53.   
  54. reg [8:0] rst_cnt = 0;

  55. always @(posedge ui_clk)
  56.     if(~fdma_rstn)begin
  57.         rst_cnt <=0;
  58.     end
  59.     else begin
  60.         if(rst_cnt[8] == 1'b0)
  61.             rst_cnt <= rst_cnt + 1'b1;
  62.         else
  63.             rst_cnt <= rst_cnt;
  64.     end

  65. always @(posedge ui_clk)begin
  66.     if(rst_cnt[8] == 1'b0)begin
  67.         T_S <=0;   
  68.         fdma_wareq  <= 1'b0;
  69.         fdma_rareq  <= 1'b0;
  70.         t_data<=0;
  71.         fdma_waddr_r <=0;      
  72.     end
  73.     else begin
  74.         case(T_S)      
  75.         WRITE1:begin
  76.             if(fdma_waddr_r==TEST_MEM_SIZE) fdma_waddr_r<=0;
  77.                
  78.             if(!fdma_wbusy)begin
  79.                 fdma_wareq  <= 1'b1;
  80.                 t_data  <= 0;
  81.             end
  82.             if(fdma_wareq&&fdma_wbusy)begin
  83.                 fdma_wareq  <= 1'b0;
  84.                 T_S         <= WRITE2;
  85.             end
  86.         end
  87.         WRITE2:begin
  88.             if(!fdma_wbusy) begin
  89.                  T_S <= WAIT;
  90.                  t_data  <= 32'd0;
  91.             end
  92.             else if(fdma_wvalid) begin
  93.                 t_data <= t_data + 1'b1;
  94.             end
  95.         end
  96.         WAIT:begin//not needed
  97.             T_S <= READ1;
  98.         end
  99.         READ1:begin
  100.             if(!fdma_rbusy)begin
  101.                 fdma_rareq  <= 1'b1;
  102.                 t_data   <= 0;
  103.             end
  104.             if(fdma_rareq&&fdma_rbusy)begin
  105.                  fdma_rareq  <= 1'b0;
  106.                  T_S         <= READ2;
  107.             end
  108.         end
  109.         READ2:begin
  110.             if(!fdma_rbusy) begin
  111.                  T_S <= WRITE1;
  112.                  t_data  <= 32'd0;
  113.                  fdma_waddr_r  <= fdma_waddr_r + ADDR_INC;//128/8=16
  114.             end
  115.             else if(fdma_rvalid) begin
  116.                 t_data <= t_data + 1'b1;
  117.             end
  118.         end   
  119.         default:
  120.             T_S <= WRITE1;     
  121.         endcase
  122.     end
  123.   end

  124. reg test_error;

  125. always @(posedge ui_clk)begin
  126.     test_error <= (fdma_rvalid && (t_data[31:0] != fdma_rdata[31:0]));
  127. end  

  128. ila_0 ila_dbg (
  129.     .clk(ui_clk),
  130.     .probe0({fdma_wdata[15:0],fdma_wareq,fdma_wvalid,fdma_wbusy}),
  131.     .probe1({fdma_rdata[15:0],t_data[15:0],fdma_rvalid,fdma_rbusy,T_S,test_error})
  132. );

  133.    IBUFDS #(
  134.       .DIFF_TERM("FALSE"),       // Differential Termination
  135.       .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE"
  136.       .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
  137.    ) IBUFDS_inst (
  138.       .O(sysclk),  // Buffer output
  139.       .I(sysclk_p),  // Diff_p buffer input (connect directly to top-level port)
  140.       .IB(sysclk_n) // Diff_n buffer input (connect directly to top-level port)
  141.    );

  142.   system system_i
  143.        (.DDR3_0_addr(DDR3_0_addr),
  144.         .DDR3_0_ba(DDR3_0_ba),
  145.         .DDR3_0_cas_n(DDR3_0_cas_n),
  146.         .DDR3_0_ck_n(DDR3_0_ck_n),
  147.         .DDR3_0_ck_p(DDR3_0_ck_p),
  148.         .DDR3_0_cke(DDR3_0_cke),
  149.         .DDR3_0_cs_n(DDR3_0_cs_n),
  150.         .DDR3_0_dm(DDR3_0_dm),
  151.         .DDR3_0_dq(DDR3_0_dq),
  152.         .DDR3_0_dqs_n(DDR3_0_dqs_n),
  153.         .DDR3_0_dqs_p(DDR3_0_dqs_p),
  154.         .DDR3_0_odt(DDR3_0_odt),
  155.         .DDR3_0_ras_n(DDR3_0_ras_n),
  156.         .DDR3_0_reset_n(DDR3_0_reset_n),
  157.         .DDR3_0_we_n(DDR3_0_we_n),
  158.         .FDMA_S_0_i_fdma_raddr(fdma_raddr),
  159.         .FDMA_S_0_i_fdma_rareq(fdma_rareq),
  160.         .FDMA_S_0_o_fdma_rbusy(fdma_rbusy),
  161.         .FDMA_S_0_o_fdma_rdata(fdma_rdata),
  162.         .FDMA_S_0_i_fdma_rready(1'b1),
  163.         .FDMA_S_0_i_fdma_rsize(fdma_rsize),
  164.         .FDMA_S_0_o_fdma_rvalid(fdma_rvalid),
  165.         .FDMA_S_0_i_fdma_waddr(fdma_waddr),
  166.         .FDMA_S_0_i_fdma_wareq(fdma_wareq),
  167.         .FDMA_S_0_o_fdma_wbusy(fdma_wbusy),
  168.         .FDMA_S_0_i_fdma_wdata(fdma_wdata),
  169.         .FDMA_S_0_i_fdma_wready(1'b1),
  170.         .FDMA_S_0_i_fdma_wsize(fdma_wsize),
  171.         .FDMA_S_0_o_fdma_wvalid(fdma_wvalid),
  172.         .I_sysclk(I_sysclk),
  173.         .fdma_rstn(fdma_rstn),
  174.         .ui_clk(ui_clk));

  175. endmodule
复制代码

3:程序分析
3-1:总流程图
如下图所示,本文的程序工作流程如下,包括请求写数据、FDMA收到写数据请求、写数据完成、请求读数据、FDMA收到读数据请求和读数据完成、校验。
909cfc53810e4bd09eb2921c8ddd1eb1.jpg
以下是fdma_test.v中读写fdma ip接口的状态机,由于读写代码对称,对fdma的读写操作可以分为2步完成,分别是:1:发送读/写请求 2:读/写有效数据。
4b6fa19d3e894c6195ce74fa29fc7b48.jpg
3-2:FDMA的写时序
f685f9b3d5a5433895fc0233d6ddd8e1.jpg
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总线协议的使用。
3-3:FDMA的读时序
2ec28d6787a14545ba34990a3b45fdc9.jpg
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总线协议的使用。
4:地址空间分配
配置完成后需要注意地址空间分配,FDMA IP的内存起始地址从0开始
a03e04c0888944bdad1b0e1eea3069b0.jpg
3RTL仿真
1:仿真tb文件
DDR3的仿真文件在对应的FPGA工程路径uisrc/02_sim路径下,把仿真文件添加进来
fe1fa3d1292c48e598beffb0f030fe23.jpg
5822ac9da5074403bd3a31e3a5a538d3.jpg
2:仿真测试
仿真时间大概仿真到 60us 处可以看 FDMA 读写开始
5c3724471ed040b180456de05b5e4489.jpg
4上板验证
添加fpga_pin.xdc文件,不同的开发板IO的约束定义一样,下图仅仅提供演示,具体以自己拿到的配套资料工程路径下的uisrc/04_pin/fpga_pin.xdc为准。当然读者也可以根据原理图自己定义约束。
55704f61c16c4e0b8a593c4bec221464.jpg
通过ila在线逻辑分析仪IP-CORE,在线观察波形
d21b9e50d90847f7bd50752d07f6aa9b.jpg
通过在线逻辑分析仪查看结果,可以看到test_error信号一直低电平
d88da85f320c4845b3d11e081e07398a.jpg



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

本版积分规则