0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

Hx ? 作者:工程師陳翠 ? 2018-06-28 11:30 ? 次閱讀

最近項目需要用到DDR,于是在網(wǎng)上找相關資料,發(fā)現(xiàn)網(wǎng)上關于Xilinx DDR的資料不多,而且比較老,官方文檔又是純英文,且超級長。所以筆者寫了這篇文章,為像筆者一樣的初學者介紹一下DDR的使用。

在此不介紹DDR是什么了,請自行查資料。(相信用到這篇文章的人不會不知道DDR是啥吧。。。)

好了,閑話休提言歸正傳。

本文使用Vivado 2015.4在Nexys4 DDR(以下簡稱N4DDR)開發(fā)板上實現(xiàn)DDR的讀寫。

FPGA如果需要對DDR進行讀寫,則需要一個DDR的控制器。根據(jù)官方的文檔(UG586,下載鏈接在文末),DDR控制器的時序主要有三:

(1)首先是控制信號,如下圖:

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

從上圖可以看出,只有當app_rdy信號有效時,程序所發(fā)出的讀寫命令才會被控制器接收。這點必須注意。

(2)然后是寫操作時序,如下圖:

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

由圖可知,在向DDR寫數(shù)據(jù)時,需要提供寫命令app_cmd、地址app_addr、數(shù)據(jù)app_wdf_data等信號,且寫入的數(shù)據(jù)最多可以比app_cmd提前一個時鐘周期有效,最遲可以比app_cmd晚兩個時鐘周期有效。

【特別注意】在寫數(shù)據(jù)的時候必須檢測app_rdy和app_wdf_rdy信號是否同時有效,否則寫入命令無法成功寫入到DDR控制器的命令FIFO中,從而導致寫操作失敗。

(3)最后是讀操作時序,如下圖所示:

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

讀操作的時序比較簡單,只需要注意app_rdy是否有效即可,其余不再贅述。

Xilinx在Vivado中提供的Memory Interface Generator的IP核就是我們需要的DDR控制器,如下圖所示。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

這里我們可以直接雙擊上面的MIG的IP核,開始例化我們所需的DDR控制器。(此時Win7以后的Windows版本(不含Win7)打開此IP核會報錯,解決方法見 )

打開后是如下圖所示的界面,點Next。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

給模塊起個名字,根據(jù)實際情況選擇控制器數(shù)量(這里筆者選擇1),繼續(xù)Next,如下圖所示。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

在開發(fā)板芯片型號所對應的方框前打勾,如下圖所示。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

根據(jù)開發(fā)板上的DDR芯片選擇DDR的種類,如N4DDR的開發(fā)板上的DDR芯片是DDR2的,因此如下圖選擇。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

然后在Clock Period中輸入合適的時鐘周期長度(N4DDR的官方文檔建議DDR的時鐘為325MHz,故此處填3077ps);

接著在Memory Part中選擇開發(fā)板上的DDR芯片的具體型號(N4DDR官方文檔上說明為MT47H64M16HR-25E);

然后輸入Data Width,此處以16為例。如下圖所示。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

選擇Input Clock Period,這里填開發(fā)板的系統(tǒng)時鐘(N4DDR為100MHz)。根據(jù)應用需要選擇地址映射方式(這里保持默認的Bank-Row-Column)。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

然后,這里的System Clock、Reference Clock建議選擇No Buffer,System Reset Polarity則根據(jù)應用需要靈活選擇(這里設置為低電平有效),如下圖所示。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

Internal Termination Impedence的選取應當參考開發(fā)板的官方文檔說明,這里選50歐姆即可,繼續(xù)Next。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

選擇Fixed Pin Out。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

接下來是DDR芯片的引腳分配。官網(wǎng)應該能找到,這里直接給出。文末會給出與此對應的引腳約束文件(n4ddr_ddr2_io_assign.ucf)。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

耐心填完之后點擊Validate按鈕,沒有錯誤的話會彈出一個對話框提示“Current Pinout is valid.”

然后的3個信號建議選擇No connect,后面由我們自己根據(jù)需要連接到板上的相應引腳。

使用Vivado 2015.4在Nexys4 DDR開發(fā)板上實現(xiàn)DDR的讀寫例程

后面一直Next下去,點Accept,然后就可以點擊Generate了。后面會再彈出一個對話框,直接點默認選中的按鈕即可。

好了,下面是筆者自己編寫的測試DDR2讀寫的程序。文末將提供對應工程的下載鏈接。
//*****************************************************************************
// Author : Z.M.J. @ CSE, SEU
// Application : MIG v2.4
// Filename : example_top.v
// Date Created : Fri Dec 30 2016
//
// Device : 7 Series (Nexys 4 DDR)
// Design Name : DDR2 SDRAM
// Purpose : A demo of DDR2's read and write
// Reference : ug586_7Series_MIS_v2.4.pdf
//*****************************************************************************

`timescale 1ps/1ps

module example_top (
// system signals
input sys_rst,
input sys_clk_i,
// application signals
input [15:0] switch_i,
output [15:0] led,
output [7:0] an,
output [7:0] select_seg,
// DDR2 chip signals
inout [15:0] ddr2_dq,
inout [1:0] ddr2_dqs_n,
inout [1:0] ddr2_dqs_p,
output [12:0] ddr2_addr,
output [2:0] ddr2_ba,
output ddr2_ras_n,
output ddr2_cas_n,
output ddr2_we_n,
output [0:0] ddr2_ck_p,
output [0:0] ddr2_ck_n,
output [0:0] ddr2_cke,
output [0:0] ddr2_cs_n,
output [1:0] ddr2_dm,
output [0:0] ddr2_odt
);

parameter DQ_WIDTH = 16;
parameter ECC_TEST = "OFF";
parameter ADDR_WIDTH = 27;
parameter nCK_PER_CLK = 4;

localparam DATA_WIDTH = 16;
localparam PAYLOAD_WIDTH = (ECC_TEST == "OFF") ? DATA_WIDTH : DQ_WIDTH;
localparam APP_DATA_WIDTH = 2 * nCK_PER_CLK * PAYLOAD_WIDTH;
localparam APP_MASK_WIDTH = APP_DATA_WIDTH / 8;

// Wire declarations
reg app_en, app_wdf_wren, app_wdf_end;
reg [2:0] app_cmd;
reg [ADDR_WIDTH-1:0] app_addr;
reg [APP_DATA_WIDTH-1:0] app_wdf_data;
wire [APP_DATA_WIDTH-1:0] app_rd_data;
wire [APP_MASK_WIDTH-1:0] app_wdf_mask;
wire app_rdy, app_rd_data_end, app_rd_data_valid, app_wdf_rdy;

//***************************************************************************
wire [7:0] an;
wire [7:0] select_seg;
reg [31:0] digit_data;
always@ (posedge sys_clk_i) begin
if (switch_i[3])
digit_data <= app_addr;
else case (switch_i[1:0])
2'b00 : digit_data <= read_data[31:0];
2'b01 : digit_data <= read_data[63:32];
2'b10 : digit_data <= read_data[95:64];
2'b11 : digit_data <= read_data[127:96];
endcase
end

digit U2(
.wb_clk_i(sys_clk_i),
.wb_rst_i(~sys_rst),
.wb_dat_i(digit_data),
.an(an),
.select_seg(select_seg)
);

reg [1:0] read_valid = 2'b0;
reg [127:0] read_data = 128'h0;
always@ (posedge app_rd_data_valid) begin
read_data = app_rd_data;
read_valid[0] = (app_rd_data == data0);
read_valid[1] = (app_rd_data == data1);
end

assign led[15] = app_en;
assign led[14] = init_calib_complete;
assign led[13] = app_rdy;
assign led[12] = app_wdf_rdy;
assign led[4] = sys_rst ? read_valid[1] : 1'b0;
assign led[3] = sys_rst ? read_valid[0] : 1'b0;
assign led[2] = stop_w[1];
assign led[1] = stop_w[0];
assign led[0] = app_cmd[0];

reg [15:0] counter = 16'h0;
parameter cnt_init = 16'h1; // minimum: 1
reg [26:0] addr0 = 27'h000_0008;
reg [26:0] addr1 = 27'h003_0100;
reg [127:0] data0 = 128'h1111_2222_3333_4444_5555_6666_7777_8888;
reg [127:0] data1 = 128'h9999_0000_aaaa_bbbb_cccc_dddd_eeee_ffff;
reg [1:0] stop_w = 2'b00;
always@ (posedge sys_clk_i or negedge sys_rst) begin
if (sys_rst == 1'b0) begin
counter = 12'b0;
stop_w = 2'b0;
app_en = 1'b0;
app_addr = 27'h0;
app_cmd = 3'b1;
app_wdf_data = 128'h0;
app_wdf_end = 1'b0;
app_wdf_wren = 1'b0;
end else begin
if (counter == cnt_init && ~stop_w[0])
if (app_rdy & app_wdf_rdy) begin
app_wdf_data = data0;
app_addr = addr0;
app_cmd = 3'b0;
app_wdf_wren = 1'b1;
app_wdf_end = 1'b1;
app_en = 1'b1;
end else // Hold specific signals until app_wdf_rdy is asserted.
counter = counter - 16'h1;
else if (counter == cnt_init + 1 && ~stop_w[0])
if (app_rdy & app_wdf_rdy) begin
app_wdf_end = 1'b0;
app_wdf_wren = 1'b0;
app_en = 1'b0;
app_cmd = 3'b1;
stop_w[0] = 1'b1;
end else // Hold specific signals until app_wdf_rdy is asserted.
counter = counter - 16'h1;
else if (counter == cnt_init + 8 && ~stop_w[1])
if (app_rdy & app_wdf_rdy) begin
app_wdf_data = data1;
app_addr = addr1;
app_cmd = 3'b0;
app_wdf_wren = 1'b1;
app_wdf_end = 1'b1;
app_en = 1'b1;
end else // Hold specific signals until app_wdf_rdy is asserted.
counter = counter - 16'h1;
else if (counter == cnt_init + 9 && ~stop_w[1])
if (app_rdy & app_wdf_rdy) begin
app_wdf_end = 1'b0;
app_wdf_wren = 1'b0;
app_en = 1'b0;
app_cmd = 3'b1;
stop_w[1] = 1'b1;
end else // Hold specific signals until app_wdf_rdy is asserted.
counter = counter - 16'h1;
else if (counter == cnt_init + 88) begin
app_addr = switch_i[2] ? addr1 : addr0;
app_en = 1'b1;
if (~app_rdy) counter = counter - 16'h1;
end else if (counter == cnt_init + 89)
app_en = 1'b0;

counter = counter + 16'h1;
end
end

// 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.
//***************************************************************************
my_ddr u_my_ddr (
// Memory interface ports
.ddr2_cs_n (ddr2_cs_n),
.ddr2_addr (ddr2_addr),
.ddr2_ba (ddr2_ba),
.ddr2_we_n (ddr2_we_n),
.ddr2_ras_n (ddr2_ras_n),
.ddr2_cas_n (ddr2_cas_n),
.ddr2_ck_n (ddr2_ck_n),
.ddr2_ck_p (ddr2_ck_p),
.ddr2_cke (ddr2_cke),
.ddr2_dq (ddr2_dq),
.ddr2_dqs_n (ddr2_dqs_n),
.ddr2_dqs_p (ddr2_dqs_p),
.ddr2_dm (ddr2_dm),
.ddr2_odt (ddr2_odt),
// Application interface ports
.app_addr (app_addr),
.app_cmd (app_cmd),
.app_en (app_en),
.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_rdy (app_rdy),
.app_sr_req (1'b0),
.app_ref_req (1'b0),
.app_zq_req (1'b0),
.app_wdf_mask (16'h0000),
.init_calib_complete (init_calib_complete),
// System Clock Ports
.sys_clk_i (sys_clk_i),
// Reference Clock Ports
.clk_ref_i (sys_clk_i),
.sys_rst (sys_rst)
);

endmodule

保存后直接生成比特流就可以下板驗證了。

在摸索過程中筆者發(fā)現(xiàn),寫入了數(shù)據(jù)之后最快要到發(fā)出寫命令的第8個系統(tǒng)時鐘才能讀出所寫入的數(shù)據(jù),且讀操作必須在寫操作后經(jīng)過8的整數(shù)倍個時鐘后進行。有時將比特流下載到N4DDR上面之后讀寫的數(shù)據(jù)有誤,但是重啟開發(fā)板再重新下載即可解決問題,知道個中緣由的朋友歡迎在評論中告知筆者,筆者在此先行謝過。

需要說明的是,此處突發(fā)長度(BL)為8,因此app_addr必須是8對齊的地址。同時,由于前面選擇的Data Width為16,因此每次讀寫數(shù)據(jù)的長度為8*16bit==128bit。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • DDR
    DDR
    +關注

    關注

    11

    文章

    704

    瀏覽量

    65164
  • Vivado
    +關注

    關注

    19

    文章

    804

    瀏覽量

    66224
收藏 人收藏

    評論

    相關推薦

    基于Digilent的Arty Artix-35T FPGA開發(fā)板DDR3讀寫控制

    開發(fā)板完成。 軟件使用Vivado 2018.1。 參考工程:ddr3_test。 第五篇:mig讀寫時序下板實現(xiàn) 1頂層文件和約束文件
    的頭像 發(fā)表于 12-15 16:45 ?2789次閱讀
    基于Digilent的Arty Artix-35T FPGA<b class='flag-5'>開發(fā)板</b>的<b class='flag-5'>DDR</b>3<b class='flag-5'>讀寫</b>控制

    基于Arty Artix-35T FPGA開發(fā)板DDR3和mig介紹

    講解xilinx FPGA 使用mig IP對DDR3的讀寫控制,旨在讓大家更快的學習和應用DDR3。 本實驗和工程基于Digilent的Arty Artix-35T FPGA開發(fā)板
    的頭像 發(fā)表于 01-01 10:09 ?4138次閱讀
    基于Arty Artix-35T FPGA<b class='flag-5'>開發(fā)板</b>的<b class='flag-5'>DDR</b>3和mig介紹

    開發(fā)板的PL端有4片的DDR,怎么管理數(shù)據(jù)的傳輸

    各位大佬好, 我現(xiàn)在想在自己的開發(fā)板上面跑通正點原子的一個例程,這個例程是關于DDR讀寫,將1024個數(shù)寫入
    發(fā)表于 09-10 19:17

    如何在Kintex7 KC705評估實現(xiàn)一個UART模塊?

    親愛的朋友們,我想在Kintex7 KC705評估實現(xiàn)一個UART模塊,我無法正常工作。我nexys4
    發(fā)表于 10-11 09:04

    使用Vivado VHDL無法運行第一個bitfile

    嗨,我剛剛購買了我的第一個FPGA開發(fā)板,一個Nexys4 DDR,我無法運行第一個bitfile。我正在做Vivado VHDL教程。仿真工作,我可以生成一個位文件,但是當我對電路
    發(fā)表于 03-27 06:54

    如何在Nexys4ddr中使用DDR內(nèi)存模塊

    嗨,我已經(jīng)看過如何在Nexys4DDR中使用BRAM,但是無法找到關于如何使用DDR內(nèi)存的任何文檔或視頻。任何人都可以指導我嗎?
    發(fā)表于 05-25 14:28

    verilog實現(xiàn)步進電機脈沖分配器(三相六拍)基于Nexys4DDR開發(fā)板 相關資料下載

    使用Verilog HDL設計的一個能夠自啟動、具有正反轉功能的三相六拍步進電機脈沖分配器。已經(jīng)寫好Nexy4DDR的管腳約束文件可以通過Nexys4DDR開發(fā)板的PMOD接口連接步進電機進行調試
    發(fā)表于 06-30 07:56

    電腦無法識別nexys4開發(fā)板

    電腦型號是華為matebookxpro,一直識別不了nexys4,想問一下大家遇到過這種情況嗎,有沒有解決辦法呢
    發(fā)表于 05-24 20:01

    基于Nexys4開發(fā)板的VGA顯示實驗(使用ISE 14.3)

    基于Nexys4開發(fā)板的VGA顯示實驗(使用 ISE 14.3 開發(fā)套件)。
    發(fā)表于 08-03 19:37 ?136次下載

    7 FPGA進階級智能互聯(lián)開發(fā)板

    FPGA芯片(Xilinx產(chǎn)品編號XC7A100T-1CSG324C)并集成了USB,以太網(wǎng)和其它端口,Nexys 4 DDR開發(fā)板實現(xiàn)
    發(fā)表于 02-08 04:33 ?676次閱讀

    Digilent公司發(fā)布新款FPGA開發(fā)板

    Digilent公司宣布了新款FPGA開發(fā)板,歸屬于其Nexys產(chǎn)品線——Nexys4——上集成一個賽靈思Artix-7100T FPGA。
    發(fā)表于 02-09 14:09 ?1564次閱讀

    digilent Artix-7 FPGA開發(fā)板簡介

    Nexys 4 DDR是一款Digilent多孔RAM-based Nexys開發(fā)板的簡易替代品。
    的頭像 發(fā)表于 11-19 14:43 ?3295次閱讀
    digilent Artix-7 FPGA<b class='flag-5'>開發(fā)板</b>簡介

    一般涉及DDR讀寫仲裁的控制邏輯需要注意哪些方面

    基于vivado2020.1和zcu102開發(fā)板(rev1.1)開發(fā)項目,工程涉及DDR4(MIG)和PL端多個讀寫接口交互的問題,通過AX
    的頭像 發(fā)表于 06-15 11:59 ?1934次閱讀

    Virtex7DDR3的測試例程

    ??這篇文章我們講一下Virtex7DDR3的測試例程,Vivado也提供了一個DDR的example,但卻是純Verilog代碼,比較復
    的頭像 發(fā)表于 08-16 10:28 ?1821次閱讀

    AXI通道讀寫DDR的阻塞問題?

    基于vivado2020.1和zcu102開發(fā)板(rev1.1)開發(fā)項目,工程涉及DDR4(MIG)和PL端多個讀寫接口交互的問題,通過AX
    的頭像 發(fā)表于 12-01 09:04 ?919次閱讀
    AXI通道<b class='flag-5'>讀寫</b><b class='flag-5'>DDR</b>的阻塞問題?