Intel QuartusII中DDR2 IP核的使用(2)

项目简述

该项目的描述是,FPGA向DDR2芯片写入数据,然后再读出数据,从而验证读写模块的正确性。该项目具有一定的实际意义,就是我们新制作的一块FPGA板卡,最有可能出问题的部分就是DDR芯片,因为DDR的实际属于高速设计,然后我们将对应的循环测试的程序下载进去,验证我们硬件板卡的正确性。通过本项目,我们可以学到QuartusII读写DDR2的方法,便可以掌握操作DDR2的操作流程。
本次实验所用到的软件环境:
1、QuartusII 13.1软件开发环境
2、modelsim仿真环境
3、锆石A4 Plus开发板

项目遇见得坑

本来不想写这个目录,但是自己得DDR2调了两天遇见了几个大坑,这里列出来给其他同学起到警示作用。
1、进行引脚分配得时候,会出现无法分配上去得情况,得添加相应的约束才能成功分配,这里应该是FPGA的Bank有规定的驱动能力。将如下的代码添加到相应的qsf文件中。

set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[0]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[1]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[2]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[3]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[4]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[5]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[6]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[7]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[8]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[9]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[10]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[11]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[12]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[13]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[14]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[15]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[16]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[17]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[18]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[19]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[20]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[21]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[22]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[23]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[24]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[25]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[26]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[27]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[28]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[29]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[30]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dq[31]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dqs[0]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dqs[1]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dqs[2]
set_instance_assignment -name OUTPUT_ENABLE_GROUP 1916506279 -to mem_dqs[3]

2、QuartusII子带的Modelsim-Aetra版本无法仿真,这里使用Modelsim-SE版本进行功能仿真。
3、这里有个大坑,但是我再技术手册中没有找到,就是DDR2 IP在初始化的时候global_reset_n必须复位10ms时间,如果不这样,那么local_init_done信号无法拉高,DDR2芯片无法完成初始化操作。这一点尤其重要,这一点在仿真的时候检测不到这点错误,只有下板的时候才能发现,实在是巨坑。
4、着device中必须对多功能引脚与没使用的引脚进行约束,否则local_init_done无法拉高,DDR2芯片无法正常工作。如下:
在这里插入图片描述
在这里插入图片描述
上面四点就是我做这个项目是所遇见的大坑,浪费了我断断续续两天时间,希望可以给同学们一些建议。

DDR2 IP接口的简单描述

对于生成的DDR2 IP核,我们现在对其接口信号做出相应的描述,以便于大家可以充分理解信号的作用:

ddr2_ctrl ddr2_ctrl_inst(
    .pll_ref_clk                (clk_100m                   ),
    .global_reset_n             (global_reset_n             ),
    .soft_reset_n               (1'b1                       ),
    .local_address              (local_address              ),
    .local_write_req            (local_write_req            ),
    .local_read_req             (local_read_req             ),
    .local_burstbegin           (local_burstbegin           ),
    .local_wdata                (local_wdata                ),
    .local_be                   (local_be                   ),
    .local_size                 (local_size                 ),
    .local_ready                (local_ready                ),
    .local_rdata                (local_rdata                ),
    .local_rdata_valid          (local_rdata_valid          ),
    .local_refresh_ack          (                           ),
    .local_init_done            (local_init_done            ),
    .mem_odt                    (mem_odt                    ),
    .mem_cs_n                   (mem_cs_n                   ),
    .mem_cke                    (mem_cke                    ),
    .mem_addr                   (mem_addr                   ),
    .mem_ba                     (mem_ba                     ),
    .mem_ras_n                  (mem_ras_n                  ),
    .mem_cas_n                  (mem_cas_n                  ),
    .mem_we_n                   (mem_we_n                   ),
    .mem_dm                     (mem_dm                     ),
    .mem_clk                    (mem_clk                    ),
    .mem_clk_n                  (mem_clk_n                  ),
    .mem_dq                     (mem_dq                     ),
    .mem_dqs                    (mem_dqs                    ),
    .phy_clk                    (phy_clk                    ),
    .reset_phy_clk_n            (reset_phy_clk_n            ),
    .reset_request_n            (                           ),
    .aux_full_rate_clk          (                           ),
    .aux_half_rate_clk          (                           )            
);

上面的信号解释如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里为了方便起见,我们进行了截图,当然同学们理解起来也更加容易。

DDR2 IP的读写时序

我们从技术手册中查找写时序的图形如下:
在这里插入图片描述
由于我们使用的QuartusII是最经典的13.1版本,比较古老,相应的技术手册已经找不到了。上面的技术手册比较新,DDR信号接口的名称已经发生改变,但是我们只需要关注后缀就行,信号的时序完全没有改变。留给我们用户的信号也就是我图中画框的信号。同理,我们给出相应的读信号的时序图如下:
在这里插入图片描述
从上面的时序图中,我们发现一点,就是写突发的数目与写请求的数目相同,但是不管读突发的数目,至于要给一个读请求信号就行了。同时从技术手册中,我们可以找到写请求和读请求不能同时进行。如果想要做多路通道同时进行,同学们可以仿照我VIVADO中MIG IP的实现方法。

MIG IP循环校验设计时序

我们由上面的图做了该项目循环测试的草图,如下:
在这里插入图片描述
这里需要注意一点,DDR2 IP的地址变化与突发长度之间的关系,这里每次突发直接加上突发长度即可,与ISE、VIVADO均不同。

MIG IP的读写循环代码

我们这里遵循传统直接给出代码供大家学习,结合上面的知识,有信心大家可以学会DDR2 IP的操作。由于下板测试与Modelsim仿真的代码稍微有点不同,所以我们将分别给出代码,如下:
Modelsim测试的代码:
ddr2_drive模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : ddr2_drive.v
// Create Time  : 2020-03-06 13:14:59
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module ddr2_drive(
    //System Interfaces
    input                   sclk                ,
    input                   rst_n               ,
    //DDR2 Interfaces   
    output  wire    [ 0:0]  mem_odt             ,
    output  wire    [ 0:0]  mem_cs_n            ,
    output  wire    [ 0:0]  mem_cke             ,
    output  wire    [12:0]  mem_addr            ,
    output  wire    [ 2:0]  mem_ba              ,
    output  wire            mem_ras_n           ,
    output  wire            mem_cas_n           ,
    output  wire            mem_we_n            ,
    output  wire    [ 3:0]  mem_dm              ,
    inout   wire    [ 0:0]  mem_clk             ,
    inout   wire    [ 0:0]  mem_clk_n           ,
    inout   wire    [31:0]  mem_dq              ,
    inout   wire    [ 3:0]  mem_dqs             ,
    //Debug
    input                   test_start         
) /*synthesis, probe_port,keep*/ ;

 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter       BL_LENGTH             =           16            ;
parameter       BL_NUM_END            =           2097152       ;
parameter       DELAY_10MS            =           21'd500000    ;
//pll_inst
wire                                         xclk_100m           ;
wire                                         locked              ;
//ddr2_ctrl_inst 
wire                                         reset_phy_clk_n     ;
(*keep*)         wire                        phy_clk             ;
(*keep*)         wire                        local_init_done     ;
(*preserve*)     reg                 [24:0]  local_address       ;
(*preserve*)     reg                         local_write_req     ;
(*preserve*)     reg                         local_read_req      ;
(*preserve*)     reg                         local_burstbegin    ;
(*preserve*)     reg                 [63:0]  local_wdata         ;
(*keep*)         wire                [ 7:0]  local_be            ;
(*keep*)         wire                [ 6:0]  local_size          ;
(*keep*)         wire                        local_ready         ;
(*keep*)         wire                [63:0]  local_rdata         ;
(*keep*)         wire                        local_rdata_valid   ;
(*preserve*)     reg                 [ 6:0]  wr_cnt              ;
(*preserve*)     reg                 [ 6:0]  rd_cnt              ;
(*preserve*)     reg                 [21:0]  bl_cnt              ;  
(* preserve *)   reg                 [63:0]  check_data          ;
//(*keep*)         wire                        test_start          ;
(* noprune *)    reg                 [63:0]  err_cnt             ;
(* noprune *)    reg                         test_done           ;  
reg                                          global_reset_n      ;

reg                                 [20:0]   delay_cnt           ;
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign      local_be        =       8'hff;
assign      local_size      =       BL_LENGTH;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        delay_cnt               <=      21'd0;
    else if(locked == 1'b0)
        delay_cnt               <=      21'd0;
    else if(delay_cnt >= DELAY_10MS)
        delay_cnt               <=      delay_cnt;
    else
        delay_cnt               <=      delay_cnt + 1'b1;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        global_reset_n          <=      1'b0;
    else if(locked == 1'b0)
        global_reset_n          <=      1'b0;
    else if(delay_cnt >= DELAY_10MS) 
        global_reset_n          <=      1'b1;
    else
        global_reset_n          <=      global_reset_n;
        
always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_write_req         <=      1'b0; 
    else if(test_start == 1'b1)
        local_write_req         <=      1'b1;
    else if(rd_cnt == (BL_LENGTH-1) && local_rdata_valid == 1'b1 && bl_cnt < (BL_NUM_END-1)) 
        local_write_req         <=      1'b1;
    else if(wr_cnt == (BL_LENGTH-1) && local_ready == 1'b1)
        local_write_req         <=      1'b0;
    else
        local_write_req         <=      local_write_req;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_burstbegin        <=      1'b0; 
    else if(test_start == 1'b1) 
        local_burstbegin        <=      1'b1;
    else if(rd_cnt == (BL_LENGTH-1) && local_rdata_valid == 1'b1 && bl_cnt < (BL_NUM_END-1))
        local_burstbegin        <=      1'b1;
    else if(wr_cnt == (BL_LENGTH-1) && local_ready == 1'b1)
        local_burstbegin        <=      1'b1;
    else
        local_burstbegin        <=      1'b0;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        wr_cnt                  <=      7'd0;
    else if(local_write_req == 1'b1 && local_ready == 1'b1 && wr_cnt == (BL_LENGTH-1))
        wr_cnt                  <=      7'd0;
    else if(local_write_req == 1'b1 && local_ready == 1'b1)
        wr_cnt                  <=      wr_cnt + 1'b1;
    else 
        wr_cnt                  <=      wr_cnt;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_wdata             <=      64'd0; 
    else if(local_write_req == 1'b1 && local_ready == 1'b1) 
        local_wdata             <=      local_wdata + 1'b1;
    else
        local_wdata             <=      local_wdata;  

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_read_req          <=      1'b0; 
    else if(wr_cnt == (BL_LENGTH-1) && local_ready == 1'b1)
        local_read_req          <=      1'b1;
    else if(local_ready == 1'b1)
        local_read_req          <=      1'b0;
    else
        local_read_req          <=      local_read_req;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        rd_cnt                  <=      7'd0;
    else if(local_rdata_valid == 1'b1 && rd_cnt == (BL_LENGTH-1)) 
        rd_cnt                  <=      7'd0;
    else if(local_rdata_valid == 1'b1)
        rd_cnt                  <=      rd_cnt + 1'b1;
    else
        rd_cnt                  <=      rd_cnt;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        bl_cnt                  <=      22'd0;
    else if(test_start == 1'b1)
        bl_cnt                  <=      22'd0;
    else if(local_rdata_valid == 1'b1 && rd_cnt == (BL_LENGTH-1))
        bl_cnt                  <=      bl_cnt + 1'b1;
    else
        bl_cnt                  <=      bl_cnt;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_address           <=      25'd0;
    else if(test_start == 1'b1)
        local_address           <=      25'd0;
    else if(local_rdata_valid == 1'b1 && rd_cnt == (BL_LENGTH-1))
        local_address           <=      local_address + BL_LENGTH;
    else 
        local_address           <=      local_address; 
    
always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        check_data              <=      64'd0; 
    else if(local_rdata_valid == 1'b1)
        check_data              <=      check_data + 1'b1;
    else
        check_data              <=      check_data;
          
always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        err_cnt                 <=      64'd0;
    else if(local_rdata_valid == 1'b1 && check_data != local_rdata) 
        err_cnt                 <=      err_cnt + 1'b1;
    else
        err_cnt                 <=      err_cnt;    

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        test_done               <=      1'b0;   
    else if(rd_cnt == (BL_LENGTH-1) && local_rdata_valid == 1'b1 && bl_cnt == (BL_NUM_END-1)) 
        test_done               <=      1'b1;
    else
        test_done               <=      1'b0;
                            
pll pll_inst(
    .areset                     (~rst_n                     ),
    .inclk0                     (sclk                       ),
    .c0                         (clk_100m                   ),
    .locked                     (locked                     )
);

ddr2_ctrl ddr2_ctrl_inst(
    .pll_ref_clk                (clk_100m                   ),
    .global_reset_n             (global_reset_n             ),
    .soft_reset_n               (1'b1                       ),
    .local_address              (local_address              ),
    .local_write_req            (local_write_req            ),
    .local_read_req             (local_read_req             ),
    .local_burstbegin           (local_burstbegin           ),
    .local_wdata                (local_wdata                ),
    .local_be                   (local_be                   ),
    .local_size                 (local_size                 ),
    .local_ready                (local_ready                ),
    .local_rdata                (local_rdata                ),
    .local_rdata_valid          (local_rdata_valid          ),
    .local_refresh_ack          (                           ),
    .local_init_done            (local_init_done            ),
    .mem_odt                    (mem_odt                    ),
    .mem_cs_n                   (mem_cs_n                   ),
    .mem_cke                    (mem_cke                    ),
    .mem_addr                   (mem_addr                   ),
    .mem_ba                     (mem_ba                     ),
    .mem_ras_n                  (mem_ras_n                  ),
    .mem_cas_n                  (mem_cas_n                  ),
    .mem_we_n                   (mem_we_n                   ),
    .mem_dm                     (mem_dm                     ),
    .mem_clk                    (mem_clk                    ),
    .mem_clk_n                  (mem_clk_n                  ),
    .mem_dq                     (mem_dq                     ),
    .mem_dqs                    (mem_dqs                    ),
    .phy_clk                    (phy_clk                    ),
    .reset_phy_clk_n            (reset_phy_clk_n            ),
    .reset_request_n            (                           ),
    .aux_full_rate_clk          (                           ),
    .aux_half_rate_clk          (                           )            
);

 
//========================================================================================\
//*******************************     Debug    **********************************
//========================================================================================/
//wire                            source                  ;
//reg                             source_r                ;
//reg                             source_r2               ;
//
//assign  test_start      =       source_r && ~source_r2;
//
//always @(posedge phy_clk)begin
//    source_r            <=      source;
//    source_r2           <=      source_r;
//end
//
//issp issp_inst(
//    .probe                      (                           ),
//    .source                     (source                     )
//);


endmodule

下板测试的代码:
ddr2_drive模块

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : ddr2_drive.v
// Create Time  : 2020-03-06 13:14:59
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module ddr2_drive(
    //System Interfaces
    input                   sclk                ,
    input                   rst_n               ,
    //DDR2 Interfaces   
    output  wire    [ 0:0]  mem_odt             ,
    output  wire    [ 0:0]  mem_cs_n            ,
    output  wire    [ 0:0]  mem_cke             ,
    output  wire    [12:0]  mem_addr            ,
    output  wire    [ 2:0]  mem_ba              ,
    output  wire            mem_ras_n           ,
    output  wire            mem_cas_n           ,
    output  wire            mem_we_n            ,
    output  wire    [ 3:0]  mem_dm              ,
    inout   wire    [ 0:0]  mem_clk             ,
    inout   wire    [ 0:0]  mem_clk_n           ,
    inout   wire    [31:0]  mem_dq              ,
    inout   wire    [ 3:0]  mem_dqs                 
) /*synthesis, probe_port,keep*/ ;

 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter       BL_LENGTH             =           16            ;
parameter       BL_NUM_END            =           2097152       ;
parameter       DELAY_10MS            =           21'd500000    ;
//pll_inst
wire                                         xclk_100m           ;
wire                                         xlocked             ;
//ddr2_ctrl_inst 
wire                                         reset_phy_clk_n     ;
(*keep*)         wire                        phy_clk             ;
(*keep*)         wire                        local_init_done     ;
(*preserve*)     reg                 [24:0]  local_address       ;
(*preserve*)     reg                         local_write_req     ;
(*preserve*)     reg                         local_read_req      ;
(*preserve*)     reg                         local_burstbegin    ;
(*preserve*)     reg                 [63:0]  local_wdata         ;
(*keep*)         wire                [ 7:0]  local_be            ;
(*keep*)         wire                [ 6:0]  local_size          ;
(*keep*)         wire                        local_ready         ;
(*keep*)         wire                [63:0]  local_rdata         ;
(*keep*)         wire                        local_rdata_valid   ;
(*preserve*)     reg                 [ 6:0]  wr_cnt              ;
(*preserve*)     reg                 [ 6:0]  rd_cnt              ;
(*preserve*)     reg                 [21:0]  bl_cnt              ;  
(* preserve *)   reg                 [63:0]  check_data          ;
(*keep*)         wire                        test_start          ;
(* noprune *)    reg                 [63:0]  err_cnt             ;
(* noprune *)    reg                         test_done           ;  
reg                                          global_reset_n      ;

reg                                 [20:0]   delay_cnt           ;
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign      local_be        =       8'hff;
assign      local_size      =       BL_LENGTH;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        delay_cnt               <=      21'd0;
    else if(locked == 1'b0)
        delay_cnt               <=      21'd0;
    else if(delay_cnt >= DELAY_10MS)
        delay_cnt               <=      delay_cnt;
    else
        delay_cnt               <=      delay_cnt + 1'b1;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        global_reset_n          <=      1'b0;
    else if(locked == 1'b0)
        global_reset_n          <=      1'b0;
    else if(delay_cnt >= DELAY_10MS) 
        global_reset_n          <=      1'b1;
    else
        global_reset_n          <=      global_reset_n;
        
always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_write_req         <=      1'b0; 
    else if(test_start == 1'b1)
        local_write_req         <=      1'b1;
    else if(rd_cnt == (BL_LENGTH-1) && local_rdata_valid == 1'b1 && bl_cnt < (BL_NUM_END-1)) 
        local_write_req         <=      1'b1;
    else if(wr_cnt == (BL_LENGTH-1) && local_ready == 1'b1)
        local_write_req         <=      1'b0;
    else
        local_write_req         <=      local_write_req;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_burstbegin        <=      1'b0; 
    else if(test_start == 1'b1) 
        local_burstbegin        <=      1'b1;
    else if(rd_cnt == (BL_LENGTH-1) && local_rdata_valid == 1'b1 && bl_cnt < (BL_NUM_END-1))
        local_burstbegin        <=      1'b1;
    else if(wr_cnt == (BL_LENGTH-1) && local_ready == 1'b1)
        local_burstbegin        <=      1'b1;
    else
        local_burstbegin        <=      1'b0;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        wr_cnt                  <=      7'd0;
    else if(local_write_req == 1'b1 && local_ready == 1'b1 && wr_cnt == (BL_LENGTH-1))
        wr_cnt                  <=      7'd0;
    else if(local_write_req == 1'b1 && local_ready == 1'b1)
        wr_cnt                  <=      wr_cnt + 1'b1;
    else 
        wr_cnt                  <=      wr_cnt;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_wdata             <=      64'd0; 
    else if(local_write_req == 1'b1 && local_ready == 1'b1) 
        local_wdata             <=      local_wdata + 1'b1;
    else
        local_wdata             <=      local_wdata;  

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_read_req          <=      1'b0; 
    else if(wr_cnt == (BL_LENGTH-1) && local_ready == 1'b1)
        local_read_req          <=      1'b1;
    else if(local_ready == 1'b1)
        local_read_req          <=      1'b0;
    else
        local_read_req          <=      local_read_req;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        rd_cnt                  <=      7'd0;
    else if(local_rdata_valid == 1'b1 && rd_cnt == (BL_LENGTH-1)) 
        rd_cnt                  <=      7'd0;
    else if(local_rdata_valid == 1'b1)
        rd_cnt                  <=      rd_cnt + 1'b1;
    else
        rd_cnt                  <=      rd_cnt;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        bl_cnt                  <=      22'd0;
    else if(test_start == 1'b1)
        bl_cnt                  <=      22'd0;
    else if(local_rdata_valid == 1'b1 && rd_cnt == (BL_LENGTH-1))
        bl_cnt                  <=      bl_cnt + 1'b1;
    else
        bl_cnt                  <=      bl_cnt;

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        local_address           <=      25'd0;
    else if(test_start == 1'b1)
        local_address           <=      25'd0;
    else if(local_rdata_valid == 1'b1 && rd_cnt == (BL_LENGTH-1))
        local_address           <=      local_address + BL_LENGTH;
    else 
        local_address           <=      local_address; 
    
always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        check_data              <=      64'd0; 
    else if(local_rdata_valid == 1'b1)
        check_data              <=      check_data + 1'b1;
    else
        check_data              <=      check_data;
          
always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        err_cnt                 <=      64'd0;
    else if(local_rdata_valid == 1'b1 && check_data != local_rdata) 
        err_cnt                 <=      err_cnt + 1'b1;
    else
        err_cnt                 <=      err_cnt;    

always @(posedge phy_clk or negedge reset_phy_clk_n)
    if(reset_phy_clk_n == 1'b0)
        test_done               <=      1'b0;   
    else if(rd_cnt == (BL_LENGTH-1) && local_rdata_valid == 1'b1 && bl_cnt == (BL_NUM_END-1)) 
        test_done               <=      1'b1;
    else
        test_done               <=      1'b0;
                            
pll pll_inst(
    .areset                     (~rst_n                     ),
    .inclk0                     (sclk                       ),
    .c0                         (clk_100m                   ),
    .locked                     (locked                     )
);

ddr2_ctrl ddr2_ctrl_inst(
    .pll_ref_clk                (clk_100m                   ),
    .global_reset_n             (global_reset_n             ),
    .soft_reset_n               (1'b1                       ),
    .local_address              (local_address              ),
    .local_write_req            (local_write_req            ),
    .local_read_req             (local_read_req             ),
    .local_burstbegin           (local_burstbegin           ),
    .local_wdata                (local_wdata                ),
    .local_be                   (local_be                   ),
    .local_size                 (local_size                 ),
    .local_ready                (local_ready                ),
    .local_rdata                (local_rdata                ),
    .local_rdata_valid          (local_rdata_valid          ),
    .local_refresh_ack          (                           ),
    .local_init_done            (local_init_done            ),
    .mem_odt                    (mem_odt                    ),
    .mem_cs_n                   (mem_cs_n                   ),
    .mem_cke                    (mem_cke                    ),
    .mem_addr                   (mem_addr                   ),
    .mem_ba                     (mem_ba                     ),
    .mem_ras_n                  (mem_ras_n                  ),
    .mem_cas_n                  (mem_cas_n                  ),
    .mem_we_n                   (mem_we_n                   ),
    .mem_dm                     (mem_dm                     ),
    .mem_clk                    (mem_clk                    ),
    .mem_clk_n                  (mem_clk_n                  ),
    .mem_dq                     (mem_dq                     ),
    .mem_dqs                    (mem_dqs                    ),
    .phy_clk                    (phy_clk                    ),
    .reset_phy_clk_n            (reset_phy_clk_n            ),
    .reset_request_n            (                           ),
    .aux_full_rate_clk          (                           ),
    .aux_half_rate_clk          (                           )            
);

 
//========================================================================================\
//*******************************     Debug    **********************************
//========================================================================================/
wire                            source                  ;
reg                             source_r                ;
reg                             source_r2               ;

assign  test_start      =       source_r && ~source_r2;

always @(posedge phy_clk)begin
    source_r            <=      source;
    source_r2           <=      source_r;
end

issp issp_inst(
    .probe                      (                           ),
    .source                     (source                     )
);


endmodule

测试模块代码

tb_ddr2模块:

`timescale 1ns / 1ps
`define     CLOCK   20
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : tb_ddr2.v
// Create Time  : 2020-03-06 14:09:39
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module tb_ddr2();
//System Interfaces
reg                         sclk                ;
reg                         rst_n               ;
//DDR2 Interfaces   
wire                [ 0:0]  mem_odt             ;
wire                [ 0:0]  mem_cs_n            ;
wire                [ 0:0]  mem_cke             ;
wire                [12:0]  mem_addr            ;
wire                [ 2:0]  mem_ba              ;
wire                        mem_ras_n           ;
wire                        mem_cas_n           ;
wire                        mem_we_n            ;
wire                [ 3:0]  mem_dm              ;
wire                [ 0:0]  mem_clk             ;
wire                [ 0:0]  mem_clk_n           ;
wire                [31:0]  mem_dq              ;
wire                [ 3:0]  mem_dqs             ;  
tri                 [ 3:0]  mem_dqs_n = 100'bz  ;
reg                         test_start          ;   
    

assign (weak1, weak0) mem_dqs_n = 1;

initial begin
    sclk            =       1'b0;
    rst_n           <=      1'b0;
    test_start      <=      1'b0;
    #(100*`CLOCK);
    rst_n           <=      1'b1;
    @(posedge ddr2_drive_inst.local_init_done)
    #(100*`CLOCK);
    test_start      <=      1'b1;
    #(20)
    test_start      <=      1'b0;
    #(10000);
    test_start      <=      1'b1;
    #(20)
    test_start      <=      1'b0;
end  
always  #(`CLOCK/2)     sclk    =       ~sclk;

ddr2_drive ddr2_drive_inst(
    //System Interfaces
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    //DDR2 Interfaces   
    .mem_odt                (mem_odt                ),
    .mem_cs_n               (mem_cs_n               ),
    .mem_cke                (mem_cke                ),
    .mem_addr               (mem_addr               ),
    .mem_ba                 (mem_ba                 ),
    .mem_ras_n              (mem_ras_n              ),
    .mem_cas_n              (mem_cas_n              ),
    .mem_we_n               (mem_we_n               ),
    .mem_dm                 (mem_dm                 ),
    .mem_clk                (mem_clk                ),
    .mem_clk_n              (mem_clk_n              ),
    .mem_dq                 (mem_dq                 ),
    .mem_dqs                (mem_dqs                ),
    .test_start             (test_start             )
);

ddr2_ctrl_mem_model mem_inst(
    .mem_dq                 (mem_dq                 ),
    .mem_dqs                (mem_dqs                ),
    .mem_dqs_n              (mem_dqs_n              ),
    .mem_addr               (mem_addr               ),
    .mem_ba                 (mem_ba                 ),
    .mem_clk                (mem_clk                ),
    .mem_clk_n              (mem_clk_n              ),
    .mem_cke                (mem_cke                ),
    .mem_cs_n               (mem_cs_n               ),
    .mem_ras_n              (mem_ras_n              ),
    .mem_cas_n              (mem_cas_n              ),
    .mem_we_n               (mem_we_n               ),
    .mem_dm                 (mem_dm                 ),
    .mem_odt                (mem_odt                )
);



endmodule

我们这里为了简洁同样不给出相应仿真模型的文件代码,生成DDR2 IP的时候会自带仿真模型文件,当然也可以进群自取。

仿真现象

我们进行modelsim仿真,得到的实验结果入下:
在这里插入图片描述
从上图中,我们可以看到误码个数为零,从而证明了我们程序的正确性,其中上面给出的源码是下板实验的最终代码,只需要稍微调整便可以在moselsim中运行,讲test_start不由vio产生,有tb产生即可。

上板调试

我们将对应的程序下到开发板中,我们用issp给触发信号,使用signaltap进行抓取,对应的现象如下图:
在这里插入图片描述
在这里插入图片描述
上面两张图验证了我们开始客结束的时候都没有误码,从而证明了该实验的正确性。

总结

创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。(工程也都在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

发布了42 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zhangningning1996/article/details/104733287