前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DDR3篇第三讲、DDR3读写测试项目分析

DDR3篇第三讲、DDR3读写测试项目分析

作者头像
根究FPGA
发布2020-06-30 11:22:40
3K0
发布2020-06-30 11:22:40
举报
文章被收录于专栏:根究FPGA根究FPGA

本节介绍一个米联客DDR3读写测试的工程,把一些难以理解的代码进行了注释,如果哪里有问题的话,感谢大家指出,最后对波形进行分析。

一、DDR3读写测试代码

前面的内容基本不需要看,重点在后面的assign赋值语句与两个状态机模块。

代码语言:javascript
复制
`timescale 1ps/1ps
module example_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              = 4,
                                     // # 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              = 32,
                                     // # of DQ (data)
   parameter DQS_WIDTH             = 4,
   parameter DQS_CNT_WIDTH         = 2,
                                     // = 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 (1)+ BANK_WIDTH(3)
                                     //     + ROW_WIDTH(15) + COL_WIDTH;(10)
                                     // 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         = 8,
                                     // write PLL VCO multiplier
   parameter DIVCLK_DIVIDE         = 1,
                                     // write PLL VCO divisor
   parameter CLKOUT0_PHASE         = 337.5,
                                     // Phase for PLL output clock (CLKOUT0)
   parameter CLKOUT0_DIVIDE        = 2,
                                     // 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           = 4,
                                     // 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 [31:0]                       ddr3_dq,
   inout [3:0]                        ddr3_dqs_n,
   inout [3: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 [3:0]           ddr3_dm,
   
   output [0:0]           ddr3_odt,
   // Inputs
   output                       tg_compare_error,
   output                       init_calib_complete,
   input                        clk100m_i,
   input                        rst_key
   
   );
   wire sys_rst;
   wire locked;
   wire clk_ref_i;
   wire sys_clk_i;
   wire clk_200;
      
   assign sys_rst = ~rst_key;//复位信号
   assign clk_ref_i = clk_200;//200M的参考时钟
   assign sys_clk_i = clk_200;//200M的系统时钟
   
   //时钟管理产生DDR需要的时钟   
   clk_wiz_0 CLK_WIZ_DDR( .clk_out1(clk_200),
   .reset(sys_rst),
   .locked(locked),
   .clk_in1(clk100m_i)
   ); 

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


  localparam DATA_WIDTH            = 32;
  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 declarations
      
  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; 
  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;
  
 

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


// Start of User Design top instance
//***************************************************************************
// The User design is instantiated below. The memory interface ports are
// connected to the top-level and the application interface ports are
// connected to the traffic generator module. This provides a reference
// for connecting the memory controller to system.
//***************************************************************************
 mig_7series_0 u_mig_7series_0
     (
// Memory interface ports
      .ddr3_addr                      (ddr3_addr),
      .ddr3_ba                        (ddr3_ba),
      .ddr3_cas_n                     (ddr3_cas_n),
      .ddr3_ck_n                      (ddr3_ck_n),
      .ddr3_ck_p                      (ddr3_ck_p),
      .ddr3_cke                       (ddr3_cke),
      .ddr3_ras_n                     (ddr3_ras_n),
      .ddr3_we_n                      (ddr3_we_n),
      .ddr3_dq                        (ddr3_dq),
      .ddr3_dqs_n                     (ddr3_dqs_n),
      .ddr3_dqs_p                     (ddr3_dqs_p),
      .ddr3_reset_n                   (ddr3_reset_n),
      .init_calib_complete            (init_calib_complete),
      .ddr3_cs_n                      (ddr3_cs_n),
      .ddr3_dm                        (ddr3_dm),
      .ddr3_odt                       (ddr3_odt),
// Application interface ports
      .app_addr                       (app_addr),
      .app_cmd                        (app_cmd),
      .app_en                         (app_en),
      .app_wdf_data                   (app_wdf_data),
      .app_wdf_end                    (app_wdf_end),
      .app_wdf_wren                   (app_wdf_wren),
      .app_rd_data                    (app_rd_data),
      .app_rd_data_end                (app_rd_data_end),
      .app_rd_data_valid              (app_rd_data_valid),
      .app_rdy                        (app_rdy),
      .app_wdf_rdy                    (app_wdf_rdy),
      .app_sr_req                     (1'b0),
      .app_ref_req                    (1'b0),
      .app_zq_req                     (1'b0),
      .app_sr_active                  (app_sr_active),
      .app_ref_ack                    (app_ref_ack),
      .app_zq_ack                     (app_zq_ack),
      .ui_clk                         (clk),
      .ui_clk_sync_rst                (rst),
      .app_wdf_mask                   (32'd0),
// System Clock Ports
      .sys_clk_i                      (sys_clk_i),
// Reference Clock Ports
      .clk_ref_i                      (clk_ref_i),
      .device_temp                    (device_temp),
      .sys_rst                        (locked)
      );
      
//以下是读写测试       
// End of User Design top instance
parameter    [2:0]CMD_WRITE    =3'd0;
parameter    [2:0]CMD_READ    =3'd1;
//parameter TEST_DATA_RANGE=24'd16777215;//全地址测试
parameter TEST_DATA_RANGE=24'd2000;//部分测试

(*mark_debug="true"*)    wire init_calib_complete;
(*mark_debug="true"*)    reg    [3:0]state=0;
(*mark_debug="true"*)    reg    [23:0]Count_64=0;// 128M*2*16/256
(*mark_debug="true"*)    reg    [23:0]Count_64_1=0;
(*mark_debug="true"*)    reg    ProsessIn=0;//表示读写操作的包络
(*mark_debug="true"*)    reg    WriteSign=0;//表示是写操作
(*mark_debug="true"*)    reg    ProsessIn1=0;//表示写操作的包络
reg    [ADDR_WIDTH-1:0]app_addr_begin=0; 
reg    [29:0]CountRead_tem=0;

(*mark_debug="true"*)    wire   error_rddata=0;

assign    app_wdf_end                        =app_wdf_wren;//两个相等即可
//根据是否允许数据写入,之后等待app_wdf_rdy为一,再等待app_rdy的应答信号,在输入请求中使用
assign    app_en                            =ProsessIn?(WriteSign?app_rdy&&app_wdf_rdy:app_rdy):1'd0;//控制命令使能
//根据是否写数据使能,发送数据写入指令,或者数据读取指令
assign    app_cmd                            =WriteSign?CMD_WRITE:CMD_READ;
//数据操作地址为begin~~~~~后续自加8
assign    app_addr                        =app_addr_begin;
assign    app_wdf_data                    =Count_64_1;//写入的数据是计数器
//根据是否允许数据写入,且DDR3准备好&MIG内部fifo准备好,使能写入数据
assign    app_wdf_wren                    =ProsessIn1?app_rdy&&app_wdf_rdy:1'd0;

//app_rdy:用户提交的请求已经被接受
//app_wdf_rdy:写入数据fifo已经准备好接收数据
always@(posedge clk)
   if(rst&!init_calib_complete)//MIG复位&未初始化结束
       begin
       state                            <=4'd0;  //初始状态
       app_addr_begin                    <=28'd0; //起始地址
       WriteSign                        <=1'd0;  //写标志
       ProsessIn                        <=1'd0;  //输入写入
       Count_64                        <=24'd0;   //数据写入个数计数器
       end
else case(state)
4'd0:    begin
       state                            <=4'd1; //在状态0时跳转到状态1
       app_addr_begin                    <=28'd0;  //传输起始地址为0
       WriteSign                        <=1'd0;     //写数据清零
       ProsessIn                        <=1'd0;     //写指令清零
       Count_64                        <=24'd0;     //数据写入个数计数器清零  
       end
4'd1:    begin
       state                            <=4'd2;
       WriteSign                        <=1'd1; //允许写入数据
       ProsessIn                        <=1'd1; //允许发送写指令   
       Count_64                        <=24'd0; //发送数据次数计数器清零   
       app_addr_begin                    <=28'd0; //写数据的起始地址 
       end
4'd2:    begin//写整片的DDR3
      //未发送完指定长度时,在该状态保持
       state                            <=(Count_64==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy?4'd3:4'd2;//最后一个地址写完之后跳出状态
     //未写入到指定长度时,一直写数据使能  
       WriteSign                        <=(Count_64==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy?1'd0:1'd1;//写数据使能
    //未写入到指定长度时,每次MIG准备好且MIG数据fifo准备好,写指令使能   
       ProsessIn                        <=(Count_64==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy?1'd0:1'd1;//写命令使能
       //MIG准备好且接收数据fifo已经准备好,则发送数据寄存器自加一
       Count_64                        <=app_rdy&&app_wdf_rdy?(Count_64+24'd1):Count_64;
     // 当提交给UI的指令已经被接受且写入数据fifo准备好接收数据,发送给数据,DDR的工作频率是800Mhz,数据宽度32,上下沿都写入数据,每次需要256bit数据
     //之后需要跳转到下一个数据地址+8   
       app_addr_begin                    <=app_rdy&&app_wdf_rdy?(app_addr_begin+28'd8):app_addr_begin;//跳到下一个(8*32=256)bit数据地址
  
       end
4'd3:    begin
       state                            <=(state1==4'd0)?4'd4:state;  //等待数据全部写入
       WriteSign                        <=1'd0;                        //将写数据使能清零,表示读取数据使能
       ProsessIn                        <=(state1==4'd0)?1'd1:1'd0;    //将写数据指令清零  表示读取指令使能
       Count_64                        <=24'd0;                        //写数据计数器清零
       app_addr_begin                    <=28'd0;                      //操作起始地址清零
       end
4'd4:    begin//读整片的DDR3
       state                            <=(Count_64==TEST_DATA_RANGE)&&app_rdy?4'd0:state;
       WriteSign                        <=1'd0;
       ProsessIn                        <=(Count_64==TEST_DATA_RANGE)&&app_rdy?1'd0:1'd1;    
       Count_64                        <=app_rdy?(Count_64+24'd1):Count_64;    
       app_addr_begin                    <=app_rdy?(app_addr_begin+28'd8):app_addr_begin; 
       end
default:begin
       state                            <=4'd1;
       app_addr_begin                    <=28'd0;
       WriteSign                        <=1'd0;
       ProsessIn                        <=1'd0;
       Count_64                        <=24'd0;
       end        
   endcase
   
(*mark_debug="true"*)    reg    [3:0]state1=0;
always@(posedge clk)//单独将写操作从上面的状态机提出来,当然也可以和上面的状态机合并到一起
   //当DDR处于复位状态且为初始化完成
   if(rst&!init_calib_complete)//
       begin
       state1                            <=4'd0;  //状态机初始状态
       ProsessIn1                        <=1'd0; //写指令不使能
       end
else case(state1)
4'd0:    begin
    //空闲状态时,等待主状态机跳转到state==1
       state1                            <=(state==4'd1)?4'd1:4'd0;  
       ProsessIn1                        <=(state==4'd1)?1'd1:1'd0;
       Count_64_1                        <=24'd0;
       end
4'd1:    begin
    //当写入数据写到指定长度,且UI接受写指令&MIG接收数据fifo准备好时返回到状态0,否则在状态0维持
       state1                            <=(Count_64_1==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy?4'd0:4'd1;
       //当写入数据写到指定长度,且UI接受写指令&MIG接收数据fifo准备好时返回到状态0,否则不断使能输入写入指令
       ProsessIn1                        <=(Count_64_1==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy?1'd0:1'd1;    
       Count_64_1                        <=app_rdy&&app_wdf_rdy?(Count_64_1+24'd1):Count_64_1;    
       end
default:begin
       state1                            <=(state==4'd1)?4'd1:4'd0;
       ProsessIn1                        <=(state==4'd1)?1'd1:1'd0;
       Count_64_1                        <=24'd0;
       end
       endcase
   
(*mark_debug="true"*)    reg    [23:0]app_rd_data_tem=0;
(*mark_debug="true"*)    reg    [23:0]cmp_data_r1=0;
reg    [23:0]cmp_data_r;
always@(posedge clk)
    if(rst&!init_calib_complete)begin
        cmp_data_r <= 24'd0;
    end 
    else if(cmp_data_r==TEST_DATA_RANGE)begin
        cmp_data_r <= 24'd0;
    end
    else if(app_rd_data_valid) begin
        cmp_data_r<=cmp_data_r+1'b1;  //每次检测到有效数据标志时,将数据计数器自加一
    end

reg app_rd_data_valid_r=1'b0;
always@(posedge clk) begin
    app_rd_data_valid_r <= app_rd_data_valid;  //寄存读取数据有效标志
    app_rd_data_tem     <= app_rd_data;         //寄存读取得到的数据
    cmp_data_r1 <= cmp_data_r;                  //寄存计数器产生的数据
end
 
/*
错误校验,当读取得到的数据与计数器产生的数据大小不一致时,且经过寄存的数据有效信号为一时,将错误标志位置一
*/ 
assign error_rddata=(app_rd_data_tem!=cmp_data_r1)&app_rd_data_valid_r; 

endmodule

二、DDR3波形分析

1、状态分析

state=4表示处于数据读取状态,state=2表示处于数据写入状态。

2、读取数据的有效标志

数据读取阶段,发出读取指令后,经过一段时间的延迟,读取数据显示到数据总线,同时data_valid信号为1.

3、数据读取操作

此处仍在进行数据单独,但是后半部分与数据写入部分重合,这说明从DDR中读取得到的数据保存在MIG内部的fifo中,从而不会影响到数据写入操作。

4、数据的写入与读取

当WriteSign为0时进行数据读取,为1时进行数据写入操作

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 根究FPGA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、状态分析
  • 2、读取数据的有效标志
  • 3、数据读取操作
  • 4、数据的写入与读取
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档