DDR3 MIG IP核仿真与学习

MIG IP核介绍

在Xilinx系列的FPGA中,为了方便用户对DDR进行读写,官方提供了用于访问DDR的IP核MIG,全称为Memory Interface Generator,具体可参考赛灵思官方文档参考手册:ug586(7 Series Devices Memory Interface Solutions v4.1)。下图是MIG IP核的架构,从图中可以看出,MIG主要有面向用户的端口和面向DDR的端口,用户通过使用MIG能够通过用户端口的信号,来完成对DDR SDRAM的访问,达到简化操作的目的。
DDR3 MIG IP核仿真与学习
在本文中,我们仅关注某些面向用户的端口,即以下端口:
clk,rst
app_addr,app_cmd,app_en,app_rdy
app_wdf_wren,app_wdf_end,app_wdf_rdy,app_wdf_data
app_rd_data,app_rd_data_valid
其中,第一行信号为全局的时钟和复位信号,第二行信号为命令通道信号,用于发出读写命令,第三行信号为写数据通道信号,最后一行信号为读数据通道信号。

命令通道

DDR3 MIG IP核仿真与学习
如上图所示,app_cmd用于指定是何命令,3’b000为写命令,而3’b001为读命令,app_addr为相应的读写地址,命令只会在app_en和app_rdy同时为高时,才会被接受。

读数据通道

DDR3 MIG IP核仿真与学习

如上图所示,读数据的时序比较简单,当读命令发出后,过若干时钟周期,app_rd_data_valid信号便会拉高,此时对应的app_rd_data就是读出的数据。

写数据通道

DDR3 MIG IP核仿真与学习
DDR3 MIG IP核仿真与学习

上图是一张MIG控制器的写时序图和官方对这几种写操作的解释,通过官方文档的介绍我们可以知道写入的数据可以在写命令给出之前,之时或者之后给出,但是在写命令之后给出的写数据不能超过两个时钟周期。在写命令之前给出写数据则没有这些限制。之所以能过这样操作,是因为在IP核内部有写入数据的FIFO能够对数据实现缓冲。
在本文中,我们采用1这种方式的数据写入

DDR3 MIG实验

在本实验中,我们实现了一个接口转换:将MIG用户侧的app_*接口信号转化为AXI4接口信号,以供用户更方便的进行调用。
转化的代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/01/03 10:38:32
// Design Name: 
// Module Name: mig2axi4
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module mig2axi4(
//AXI4接口
//全局信号
input logic ACLK,
input logic ARESETn,
//读地址通道
input logic [31:0] ARADDR,
input logic [7:0] ARLEN,
input logic [2:0] ARSIZE,               //2^7=128
input logic [1:0] ARBURST,
input logic [2:0] ARPROT,
input logic ARVALID,
output logic ARREADY,
//读数据通道
output logic RVALID,
output logic [127:0] RDATA,
output logic [1:0] RRESP,
output logic RLAST,
input logic RREADY,
//写地址通道                 
input logic [31:0] AWADDR,
input logic [7:0] AWLEN,
input logic [2:0] AWSIZE,
input logic [1:0] AWBURST,
input logic [2:0] AWPROT,            
input logic AWVALID,
output logic AWREADY,
//写数据通道
input logic [127:0]WDATA,
input logic [15:0] WSTRB,
input logic WLAST,
input logic WVALID,
output logic WREADY,
//写应答通道
output logic [1:0] BRESP,
output logic BVALID,
input logic BREADY,
//MIG侧接口        
input  logic ui_clk,                
input  logic ui_clk_sync_rst,       
input  logic init_calib_complete,   
input  logic app_rdy,               
input  logic app_wdf_rdy,           
input  logic app_rd_data_valid,     
input  logic [127:0]   app_rd_data,           
output logic [27:0]    app_addr,                                 
output logic app_en,                
output logic app_wdf_wren,          
output logic app_wdf_end,           
output logic [2:0]     app_cmd,               //3'b000为写数据,3'b001为读数据           
output logic [127:0]   app_wdf_data
    );

logic [31:0] rd_base_addr;                       //地址每次递增128/16=8
logic [8:0]  rd_len;
logic [31:0] wr_base_addr;
logic [8:0] wr_len;
logic [127:0] rd_buffer [0:255];                  //读数据缓存
logic [31:0] rd_cnt;                              //数据缓存中数据个数
logic [31:0] rd_cmd_cnt;                          //读命令个数
logic [31:0] wr_cnt;                             
logic [31:0] send_cnt;                            //数据发送个数
typedef enum bit [7:0] { 
    IDLE,
    READ,
    WRITE
} State;
State cur_state,next_state;
//cur_state
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
    cur_state<=IDLE;
else
    cur_state<=next_state;
//next_state
always_comb
begin
    case(cur_state)
        IDLE:if(ARVALID)
                 next_state=READ;
             else if(AWVALID)
                 next_state=WRITE;
             else
                 next_state=IDLE;
        WRITE:if(BVALID&&BREADY&&BRESP==2'b00)
                  next_state=IDLE;
              else
                  next_state=WRITE;
        READ:if(RVALID&&RREADY&&RLAST)          //最后一个数据读取完成
                 next_state=IDLE;
             else
                 next_state=READ;
        default:next_state=IDLE;
    endcase
end
/********************************************************读数据***************************************************/
//ARREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    ARREADY<=0;
else if(ARVALID&&~ARREADY)
    ARREADY<=1;
else if(ARREADY&&ARVALID)                       //读地址通道握手完成
    ARREADY<=0;
//rd_base_addr
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    rd_base_addr<=0;
else if(ARVALID&&ARREADY)
    rd_base_addr<=ARADDR;
//rd_len
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    rd_len<=0;
else if(ARVALID&&ARREADY)
    rd_len<=ARLEN+1;
//app_cmd
always_comb
if(cur_state==WRITE)
    app_cmd=3'b000;
else if(cur_state==READ)
    app_cmd=3'b001;
else
    app_cmd=3'b000;
//app_en
always_comb
case(cur_state)
     IDLE:app_en=0;
     READ:if(rd_cmd_cnt<rd_len&&app_rdy)                 //app_rdy为高且读命令次数还未到rd_len
              app_en=1;
          else
              app_en=0;
     WRITE:if(app_rdy&&app_wdf_rdy&&WVALID)               //DDR准备好接受数据,并且WDATA有效时,拉高app_en                 
              app_en=1;
           else
              app_en=0;
     default:app_en=0;
endcase
//app_addr
always_ff@(posedge ACLK)
if(ARVALID&&ARREADY)
    app_addr<=ARADDR;
else if(AWVALID&&AWREADY)
    app_addr<=AWADDR;
else if(app_en&&app_rdy)                               //写数据成功或者读命令发送成功
    app_addr<=app_addr+8;
//rd_cmd_cnt
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    rd_cmd_cnt<=0;
else if(cur_state==READ&&app_en&&app_rdy)      //发出一次读命令
    rd_cmd_cnt<=rd_cmd_cnt+1;
else if(cur_state==IDLE)
    rd_cmd_cnt<=0;
//rd_cnt
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    rd_cnt<=0;
else if(app_rd_data_valid)
    rd_cnt<=rd_cnt+1;
else if(cur_state==IDLE)
    rd_cnt<=0;
//rd_buffer
always_ff@(posedge ACLK)
if(app_rd_data_valid)
    rd_buffer[rd_cnt]<=app_rd_data;                                                //数据缓存
//send_cnt
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    send_cnt<=0;
else if(ARVALID&&ARREADY)
    send_cnt<=0;
else if(rd_cnt==rd_len&&RVALID&&RREADY)                                         //数据缓存完毕
    send_cnt<=send_cnt+1;
else if(cur_state==IDLE)
    send_cnt<=0;
//RVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    RVALID<=0;
else if(rd_cnt==rd_len&&RVALID&&RREADY&&RLAST)                    //之后send_cnt==rd_len                        
    RVALID<=0;
else if(rd_cnt==rd_len&&send_cnt<rd_len)                          
    RVALID<=1;
//RDATA
always_comb
    RDATA=rd_buffer[send_cnt];
//RLAST
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    RLAST<=0;
else if(send_cnt==rd_len-2&&RVALID&&RREADY)                        //倒数第二个数据发送完成,拉高RLAST,表明下一个数据是最后一个数据
    RLAST<=1;
else if(send_cnt==rd_len-1&&RVALID&&RREADY&&RLAST)                 //最后一个数据发送完成,拉低RLAST
    RLAST<=0;
/***************************************************************************写数据*******************************************************************/
//awready
always_ff @(posedge ACLK) 
begin    
    if(~ARESETn)
        AWREADY<=0;
    else if(AWVALID&&~AWREADY)
        AWREADY<=1;
    else if(AWVALID&&AWREADY)                //写地址通道握手完成
        AWREADY<=0;
end
//wr_len
always_ff@(posedge ACLK)
if(AWVALID&&AWREADY)
    wr_len<=AWLEN+1;
//wr_base_addr
always_ff@(posedge ACLK)
if(AWVALID&&AWREADY)
    wr_base_addr<=AWADDR;
//app_wdf_wren                                   若app_rdy和app_wdf_rdy以及WVALID均为高,则拉高app_wdf_wren和wready         (WVALID为高说明数据有效,app_rdy和app_wdf_rdy为高说明DDR可写入数据)
always_comb
if(cur_state==WRITE&&app_rdy&&app_wdf_rdy&&WVALID)
     app_wdf_wren=1;
else
     app_wdf_wren=0;
//wready
assign WREADY=app_wdf_wren;
//app_wdf_end
assign app_wdf_end=app_wdf_wren;
//app_wdf_data
assign app_wdf_data=WDATA;
//BRESP
assign BRESP=2'b00;         //OK
//BVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    BVALID<=0;
else if(WVALID&&WREADY&&WLAST)            //最后一个数据写入完成后拉高BVALID
    BVALID<=1;
else if(BVALID&&BREADY&&BRESP==2'b00)     //写响应通道握手完成
    BVALID<=0;
//BID
endmodule

我们通过MIG IP提供的DDR3仿真模型进行仿真,如下图所示,只需添加ddr3_model.svddr3_model_parameters.vh文件至仿真平台即可。
DDR3 MIG IP核仿真与学习
DDR3 MIG IP核仿真与学习
下面的代码用于产生AXI4测试信号:
axi4_master.sv

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/01/03 12:56:25
// Design Name: 
// Module Name: axi4_master
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module axi4_master(
input logic ACLK,
input logic ARESETn,
//读地址通道
output logic [31:0] ARADDR,
output logic [7:0] ARLEN,
output logic ARVALID,
input logic ARREADY,
//读数据通道
input logic [127:0] RDATA,
input logic [1:0] RRESP,
input logic RLAST,
input logic RVALID,
output logic RREADY,
//写地址通道                 
output logic [31:0] AWADDR,
output logic [7:0] AWLEN,              
output logic AWVALID,
input logic AWREADY,
//写数据通道
output logic [127:0] WDATA,
output logic [15:0] WSTRB,
output logic WLAST,
output logic WVALID,
input logic WREADY,
//写应答通道
input logic [1:0] BRESP,
input logic BVALID,
output logic BREADY,
//初始化完成信号
input  logic init_calib_complete  
    );
parameter WRITE_BASE_ADDR = 256;
parameter READ_BASE_ADDR = 256;
parameter TEST_LEN = 32-1;
logic init_calib_complete_d1;
logic start;
logic [31:0] wr_cnt;
logic [31:0] rd_cnt;
//start
assign start=init_calib_complete&(~init_calib_complete_d1);
//init_calib_complete_d1
always_ff@(posedge ACLK)
    init_calib_complete_d1<=init_calib_complete;
//写16个数据
//AWADDR
assign AWADDR=WRITE_BASE_ADDR;
//AWLEN
assign AWLEN=TEST_LEN;
//AWVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    AWVALID<=0;
else if(start)
    AWVALID<=1;
else if(AWVALID&&AWREADY)
    AWVALID<=0;
//WDATA
always_comb
    WDATA=wr_cnt;
//WSTRB
assign WSTRB=16'hffff;
//WLAST
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    WLAST<=0;
else if(WVALID&&WREADY&&wr_cnt==TEST_LEN+1-2)            //倒数第二个数据写入完成
    WLAST<=1;
else if(WVALID&&WREADY&&WLAST)                         //最后一个数据传输完成
    WLAST<=0;
//WVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
   WVALID<=0;
else if(AWVALID&&AWREADY)
   WVALID<=1;
else if(WVALID&&WREADY&&WLAST)
   WVALID<=0;
//BREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    BREADY<=0;
else if(AWVALID&&AWREADY)
    BREADY<=1;
else if(BVALID&&BREADY&&BRESP==2'b00)
    BREADY<=0;
//wr_cnt
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    wr_cnt<=0;
else if(start)
    wr_cnt<=0;
else if(WVALID&&WREADY)
    wr_cnt<=wr_cnt+1;
//读16个数据
//ARADDR
assign ARADDR=READ_BASE_ADDR;
//ARLEN
assign  ARLEN=TEST_LEN;
//ARVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    ARVALID<=0;
else if(BVALID&&BREADY)
    ARVALID<=1;
else if(ARVALID&&ARREADY)
    ARVALID<=0;
//rready
always_ff@(posedge ACLK,negedge ARESETn)
if(~ARESETn)
    RREADY<=0;
else if(ARVALID&&ARREADY)
    RREADY<=1;
else if(RVALID&&RREADY&&RLAST)
    RREADY<=0;

endmodule

整个工程的顶层文件top.sv为:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/01/03 14:22:32
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module top(
input              sys_clk,         //ϵͳʱ��
input              sys_rst_n,       //��λ,����Ч
// DDR3
inout [15:0]       ddr3_dq,         //DDR3 ����
inout [1:0]        ddr3_dqs_n,      //DDR3 dqs��
inout [1:0]        ddr3_dqs_p,      //DDR3 dqs��       
output [13:0]      ddr3_addr,       //DDR3 ��ַ
output [2:0]       ddr3_ba,         //DDR3 banck ѡ��
output             ddr3_ras_n,      //DDR3 ��ѡ��
output             ddr3_cas_n,      //DDR3 ��ѡ��
output             ddr3_we_n,       //DDR3 ��дѡ��
output             ddr3_reset_n,    //DDR3 ��λ
output [0:0]       ddr3_ck_p,       //DDR3 ʱ����
output [0:0]       ddr3_ck_n,       //DDR3 ʱ�Ӹ�
output [0:0]       ddr3_cke,        //DDR3 ʱ��ʹ��
output [0:0]       ddr3_cs_n,       //DDR3 Ƭѡ
output [1:0]       ddr3_dm,         //DDR3_dm
output [0:0]       ddr3_odt         //DDR3_odt
    );

//axi接口
logic ACLK;
logic ARESETn;
//读地址通道
logic [31:0] ARADDR;
logic [7:0] ARLEN;
logic ARVALID;
logic ARREADY;
//读数据通道
logic [127:0] RDATA;
logic [1:0] RRESP;
logic RLAST;
logic RVALID;
logic RREADY;
//写地址通道
logic [31:0] AWADDR;
logic [7:0] AWLEN;
logic AWVALID;
logic AWREADY;
//写数据通道
logic [127:0] WDATA;
logic [15:0] WSTRB;
logic WLAST;
logic WVALID;
logic WREADY;
//写应答通道
logic [1:0] BRESP;
logic BVALID;
logic BREADY;
//mig接口
logic app_rdy;               
logic app_wdf_rdy;           
logic app_rd_data_valid;     
logic [127:0] app_rd_data;           
logic [27:0] app_addr;                                 
logic app_en;                
logic app_wdf_wren;          
logic app_wdf_end;           
logic [2:0] app_cmd;               //3'b000为写数据,3'b001为读数据           
logic [127:0] app_wdf_data;
logic init_calib_complete;
//
logic clk_200;
logic ui_clk;
logic ui_clk_sync_rst;
logic locked;
//
assign ACLK=ui_clk;
assign ARESETn=~ui_clk_sync_rst;        
//ddr侧信号
// logic ddr3_addr                      (ddr3_addr),   // output [14:0]	ddr3_addr
// logic ddr3_ba                        (ddr3_ba),     // output [2:0]	ddr3_ba
// logic ddr3_cas_n                     (ddr3_cas_n),  // output		ddr3_cas_n
// logic ddr3_ck_n                      (ddr3_ck_n),   // output [0:0]	ddr3_ck_n
// logic ddr3_ck_p                      (ddr3_ck_p),   // output [0:0]	ddr3_ck_p
// logic ddr3_cke                       (ddr3_cke),    // output [0:0]	ddr3_cke
// logic ddr3_ras_n                     (ddr3_ras_n),  // output		ddr3_ras_n
// logic ddr3_reset_n                   (ddr3_reset_n),// output		ddr3_reset_n
// logic ddr3_we_n                      (ddr3_we_n),   // output		ddr3_we_n
// logic ddr3_dq                        (ddr3_dq),     // inout [31:0]	ddr3_dq
// logic ddr3_dqs_n                     (ddr3_dqs_n),  // inout [3:0]	ddr3_dqs_n
// logic ddr3_dqs_p                     (ddr3_dqs_p),  // inout [3:0]	ddr3_dqs_p
// logic ddr3_cs_n                      (ddr3_cs_n),   // output [0:0]	ddr3_cs_n
// logic ddr3_dm                        (ddr3_dm),     // output [3:0]	ddr3_dm
// logic ddr3_odt                       (ddr3_odt),    // output [0:0]	ddr3_odt

//模块例化
axi4_master U1(
.ACLK(ACLK),
.ARESETn(ARESETn),
//读地址通道
.ARADDR(ARADDR),
.ARLEN(ARLEN),
.ARVALID(ARVALID),
.ARREADY(ARREADY),
//读数据通道
.RVALID(RVALID),
.RDATA(RDATA),
.RRESP(RRESP),
.RLAST(RLAST),
.RREADY(RREADY),
//写地址通道                 
.AWADDR(AWADDR),
.AWLEN(AWLEN),              
.AWVALID(AWVALID),
.AWREADY(AWREADY),
//写数据通道
.WDATA(WDATA),
.WSTRB(WSTRB),
.WLAST(WLAST),
.WVALID(WVALID),
.WREADY(WREADY),
//写应答通道
.BRESP(BRESP),
.BVALID(BVALID),
.BREADY(BREADY),
//初始化完成信号
.init_calib_complete(init_calib_complete)  
    );


mig2axi4 U2(
//AXI4接口
//全局信号
.ACLK(ACLK),
.ARESETn(ARESETn),
//读地址通道
.ARADDR(ARADDR),
.ARLEN(ARLEN),
.ARSIZE(),               //2^7=128
.ARBURST(),
.ARPROT(),
.ARVALID(ARVALID),
.ARREADY(ARREADY),
//读数据通道
.RVALID(RVALID),
.RDATA(RDATA),
.RRESP(RRESP),
.RLAST(RLAST),
.RREADY(RREADY),
//写地址通道                 
.AWADDR(AWADDR),
.AWLEN(AWLEN),
.AWSIZE(),
.AWBURST(),
.AWPROT(),            
.AWVALID(AWVALID),
.AWREADY(AWREADY),
//写数据通道
.WDATA(WDATA),
.WSTRB(WSTRB),
.WLAST(WLAST),
.WVALID(WVALID),
.WREADY(WREADY),
//写应答通道
.BRESP(BRESP),
.BVALID(BVALID),
.BREADY(BREADY),
//MIG侧接口        
.ui_clk(ui_clk),                
.ui_clk_sync_rst(ui_clk_sync_rst),       
.init_calib_complete(init_calib_complete),   
.app_rdy(app_rdy),               
.app_wdf_rdy(app_wdf_rdy),           
.app_rd_data_valid(app_rd_data_valid),     
.app_rd_data(app_rd_data),           
.app_addr(app_addr),                                 
.app_en(app_en),                
.app_wdf_wren(app_wdf_wren),          
.app_wdf_end(app_wdf_end),           
.app_cmd(app_cmd),               //3'b000为写数据,3'b001为读数据           
.app_wdf_data(app_wdf_data)
    );

mig_7series_0 u_mig_7series_0 (
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr),   // output [14:0]	ddr3_addr
    .ddr3_ba                        (ddr3_ba),     // output [2:0]	ddr3_ba
    .ddr3_cas_n                     (ddr3_cas_n),  // output		ddr3_cas_n
    .ddr3_ck_n                      (ddr3_ck_n),   // output [0:0]	ddr3_ck_n
    .ddr3_ck_p                      (ddr3_ck_p),   // output [0:0]	ddr3_ck_p
    .ddr3_cke                       (ddr3_cke),    // output [0:0]	ddr3_cke
    .ddr3_ras_n                     (ddr3_ras_n),  // output		ddr3_ras_n
    .ddr3_reset_n                   (ddr3_reset_n),// output		ddr3_reset_n
    .ddr3_we_n                      (ddr3_we_n),   // output		ddr3_we_n
    .ddr3_dq                        (ddr3_dq),     // inout [31:0]	ddr3_dq
    .ddr3_dqs_n                     (ddr3_dqs_n),  // inout [3:0]	ddr3_dqs_n
    .ddr3_dqs_p                     (ddr3_dqs_p),  // inout [3:0]	ddr3_dqs_p
    .init_calib_complete            (init_calib_complete), 
                                                   // init_calib_complete
	.ddr3_cs_n                      (ddr3_cs_n),   // output [0:0]	ddr3_cs_n
    .ddr3_dm                        (ddr3_dm),     // output [3:0]	ddr3_dm
    .ddr3_odt                       (ddr3_odt),    // output [0:0]	ddr3_odt
    // Application interface ports
    .app_addr                       (app_addr),    // input [28:0]	app_addr
    .app_cmd                        (app_cmd),     // input [2:0]	app_cmd
    .app_en                         (app_en),      // input			app_en
    .app_wdf_data                   (app_wdf_data),// input [255:0] app_wdf_data
    .app_wdf_end                    (app_wdf_end), // input         app_wdf_end
    .app_wdf_wren                   (app_wdf_wren),// input	        app_wdf_wren
    .app_rd_data                    (app_rd_data), // output [255:0]app_rd_data
    .app_rd_data_end                (app_rd_data_end),  
                                                   // output	    app_rd_data_end
    .app_rd_data_valid              (app_rd_data_valid),  
                                                   // output	    app_rd_data_valid
    .app_rdy                        (app_rdy),     // output	    app_rdy
    .app_wdf_rdy                    (app_wdf_rdy), // output	    app_wdf_rdy
    .app_sr_req                     (1'b0),            // input	        app_sr_req
    .app_ref_req                    (1'b0),            // input	        app_ref_req
    .app_zq_req                     (1'b0),            // input	        app_zq_req
    .app_sr_active                  (app_sr_active),// output	    app_sr_active
    .app_ref_ack                    (app_ref_ack),  // output	    app_ref_ack
    .app_zq_ack                     (app_zq_ack),   // output	    app_zq_ack
    .ui_clk                         (ui_clk),       // output	    ui_clk
    .ui_clk_sync_rst                (ui_clk_sync_rst), 
                                                    // output       ui_clk_sync_rst
    .app_wdf_mask                   (31'b0),        // input [31:0]	app_wdf_mask
    // System Clock Ports
    .sys_clk_i                      (clk_200),
    // Reference Clock Ports
    .clk_ref_i                      (clk_200),
    .sys_rst                        (sys_rst_n)     // input         sys_rst
    );
    
//PLLģ�� 
clk_wiz_0 u_clk_wiz_0
   (
    // Clock out ports
    .clk_out1(clk_200),     // output clk_out1
    // Status and control signals
    .reset(1'b0),           // input resetn
    .locked(locked),        // output locked
   // Clock in ports
    .clk_in1(sys_clk)
    );        

endmodule

测试平台文件编写如下,只需例化top模块和ddr3仿真模型模块即可:
test_tb.sv

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/29 20:23:18
// Design Name: 
// Module Name: test_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module test_tb(

    );
parameter T=20;
    //
logic led;
logic sys_clk;
logic sys_rst_n;
//
wire   [13:0]		ddr3_addr;

wire   [2:0]		ddr3_ba;

wire  			ddr3_cas_n;

wire   [0:0]		ddr3_ck_n;

wire   [0:0]		ddr3_ck_p;

wire   [0:0]		ddr3_cke;

wire  			ddr3_ras_n;

wire  			ddr3_reset_n;

wire  			ddr3_we_n;

wire  [15:0]		ddr3_dq;

wire  [1:0]		ddr3_dqs_n;

wire  [1:0]		ddr3_dqs_p;

wire   [0:0]		ddr3_cs_n;

wire   [1:0]		ddr3_dm;

wire   [0:0]		ddr3_odt;
//sys_clk
initial begin
    sys_clk=0;
    forever begin
        #(T/2) sys_clk=~sys_clk;
    end
end
//sys_rst_n
initial begin
   sys_rst_n=0;
   #(10*T)
   sys_rst_n=1; 
end

//
// ddr3_rw_top U(
//    .sys_clk(sys_clk),         //
//    .sys_rst_n(sys_rst_n),       //
//     // DDR3
//    .ddr3_dq(ddr3_dq),         //
//    .ddr3_dqs_n(ddr3_dqs_n),      //
//    .ddr3_dqs_p(ddr3_dqs_p),      //   
//    .ddr3_addr(ddr3_addr),       //
//    .ddr3_ba(ddr3_ba),         //
//    .ddr3_ras_n(ddr3_ras_n),      //
//    .ddr3_cas_n(ddr3_cas_n),      //
//    .ddr3_we_n(ddr3_we_n),       //
//    .ddr3_reset_n(ddr3_reset_n),    //
//    .ddr3_ck_p(ddr3_ck_p),       //
//    .ddr3_ck_n(ddr3_ck_n),       //
//    .ddr3_cke(ddr3_cke),        //
//    .ddr3_cs_n(ddr3_cs_n),       //
//    .ddr3_dm(ddr3_dm),         //
//    .ddr3_odt(ddr3_odt),        //
//    .led(led)              //
//     ); 
top U(
.sys_clk(sys_clk),         //ϵͳʱ��
.sys_rst_n(sys_rst_n),       //��λ,����Ч
// DDR3
.ddr3_dq(ddr3_dq),         //DDR3 ����
.ddr3_dqs_n(ddr3_dqs_n),      //DDR3 dqs��
.ddr3_dqs_p(ddr3_dqs_p),      //DDR3 dqs��       
.ddr3_addr(ddr3_addr),       //DDR3 ��ַ
.ddr3_ba(ddr3_ba),         //DDR3 banck ѡ��
.ddr3_ras_n(ddr3_ras_n),      //DDR3 ��ѡ��
.ddr3_cas_n(ddr3_cas_n),      //DDR3 ��ѡ��
.ddr3_we_n(ddr3_we_n),       //DDR3 ��дѡ��
.ddr3_reset_n(ddr3_reset_n),    //DDR3 ��λ
.ddr3_ck_p(ddr3_ck_p),       //DDR3 ʱ����
.ddr3_ck_n(ddr3_ck_n),       //DDR3 ʱ�Ӹ�
.ddr3_cke(ddr3_cke),        //DDR3 ʱ��ʹ��
.ddr3_cs_n(ddr3_cs_n),       //DDR3 Ƭѡ
.ddr3_dm(ddr3_dm),         //DDR3_dm
.ddr3_odt(ddr3_odt)         //DDR3_odt
);

ddr3_model V
(
    .rst_n   (ddr3_reset_n),
    .ck      (ddr3_ck_p),
    .ck_n    (ddr3_ck_n),
    .cke     (ddr3_cke),
    .cs_n    (ddr3_cs_n),
    .ras_n   (ddr3_ras_n),
    .cas_n   (ddr3_cas_n),
    .we_n    (ddr3_we_n),
    .dm_tdqs (ddr3_dm),
    .ba      (ddr3_ba),
    .addr    (ddr3_addr),
    .dq      (ddr3_dq),
    .dqs     (ddr3_dqs_p),
    .dqs_n   (ddr3_dqs_n),
    .tdqs_n  (),
    .odt     (ddr3_odt)
    );

endmodule

下图是整个工程的RTL视图,它很好的揭示了上述代码之间的关系:
DDR3 MIG IP核仿真与学习

结果分析

AXI4写数据波形图

DDR3 MIG IP核仿真与学习

可以看到,当app_rdy、app_wdf_rdy(表明ddr已经准备好接收数据)以及WVALID信号(表示要写的数据已经有效)同时为高时,app_en、app_wdf_wren、WREADY信号均被拉高,开始写入一个数据。

AXI4读数据波形图

写命令

DDR3 MIG IP核仿真与学习
如图所示,app_cmd为3’b001,app_en,app_rdy均为高,表示该读命令已被接受,而app_addr为要读的数据的地址。

读数据

DDR3 MIG IP核仿真与学习
如图所示,MIG将读取的数据先存入缓存中,然后再将缓存中的数据通过AXI4的读数据通道发送给主机。(通过使用FIFO可以进一步增大读数据的效率)

上一篇:【Rust】子进程


下一篇:phpstorm xdebug环境搭建