寫在前面
接上一篇文章(配置MIG IP程序):快速上手Xilinx DDR3 IP核(1)----MIG IP核的介紹及配置(Native介面)
1、官方例程(example design)
在我心中,Xilinx是一家完美的公司(自動忽略vivado編譯太慢),技術生態支持實在是做的太好了,Xilinx也知道我們不會用DDR3,所以提供了一個example design給你學習,怎么樣?驚不驚喜?意不意外?(實際上,絕大多數的IP Xilinx都提供了example design~~)
那么接下來就一起學習下官方提供的參考設計唄,
1.1、示例生成步驟
右擊生成的IP核(默認你已經生成了MIG IP核),選擇open IP example design,選擇好路徑后就會生成一個新的工程mig_7series_0_ex,

打開工程mig_7series_0_ex,看下整個工程的結構----2個主要部分:1、MIG IP核;2、讀寫測驗的資料生成模塊

讀寫測驗模塊我們不展開講(太多了,一時半會講不完),我們直接通過波形來學習,
點擊RUN SIMULATION,直接執行仿真(這里可能等幾分鐘(吃電腦配置)----強烈建議使用Modelsim仿真,速度快的不是一星半點)

1.2、仿真結果
仿真結果出來后,只保留一些比較有用的信號,并進行分組,如下:
找init_calib_complete拉高后(DDR3初始化完成)的一些區域時序圖,如下圖:
藍線、和黃線處,app_cmd為0,app_en、app_rdy均為高,代表在執行寫命令操作;同時app_wdf_data同地址一致,app_wdf_en、app_wdf_rdy均為高,代表在進行寫資料操作

如下圖:
很多個周期的app_rdy拉低,代表MIG沒有準備好進行寫資料命令,但此時app_wdf_data同地址一致,app_wdf_en、app_wdf_rdy均為高,代表在進行寫資料操作,這正是前面章節提到了寫資料提前與寫命令,因為MIG種有一個FIFO,將要寫入的資料存入FIFO,等到寫命令有效時,再對FIFO進行讀取,這樣做的好處是可以很大程度上做到寫命令和寫資料分離,

如下圖:
藍線處,app_cmd為1,app_en、app_rdy均為高,地址app_addr為0x0000200,代表在執行讀命令操作,經過若干個周期后,黃線處讀取資料有效標志信號app_rd_data_valid拉高,代表成功讀取到了一個資料,資料為0x0000200000020000002000000200,與前面寫入的一致

2、讀寫測驗模塊
通過上節對官方例程的學習,已經初步了解了DDR3的介面時序,接下來就自己撰寫一個簡單的讀寫測驗模塊來對DDR3操作一番,(不當云玩家)
2.1、Verilog代碼
讀寫測驗模塊預期要實作的功能:
- 寫入一定量的資料(可設定,默認512個)到DDR3,寫入地址從0開始
- 從地址0開始讀取之前寫入DDR3的資料,同時判斷讀、寫資料是否一致
- 回圈上兩個步驟,即寫、讀、寫、讀····
不妨先來回顧下DDR3提供的Native介面的時序,
DDR3 的讀或者寫都包含寫命令操作,其中寫操作命令(app_cmd)的值等于 0,讀操作 app_cmd 的值 等于 1,首先來看寫命令時序,如下圖所示,首先檢查 app_rdy,為高則表明此時 IP 核命令接收處于準備好 狀態,可以接收用戶命令,在當前時鐘拉高 app_en,同時發送命令(app_cmd)和地址(app_addr),此時命令和地址被寫入,

寫資料的時序,如下圖所示:

寫資料的情況有3種:
- 寫命令與寫資料發生在同一時鐘周期
- 寫資料先于寫命令發生(不一定是圖上的一個時鐘周期,因為資料是先寫到了FIFO)發生在同一時鐘周期
- 寫資料落后于寫命令發生,但不能超過兩個時鐘周期
結合上圖,寫時序總結如下:首先需要檢查 app_wdf_rdy,該信號為高表明此時 IP 核資料接收處于準備完成狀態,可以接收用戶發過來的資料,在當前時鐘拉高寫使能(app_wdf_wren),給出寫資料 (app_wdf_data),這樣加上發起的寫命令操作就可以成功向 IP 核寫資料,
接著來看讀資料,如下圖所示:

讀時序比較簡單,發出讀命令后,用戶只需等待資料有效信號(app_rd_data_valid)拉高,為高表明此 時資料總線上的資料是有效的回傳資料,需要注意的是,在發出讀命令后,有效讀資料要晚若干周期才出現在資料總線上 (延遲的時間不定),
2.1.1、PLL模塊
本來仿真是不需要PLL模塊的(直接寫時鐘激勵就行),但是考慮到等下還需要上板測驗,而我手頭的開發板主頻50M,MIG的作業時鐘卻是200M,所以需要PLL模塊生成作業時鐘,具體配置略,
2.1.2、DDR3仿真模型
在生成的example design中提供了DDR3的仿真模型,共兩個檔案:
1、DDR3仿真模型的頭檔案
2、DDR3仿真模型
找到這兩個檔案,把它們添加進我們自己的測驗工程里,

2.1.3、讀寫測驗模塊
讀寫測驗模塊生成對MIG IP核的控制時序,并使用一個狀態機來實作回圈寫、讀的程序,狀態機如下:

- IDLE:初始狀態,等MIG IP核初始化完成后跳轉到寫資料狀態WRITE
- WRITE:寫資料狀態,在這個狀態向MIG IP核寫入一定量的資料(測驗為512個),當寫入最后一個資料時,同步跳轉到等待狀態WAIT
- WAIT:過渡狀態,僅維持一個周期
- READ:讀資料狀態,在這個狀態從MIG IP核讀取一定量的資料(測驗為512個),當讀取最后一個資料時,同步跳轉到初始狀態IDLE,開始新一輪的寫、讀程序
前面說到,DDR3寫資料的時候有3種模式,我們在測驗的時候,固定使用“寫命令與寫資料發生在同一時鐘周期”這一模式,這樣一來,代碼撰寫會簡單很多,但相應的會犧牲一點點效率(不會造成多大的影響),
完整的讀寫模塊測驗代碼如下:
//**************************************************************************
// *** 名稱 : ddr3_rw
// *** 作者 : 孤獨的單刀
// *** 博客 : https://blog.csdn.net/wuzhikaidetb
// *** 日期 : 2021.12
// *** 描述 : 對DDR3進行回圈讀寫
//**************************************************************************
//============================< 埠 >======================================
module ddr3_rw #
(
parameter integer WR_LEN = 1024 , //讀、寫長度
parameter integer DATA_WIDTH = 128 , //資料位寬,突發長度為8,16bit,共128bit
parameter integer ADDR_WIDTH = 28 //根據MIG例化而來
)(
//DDR3相關 ------------------------------------------------------
input ui_clk , //用戶時鐘
input ui_clk_sync_rst , //復位,高有效
input init_calib_complete , //DDR3初始化完成
//DDR3相關 ------------------------------------------------------
input app_rdy , //MIG 命令接收準備好標致
input app_wdf_rdy , //MIG資料接收準備好
input app_rd_data_valid , //讀資料有效
input [DATA_WIDTH - 1:0] app_rd_data , //用戶讀資料
output reg [ADDR_WIDTH - 1:0] app_addr , //DDR3地址
output app_en , //MIG IP發送命令使能
output app_wdf_wren , //用戶寫資料使能
output app_wdf_end , //突發寫當前時鐘最后一個資料
output [2:0] app_cmd , //MIG IP核操作命令,讀或者寫
output reg [DATA_WIDTH - 1:0] app_wdf_data , //用戶寫資料
//指示 ----------------------------------------------------------
output reg error_flag //讀寫錯誤標志
);
//============================< 信號定義 >======================================
//測驗狀態機-----------------------------------------
localparam IDLE = 4'b0001 ; //空閑狀態
localparam WRITE = 4'b0010 ; //寫狀態
localparam WAIT = 4'b0100 ; //讀到寫過度等待
localparam READ = 4'b1000 ; //讀狀態
//reg define ----------------------------------------
reg [3:0] cur_state ; //三段式狀態機現態
reg [3:0] next_state ; //三段式狀態機次態
reg [ADDR_WIDTH - 1:0] rd_addr_cnt ; //用戶讀地址計數
reg [ADDR_WIDTH - 1:0] wr_addr_cnt ; //用戶寫地址計數
reg [ADDR_WIDTH - 1:0] rd_cnt ; //實際讀地址標記
//wire define ---------------------------------------
wire error ; //讀寫錯誤標記
wire rst_n ; //復位,低有效
wire wr_proc ; //拉高表示寫程序進行
wire wr_last ; //拉高表示寫入最后一個資料
wire rd_addr_last ; //拉高表示是最后一個讀地址
//*********************************************************************************************
//** main code
//**********************************************************************************************
//==========================================================================
//== 信號賦值
//==========================================================================
assign rst_n = ~ui_clk_sync_rst;
//當MIG準備好后,用戶同步準備好
assign app_en = app_rdy && ((cur_state == WRITE && app_wdf_rdy) || cur_state == READ);
//寫指令,命令接收和資料接收都準備好,此時拉高寫使能
assign app_wdf_wren = (cur_state == WRITE) && wr_proc;
//由于DDR3芯片時鐘和用戶時鐘的分頻選擇4:1,突發長度為8,故兩個信號相同
assign app_wdf_end = app_wdf_wren;
assign app_cmd = (cur_state == READ) ? 3'd1 :3'd0; //處于讀的時候命令值為1,其他時候命令值為0
assign wr_proc = ~app_cmd && app_rdy && app_wdf_rdy; //拉高表示寫程序進行
//處于寫使能且是最后一個資料
assign wr_last = app_wdf_wren && (wr_addr_cnt == WR_LEN - 1) ;
//處于讀指令、讀有效且是最后一個資料
assign rd_addr_last = (rd_addr_cnt == WR_LEN - 1) && app_rdy && app_cmd;
//==========================================================================
//== 狀態機
//==========================================================================
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
end
always @(*) begin
if(~rst_n)
next_state = IDLE;
else
case(cur_state)
IDLE:
if(init_calib_complete) //MIG IP核初始化完成
next_state = WRITE;
else
next_state = IDLE;
WRITE:
if(wr_last) //寫入最后一個資料
next_state = WAIT;
else
next_state = WRITE;
WAIT:
next_state = READ;
READ:
if(rd_addr_last) //寫入最后一個讀地址,資料讀出需要時間
next_state = IDLE;
else
next_state = READ;
default:;
endcase
end
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n) begin
app_wdf_data <= 0;
wr_addr_cnt <= 0;
rd_addr_cnt <= 0;
app_addr <= 0;
end
else
case(cur_state)
IDLE:begin
app_wdf_data <= 0;
wr_addr_cnt <= 0;
rd_addr_cnt <= 0;
app_addr <= 0;
end
WRITE:begin
if(wr_proc)begin //寫條件滿足
app_wdf_data <= app_wdf_data + 1; //寫資料自加
wr_addr_cnt <= wr_addr_cnt + 1; //寫地址自加
app_addr <= app_addr + 8; //DDR3 地址加8
end
else begin //寫條件不滿足,保持當前值
app_wdf_data <= app_wdf_data;
wr_addr_cnt <= wr_addr_cnt;
app_addr <= app_addr;
end
end
WAIT:begin
rd_addr_cnt <= 0; //讀地址復位
app_addr <= 0; //DDR3讀從地址0開始
end
READ:begin //讀到設定的地址長度
if(app_rdy)begin //若MIG已經準備好,則開始讀
rd_addr_cnt <= rd_addr_cnt + 1'd1; //用戶地址每次加一
app_addr <= app_addr + 8; //DDR3地址加8
end
else begin //若MIG沒準備好,則保持原值
rd_addr_cnt <= rd_addr_cnt;
app_addr <= app_addr;
end
end
default:begin
app_wdf_data <= 0;
wr_addr_cnt <= 0;
rd_addr_cnt <= 0;
app_addr <= 0;
end
endcase
end
//==========================================================================
//== 其他
//==========================================================================
//讀信號有效,且讀出的數不是寫入的數時,將錯誤標志位拉高
assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));
//寄存狀態標志位
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n)
error_flag <= 0;
else if(error)
error_flag <= 1;
end
//對DDR3實際讀資料個數編號計數
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n)
rd_cnt <= 0;
//若計數到讀寫長度,且讀有效,地址計數器則置0
else if(app_rd_data_valid && rd_cnt == WR_LEN - 1)
rd_cnt <= 0;
else if (app_rd_data_valid ) //讀有效情況下每個時鐘+1
rd_cnt <= rd_cnt + 1;
end
endmodule
注釋還是比較詳細的,就不講解了,只說幾個需要注意的點,
- DDR3的突發長度固定為8,選擇的DDR3芯片為16bit,所以資料位寬 = 8 * 16bit = 128bit
- DDR3的突發長度固定為8,等于每次操作實際上是對8個地址操作,所以每次寫入資料的地址需要累加8
2.1.4、頂層模塊
頂層模塊的設計十分簡單:只要例化上述3個模塊即可,如下:
//**********************************************************************************
// *** 名稱 : ddr3_rw_top
// *** 作者 : 孤獨的單刀
// *** 博客 : https://blog.csdn.net/wuzhikaidetb
// *** 日期 : 2021.12
// *** 描述 : 頂層模塊----例化DDR3仿真模型與DDR3讀寫測驗模塊,對DDR3進行回圈讀寫測驗
//**********************************************************************************
//============================< 埠 >======================================
module ddr3_rw_top(
//時鐘和復位 ---------------------------
input sys_clk , //系統時鐘
input sys_rst_n , //復位,低有效
//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
//標志相關------------------------------
output error_flag //錯誤指示信號
);
//============================< 信號定義 >======================================
//parameter define
parameter integer WR_LEN = 512 ; //讀、寫長度
parameter integer DATA_WIDTH = 128 ; //資料位寬,突發長度為8,16bit,共128bit
parameter integer ADDR_WIDTH = 28 ; //根據MIG例化而來
//wire define
wire ui_clk ; //用戶時鐘
wire [ADDR_WIDTH - 1:0] app_addr ; //DDR3 地址
wire [2:0] app_cmd ; //用戶讀寫命令
wire app_en ; //MIG IP核使能
wire app_rdy ; //MIG IP核空閑
wire [DATA_WIDTH - 1:0] app_rd_data ; //用戶讀資料
wire app_rd_data_end ; //突發讀當前時鐘最后一個資料
wire app_rd_data_valid ; //讀資料有效
wire [DATA_WIDTH - 1:0] app_wdf_data ; //用戶寫資料
wire app_wdf_end ; //突發寫當前時鐘最后一個資料
wire [15:0] app_wdf_mask ; //寫資料屏蔽
wire app_wdf_rdy ; //寫空閑
wire app_wdf_wren ; //DDR3 寫使能
wire locked ; //鎖相環頻率穩定標志
wire clk_ref_i ; //DDR3參考時鐘
wire sys_clk_i ; //MIG IP核輸入時鐘
wire clk_200 ; //200M時鐘
wire ui_clk_sync_rst ; //用戶復位信號
wire init_calib_complete ; //校準完成信號
//*****************************************************************************************
//** main code
//*****************************************************************************************
//============================< 例化DDR3讀寫測驗模塊 >======================================
ddr3_rw #(
.WR_LEN (WR_LEN ),
.DATA_WIDTH (DATA_WIDTH ),
.ADDR_WIDTH (ADDR_WIDTH )
)
u_ddr3_rw(
.ui_clk (ui_clk ),
.ui_clk_sync_rst (ui_clk_sync_rst ),
.init_calib_complete (init_calib_complete ),
.app_rdy (app_rdy ),
.app_wdf_rdy (app_wdf_rdy ),
.app_rd_data_valid (app_rd_data_valid ),
.app_rd_data (app_rd_data ),
.app_addr (app_addr ),
.app_en (app_en ),
.app_wdf_wren (app_wdf_wren ),
.app_wdf_end (app_wdf_end ),
.app_cmd (app_cmd ),
.app_wdf_data (app_wdf_data ),
.error_flag (error_flag )
);
//============================< 例化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_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_wdf_mask (31'b0 ),
.app_rdy (app_rdy ),
.app_wdf_rdy (app_wdf_rdy ),
.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 )
);
//============================< 例化PLL模塊 >===============================================
clk_wiz_0 u_clk_wiz_0
(
.clk_out1 (clk_200 ), // output clk_out1
.reset (1'b0 ), // input resetn
.locked (locked ), // output locked
.clk_in1 (sys_clk ) // input clk_in1
);
endmodule
2.2、Testbench及仿真結果
好的,Verilog代碼寫完了,接下來就跑跑仿真驗證以下,
2.2.1、Testbench
Testbench也很簡單,只需要提供時鐘、復位激勵;例化讀寫測驗模塊及DDR3仿真模型就好了,如下:
//**************************************************************************
// *** 名稱 : tb_ddr3_rw_top
// *** 作者 : 孤獨的單刀
// *** 博客 : https://blog.csdn.net/wuzhikaidetb
// *** 日期 : 2021.12
// *** 描述 : 對DDR3進行回圈讀寫測驗
//**************************************************************************
`timescale 1ns/100ps
module tb_ddr3_rw_top();
//============================< 信號 >======================================
//時鐘和復位 --------------------
reg sys_clk ; //系統時鐘
reg sys_rst_n ; //系統復位
//DDR3相關 ----------------------
wire [15:0] ddr3_dq ; //DDR3 資料
wire [1:0] ddr3_dqs_n ; //DDR3 dqs負
wire [1:0] ddr3_dqs_p ; //DDR3 dqs正
wire [13:0] ddr3_addr ; //DDR3 地址
wire [2:0] ddr3_ba ; //DDR3 banck 選擇
wire ddr3_ras_n ; //DDR3 行選擇
wire ddr3_cas_n ; //DDR3 列選擇
wire ddr3_we_n ; //DDR3 讀寫選擇
wire ddr3_reset_n ; //DDR3 復位
wire [0:0] ddr3_ck_p ; //DDR3 時鐘正
wire [0:0] ddr3_ck_n ; //DDR3 時鐘負
wire [0:0] ddr3_cke ; //DDR3 時鐘使能
wire [0:0] ddr3_cs_n ; //DDR3 片選
wire [1:0] ddr3_dm ; //DDR3_dm
wire [0:0] ddr3_odt ; //DDR3_odt
//標志相關-----------------------
wire error_flag ; //讀、寫錯誤標志
//============================< 測驗條件設定 >===============================
//設定初始測驗條件--------------------------------------------------------
initial begin
sys_clk = 1'b0 ; //初始時鐘為0
sys_rst_n <= 1'b0 ; //初始復位
#50 //5個時鐘周期后
sys_rst_n <= 1'b1 ; //拉高復位,系統進入作業狀態
end
//設定時鐘----------------------------------------------------------------
always #10 sys_clk = ~sys_clk; //系統時鐘周期20ns
//============================< 被測驗模塊例化 >===============================
//例化DDR3讀寫測驗-------------------
ddr3_rw_top ddr3_rw_top_inst(
.sys_clk (sys_clk ), //系統時鐘
.sys_rst_n (sys_rst_n ), //復位,低有效
.ddr3_dq (ddr3_dq ), //DDR3 資料
.ddr3_dqs_n (ddr3_dqs_n ), //DDR3 dqs負
.ddr3_dqs_p (ddr3_dqs_p ), //DDR3 dqs正
.ddr3_addr (ddr3_addr ), //DDR3 地址
.ddr3_ba (ddr3_ba ), //DDR3 banck 選擇
.ddr3_ras_n (ddr3_ras_n ), //DDR3 行選擇
.ddr3_cas_n (ddr3_cas_n ), //DDR3 列選擇
.ddr3_we_n (ddr3_we_n ), //DDR3 讀寫選擇
.ddr3_reset_n (ddr3_reset_n ), //DDR3 復位
.ddr3_ck_p (ddr3_ck_p ), //DDR3 時鐘正
.ddr3_ck_n (ddr3_ck_n ), //DDR3 時鐘負
.ddr3_cke (ddr3_cke ), //DDR3 時鐘使能
.ddr3_cs_n (ddr3_cs_n ), //DDR3 片選
.ddr3_dm (ddr3_dm ), //DDR3_dm
.ddr3_odt (ddr3_odt ), //DDR3_odt
.error_flag (error_flag ) //錯誤標志
);
//例化DDR3模型-----------------------
ddr3_model ddr3_model_inst
(
.rst_n (sys_rst_n ),
.ck (ddr3_ck_p ),
.ck_n (ddr3_ck_n ),
.cke (ddr3_cke ),
.cs_n (ddr3_cs_n ),
.ras_n (ddr3_ras_n ),
.cas_n (ddr3_cas_n ),
.we_n (ddr3_we_n ),
.dm_tdqs (ddr3_dm ),
.ba (ddr3_ba ),
.addr (ddr3_addr ),
.dq (ddr3_dq ),
.dqs (ddr3_dqs_p ),
.dqs_n (ddr3_dqs_n ),
.tdqs_n ( ), //NULL
.odt (ddr3_odt )
);
endmodule
2.2.2、仿真結果
仿真我們不用Vivado跑了(不是說慢,是真慢),改用Modelsim跑,Modelsim跑仿真速度快,來來回回的修改除錯也方便,
廢話少說,仿真結果如下:

上圖中:MIG初始化完成信號init_calib_complete拉高,表示MIG控制器初始化完成,然后,進入開始寫資料,再讀資料,如此不停重復,

上圖是:寫資料程序的開頭部分:
寫入地址從0開始累加,每次累加8,因為突發長度為8;
寫入資料從0開始累加,每次累加1;
當命令有效、寫命令有效,且寫指令為低(代表寫)時,資料被寫入,

上圖是:寫資料程序的結束部分:
寫入地址從0開始累加,每次累加8,因為突發長度為8,到4088截止;
寫入資料從0開始累加,每次累加1,到511截止,共寫入512個資料;
當命令有效、寫命令有效,且寫指令為低(代表寫)時,資料被寫入,

上圖是:讀資料程序的開始部分:
讀取地址從0開始累加,每次累加8,因為突發長度為8;
從發出讀取指令到真正讀到資料需要一定的時間;
當命令有效,且指令為高(代表讀)時,資料被讀出;
讀出的資料與寫入的資料一致(0-1-2-······),

上圖是:讀資料程序的結束部分:
讀取地址從0開始累加,每次累加8,因為突發長度為8,到4088;
從發出讀取指令到真正讀到資料需要一定的時間;
當命令有效,且指令為高(代表讀)時,資料被讀出;
讀出的資料與寫入的資料一致(0-1-2-······511),
3、上板驗證
3.1、驗證環境
FPGA:XC7A35T FGG484-2
DDR3:NT5CB128M16CP-DI
Vivado:Vivado 2019.2
Modelsim:Modelsim SE-64 2020.4
檔案:V1.0
編號:71
3.2、驗證結果
結果基本與仿真結果一致,寫、讀驗證無誤,讀取資料一致,截幾張圖貼出來
初始化完成后進行的寫操作:

讀操作:

至此,讀寫模塊驗證成功,
3、其他
3.1、可能遇到的問題:
3.1.1、Vivado如何聯合modelsim仿真?
使用modelsim仿真xilinx的IP核,主要需要解決仿真庫的問題,不得不說modelsim是真的好用,
供參考:vivado與modelsim的聯合仿真
3.1.2、使用modelsim仿真DDR3報錯Module ‘SIP_PHY_CONTROL‘ is not defined
沒找到什么好辦法,重裝了新版本的modelsim(2020.4版本),重裝以后問題解決,
3.1.3、VIVADO呼叫 ModelSim一直無回應
基本上是語法錯誤,請仔細檢查,
供參考:Xilinx VIVADO 仿真時無法呼叫 ModelSim 失敗的解決辦法
3.1.4、app_rdy信號被拉高一段時間后,然后一直為低的問題
下面三個保留的信號全接到0后解決(Xilinx都在手冊告訴你這么做了,你又不聽,哎······)

3.2、后記
- 可以看到,Native介面其實相對來講已經很好用了,但是有沒有辦法用起來更方便一點?當然有咯!FIFO用起來方不方便,一個使能信號就能寫資料了,我們接下來就把MIG給封裝成一個FIFO來用,大大簡化作業量,
- 創作不易,如果本文對您有幫助,還請多多點贊、評論和收藏,您的支持是我持續更新的最大動力!
- 關于本文,您有什么想法均可在評論區留言交流,如果需要整個工程,請在評論留下郵箱或者私信我郵箱(注意保護隱私),
- 自身能力不足,如有錯誤還請多多指出!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/375811.html
標籤:其他
