Spartan6 FPGA DDR3自建寫測試平台
FPGA
先簡單總結一下前面建立FPGA DDR3 IP Core,以及簡單簡述一下DDR3 IP Core的信号。
1.DDR3 IP core建立的時候,如果網上沒有搭建這個IP核的資料,我們要怎麼搭建呢? 2.打開後得到這個文檔,點選Click here,可以獲得這個IP Core的搭建設定方法 3.點選Click here後,得到User Guide手冊
DDR3 IP Core各個信号,檢視MCB這個文檔 他的引腳信号分布 我們控制的就是右邊的IOB信号。左邊的信号有幾個端口就要看你建立IP核的時候的Port設定了。 因為我之前建立IP核的時候是将他設定成了2個64bit的Port。是以這裡是有P0和P1信号端口。 接口的分類
command path 中比較有用的幾個信号
(c3_p0_cmd_clk),//cmd FIFO的使用者時鐘,上升沿有效
(c3_p0_cmd_en), //該高電平有效信号是用于寫入的寫入使能信号
(c3_p0_cmd_instr),//指令端口
(c3_p0_cmd_bl),//突發長度,0-63
(c3_p0_cmd_byte_addr),//位元組開始的位址,他的後面幾位不能為0
(c3_p0_cmd_empty),//FIFO的空标志,高有效
(c3_p0_cmd_full),//FIFO的滿标志
由MCB的手冊可知,這些信号都是Command path信号
那我們如何向DDR3寫入command呢?打開目錄,找到command path timing,找到他的時序圖,根據手冊時序圖寫程式就可以向DDR3寫入指令。 command path的讀寫時序。
寫時序
注意:這裡需要注意時鐘,這裡有好幾個時鐘,command path timing(write)時鐘是cmd_clk,這個時鐘主要是用來檢測cmd_en信号的,而我們如果需要将instr指令端口信号寫入DDR3,則使用的是Wr_clk
我們看看cmd_clk和wr_clk手冊怎麼說的吧
cmd_clk wr_clk
write_path_timing
那cmd和wirte是怎麼協調的呢?
我們可以看到在command path timing 時序和write path timing都可以看到cmd和write都有調用到FIFO。cmd_FIFO存儲的是指令指令,write data fifo裡面存儲是要寫入到DDR3中的資料。那我們要往DDR3寫入資料,那麼DDR3先讀入cmd_instr的資料。先知道是讀操作還是寫操作。得知是寫操作後,會去通路Write Data FIFO裡面的資料,将Write FIFO裡面的資料寫入DDR3 FIFO。讀操作,就是講Read FIFO裡面的資料讀出來。是以,在寫指令的時候,我們需要確定DDR3的Wr_data_FIFO裡面是有資料的。是以我們要将上面的步驟反過來,先向Write data fifo寫入想要DDR3晶片内部資料,之後再向cmd fifo中寫入相應的寫指令。這樣的話,我們的時序圖可以這麼寫
cmd_en也就是去檢測wr_en的下降沿。
代碼的實作
将頂層檔案DDR3的command 引腳和 write引腳引出來。
因為我們是仿真時序,是以我們建立一個wr_trig來控制wr_en。
仿真時序圖
建立一個ddr3_drive檔案。
上圖是ddr3_drive的引腳資訊。因為我們是仿真,是以我們将instr直接固定成3'b000寫模式,将Bl,mask也固定。
ddr3_drive代碼:
module ddr3_drive(
//system signal
input s_clk ,
input s_rst_n ,
//DDR3 User Interface
output wire p0_cmd_en ,
output wire [2:0] p0_cmd_instr ,
output wire [5:0] p0_cmd_bl ,
output wire [29:0] p0_cmd_byte_addr ,
output reg p0_wr_en ,
output wire [7:0] p0_wr_mask ,
output reg [63:0] p0_wr_data ,
//Debug
input wr_trig
);
//========================================================================\
// =========== Define Parameter and Internal signals ===========
//========================================================================/
reg p0_wr_en_r1 ; //用于捕獲wr_en的下降沿
//=============================================================================
//************** Main Code **************
//=============================================================================
always @(posedge s_clk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
p0_wr_en <= 'd0;
else if(p0_wr_data >= 'd15)
p0_wr_en <= 'd0;
else if(wr_trig == 1'b1)
p0_wr_en <= 1'b1;
end
always @(posedge s_clk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
p0_wr_data <= 'd0;
else if(p0_wr_en == 1'b1)
p0_wr_data <= p0_wr_data + 1'b1;
end
always @(posedge s_clk) begin
p0_wr_en_r1 <= p0_wr_en;
end
assign p0_cmd_en = ~p0_wr_en & p0_wr_en_r1;
assign p0_cmd_instr = 3'b000;//write
assign p0_cmd_bl = 'd15;
assign p0_cmd_byte_addr = 'd0;
assign p0_wr_mask = 8'b0;
endmodule
頂層檔案将ddr3_drive檔案例化進去。 注意:這裡給ddr3_drive的是ddr3例化檔案給出的user clock和user reset。reset是高電平有效的,即高電平複位。是以給ddr3_drive傳入的是~c3_rst0。需要去一次反。
讓ddr3_drive的信号與ddr3的例化檔案信号一緻。
将cmd_clk和wr_clk都用c3_clk0傳入。
在top檔案中再聲明一下即可。
接下來就是寫仿真的腳本檔案了
主要就是去産生一個wr_trig信号
代碼如下
initial begin
wr_trig = 0;
@(posedge c3_calib_done)
#10_0000;//10ns
wr_trig = 1;
#25600;
wr_trig = 0;
end
因為ddr3的操作要在calib_done完成後(calib_done是指MCB初始化完成的信号)才能讀寫ddr3.是以top檔案也需要将c3_calib_done引出,在tb檔案中例化進來。
接着因為我們需要檢視ddr3_drive_inst,u_mig_39_2的信号,為了提高效率,我們通過tcl腳本添加。 在工程檔案中有fdo和udo兩個檔案。這就相當于我們平時自己寫的do檔案。fdo是ISE産生的,一般不改變,udo是ISE給使用者添加的do檔案。udo也就是user do的意思。 将分組和添加信号的腳本解除安裝udo檔案中。 點選開始仿真。
波形的分析
我們要先找到wr_trig信号。因為真正的資料端口是ddr3_dq引腳,我們主要檢視的是wr_trig拉高後的ddr3_dq引腳信号。看波形我們可以知道wr_trig引腳在34855200ps後拉高。我們就去看這個時間後的列印資訊。
注意:因為我們FPGA DDR3 IP是64bit port,而我們DDR3實際是16bit的port。是以我們這裡是并不是0000,0001,0002這樣下去。
而是這樣四個一組的。
因為我們測試是0-15,是以最終到0x0f,看列印資訊,可知仿真正确。
歡迎關注微信公衆号:文鴻開源工作室