Xilinxddr3 mig ip核:基於native介面的ddr3讀寫控制

言知木發表於2024-06-18

一、MIG IP核讀寫時序

  如下圖是7系列的MIG IP核結構框圖。左側是使用者介面,即使用者(FPGA)同MIG互動的介面,使用者就必須掌握這些介面才可以使用該IP核。

  將使用者側的訊號分類如下圖。

  其中的輸入輸出是相對於MIG IP核的,即對使用者側來說是相反的。
  寫命令操作時序如下,其中,寫操作app_cmd的值等於0,讀操作app_cmd的值等於1。首先檢查app_rdy,為高則表明此時IP核可以接受使用者命令,在當前時鐘拉高app_en,同時傳送命令app_cmd和地址app_addr,此時命令和地址寫入。

  寫資料的時序圖如下。

  個人認為一般native介面最好設定為4:1模式,這樣app_wdf_wren與app_wdf_end是同步的。解釋見後文。
  由圖可知,寫資料有3種情況,即寫資料與寫命令發生在同一週期,寫資料先於寫命令,寫資料落後與寫命令發生(但不能超過兩個時鐘週期)。
  所以寫資料時序為:先檢查app_wdf_rdy,為高則表明此時IP核可以接收資料,在當前時鐘拉高寫使能app_wdf_wren,給出寫資料app_wdf_data。
  讀資料的時序圖如下。

  發出讀命令後,使用者等待資料有效訊號app_rd_data_valid拉高,表明此時讀資料有效。


二、MIG IP核配置


  這裡說明兩點。
  1.clock period:ddr晶片的時脈頻率,雙沿取樣,2:1,4:1決定了使用者時鐘ui_clk。若選擇2:1,就需要2個週期的寫使能有效才能完成一次突發,app_wdf_end將落後app_wdf_end一個時鐘週期。若選擇4:1,app_wdf_wren與app_wdf_end是同步的。
  2.data_width:ddr晶片的資料位寬,看可以選什麼,決定了使用者介面的輸入資料位寬和輸出資料位寬。需要滿足頻寬一致,即ddr晶片時鐘* 2 * data_width==使用者介面的輸入資料位寬 * ui_clk。

  input clock period:給MIG IP的系統時鐘,與使用者無關,一般設定為200mhz。MIG IP內部有個鎖相環,利用這個系統時鐘產生各種所需的時鐘。

  system clock: 這裡的系統200M時鐘由FPGA內部提供,不由管腳輸入,根據管腳時鐘用IP核生成。選擇 No Buffer,如果實際硬體管腳有提供 200MHz 時鐘,也可以選擇 Differential(差分輸入)或 Signal-Ended(單端輸入)。
  reference clock:該時鐘需要頻率為200MHz時鐘,若在前面配置中將系統時鐘設定為200MHz,所以可以選擇Use System Clock,這樣就可以將兩個輸入時鐘合併一個共用的 200MH 輸入。這裡我們還是選擇No Buffer。

  這裡選擇第二個。其他未出現的訊號預設即可。然後綁DDR引腳,生成IP核。


三、模組框圖

  該ddr3讀寫控制框圖如下。具體流程為:使用者將需要儲存的資料存入寫fifo,fifo_ctrl模組根據寫fifo的狀態產生寫突發訊號控制ddr3_wr模組,完成寫操作;使用者給出讀請求,fifo_ctrl模組根據讀fifo的狀態產生讀突發訊號控制ddr3_rd模組,完成讀操作。

1.ddr3_wr模組

module ddr3_wr 
#( 
	parameter	integer					DATA_WIDTH	= 128	,		//資料位寬,根據MIG例化而來
	parameter	integer					ADDR_WIDTH	= 28			//地址位寬
)(  
//時鐘與復位-------------------------------------------------       	
    input                   			ui_clk				,		//使用者時鐘
    input                   			ui_clk_sync_rst		,		//復位,高有效
//使用者端訊號------------------------------------------------- 	
	input								wr_burst_start		,		//一次突發寫開始	由外部寫請求產生						
	input	[ADDR_WIDTH - 1:0]			wr_burst_len		,		//突發寫入的長度							
	input	[ADDR_WIDTH - 1:0]			wr_burst_addr		,		//突發寫入的首地址							
	input	[DATA_WIDTH - 1:0]			wr_burst_data		,		//需要突發寫入的資料。來源寫FIFO							
	output								wr_burst_ack		,		//突發寫響應,高電平表示正在進行突發寫操作							
	output	reg							wr_burst_done		,		//一次突發寫完成
	output	reg							wr_burst_busy		,		//突發寫忙狀態,高電平有效	
//MIG端控制訊號----------------------------------------------	
    output	reg                			app_en				,		//MIG IP傳送命令使能	
    input                   			app_rdy				,		//MIG IP命令接收準備好標緻 空閒
    output		[2:0]     				app_cmd				,		//MIG IP操作命令,讀或者寫
    output	reg	[ADDR_WIDTH - 1:0]		app_addr			,		//MIG IP操作地址	
    input                   			app_wdf_rdy			,		//MIG IP資料接收準備好 寫資料空閒
    output	                			app_wdf_wren		,		//MIG IP寫資料使能
	output								app_wdf_end			,		//MIG IP突發寫當前時鐘最後一個資料
 	output		[(DATA_WIDTH/8) - 1:0]	app_wdf_mask		,		//MIG IP資料掩碼
    output		[DATA_WIDTH - 1:0]		app_wdf_data				//MIG IP寫資料
);


//parameter define
localparam								WRITE = 3'b000;				//MIG寫命令
//reg define
reg				[ADDR_WIDTH - 1:0]		wr_burst_cnt		;		//寫入資料個數計數器計數
reg				[ADDR_WIDTH - 1:0]		wr_burst_len_r		;		//鎖存突發長度
reg				[ADDR_WIDTH - 1:0]		wr_burst_addr_r		;		//鎖存突發地址
reg										wr_burst_start_r	;		//突發開始打一拍,用於鎖存突發地址、突發長度   
//wire define					
wire									wr_burst_last		;		//拉高表示寫入最後一個資料

 //*********************************************************************************************
//**                    main code
//**********************************************************************************************

//不使用掩碼
assign app_wdf_mask = 0	;
//固定為寫狀態 
assign app_cmd = WRITE;				              
//寫指令,命令接收和資料接收都準備好,此時拉高寫使能
assign app_wdf_wren = wr_burst_ack;
//從使用者端輸入的資料直接賦值給MIG IP核
assign app_wdf_data = wr_burst_data; 
//由於DDR3晶片時鐘和使用者時鐘的分頻選擇4:1,突發長度為8,故兩個訊號相同
assign app_wdf_end = app_wdf_wren; 
//處於寫使能且是最後一個資料
assign wr_burst_last = app_wdf_wren && (wr_burst_cnt == wr_burst_len_r - 1) ;
//寫響應訊號,用於從前級獲取資料
assign wr_burst_ack = app_en && app_wdf_rdy && app_rdy;

//wr_burst_start打一拍,鎖存突發地址、突發長度   
always @(posedge ui_clk) begin
	if(ui_clk_sync_rst)
		wr_burst_start_r <= 0;
	else 
		wr_burst_start_r <= wr_burst_start;		
end

//鎖存突發長度
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        wr_burst_len_r <= 1'b0;
	else if(wr_burst_start)
		wr_burst_len_r <= wr_burst_len;
end

//鎖存突發地址
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        wr_burst_addr_r <= 1'b0;
	else if(wr_burst_start)
		wr_burst_addr_r <= wr_burst_addr;
end

//app_en控制:突發開始時一直拉高,直到突發結束   
always @(posedge ui_clk) begin
	if(ui_clk_sync_rst)
		app_en <= 0;
	else if(wr_burst_start_r)
		app_en <= 1;
	else if(wr_burst_last)
		app_en <= 0;
	else 
		app_en <= app_en;		
end

//突發寫忙狀態,拉高表示其處於突發寫狀態
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        wr_burst_busy <= 0;
	else if(wr_burst_start)
		wr_burst_busy <= 1;				//進入寫忙狀態
	else if(wr_burst_done)
		wr_burst_busy <= 0;				//突發寫完成,拉低寫忙狀態
	else 
		wr_burst_busy <= wr_burst_busy;		
end

//寫入地址
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        app_addr <= 0;
	else if(wr_burst_start_r)					
		app_addr <= wr_burst_addr_r;	//將突發寫的初始地址賦值給MIG
	else if(app_wdf_wren)				//
		app_addr <= app_addr + 8;		//
	else 
		app_addr <= app_addr;
end

//突發寫完成訊號
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        wr_burst_done <= 0;
	else if(wr_burst_last)
		wr_burst_done <= 1;				
	else 
		wr_burst_done <= 0;		
end

//寫入資料個數(突發長度計數器)
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        wr_burst_cnt <= 0;
	else if(app_wdf_wren)begin
		if(wr_burst_cnt == wr_burst_len_r - 1)		
			wr_burst_cnt <= 0;					//進入寫忙狀態
		else
			wr_burst_cnt <= wr_burst_cnt + 1;
	end
	else 
		wr_burst_cnt <= wr_burst_cnt;
end

endmodule

  對於MIG端的控制訊號,根據時序圖編寫程式碼即可。使用者端訊號,實際是與fifo_ctrl模組進行互動所需的訊號,這裡將突發開始訊號,突發長度,突發首地址以及突發資料均傳入,也需要將該模組的目前狀態反饋給前一級模組。

2.ddr3_rd模組

module ddr3_rd 
#( 
	parameter	integer					DATA_WIDTH	= 128	,		//資料位寬,根據MIG例化而來
	parameter	integer					ADDR_WIDTH	= 28			//地址位寬
)	
(   
//時鐘與復位-------------------------------------------------       	
    input                   			ui_clk				,		//使用者時鐘
    input                   			ui_clk_sync_rst		,		//復位,高有效	
//使用者端訊號------------------------------------------------- 	
	input								rd_burst_start		,		//一次突發讀開始								
	input	[ADDR_WIDTH - 1:0]			rd_burst_len		,		//突發讀取的長度								
	input	[ADDR_WIDTH - 1:0]			rd_burst_addr		,		//突發讀取的首地址								
	output	[DATA_WIDTH - 1:0]			rd_burst_data		,		//突發讀取的資料。存入讀FIFO			
	output								rd_burst_ack		,		//突發讀響應,高電平表示正在進行突發讀操作			
	output	reg							rd_burst_done		,   	//一次突發讀完成
	output	reg							rd_burst_busy		,		//突發讀忙狀態,高電平有效	
//MIG端控制訊號----------------------------------------------	
    output	reg                			app_en				,		//MIG傳送命令使能	
    input                   			app_rdy				,		//MIG命令接收準備好標緻
    output		[2:0]     				app_cmd				,		//MIG操作命令,讀或者寫
    output	reg	[ADDR_WIDTH - 1:0]		app_addr			,		//MIG讀取DDR3地址							
	input	    [DATA_WIDTH - 1:0]   	app_rd_data         ,		//MIG讀出的資料
	input								app_rd_data_end     ,		//MIG讀出的最後一個資料
	input								app_rd_data_valid   		//MIG讀出的資料有效
);

//parameter define
localparam								READ = 3'b001;				//MIG讀命令
//reg define
reg				[ADDR_WIDTH - 1:0]		rd_addr_cnt			;		//讀取地址個數計數
reg				[ADDR_WIDTH - 1:0]		rd_data_cnt			;		//讀取資料個數計數
reg				[ADDR_WIDTH - 1:0]		rd_burst_len_r		;		//鎖存突發長度
reg				[ADDR_WIDTH - 1:0]		rd_burst_addr_r		;		//鎖存突發地址
reg										rd_burst_start_r	;		//突發開始打一拍,用於鎖存突發地址、突發長度   
//wire define					
wire									rd_addr_last		;		//拉高
wire									rd_data_last		;		//拉高讀出最後一個資料

//*********************************************************************************************
//**                    main code
//**********************************************************************************************

//固定為讀狀態 
assign app_cmd = READ;
//將從MIG中讀出的資料直接賦值給上級模組
assign rd_burst_data = app_rd_data;				              
//讀出的最後一個地址
assign rd_addr_last = app_en && app_rdy && (rd_addr_cnt == rd_burst_len_r - 1) ;
//讀出的最後一個資料
assign rd_data_last = app_rd_data_valid && (rd_data_cnt == rd_burst_len_r - 1) ;
//讀響應訊號,用於將MIG中的資料輸出給上級模組(讀出)
assign rd_burst_ack = app_rd_data_valid;

//rd_burst_start打一拍,鎖存突發地址、突發長度   
always @(posedge ui_clk) begin
	if(ui_clk_sync_rst)
		rd_burst_start_r <= 0;
	else 
		rd_burst_start_r <= rd_burst_start;		
end

//鎖存突發長度
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        rd_burst_len_r <= 1'b0;
	else if(rd_burst_start)
		rd_burst_len_r <= rd_burst_len;
end

//鎖存突發地址
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        rd_burst_addr_r <= 1'b0;
	else if(rd_burst_start)
		rd_burst_addr_r <= rd_burst_addr;
end

//app_en控制:突發開始時一直拉高,直到突發結束   
always @(posedge ui_clk) begin
	if(ui_clk_sync_rst)
		app_en <= 0;
	else if(rd_burst_start_r)
		app_en <= 1;
	else if(rd_addr_last)
		app_en <= 0;
	else 
		app_en <= app_en;		
end

//突發讀忙狀態,拉高表示其處於突發讀狀態
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        rd_burst_busy <= 0;
	else if(rd_burst_start)
		rd_burst_busy <= 1;				//進入寫忙狀態
	else if(rd_burst_done)
		rd_burst_busy <= 0;				//突發傳輸完成,拉低寫忙狀態
	else 
		rd_burst_busy <= rd_burst_busy;		
end

//讀取地址
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        app_addr <= 0;
	else if(rd_burst_start_r)					
		app_addr <= rd_burst_addr_r;		//將突發寫的初始地址賦值給MIG
	else if(app_en && app_rdy)	
		app_addr <= app_addr + 8;		//
	else 
		app_addr <= app_addr;
end

//突發讀完成訊號
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        rd_burst_done <= 0;
	else if(rd_data_last)
		rd_burst_done <= 1;				
	else 
		rd_burst_done <= 0;		
end
//讀出地址個數計數(讀取突發長度計數器)
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        rd_addr_cnt <= 0;
	else if(rd_addr_last)
		rd_addr_cnt <= 0;
	else if(app_en && app_rdy)begin
		rd_addr_cnt <= rd_addr_cnt + 1;
	end
	else 
		rd_addr_cnt <= rd_addr_cnt;
end

//讀出資料個數計數
always @(posedge ui_clk) begin
    if(ui_clk_sync_rst)
        rd_data_cnt <= 0;
	else if(rd_data_last)
		rd_data_cnt <= 0;
	else if(app_rd_data_valid)begin
		rd_data_cnt <= rd_data_cnt + 1;
	end
	else 
		rd_data_cnt <= rd_data_cnt;
end

endmodule

3.fifo_ctrl模組

module fifo_ctrl 
#(
	parameter	integer				FIFO_DATA_WIDTH	= 16		,	//根據ddr資料位寬而來
	parameter	integer				FIFO_ADDR_WIDTH	= 28		,	
	parameter	integer				MIG_DATA_WIDTH	= 128		,	//資料位寬,根據MIG例化而來
	parameter	integer				MIG_ADDR_WIDTH	= 28			//地址位寬
)(
//時鐘和復位 -----------------------------------------------------
	input                   				ui_clk				,	//使用者時鐘
    input                   				ui_clk_sync_rst		,	//復位,高有效
	input               					init_calib_complete	,   //MIG初始化完成標誌	
//使用者操作訊號 --------------------------------------------	
	//寫fifo訊號-------------------------------------------------
    input   	           					wr_fifo_wr_clk  	,   //寫FIFO寫時鐘
    input   	           					wr_fifo_wr_req  	,   //寫FIFO寫請求
    input   	[FIFO_DATA_WIDTH - 1:0]		wr_fifo_wr_data 	,   //寫FIFO寫資料	
    input   	           					wr_fifo_rst			,   //寫FIFO復位訊號	
    input   	[FIFO_ADDR_WIDTH - 1:0]  	wr_b_addr  			,   //寫DDR3首地址
    input   	[FIFO_ADDR_WIDTH - 1:0]  	wr_e_addr  			,   //寫DDR3末地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		wr_len    			,   //寫DDR3突發長度
    output  	[8:0]						wr_fifo_num     	,   //寫fifo中的資料量,實際是可以讀出的資料量
	//讀fifo訊號-------------------------------------------------		
    input   	            				rd_fifo_rd_clk  	,   //讀FIFO讀時鐘
    input   	            				rd_fifo_rd_req  	,   //讀FIFO讀請求
    output  	[FIFO_DATA_WIDTH - 1:0]		rd_fifo_rd_data 	,   //讀FIFO讀資料	
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_b_addr 			,   //讀DDR3首地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_e_addr 			,   //讀DDR3末地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_len    			,   //讀DDR3資料突發長度
    input   	        					rd_fifo_rst			,   //讀FIFO復位訊號
    output  	[8:0]						rd_fifo_num     	,   //讀fifo中的資料量,實際是已經寫進去的資料量
	//控制訊號----------------------------------------------------						
    input               					read_valid      	,	//DDR3讀使能
//突發讀、寫模組訊號 --------------------------------------------
	//突發寫模組 ------------------------------------------------
	input									wr_burst_busy		,	//高電平表示正在進行突發寫操作
	input									wr_burst_done   	,	//高電平表示完成了一次突發寫操作
	input									wr_burst_ack		,   //突發寫模組要寫入的資料有效
	output		[MIG_DATA_WIDTH - 1:0]		wr_burst_data		,	//突發寫模組要寫入的資料							
	output		[FIFO_ADDR_WIDTH - 1:0]		wr_burst_len		,	//突發寫長度							
	output	reg								wr_burst_req		,	//請求進行突發寫,驅動突發寫模組
	output	reg	[FIFO_ADDR_WIDTH - 1:0]		wr_burst_addr		,	//突發寫入的首地址
	//突發讀模組 ------------------------------------------------
	input									rd_burst_busy   	,	//高電平表示正在進行突發讀操作
	input									rd_burst_done   	,	//高電平表示完成了一次突發讀操作
	input									rd_burst_ack		,	//突發讀模組讀出的資料有效
	output		[FIFO_ADDR_WIDTH - 1:0]		rd_burst_len		,	//突發讀長度	
	output		[MIG_DATA_WIDTH - 1:0]   	rd_burst_data       ,	//突發讀模組讀出的資料
	output	reg								rd_burst_req		,	//請求進行突發讀
	output	reg	[FIFO_ADDR_WIDTH - 1:0]		rd_burst_addr			//突發讀的首地址	
);
//*********************************************************************************************
//**                    main code
//**********************************************************************************************

assign	wr_burst_len = wr_len;
assign	rd_burst_len = rd_len;

//wr_burst_req,rd_burst_req:讀寫請求訊號
always@(posedge ui_clk)begin
    if(ui_clk_sync_rst)begin
        wr_burst_req <= 1'b0;
        rd_burst_req <= 1'b0;
    end
	//初始化完成後響應讀寫請求,優先執行寫操作,防止寫入ddr3中的資料丟失
    else if(init_calib_complete) begin
		if(~wr_burst_busy && ~rd_burst_busy)begin	
			//寫FIFO中的資料量達到寫突發長度
			if(wr_fifo_num >= wr_len && ~wr_burst_req)begin   	
				wr_burst_req <= 1'b1;   			//寫請求有效
				rd_burst_req <= 1'b0;
			end
			//讀FIFO中的資料量小於讀突發長度,且讀使能訊號有效
			else if((rd_fifo_num < rd_len) && read_valid && ~rd_burst_req)begin 
				wr_burst_req <= 1'b0;
				rd_burst_req <= 1'b1;   			//讀請求有效
			end
			else begin
				wr_burst_req <= 1'b0;
				rd_burst_req <= 1'b0;
			end
		end
		else begin		//非空閒狀態
			wr_burst_req <= 1'b0;
			rd_burst_req <= 1'b0;				
		end
	end
    else begin			//MIG初始化未完成
		wr_burst_req <= 1'b0;
		rd_burst_req <= 1'b0;
	end
end

//wr_burst_addr:ddr3寫地址
always@(posedge ui_clk)begin
    if(ui_clk_sync_rst)
        wr_burst_addr <= wr_b_addr;
    else if(wr_fifo_rst)
        wr_burst_addr <= wr_b_addr;								//復位fifo則地址為初始地址
    else if(wr_burst_done) 										//一次突發寫結束,更改寫地址
        begin
            if(wr_burst_addr < (wr_e_addr - wr_len * 8))                       
                wr_burst_addr <= wr_burst_addr + wr_len * 8;	//未達到末地址,寫地址累加
            else        
                wr_burst_addr <= wr_b_addr;						//到達末地址,回到寫起始地址
        end
end		
 
//rd_burst_addr:ddr3讀地址
always@(posedge ui_clk)begin
    if(ui_clk_sync_rst)
        rd_burst_addr <= rd_b_addr;
    else if(rd_fifo_rst)
        rd_burst_addr <= rd_b_addr;
    else if(rd_burst_done) 										//一次突發讀結束,更改讀地址
        begin
            if(rd_burst_addr < (rd_e_addr - rd_len * 8))                    
                rd_burst_addr <= rd_burst_addr + rd_len * 8;	//讀地址未達到末地址,讀地址累加
            else    
                rd_burst_addr <= rd_b_addr;						//到達末地址,回到首地址
        end
end

//例化寫FIFO-----------------------------------------
wr_fifo_16_128	wr_fifo_16_128_inst (
	//寫資料介面 ----------------------------------------------
	.wr_clk				(wr_fifo_wr_clk		),           
	.wr_rst				(wr_fifo_rst		),           
	.wr_en				(wr_fifo_wr_req		),           
	.din				(wr_fifo_wr_data	),           
	//讀資料介面 ----------------------------------------------	
	.rd_clk				(ui_clk				),           
	.rd_rst				(ui_clk_sync_rst	),           
	.rd_en				(wr_burst_ack		),           
	.dout				(wr_burst_data		),           
	//指示介面 ----------------------------------------------		
	.full				(					),           
	.empty				(					),           
	.rd_data_count		(wr_fifo_num		)		// output wire [8 : 0] rd_data_count
);

//例化讀FIFO-----------------------------------------
rd_fifo_128_16	rd_fifo_128_16_inst (
	//寫資料介面 ----------------------------------------------
	.wr_clk				(ui_clk				),             
	.wr_rst				(ui_clk_sync_rst	),             
	.din				(rd_burst_data		),             
	.wr_en				(rd_burst_ack		),             
	//讀資料介面 ----------------------------------------------			
	.rd_clk				(rd_fifo_rd_clk		),             
	.rd_rst				(rd_fifo_rst		),             
	.rd_en				(rd_fifo_rd_req		),             
	.dout				(rd_fifo_rd_data	),             
	//指示介面 ----------------------------------------------			
	.full				(					),             
	.empty				(					),             
	.wr_data_count		(rd_fifo_num		)	// output wire [8 : 0] wr_data_count
);
	
endmodule

  該模組中例化了兩個fifo。寫fifo,寫位寬16位(根據我們設定的ddr資料位寬),讀位寬128位(根據頻寬一致,理解不了直接看例化的位寬是多少),將rd_data_count引出,該訊號代表寫fifo中具有的128位寬資料的個數,根據這個個數,產生寫突發請求。讀fifo,讀寫位寬與寫fifo相反,將wr_data_count引出,代表寫進讀fifo中的128位寬資料的個數。在產生讀突發請求的過程中,首先需要讀ddr3使能read_valid有效,其次,若讀fifo中寫進去的資料個數不足讀突發長度,就產生一次讀突發。
  對於突發地址,在fifo_ctrl模組,每當接收當讀或寫模組的burst_done訊號,就代表一次突發完成,此時,fifo_ctrl模組需要更新突發地址,即burst_addr+8,當然前提是地址不會超過讀寫結束地址。這裡的一次突發很容易混淆。在讀寫控制模組中,一次固定突發8個地址。在fifo_ctrl模組中,我們設定的突發長度為len,就代表fifo_ctrl模組中的一次突發,在讀寫模組中需要完成len次讀寫突發。

4.native_ddr3_ctrl模組

module native_ddr3_ctrl
#(
	parameter	integer				FIFO_DATA_WIDTH	= 16		,	//寫fifo寫位寬
	parameter	integer				FIFO_ADDR_WIDTH	= 28		,	
	parameter	integer				MIG_DATA_WIDTH	= 128		,	//資料位寬,根據MIG例化而來
	parameter	integer				MIG_ADDR_WIDTH	= 28			//地址位寬
)(
//時鐘和復位 ------------------------------------------------------
	input                   				ui_clk				,	//使用者時鐘
    input                   				ui_clk_sync_rst		,	//復位,高有效
	input               					init_calib_complete	,   //MIG初始化完成標誌	
//使用者操作訊號 -------------------------------------------------
	//寫fifo訊號---------------------------------------------------
    input   	           					wr_fifo_wr_clk  	,   //寫FIFO寫時鐘
    input   	           					wr_fifo_wr_req  	,   //寫FIFO寫請求
    input   	[FIFO_DATA_WIDTH - 1:0]		wr_fifo_wr_data 	,   //寫FIFO寫資料	
    input   	           					wr_fifo_rst			,   //寫FIFO復位訊號	
    input   	[FIFO_ADDR_WIDTH - 1:0]  	wr_b_addr  			,   //寫DDR3首地址
    input   	[FIFO_ADDR_WIDTH - 1:0]  	wr_e_addr  			,   //寫DDR3末地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		wr_len    			,   //寫DDR3突發長度
    output  	[8:0]						wr_fifo_num     	,   //寫fifo中的資料量
	//讀fifo訊號----------------------------------------------		
    input   	            				rd_fifo_rd_clk  	,   //讀FIFO讀時鐘
    input   	            				rd_fifo_rd_req  	,   //讀FIFO讀請求
    output  	[FIFO_DATA_WIDTH - 1:0]		rd_fifo_rd_data 	,   //讀FIFO讀資料	
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_b_addr 			,   //讀DDR3首地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_e_addr 			,   //讀DDR3末地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_len    			,   //讀DDR3資料突發長度
    input   	        					rd_fifo_rst			,   //讀FIFO復位訊號
    output  	[8:0]						rd_fifo_num     	,   //讀fifo中的資料量
	//其他----------------------------------------------						
    input               					read_valid      	,   //DDR3讀使能	
//MIG介面 --------------------------------------------------------------------------------------
	//命令介面 ----------------------------------------------
    input                   				app_rdy				,	//MIG 命令接收準備好標	
	output	                				app_en				,	//MIG IP傳送命令使能	
    output		[2:0]     					app_cmd				,	//MIG IP核操作命令,讀或者寫
    output		[MIG_ADDR_WIDTH - 1:0]		app_addr			,	//DDR3地址						
	//讀介面 ----------------------------------------------	
	input	    [MIG_DATA_WIDTH - 1:0]   	app_rd_data         ,	//從MIG中讀出的資料
	input									app_rd_data_end     ,	//從MIG中讀出的最後一個資料
	input									app_rd_data_valid   ,	//從MIG中讀出的資料有效
	//寫介面 ----------------------------------------------	
    input                   				app_wdf_rdy			,	//MIG資料接收準備好
    output	                				app_wdf_wren		,	//使用者寫資料使能
	output									app_wdf_end			,	//突發寫當前時鐘最後一個資料
 	output		[(MIG_DATA_WIDTH/8) - 1:0]	app_wdf_mask		,
    output		[MIG_DATA_WIDTH - 1:0]		app_wdf_data			//使用者寫資料	
);

wire										wr_burst_req		;	//請求對DDR3寫入資料
wire	[FIFO_ADDR_WIDTH - 1:0]				wr_burst_addr		;	//當前寫入DDR3的地址
wire										wr_burst_busy		;	//高電平表示正在進行突發寫操作
wire										wr_burst_done   	;	//高電平表示完成了一次突發寫操作
wire	[MIG_DATA_WIDTH - 1:0]				wr_burst_data		;	//突發寫模組要寫入的資料							
wire										wr_burst_ack		;   //突發寫模組要寫入的資料有效
wire	[FIFO_ADDR_WIDTH - 1:0]				wr_burst_len		;	//突發寫長度
wire										rd_burst_req		;	//請求從DDR3讀出資料
wire	[FIFO_ADDR_WIDTH - 1:0]				rd_burst_addr		;	//當前讀出DDR3的地址
wire										rd_burst_busy   	;	//高電平表示正在進行突發讀操作
wire										rd_burst_done   	;	//高電平表示完成了一次突發讀操作
wire	[MIG_DATA_WIDTH - 1:0]   			rd_burst_data       ;	//突發讀模組讀出的資料
wire										rd_burst_ack		;	//突發讀模組讀出的資料有效
wire	[FIFO_ADDR_WIDTH - 1:0]				rd_burst_len		;	//突發讀長度


//MIG介面 -----------------------------------------------------
wire										app_en_wr   		; 	
wire	[2:0]     							app_cmd_wr  		; 		
wire	[MIG_ADDR_WIDTH - 1:0]				app_addr_wr 		;
wire										app_en_rd			; 	
wire	[2:0]     							app_cmd_rd  		;
wire	[MIG_ADDR_WIDTH - 1:0]				app_addr_rd 		;

//*********************************************************************************************
//**                    main code
//**********************************************************************************************

//突發讀寫模組共用MIG的控制訊號,所以需要分時複用
assign	app_en	 = wr_burst_busy ? app_en_wr   : app_en_rd		; 
assign  app_cmd	 = wr_burst_busy ? app_cmd_wr  : app_cmd_rd		; 
assign  app_addr = wr_burst_busy ? app_addr_wr : app_addr_rd	; 

fifo_ctrl 
#(
	.FIFO_DATA_WIDTH						(FIFO_DATA_WIDTH	),	
	.FIFO_ADDR_WIDTH						(FIFO_ADDR_WIDTH	),	
	.MIG_DATA_WIDTH							(MIG_DATA_WIDTH		),	//資料位寬,根據MIG例化而來
	.MIG_ADDR_WIDTH							(MIG_ADDR_WIDTH		)	//
)
fifo_ctrl_inst(
//時鐘和復位 -----------------------------------------------------
	.ui_clk									(ui_clk				),	//使用者時鐘
    .ui_clk_sync_rst						(ui_clk_sync_rst	),	//復位,高有效
	.init_calib_complete					(init_calib_complete),	//MIG初始化完成標誌	
//使用者操作訊號 ----------------------------------------------------	
	//寫fifo訊號-------------------------------------------------
    .wr_fifo_wr_clk  						(wr_fifo_wr_clk		),   //寫FIFO寫時鐘
    .wr_fifo_wr_req  						(wr_fifo_wr_req		),   //寫FIFO寫請求
    .wr_fifo_wr_data 						(wr_fifo_wr_data	),   //寫FIFO寫資料	
    .wr_fifo_rst							(wr_fifo_rst		),   //寫FIFO復位訊號	
    .wr_b_addr  							(wr_b_addr			),   //寫DDR3首地址
    .wr_e_addr  							(wr_e_addr			),   //寫DDR3末地址
    .wr_len    								(wr_len				),   //寫DDR3突發長度
    .wr_fifo_num     						(wr_fifo_num		),   //寫fifo中的資料量
	//讀fifo訊號-------------------------------------------------		
    .rd_fifo_rd_clk  						(rd_fifo_rd_clk  	),   //讀FIFO讀時鐘
    .rd_fifo_rd_req  						(rd_fifo_rd_req  	),   //讀FIFO讀請求
    .rd_fifo_rd_data 						(rd_fifo_rd_data 	),   //讀FIFO讀資料	
    .rd_b_addr 								(rd_b_addr 			),   //讀DDR3首地址
    .rd_e_addr 								(rd_e_addr 			),   //讀DDR3末地址
    .rd_len    								(rd_len    			),   //讀DDR3資料突發長度
    .rd_fifo_rst							(rd_fifo_rst		),   //讀FIFO復位訊號
    .rd_fifo_num     						(rd_fifo_num     	),   //讀fifo中的資料量
	//控制訊號----------------------------------------------------						
    .read_valid      						(read_valid			),	//DDR3讀使能
//突發讀、寫模組訊號 --------------------------------------------
	//突發寫模組 ------------------------------------------------
	.wr_burst_busy							(wr_burst_busy		),	//高電平表示正在進行突發寫操作
	.wr_burst_done   						(wr_burst_done  	),	//高電平表示完成了一次突發寫操作
	.wr_burst_ack							(wr_burst_ack		),   //突發寫模組要寫入的資料有效
	.wr_burst_data							(wr_burst_data		),	//突發寫模組要寫入的資料							
	.wr_burst_req							(wr_burst_req		),	//請求進行突發寫
	.wr_burst_addr							(wr_burst_addr		),	//突發寫入的首地址
	.wr_burst_len							(wr_burst_len		),	//突發寫長度
	//突發讀模組 ------------------------------------------------
	.rd_burst_busy   						(rd_burst_busy  	),	//高電平表示正在進行突發讀操作
	.rd_burst_done   						(rd_burst_done  	),	//高電平表示完成了一次突發讀操作
	.rd_burst_ack							(rd_burst_ack		),	//突發讀模組讀出的資料有效
	.rd_burst_data      					(rd_burst_data  	),	//突發讀模組讀出的資料
	.rd_burst_req							(rd_burst_req		),	//請求進行突發讀
	.rd_burst_addr							(rd_burst_addr		),	//突發讀的首地址
	.rd_burst_len							(rd_burst_len		)	//突發讀長度	
);


ddr3_wr 
#( 
	.DATA_WIDTH								(MIG_DATA_WIDTH		),	//資料位寬,根據MIG例化而來
	.ADDR_WIDTH								(MIG_ADDR_WIDTH		)	//
)
ddr3_wr_inst(         
    .ui_clk									(ui_clk				),	//使用者時鐘
    .ui_clk_sync_rst						(ui_clk_sync_rst	),	//復位,高有效
//突發寫相關訊號        					                                    
	.wr_burst_start							(wr_burst_req		),								
	.wr_burst_len							(wr_burst_len		),								
	.wr_burst_addr							(wr_burst_addr		),								
	.wr_burst_data							(wr_burst_data		),								
	.wr_burst_ack							(wr_burst_ack		),								
	.wr_burst_done							(wr_burst_done		),
	.wr_burst_busy							(wr_burst_busy		),	//突發寫忙狀態	
//MIG 使用者端控制介面    					 					                                   
    .app_en									(app_en_wr			),	
    .app_cmd								(app_cmd_wr			),	
    .app_addr								(app_addr_wr		),	
									
	.app_rdy								(app_rdy			),	//MIG 命令接收準備好標緻
    .app_wdf_rdy							(app_wdf_rdy		),	//MIG資料接收準備好
    .app_wdf_wren							(app_wdf_wren		),	//使用者寫資料使能
	.app_wdf_end							(app_wdf_end		),	//突發寫當前時鐘最後一個資料
 	.app_wdf_mask							(app_wdf_mask		),
    .app_wdf_data							(app_wdf_data		)	//使用者寫資料
);					
						
ddr3_rd #( 					
	.DATA_WIDTH								(MIG_DATA_WIDTH		),	//資料位寬,根據MIG例化而來
	.ADDR_WIDTH								(MIG_ADDR_WIDTH		)	//
)					
ddr3_rd_inst(         					
    .ui_clk									(ui_clk				),	//使用者時鐘
    .ui_clk_sync_rst						(ui_clk_sync_rst	),	//復位,高有效
//突發讀相關訊號        					 		                                   
	.rd_burst_start							(rd_burst_req		),								
	.rd_burst_len							(rd_burst_len		),								
	.rd_burst_addr							(rd_burst_addr		),								
	.rd_burst_data							(rd_burst_data		),								
	.rd_burst_ack							(rd_burst_ack		),								
	.rd_burst_done							(rd_burst_done		),
	.rd_burst_busy							(rd_burst_busy		),	//突發寫忙狀態	
//MIG 使用者端控制介面    					 		                                   
    .app_en									(app_en_rd			),	
    .app_cmd								(app_cmd_rd			),	
    .app_addr								(app_addr_rd		),	
						
    .app_rdy								(app_rdy			),	//MIG 命令接收準備好標緻			
	.app_rd_data        					(app_rd_data		),	//從MIG中讀出的資料
	.app_rd_data_end    					(app_rd_data_end	),	//從MIG中讀出的最後一個資料
	.app_rd_data_valid  					(app_rd_data_valid	)	//從MIG中讀出的資料有效
);
	
endmodule

5.native_ddr3_top模組

module native_ddr3_top
#(
	parameter	integer				FIFO_DATA_WIDTH	= 16		,	
	parameter	integer				FIFO_ADDR_WIDTH	= 28		,	
	parameter	integer				MIG_DATA_WIDTH	= 128		,	//資料位寬,突發長度為8,16bit,共128bit
	parameter	integer				MIG_ADDR_WIDTH	= 28			//根據MIG例化而來
)(
//時鐘和復位 ------------------------------------------------------
	input              						clk_200				,	//
	input              						sys_rst_n			,	//復位,低有效
//使用者操作訊號 -------------------------------------------------
	//寫fifo訊號---------------------------------------------------
    input   	           					wr_fifo_wr_clk  	,   //寫FIFO寫時鐘
    input   	           					wr_fifo_wr_req  	,   //寫FIFO寫請求
    input   	[FIFO_DATA_WIDTH - 1:0]		wr_fifo_wr_data 	,   //寫FIFO寫資料	
    input   	           					wr_fifo_rst			,   //寫FIFO復位訊號	
    input   	[FIFO_ADDR_WIDTH - 1:0]  	wr_b_addr  			,   //寫DDR3首地址
    input   	[FIFO_ADDR_WIDTH - 1:0]  	wr_e_addr  			,   //寫DDR3末地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		wr_len    			,   //寫DDR3突發長度
    output  	[8:0]						wr_fifo_num     	,   //寫fifo中的資料量
	//讀fifo訊號----------------------------------------------		
    input   	            				rd_fifo_rd_clk  	,   //讀FIFO讀時鐘
    input   	            				rd_fifo_rd_req  	,   //讀FIFO讀請求
    output  	[FIFO_DATA_WIDTH - 1:0]		rd_fifo_rd_data 	,   //讀FIFO讀資料	
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_b_addr 			,   //讀DDR3首地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_e_addr 			,   //讀DDR3末地址
    input   	[FIFO_ADDR_WIDTH - 1:0]		rd_len    			,   //讀DDR3資料突發長度
    input   	        					rd_fifo_rst			,   //讀FIFO復位訊號
    output  	[8:0]						rd_fifo_num     	,   //讀fifo中的資料量
	//其他----------------------------------------------------------						
    input               					read_valid      	,   //DDR3讀使能	
//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	
);
//mig_ctrl相關 -----------------------------------------------------	
wire                  						ui_clk				;		//使用者時鐘
wire                  						ui_clk_sync_rst		;     	//使用者復位訊號
wire                  						init_calib_complete	;		//校準完成訊號
wire                   						app_rdy				;		//MIG 命令接收準備好標	
wire	                					app_en				;		//MIG IP傳送命令使能	
wire		[2:0]     						app_cmd				;		//MIG IP核操作命令,讀或者寫
wire		[MIG_ADDR_WIDTH - 1:0]			app_addr			;		//DDR3地址						
wire		[MIG_DATA_WIDTH - 1:0]			app_rd_data         ;		//從MIG中讀出的資料
wire										app_rd_data_end     ;		//從MIG中讀出的最後一個資料
wire										app_rd_data_valid   ;		//從MIG中讀出的資料有效
wire                   						app_wdf_rdy			;		//MIG資料接收準備好
wire	                					app_wdf_wren		;		//使用者寫資料使能
wire										app_wdf_end			;		//突發寫當前時鐘最後一個資料
wire		[(MIG_DATA_WIDTH/8) - 1:0]		app_wdf_mask		;	
wire		[MIG_DATA_WIDTH - 1:0]			app_wdf_data		;		//使用者寫資料

//*********************************************************************************************
//**                    main code
//**********************************************************************************************


//============================< 例化native_ddr3_ctrl模組 >=============================================== 
native_ddr3_ctrl 
#(
	.FIFO_DATA_WIDTH				(FIFO_DATA_WIDTH	),	
	.FIFO_ADDR_WIDTH				(FIFO_ADDR_WIDTH	),	
	.MIG_DATA_WIDTH					(MIG_DATA_WIDTH		),	//資料位寬,根據MIG例化而來
	.MIG_ADDR_WIDTH					(MIG_ADDR_WIDTH		)	//
)		
native_ddr3_ctrl_inst(		
	.ui_clk							(ui_clk				),	//使用者時鐘
    .ui_clk_sync_rst				(ui_clk_sync_rst	),	//復位,高有效
	.init_calib_complete			(init_calib_complete),   //MIG初始化完成標誌
	
    .wr_fifo_wr_clk  				(wr_fifo_wr_clk  	),   //寫FIFO寫時鐘
    .wr_fifo_wr_req  				(wr_fifo_wr_req  	),   //寫FIFO寫請求
    .wr_fifo_wr_data 				(wr_fifo_wr_data 	),   //寫FIFO寫資料
    .wr_fifo_rst					(~sys_rst_n			),   //寫FIFO復位訊號	
    .wr_b_addr  					(wr_b_addr  		),   //寫DDR3首地址
    .wr_e_addr  					(wr_e_addr   		),   //寫DDR3末地址
    .wr_len    						(wr_len  			),   //寫DDR3突發長度
    .wr_fifo_num     				(wr_fifo_num     	),   //寫fifo中的資料量
	
    .rd_fifo_rd_clk  				(rd_fifo_rd_clk  	),   //讀FIFO讀時鐘
    .rd_fifo_rd_req  				(rd_fifo_rd_req  	),   //讀FIFO讀請求
    .rd_fifo_rd_data 				(rd_fifo_rd_data 	),   //讀FIFO讀資料
    .rd_b_addr 						(rd_b_addr  		),   //讀DDR3首地址
    .rd_e_addr 						(rd_e_addr 			),   //讀DDR3末地址
    .rd_len    						(rd_len 			),   //讀DDR3資料突發長度
    .rd_fifo_rst					(~sys_rst_n			),   //讀FIFO復位訊號
    .rd_fifo_num     				(rd_fifo_num     	),   //讀fifo中的資料量
	
    .read_valid      				(read_valid			),   //DDR3讀使能
		
	.app_en							(app_en				),	 //MIG IP傳送命令使能	
    .app_cmd						(app_cmd			),	 //MIG IP核操作命令,讀或者寫
    .app_addr						(app_addr			),	 //DDR3地址
	
    .app_rdy						(app_rdy			),	 //MIG 命令接收準備好標	
	.app_rd_data         			(app_rd_data        ),	 //從MIG中讀出的資料
	.app_rd_data_end     			(app_rd_data_end    ),	 //從MIG中讀出的最後一個資料
	.app_rd_data_valid   			(app_rd_data_valid  ),	 //從MIG中讀出的資料有效
    .app_wdf_rdy					(app_wdf_rdy		),	 //MIG資料接收準備好
    .app_wdf_wren					(app_wdf_wren		),	 //使用者寫資料使能
	.app_wdf_end					(app_wdf_end		),	 //突發寫當前時鐘最後一個資料
 	.app_wdf_mask					(app_wdf_mask		),
    .app_wdf_data					(app_wdf_data		)	//使用者寫資料	
);
//============================< 例化MIG IP核模組 >=============================================== 
mig_7series_0 u_mig_7series_0 (
	//DDR3介面-------------------------------------------
    .ddr3_addr                      (ddr3_addr			),   
    .ddr3_ba                        (ddr3_ba			),     
    .ddr3_cas_n                     (ddr3_cas_n			),  
    .ddr3_ck_n                      (ddr3_ck_n			),   
    .ddr3_ck_p                      (ddr3_ck_p			),   
    .ddr3_cke                       (ddr3_cke			),    
    .ddr3_ras_n                     (ddr3_ras_n			),  
    .ddr3_reset_n                   (ddr3_reset_n		),
    .ddr3_we_n                      (ddr3_we_n			),   
    .ddr3_dq                        (ddr3_dq			),     
    .ddr3_dqs_n                     (ddr3_dqs_n			),  
    .ddr3_dqs_p                     (ddr3_dqs_p			),                                                    
	.ddr3_cs_n                      (ddr3_cs_n			),   
    .ddr3_dm                        (ddr3_dm			),     
    .ddr3_odt                       (ddr3_odt			),
	//使用者介面-------------------------------------------    
    .app_addr                       (app_addr			),    
    .app_cmd                        (app_cmd			),     
    .app_en                         (app_en				),
    .app_rdy                        (app_rdy			), 	
    .app_wdf_mask                   (app_wdf_mask		),                                                    
    .app_wdf_rdy                    (app_wdf_rdy		), 	
    .app_wdf_data                   (app_wdf_data		),
    .app_wdf_end                    (app_wdf_end		), 
    .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_sr_req                     (1'b0				), 
    .app_ref_req                    (1'b0				), 
    .app_zq_req                     (1'b0				), 
    .app_sr_active                  (					),
    .app_ref_ack                    (					),  
    .app_zq_ack                     (					),
	//全域性訊號-------------------------------------------	
    .ui_clk                         (ui_clk				),       
    .ui_clk_sync_rst                (ui_clk_sync_rst	), 
    .init_calib_complete            (init_calib_complete), 														      
    .sys_clk_i                      (clk_200			),
    .clk_ref_i                      (clk_200			),
    .sys_rst                        (sys_rst_n			)     
);

endmodule

  本文主要參考文章:> https://blog.csdn.net/wuzhikaidetb/article/details/121841813
  本人水平不高,如有問題,歡迎各位批評指正。

相關文章