基於Vivado MIG IP核的DDR3讀寫實驗(top_rom_ddr/ddr_top)


一、前言

二、實驗內容

本次實驗的內容主要是通過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

四、仿真結果

寫操作時序:
在這里插入圖片描述
讀操作時序:
在這里插入圖片描述
可以看到,寫進去的數據和讀出來的數據是一致的!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM