【JokerのKintex7325】DDR_TEST_PROGRAM。

软件环境:vivado 2017.4        硬件平台:XC7K325


之前有发过一篇PL侧MIG的配置方式的博客,但是当时没有留测试代码,是再犹豫代码要不要统一搞GIT上,但是我这网不知道是咋了,老是代码同步不过去,火大的很,气的我就不搞了,过一段时间以后就。。。就忘了密码了,然后又找回来,又GIT不上去,又火大。。。循环循环循环,算了,干脆老样子,直接贴下面算了。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2020/08/15 15:09:24
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module top
#(//***************************************************************************

   // Traffic Gen related parameters

   //***************************************************************************

   parameter PORT_MODE             = "BI_MODE",

   parameter DATA_MODE             = 4'b0010,

   parameter TST_MEM_INSTR_MODE    = "R_W_INSTR_MODE",

   parameter EYE_TEST              = "FALSE",

                                     // set EYE_TEST = "TRUE" to probe memory

                                     // signals. Traffic Generator will only

                                     // write to one single location and no

                                     // read transactions will be generated.

   parameter DATA_PATTERN          = "DGEN_ALL",

                                      // For small devices, choose one only.

                                      // For large device, choose "DGEN_ALL"

                                      // "DGEN_HAMMER", "DGEN_WALKING1",

                                      // "DGEN_WALKING0","DGEN_ADDR","

                                      // "DGEN_NEIGHBOR","DGEN_PRBS","DGEN_ALL"

   parameter CMD_PATTERN           = "CGEN_ALL",

                                      // "CGEN_PRBS","CGEN_FIXED","CGEN_BRAM",

                                      // "CGEN_SEQUENTIAL", "CGEN_ALL"

   parameter CMD_WDT               = 'h3FF,

   parameter WR_WDT                = 'h1FFF,

   parameter RD_WDT                = 'h3FF,

   parameter SEL_VICTIM_LINE       = 0,

   parameter BEGIN_ADDRESS         = 32'h00000000,

   parameter END_ADDRESS           = 32'h00ffffff,

   parameter PRBS_EADDR_MASK_POS   = 32'hff000000,



   //***************************************************************************

   // The following parameters refer to width of various ports

   //***************************************************************************

   parameter CK_WIDTH              = 1,

                                     // # of CK/CK# outputs to memory.

   parameter nCS_PER_RANK          = 1,

                                     // # of unique CS outputs per rank for phy

   parameter CKE_WIDTH             = 1,

                                     // # of CKE outputs to memory.

   parameter DM_WIDTH              = 8,

                                     // # of DM (data mask)

   parameter ODT_WIDTH             = 1,

                                     // # of ODT outputs to memory.

   parameter BANK_WIDTH            = 3,

                                     // # of memory Bank Address bits.

   parameter COL_WIDTH             = 10,

                                     // # of memory Column Address bits.

   parameter CS_WIDTH              = 1,

                                     // # of unique CS outputs to memory.

   parameter DQ_WIDTH              = 64,

                                     // # of DQ (data)

   parameter DQS_WIDTH             = 8,

   parameter DQS_CNT_WIDTH         = 3,

                                     // = ceil(log2(DQS_WIDTH))

   parameter DRAM_WIDTH            = 8,

                                     // # of DQ per DQS

   parameter ECC                   = "OFF",

   parameter ECC_TEST              = "OFF",

   //parameter nBANK_MACHS           = 4,

   parameter nBANK_MACHS           = 4,

   parameter RANKS                 = 1,

                                     // # of Ranks.

   parameter ROW_WIDTH             = 15,

                                     // # of memory Row Address bits.

   parameter ADDR_WIDTH            = 29,

                                     // # = RANK_WIDTH + BANK_WIDTH

                                     //     + ROW_WIDTH + COL_WIDTH;

                                     // Chip Select is always tied to low for

                                     // single rank devices



   //***************************************************************************

   // The following parameters are mode register settings

   //***************************************************************************

   parameter BURST_MODE            = "8",

                                     // DDR3 SDRAM:

                                     // Burst Length (Mode Register 0).

                                     // # = "8", "4", "OTF".

                                     // DDR2 SDRAM:

                                     // Burst Length (Mode Register).

                                     // # = "8", "4".



   

   //***************************************************************************

   // The following parameters are multiplier and divisor factors for PLLE2.

   // Based on the selected design frequency these parameters vary.

   //***************************************************************************

   parameter CLKIN_PERIOD          = 5000,

                                     // Input Clock Period

   parameter CLKFBOUT_MULT         = 4,

                                     // write PLL VCO multiplier

   parameter DIVCLK_DIVIDE         = 1,

                                     // write PLL VCO divisor

   parameter CLKOUT0_PHASE         = 315.0,

                                     // Phase for PLL output clock (CLKOUT0)

   parameter CLKOUT0_DIVIDE        = 1,

                                     // VCO output divisor for PLL output clock (CLKOUT0)

   parameter CLKOUT1_DIVIDE        = 2,

                                     // VCO output divisor for PLL output clock (CLKOUT1)

   parameter CLKOUT2_DIVIDE        = 32,

                                     // VCO output divisor for PLL output clock (CLKOUT2)

   parameter CLKOUT3_DIVIDE        = 8,

                                     // VCO output divisor for PLL output clock (CLKOUT3)

   parameter MMCM_VCO              = 800,

                                     // Max Freq (MHz) of MMCM VCO

   parameter MMCM_MULT_F           = 8,

                                     // write MMCM VCO multiplier

   parameter MMCM_DIVCLK_DIVIDE    = 1,

                                     // write MMCM VCO divisor



   //***************************************************************************

   // Simulation parameters

   //***************************************************************************

   parameter SIMULATION            = "FALSE",

                                     // Should be TRUE during design simulations and

                                     // FALSE during implementations



   //***************************************************************************

   // IODELAY and PHY related parameters

   //***************************************************************************

   parameter TCQ                   = 100,

   

   parameter DRAM_TYPE             = "DDR3",



   

   //***************************************************************************

   // System clock frequency parameters

   //***************************************************************************

   parameter nCK_PER_CLK           = 4,

                                     // # of memory CKs per fabric CLK



   



   //***************************************************************************

   // Debug parameters

   //***************************************************************************

   parameter DEBUG_PORT            = "OFF",

                                     // # = "ON" Enable debug signals/controls.

                                     //   = "OFF" Disable debug signals/controls.

      

   parameter RST_ACT_LOW           = 1

                                     // =1 for active low reset,

                                     // =0 for active high.)
)
(
    // Inouts
   inout [63:0]                       ddr3_dq,
   inout [7:0]                        ddr3_dqs_n,
   inout [7:0]                        ddr3_dqs_p,

   // Outputs
   output [14:0]                      ddr3_addr,
   output [2:0]                       ddr3_ba,
   output                             ddr3_ras_n,
   output                             ddr3_cas_n,
   output                             ddr3_we_n,
   output                             ddr3_reset_n,
   output [0:0]                       ddr3_ck_p,
   output [0:0]                       ddr3_ck_n,
   output [0:0]                       ddr3_cke,
   
   output [0:0]           ddr3_cs_n,
   
   output [7:0]           ddr3_dm,
   
   output [0:0]           ddr3_odt,
   

   // Inputs
   
   //output                       tg_compare_error,
   //output                       init_calib_complete,
   input                        clk_50
    );
    
    wire clk_200;
    wire locked;
    wire sys_clk_i;
    wire tg_compare_error;
    wire init_calib_complete;
    
  localparam DATA_WIDTH            = 64;
  localparam RANK_WIDTH            = clogb2(RANKS);
  localparam PAYLOAD_WIDTH         = (ECC_TEST == "OFF") ? DATA_WIDTH : DQ_WIDTH;
  localparam BURST_LENGTH          = STR_TO_INT(BURST_MODE);
  localparam APP_DATA_WIDTH        = 2 * nCK_PER_CLK * PAYLOAD_WIDTH;
  localparam APP_MASK_WIDTH        = APP_DATA_WIDTH / 8;

  //***************************************************************************
  // Traffic Gen related parameters (derived)
  //***************************************************************************
  localparam  TG_ADDR_WIDTH = ((CS_WIDTH == 1) ? 0 : RANK_WIDTH)
                                 + BANK_WIDTH + ROW_WIDTH + COL_WIDTH;
  localparam MASK_SIZE             = DATA_WIDTH/8;
    
    wire [(2*nCK_PER_CLK)-1:0]            app_ecc_multiple_err;
  wire [(2*nCK_PER_CLK)-1:0]            app_ecc_single_err;
  wire [ADDR_WIDTH-1:0]                 app_addr;
  wire [2:0]                            app_cmd;
  wire                                  app_en;
  wire                                  app_rdy;
  wire [APP_DATA_WIDTH-1:0]             app_rd_data;
  wire                                  app_rd_data_end;
  wire                                  app_rd_data_valid;
  wire [APP_DATA_WIDTH-1:0]             app_wdf_data;
  wire                                  app_wdf_end;
  wire [APP_MASK_WIDTH-1:0]             app_wdf_mask;
  wire                                  app_wdf_rdy;
  wire                                  app_sr_active;
  wire                                  app_ref_ack;
  wire                                  app_zq_ack;
  wire                                  app_wdf_wren;
  wire [(64+(2*APP_DATA_WIDTH))-1:0]      error_status;
  wire [(PAYLOAD_WIDTH/8)-1:0] cumlative_dq_lane_error;
  wire                                  mem_pattern_init_done;
  wire [47:0]                           tg_wr_data_counts;
  wire [47:0]                           tg_rd_data_counts;
  wire                                  modify_enable_sel;
  wire [2:0]                            data_mode_manual_sel;
  wire [2:0]                            addr_mode_manual_sel;
  wire [APP_DATA_WIDTH-1:0]             cmp_data;
  reg [63:0]                            cmp_data_r;
  wire                                  cmp_data_valid;
  reg                                   cmp_data_valid_r;
  wire                                  cmp_error;
  wire [(PAYLOAD_WIDTH/8)-1:0]          dq_error_bytelane_cmp;

  wire                                  clk;
  wire                                  rst;
     
  wire [11:0]                           device_temp;
    
    assign sys_clk_i = clk_200;//200M的系统时钟
    
    clk_wiz_0 clk_wiz_0
 (
  // Clock out ports
  .clk_out1(clk_200),//output        
  // Status and control signals
  .reset(1'b0),//input         
  .locked(locked),//output        
 // Clock in ports
  .clk_in1(clk_50)//input         
 );
    
    function integer clogb2 (input integer size);
    begin
      size = size - 1;
      for (clogb2=1; size>1; clogb2=clogb2+1)
        size = size >> 1;
    end
  endfunction // clogb2

  function integer STR_TO_INT;
    input [7:0] in;
    begin
      if(in == "8")
        STR_TO_INT = 8;
      else if(in == "4")
        STR_TO_INT = 4;
      else
        STR_TO_INT = 0;
    end
  endfunction
    
mig_7series_0 u_mig_7series_0(
  // Inouts
  .ddr3_dq(ddr3_dq),    //inout [63:0]     
  .ddr3_dqs_n(ddr3_dqs_n), //inout [7:0]      
  .ddr3_dqs_p(ddr3_dqs_p), //inout [7:0]      

  // Outputs
  .ddr3_addr(ddr3_addr),    //output [14:0]    
  .ddr3_ba(ddr3_ba),      //output [2:0]     
  .ddr3_ras_n(ddr3_ras_n),   //output           
  .ddr3_cas_n(ddr3_cas_n),   //output           
  .ddr3_we_n(ddr3_we_n),    //output           
  .ddr3_reset_n(ddr3_reset_n), //output           
  .ddr3_ck_p(ddr3_ck_p),    //output [0:0]     
  .ddr3_ck_n(ddr3_ck_n),    //output [0:0]     
  .ddr3_cke(ddr3_cke),     //output [0:0]     
  .ddr3_cs_n(ddr3_cs_n),    //output [0:0]     
  .ddr3_dm(ddr3_dm),      //output [7:0]     
  .ddr3_odt(ddr3_odt),     //output [0:0]     

  // Inputs
  // Single-ended system clock
  .sys_clk_i(sys_clk_i),//                  input             

  // user interface signals
  .app_addr(app_addr),            //input [28:0]       
  .app_cmd(app_cmd),             //input [2:0]        
  .app_en(app_en),              //input              
  .app_wdf_data(app_wdf_data),        //input [511:0]      
  .app_wdf_end(app_wdf_end),         //input              
  .app_wdf_mask(64'd0),        //input [63:0]       
  .app_wdf_wren(app_wdf_wren),        //input              
  .app_rd_data(app_rd_data),         //output [511:0]   	
  .app_rd_data_end(app_rd_data_end),     //output             
  .app_rd_data_valid(app_rd_data_valid),   //output             
  .app_rdy(app_rdy),             //output             
  .app_wdf_rdy(app_wdf_rdy),         //output             
  .app_sr_req(1'b0),          //input         		  
  .app_ref_req(1'b0),         //input         		  
  .app_zq_req(1'b0),          //input         		  
  .app_sr_active(app_sr_active),       //output             
  .app_ref_ack(app_ref_ack),         //output             
  .app_zq_ack(app_zq_ack),          //output             
  .ui_clk(clk),              //output             
  .ui_clk_sync_rst(rst),     //output             
  .init_calib_complete(init_calib_complete), //output             
  .device_temp(device_temp),         //output [11:0]      
  .sys_rst(locked)              //input							
  );
  
  //ddr test application
localparam    [1:0]IDLE  =2'd0;
localparam    [1:0]WRITE =2'd1;
localparam    [1:0]WAIT  =2'd2;
localparam    [1:0]READ  =2'd3;
localparam    [2:0]CMD_WRITE    =3'd0;
localparam    [2:0]CMD_READ     =3'd1;
localparam    TEST_DATA_RANGE   =24'd1000;//数据范围

 reg   [1 :0]state=0;
reg    [23:0]Count_64=0;
reg    [ADDR_WIDTH-1:0]app_addr_begin=0;
reg    [7:0] count_temp=0;//读数据比对
reg    [15:0] count_temp2=0;//确保读1000个数据标志

assign    app_wdf_end                     =app_wdf_wren;//两个相等即可
assign    app_en                          =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : ((state==READ)&&app_rdy&&(count_temp2 <= TEST_DATA_RANGE));//读1000个数后取消使能
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[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
                                            Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
                                            Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
                                            Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
                                            Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
                                            Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
                                            Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
                                            Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0]
                                            };//写入测试数据
  
(* KEEP = "TRUE" *) reg [31:0]app_rd_data_r;
(* KEEP = "TRUE" *) reg app_rd_data_valid_r;
always @(posedge clk) begin
    app_rd_data_r <=  app_rd_data[31:0];
    app_rd_data_valid_r <= app_rd_data_valid;
end                                          

always@(posedge clk)
   if(rst&!init_calib_complete)
       begin
       state                           <=IDLE;
       app_addr_begin                  <=29'd0;
       Count_64                        <=24'd0;
       end
else case(state)
    IDLE:    begin
       state                            <=WRITE;
       Count_64                         <=24'd0;
       count_temp2                      <= 16'd0;
       app_addr_begin                   <=29'd0;    
       end
    WRITE:    begin
       state                            <=(Count_64==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy ? WAIT:state;//写够跳等待
       Count_64                         <=app_rdy&&app_wdf_rdy?(Count_64+24'd1):Count_64;//写有效自增    
       app_addr_begin                   <=app_rdy&&app_wdf_rdy?(app_addr_begin+29'd8):app_addr_begin;//下一跳(512/64 = 8)自增地址
       end
    WAIT:    begin
       state                            <=READ;
       Count_64                         <=24'd0;    
       app_addr_begin                   <=29'd0;    
       end
    READ:    begin
       state                            <=(Count_64==TEST_DATA_RANGE)&&app_rdy? IDLE:state;//读完退出
       count_temp2                      <=app_rdy?(count_temp2+24'd1):count_temp2;  //读够1000关app_en
       Count_64                         <=app_rd_data_valid_r?(Count_64+24'd1):Count_64;//读取个数计数    
       app_addr_begin                   <=app_rdy?(app_addr_begin+29'd8):app_addr_begin;//下一跳(512/64 = 8)自增地址
       end
default:begin   
       state                            <=IDLE;
       app_addr_begin                   <=29'd0;
       Count_64                         <=24'd0;
       end        
   endcase

//16bit count used for comparation
//读取数据比对组
always @(posedge clk) begin
    if(app_rd_data_valid_r)
        count_temp<= count_temp + 8'b1;
     else if(app_cmd==CMD_WRITE)
        count_temp <= 8'd0;   
 end

wire [31:0]cm_data;//compare  data
assign cm_data = {count_temp,count_temp,count_temp,count_temp};
assign tg_compare_error=(app_rd_data_valid_r&&(cm_data!=app_rd_data_r));//读出不相等,出错

ila_0 degbug_0 (
	.clk(clk), // input wire clk
	.probe0(app_rd_data_r), // input wire [31:0]  probe0  
	.probe1(cm_data), // input wire [31:0]  probe1 
	.probe2(state[1:0]), // input wire [2:0]  probe2 
	.probe3(app_rd_data_valid_r), // input wire [0:0]  probe4
	.probe4(tg_compare_error),
	.probe5(app_rdy),
	.probe6(app_wdf_rdy),
	.probe7(app_addr_begin),//29
	.probe8(Count_64),//24
	.probe9(app_en),
	.probe10(app_wdf_wren),
	.probe11(app_cmd)
);
endmodule

说下测试原理,往DDR中写1000个数,数据量的大小由TEST_DATA_RANGE来控制,因为我这里只是简单测试,简单认为1000个循环读写没问题,程序能用,读写正常就行,更复杂的地址遍历当然也可以,再这个基础上改就行。

首先是写状态,在app_rdy与app_wdf_rdy均有效的情况下,可以往DDR中写,当写够数量以后,跳到WAIT状态,而后进入读,在读状态这里需要注意一下,app_en在读的时候判别式是这样的,(state==READ)&&app_rdy&&(count_temp2 <= TEST_DATA_RANGE),在app_rdy拉高可以进行读操作的时候,count_temp2变量自增,自增到TEST_DATA_RANGE范围后,判别式为假,app_en拉低,读地址无效,此时只是伴随读地址增加,不再继续读而已,但是读过程并没有结束,因为读过程结束不结束是由Count_64来控制的,app_rd_data_valid_r拉高才表示真正读到了数据,读数据有效,等Count_64自增到TEST_DATA_RANGE,读过程才结束,进入下一个写-等-读循环。这里在读状态,为什么要搞两个变量控制,看我下面实测的图,我进一步说明。

下图是写状态时候抓的。

 展开以后是。

 可以看到,在进入写状态以后,随着app_rdy和app_wdf_rdy为高,app_en为高,地址开始增加,往DDR里开始写数就行,这里写进去的数就是Count_64拼成的带宽512 bit的数。写时序如下所示。

 在读状态时,抓图如下。

 先看读状态时候时序。当app_rdy为高,拉高app_en写入读地址app_addr,而后!在若干个周期之后!等待app_rd_data_valid为高,此时预示着数据被读出,读出的数据有效。

展开读状态的图进一步细说。

在进入读状态之后,可以看到伴随app_rdy为高,app_en拉高以后,app_rd_data_valid在过了30多个clk之后才为高,此时数据才慢慢被读出,但是这个时候,读地址已经写了30多个了,所以当读地址写够1000个时候,数据还有30来个没读完呢,这才用两个变量,Count_64用来计读出来多少个数据了,count_temp2用来计读地址写了多少个了,地址写够1000不写了,等着数据读完就行。用count_temp来在读的过程中生成对照组,当读出来的数据与count_temp不一致时,拉高tg_compare_error,表示读取错误。下图是再展开时候读取比对的图,从读状态时候的第一张图也能看出,读取过程中没有出错。

 整个过程就是这样,回头说说代码中最前端的默认参数哪来的,怎么设置,其实打开生成好的MIG核的example design,最开始的地方就能看到。

打开以后就是下面这个样子啦。

这种测试其实最多测试一下功能正常不正常,测速行不行?行,但是没啥意义,因为这种只有MIG的测试与实际使用差别太大,实际工程项目中,随着工程越来越大,布局布线方式不同,以及工作环境温度的变化,对DDR的稳定性都是有影响的,都会影响到DDR最高稳定工作频率,所以说单独测速没意义,功能正常基本就行。

最后再说这里面的一个坑,在MIG接口列表里,需要注意一下复位, .sys_rst()这个接口,一般习惯上来说,接口上不带n就表示高电平有效,也就是高电平复位,但是!但是!这里,即便这个接口不带n,也是低电平有效!再说一次!MIG核的sys_rst信号!低电平复位!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值