【FPGA】MIG DDR3读写逻辑测试

前言

        笔者在之前通过microblaze软核的方式实现了DDR3芯片的读写测试,当时对于Xilinx MIG DDR控制器的理解还比较肤浅。还是想通过控制用户接口时序的方式来读写DDR,扩展和加深自己对DDR的理解。

MIG IP核配置请看我的前一篇文章

【FPGA测试】Microblaze测试DDR读写_microblaze ddr-CSDN博客

里面关于MIG参考时钟输入有错误的地方,这里改正一下。

MIG的输入时钟有2种,一个是系统时钟,如下图所示,input Clock period。还有一个时钟为参考时钟,固定为200MHz。

DDR读写时序

首先是命令,如图,只有当app_rdy信号为高时,用户给MIG的指令才有效。

然后是写指令,一种是写数据和写命令同时进行。第二种是写的数据慢于写命令。第三种是写的数据快于写指令。一般来说采用同时写数据和写命令即可。

        当输入读命令和读地址时,延迟几个时钟周期后,开始读出数据。当app_rd_data_vaild为高时,读出的数据才是正确且有效的。

测试代码如下:

每给一个地址,会往ddr里写8个32bit的数据。这样一次写命令就可以写满8个地址。

所以每写一次,地址都要加8。

同时。每读一次,会读出8个地址的数据,所以每次读一次,地址也要加8。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/12/23 14:03:36
// Design Name: 
// Module Name: DDR3
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module DDR3(
    ddr3_addr,
    ddr3_ba,
    ddr3_cas_n,
    ddr3_ck_n,
    ddr3_ck_p,
    ddr3_cke,
    ddr3_cs_n,
    ddr3_dm,
    ddr3_dq,
    ddr3_dqs_n,
    ddr3_dqs_p,
    ddr3_odt,
    ddr3_ras_n,
    ddr3_reset_n,
    ddr3_we_n,
    
    clk_100M,
    rst_n
    );
	
  output [14:0]ddr3_addr;
  output [2:0]ddr3_ba;
  output ddr3_cas_n;
  output [0:0]ddr3_ck_n;
  output [0:0]ddr3_ck_p;
  output [0:0]ddr3_cke;
  output [0:0]ddr3_cs_n;
  output [3:0]ddr3_dm;
  inout [31:0]ddr3_dq;
  inout [3:0]ddr3_dqs_n;
  inout [3:0]ddr3_dqs_p;
  output [0:0]ddr3_odt;
  output ddr3_ras_n;
  output ddr3_reset_n;
  output ddr3_we_n;
  
  input clk_100M;
  input rst_n;
  
  wire [14:0]ddr3_addr;
  wire [2:0]ddr3_ba;
  wire ddr3_cas_n;
  wire [0:0]ddr3_ck_n;
  wire [0:0]ddr3_ck_p;
  wire [0:0]ddr3_cke;
  wire [0:0]ddr3_cs_n;
  wire [3:0]ddr3_dm;
  wire [31:0]ddr3_dq;
  wire [3:0]ddr3_dqs_n;
  wire [3:0]ddr3_dqs_p;
  wire [0:0]ddr3_odt;
  wire ddr3_ras_n;
  wire ddr3_reset_n;
  wire ddr3_we_n;

  wire clk_100M;
  wire rst_n;
  
  wire init_calib_complete; 
  wire [28:0]		app_addr;
  wire [2:0]		app_cmd;
  wire app_en;
  wire [255:0]		app_wdf_data;
  wire app_wdf_end;
  wire app_wdf_wren;
  wire [255:0] app_rd_data;
  wire app_rd_data_end;
  wire app_rd_data_valid;
  wire app_rdy;
  wire app_wdf_rdy;
  wire app_sr_req;
  wire app_ref_req;
  wire app_zq_req;
  wire app_sr_active;
  wire app_ref_ack;
  wire app_zq_ack;
  wire ui_clk;
  wire ui_clk_sync_rst;
  wire [31:0] app_wdf_mask;
  wire sys_clk_i;
  wire clk_ref_i;
  wire sys_rst;
  
  mig_7series_0 u_mig_7series_0 (


    // Memory interface ports
    .ddr3_addr                      (ddr3_addr),  // output [14:0]		ddr3_addr
    .ddr3_ba                        (ddr3_ba),  // output [2:0]		ddr3_ba
    .ddr3_cas_n                     (ddr3_cas_n),  // output			ddr3_cas_n
    .ddr3_ck_n                      (ddr3_ck_n),  // output [0:0]		ddr3_ck_n
    .ddr3_ck_p                      (ddr3_ck_p),  // output [0:0]		ddr3_ck_p
    .ddr3_cke                       (ddr3_cke),  // output [0:0]		ddr3_cke
    .ddr3_ras_n                     (ddr3_ras_n),  // output			ddr3_ras_n
    .ddr3_reset_n                   (ddr3_reset_n),  // output			ddr3_reset_n
    .ddr3_we_n                      (ddr3_we_n),  // output			ddr3_we_n
    .ddr3_dq                        (ddr3_dq),  // inout [31:0]		ddr3_dq
    .ddr3_dqs_n                     (ddr3_dqs_n),  // inout [3:0]		ddr3_dqs_n
    .ddr3_dqs_p                     (ddr3_dqs_p),  // inout [3:0]		ddr3_dqs_p
    .init_calib_complete            (init_calib_complete),  // output			init_calib_complete


	.ddr3_cs_n                      (ddr3_cs_n),  // output [0:0]		ddr3_cs_n
    .ddr3_dm                        (ddr3_dm),  // output [3:0]		ddr3_dm
    .ddr3_odt                       (ddr3_odt),  // output [0:0]		ddr3_odt
    // Application interface ports
    .app_addr                       (app_addr),  // input [28:0]		app_addr
    .app_cmd                        (app_cmd),  // input [2:0]		app_cmd
    .app_en                         (app_en),  // input				app_en
    .app_wdf_data                   (app_wdf_data),  // input [255:0]		app_wdf_data
    .app_wdf_end                    (app_wdf_end),  // input				app_wdf_end
    .app_wdf_wren                   (app_wdf_wren),  // input				app_wdf_wren
    .app_rd_data                    (app_rd_data),  // output [255:0]		app_rd_data
    .app_rd_data_end                (app_rd_data_end),  // output			app_rd_data_end
    .app_rd_data_valid              (app_rd_data_valid),  // output			app_rd_data_valid
    .app_rdy                        (app_rdy),  // output			app_rdy
    .app_wdf_rdy                    (app_wdf_rdy),  // output			app_wdf_rdy
    .app_sr_req                     (app_sr_req),  // input			app_sr_req
    .app_ref_req                    (app_ref_req),  // input			app_ref_req
    .app_zq_req                     (app_zq_req),  // input			app_zq_req
    .app_sr_active                  (app_sr_active),  // output			app_sr_active
    .app_ref_ack                    (app_ref_ack),  // output			app_ref_ack
    .app_zq_ack                     (app_zq_ack),  // output			app_zq_ack
    .ui_clk                         (ui_clk),  // output			ui_clk
    .ui_clk_sync_rst                (ui_clk_sync_rst),  // output			ui_clk_sync_rst
    .app_wdf_mask                   (app_wdf_mask),  // input [31:0]		app_wdf_mask
    // System Clock Ports
    .sys_clk_i                       (sys_clk_i),
    // Reference Clock Ports
    .clk_ref_i                      (clk_ref_i),
    .sys_rst                        (sys_rst) // input sys_rst

    );
 
 wire clk_200M;
 wire rst;
 wire locked;
   clk_wiz_0 instance_name
   (
    // Clock out ports
    .clk_out1(clk_200M),     // output clk_out1
    // Status and control signals
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(clk_100M));  
 
 assign sys_clk_i = clk_200M;
 assign clk_ref_i = clk_200M;
 assign rst = rst_n;
 assign sys_rst = rst_n;
 
 parameter    [2:0]IDLE  =3'd0;
 parameter    [2:0]WRITE =3'd1;
 parameter    [2:0]CLC=3'd2;
 parameter    [2:0]WAIT  =3'd3;
 parameter    [2:0]READ  =3'd4;
 parameter    [2:0]CMD_WRITE    =3'd0;
 parameter    [2:0]CMD_READ     =3'd1;
 parameter    TEST_DATA_RANGE   =28'd1000;//测试1000个地址

 reg   [2 :0]state=0;
 reg    [31:0]Count_64; 
 reg    [27:0]app_addr_begin; 
 
 assign    app_wdf_end                     =app_wdf_wren;
 assign    app_en                          =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : ((state==READ)&&app_rdy);
 assign    app_wdf_wren                    =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : 1'b0;
 assign    app_cmd                         =(state==WRITE) ? CMD_WRITE : CMD_READ;
 assign    app_addr                        =app_addr_begin;
 assign    app_wdf_data                    ={
                                                Count_64[31:0],(Count_64[31:0]+32'd1),(Count_64[31:0]+32'd2),(Count_64[31:0]+32'd3),
                                                (Count_64[31:0]+32'd4),(Count_64[31:0]+32'd5),(Count_64[31:0]+32'd6),(Count_64[31:0]+32'd7)
                                             };//一次写入8个地址


always@(posedge clk_100M)
    if(!rst&!init_calib_complete)
        begin
        state                           <=IDLE;
        app_addr_begin                  <=28'd0;
        Count_64                       <=32'd0;
        end
 else case(state)
     IDLE:    begin
        state                            <=WRITE;
        if(app_addr_begin >= TEST_DATA_RANGE)
        app_addr_begin                 <=28'd0;
        Count_64                         <=32'd0;
        end
     WRITE:    begin
        state                            <=(Count_64>=TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy ? CLC:state;
        Count_64                         <=app_rdy&&app_wdf_rdy?(Count_64+32'd8):Count_64;    
        app_addr_begin                   <=app_rdy&&app_wdf_rdy?(app_addr_begin+28'd8):app_addr_begin;8*32=256
        end
     CLC:    begin
        state                            <=WAIT;
        Count_64                         <=32'd0;    
        app_addr_begin                   <=28'd0;    
        end
     WAIT:    begin
        state                            <=READ;
        Count_64                         <=32'd0;    
        app_addr_begin                   <=28'd0;    
        end     
     READ:    begin
        state                            <=(Count_64>=TEST_DATA_RANGE)&&app_rdy? IDLE:state;
        Count_64                         <=app_rdy?(Count_64+32'd8):Count_64;    
        app_addr_begin                   <=app_rdy?(app_addr_begin+28'd8):app_addr_begin;
        end
 default:begin
        state                            <=IDLE;
        app_addr_begin                   <=28'd0;
        Count_64                         <=32'd0;
        end        
    endcase
 
reg [27:0] count;

always@(posedge clk_100M)
    if(!rst)
       count <= 28'b0;
    else if(app_rd_data_valid)begin
        if(app_rd_data[255:224]==32'd0)
          count <= 28'd8;
        else  
          count <= count + 28'd8;
    end
    else if(count >= 28'd1000)   
       count <= 28'b0;  
       
 
 ila_0 u1 (
	.clk(clk_100M), // input wire clk
	.probe0(app_cmd), // input wire [2:0]  probe0  
	.probe1(state), // input wire [1:0]  probe1 
	.probe2(Count_64), // input wire [23:0]  probe2 
	.probe3(app_addr_begin), // input wire [27:0]  probe3 
	.probe4(app_rd_data), // input wire [255:0]  probe4 
	.probe10(app_rd_data_valid), // input wire [0:0]  probe5 
	.probe6(app_en), // input wire [0:0]  probe6 
	.probe7(app_wdf_wren), // input wire [0:0]  probe7 
	.probe8(app_rdy), // input wire [0:0]  probe8 
	.probe9(app_wdf_rdy), // input wire [0:0]  probe9 
    .probe5(app_wdf_data),
    .probe11(app_rd_data[31:0]),
    .probe12(app_rd_data[255:224]),
    .probe13(count),
    .probe14(app_rd_data_valid)
);
endmodule

首先看一下写的情况,从零地址开始,每个地址写上地址数,共写1000个地址。

再看一下读的情况,每次读出8个地址的32位数据。

如上图所示,黄线处开始读命令,但是此时并没有有效数据读出,需要等待几个时钟周期,才能读出正确数据。

如上图所示,读出的数据是正确的。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在进行FPGADDR3读写测试时,我们可以使用Xilinx公司的MIG IP核来实现。DDR3是一种常见的存储器,广泛应用于计算机和嵌入式产品中,特别是在需要大量数据交互的场合,比如电脑的内存条。DDR3相对于SDRAM是双沿触发,读写速度快一倍,并且具有更高的运行性能和更低的电压。在本次实验中,我们使用的DDR3芯片是南亚的NT5CB128M16CP-DI,它的地址大小为128M,数据位宽为16bit,容量大小为256MByte。在配置原理中,我们需要设置DDR3 IO接口时钟和DDR3 MIG IP核用户接口时钟的比例,以确保正确的时钟频率。另外,我们还可以根据需要选择IP核提供的定制化镁光系列芯片,或者自己输出DDR3芯片的相关参数进行配置。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [FPGA学习之DDR3读写实验](https://blog.csdn.net/m0_51466525/article/details/122584907)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Xilinx FPGA DDR3设计(三)DDR3 IP核详解及读写测试](https://blog.csdn.net/gslscyx/article/details/130694959)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值