一、前言
-
關於Vivado MIG IP核詳細配置可以參考我之前的文章:基於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_photo
和timing_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
四、仿真結果
寫操作時序:
讀操作時序:
可以看到,寫進去的數據和讀出來的數據是一致的!!!