1)實驗平臺:正點原子MPSoC開發(fā)板
2)平臺購買地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套實驗源碼+手冊+視頻下載地址: http://www.openedv.com/thread-340252-1-1.html
第三十一章DDR4讀寫測試實驗
DDR4 SDRAM(Double-Data-Rate Fourth Generation Synchronous Dynamic Random Access Memory,簡稱為DDR4 SDRAM),是一種高速動態(tài)隨機(jī)存取存儲器,它屬于SDRAM家族的存儲器產(chǎn)品,提供了相較于DDR3 SDRAM更高的運行性能與更低的電壓。
本章包括以下幾個部分:
243131.1MIG簡介
31.2實驗任務(wù)
31.3硬件設(shè)計
31.4程序設(shè)計
31.5下載驗證
31.1MIG簡介
DFZU2EG/4EV MPSoC開發(fā)板板載了五片鎂光的DDR4顆粒,它們的型號是MT40A256M16,這5片DDR4芯片有4片位于PS端,有1片位于PL端,本節(jié)實驗使用的是位于PL端的DDR4芯片。下面我們來簡單了解一下這款板載的DDR4芯片,DDR4內(nèi)部結(jié)構(gòu)圖如下所示:
圖 31.1.1 DDR4結(jié)構(gòu)圖
從上圖我們可以看到,DFZU2EG/4EV MPSoC開發(fā)板板載的這款DDR4芯片的行地址是15bit位寬,列地址是10bit位寬,而整個存儲區(qū)域分為兩個BANK組,每個BANK組又由4個子BANK組成,所以整片DDR4的容量就是215*210816bit=256M16bit。DDR4相較于DDR3在指令引腳上也發(fā)生了變化,DDR4取消了我們所熟悉的使能WE、行激活RAS和列激活CAS這三個命令引腳,而是將這三個命令引腳和地址線A14、A15以及A16復(fù)用了。除此之外在尋址的時候也不再是直接去尋址BANK,而是先尋址BANK組,然后再找到這個BANK組中的某個子BANK。整個數(shù)據(jù)的吞吐是8倍預(yù)取,因此數(shù)據(jù)在讀寫的時候就是16bit8=128bit的數(shù)據(jù)量進(jìn)行吞吐(注意雖然是8倍預(yù)取,但是每一次IO引腳上的數(shù)據(jù)傳輸依舊是16bit,因為數(shù)據(jù)線就16根,至于為何可以達(dá)到8倍預(yù)取和DDR4內(nèi)部的雙沿采樣,F(xiàn)IFO緩沖,寫數(shù)據(jù)邏輯結(jié)構(gòu)有關(guān))。
關(guān)于DDR4更詳細(xì)的介紹大家可以去參考鎂光的官方數(shù)據(jù)手冊,這里不再贅述。
由于DDR4的時序非常復(fù)雜,如果直接編寫DDR4的控制器代碼,那么工作量是非常大的,且性能難以得到保證。值得一提的是,Vivado軟件自帶了DDR4控制器IP核,用戶可以直接借助IP核來實現(xiàn)對DDR4的讀寫操作,從而大大降低了DDR4的開發(fā)難度。本次實驗將使用Xilinx公司MIG(Memory Interface Generators) IP核來實現(xiàn)DDR4讀寫測試。
MIG IP核是Xilinx公司針對DDR存儲器開發(fā)的IP,里面集成存儲器控制模塊,實現(xiàn)DDR讀寫操作的控制流程,下圖是MIG IP 核結(jié)構(gòu)框圖。MIG IP核對外分出了兩組接口,左側(cè)是用戶接口,就是用戶(FPGA)同MIG 交互的接口,用戶只有充分掌握了這些接口才能操作MIG;右側(cè)為DDR物理芯片接口,負(fù)責(zé)產(chǎn)生具體的操作時序,并直接操作芯片管腳,這一側(cè)用戶只負(fù)責(zé)分配正確的管腳,其他不用關(guān)心。
圖 31.1.2 MIG IP 核結(jié)構(gòu)框圖
使用這個IP 核,用戶將可以進(jìn)行DDR4的讀寫操作,而不必熟悉DDR4具體的讀寫控制時序,當(dāng)然用戶必須掌握用戶接口側(cè)的操作時序,并嚴(yán)格遵照時序來編寫代碼,這樣才能正確實現(xiàn)對DDR4的讀寫操作。在了解具體時序之前,大家有必要先了解相關(guān)的信號定義。下圖給出了MIG IP核用戶接口的信號及其說明。
圖 31.1.3 用戶接口信號定義
MIG IP核用戶側(cè)端口數(shù)量共26個,當(dāng)然用戶并不用去關(guān)心所有的信號,只需要了解本實驗要用到幾組重要信號。下面將對這些信號逐一講解并以表格的形式呈現(xiàn)給大家。為了與官方的文檔保持一致,表中標(biāo)明的信號的方向是以MIG IP核作為參照的,例如表格中的信號方向定義為輸出,那么相對于用戶端(FPGA)來說實際上是輸入。
表 31.1.1 MIG IP 核用戶接口部分信號定義
以上是用戶需要用到的信號,其他信號請大家自行了解。
DDR4的讀或者寫都包含寫命令操作,其中寫操作命令(app_cmd)的值等于0,讀操作app_cmd的值等于1。首先來看寫命令時序,如下圖所示。首先檢查app_rdy,為高則表明此時IP核命令接收處于準(zhǔn)備好狀態(tài),可以接收用戶命令,在當(dāng)前時鐘拉高app_en,同時發(fā)送命令(app_cmd)和地址(app_addr),此時命令和地址被寫入。
圖 31.1.4 寫命令時序
下面來看寫數(shù)據(jù)的時序,如下圖所示。
圖 31.1.5 非背靠背寫時序
如上圖所示,寫數(shù)據(jù)有三種情形均可以正確寫入:
(1)寫數(shù)據(jù)時序和寫命令時序發(fā)生在同一拍;
(2)寫數(shù)據(jù)時序比寫命令時序提前一拍;
(3)寫數(shù)據(jù)時序比寫命令時序至多延遲晚兩拍;
結(jié)合上圖,寫時序總結(jié)如下:首先需要檢查app_wdf_rdy,該信號為高表明此時IP核數(shù)據(jù)接收處于準(zhǔn)備完成狀態(tài),可以接收用戶發(fā)過來的數(shù)據(jù),在當(dāng)前時鐘拉高寫使能(app_wdf_wren),給出寫數(shù)據(jù)(app_wdf_data)。這樣加上發(fā)起的寫命令操作就可以成功向IP核寫數(shù)據(jù)。這里有一個信號app_wdf_mask,它是用來屏蔽寫入數(shù)據(jù)的,該信號為高則屏蔽相應(yīng)的字節(jié),該信號為0默認(rèn)不屏蔽任何字節(jié)。
這里需要指出的是,DDR4的讀或者寫操作都可以分為背靠背和非背靠背兩種情形。背靠背,即讀或者寫每個時鐘都連續(xù)進(jìn)行,中間沒有間隙;非背靠背寫則是非連續(xù)的讀寫。
對于背靠背寫,其實也有三種情形,唯一點不同的是,它沒有最大延遲限制,如下圖所示。
圖 31.1.6 背靠背寫時序
接著來看讀數(shù)據(jù),如下圖所示:
圖 31.1.7 讀時序
讀時序比較簡單,發(fā)出讀命令后,用戶只需等待數(shù)據(jù)有效信號(app_rd_data_valid)拉高,為高表明此時數(shù)據(jù)總線上的數(shù)據(jù)是有效的返回數(shù)據(jù)。需要注意的是,在發(fā)出讀命令后,有效讀數(shù)據(jù)要晚若干周期才出現(xiàn)在數(shù)據(jù)總線上。下面是背靠背讀的情況,如下圖所示。
圖 31.1.8 背靠背讀時序圖
需要注意的是,在連續(xù)讀的時候,讀到的數(shù)據(jù)順序跟請求的命令/地址是相對應(yīng)的。通常使用DDR4的時候,為了最大限度地提高DDR4效能,充分利用突發(fā)寫的特點,非背靠背很少用,而更多地采用背靠背操作。本章實驗的讀寫操作就是基于背靠背模式操作的。
31.2實驗任務(wù)
本節(jié)的實驗任務(wù)是先向DDR4的存儲器地址0至999分別寫入數(shù)據(jù)0999;寫完之后再讀取存儲器地址0999中的數(shù)據(jù),若讀取的值全部正確則LED燈常亮,否則LED燈閃爍。
31.3硬件設(shè)計
本節(jié)實驗使用的是PL端的DDR4,如果想使用PS端的DDR4,可以參考正點原子的嵌入式Vitis開發(fā)指南教程。PL端DDR4的硬件原理圖如下圖所示。在PCB的設(shè)計上,完全遵照Xilinx的DDR4硬件設(shè)計規(guī)范,嚴(yán)格保證等長設(shè)計和阻抗控制,從而保證高速信號的數(shù)據(jù)傳輸?shù)目煽啃浴?br>
圖 31.3.1 DDR4硬件原理圖
接下來我們來看看本節(jié)實驗所用到的管腳,管腳分配可以放在工程的XDC文件中,也可以打開io planning窗口在io ports欄中分配管腳。我們本節(jié)實驗用到的管腳如下表所示(因為管腳太多這里只貼出部分管腳):
表 31.3.1 DDR4讀寫測試實驗部分管腳分配
信號名 方向 管腳 端口說明 電平標(biāo)準(zhǔn)
sys_clk_p input AE5 系統(tǒng)差分輸入時鐘 DIFF_HSTL_I_12
sys_clk_n input AF5 系統(tǒng)差分輸入時鐘 DIFF_HSTL_I_12
sys_rst_n input AH11 系統(tǒng)復(fù)位按鍵,低電平有效 LVCMOS33
led output AE10 讀寫結(jié)果指示燈 LVCMOS33
c0_ddr4_dq[15] inout AC4 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[14] inout AC3 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[13] inout AB4 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[12] inout AB3 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[11] inout AB2 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[10] inout AC2 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[9] inout AB1 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[8] inout AC1 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[7] inout AG3 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[6] inout AH3 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[5] inout AE3 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[4] inout AF3 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[3] inout AH2 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[2] inout AH1 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[1] inout AF1 DDR4數(shù)據(jù) POD12
c0_ddr4_dq[0] inout AG1 DDR4數(shù)據(jù) POD12
c0_ddr4_dqs_t[1]} inout AD2 DDR4數(shù)據(jù)選取脈沖 DIFF_ POD12
c0_ddr4_dqs_c[1]} inout AD1 DDR4數(shù)據(jù)選取脈沖 DIFF_ POD12
c0_ddr4_dqs_t[0]} inout AE2 DDR4數(shù)據(jù)選取脈沖 DIFF_ POD12
c0_ddr4_dqs_c[0]} inout AF2 DDR4數(shù)據(jù)選取脈沖 DIFF_ POD12
c0_ddr4_adr[16] output AB5 DDR4地址線 SSTL12
c0_ddr4_adr[15] output AB7 DDR4地址線 SSTL12
c0_ddr4_adr[14] output AF6 DDR4地址線 SSTL12
c0_ddr4_adr[13] output AD9 DDR4地址線 SSTL12
c0_ddr4_adr[12] output AC6 DDR4地址線 SSTL12
c0_ddr4_adr[11] output AH9 DDR4地址線 SSTL12
c0_ddr4_adr[10] output AE7 DDR4地址線 SSTL12
c0_ddr4_adr[9] output AC9 DDR4地址線 SSTL12
c0_ddr4_adr[8] output AH8 DDR4地址線 SSTL12
c0_ddr4_adr[7] output AE9 DDR4地址線 SSTL12
c0_ddr4_adr[6] output AH7 DDR4地址線 SSTL12
c0_ddr4_adr[5] output AD7 DDR4地址線 SSTL12
c0_ddr4_adr[4] output AF7 DDR4地址線 SSTL12
c0_ddr4_adr[3] output AC8 DDR4地址線 SSTL12
c0_ddr4_adr[2] output AF8 DDR4地址線 SSTL12
c0_ddr4_adr[1] output AB8 DDR4地址線 SSTL12
c0_ddr4_adr[0] output AG8 DDR4地址線 SSTL12
c0_ddr4_ba[1] output AC7 DDR4 Bank地址 SSTL12
c0_ddr4_ba[0] output AH6 DDR4 Bank地址 SSTL12
c0_ddr4_bg output AE8 DDR4 Bank組地址 SSTL12
c0_ddr4_reset_n output AG9 DDR4 復(fù)位信號 LVCMOS12
c0_ddr4_cke output AE4 DDR4 時鐘使能信號 SSTL12
c0_ddr4_cs_n output AB6 DDR4 片選信號 SSTL12
c0_ddr4_dm_dbi_n[1] output AD5 DDR4 數(shù)據(jù)掩碼 POD12
c0_ddr4_dm_dbi_n[0] output AG4 DDR4 數(shù)據(jù)掩碼 POD12
c0_ddr4_odt output E2 DDR4 終端電阻使能 SSTL12
c0_ddr4_ck_t output AG6 DDR4 差分時鐘正 DIFF_ SSTL12
c0_ddr4_ck_c output AG5 DDR4 差分時鐘負(fù) DIFF_ SSTL12
如上表所示,本次實驗用到了五種電平,分別是LVCMOS、SSTL、POD12、DIFF_SSTL以及DIFF_ POD12,下面對于這五種電平我們做一個簡單的介紹。
LVCMOS:全稱Low Voltage Complementary Metal Oxide Semiconductor,低壓互補金屬氧化物半導(dǎo)體;
SSTL:全稱Stub Series Terminated Logic,短截線串聯(lián)端接邏輯;
DIFF_SSTL:全稱Difference Stub Series Terminated Logic,差分短截線串聯(lián)端接邏輯;
POD12:POD12是由VDDQ端提供的電壓,一般用于DDR4,當(dāng)POD12電壓驅(qū)動端的上拉電路導(dǎo)通,電路處于高電平時,回路上沒有電流流過,這樣的設(shè)計較少了功耗;
DIFF_ POD12:與POD12一樣,只不過是差分電壓。
除此之外,LVCMOS的特點是噪聲容限大,速度較SSTL慢;SSTL速度快,通常要匹配合適的端接電阻,常用于高速內(nèi)存接口如DDR4;DIFF_SSTL則是帶有差分功能的SSTL。POD和SSTL的最大區(qū)別在于接收端的終端電壓(POD為VDDQ,SSTL為VDDQ/2)。POD可以降低寄生引腳電容和I/O終端功耗,并且即使在VDD電壓降低的情況下也能穩(wěn)定工作。
DDR4的管腳使用的是SSTL、POD12、DIFF_SSTL以及DIFF_ POD12,這里要注意一下DDR4復(fù)位信號c0_DDR4_reset_n,它并沒有使用SSTL,原因是SSTL的噪聲容限不夠大,容易受干擾造成誤復(fù)位。
本實驗對應(yīng)的XDC約束語句如下所示。注意,在這里僅給出了時鐘、復(fù)位、和LED讀寫指示信號的管腳約束。DDR4部分的管腳約束在文檔中不再給出,大家可以從提供的例程中查看。
set_property -dict {PACKAGE_PIN AE5 IOSTANDARD DIFF_HSTL_I_12} [get_ports c0_sys_clk_p]
set_property -dict {PACKAGE_PIN AF5 IOSTANDARD DIFF_HSTL_I_12} [get_ports c0_sys_clk_n]
set_property -dict {PACKAGE_PIN AH11 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN AE10 IOSTANDARD LVCMOS33} [get_ports led]
31.4程序設(shè)計
根據(jù)實驗任務(wù),可以大致規(guī)劃出系統(tǒng)的控制流程:首先FPGA通過調(diào)用MIG IP核向DDR4芯片寫入數(shù)據(jù),寫完之后通過MIG IP核從DDR4芯片讀出所寫入的數(shù)據(jù),并判斷讀出的數(shù)據(jù)與寫入的數(shù)據(jù)是否相同,如果相同則LED燈常亮,否則LED燈閃爍。由此畫出系統(tǒng)的功能框圖如下圖所示:
圖 31.4.1 DDR4 讀寫測試實驗系統(tǒng)框圖
由系統(tǒng)總體框圖可知,F(xiàn)PGA頂層模塊例化了以下兩個模塊,分別是讀寫模塊(ddr4_rw)和MIG IP核模塊。
各模塊端口及信號連接如下圖所示:
圖 31.4.2 頂層模塊原理圖
其中DDR4讀寫模塊的作用是生成DDR4的讀寫數(shù)據(jù)和MIG IP核的驅(qū)動時序,并且在DDR4模塊里比較寫入DDR4的數(shù)據(jù)與讀出的數(shù)據(jù)是否一致,如果讀寫一致,說明DDR4讀寫正確,此時LED指示燈常亮;否則說明DDR4讀寫失敗,LED指示燈處于閃爍狀態(tài)。而MIG IP核內(nèi)部包含了整個DDR4的驅(qū)動,一方面它負(fù)責(zé)驅(qū)動板載的DDR4芯片,另一方面和用戶模塊進(jìn)行數(shù)據(jù)交互,在本節(jié)實驗中用戶模塊就是DDR4讀寫模塊,MIG IP核負(fù)責(zé)和DDR4讀寫模塊進(jìn)行數(shù)據(jù)交互。
需要注意的是,本節(jié)實驗的系統(tǒng)時鐘是一對差分時鐘,頻率為100Mhz(c0_sys_clk_p,c0_sys_clk_n),這對差分時鐘直接連接到MIG IP核,由MIG IP核生成DDR4的驅(qū)動時鐘(c0_DDR4_ck_t,c0_DDR4_ck_c),輸出給外部DDR4(MT40A256M16GE-083E)芯片使用。除此之外,F(xiàn)PGA內(nèi)部的用戶模塊時鐘也是由MIG IP核提供,本節(jié)實驗的用戶模塊時鐘是c0_DDR4_ui_clk,作為DDR4讀寫模塊的操作時鐘。
接下來我們介紹下如何對MIG IP核進(jìn)行配置。首先在Vivado環(huán)境里新建一個工程,本實驗取名為ddr4_rw_top。再點擊Project Manager界面下的IP Catalog,打開IP Catalog界面。如下圖所示。
圖 31.4.3 “IP Catalog”按鈕
在搜索欄中輸入MIG,此時出現(xiàn)MIG IP核,我們找到DDR4 SDRAM(MIG),如下圖所示。
圖 31.4.4 搜索欄中輸入關(guān)鍵字
雙擊打開DDR4 SDRAM(MIG),出現(xiàn)如下圖所示界面。
圖 31.4.5 IP核配置
上圖所示的是MIG IP 核的Basic配置界面,這里我們對幾個重要的配置信息作出說明:
Component Name:MIG IP核的命名,可以保持默認(rèn),也可以自己取一個名字。
Mode and Interface:控制器的模式和接口選項,可以選擇AXI4接口或者普通模式,并生成對應(yīng)的PHY組件(詳情請參考官方文檔pg150)。
Memory Device Interface Speed:板載DDR4芯片的IO總線時鐘頻率,這里可以最大支持938ps(1066MHz),IO總線時鐘頻率等于2倍DDR4芯片核心頻率(533 MHz)。
PHY to controller clock frequency ratio:用戶時鐘分頻系數(shù),這里只能選擇4比1,因此本節(jié)實驗的用戶時鐘頻率等于DDR4芯片核心頻率的四分之一,即133.25MHz。
Reference input Clock Speed:參考時鐘,本節(jié)實驗選擇10006ps(參考時鐘頻率和系統(tǒng)時鐘頻率保持一致即100MHz)。
Controller Options:控制器配置欄,如果使用MIG IP核內(nèi)部默認(rèn)的DDR4芯片,則只需要在Memory Part欄選中對應(yīng)的DDR4芯片型號即可,例如我們板載的DDR4芯片型號為MT40A256M16GE-083E(如果多片DDR4聯(lián)用注意修改數(shù)據(jù)位寬)。如果使用的DDR4芯片型號不在MIG IP和的默認(rèn)配置中就需要手動定義DDR4芯片的參數(shù)文件,這個時候就需要使能定制型號(Enable Custom Parts Data File),然后加載配置文件(Custom Parts Data File)。
Memory Options:配置突發(fā)長度和CAS延遲的,這里保持默認(rèn)即可(如果需要修改請參考DDR4芯片數(shù)據(jù)手冊)。
接下來我們選擇MIG IP核配置界面的Advanced Clocking界面,如下所示:
圖 31.4.6 IP核配置界面
Advanced Clocking界面只需要關(guān)注一下Additional Clock Outputs配置,這里可以額外輸出四路時鐘,如果有需求這里可以生成,我們本節(jié)實驗不需要用到額外時鐘,所以全部“None”即可。
接下來切換至Advanced Options界面,如下圖所示:
圖 31.4.7 IP核配置界面
Advanced Options界面的配置信息如下:
Debug Signals for controller:在Xilinx示例設(shè)計中,啟用此功能會把狀態(tài)信號連接到ChipScope ILA核中。
Microblaze MCS ECC option:Microblaze的配置選項,選中它Microblaze的MCS ECC尺寸會增加。
Simulation Options:此選項僅對仿真有效。在選擇BFM選項時,為XiPhy庫使用行為模型,以加快模擬運行時間。選擇Unisim則對XiPhy原語使用Unisim庫。
Example Design Options:示例工程仿真文件的選擇。
Advance Memory Options:提高運行性能的選項,可以選擇自刷新和校準(zhǔn)功能,并將這些信息保存在XSDB BRAM中,也可以把XSDB BRAM中的信息存儲在外部存儲器中 。
Migration Options:引腳兼容選項,如果想兼容UlitraScale和UltraScale+ fpga,就把這個選項選中。
最后我們再來看看IO Planning and Design Checklist界面,如下圖所示:
圖 31.4.8 IP核配置界面
IO Planning and Design Checklist界面提示我們DDR4 IO引腳分配的方式發(fā)生改變,不再像之前DDR3那樣,需要在MIG IP核中就分配好管腳,DDR4可以在IO Planning窗口分配管腳(或者直接編寫XDC文件)。
當(dāng)所有的配置信息全部修改完成后,點擊“OK”按鈕,彈出的界面如下圖所示:
圖 31.4.9 生成IP核
點擊Generate按鈕生成IP核,IP核生成完成后,開始編寫DDR4讀寫實驗的代碼。
頂層模塊的代碼,如下所示:
1 module ddr4_rw_top(
2 output c0_ddr4_act_n ,
3 output [16:0] c0_ddr4_adr ,
4 output [1:0] c0_ddr4_ba ,
5 output [0:0] c0_ddr4_bg ,
6 output [0:0] c0_ddr4_cke ,
7 output [0:0] c0_ddr4_odt ,
8 output [0:0] c0_ddr4_cs_n ,
9 output [0:0] c0_ddr4_ck_t ,
10 output [0:0] c0_ddr4_ck_c ,
11 output c0_ddr4_reset_n ,
12 inout [1:0] c0_ddr4_dm_dbi_n,
13 inout [15:0] c0_ddr4_dq ,
14 inout [1:0] c0_ddr4_dqs_c ,
15 inout [1:0] c0_ddr4_dqs_t ,
16
17 //Differential system clocks
18 input c0_sys_clk_p,
19 input c0_sys_clk_n,
20 output[1:0] led,
21 input sys_rst_n
22 );
23
24 //wire define
25
26 wire error_flag;
27
28 wire c0_ddr4_ui_clk ;
29 wire c0_ddr4_ui_clk_sync_rst ;
30 wire c0_ddr4_app_en ;
31 wire c0_ddr4_app_hi_pri ;
32 wire c0_ddr4_app_wdf_end ;
33 wire c0_ddr4_app_wdf_wren ;
34 wire c0_ddr4_app_rd_data_end ;
35 wire c0_ddr4_app_rd_data_valid ;
36 wire c0_ddr4_app_rdy ;
37 wire c0_ddr4_app_wdf_rdy ;
38 wire [27 : 0] c0_ddr4_app_addr ;
39 wire [2 : 0] c0_ddr4_app_cmd ;
40 wire [127 : 0] c0_ddr4_app_wdf_data;
41 wire [15 : 0] c0_ddr4_app_wdf_mask ;
42 wire [127 : 0] c0_ddr4_app_rd_data ;
43
44
45
46 wire locked; //鎖相環(huán)頻率穩(wěn)定標(biāo)志
47 wire clk_ref_i; //DDR4參考時鐘
48 wire sys_clk_i; //MIG IP核輸入時鐘
49 wire clk_200; //200M時鐘
50 wire ui_clk_sync_rst; //用戶復(fù)位信號
51 wire init_calib_complete; //校準(zhǔn)完成信號
52 wire [20:0] rd_cnt; //實際讀地址計數(shù)
53 wire [1 :0] state; //狀態(tài)計數(shù)器
54 wire [23:0] rd_addr_cnt; //用戶讀地址計數(shù)器
55 wire [23:0] wr_addr_cnt; //用戶寫地址計數(shù)器
56
57 //*****************************************************
58 //** main code
59 //*****************************************************
60
61 //讀寫模塊
62 ddr4_rw u_ddr4_rw(
63 .ui_clk (c0_ddr4_ui_clk),
64 .ui_clk_sync_rst (c0_ddr4_ui_clk_sync_rst),
65 .init_calib_complete (c0_init_calib_complete),
66 .app_rdy (c0_ddr4_app_rdy),
67 .app_wdf_rdy (c0_ddr4_app_wdf_rdy),
68 .app_rd_data_valid (c0_ddr4_app_rd_data_valid),
69 .app_rd_data (c0_ddr4_app_rd_data),
70
71 .app_addr (c0_ddr4_app_addr),
72 .app_en (c0_ddr4_app_en),
73 .app_wdf_wren (c0_ddr4_app_wdf_wren),
74 .app_wdf_end (c0_ddr4_app_wdf_end),
75 .app_cmd (c0_ddr4_app_cmd),
76 .app_wdf_data (c0_ddr4_app_wdf_data),
77 .state (state),
78 .rd_addr_cnt (rd_addr_cnt),
79 .wr_addr_cnt (wr_addr_cnt),
80 .rd_cnt (rd_cnt),
81
82 .error_flag (error_flag),
83 .led (led)
84 );
85 ddr4_0 u_ddr4_0 (
86 .c0_init_calib_complete(c0_init_calib_complete),//初始化完成信號
87 .dbg_clk(),
88 .c0_sys_clk_p(c0_sys_clk_p), // 系統(tǒng)差分時鐘p
89 .c0_sys_clk_n(c0_sys_clk_n), // 系統(tǒng)差分時鐘n
90 .dbg_bus(),
91 .c0_ddr4_adr(c0_ddr4_adr), // 行列地址
92 .c0_ddr4_ba(c0_ddr4_ba), // bank地址
93 .c0_ddr4_cke(c0_ddr4_cke), // 時鐘使能
94 .c0_ddr4_cs_n(c0_ddr4_cs_n), // 片選信號
95 .c0_ddr4_dm_dbi_n(c0_ddr4_dm_dbi_n), // 數(shù)據(jù)掩碼
96 .c0_ddr4_dq(c0_ddr4_dq), // 數(shù)據(jù)線
97 .c0_ddr4_dqs_c(c0_ddr4_dqs_c), // 數(shù)據(jù)選通信號
98 .c0_ddr4_dqs_t(c0_ddr4_dqs_t), // 數(shù)據(jù)選通信號
99 .c0_ddr4_odt(c0_ddr4_odt), // 終端電阻
100 .c0_ddr4_bg(c0_ddr4_bg), // bank組地址
101 .c0_ddr4_reset_n(c0_ddr4_reset_n), // 復(fù)位信號
102 .c0_ddr4_act_n(c0_ddr4_act_n), // 激活指令引腳
103 .c0_ddr4_ck_c(c0_ddr4_ck_c), //時鐘信號
104 .c0_ddr4_ck_t(c0_ddr4_ck_t), // 時鐘信號
105 //user interface
106 .c0_ddr4_ui_clk(c0_ddr4_ui_clk), // 用戶時鐘
107 .c0_ddr4_ui_clk_sync_rst(c0_ddr4_ui_clk_sync_rst), // 用戶復(fù)位
108 .c0_ddr4_app_en(c0_ddr4_app_en), // 指令寫入使能
109 .c0_ddr4_app_hi_pri(1'b0),
110 .c0_ddr4_app_wdf_end(c0_ddr4_app_wdf_end), // 寫數(shù)據(jù)最后一個時鐘
111 .c0_ddr4_app_wdf_wren(c0_ddr4_app_wdf_wren), // 寫數(shù)據(jù)使能
112 .c0_ddr4_app_rd_data_end(c0_ddr4_app_rd_data_end),// 讀數(shù)據(jù)最后一個數(shù)據(jù)
113 .c0_ddr4_app_rd_data_valid(c0_ddr4_app_rd_data_valid), // 讀數(shù)據(jù)有效
114 .c0_ddr4_app_rdy(c0_ddr4_app_rdy), // 指令接收準(zhǔn)備完成,可以開始接收指令
115 .c0_ddr4_app_wdf_rdy(c0_ddr4_app_wdf_rdy), // 數(shù)據(jù)接收準(zhǔn)備完成,可以開始接收數(shù)據(jù)
116 .c0_ddr4_app_addr(c0_ddr4_app_addr), // 用戶地址
117 .c0_ddr4_app_cmd(c0_ddr4_app_cmd), // 讀寫控制命令
118 .c0_ddr4_app_wdf_data(c0_ddr4_app_wdf_data), // 寫數(shù)據(jù)
119 .c0_ddr4_app_wdf_mask(16'b0), // input wire [15 : 0] c0_ddr4_app_wdf_mask
120 .c0_ddr4_app_rd_data(c0_ddr4_app_rd_data), // 讀數(shù)據(jù)
121 .sys_rst(~sys_rst_n) //系統(tǒng)復(fù)位
122 );
123
124 endmodule
頂層模塊的代碼共例化了兩個子模塊,分別是DDR4讀寫模塊(ddr4_rw)和MIG IP核模塊(ddr4_0),主要實現(xiàn)各模塊的數(shù)據(jù)交互。
DDR4讀寫模塊的代碼如下:
1 module ddr4_rw (
2 input ui_clk, //用戶時鐘
3 input ui_clk_sync_rst, //復(fù)位,高有效
4 input init_calib_complete, //DDR4初始化完成
5 input app_rdy, //MIG 命令接收準(zhǔn)備好標(biāo)致
6 input app_wdf_rdy, //MIG數(shù)據(jù)接收準(zhǔn)備好
7 input app_rd_data_valid, //讀數(shù)據(jù)有效
8 input [127:0] app_rd_data, //用戶讀數(shù)據(jù)
9 output reg [27:0] app_addr, //DDR4地址
10 output app_en, //MIG IP發(fā)送命令使能
11 output app_wdf_wren, //用戶寫數(shù)據(jù)使能
12 output app_wdf_end, //突發(fā)寫當(dāng)前時鐘最后一個數(shù)據(jù)
13 output [2:0] app_cmd, //MIG IP核操作命令,讀或者寫
14 output reg [127:0] app_wdf_data, //用戶寫數(shù)據(jù)
15 output reg [1 :0] state, //讀寫狀態(tài)
16 output reg [23:0] rd_addr_cnt, //用戶讀地址計數(shù)
17 output reg [23:0] wr_addr_cnt, //用戶寫地址計數(shù)
18 output reg [20:0] rd_cnt, //實際讀地址標(biāo)記
19 output reg error_flag, //讀寫錯誤標(biāo)志
20 output reg led //讀寫測試結(jié)果指示燈
21 );
22
23 //parameter define
24 parameter TEST_LENGTH = 10;
25 parameter L_TIME = 25'd25_000_000;
26 parameter IDLE = 2'd0; //空閑狀態(tài)
27 parameter WRITE = 2'd1; //寫狀態(tài)
28 parameter WAIT = 2'd2; //讀到寫過度等待
29 parameter READ = 2'd3; //讀狀態(tài)
30
31 //reg define
32 reg [24:0] led_cnt; //led計數(shù)
33
34 //wire define
35 wire error; //讀寫錯誤標(biāo)記
36 wire rst_n; //復(fù)位,低有效
代碼第2行到第20行是模塊的接口定義,大部分是MIG IP核中要操作的用戶接口信號,這些信號在本章簡介部分中已經(jīng)以表格的形式詳細(xì)呈現(xiàn)給大家,有不清楚的地方可以回去查看。
代碼第24行和第29行是參數(shù)定義,首先定義了TEST_LENGTH(測試長度),注意這里的測試長度是相對于用戶端來說的,一個測試長度對應(yīng)到DDR芯片需要訪問是8個地址,即8個16Byte(共128位)。接下來定義了L_TIME(閃爍間隔),該參數(shù)用來控制LED閃爍頻率。最后定義了讀寫測試的四個狀態(tài),IDLE(空閑)、WRITE(寫)、WAIT(等待)和READ(讀)。
37
38 //*****************************************************
39 //** main code
40 //*****************************************************
41
42 assign rst_n = ~ui_clk_sync_rst;
43 //讀信號有效,且讀出的數(shù)不是寫入的數(shù)時,將錯誤標(biāo)志位拉高
44 assign error = (app_rd_data_valid && (rd_cnt! = app_rd_data));
45
46 //在寫狀態(tài)MIG IP 命令接收和數(shù)據(jù)接收都準(zhǔn)備好,或者在讀狀態(tài)命令接收準(zhǔn)備好,此時拉高使能信號,
47 assign app_en = ((state == WRITE && (app_rdy && app_wdf_rdy))
48 ||(state == READ && app_rdy)) ? 1'b1:1'b0;
49
50 //在寫狀態(tài),命令接收和數(shù)據(jù)接收都準(zhǔn)備好,此時拉高寫使能
51 assign app_wdf_wren = (state == WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
52
53 //由于DDR4芯片時鐘和用戶時鐘的分頻選擇4:1,突發(fā)長度為8,故兩個信號相同
54 assign app_wdf_end = app_wdf_wren;
55
56 //處于讀的時候命令值為1,其他時候命令值為0
57 assign app_cmd = (state == READ) ? 3'd1 :3'd0;
58
代碼第42行,信號ui_clk_sync_rst是MIG IP核產(chǎn)生,用于復(fù)位用戶邏輯,其高電平有效。在代碼中將ui_clk_sync_rst信號取反,是為了轉(zhuǎn)換成代碼中習(xí)慣使用的復(fù)位低電平有效。
代碼第44行,是錯誤標(biāo)志的邏輯判斷。讀數(shù)據(jù)有效信號(app_rd_data_valid)為高時去比較app_rd_data和rd_cnt的值,不相等則讀寫錯誤。關(guān)于rd_cnt的含義將會在下文詳細(xì)介紹。
代碼第47行到48行,首先產(chǎn)生了MIG IP核命令發(fā)送使能信號,這里可以看出,讀或者寫狀態(tài)都要拉高該信號。需要特別指出的是,在寫的時候,app_en拉高的條件是:app_rdy(MIG命令接收準(zhǔn)備好標(biāo)志)和app_wdf_rdy(MIG數(shù)據(jù)接收準(zhǔn)備好標(biāo)致)同時為高,根據(jù)數(shù)據(jù)手冊,用戶可以只判斷app_rdy信號。當(dāng)app_rdy信號為高電平時,可以拉高使能app_en,此處之所以同時判斷app_rdy和app_wdf_rdy,是因為本實驗只考慮DDR4寫狀態(tài)命令發(fā)送和數(shù)據(jù)發(fā)送同拍的情形,這樣的做法基本上不會對DDR4讀寫操作效率造成很大影響,但是卻能大大簡化代碼編寫難度。
代碼第51行,產(chǎn)生寫使能,如上所述,本實驗只考慮寫命令和寫數(shù)據(jù)同時發(fā)起的情形,當(dāng)檢測到命令接收和數(shù)據(jù)接收標(biāo)志同時拉高的時候,寫操作也在同一時間被發(fā)起,即拉高app_wdf_wren(寫數(shù)據(jù)使能)。
這里要解釋一下代碼第54行。上文說過app_wdf_end表明當(dāng)前時鐘是突發(fā)寫過程的最后一個時鐘周期,由于本實驗采用的是4:1模式,每一個用戶時鐘周期實際上都完成了一個寫突發(fā)操作,所以在突發(fā)寫有效時,即app_wdf_wren拉高,每個時鐘,app_wdf_end也跟著拉高,所以在4:1模式信號app_wdf_end和app_wdf_wren是同步變化的。
59 //DDR4讀寫邏輯實現(xiàn)
60 always @(posedge ui_clk or negedge rst_n) begin
61 if((~rst_n)||(error_flag)) begin
62 state <= IDLE;
63 app_wdf_data <= 128'd0;
64 wr_addr_cnt <= 24'd0;
65 rd_addr_cnt <= 24'd0;
66 app_addr <= 28'd0;
67 end
68 else if(init_calib_complete)begin //MIG IP核初始化完成
69 case(state)
70 IDLE:begin
71 state <= WRITE;
72 app_wdf_data <= 128'd0;
73 wr_addr_cnt <= 24'd0;
74 rd_addr_cnt <= 24'd0;
75 app_addr <= 28'd0;
76 end
77 WRITE:begin
78 if(wr_addr_cnt == TEST_LENGTH - 1 &&(app_rdy && app_wdf_rdy))
79 state <= WAIT; //寫到設(shè)定的長度跳到等待狀態(tài)
80 else if(app_rdy && app_wdf_rdy)begin //寫條件滿足
81 app_wdf_data <= app_wdf_data + 1; //寫數(shù)據(jù)自加
82 wr_addr_cnt <= wr_addr_cnt + 1; //寫地址自加
83 app_addr <= app_addr + 8; //DDR4 地址加8
84 end
85 else begin //寫條件不滿足,保持當(dāng)前值
86 app_wdf_data <= app_wdf_data;
87 wr_addr_cnt <= wr_addr_cnt;
88 app_addr <= app_addr;
89 end
90 end
91 WAIT:begin
92 state <= READ; //下一個時鐘,跳到讀狀態(tài)
93 rd_addr_cnt <= 24'd0; //讀地址復(fù)位
94 app_addr <= 28'd0; //DDR4讀從地址0開始
95 end
96 READ:begin //讀到設(shè)定的地址長度
97 if(rd_addr_cnt == TEST_LENGTH - 1 && app_rdy)
98 state <= IDLE; //則跳到空閑狀態(tài)
99 else if(app_rdy)begin //若MIG已經(jīng)準(zhǔn)備好,則開始讀
100 rd_addr_cnt <= rd_addr_cnt + 1'd1; //用戶地址每次加一
101 app_addr <= app_addr + 8; //DDR4地址加8
102 end
103 else begin //若MIG沒準(zhǔn)備好,則保持原值
104 rd_addr_cnt <= rd_addr_cnt;
105 app_addr <= app_addr;
106 end
107 end
108 default:begin
109 state <= IDLE;
110 app_wdf_data <= 128'd0;
111 wr_addr_cnt <= 24'd0;
112 rd_addr_cnt <= 24'd0;
113 app_addr <= 28'd0;
114 end
115 endcase
116 end
117 end
118
119 //對DDR4實際讀數(shù)據(jù)個數(shù)編號計數(shù)
120 always @(posedge ui_clk or negedge rst_n) begin
121 if(~rst_n)
122 rd_cnt <= 0; //若計數(shù)到讀寫長度,且讀有效,地址計數(shù)器則置0
123 else if(app_rd_data_valid && rd_cnt == TEST_LENGTH - 1)
124 rd_cnt <= 0; //其他條件只要讀有效,每個時鐘自增1
125 else if (app_rd_data_valid )
126 rd_cnt <= rd_cnt + 1;
127 end
128
129 //寄存狀態(tài)標(biāo)志位
130 always @(posedge ui_clk or negedge rst_n) begin
131 if(~rst_n)
132 error_flag <= 0;
133 else if(error)
134 error_flag <= 1;
135 end
136
137 //led指示效果控制
138 always @(posedge ui_clk or negedge rst_n) begin
139 if((~rst_n) || (~init_calib_complete )) begin
140 led_cnt <= 25'd0;
141 led <= 1'b0;
142 end
143 else begin
144 if(~error_flag) //讀寫測試正確
145 led <= 1'b1; //led燈常亮
146 else begin //讀寫測試錯誤
147 led_cnt <= led_cnt + 25'd1;
148 if(led_cnt == L_TIME - 1'b1) begin
149 led_cnt <= 25'd0;
150 led <= ~led; //led燈閃爍
151 end
152 end
153 end
154 end
155
156 endmodule
代碼60行起的always模塊是DDR4讀寫邏輯的實現(xiàn)。采用了一段式狀態(tài)機(jī)來編寫整個讀寫測試邏輯,如下圖所示,一共分為四個狀態(tài)。描述如下:上電一開始處于IDLE(空閑)狀態(tài),初始化完成后跳到WRITE(DDR4寫),寫到測試長度跳到WAIT(等待)狀態(tài),在此消耗一個實在周期后無條件跳到READ(DDR4讀),讀到測試長度跳回空閑狀態(tài)。
圖 31.4.10 讀寫測試狀態(tài)跳轉(zhuǎn)圖
下面來講解具體的各狀態(tài),主要是寫和讀狀態(tài)。
首先來看看寫過程,為了便于理解,下面給出了ILA中采集到的DDR4寫的波形,同時為了方便觀察完整的寫過程,把本次實驗的一次讀寫長度設(shè)為10。如下圖所示:
圖 31.4.11 DDR4寫時序
寫狀態(tài)首先判斷MIG IP核發(fā)送過來的信號app_rdy和app_wdf_rdy,當(dāng)這兩個信號同時為高時,拉高app_en和app_wdf_wren,同時時給寫出命令,即app_cmd為0,此刻正式進(jìn)行DDR4寫過程。寫的過程中,寫數(shù)據(jù)app_wdf_data和地址計數(shù)wr_addr_cnt在每個時鐘自加1。另外大家注意到app_addr每次自加8,前面其實提到,用戶端在每一個用戶時鐘進(jìn)行一個128bit的數(shù)據(jù)的傳輸,在DDR4物理芯片端需要分8次傳輸,每次傳輸一個地址位寬16bit,8次就需要8個地址。通過寫時序圖和ILA圖進(jìn)行對比,可以發(fā)現(xiàn)兩者是一致的,即說明本次實驗是成功的
代碼第96到107行是DDR4讀過程,在讀狀態(tài),判斷MIG IP核發(fā)送過來的信號app_rdy,當(dāng)這個信號為高時,拉高app_en,同時給出讀命令,即app_cmd為1,此時開始進(jìn)行讀操作。在進(jìn)行讀操作的時候,app_addr同樣每次自加8。下面還是給出ILA上觀察到的讀信號波形,方便大家理解查看。
圖 31.4.12 DDR4讀時序
讀操作結(jié)束又跳回空閑狀態(tài),如代碼第98行所示。
代碼第120行到127行的always模塊,產(chǎn)生rd_cnt,實現(xiàn)了對讀出的地址數(shù)據(jù)的編號。為什么會有這樣的處理,根本原因在于讀數(shù)據(jù)的返回和讀操作并不是在同一拍出現(xiàn)的,會晚若干周期出現(xiàn)。這樣意味著不能用讀數(shù)據(jù)直接和當(dāng)前的讀地址去比較,所以本次實驗單獨構(gòu)造了rd_cnt這個信號,它代表一次讀操作真正返回的有效的讀數(shù)據(jù)個數(shù)。
代碼第130行到135行的always模塊,實現(xiàn)了錯誤標(biāo)識的寄存。代碼第138行到154行的always模塊實現(xiàn)讀寫測試LED燈的顯示邏輯。錯誤則閃爍,正確則常亮。
最后通過采集到的波形來看讀寫測試結(jié)果,在讀數(shù)據(jù)有效信號app_rd_data_valid為高時,數(shù)據(jù)總線上的app_rd_data分別和地址編號rd_cnt相等。按照本次實驗的設(shè)計,寫進(jìn)DDR4的數(shù)值是它的地址編號,也即等于數(shù)據(jù)編號,說明是正確的讀寫結(jié)果。當(dāng)然大家可以看到錯誤指示信號error_flag始終為0,即沒有出現(xiàn)讀寫錯誤。
圖 31.4.13 DDR4讀結(jié)果
31.5下載驗證
將下載器一端連接電腦,另一端與開發(fā)板上的JTAG下載口連接,接下來連接電源線后撥動開關(guān)按鍵給開發(fā)板上電。
點擊Vivado左側(cè)“Flow Navigator”窗口最下面的“Open Hardware Manager”,此時Vivado軟件識別到下載器,點擊“Hardware”窗口中“Progam Device”下載程序,在彈出的界面中選擇“Program”下載程序。
程序下載完成后,開發(fā)板上的PL_LED1在短暫延時之后,始終處于常亮的狀態(tài),如下圖所示:
圖 31.5.1 DDR4板子測試結(jié)果
此時說明DDR4讀寫測試成功。
在這里再額外補充一點,將帶有DDR4 MIG IP核的工程下載進(jìn)開發(fā)板時,會出現(xiàn)一個MIG自帶的狀態(tài)檢測核,如下圖所示:文章來源:http://www.zghlxwxcb.cn/news/detail-792474.html
圖 31.5.2 DDR4狀態(tài)
從上圖中可以看到MIG運行的一些狀態(tài)信息,如果運行正常會顯示“PASS”。到這里DDR4讀寫實驗就給大家全部講解完了。文章來源地址http://www.zghlxwxcb.cn/news/detail-792474.html
到了這里,關(guān)于【正點原子FPGA連載】第三十一章DDR4讀寫測試實驗 摘自【正點原子】DFZU2EG/4EV MPSoC 之FPGA開發(fā)指南V1.0的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!