该项目的描述是,FPGA向DDR3芯片写入数据,然后再读出数据,从而验证读写模块的正确性。该项目具有一定的实际意义,就是我们新制作的一块FPGA板卡,最有可能出问题的部分就是DDR芯片,因为DDR的实际属于高速设计,然后我们将对应的循环测试的程序下载进去,验证我们硬件板卡的正确性。通过本项目,我们可以学到ISE MIG的读写DDR3的方法,便可以掌握ISE操作DDR3的操作流程。由于身边没有S6系列的板卡,所以本次实验我们只做modelsim仿真。
本次实验所用到的软件环境:
1、ISE14.7软件开发环境
2、modelsim仿真环境
IP的FIFO结构图分别如下:
对于生成的MIG IP核,我们现在对其接口信号做出相应的描述,以便于大家可以充分理解信号的作用,
mig_39_2 # (
.C3_P0_MASK_SIZE (8 ),
.C3_P0_DATA_PORT_SIZE (64 ),
.C3_P1_MASK_SIZE (8 ),
.C3_P1_DATA_PORT_SIZE (64 ),
.DEBUG_EN (0 ),
.C3_MEMCLK_PERIOD (3200 ),
.C3_CALIB_SOFT_IP ("TRUE" ),
.C3_SIMULATION ("TURE" ),
.C3_RST_ACT_LOW (0 ),
.C3_INPUT_CLK_TYPE ("SINGLE_ENDED" ),
.C3_MEM_ADDR_ORDER ("BANK_ROW_COLUMN" ),
.C3_NUM_DQ_PINS (16 ),
.C3_MEM_ADDR_WIDTH (13 ),
.C3_MEM_BANKADDR_WIDTH (3 )
)
u_mig_39_2 (
.c3_sys_clk (c3_sys_clk ),
.c3_sys_rst_i (c3_sys_rst_i ),
.mcb3_dram_dq (mcb3_dram_dq ),
.mcb3_dram_a (mcb3_dram_a ),
.mcb3_dram_ba (mcb3_dram_ba ),
.mcb3_dram_ras_n (mcb3_dram_ras_n ),
.mcb3_dram_cas_n (mcb3_dram_cas_n ),
.mcb3_dram_we_n (mcb3_dram_we_n ),
.mcb3_dram_odt (mcb3_dram_odt ),
.mcb3_dram_cke (mcb3_dram_cke ),
.mcb3_dram_ck (mcb3_dram_ck ),
.mcb3_dram_ck_n (mcb3_dram_ck_n ),
.mcb3_dram_dqs (mcb3_dram_dqs ),
.mcb3_dram_dqs_n (mcb3_dram_dqs_n ),
.mcb3_dram_udqs (mcb3_dram_udqs ), // for X16 parts
.mcb3_dram_udqs_n (mcb3_dram_udqs_n ), // for X16 parts
.mcb3_dram_udm (mcb3_dram_udm ), // for X16 parts
.mcb3_dram_dm (mcb3_dram_dm ),
.mcb3_dram_reset_n (mcb3_dram_reset_n ),
.mcb3_rzq (mcb3_rzq ),
.mcb3_zio (mcb3_zio ),
.c3_clk0 (c3_clk0 ),
.c3_rst0 (c3_rst0 ),
.c3_calib_done (c3_calib_done ),
.c3_p0_cmd_clk (c3_p0_cmd_clk ),
.c3_p0_cmd_en (c3_p0_cmd_en ),
.c3_p0_cmd_instr (c3_p0_cmd_instr ),
.c3_p0_cmd_bl (c3_p0_cmd_bl ),
.c3_p0_cmd_byte_addr (c3_p0_cmd_byte_addr ),
.c3_p0_cmd_empty (c3_p0_cmd_empty ),
.c3_p0_cmd_full (c3_p0_cmd_full ),
.c3_p0_wr_clk (c3_p0_wr_clk ),
.c3_p0_wr_en (c3_p0_wr_en ),
.c3_p0_wr_mask (c3_p0_wr_mask ),
.c3_p0_wr_data (c3_p0_wr_data ),
.c3_p0_wr_full (c3_p0_wr_full ),
.c3_p0_wr_empty (c3_p0_wr_empty ),
.c3_p0_wr_count (c3_p0_wr_count ),
.c3_p0_wr_underrun (c3_p0_wr_underrun ),
.c3_p0_wr_error (c3_p0_wr_error ),
.c3_p0_rd_clk (c3_p0_rd_clk ),
.c3_p0_rd_en (c3_p0_rd_en ),
.c3_p0_rd_data (c3_p0_rd_data ),
.c3_p0_rd_full (c3_p0_rd_full ),
.c3_p0_rd_empty (c3_p0_rd_empty ),
.c3_p0_rd_count (c3_p0_rd_count ),
.c3_p0_rd_overflow (c3_p0_rd_overflow ),
.c3_p0_rd_error (c3_p0_rd_error ),
.c3_p1_cmd_clk (c3_p1_cmd_clk ),
.c3_p1_cmd_en (c3_p1_cmd_en ),
.c3_p1_cmd_instr (c3_p1_cmd_instr ),
.c3_p1_cmd_bl (c3_p1_cmd_bl ),
.c3_p1_cmd_byte_addr (c3_p1_cmd_byte_addr ),
.c3_p1_cmd_empty (c3_p1_cmd_empty ),
.c3_p1_cmd_full (c3_p1_cmd_full ),
.c3_p1_wr_clk (c3_p1_wr_clk ),
.c3_p1_wr_en (c3_p1_wr_en ),
.c3_p1_wr_mask (c3_p1_wr_mask ),
.c3_p1_wr_data (c3_p1_wr_data ),
.c3_p1_wr_full (c3_p1_wr_full ),
.c3_p1_wr_empty (c3_p1_wr_empty ),
.c3_p1_wr_count (c3_p1_wr_count ),
.c3_p1_wr_underrun (c3_p1_wr_underrun ),
.c3_p1_wr_error (c3_p1_wr_error ),
.c3_p1_rd_clk (c3_p1_rd_clk ),
.c3_p1_rd_en (c3_p1_rd_en ),
.c3_p1_rd_data (c3_p1_rd_data ),
.c3_p1_rd_full (c3_p1_rd_full ),
.c3_p1_rd_empty (c3_p1_rd_empty ),
.c3_p1_rd_count (c3_p1_rd_count ),
.c3_p1_rd_overflow (c3_p1_rd_overflow ),
.c3_p1_rd_error (c3_p1_rd_error )
);
查阅技术手册,我们对上面的信号做系列陈述:
因为我们再MIG的设置中选择了P0与P1两个64位读写通道,为了简洁起见,我们只对P0通道的数据信号进行讲解
1、C3_P0_MASK_SIZE:设置掩码的数目,一般是数据位宽的字节数
2、C3_P0_DATA_PORT_SIZE:MIG读写通信数据的位宽
3、DEBUG_EN:MIG的debug信号,我们不使用它的debug,想调试自己设置ila进行调试,一般设置为0
4、C3_MEMCLK_PERIOD:MIG IP工作的时钟周期3200ps
5、C3_CALIB_SOFT_IP:与DDR的初始化完成有关,选择TURE
6、C3_SIMULATION :仿真的时候选择TURE,下板的时候选择false即可
7、RST_ACT_LOW:1为MIG IP低复位,0为MIG IP高复位
8、C3_INPUT_CLK_TYPE:输入时钟,我们这里选择单端信号
9、C3_MEM_ADDR_ORDER:MIG中的地址与DDR硬件地址的对应
10、C3_NUM_DQ_PINS:DDR芯片数据位的数目
11、C3_MEM_ADDR_WIDTH:DDR芯片地址的位宽
12、C3_MEM_BANKADDR_WIDTH :DDR芯片bank的数目
上面的设置都与我们上篇文章中MIG的设置有关
13、c3_sys_clk:系统的参考时钟
14、c3_sys_rst_i:MIG对应的复位信号
15、所有带mcb的信号都是与DDR硬件连接的信号,具体含义参考DDR的数据手册,相信如果大家学过SDRAM控制器,对下面的信号很熟悉:
mcb3_dram_dq
mcb3_dram_a
mcb3_dram_ba
mcb3_dram_ras_n
mcb3_dram_cas_n
mcb3_dram_we_n
mcb3_dram_odt
mcb3_dram_cke
mcb3_dram_ck
mcb3_dram_ck_n
mcb3_dram_dqs
mcb3_dram_dqs_n
mcb3_dram_udqs
mcb3_dram_udqs_n
mcb3_dram_udm
mcb3_dram_dm
mcb3_dram_reset_n
mcb3_rzq
mcb3_zio
16、c3_clk0 :MIG给用户提供的时钟,可用可不用,但是vivado中相应的时钟必须使用同步
17、c3_rst0:MIG给用户提供的复位,可用可不用,但是vivado中相应的复位必须使用同步
18、c3_calib_done:MIG自我配置成功之后,该信号拉高,对DDR的操作必须等到该位拉高之后进行
19、c3_p0_cmd_clk:P0通道命令FIFO的时钟接口
20、c3_p0_cmd_en:P0通道命令FIFO的写使能端口
21、c3_p0_cmd_instr:P0通道命令FIFO的写数据,不同的编码代表不同的操作的命令
22、c3_p0_cmd_bl:P0通道每一个命令,数据FIFO突发的长度
23、c3_p1_cmd_byte_addr:DDR命令操作的起始地址,在c3_p0_cmd_en有效的时候被锁存
24、c3_p0_cmd_empty:P0通道命令FIFO的空标志
25、c3_p0_cmd_full:P0通道命令FIFO的满标志
26、c3_p0_wr_clk:P0通道数据写FIFO的时钟
27、c3_p0_wr_en:P0通道数据写FIFO的使能信号
28、c3_p0_wr_mask:P0通道数据写FIFO的数据掩码
29、c3_p0_wr_data:P0通道数据写FIFO的数据
30、c3_p0_wr_full:P0通道数据写FIFO的满信号
31、c3_p0_wr_empty:P0通道数据写FIFO的空信号
32、c3_p0_wr_count:P0通道数据写FIFO写数据计数信号
33、c3_p0_wr_underrun:高电平表示写数据FIFO中没有足够的空间供写入数据
34、c3_p0_wr_error:这个信号表明写入数据FIFO错误发生了,因为FIFO指针没有同步。需要MCB复位才能从这种情况中恢复
35、c3_p0_rd_clk:P0通道读数据FIFO的时钟接口
36、c3_p0_rd_en: P0通道数据读FIFO的使能信号
37、c3_p0_rd_data :P0通道数据读FIFO的数据
38、c3_p0_rd_full:P0通道数据读FIFO的满信号
39、c3_p0_rd_empty:P0通道数据读FIFO的空信号
40、c3_p0_rd_count:P0通道数据读FIFO读数据计数信号
41、c3_p0_rd_overflow:高电平表示读数据FIFO中没有足够的空间供写入数据
42、c3_p0_rd_error:这个信号表明读出数据FIFO错误发生了,因为FIFO指针没有同步。需要MCB复位才能从这种情况中恢复
我们从技术手册中查找写时序的图形如下:
命令FIFO的时序图:
写数据的时序图:
写数据与写命令一般按照如下顺序:
1、在执行写命令之前,一般要确保写数据FIFO中有数据
2、由1可知,先向写FIFO写完相应的数据,再向命令FIFO送写命令
读数据的时序图:
读数据与读命令一般按照如下顺序:
1、在执行读数据之前,一般要确保写数据FIFO中有数据
2、由1可知,先给命令FIFO读命令,再给出相应的读数据FIFO信号
一次循环测试的简要时序图
整个DDR芯片的循环测试
由于ISE MIG已经给封装的比较完整,我们接下来写代码,同学们可以观看代码与上面的时序图,相信大家可以理解该IP的读写逻辑
ddr3_top模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : ddr3_top.v
// Create Time : 2020-02-21 22:56: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 ddr3_top(
//System Interfaces
input c3_sys_clk ,
input c3_sys_rst_i ,
// DDR3 Interfaces
inout [15:0] mcb3_dram_dq ,
output wire [12:0] mcb3_dram_a ,
output wire [02:0] mcb3_dram_ba ,
output wire mcb3_dram_ras_n ,
output wire mcb3_dram_cas_n ,
output wire mcb3_dram_we_n ,
output wire mcb3_dram_odt ,
output wire mcb3_dram_reset_n ,
output wire mcb3_dram_cke ,
output wire mcb3_dram_dm ,
inout mcb3_dram_udqs ,
inout mcb3_dram_udqs_n ,
inout mcb3_rzq ,
inout mcb3_zio ,
output wire mcb3_dram_udm ,
inout mcb3_dram_dqs ,
inout mcb3_dram_dqs_n ,
output wire mcb3_dram_ck ,
output wire mcb3_dram_ck_n ,
//Debug
input test_trig ,
output wire [63:0] err_cnt ,
output wire err_done
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
wire c3_clk0 ;
wire c3_rst0 ;
wire c3_p0_cmd_clk ;
wire c3_p0_cmd_en ;
wire [ 2:0] c3_p0_cmd_instr ;
wire [29:0] c3_p0_cmd_byte_addr ;
wire [ 5:0] c3_p0_cmd_bl ;
wire c3_p0_wr_clk ;
wire c3_p0_wr_en ;
wire [ 7:0] c3_p0_wr_mask ;
wire [63:0] c3_p0_wr_data ;
wire [ 6:0] c3_p0_wr_count ;
wire c3_p1_cmd_clk ;
wire c3_p1_cmd_en ;
wire [ 2:0] c3_p1_cmd_instr ;
wire [29:0] c3_p1_cmd_byte_addr ;
wire [ 5:0] c3_p1_cmd_bl ;
wire c3_p1_rd_clk ;
wire c3_p1_rd_en ;
wire [63:0] c3_p1_rd_data ;
wire [ 6:0] c3_p1_rd_count ;
wire c3_calib_done ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
mig_39_2 # (
.C3_P0_MASK_SIZE (8 ),
.C3_P0_DATA_PORT_SIZE (64 ),
.C3_P1_MASK_SIZE (8 ),
.C3_P1_DATA_PORT_SIZE (64 ),
.DEBUG_EN (0 ),
.C3_MEMCLK_PERIOD (3200 ),
.C3_CALIB_SOFT_IP ("TRUE" ),
.C3_SIMULATION ("TURE" ),
.C3_RST_ACT_LOW (0 ),
.C3_INPUT_CLK_TYPE ("SINGLE_ENDED" ),
.C3_MEM_ADDR_ORDER ("BANK_ROW_COLUMN" ),
.C3_NUM_DQ_PINS (16 ),
.C3_MEM_ADDR_WIDTH (13 ),
.C3_MEM_BANKADDR_WIDTH (3 )
)
u_mig_39_2 (
.c3_sys_clk (c3_sys_clk ),
.c3_sys_rst_i (c3_sys_rst_i ),
.mcb3_dram_dq (mcb3_dram_dq ),
.mcb3_dram_a (mcb3_dram_a ),
.mcb3_dram_ba (mcb3_dram_ba ),
.mcb3_dram_ras_n (mcb3_dram_ras_n ),
.mcb3_dram_cas_n (mcb3_dram_cas_n ),
.mcb3_dram_we_n (mcb3_dram_we_n ),
.mcb3_dram_odt (mcb3_dram_odt ),
.mcb3_dram_cke (mcb3_dram_cke ),
.mcb3_dram_ck (mcb3_dram_ck ),
.mcb3_dram_ck_n (mcb3_dram_ck_n ),
.mcb3_dram_dqs (mcb3_dram_dqs ),
.mcb3_dram_dqs_n (mcb3_dram_dqs_n ),
.mcb3_dram_udqs (mcb3_dram_udqs ), // for X16 parts
.mcb3_dram_udqs_n (mcb3_dram_udqs_n ), // for X16 parts
.mcb3_dram_udm (mcb3_dram_udm ), // for X16 parts
.mcb3_dram_dm (mcb3_dram_dm ),
.mcb3_dram_reset_n (mcb3_dram_reset_n ),
.mcb3_rzq (mcb3_rzq ),
.mcb3_zio (mcb3_zio ),
.c3_clk0 (c3_clk0 ),
.c3_rst0 (c3_rst0 ),
.c3_calib_done (c3_calib_done ),
.c3_p0_cmd_clk (c3_p0_cmd_clk ),
.c3_p0_cmd_en (c3_p0_cmd_en ),
.c3_p0_cmd_instr (c3_p0_cmd_instr ),
.c3_p0_cmd_bl (c3_p0_cmd_bl ),
.c3_p0_cmd_byte_addr (c3_p0_cmd_byte_addr ),
.c3_p0_cmd_empty ( ),
.c3_p0_cmd_full ( ),
.c3_p0_wr_clk (c3_p0_wr_clk ),
.c3_p0_wr_en (c3_p0_wr_en ),
.c3_p0_wr_mask (c3_p0_wr_mask ),
.c3_p0_wr_data (c3_p0_wr_data ),
.c3_p0_wr_full ( ),
.c3_p0_wr_empty ( ),
.c3_p0_wr_count (c3_p0_wr_count ),
.c3_p0_wr_underrun ( ),
.c3_p0_wr_error ( ),
.c3_p0_rd_clk (1'b0 ),
.c3_p0_rd_en (1'b0 ),
.c3_p0_rd_data ( ),
.c3_p0_rd_full ( ),
.c3_p0_rd_empty ( ),
.c3_p0_rd_count ( ),
.c3_p0_rd_overflow ( ),
.c3_p0_rd_error ( ),
.c3_p1_cmd_clk (c3_p1_cmd_clk ),
.c3_p1_cmd_en (c3_p1_cmd_en ),
.c3_p1_cmd_instr (c3_p1_cmd_instr ),
.c3_p1_cmd_bl (c3_p1_cmd_bl ),
.c3_p1_cmd_byte_addr (c3_p1_cmd_byte_addr ),
.c3_p1_cmd_empty ( ),
.c3_p1_cmd_full ( ),
.c3_p1_wr_clk (1'b0 ),
.c3_p1_wr_en (1'b0 ),
.c3_p1_wr_mask (8'd0 ),
.c3_p1_wr_data (64'd0 ),
.c3_p1_wr_full ( ),
.c3_p1_wr_empty ( ),
.c3_p1_wr_count ( ),
.c3_p1_wr_underrun ( ),
.c3_p1_wr_error ( ),
.c3_p1_rd_clk (c3_p1_rd_clk ),
.c3_p1_rd_en (c3_p1_rd_en ),
.c3_p1_rd_data (c3_p1_rd_data ),
.c3_p1_rd_full ( ),
.c3_p1_rd_empty ( ),
.c3_p1_rd_count (c3_p1_rd_count ),
.c3_p1_rd_overflow ( ),
.c3_p1_rd_error ( )
);
ddr3_drive ddr3_drive_inst(
//System Interfaces
.sclk (c3_clk0 ),
.rst_n (c3_calib_done ),
//DDR3 Interfaces
.c3_p0_cmd_clk (c3_p0_cmd_clk ),
.c3_p0_cmd_en (c3_p0_cmd_en ),
.c3_p0_cmd_instr (c3_p0_cmd_instr ),
.c3_p0_cmd_byte_addr (c3_p0_cmd_byte_addr ),
.c3_p0_cmd_bl (c3_p0_cmd_bl ),
.c3_p0_wr_clk (c3_p0_wr_clk ),
.c3_p0_wr_en (c3_p0_wr_en ),
.c3_p0_wr_mask (c3_p0_wr_mask ),
.c3_p0_wr_data (c3_p0_wr_data ),
.c3_p0_wr_count (c3_p0_wr_count ),
.c3_p1_cmd_clk (c3_p1_cmd_clk ),
.c3_p1_cmd_en (c3_p1_cmd_en ),
.c3_p1_cmd_instr (c3_p1_cmd_instr ),
.c3_p1_cmd_byte_addr (c3_p1_cmd_byte_addr ),
.c3_p1_cmd_bl (c3_p1_cmd_bl ),
.c3_p1_rd_clk (c3_p1_rd_clk ),
.c3_p1_rd_en (c3_p1_rd_en ),
.c3_p1_rd_data (c3_p1_rd_data ),
.c3_p1_rd_count (c3_p1_rd_count ),
//Debug
.test_trig (test_trig ),
.err_cnt (err_cnt ),
.err_done (err_done )
);
endmodule
ddr3_drive模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : ddr3_drive.v
// Create Time : 2020-02-22 11:41:08
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module ddr3_drive(
//System Interfaces
input sclk ,
input rst_n ,
//DDR3 Interfaces
output c3_p0_cmd_clk ,
output wire c3_p0_cmd_en ,
output wire [ 2:0] c3_p0_cmd_instr ,
output reg [29:0] c3_p0_cmd_byte_addr ,
output wire [ 5:0] c3_p0_cmd_bl ,
output c3_p0_wr_clk ,
output reg c3_p0_wr_en ,
output wire [ 7:0] c3_p0_wr_mask ,
output reg [63:0] c3_p0_wr_data ,
input [ 6:0] c3_p0_wr_count ,
output c3_p1_cmd_clk ,
output reg c3_p1_cmd_en ,
output wire [ 2:0] c3_p1_cmd_instr ,
output reg [29:0] c3_p1_cmd_byte_addr ,
output wire [ 5:0] c3_p1_cmd_bl ,
output c3_p1_rd_clk ,
output reg c3_p1_rd_en ,
input [63:0] c3_p1_rd_data ,
input [ 6:0] c3_p1_rd_count ,
//Debug
input test_trig ,
output reg [63:0] err_cnt ,
output reg err_done
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
//parameter BURST_NUM = 1048576 ;
parameter BURST_NUM = 1000 ;
parameter BURST_LENFTH = 6'd15 ;
reg flag_done ;
reg test_trig_r ;
reg [ 3:0] wr_cnt ;
reg c3_p0_wr_en_r ;
reg c3_p1_rd_en_r ;
wire c3_p1_rd_en_nege ;
reg [20:0] burst_cnt ;
reg rd_flag ;
reg rd_cmd_flag ;
reg [ 3:0] rd_cnt ;
reg [63:0] check_data ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign c3_p0_cmd_instr = 3'b000;
assign c3_p0_cmd_bl = BURST_LENFTH;
assign c3_p0_cmd_en = ~c3_p0_wr_en && c3_p0_wr_en_r;
assign c3_p0_cmd_clk = sclk;
assign c3_p0_wr_clk = sclk;
assign c3_p0_wr_mask = 8'd0;
assign c3_p1_cmd_instr = 3'b001;
assign c3_p1_cmd_bl = BURST_LENFTH;
assign c3_p1_rd_en_nege = ~c3_p1_rd_en && c3_p1_rd_en_r;
assign c3_p1_cmd_clk = sclk;
assign c3_p1_rd_clk = sclk;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_done <= 1'b0;
else if(burst_cnt == BURST_NUM - 1'b1 && c3_p1_rd_en_nege == 1'b1)
flag_done <= 1'b0;
else if(test_trig == 1'b1)
flag_done <= 1'b1;
else
flag_done <= flag_done;
always @(posedge sclk)
test_trig_r <= test_trig;
always @(posedge sclk)
c3_p1_rd_en_r <= c3_p1_rd_en;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
c3_p0_wr_en <= 1'b0;
else if(flag_done == 1'b1 && test_trig_r == 1'b1)
c3_p0_wr_en <= 1'b1;
else if(c3_p1_rd_en_nege == 1'b1 && burst_cnt < BURST_NUM - 1'b1)
c3_p0_wr_en <= 1'b1;
else if(wr_cnt == 4'd15)
c3_p0_wr_en <= 1'b0;
else
c3_p0_wr_en <= c3_p0_wr_en;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_cnt <= 4'd0;
else if(c3_p0_wr_en == 1'b1)
wr_cnt <= wr_cnt + 1'b1;
else
wr_cnt <= 4'd0;
always @(posedge sclk)
c3_p0_wr_en_r <= c3_p0_wr_en;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_cmd_flag <= 1'b0;
else if(c3_p0_cmd_en == 1'b1)
rd_cmd_flag <= 1'b1;
else if(rd_flag == 1'b1 && c3_p0_wr_count == 7'd0)
rd_cmd_flag <= 1'b0;
else
rd_cmd_flag <= rd_cmd_flag;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
c3_p1_cmd_en <= 1'b0;
else if(rd_cmd_flag == 1'b1 && c3_p0_wr_count == 7'd0)
c3_p1_cmd_en <= 1'b1;
else
c3_p1_cmd_en <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_flag <= 1'b0;
else if(c3_p0_cmd_en == 1'b1)
rd_flag <= 1'b1;
else if(rd_cnt == 4'd15)
rd_flag <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
c3_p1_rd_en <= 1'b0;
else if(rd_cnt == 4'd15)
c3_p1_rd_en <= 1'b0;
else if(rd_flag == 1'b1 && c3_p1_rd_count == BURST_LENFTH + 1)
c3_p1_rd_en <= 1'b1;
else
c3_p1_rd_en <= c3_p1_rd_en;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_cnt <= 4'd0;
else if(c3_p1_rd_en == 1'b1)
rd_cnt <= rd_cnt + 1'b1;
else
rd_cnt <= 4'd0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
c3_p0_wr_data <= 64'd0;
else if(c3_p0_wr_en == 1'b1 && flag_done == 1'b1)
c3_p0_wr_data <= c3_p0_wr_data + 1'b1;
else
c3_p0_wr_data <= c3_p0_wr_data;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
check_data <= 64'd0;
else if(c3_p1_rd_en == 1'b1 && flag_done == 1'b1)
check_data <= check_data + 1'b1;
else
check_data <= check_data;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
err_cnt <= 21'd0;
else if(test_trig == 1'b1)
err_cnt <= 21'd0;
else if(c3_p1_rd_en == 1'b1 && check_data != c3_p1_rd_data)
err_cnt <= err_cnt + 1'b1;
else
err_cnt <= err_cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
burst_cnt <= 21'd0;
else if(burst_cnt == BURST_NUM - 1'b1 && c3_p1_rd_en_nege == 1'b1)
burst_cnt <= 21'd0;
else if(c3_p1_rd_en_nege == 1'b1)
burst_cnt <= burst_cnt + 1'b1;
else
burst_cnt <= burst_cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
err_done <= 1'b0;
else if(burst_cnt == BURST_NUM - 1'b1 && c3_p1_rd_en_nege == 1'b1)
err_done <= 1'b1;
else
err_done <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
c3_p0_cmd_byte_addr <= 30'd0;
else if(burst_cnt == BURST_NUM - 1'b1 && c3_p0_cmd_en == 1'b1)
c3_p0_cmd_byte_addr <= 30'd0;
else if(c3_p0_cmd_en == 1'b1)
c3_p0_cmd_byte_addr <= c3_p0_cmd_byte_addr + 30'd128;
else
c3_p0_cmd_byte_addr <= c3_p0_cmd_byte_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
c3_p1_cmd_byte_addr <= 30'd0;
else if(burst_cnt == BURST_NUM - 1'b1 && c3_p1_cmd_en == 1'b1)
c3_p1_cmd_byte_addr <= 30'd0;
else if(c3_p1_cmd_en == 1'b1)
c3_p1_cmd_byte_addr <= c3_p1_cmd_byte_addr + 30'd128;
else
c3_p1_cmd_byte_addr <= c3_p1_cmd_byte_addr;
endmodule
这里从ddr3_drive联合上面的时序内容与接口信号的描述,相信大家可以很快学会ISE中MIG的使用方法。
tb_ddr3_top模块的代码:
`timescale 1ps / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : tb_ddr3_top.v
// Create Time : 2020-02-21 23:21:37
// 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_ddr3_top;
parameter C3_MEMCLK_PERIOD = 3200 ;
wire [12:0] mcb3_dram_a ;
wire [02:0] mcb3_dram_ba ;
wire mcb3_dram_ck ;
wire mcb3_dram_ck_n ;
wire [15:0] mcb3_dram_dq ;
wire mcb3_dram_dqs ;
wire mcb3_dram_dqs_n ;
wire mcb3_dram_dm ;
wire mcb3_dram_ras_n ;
wire mcb3_dram_cas_n ;
wire mcb3_dram_we_n ;
wire mcb3_dram_cke ;
wire mcb3_dram_odt ;
wire mcb3_dram_reset_n ;
wire mcb3_dram_udqs ; // for X16 parts
wire mcb3_dram_udqs_n ; // for X16 parts
wire mcb3_dram_udm ; // for X16 parts
wire mcb3_rzq ;
wire mcb3_zio ;
reg c3_sys_clk ;
reg c3_sys_rst_i ;
//Debug
reg test_trig ;
wire [63:0] err_cnt ;
wire err_done ;
initial c3_sys_clk = 1'b0;
always #(C3_MEMCLK_PERIOD/2) c3_sys_clk = ~c3_sys_clk;
initial begin
c3_sys_rst_i <= 1'b1;
test_trig <= 1'b0;
#(100*C3_MEMCLK_PERIOD);
c3_sys_rst_i <= 1'b0;
#(500_000_000);
test_trig <= 1'b1;
#(6*C3_MEMCLK_PERIOD);
test_trig <= 1'b0;
#(2_000_000_000);
test_trig <= 1'b1;
#(6*C3_MEMCLK_PERIOD);
test_trig <= 1'b0;
#(2_000_000_000);
$stop;
end
PULLDOWN zio_pulldown3 (.O(mcb3_zio) );
PULLDOWN rzq_pulldown3 (.O(mcb3_rzq) );
ddr3_top ddr3_top_inst(
// DDR3 Interfaces
.mcb3_dram_dq (mcb3_dram_dq ),
.mcb3_dram_a (mcb3_dram_a ),
.mcb3_dram_ba (mcb3_dram_ba ),
.mcb3_dram_ras_n (mcb3_dram_ras_n ),
.mcb3_dram_cas_n (mcb3_dram_cas_n ),
.mcb3_dram_we_n (mcb3_dram_we_n ),
.mcb3_dram_odt (mcb3_dram_odt ),
.mcb3_dram_reset_n (mcb3_dram_reset_n ),
.mcb3_dram_cke (mcb3_dram_cke ),
.mcb3_dram_dm (mcb3_dram_dm ),
.mcb3_dram_udqs (mcb3_dram_udqs ),
.mcb3_dram_udqs_n (mcb3_dram_udqs_n ),
.mcb3_rzq (mcb3_rzq ),
.mcb3_zio (mcb3_zio ),
.mcb3_dram_udm (mcb3_dram_udm ),
.c3_sys_clk (c3_sys_clk ),
.c3_sys_rst_i (c3_sys_rst_i ),
.mcb3_dram_dqs (mcb3_dram_dqs ),
.mcb3_dram_dqs_n (mcb3_dram_dqs_n ),
.mcb3_dram_ck (mcb3_dram_ck ),
.mcb3_dram_ck_n (mcb3_dram_ck_n ),
//Debug
.test_trig (test_trig ),
.err_cnt (err_cnt ),
.err_done (err_done )
);
ddr3_model_c3 u_mem_c3(
.ck (mcb3_dram_ck ),
.ck_n (mcb3_dram_ck_n ),
.cke (mcb3_dram_cke ),
.cs_n (1'b0 ),
.ras_n (mcb3_dram_ras_n ),
.cas_n (mcb3_dram_cas_n ),
.we_n (mcb3_dram_we_n ),
.dm_tdqs ({mcb3_dram_udm,mcb3_dram_dm} ),
.ba (mcb3_dram_ba ),
.addr (mcb3_dram_a ),
.dq (mcb3_dram_dq ),
.dqs ({mcb3_dram_udqs,mcb3_dram_dqs} ),
.dqs_n ({mcb3_dram_udqs_n,mcb3_dram_dqs_n}),
.tdqs_n ( ),
.odt (mcb3_dram_odt ),
.rst_n (mcb3_dram_reset_n )
);
endmodule
这里DDR3模型文件和相应的配置文件的代码不再给出,这部分代码是从DDR3的示例工程中获得的,在上一篇文章中也给出了模型文件的代码。当然也可以进群获得。
从图形中,我们可以看出我们读写测试了两次,均没有发生错误,这验证了我们程序的正确性。
创作不易,认为文章有帮助的同学们可以关注点赞支持。(工程也都在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: