:MIG之外的關(guān)鍵設(shè)計)
簡介面向FPGA開發(fā)工程師、嵌入式系統(tǒng)設(shè)計者和數(shù)字IC方向?qū)W習(xí)者這份資料基于賽靈思Spartan-6系列XC6SLX16芯片用Verilog HDL完整搭建DDR3內(nèi)存讀寫工程。工程內(nèi)含地址發(fā)生器、命令控制器、數(shù)據(jù)路徑、控制邏輯、時鐘管理與同步、RTL仿真、綜合實現(xiàn)及PHY適配等關(guān)鍵模塊可幫助讀者理解DDR3預(yù)充電、激活、刷新、讀寫命令和CAS/RAS/CWL時序參數(shù)適用于高速數(shù)據(jù)緩存、圖像處理、嵌入式存儲擴展等實際場景。壓縮包共506個文件大小約6.23MB除了Verilog/VHDL源碼還包含Xilinx工程配置文件、UCF約束文件、Tcl腳本、仿真批處理腳本以及綜合實現(xiàn)后的日志報告與比特流文件整體是一個能直接編譯運行的工程目錄。目前已有202人瀏覽學(xué)習(xí)。由于項目代碼可順利編譯和運行讀者可以結(jié)合源碼從地址生成、命令調(diào)度到數(shù)據(jù)讀寫逐步拆解適合作為Spartan-6平臺上DDR3接口設(shè)計的入門范例和工程參考。1. 在XC6SLX16上把DDR3讀寫跑通真正的難點在MIG之外一份能跑通的DDR3讀寫工程寫完MIG配置只算完成了三分之一。XC6SLX16上的DDR3控制器負責(zé)物理層訓(xùn)練、刷新和命令調(diào)度但用戶看到的是命令通道與數(shù)據(jù)通道分離的握手接口地址怎么拆分、命令怎么排隊、128位數(shù)據(jù)怎么在不同時鐘域之間搬運都需要用Verilog HDL在上層邏輯里解決。許多人在仿真模型上通過一上板就出現(xiàn)隨機數(shù)據(jù)錯誤問題大多出在FIFO水位設(shè)置和讀數(shù)據(jù)返回延遲的處理上。這篇文章基于一個包含PLL、三組FIFO和回環(huán)比對邏輯的ISE工程拆解DDR3讀寫數(shù)據(jù)路徑的完整實現(xiàn)適合準(zhǔn)備在Spartan-6上接DDR3做圖像緩存或高速采集的開發(fā)者。2. Spartan-6 DDR3接口的選型邊界與MIG生成配置2.1 先定用戶接口風(fēng)格再談DDR3時序MIG在Spartan-6上生成DDR3控制器有兩種用戶接口風(fēng)格。一種是帶AXI4包裝的接口命令、讀數(shù)據(jù)、寫數(shù)據(jù)各自獨立通道另一種是簡單用戶接口把控制器封裝成一組類似SRAM的握手信號。對于XC6SLX16這樣邏輯資源有限、目標(biāo)功能集中的工程建議直接用簡單用戶接口它把DDR3控制器的內(nèi)部狀態(tài)暴露在app_addr、app_cmd、app_en這類端口上出現(xiàn)問題時ChipScope抓到的波形能直接理解因果。更關(guān)鍵的是Spartan-6時代的AXI4接口經(jīng)過MIG轉(zhuǎn)換層后地址映射和處理延遲對外不可見想精細控制Bank切換和讀寫優(yōu)先級反而更吃力。工程文件里的video_driver.v.bak和多組FIFO都是圍繞簡單用戶接口組織的。這實際上是早期Spartan-6工程最常見的設(shè)計形態(tài)用戶狀態(tài)機加FIFO緩沖再加MIG用戶接口。簡單用戶接口的代價是需要自己維護三個狀態(tài)機命令發(fā)送狀態(tài)機、寫數(shù)據(jù)排隊狀態(tài)機、讀數(shù)據(jù)接收狀態(tài)機。這三個狀態(tài)機之間的握手關(guān)系是DDR3讀寫通路的骨架FIFO只是給這個骨架填充緩沖能力。后面涉及的地址映射、時序參數(shù)和跨時鐘域處理全部要在這層落地。2.2 頻率、位寬與突發(fā)長度三個參數(shù)的綁定關(guān)系MIG配置界面需要確定的參數(shù)不多但每個都直接決定用戶側(cè)代碼怎么寫。存儲時鐘選400MHz還是333MHz對應(yīng)DDR3-800還是DDR3-667數(shù)據(jù)位寬常見16位對應(yīng)一組DQ、DQS、DM信號突發(fā)長度固定為BL8一次命令覆蓋8個16位數(shù)據(jù)合計128比特。顆粒型號末尾數(shù)字代表速率等級選型時按實際顆粒標(biāo)稱速率設(shè)置寧可低配不要超頻。參數(shù)選型對用戶代碼的影響Memory Clock333/400 MHz決定顆粒速率等級和刷新間隔User Clock100/133 MHzFIFO與狀態(tài)機工作時鐘DQ位寬16 bit突發(fā)數(shù)據(jù)128 bitBurst LengthBL8每次命令讀寫8個連續(xù)列地址控制器實現(xiàn)MIG生成決定決定PHY邏輯與用戶邏輯的資源占比這幾個參數(shù)在MIG生成后固定進IP核運行中不能改動。用戶邏輯需要從32位或16位粒度訪問DDR3時只能靠FIFO做位寬轉(zhuǎn)換直接在MIG端口上做位拼接會有嚴(yán)重時序風(fēng)險。工程里出現(xiàn)fifo_512x128b、fifo_1024x32b、sync_fifo_2048x16b三種不同深度位寬的FIFO正是為了在不同位置處理這種轉(zhuǎn)換和緩沖。2.3 生成后的工程結(jié)構(gòu)PLL、FIFO與控制邏輯的分工MIG生成完DDR3控制器后工程里會出現(xiàn)PHY物理層、校準(zhǔn)邏輯、命令調(diào)度和用戶接口封裝等一組文件。pll.asy是PLL核的封裝文件負責(zé)把外部時鐘轉(zhuǎn)換成控制器工作時鐘同時輸出用戶時鐘給上層邏輯。fifo_512x128b、fifo_1024x32b和sync_fifo_2048x16b分別承擔(dān)寫緩沖、讀緩沖和視頻像素緩存。rem_files.bat、ise_flow.bat和implement.bat對應(yīng)ISE命令行完整流程清理生成文件、調(diào)用xst綜合、ngdbuild映射、par布局布線、bitgen生成比特流。保留這些腳本的原因在于DDR3工程編譯鏈路長手動在ISE圖形界面里點擊容易漏步驟腳本化后可以反復(fù)回歸。每次修改FIFO深度或狀態(tài)機后重新編譯要確認時序收斂和校準(zhǔn)邏輯沒有退化腳本配合日志輸出比人工操作快得多。MIG控制器自帶初始化校準(zhǔn)和自動刷新邏輯用戶邏輯不要往命令通道里插入刷新命令。上電正確復(fù)位后等待控制器的初始化完成信號拉高再開始正常的讀寫事務(wù)。具體信號名在不同版本MIG中略有差異例如app_calib_done或app_done以生成頂層文件里的實際端口為準(zhǔn)。3. 地址映射與讀寫命令狀態(tài)機的Verilog實現(xiàn)3.1 Bank、行、列地址的拆分與對齊DDR3顆粒的存儲陣列按Bank、行、列組織。用戶送進MIG的地址是連續(xù)邏輯地址映射到三組物理地址的策略直接影響連續(xù)讀寫效率。常見做法是邏輯地址低若干位映射為列地址中間位映射為Bank高位映射為行地址。這樣連續(xù)邏輯地址落在同一行的不同列BL8突發(fā)可以順序執(zhí)行不需要頻繁激活新行。以16位DQ、8個Bank、行地址15位、列地址10位的顆粒為例邏輯地址可以這樣拆assign app_addr { logic_addr[27:21], // 行地址高7位 logic_addr[20:18], // Bank地址3位 logic_addr[17:10], // 行地址低8位 logic_addr[9:3], // 列地址高7位 3b000 // 突發(fā)內(nèi)偏移BL8固定 };低3位補零是因為一次BL8突發(fā)跨越8個列地址命令地址必須按8對齊。列地址只取高7位說明一次突發(fā)訪問列地址區(qū)間的一半剩余列空間由下一次地址遞增到達。這段映射邏輯放在MIG接口外邊映射錯誤時讀寫本身正常但數(shù)據(jù)落到錯誤位置仿真階段要重點比對地址分布。3.2 讀寫狀態(tài)機的握手與命令序列簡單用戶接口下一次突發(fā)寫事務(wù)是這樣的IDLE狀態(tài)收到用戶請求后將映射地址放到app_addr同時拉高app_en和寫命令編碼隨后在寫數(shù)據(jù)通道拉高app_wdf_wren把128位數(shù)據(jù)送上app_wdf_data命令被MIG接受后狀態(tài)機回到IDLE。讀事務(wù)類似只是不發(fā)送寫數(shù)據(jù)改為在若干周期后等待app_rd_data_valid指示讀數(shù)據(jù)有效。localparam IDLE 3d0, WR_CMD 3d1, RD_WAIT 3d2; always (posedge clk_user or posedge rst) begin if (rst) begin state IDLE; app_en 1b0; end else case (state) IDLE: if (req_valid) begin app_addr mapped_addr; app_cmd req_write ? 3b000 : 3b001; app_en 1b1; state req_write ? WR_CMD : RD_WAIT; end WR_CMD: begin app_en 1b0; app_wdf_wren 1b1; state IDLE; end RD_WAIT: if (app_rd_data_valid) state IDLE; endcase endapp_cmd編碼中3b000代表寫命令3b001代表讀命令這個編碼與后來的Vivado MIG不同遷移代碼時要特別注意。寫數(shù)據(jù)通道和命令通道是獨立握手的MIG允許寫命令先發(fā)出、寫數(shù)據(jù)在后續(xù)周期跟上但前一突發(fā)數(shù)據(jù)必須在下一命令被接受前穩(wěn)定。狀態(tài)機里還應(yīng)檢查命令通道的滿信號和寫數(shù)據(jù)通道的滿信號避免在MIG忙時無效握手。讀寫請求同時到達時需要一個優(yōu)先級策略。常見做法是寫優(yōu)先因為寫FIFO接近滿水位就隨時可能溢出而讀FIFO暫時為空只是讓用戶邏輯等待。體現(xiàn)在代碼上就是req_valid信號在讀寫同時有效時優(yōu)先受理寫請求。3.3 刷新與時序參數(shù)需要留出的帶寬余量DDR3要求每64毫秒完成全陣列刷新典型顆粒拆成8192次刷新命令平均每7.8微秒一次。MIG在內(nèi)部自動安排刷新并暫停接收新命令對用戶邏輯來說寫FIFO里的數(shù)據(jù)暫時無法被消費。512深度、128位寬的寫FIFO在用戶時鐘下可連續(xù)接收512拍數(shù)據(jù)折合64K字節(jié)足以覆蓋數(shù)次刷新窗口積壓的數(shù)據(jù)。讀延遲同樣要留余量。從讀命令被MIG接受到讀數(shù)據(jù)出現(xiàn)在app_rd_data上中間經(jīng)過命令排隊、顆粒存取和PHY延遲典型值是幾十個用戶時鐘周期。用戶邏輯不能用固定延遲計數(shù)去等待結(jié)果應(yīng)該用讀數(shù)據(jù)有效信號驅(qū)動狀態(tài)轉(zhuǎn)移。時序參數(shù)典型值用戶邏輯關(guān)注點tRFC110ns至350ns刷新導(dǎo)致命令暫停FIFO水位要留余量tRCD大約13ns行激活到列命令間隔MIG內(nèi)部消化CAS Latency5至7周期讀數(shù)據(jù)返回延遲用FIFO吸收tFAW20至40ns四Bank窗口限制連續(xù)寫受約束另一個常見邊界是復(fù)位時序。MIG的復(fù)位需要持續(xù)若干個用戶時鐘周期復(fù)位釋放后不能立刻發(fā)起讀寫必須等待初始化校準(zhǔn)完成。很多上板后讀出全零或首筆數(shù)據(jù)丟失根源是復(fù)位釋放后過早發(fā)送命令MIG還沒有完成DLL鎖定和寫均衡訓(xùn)練。4. 數(shù)據(jù)通路設(shè)計從FIFO到MIG用戶端的跨時鐘域讀寫4.1 為什么跨時鐘域必須用FIFO而不是寄存器打拍用戶邏輯時鐘和MIG用戶時鐘即使頻率相同相位關(guān)系在布局布線后也無法保證穩(wěn)定。直接用寄存器打拍跨時鐘域綜合工具不會對兩個時鐘域信號做完整時序約束每次編譯后延遲都可能變化。異步FIFO內(nèi)部用格雷碼指針和兩級同步器處理時鐘域差異指針跳變每次只有一位變化采樣錯誤被限制在單個數(shù)據(jù)項上不會污染整個鏈路。工程里三組FIFO的角色需要分清楚。它們不是隨手加的緩存而是分別對應(yīng)寫數(shù)據(jù)排隊、讀數(shù)據(jù)緩沖、視頻像素平滑三個用途FIFO實例深度與位寬位置類型fifo_512x128b512×128 bit寫數(shù)據(jù)緩沖異步FIFOfifo_1024x32b1024×32 bit讀數(shù)據(jù)緩沖與位寬轉(zhuǎn)換異步FIFOsync_fifo_2048x16b2048×16 bit視頻驅(qū)動像素行緩存同步FIFO三組FIFO的選擇不是隨意的。512深度匹配DDR3刷新窗口內(nèi)的寫入余量1024深度覆蓋讀響應(yīng)延遲的波動范圍2048深度用于緩存圖像數(shù)據(jù)的一行或兩行像素保證視頻驅(qū)動在DDR3調(diào)度間隙不產(chǎn)生畫面撕裂。4.2 寫數(shù)據(jù)通路水位閾值決定有效帶寬寫路徑的典型數(shù)據(jù)流是用戶模塊把一次事務(wù)的128位數(shù)據(jù)寫入fifo_512x128bFIFO輸出端接到MIG寫數(shù)據(jù)通道。當(dāng)DDR3控制器正在刷新或切換Bank時FIFO暫時被阻塞數(shù)據(jù)排隊等待用戶模塊只要看到FIFO未滿就可以繼續(xù)寫。這解耦了用戶邏輯寫入節(jié)奏與DDR3介質(zhì)固有延遲。fifo_512x128b u_wr_fifo ( .wr_clk (clk_src), // 用戶數(shù)據(jù)源時鐘 .rd_clk (clk_user), // MIG用戶時鐘 .din (user_wr_data[127:0]), .wr_en (user_wr_valid), .rd_en (app_wdf_wren), .dout (app_wdf_data[127:0]), .full (wr_fifo_full), .empty (wr_fifo_empty), .wr_data_count (wr_fifo_wcnt[8:0]) ); // 寫側(cè)水位已有數(shù)據(jù)不超過384拍時允許繼續(xù)寫入 assign user_wr_ready (wr_fifo_wcnt 9d384);wr_data_count表示寫側(cè)已累積的數(shù)據(jù)量。閾值384對應(yīng)512深度下預(yù)留128拍空間覆蓋MIG端因為刷新和命令調(diào)度的最大背壓周期。閾值設(shè)太小MIG接受寫數(shù)據(jù)需要幾十拍FIFO卻在滿水位停止接收用戶數(shù)據(jù)寫帶寬下降閾值設(shè)太大則犧牲緩沖深度長突發(fā)時溢出概率增加。這個值要根據(jù)實際時序報告微調(diào)。寫FIFO讀使能直接接app_wdf_wrenMIG每拍拉高該信號都會從FIFO取走數(shù)據(jù)不需要額外反壓。用戶邏輯側(cè)如果數(shù)據(jù)源時鐘與clk_user同頻同相工程里也可以把兩端接到同一個時鐘但考慮到后續(xù)擴展保留異步端口更穩(wěn)妥。4.3 讀數(shù)據(jù)通路用FIFO吸收不確定延遲讀路徑上MIG返回的讀數(shù)據(jù)與讀命令之間相隔周期數(shù)不固定直接依賴狀態(tài)機計數(shù)容易錯位。把有效數(shù)據(jù)寫入FIFO數(shù)據(jù)自己排隊用戶模塊只需檢查FIFO非空即可取數(shù)。工程里的fifo_1024x32b位于讀路徑后端承接的是32位粒度的圖像灰度或采樣數(shù)據(jù)。MIG的128位讀數(shù)據(jù)總線上一次突發(fā)給出8個16位像素視頻驅(qū)動模塊按像素順序提取并寫入32位FIFO同時完成位寬轉(zhuǎn)換和延遲吸收。fifo_1024x32b u_rd_fifo ( .wr_clk (clk_user), // MIG讀數(shù)據(jù)返回時鐘 .rd_clk (clk_display), // 下游圖像處理時鐘 .din (rd_slice_data), // 128位總線切片后的32位數(shù)據(jù) .wr_en (rd_slice_valid), // 切片數(shù)據(jù)有效 .rd_en (user_rd_ready), .dout (user_rd_data[31:0]), .full (rd_fifo_full), .empty (rd_fifo_empty) );讀FIFO寫使能從app_rd_data_valid和切片邏輯共同產(chǎn)生。切片邏輯把每次MIG返回的128位數(shù)據(jù)拆成4個32位字按順序?qū)懭隖IFO。只要MIG聲明數(shù)據(jù)有效切片邏輯就開始拆分不需要判斷數(shù)據(jù)屬于哪個事務(wù)因為在簡單用戶接口下MIG保證讀數(shù)據(jù)按命令順序返回FIFO天然完成重新排序。這個特性讓讀路徑狀態(tài)機很簡單代價是FIFO深度要覆蓋最壞情況下的在途數(shù)據(jù)總量。如果下游處理模塊只需要16位像素寬度可以在fifo_1024x32b后再接sync_fifo_2048x16b做一次寬度轉(zhuǎn)換兩級FIFO串聯(lián)的模式在視頻通路里很常見前級吸收DDR3讀延遲后級平滑像素消費速率的波動。4.4 回環(huán)驗證邏輯讓讀寫路徑自動暴露問題工程里用于上板驗證的是一段回環(huán)邏輯。它先從固定起始地址寫入遞增模式數(shù)據(jù)然后按同一地址順序回讀并逐拍比對。比對結(jié)果不一致時內(nèi)部計數(shù)器記錄錯誤地址同時點亮狀態(tài)LED。這段邏輯覆蓋了地址映射、命令狀態(tài)機、寫FIFO水位、讀FIFO延遲、MIG初始化校準(zhǔn)全部環(huán)節(jié)任何一段出問題都會表現(xiàn)為比對失敗或超時。回環(huán)邏輯建議支持兩個開關(guān)一個是復(fù)位DDR3控制器與用戶邏輯的全局復(fù)位另一個是錯誤后暫停繼續(xù)讀寫的禁止位。上板時如果回環(huán)比對失敗先復(fù)位再觀察錯誤是否穩(wěn)定復(fù)現(xiàn)。錯誤地址固定在某一段優(yōu)先懷疑地址映射錯誤地址隨機跳動且伴隨寫FIFO滿信號頻繁拉高則優(yōu)先調(diào)整水位閾值。這類邊界定位技巧比直接抓波形更快。5. 仿真驗證、ILA調(diào)試與容易翻車的邊界點5.1 用MIG自帶模型做讀寫回歸MIG生成時會輸出內(nèi)存顆粒模型和控制器行為模型不需要額外下載第三方模型。在ModelSim里把生成目錄下的sim_model和RTL源碼一起編譯寫一個testbench驅(qū)動用戶接口發(fā)起固定次數(shù)讀寫事務(wù)后自動比對讀回數(shù)據(jù)并打印結(jié)果。initial begin wait (cal_done_sig 1b1); for (int i 0; i 128; i) begin user_wr_addr base_addr i[8:0]; user_wr_data {32hA5A5_0000, i[25:0]}; user_wr_valid 1b1; (posedge clk_user); end repeat (200) (posedge clk_user); for (int i 0; i 128; i) begin user_rd_addr base_addr i[8:0]; user_rd_req 1b1; (posedge clk_user); end end注意地址增量是1對應(yīng)突發(fā)序號如果用戶邏輯內(nèi)部按字節(jié)定義地址步長要改成8。仿真目標(biāo)不只是數(shù)據(jù)正確還要觀察讀寫FIFO余量曲線確認最壞情況下不會觸發(fā)滿信號。5.2 用ChipScope抓三類信號上板調(diào)試時在ISE的ChipScope里插ILA核優(yōu)先抓三類信號用戶讀寫請求與MIG命令通道使能的對應(yīng)關(guān)系確認請求沒有被忙信號長期阻塞寫FIFO的水位和滿信號判斷閾值是否合理讀FIFO的empty與MIG讀數(shù)據(jù)有效信號的分布觀察讀延遲抖動范圍。觸發(fā)條件設(shè)置為回環(huán)比對錯誤信號的上升沿抓取錯誤前后的時序上下文比隨機抓波形更高效。5.3 邊界條件排查現(xiàn)象可能原因驗證手段回環(huán)比對錯位固定讀FIFO直通模式配置與預(yù)期不符關(guān)閉直通模式重測高地址段數(shù)據(jù)異常地址映射與MIG行列拆分不一致對比生成的地址位序間歇性數(shù)據(jù)丟失寫FIFO水位閾值過小閾值增大到512的75%初始化校準(zhǔn)不完成PLL輸出時鐘不滿足MIG要求抓PLL鎖定信號復(fù)位后首筆失敗MIG未完成校準(zhǔn)就發(fā)命令增加校準(zhǔn)完成等待邏輯寫FIFO水位閾值和校準(zhǔn)等待最容易被忽略。前者涉及帶寬與安全深度的取舍建議在仿真階段根據(jù)最壞刷新間隔做含水測試后者屬于時序邊界很多設(shè)計仿真時沒模擬MIG的啟動過程直接把校準(zhǔn)完成信號當(dāng)作恒高上板行為自然與仿真不一致。工程里的implement.bat腳本能快速重編復(fù)驗每次調(diào)參后重跑完整構(gòu)建流程把仿真與上板結(jié)果記錄成日志小改動也能追溯到對應(yīng)版本。本文還有配套的精品資源點擊獲取