一、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
本人水平不高,如有問題,歡迎各位批評指正。