基于Vivado MIG IP核的DDR3读写实验(top_rom_ddr/ddr_top)


基于Vivado MIG IP核的DDR3控制器(DDR3_CONTROL)

  • 关于MIG IP核的用户端的接口时序可以参考这篇文章:XILINX 的 MIG IP(非AXI4)接口时序以及控制

  • 二、实验内容

    本次实验的内容主要是通过MIG IP核向DDR3读写数据,DDR3的接口时序由ddr_top模块提供:

    • ddr_top模块的数据来源是wr_fifo,wr_fifo的数据实际来自top_sd_photo模块(本实验仿真时在tb文件中手动提供数据)
    • ddr_top模块的数据输出到rd_fifo,rd_fifo的数据被timing_gen模块读出(本实验只输出观察)。

    top_sd_phototiming_gen模块在本专栏中前面的文章中都介绍过,这里就不说了,重点只是如何提供MIG IP核用户端的接口时序,从而对DDR3完成读写操作,也即ddr_top模块的设计。

    1、顶层模块:
    在这里插入图片描述
    2、ddr_top模块:
    在这里插入图片描述

    • mem_test模块:发送读写请求
    • mem_burst模块:提供MIG IP核用户端的接口时序
    • DDR3_CONTROL:MIG IP核,用来将用户端的读写时序转换成直接提供给DDR3的读写时序

    三、程序设计

    这里我们只讲解ddr_top中的三个子模块。

    1、mem_test 模块:就是利用状态机生成读写请求、读写数据长度、读写起始地址的信号,并发送到mem_burst模块。

    module mem_test
    #(
    	parameter MEM_DATA_BITS = 256, //8突发,8*32=256
    	parameter ADDR_BITS = 29
    )
    (
        input rd_fifo_full,							  	  //来自rd_fifo的满信号
    	input rst,                                        //复位
    	input mem_clk,                                    //来自mig IP核的用户时钟
    
    	//mem_burst连接信号
    	input wr_burst_data_req,                          //ddr写数据信号
    	input rd_burst_finish,                            //ddr读完成
    	input wr_burst_finish,                            //ddr写完成
    	output reg rd_burst_req,                          //ddr读请求
    	output reg wr_burst_req,                          //ddr写请求
    	output reg[15:0] rd_burst_len,                    //读ddr数据长度
    	output reg[15:0] wr_burst_len,                    //写ddr数据长度
    	output reg[ADDR_BITS - 1:0] rd_burst_addr,        //读ddr首地址
    	output reg[ADDR_BITS - 1:0] wr_burst_addr,        //写ddr首地址
    	
    	//output[MEM_DATA_BITS - 1:0] wr_burst_data,        //输出给ddr的测试数据 正常应该是wr_fifo给数据
        output reg read_ddr	//输出给外部,用来标志rd_fifo可以读取的信号
    );
    localparam IDLE = 3'd0;
    localparam MEM_WRITE  = 3'd1;
    localparam MEM_READ = 3'd2;
    
    
    reg[2:0] state;
    always@(posedge mem_clk or posedge rst)
    begin
    	if(rst)
    	begin
    		state <= IDLE;
    		wr_burst_req <= 1'b0;
    		rd_burst_req <= 1'b0;
    		rd_burst_len <= 16'd0;          	
    		wr_burst_len <= 16'd0;			//512*512/8 = 32768个数一次写入
    		rd_burst_addr <= 0;                 //读写起始地址均为0
    		wr_burst_addr <= 0;
    		read_ddr <= 0;
    	end
    	else
    	begin
    		case(state)
    			IDLE:
    			begin
    				state <= MEM_WRITE;         //复位后进入写操作,将写请求拉高,发送到mem_burst模块
    				
    				wr_burst_req <= 1'b1;
    				wr_burst_len <= 16'd30;
    				wr_burst_addr <= 29'h0;
    			end
    			MEM_WRITE:
    			begin
    				if(wr_burst_finish)//写突发结束进入读突发阶段
    				begin
    					state <= MEM_READ;     //检测到wr_burst_finish后,将状态切换至read状态
    					wr_burst_req <= 1'b0;  //写请求拉低,读请求拉高,给出读数据个数,并给出读的起始地址,发送到mem_burst模块
    					rd_burst_len <= 16'd30;
    					rd_burst_addr <= 29'h0;
    				end
    			end
    			MEM_READ:
    			begin
    				if(rd_burst_finish)//读突发结束拉高read_ddr,告诉外部可以读取rd_fifo了
    								   //因为从ddr读出的数据会先缓存到rd_fifo中
    				begin
    					read_ddr <= 1;
    					rd_burst_req <= 1'b0;				
    				end
    				else if(!rd_fifo_full)
    					rd_burst_req <= 1'b1;    
    				else 
    					rd_burst_req <= 0;
    			end
    			default:
    				state <= IDLE;
    		endcase
    	end
    end
    
    endmodule
    

    2、mem_burst模块:也是利用状态机以及其他逻辑生成MIG IP核用户端所需的各种信号。

    module mem_burst
    #(
    	parameter MEM_DATA_BITS = 256,//8突发,8*32=256
    	parameter ADDR_BITS = 29
    )
    (
    	input rst,                                   
    	input mem_clk,                               /*来自mig IP核的用户时钟*/
    
    	//mem_test模块的连接信号
    	input rd_burst_req,                          /*读ddr请求*/
    	input wr_burst_req,                          /*写ddr请求*/ 
    	input[15:0] rd_burst_len,                    /*读数据长度*/
    	input[15:0] wr_burst_len,                    /*写数据长度*/
    	input[ADDR_BITS - 1:0] rd_burst_addr,        /*读首地址*/
    	input[ADDR_BITS - 1:0] wr_burst_addr,        /*写首地址*/
    	output rd_burst_finish,                      /*读完成*/
    	output wr_burst_finish,                      /*写完成*/
    
    
    	//wr_fifo的连接信号
    	input wr_fifo_empty,						 /*来自wr_fifo的fifo空信号,因为要向该fifo读数据,所以关注空信号*/
    	input[MEM_DATA_BITS - 1:0] wr_burst_data,    /*来自wr_fifo的要写入ddr的数据*/
    	output  wr_burst_data_req,                	 /*输出给wr_fifo的数据请求信号,作为其rd_en信号*/
    
    	//rd_fifo的连接信号
    	input rd_fifo_full,						 	 /*来自rd_fifo的满信号,因为要往该fifo写数据,所以关注满信号*/
    	output rd_burst_data_valid,                  /*数据有效信号,作为rd_fifo的wr_en信号*/
    	output[MEM_DATA_BITS - 1:0] rd_burst_data,   /*ddr读出的要写入rd_fifo的数据*/
    	
    	
    	
    	//下面的信号输出给mig IP核
    	output[ADDR_BITS-1:0]                       app_addr,
    	output[2:0]                                 app_cmd,
    	output                                      app_en,
    	output [MEM_DATA_BITS-1:0]                  app_wdf_data,
    	output                                      app_wdf_end,
    	output [MEM_DATA_BITS/8-1:0]                app_wdf_mask,
    	output                                      app_wdf_wren,
    	
    	//下面的信号来自mig IP核
    	input [MEM_DATA_BITS-1:0]                   app_rd_data,
    	input                                       app_rd_data_end,
    	input                                       app_rd_data_valid,
    	input                                       app_rdy,
    	input                                       app_wdf_rdy,
    	input                                       init_calib_complete,
       
       	output reg write_done
    );
    
    assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};
    
    
    localparam IDLE = 3'd0;
    localparam MEM_READ = 3'd1;
    localparam MEM_READ_WAIT = 3'd2;
    localparam MEM_WRITE  = 3'd3;
    localparam MEM_WRITE_WAIT = 3'd4;
    localparam READ_END = 3'd5;
    localparam WRITE_END = 3'd6;
    
    
    reg[2:0]  state;	
    reg[15:0] rd_addr_cnt;
    reg[15:0] rd_data_cnt;
    reg[15:0] wr_addr_cnt;
    reg[15:0] wr_data_cnt;
    
    reg[2:0] app_cmd_r;
    reg[ADDR_BITS-1:0] app_addr_r;
    reg app_en_r;
    reg app_wdf_wren_r;
    
    
    /**********************************************************************************/
    //读写突发结束标志
    assign rd_burst_finish = (state == READ_END);
    assign wr_burst_finish = (state == WRITE_END);
    /**********************************************************************************/
    //读取的ddr的数据要输出给rd_fifo,信号来自mig IP核的数据输出端口
    assign rd_burst_data = app_rd_data;
    assign rd_burst_data_valid = app_rd_data_valid;
    /**********************************************************************************/
    
    always @(posedge mem_clk or posedge rst)
    begin
    	if (rst)
    		write_done <= 0;
    	else if(wr_burst_finish)
    		write_done <=1;
    	else 
    		write_done <= write_done;
    end
    

    重点是以下信号的生成:

    1、app_en信号的生成逻辑:

    • 写数据时,当app_rdy和wr_burst_data_req有效(包含app_wdf_rdy有效)时拉高app_en信号,这是因为app_rdy和app_en同时有效时命令和地址可以被写入,所以干脆让app_en跟随app_rdy。
    • 但又加了一个wr_burst_data_req信号,这是因为由于数据来自fifo,fifo存在空满现象,也即有时读数据这个动作并不是连续的。既然此时不读新数据,那么app_en也拉低以避免命令和地址的写入。
    assign app_en = app_en_r;
    
    always @(*) begin
    	if (state == MEM_WRITE) begin
    		app_en_r <= app_rdy && wr_burst_data_req;
    	end
    	else if (state == MEM_WRITE_WAIT) begin
    		app_en_r <= app_rdy;
    	end
    	else if (state == MEM_READ) begin
    		app_en_r <= app_rdy && !rd_fifo_full;
    	end
    	else
    		app_en_r <= 0;
    end
    

    2、写数据时的app_wdf_wren信号的生成逻辑:

    • 由于读数据请求信号wr_burst_data_req有效时,读出的数据会延迟一拍,所以刚好也让app_wdf_wren_r比wr_burst_data_req延迟一个周期,这样读出的数据wr_burst_data和app_wdf_wren_r同步。
    • 由于wr_burst_data_req有效包含app_wdf_rdy有效,也符合app_wdf_rdy和app_wdf_wren都有效时,数据wr_burst_data才能被写入的逻辑。
    //当app_wdf_rdy和app_wdf_wren_r都有效时,IP核才会接收到用户端发送的app_wdf_data
    assign app_wdf_wren = wr_burst_finish? 0 : app_wdf_wren_r;
    assign app_wdf_end = app_wdf_wren;
    
    always@(posedge mem_clk)
    begin
    	if (wr_data_cnt < wr_burst_len) begin
    		//app_wdf_wren_r比wr_burst_data_req延迟一个周期
    		//wr_burst_data_req连接到wr_fifo的rd_en,刚好读出的wr_burst_data和app_wdf_wren_r同步
    		app_wdf_wren_r <= wr_burst_data_req;
    	end
    	/*
    	else if (wr_data_cnt == wr_burst_len) begin
    		app_wdf_wren_r <= 0;
    	end*/
    	else 
    		app_wdf_wren_r <= 0;
    end
    

    3、wr_burst_data_req(也即app_wdf_data)的生成逻辑:处于写状态、mig IP核写空闲信号有效、wr_fifo中有数据,上述条件同时满足时就拉高wr_burst_data_req,开始请求读取wr_fifo中的数据wr_burst_data(app_wdf_data)

    assign app_wdf_data = wr_burst_data;
    
    //也可以再加一个条件:app_rdy拉高,但是由于该信号会忽高忽低,所以读出的数据也不连续,根据实际情况处理。
    assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy & !wr_fifo_empty;
    

    4、状态机控制 app_cmd、app_addr的生成逻辑:这两个就一直有效即可,在一次写入或者读取完成之后,变化到下一个地址或者命令即可。

    • 读操作有效:app_rdy有效、app_en有效、并且rd_fifo不满即可。
    • 写操作有效:app_rdy有效、app_en有效、app_wdf_rdy有效、wr_fifo不空;最后两个信号有效其实也就是wr_burst_data_req有效,而wr_burst_data_req有效时,wr_burst_data和app_wdf_wren刚好也有效;这样刚好在命令和地址有效写入时,数据也有效可以被写入。
    assign app_cmd = app_cmd_r;
    assign app_addr = app_addr_r;
    
    always@(posedge mem_clk or posedge rst)
    begin
    	if(rst)
    	begin
    		state <= IDLE;
    		app_cmd_r <= 3'b000;
    		app_addr_r <= 0;
    		app_en_r <= 1'b0;
    		rd_addr_cnt <= 0;
    		rd_data_cnt <= 0;
    		wr_addr_cnt <= 0;
    		wr_data_cnt <= 0;
    		app_wdf_wren_r <= 1'b0;
    	end
    	else if(init_calib_complete == 1'b1)//DDR3复位完成
    	begin
    		case(state)
    			IDLE:  //复位状态、一次写完成、一次读完成均会回到IDLE状态
    			begin
    				if(rd_burst_req)//检测到rd_burst_req 信号后,进入读状态,
    								//发送读命令、读起始地址、使能信号app_en 到 DDR_CONTROL
    				begin
    					state <= MEM_READ;   //进入读状态
    					
    					//下面三个信号一般都是一块给出
    					app_cmd_r <= 3'b001; //发送读命令
    					app_addr_r <= rd_burst_addr;//发送读初始地址
    					app_en_r <= 1'b1;//发送命令使能
    
    					rd_addr_cnt <= 0;
    					rd_data_cnt <= 0;
    				end	
    				else if(wr_burst_req)    //检测到wr_burst_req 信号后,进入写状态,发送 写命令、写起始地址、 使能信号app_en 到DDR_CONTROL
    				begin
    					state <= MEM_WRITE;  //进入写状态
    
    					//下面三个信号一般都是一块给出
    					app_cmd_r <= 3'b000; //发送写命令
    					app_addr_r <= wr_burst_addr;//发送写初始地址
    					app_en_r <= 1'b1;//发送命令使能
    
    					wr_addr_cnt <= 0;
    					wr_data_cnt <= 0;
    				end
    			end
    			MEM_READ:
    			begin
    				if(app_rdy && !rd_fifo_full)//等待DDR_CONTROL的app_ready 信号拉高
    				begin
    					app_addr_r <= app_addr_r + 8; //地址每次加8
    					
    					if(rd_addr_cnt == rd_burst_len - 1)  //检测读地址是否将要写完成
    					begin
    						//因为写地址完成肯定在数据读取完成之前,所以计数时前者先结束
    						//但此时不能直接进入end状态,所以就定义了一个MEM_READ_WAIT状态
    						state <= MEM_READ_WAIT;
    						rd_addr_cnt <= 0;	         
    						//app_en_r <= 1'b0; //地址写完成后拉低使能信号app_en
    					end
    					else
    						rd_addr_cnt <= rd_addr_cnt + 1; //每写一次地址,加1,用于检测写入的读地址的个数
    				end
    				
    				if(app_rd_data_valid)//读出数据有效信号
    				begin
    					if(rd_data_cnt == rd_burst_len - 1)//检测读出的数据的个数
    					begin
    						rd_data_cnt <= 0;
    						state <= READ_END;//读完成后跳转至READ_END状态
    					end
    					else
    					begin
    						rd_data_cnt <= rd_data_cnt + 1;
    					end
    				end
    			end
    			MEM_READ_WAIT:
    			begin
    				if(app_rd_data_valid)
    				begin
    					if(rd_data_cnt == rd_burst_len - 1)
    					begin
    						rd_data_cnt <= 0;
    						state <= READ_END;
    					end
    					else
    					begin
    						rd_data_cnt <= rd_data_cnt + 1;
    					end
    				end
    			end
    			
    			MEM_WRITE:
    			begin
    			/*
    			
    				正常情况下,只要app_rdy和app_wdf_rdy都有效,然后写相关信号与数据都有效就可以完成写操作了。
    				但是这里是要求wr_burst_data_req有效(其中包含app_wdf_rdy有效),也即多了个wr_fifo是否为空
    				的信号,这是因为写数据来自wr_fifo,当没有数据可写的时候就保持cmd、addr不变,然后将app_en
    				和app_wdf_wren拉低即可,表示此时不进行写操作了,这里没有直接拉低app_wdf_wren,但由于
    				app_wdf_wren = wr_burst_data_req,所以,由于此时wr_burst_data_req也为低,因此app_wdf_wren也拉低了。
    
    				
    			*/
    				if(app_rdy && wr_burst_data_req)
    				begin
    					//app_en_r <= 1'b1;
    					app_addr_r <= app_addr_r + 8;   //地址每次加8
    					if(wr_addr_cnt == wr_burst_len - 1)  //检测写地址是否将要写完成
    					begin 
    						wr_addr_cnt <= 0; 
    						//app_en_r <= 1'b0;//地址写完成后拉低使能信号app_en
    					end
    					else
    					begin
    						wr_addr_cnt <= wr_addr_cnt + 1;  //每写一次地址,加1,用于检测写入的写地址的个数
    					end
    				end
    				else
    					app_en_r <= 1'b0;
    						
    				if(wr_burst_data_req)
    				begin
    					
    					if(wr_data_cnt == wr_burst_len - 1) //检测写数据是否将要完成
    					begin
    						wr_data_cnt <= 0; 
    						state <= MEM_WRITE_WAIT;//数据写完成后,跳转至MEM_WRITE_WAIT状态,等待所有地址发送完毕
    					end
    					else
    					begin
    						wr_data_cnt <= wr_data_cnt + 1; //每写一次数据,加1,用于检测写数据的个数
    					end
    				end
    				
    			end
    			READ_END:
    				begin
    					state <= IDLE; //返回初始状态
    					app_addr_r <=0;
    				end
    			
    			MEM_WRITE_WAIT:
    			begin
    				if(app_rdy)
    				begin
    					//app_en_r <= 1'b1;
    					app_addr_r <= app_addr_r + 8; //等待所有地址发送完毕
    					if(wr_addr_cnt == wr_burst_len - 1)
    					begin
    						wr_addr_cnt <= 0; 
    						//app_en_r <= 1'b0;
    						
    						state <= WRITE_END;    //写数据结束,拉高wr_burst_finish,代表本次突发写数据结束,将wr_burst_finish发送到mem_test模块
    					end						   //在mem_test模块中,检测到wr_burst_finish后,将状态切换至read状态
    					else
    					begin
    						wr_addr_cnt <= wr_addr_cnt + 1;
    					end
    				end
    				
    			end
    			WRITE_END:
    				begin
    					state <= IDLE; //返回初始状态
    					app_addr_r <=0;
    				end
    			default:
    				state <= IDLE;
    		endcase
    	end
    end
    endmodule 
    

    3、ddr_top模块:就是将三个子模块连接起来。

    
    //`define SKIP_CALIB
    `timescale 1ps/1ps
    
     /*module ddr_top #
      (
        
       //***************************************************************************
       // The following parameters refer to width of various ports
       //***************************************************************************
       parameter CK_WIDTH              = 1, //ck时钟信号的宽度
                                         // # of CK/CK# outputs to memory.
       parameter nCS_PER_RANK          = 1, //每个RANK的片选信号数量
                                         // # of unique CS outputs per rank for phy
       parameter CKE_WIDTH             = 1, //ck时钟有效信号的宽度
                                         // # of CKE outputs to memory.
       parameter DM_WIDTH              = 4, //数据掩码的宽度 一位控制一个字节
                                         // # of DM (data mask)
       parameter ODT_WIDTH             = 1, //片上终端使能信号的宽度
                                         // # of ODT outputs to memory.
       parameter BANK_WIDTH            = 3, //bank地址的宽度
                                         // # of memory Bank Address bits.
       parameter COL_WIDTH             = 10,//列地址宽度
                                         // # of memory Column Address bits.
       parameter CS_WIDTH              = 1, //片选信号的宽度
                                         // # of unique CS outputs to memory.
       parameter DQ_WIDTH              = 32, //数据宽度
                                         // # of DQ (data)
       parameter DQS_WIDTH             = 4, //数据选取脉冲的宽度 一位对应一个字节
       parameter DQS_CNT_WIDTH         = 2,
                                         // = ceil(log2(DQS_WIDTH))
       parameter DRAM_WIDTH            = 8,
                                         // # of DQ per DQS
       parameter ECC                   = "OFF",
       parameter ECC_TEST              = "OFF",
       
       parameter nBANK_MACHS           = 4,//管理bank的machine的数量,一个machine管理一个DRAM bank
                                           
       parameter RANKS                 = 1,//RANK的数量,每64bit为一个RANK
                                         // # of Ranks.
       parameter ROW_WIDTH             = 15, //行地址的宽度
                                         // # of memory Row Address bits.
       parameter ADDR_WIDTH            = 29,
                                         // # = RANK_WIDTH + BANK_WIDTH 1+3+15+10
                                         //     + ROW_WIDTH + COL_WIDTH;
                                         // Chip Select is always tied to low for
                                         // single rank devices
    
       //***************************************************************************
       // The following parameters are mode register settings
       //***************************************************************************
       parameter BURST_MODE            = "8",//突发模式
                                         // DDR3 SDRAM:
                                         // Burst Length (Mode Register 0).
                                         // # = "8", "4", "OTF".
                                         // DDR2 SDRAM:
                                         // Burst Length (Mode Register).
                                         // # = "8", "4".
    
       
       //***************************************************************************
       // The following parameters are multiplier and divisor factors for PLLE2.
       // Based on the selected design frequency these parameters vary.
       //***************************************************************************
       parameter CLKIN_PERIOD          = 5000,
                                         // Input Clock Period
       parameter CLKFBOUT_MULT         = 8,
                                         // write PLL VCO multiplier
       parameter DIVCLK_DIVIDE         = 1,
                                         // write PLL VCO divisor
       parameter CLKOUT0_PHASE         = 337.5,
                                         // Phase for PLL output clock (CLKOUT0)
       parameter CLKOUT0_DIVIDE        = 2,
                                         // VCO output divisor for PLL output clock (CLKOUT0)
       parameter CLKOUT1_DIVIDE        = 2,
                                         // VCO output divisor for PLL output clock (CLKOUT1)
       parameter CLKOUT2_DIVIDE        = 32,
                                         // VCO output divisor for PLL output clock (CLKOUT2)
       parameter CLKOUT3_DIVIDE        = 8,
                                         // VCO output divisor for PLL output clock (CLKOUT3)
       parameter MMCM_VCO              = 800,
                                         // Max Freq (MHz) of MMCM VCO
       parameter MMCM_MULT_F           = 4,
                                         // write MMCM VCO multiplier
       parameter MMCM_DIVCLK_DIVIDE    = 1,
                                         // write MMCM VCO divisor
    
       //***************************************************************************
       // Simulation parameters
       //***************************************************************************
       parameter SIMULATION            = "FALSE",
                                         // Should be TRUE during design simulations and
                                         // FALSE during implementations
    
       //***************************************************************************
       // IODELAY and PHY related parameters
       //***************************************************************************
       parameter TCQ                   = 100,
       
       parameter DRAM_TYPE             = "DDR3",
    
       
       //***************************************************************************
       // System clock frequency parameters
       //***************************************************************************
       parameter nCK_PER_CLK           = 4,//ddr3工作频率:用户端ui_clk
                                         // # of memory CKs per fabric CLK
    
       
    
       //***************************************************************************
       // Debug parameters
       //***************************************************************************
       parameter DEBUG_PORT            = "OFF",
                                         // # = "ON" Enable debug signals/controls.
                                         //   = "OFF" Disable debug signals/controls.
          
       parameter RST_ACT_LOW           = 1
                                         // =1 for active low reset,
                                         // =0 for active high.
    	
      )*/
    
      module ddr_top(
    
       /*
          wr_fifo 是写ddr3前缓存数据的fifo,也即ddr3需要写入的数据从wr_fifo中读出
    
          rd_fifo 是读ddr3后缓存数据的fifo,也即从ddr3读出的数据写入rd_fifo
       */
    	
       input wr_fifo_empty,//wr_fifo的空信号
       input [255:0]wr_fifo_dout,//wr_fifo的读取数据
       output wire wr_fifo_read,//读wr_fifo的使能信号
       
       input rd_fifo_full,//rd_fifo的满信号
       output wire rd_burst_data_valid,//写入rd_fifo的数据的有效信号
       output wire [255:0]rd_burst_data,//写入rd_fifo的数据
    
       // Inouts
       inout [31:0]           ddr3_dq,//inout [31:0] 数据
       inout [3:0]            ddr3_dqs_n,//inout [3:0] 数据选取脉冲
       inout [3:0]            ddr3_dqs_p,//inout [3:0] 数据选取脉冲
       // Outputs
       output [14:0]          ddr3_addr,//output [14:0] 行列地址
       output [2:0]           ddr3_ba,//output [2:0] bank地址
       output                 ddr3_ras_n,//output 行地址选通,低电平有效
       output                 ddr3_cas_n,//output 列地址选通,低电平有效
       output                 ddr3_we_n,//output 0-写允许,1-读允许
       output                 ddr3_reset_n,//output 复位信号,低电平有效
       output [0:0]           ddr3_ck_p,//output [0:0] 差分时钟p端
       output [0:0]           ddr3_ck_n,//output [0:0] 差分时钟n端
       output [0:0]           ddr3_cke,//output [0:0] 时钟有效信号,高电平有效
       output [0:0]           ddr3_cs_n,//output [0:0] 片选信号,低表示命令有效,否则命令屏蔽
       output [3:0]           ddr3_dm,//output [3:0] 数据掩码 一位控制一个字节
                                      //数据是32位,所以刚好是四位
       output [0:0]           ddr3_odt,//output [0:0] 片上终端使能,高电平有效
       // Inputs
       // Differential system clocks
       input                  sys_clk_p,//系统的差分时钟
       input                  sys_clk_n,
       output                 init_calib_complete,//output ddr3 初始化完成信号,高电平有效
       // System reset - Default polarity of sys_rst_n pin is Active Low.
       // System reset polarity will change based on the option 
       // selected in GUI.
       input                  sys_rst_n,//复位信号
       output                 clk,//mig ip核生成的ui_clk
       output                 read_ddr,
       output                 write_done
       );
    
    
      localparam ADDR_WIDTH            = 29;
      localparam DQ_WIDTH              = 32;
     
      localparam PAYLOAD_WIDTH         = DQ_WIDTH;
    
      localparam APP_DATA_WIDTH        = 2 * 4 * PAYLOAD_WIDTH;//256bits
      localparam APP_MASK_WIDTH        = APP_DATA_WIDTH / 8;//32
    
      // Wire declarations
    
      wire [ADDR_WIDTH-1:0]                 app_addr;//input [28:0]将要访问的DDR内存地址,具体位宽与用户生成IP核时的设置有关
      wire [2:0]                            app_cmd;//input [2:0]命令总线,3’b000表示写命令,3’b001表示读命令
      wire                                  app_en;//input命令使能信号,该信号有效且app_rdy有效时,命令才能被使用,高电平有效
      wire                                  app_rdy;
      wire [APP_DATA_WIDTH-1:0]             app_rd_data;
      wire                                  app_rd_data_end;
      wire                                  app_rd_data_valid;
      wire [APP_DATA_WIDTH-1:0]             app_wdf_data;//input [255:0]用户写入IP核的256bit数据
      wire                                  app_wdf_end;//input 该信号有效时,表示当前是一次DDR写突发的最后一个数据,高电平有效
      wire [APP_MASK_WIDTH-1:0]             app_wdf_mask;
      wire                                  app_wdf_rdy;
      wire                                  app_sr_active;
      wire                                  app_ref_ack;
      wire                                  app_zq_ack;
      wire                                  app_wdf_wren;
    
      wire                                  rst;
      wire [11:0]                           device_temp;
      
    
    //***************************************************************************
    wire wr_burst_finish;
    wire rd_burst_finish;
    wire rd_burst_req;
    wire wr_burst_req;
    wire[15:0] rd_burst_len;
    wire[15:0] wr_burst_len;
    wire[28:0] rd_burst_addr;
    wire[28:0] wr_burst_addr;
    
    wire[255 : 0] wr_burst_data;
    
    mem_burst
    #(
    	.MEM_DATA_BITS(APP_DATA_WIDTH),
    	.ADDR_BITS(ADDR_WIDTH)
    )
    mem_burst_m0
    (   
        .rd_fifo_full(rd_fifo_full),
    	  .wr_fifo_empty(wr_fifo_empty),
        .rst(rst),                                 
        .mem_clk(clk), //用户时钟                              
        .rd_burst_req(rd_burst_req),               
        .wr_burst_req(wr_burst_req),               
        .rd_burst_len(rd_burst_len),               
        .wr_burst_len(wr_burst_len),               
        .rd_burst_addr(rd_burst_addr),             
        .wr_burst_addr(wr_burst_addr),             
        .rd_burst_data_valid(rd_burst_data_valid), 
        .wr_burst_data_req(wr_fifo_read),          
        .rd_burst_data(rd_burst_data),             
        .wr_burst_data(wr_fifo_dout),              
        .rd_burst_finish(rd_burst_finish),         
        .wr_burst_finish(wr_burst_finish),                                 
        
    
        .app_addr(app_addr),
        .app_cmd(app_cmd),
        .app_en(app_en),
        .app_wdf_data(app_wdf_data),
        .app_wdf_end(app_wdf_end),
        .app_wdf_mask(app_wdf_mask),
        .app_wdf_wren(app_wdf_wren),
        .app_rd_data(app_rd_data),
        .app_rd_data_end(app_rd_data_end),
        .app_rd_data_valid(app_rd_data_valid),
        .app_rdy(app_rdy),
        .app_wdf_rdy(app_wdf_rdy),
        .init_calib_complete(init_calib_complete),
    	.write_done(write_done)
    );
    
    mem_test
    #(
    	.MEM_DATA_BITS(APP_DATA_WIDTH),
    	.ADDR_BITS(ADDR_WIDTH)
    )
     mem_test_m0
    (
    	.rd_fifo_full(rd_fifo_full),
    	.rst(rst),                                
    	.mem_clk(clk), //用户时钟  
    	.read_ddr(read_ddr),	
    	.rd_burst_req(rd_burst_req),                       
    	.wr_burst_req(wr_burst_req),                         
    	.rd_burst_len(rd_burst_len),                   
    	.wr_burst_len(wr_burst_len),                     
    	.rd_burst_addr(rd_burst_addr),        
    	.wr_burst_addr(wr_burst_addr),                    
    	.wr_burst_data_req(wr_fifo_read),                     
    	//.wr_burst_data(wr_burst_data),    
    	.rd_burst_finish(rd_burst_finish),                    
    	.wr_burst_finish(wr_burst_finish)
    );
    
          
    // Start of User Design top instance
    //***************************************************************************
    // The User design is instantiated below. The memory interface ports are
    // connected to the top-level and the application interface ports are
    // connected to the traffic generator module. This provides a reference
    // for connecting the memory controller to system.
    //***************************************************************************
    
    always @(posedge clk or negedge sys_rst_n) begin
        if (!sys_rst_n) begin
            app_wdf_wren_r1<=0;
            app_wdf_wren_r2<=0;
            app_wdf_wren_r3<=0;
            app_wdf_data_r1<=0;
            app_wdf_data_r2<=0;
            app_wdf_data_r3<=0;
        end
        else 
            app_wdf_wren_r1<=app_wdf_end;
            app_wdf_wren_r2<=app_wdf_wren_r1;
            app_wdf_wren_r3<=app_wdf_wren_r2;
    
            app_wdf_data_r1<=app_wdf_data;
            app_wdf_data_r2<=app_wdf_data_r1;
            app_wdf_data_r3<=app_wdf_data_r2;
    end
    
    DDR3_CONTROL u_DDR3_CONTROL
          (
            // Memory interface ports ddr端接口
    
           .ddr3_addr                      (ddr3_addr),//output [14:0] 行列地址
           .ddr3_ba                        (ddr3_ba),//output [2:0] bank地址
           .ddr3_cas_n                     (ddr3_cas_n),//output 列地址选通,低电平有效
           .ddr3_ck_n                      (ddr3_ck_n),//output [0:0] 差分时钟n端
           .ddr3_ck_p                      (ddr3_ck_p),//output [0:0] 差分时钟p端
           .ddr3_cke                       (ddr3_cke),//output [0:0] 时钟有效信号,高电平有效
           .ddr3_ras_n                     (ddr3_ras_n),//output 行地址选通,低电平有效
           .ddr3_we_n                      (ddr3_we_n),//output 0-写允许,1-读允许
           .ddr3_dq                        (ddr3_dq),//inout [31:0] 数据
           .ddr3_dqs_n                     (ddr3_dqs_n),//inout [3:0] 数据选取脉冲
           .ddr3_dqs_p                     (ddr3_dqs_p),//inout [3:0] 数据选取脉冲
           .ddr3_reset_n                   (ddr3_reset_n),//output 复位信号,低电平有效
           .init_calib_complete            (init_calib_complete),//output ddr3 初始化完成信号,高电平有效
           .ddr3_cs_n                      (ddr3_cs_n),//output [0:0] 片选信号,低表示命令有效,否则命令屏蔽
           .ddr3_dm                        (ddr3_dm),//output [3:0] 数据掩码 一位控制一个字节
                                                     //数据是32位,所以刚好是四位
           .ddr3_odt                       (ddr3_odt),//output [0:0] 片上终端使能,高电平有效
            // Application interface ports 用户端接口
           .app_addr                       (app_addr),//input [28:0]将要访问的DDR内存地址,具体位宽与用户生成IP核时的设置有关
           .app_cmd                        (app_cmd),//input [2:0]命令总线,3’b000表示写命令,3’b001表示读命令
           .app_en                         (app_en),//input命令使能信号,该信号有效且app_rdy有效时,命令才能被使用,高电平有效
           .app_wdf_data                   (app_wdf_data),//input [255:0]用户写入IP核的256bit数据
           .app_wdf_end                    (app_wdf_end),//input该信号有效时,表示当前是一次DDR写突发的最后一个数据,高电平有效
           .app_wdf_wren                   (app_wdf_wren),//input写数据有效信号,当app_wdf_rdy也为有效时,
                                                          //IP核才会接收到用户端发送的app_wdf_data,高电平有效
           .app_rd_data                    (app_rd_data),//output [255:0]从DDR中读出得数据,一次突发读出8个32bit数据
           .app_rd_data_end                (app_rd_data_end),//output指示当前数据是突发读写的最后一个周期的数据,
                                                             //这个信号与设置的用户时钟和DDR时钟的比例有关
           .app_rd_data_valid              (app_rd_data_valid),//output读出数据有效信号,高电平有效,表示从IP核中读出的数据有效
           .app_rdy                        (app_rdy),//output空闲信号,指示当前IP核的工作状态,只有该信号为高时,
                                                     //IP核才能正确的使用用户给出的命令,高电平有效
           .app_wdf_rdy                    (app_wdf_rdy),//output写空闲信号,IP核内部的写FIFO能够接收用户数据的标志,高电平有效
           
           .app_sr_req                     (1'b0),//input一系列的请求信号,一般为0 代表用户对MIG IP没有强力干预
           .app_ref_req                    (1'b0),//input
           .app_zq_req                     (1'b0),//input
           .app_sr_active                  (app_sr_active),//output上面请求的响应信号
           .app_ref_ack                    (app_ref_ack),//output
           .app_zq_ack                     (app_zq_ack),//output
           .ui_clk                         (clk),//output IP核提供给用户端使用的clk,和ddr3_ck_n/p的比例是1:2或者1:4
           .ui_clk_sync_rst                (rst),//output 是ui_clk的复位指示信号,ui_clk复位完成后拉低
          
           .app_wdf_mask                   (0),//input [31:0] 32bit数据掩码,每一位对应app_wdf_data的一个8bit数据
          
           
    // System Clock Ports
           .sys_clk_p                       (sys_clk_p),
           .sys_clk_n                       (sys_clk_n),
           .device_temp                     (device_temp),
           `ifdef SKIP_CALIB
           .calib_tap_req                    (calib_tap_req),
           .calib_tap_load                   (calib_tap_load),
           .calib_tap_addr                   (calib_tap_addr),
           .calib_tap_val                    (calib_tap_val),
           .calib_tap_load_done              (calib_tap_load_done),
           `endif
          
           .sys_rst                          (sys_rst_n)
           );
    // End of User Design top instance
      
    endmodule
    
    

    四、仿真结果

    写操作时序:
    在这里插入图片描述
    读操作时序:
    在这里插入图片描述
    可以看到,写进去的数据和读出来的数据是一致的!!!

    相关