QuartusII 中基于DDR2 IP完成DDR2的循环测试
项目简述
该项目的描述是,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 : nnzhang1996@foxmail.com
// 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 : nnzhang1996@foxmail.com
// 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 : nnzhang1996@foxmail.com
// 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进行抓取,对应的现象如下图:
上面两张图验证了我们开始客结束的时候都没有误码,从而证明了该实验的正确性。
总结
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。(工程也都在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: