接口實(shí)戰(zhàn):AXI4信號(hào)解析與帶寬時(shí)序調(diào)試)
做DDR4 IP調(diào)試這幾年幾乎每天都在跟“用戶(hù)接口”打交道。很多第一次接觸FPGA DDR4設(shè)計(jì)的工程師拿到IP核生成的示例工程后第一個(gè)反應(yīng)往往是一堆AXI4接口信號(hào)幾百個(gè)端口定義完全不知道從哪里下手。我早期也走過(guò)不少?gòu)澛匪赃@篇內(nèi)容想從一個(gè)實(shí)操者的角度把DDR4 IP的用戶(hù)接口掰開(kāi)揉碎講清楚——從信號(hào)含義到時(shí)序關(guān)系從帶寬計(jì)算到讀寫(xiě)測(cè)試再到那些文檔里不會(huì)寫(xiě)的坑。不管你是正在做DDR4硬件設(shè)計(jì)還是被“cal fail”折磨到頭疼這篇內(nèi)容應(yīng)該都能給你一個(gè)相對(duì)完整的參考坐標(biāo)。1. DDR4 IP用戶(hù)接口的整體設(shè)計(jì)與思路拆解1.1 為什么用戶(hù)接口選擇AXI4而不是黑盒式讀寫(xiě)DDR4 IP核在FPGA內(nèi)部為用戶(hù)側(cè)提供了多種接口方式最常見(jiàn)的就是AXI4接口。為什么主流方案都選AXI4核心原因在于A(yíng)XI4的通道分離設(shè)計(jì)非常契合DDR控制器的讀寫(xiě)調(diào)度邏輯。DDR控制器本質(zhì)上是一個(gè)復(fù)雜的狀態(tài)機(jī)需要不斷在預(yù)充電、激活、讀、寫(xiě)、刷新之間切換。如果給用戶(hù)一個(gè)非常簡(jiǎn)單的讀寫(xiě)接口比如直接給地址和命令用戶(hù)寫(xiě)起來(lái)確實(shí)簡(jiǎn)單但控制器內(nèi)部調(diào)度會(huì)很痛苦因?yàn)槟愫茈y在不了解時(shí)序細(xì)節(jié)的情況下高效排布命令。AXI4則把讀地址通道、讀數(shù)據(jù)通道、寫(xiě)地址通道、寫(xiě)數(shù)據(jù)通道、寫(xiě)響應(yīng)通道全部拆開(kāi)每個(gè)通道獨(dú)立握手機(jī)制這樣控制器可以分別調(diào)度讀和寫(xiě)甚至可以在寫(xiě)數(shù)據(jù)還沒(méi)準(zhǔn)備好的時(shí)候先接收寫(xiě)地址靈活性高很多。AXI4接口對(duì)軟件工程師也很友好因?yàn)樗旧砭褪茿RM處理器的標(biāo)準(zhǔn)總線(xiàn)。如果你在FPGA內(nèi)部集成了軟核或者硬核處理器那么通過(guò)AXI4總線(xiàn)直接訪(fǎng)問(wèn)DDR4幾乎不需要額外的橋接邏輯整個(gè)系統(tǒng)的數(shù)據(jù)通路就順下來(lái)了。1.2 從用戶(hù)接口到物理存儲(chǔ)顆粒之間的分層關(guān)系一個(gè)完整的DDR4存儲(chǔ)子系統(tǒng)可以劃分為三層用戶(hù)邏輯層、控制器層、物理層。用戶(hù)接口位于最上層物理層PHY連接實(shí)際的DDR4顆粒中間控制器負(fù)責(zé)把AXI4事務(wù)轉(zhuǎn)換成DDR命令序列。用戶(hù)在用戶(hù)接口上發(fā)起一次寫(xiě)操作數(shù)據(jù)并不會(huì)立刻落到存儲(chǔ)顆粒上而是先進(jìn)入控制器內(nèi)部的寫(xiě)數(shù)據(jù)緩沖再由控制器根據(jù)Bank狀態(tài)、行狀態(tài)、刷新要求等條件把數(shù)據(jù)分批寫(xiě)入。同樣讀操作也不是簡(jiǎn)單的地址直通控制器可能根據(jù)最近的行命中情況調(diào)整Bank和行的打開(kāi)順序從而避免頻繁的“預(yù)充電激活”開(kāi)銷(xiāo)。理解這個(gè)分層非常重要因?yàn)楹芏嗾{(diào)試問(wèn)題其實(shí)不在用戶(hù)接口本身而在控制器調(diào)度策略上。比如你在用戶(hù)接口測(cè)得的延遲并不等于顆粒的真實(shí)讀寫(xiě)延遲中間還有隊(duì)列和仲裁的等待時(shí)間。1.3 了解接口共性與差異DDR2/DDR3/DDR4的用戶(hù)接口到底哪里不同網(wǎng)上經(jīng)常能看到“DDR2、DDR3、DDR4區(qū)別”這類(lèi)熱詞大家關(guān)注點(diǎn)多在速率、電壓、容量和引腳差異上。但從FPGA IP用戶(hù)接口角度來(lái)說(shuō)這幾代的AXI4接口信號(hào)框架其實(shí)是高度相似的最大的變化在內(nèi)部參數(shù)配置和物理層約束。時(shí)鐘頻率不同DDR4用戶(hù)接口時(shí)鐘通??梢耘艿?00MHz以上DDR3多在200MHz-400MHz之間DDR2則更低。突發(fā)長(zhǎng)度不同DDR3和DDR4默認(rèn)BL8突發(fā)8DDR2默認(rèn)BL4這直接影響用戶(hù)接口數(shù)據(jù)位寬和有效帶寬計(jì)算。片上校準(zhǔn)流程不同DDR4的training流程更復(fù)雜包括寫(xiě)電平校準(zhǔn)、讀DQS門(mén)限校準(zhǔn)等所以DDR4 IP的初始化時(shí)間明顯更長(zhǎng)。對(duì)做用戶(hù)邏輯的人來(lái)說(shuō)最大的感受是只要IP配置得當(dāng)上層讀寫(xiě)代碼的編寫(xiě)思路幾乎可以沿用但如果在遷移到DDR4時(shí)還按DDR3的老思路去估算時(shí)序和帶寬就會(huì)踩坑。2. 用戶(hù)接口核心信號(hào)解析與實(shí)操要點(diǎn)2.1 讀通道信號(hào)逐項(xiàng)拆解在Xilinx系列FPGA的DDR4 IP中用戶(hù)接口以AXI4為主讀通道核心信號(hào)包括以下幾組。讀地址通道信號(hào)以常見(jiàn)命名風(fēng)格為例s_axi_araddr讀地址寬度與用戶(hù)地址寬度一致。s_axi_arlen突發(fā)長(zhǎng)度AXI4標(biāo)準(zhǔn)里是8bit表示“突發(fā)次數(shù)減1”也就是arlen7代表一次讀8拍數(shù)據(jù)。s_axi_arready控制器準(zhǔn)備好接收讀地址。s_axi_arvalid用戶(hù)邏輯請(qǐng)求發(fā)出讀地址。讀數(shù)據(jù)通道信號(hào)s_axi_rdata讀數(shù)據(jù)位寬通常是DDR顆粒位寬和突發(fā)長(zhǎng)度的乘積關(guān)系。以64bit顆粒位寬、BL8為例如果數(shù)據(jù)總線(xiàn)是64bit那么一次突發(fā)需要8拍但如果用戶(hù)接口數(shù)據(jù)位寬是512bit那么一次突發(fā)一拍就能返回全部數(shù)據(jù)。s_axi_rresp讀響應(yīng)DDR控制器的響應(yīng)基本都是OKAY如果出現(xiàn)錯(cuò)誤往往意味著地址對(duì)齊問(wèn)題或者ECC校驗(yàn)失敗。s_axi_rvalid和s_axi_rready一起構(gòu)成讀數(shù)據(jù)的握手。有一點(diǎn)要特別注意讀數(shù)據(jù)的返回順序。在DDR4 IP中在讀地址仲裁完成后讀數(shù)據(jù)會(huì)在若干周期后返回返回時(shí)由rvalid標(biāo)識(shí)有效窗口。用戶(hù)邏輯必須等rvalid拉高的那一拍再采樣rdata而不是自己推算延遲。依賴(lài)固定延遲的做法很不穩(wěn)妥。2.2 寫(xiě)通道信號(hào)逐項(xiàng)拆解寫(xiě)通道的信號(hào)同樣分兩組。寫(xiě)地址通道s_axi_awaddr寫(xiě)地址。s_axi_awlen寫(xiě)突發(fā)長(zhǎng)度。s_axi_awvalid和s_axi_awready構(gòu)成寫(xiě)地址握手。寫(xiě)數(shù)據(jù)通道s_axi_wdata寫(xiě)數(shù)據(jù)。s_axi_wstrb寫(xiě)字節(jié)使能用于掩碼寫(xiě)入位寬是數(shù)據(jù)位寬除以8。s_axi_wlast寫(xiě)數(shù)據(jù)最后一拍的標(biāo)志。s_axi_wvalid和s_axi_wready構(gòu)成寫(xiě)數(shù)據(jù)握手。寫(xiě)響應(yīng)通道s_axi_bresp和s_axi_bvalid。寫(xiě)響應(yīng)表示一次寫(xiě)事務(wù)是否被控制器接受與讀響應(yīng)一樣正常情況下都是OKAY。寫(xiě)通道最容易出問(wèn)題的不是信號(hào)本身而是握手時(shí)序。AXI4協(xié)議要求寫(xiě)數(shù)據(jù)必須最后到來(lái)而且wlast要準(zhǔn)確標(biāo)注最后一拍。很多初學(xué)用戶(hù)在寫(xiě)數(shù)據(jù)長(zhǎng)度計(jì)算上出錯(cuò)導(dǎo)致wlast無(wú)法和wvalid同時(shí)拉高控制器一直等不到完整突發(fā)整個(gè)寫(xiě)通道就卡死了。2.3 地址映射、數(shù)據(jù)掩碼與對(duì)齊的經(jīng)典誤區(qū)DDR4用戶(hù)接口的地址映射是把AXI地址翻譯到“Bank、Row、Column”的過(guò)程但用戶(hù)往往不需要直接操作Bank/Row/Column只需要知道地址如何映射即可。不過(guò)有幾個(gè)關(guān)鍵點(diǎn)容易忽略。地址位寬不等于顆粒容量位寬。用戶(hù)接口地址是字節(jié)地址而控制器內(nèi)部按照突發(fā)粒度映射低幾位地址會(huì)被忽略或用于片選。寫(xiě)掩碼wstrb必須按字節(jié)對(duì)應(yīng)。512bit數(shù)據(jù)總線(xiàn)對(duì)應(yīng)64字節(jié)wstrb就是64bit每一位對(duì)應(yīng)一個(gè)字節(jié)。如果你只寫(xiě)半個(gè)字節(jié)wstrb對(duì)應(yīng)位置1控制器會(huì)按掩碼合并數(shù)據(jù)不會(huì)破壞其他字節(jié)。對(duì)齊問(wèn)題。AXI4要求突發(fā)的首地址必須按突發(fā)長(zhǎng)度對(duì)齊。比如256bit數(shù)據(jù)位寬、BL8的配置下一次突發(fā)的字節(jié)數(shù)是32字節(jié)那么起始地址的低5位必須是0。很多用戶(hù)拿一個(gè)任意地址去寫(xiě)結(jié)果控制器要么報(bào)錯(cuò)要么行為不符合預(yù)期。2.4 握手協(xié)議的本質(zhì)valid與ready出現(xiàn)的先后關(guān)系A(chǔ)XI4握手看似簡(jiǎn)單但細(xì)節(jié)要求很多。從DDR4 IP實(shí)際應(yīng)用來(lái)說(shuō)需要記住一個(gè)核心規(guī)則valid信號(hào)一旦拉高必須保持到ready為高且握手成功的那一拍而ready可以等待validvalid不能等待ready才拉高。換句話(huà)說(shuō)用戶(hù)邏輯不能因?yàn)榭吹絘rready或wready為低就把valid撤掉。正確做法是只要內(nèi)部有未完成的事務(wù)請(qǐng)求valid就維持有效直到握手成功。這個(gè)規(guī)則同時(shí)適用于地址通道和數(shù)據(jù)通道。此外還要注意DDR4 IP的用戶(hù)接口在某些配置下數(shù)據(jù)通道的valid和ready關(guān)系可能帶有附加約束。比如寫(xiě)數(shù)據(jù)通道如果控制器在握手成功前不接受新的寫(xiě)數(shù)據(jù)wready會(huì)暫時(shí)拉低此時(shí)用戶(hù)數(shù)據(jù)必須保持在總線(xiàn)上不變同時(shí)wvalid繼續(xù)保持有效。數(shù)據(jù)、wstrb、wlast都必須同步保持不變。我實(shí)際調(diào)試中遇到過(guò)一種情況代碼里用狀態(tài)機(jī)處理多個(gè)寫(xiě)事務(wù)某個(gè)狀態(tài)判斷wready拉低后就跳走結(jié)果寫(xiě)數(shù)據(jù)被截?cái)郉DR里出現(xiàn)了錯(cuò)誤數(shù)據(jù)。后來(lái)所有通道都改成“當(dāng)前狀態(tài)保持直到握手成功”的邏輯問(wèn)題才徹底消失。3. 帶寬計(jì)算、時(shí)序收斂與用戶(hù)接口的工程配置3.1 如何計(jì)算DDR4用戶(hù)接口的理論帶寬與實(shí)際帶寬很多硬件工程師在做DDR4硬件設(shè)計(jì)時(shí)會(huì)畫(huà)原理圖、算顆粒帶寬但到了FPGA用戶(hù)接口層面反而容易忽略有效帶寬的折算。理論帶寬的計(jì)算公式是顆粒速率 × 數(shù)據(jù)位寬。比如DDR4-240064bit顆粒位寬理論帶寬就是2400MT/s × 64bit / 8 19.2GB/s。這是一個(gè)非常好的參考數(shù)值但用戶(hù)接口實(shí)際能拿到的帶寬通常低于這個(gè)值。實(shí)際帶寬受幾個(gè)因素影響刷新開(kāi)銷(xiāo)DDR4需要周期性刷新刷新期間控制器暫停正常讀寫(xiě)。按典型tREFI和刷新時(shí)間計(jì)算刷新開(kāi)銷(xiāo)一般在2%到5%之間。讀寫(xiě)總線(xiàn)反轉(zhuǎn)開(kāi)銷(xiāo)從寫(xiě)切到讀中間需要tWTR等時(shí)序間隔從讀切到寫(xiě)需要tRTW等間隔。如果頻繁交替讀寫(xiě)效率會(huì)明顯下降。激活和預(yù)充電開(kāi)銷(xiāo)隨機(jī)訪(fǎng)問(wèn)場(chǎng)景下每一筆事務(wù)都可能需要新的行激活這會(huì)消耗大量周期。順序訪(fǎng)問(wèn)場(chǎng)景則基本不需要額外激活。指令和數(shù)據(jù)包排隊(duì)開(kāi)銷(xiāo)控制器內(nèi)部仲裁器、隊(duì)列緩沖有限當(dāng)多個(gè)master競(jìng)爭(zhēng)訪(fǎng)問(wèn)時(shí)仲裁等待會(huì)拉低實(shí)際吞吐。所以在做用戶(hù)邏輯帶寬規(guī)劃時(shí)不能拿19.2GB/s當(dāng)預(yù)期值除非你的訪(fǎng)問(wèn)模式是極長(zhǎng)的順序讀寫(xiě)。通常情況下取理論帶寬的70%到80%作為設(shè)計(jì)上限比較靠譜。3.2 用戶(hù)接口時(shí)鐘頻率與數(shù)據(jù)位寬的選擇邏輯DDR4 IP的時(shí)鐘參數(shù)高度依賴(lài)于顆粒配置。選擇用戶(hù)接口數(shù)據(jù)位寬時(shí)要綜合考慮內(nèi)部邏輯利用率、跨時(shí)鐘域難度和PCB布線(xiàn)能力。舉個(gè)例子DDR4-2400、64bit顆粒寬度如果BL8那么8個(gè)突發(fā)數(shù)據(jù)總量是512bit。如果你把用戶(hù)接口數(shù)據(jù)位寬配置為512bit那么用戶(hù)側(cè)時(shí)鐘可以比顆粒時(shí)鐘低很多內(nèi)部邏輯只需要工作在較低頻率如果配置為64bit那么用戶(hù)側(cè)時(shí)鐘接近顆粒時(shí)鐘的等效數(shù)據(jù)速率邏輯時(shí)序收斂壓力很大。具體來(lái)說(shuō)常見(jiàn)配置有兩種配置場(chǎng)景顆粒配置用戶(hù)接口位寬用戶(hù)接口時(shí)鐘適合場(chǎng)合高吞吐場(chǎng)景64bit DDR4-2400512bit300MHz左右視頻處理、大數(shù)據(jù)緩存低復(fù)雜度場(chǎng)景64bit DDR4-2400128bit1200MHz一般較難收斂小緩存、輕量存儲(chǔ)實(shí)際工程中大多數(shù)FPGA設(shè)計(jì)更傾向把用戶(hù)接口位寬配大一點(diǎn)換取更低的用戶(hù)時(shí)鐘頻率因?yàn)楦哳l率邏輯在綜合和布局布線(xiàn)階段非常痛苦尤其當(dāng)設(shè)計(jì)里還有大量跨時(shí)鐘域邏輯時(shí)。3.3 跨時(shí)鐘域處理用戶(hù)邏輯時(shí)鐘與IP時(shí)鐘的同步DDR4 IP的用戶(hù)接口域時(shí)鐘比如ui_clk和用戶(hù)自己的業(yè)務(wù)時(shí)鐘往往是兩個(gè)頻率。所有從業(yè)務(wù)時(shí)鐘進(jìn)入DDR用戶(hù)接口的信號(hào)都必須做跨時(shí)鐘域處理否則會(huì)出現(xiàn)亞穩(wěn)態(tài)。最常用的方式是異步FIFO。業(yè)務(wù)側(cè)數(shù)據(jù)寫(xiě)入FIFODDR側(cè)邏輯從FIFO讀取并產(chǎn)生AXI事務(wù)讀回的數(shù)據(jù)也通過(guò)FIFO回到業(yè)務(wù)側(cè)。這里有個(gè)細(xì)節(jié)FIFO的深度不要只按“最大突發(fā)長(zhǎng)度”來(lái)算要按“最大排隊(duì)延遲”來(lái)算。DDR控制器在刷新、Bank沖突時(shí)可能延遲多個(gè)周期才響應(yīng)如果FIFO太淺背壓就會(huì)傳導(dǎo)到業(yè)務(wù)側(cè)。我之前設(shè)計(jì)過(guò)一個(gè)32GB/s吞吐的視頻緩存模塊FIFO深度取了2048x512bit輪詢(xún)兩個(gè)master時(shí)實(shí)測(cè)下來(lái)FIFO利用率最多到60%留下的余量剛好覆蓋極端刷新場(chǎng)景。3.4 DDR4原理圖設(shè)計(jì)的幾個(gè)關(guān)鍵檢查點(diǎn)這里順帶提一句DDR4硬件設(shè)計(jì)。因?yàn)橛脩?hù)接口調(diào)試出了問(wèn)題很多時(shí)候不是FPGA邏輯問(wèn)題而是原理圖和PCB布線(xiàn)問(wèn)題。查看DDR4原理圖時(shí)建議重點(diǎn)檢查以下幾點(diǎn)電源去耦VDD、VDDQ、VTT、VPP的濾波電容是否按推薦布局放置。參考電壓VREFDDR4對(duì)VREF精度要求高分壓電阻精度要選1%或更好。端接電阻地址線(xiàn)、控制線(xiàn)是否需要ODT和VTT端接位置選擇是否正確。時(shí)鐘差分對(duì)CK_t和CK_c必須嚴(yán)格等長(zhǎng)常用100歐差分阻抗。讀寫(xiě)DQS差分對(duì)每組DQS的差分對(duì)內(nèi)等長(zhǎng)要做得比信號(hào)間等長(zhǎng)更嚴(yán)格。如果原理圖或者PCB的等長(zhǎng)控制不達(dá)標(biāo)用戶(hù)接口可能會(huì)出現(xiàn)偶發(fā)寫(xiě)錯(cuò)、讀錯(cuò)很難穩(wěn)定復(fù)現(xiàn)。遇到這種問(wèn)題時(shí)優(yōu)先用ILA抓取用戶(hù)接口波形如果發(fā)現(xiàn)數(shù)據(jù)錯(cuò)誤位置隨機(jī)、并非某個(gè)固定地址就得回頭查硬件的信號(hào)完整性。4. 實(shí)操過(guò)程從IP配置到DDR4讀寫(xiě)測(cè)試的完整流程4.1 IP配置界面中的關(guān)鍵參數(shù)選擇在Vivado或Quartus里創(chuàng)建DDR4 IP時(shí)參數(shù)較多我只挑影響用戶(hù)接口的幾個(gè)重點(diǎn)說(shuō)起。Controller Options里的“AXI Data Width”參數(shù)直接決定用戶(hù)接口位寬?!癆XI Address Width”要和地址空間大小匹配不要盲目設(shè)置過(guò)大?!癕emory Part”要選對(duì)具體顆粒型號(hào)選錯(cuò)可能導(dǎo)致初始化參數(shù)不正確?!癐nput Clock Period”填的是DDR顆粒的時(shí)鐘周期。比如DDR4-2400顆粒時(shí)鐘是1200MHz周期約833ps填的時(shí)候要確認(rèn)是周期單位不是頻率?!癝ystem Clock”則是用戶(hù)接口和PHY內(nèi)部邏輯的工作時(shí)鐘來(lái)源一般選No Buffer或者專(zhuān)用時(shí)鐘引腳。刷新配置建議保持默認(rèn)“Auto Refresh”除非你需要手動(dòng)控制刷新窗口。配置完成生成IP后建議先跑一下示例工程尤其是自帶的仿真測(cè)試bench能幫你快速確認(rèn)IP配置是否符合預(yù)期。4.2 初始化狀態(tài)檢查從“cal fail”到初始化成功FPGA上電后DDR4 IP內(nèi)部有一個(gè)完整的初始化和校準(zhǔn)流程校準(zhǔn)完成后會(huì)輸出init_calib_complete信號(hào)。很多第一次上手的朋友在調(diào)試時(shí)發(fā)現(xiàn)這個(gè)信號(hào)一直沒(méi)拉高或者直接拉高幾十微秒后又被拉低。先排查最簡(jiǎn)單的部分時(shí)鐘和復(fù)位。DDR4 IP對(duì)復(fù)位時(shí)序很敏感復(fù)位信號(hào)必須在時(shí)鐘穩(wěn)定后再釋放而且釋放要保持足夠的低電平時(shí)間。我之前用外部按鍵復(fù)位因?yàn)橄稌r(shí)間太短導(dǎo)致IP在初始化中途再次被復(fù)位出現(xiàn)偶發(fā)cal fail。換成上電自動(dòng)延時(shí)復(fù)位后問(wèn)題就消失了。如果復(fù)位沒(méi)問(wèn)題那就要看實(shí)際顆粒的接線(xiàn)和參數(shù)。特別是DDR4顆粒的CKE、CS、ODT控制信號(hào)任何一個(gè)接錯(cuò)training都可能失敗。拿到一塊新板子先用萬(wàn)用表確認(rèn)這些信號(hào)到了顆粒引腳再往上查IP配置的引腳約束。4.3 用ILA抓取用戶(hù)接口讀寫(xiě)波形的實(shí)操記錄當(dāng)init_calib_complete拉高后就可以開(kāi)始做讀寫(xiě)測(cè)試了。我的習(xí)慣是先用IP自帶的示例工程跑數(shù)據(jù)比對(duì)再換成自己的用戶(hù)邏輯。但示例工程往往只是一個(gè)“能跑”的框架不一定適合你的場(chǎng)景所以關(guān)鍵還是要會(huì)抓波形。實(shí)操步驟大致是這樣在ILA中例化只抓用戶(hù)接口的寫(xiě)地址通道、寫(xiě)數(shù)據(jù)通道、寫(xiě)響應(yīng)通道和讀通道信號(hào)。設(shè)置觸發(fā)條件為“awvalid為高且awready為高”這樣能抓到第一次寫(xiě)事務(wù)的完整握手。再設(shè)一個(gè)觸發(fā)條件為“rvalid為高”抓讀寫(xiě)回的返回路徑。抓完后把數(shù)據(jù)波形導(dǎo)出和寫(xiě)進(jìn)去的數(shù)據(jù)做比對(duì)。第一次抓波形時(shí)注意觀(guān)察握手的滿(mǎn)足關(guān)系。用手動(dòng)寫(xiě)一個(gè)小塊數(shù)據(jù)然后讀取同一塊地址如果比對(duì)一致說(shuō)明基礎(chǔ)讀寫(xiě)路徑正常。如果比對(duì)不一致優(yōu)先檢查wstrb是否正確很多“隨機(jī)錯(cuò)一個(gè)字節(jié)”的問(wèn)題都是因?yàn)樽止?jié)使能沒(méi)置全。4.4 讀寫(xiě)測(cè)試的幾個(gè)典型場(chǎng)景與判定標(biāo)準(zhǔn)完整搬移測(cè)試適合大塊數(shù)據(jù)傳輸一般做法是往一個(gè)4KB對(duì)齊的區(qū)域?qū)懭脒f增數(shù)據(jù)再讀出來(lái)比對(duì)。遞增數(shù)據(jù)的好處是能快速定位地址漂移如果讀回的遞增序列出現(xiàn)“跳號(hào)”說(shuō)明某個(gè)地址的數(shù)據(jù)被寫(xiě)錯(cuò)或讀錯(cuò)。固定式偽隨機(jī)測(cè)試適合檢測(cè)數(shù)據(jù)線(xiàn)之間的串?dāng)_和信號(hào)完整性問(wèn)題。寫(xiě)入偽隨機(jī)序列讀回后與本地重新生成的序列比對(duì)能發(fā)現(xiàn)單bit或成組bit跳變。特別在DDR4顆粒散熱不好、信號(hào)質(zhì)量邊緣化的時(shí)候這種測(cè)試穩(wěn)定跑幾小時(shí)不出錯(cuò)才算基本合格。讀寫(xiě)交替壓力測(cè)試適合評(píng)估控制器切換效率。大量交替讀寫(xiě)不僅測(cè)試數(shù)據(jù)通路更能暴露控制器調(diào)度策略的問(wèn)題。如果交替頻繁時(shí)出現(xiàn)卡死大概率是用戶(hù)邏輯沒(méi)有處理寫(xiě)響應(yīng)或者讀數(shù)據(jù)背壓。4.5 常見(jiàn)問(wèn)題與排查技巧實(shí)錄我整理了一張速查表記錄日常工作中最常遇到的幾類(lèi)問(wèn)題方便快速定位。現(xiàn)象可能原因排查手段init_calib_complete一直為低時(shí)鐘/復(fù)位異常、DDR4顆粒排線(xiàn)未接好、IP參數(shù)錯(cuò)誤檢查復(fù)位時(shí)序、量測(cè)CKE、CS、ODT核對(duì)顆粒型號(hào)偶發(fā)cal fail電源紋波過(guò)大、參考電壓不干凈、SI質(zhì)量差用示波器看電源紋波、檢查VREF分壓、檢查走線(xiàn)等長(zhǎng)寫(xiě)數(shù)據(jù)回讀偶發(fā)錯(cuò)誤wstrb位沒(méi)寫(xiě)對(duì)、數(shù)據(jù)位跨字節(jié)錯(cuò)位、FIFO跨時(shí)鐘域異常抓取wdata和wstrb波形與預(yù)期數(shù)據(jù)比對(duì)讀數(shù)據(jù)超時(shí)讀地址沒(méi)有發(fā)出去、讀數(shù)據(jù)FIFO未及時(shí)取走、控制器死鎖抓arvalid/arready、rvalid/rready握手長(zhǎng)時(shí)間跑測(cè)試后出錯(cuò)溫度升高導(dǎo)致時(shí)序裕量下降、刷新漏扣加強(qiáng)散熱、延長(zhǎng)壓力測(cè)試時(shí)間、檢查刷新配置效率遠(yuǎn)低于理論帶寬隨機(jī)小粒度突發(fā)過(guò)多、頻繁讀寫(xiě)切換檢查訪(fǎng)問(wèn)模式、適當(dāng)增加突發(fā)長(zhǎng)度、用內(nèi)部緩存聚攏訪(fǎng)問(wèn)實(shí)際踩過(guò)的一個(gè)坑是在DDR4 IP的寫(xiě)數(shù)據(jù)通道中有些IP核要求wdata和wstrb必須提前于wvalid一個(gè)周期穩(wěn)定。我的邏輯一開(kāi)始是同時(shí)變化結(jié)果表現(xiàn)為偶發(fā)數(shù)據(jù)錯(cuò)位。后來(lái)看IP的時(shí)序圖才發(fā)現(xiàn)這個(gè)細(xì)節(jié)調(diào)整后恢復(fù)正常。所以建議各位拿到IP的第一時(shí)間不要只看信號(hào)列表要把波形時(shí)序圖完整過(guò)一遍。4.6 常見(jiàn)問(wèn)題與排查技巧實(shí)錄的補(bǔ)充還有一個(gè)值得單獨(dú)說(shuō)的點(diǎn)phy_clk和ui_clk的關(guān)系。很多IP同時(shí)提供了多個(gè)時(shí)鐘輸出比如sys_clk、ui_clk、phy_clk和dram_clk。在調(diào)試時(shí)務(wù)必區(qū)分它們分別供哪個(gè)模塊使用。phy_clk負(fù)責(zé)PHY邏輯和DDR顆粒時(shí)鐘域ui_clk才是用戶(hù)接口的邏輯時(shí)鐘。如果把兩者混用會(huì)出現(xiàn)波形正常但時(shí)序收斂困難的情況。親測(cè)比較穩(wěn)妥的做法是把ui_clk接入所有用戶(hù)邏輯作為AXI接口的同步時(shí)鐘而phy_clk只在物理層內(nèi)部使用。不要嘗試用phy_clk驅(qū)動(dòng)業(yè)務(wù)邏輯也不要用業(yè)務(wù)時(shí)鐘驅(qū)動(dòng)用戶(hù)接口除非你非常清楚自己在做什么。另一點(diǎn)和“邏輯復(fù)位”有關(guān)。DDR4 IP通常會(huì)提供一個(gè)可選的同步復(fù)位輸出連接到用戶(hù)接口的復(fù)位輸入。用的時(shí)候要注意IP內(nèi)部復(fù)位的釋放時(shí)間可能與校驗(yàn)完成時(shí)間不同。在邏輯里最好把復(fù)位釋放和init_calib_complete兩者一起處理成統(tǒng)一的“系統(tǒng)就緒”條件再對(duì)外發(fā)業(yè)務(wù)啟動(dòng)信號(hào)避免在DDR還沒(méi)完全就緒時(shí)就開(kāi)始讀寫(xiě)。5. 進(jìn)階技巧與工程經(jīng)驗(yàn)補(bǔ)充5.1 多端口訪(fǎng)問(wèn)DDR4時(shí)的仲裁優(yōu)先級(jí)設(shè)計(jì)很多產(chǎn)品中不止一個(gè)模塊要訪(fǎng)問(wèn)DDR4比如CPU寫(xiě)日志、DMA搬數(shù)據(jù)、視頻采集總線(xiàn)同時(shí)在跑。此時(shí)用戶(hù)接口前需要自行設(shè)計(jì)一個(gè)仲裁器。仲裁器最簡(jiǎn)單的實(shí)現(xiàn)是固定優(yōu)先級(jí)但有明顯缺點(diǎn)低優(yōu)先級(jí)業(yè)務(wù)可能被餓死。我的做法是給不同端口配置權(quán)重高權(quán)重端口獲得更高占有率但不完全搶占低權(quán)重端口在若干周期后強(qiáng)制提升優(yōu)先級(jí)保證每個(gè)端口都能拿到帶寬。這個(gè)邏輯可以用一個(gè)計(jì)數(shù)器實(shí)現(xiàn)核心算法不復(fù)雜難的是根據(jù)業(yè)務(wù)特征確定權(quán)重。另一個(gè)要注意的問(wèn)題是突發(fā)粒度。如果每次仲裁都把整個(gè)AXI事務(wù)走完長(zhǎng)事務(wù)會(huì)阻塞其他端口如果把事務(wù)切成更小的粒度切換開(kāi)銷(xiāo)又隨之上升。工程上建議把自動(dòng)切分的粒度設(shè)置為和IP的burst length對(duì)齊比如burst length為16則仲裁粒度設(shè)為16拍數(shù)據(jù)這樣既保證連續(xù)性又避免獨(dú)占時(shí)間過(guò)長(zhǎng)。5.2 自定義時(shí)序約束和驗(yàn)證技巧當(dāng)設(shè)計(jì)規(guī)模變大以后綜合和布局布線(xiàn)工具不會(huì)自動(dòng)保證用戶(hù)接口時(shí)序一定收斂。對(duì)用戶(hù)接口相關(guān)路徑建議做以下約束對(duì)ui_clk創(chuàng)建真實(shí)的時(shí)鐘約束同時(shí)關(guān)聯(lián)ACLK。在約束文件中把用戶(hù)接口寄存器分組到邏輯時(shí)鐘域確保工具不會(huì)把這些路徑視為跨時(shí)鐘域而不做時(shí)序檢查。對(duì)異步復(fù)位釋放路徑添加set_false_path但對(duì)同步復(fù)位釋放路徑要保留檢查避免復(fù)位釋放引起的亞穩(wěn)態(tài)。對(duì)DDR4顆粒物理引腳要正確加入IDELAY約束這個(gè)一般由IP自動(dòng)生成但用戶(hù)盡量不要手動(dòng)改。在功能仿真階段建議使用IP自帶的memory model進(jìn)行隨機(jī)讀寫(xiě)驗(yàn)證??梢园押芏噙吔鐥l件放到仿真里跑比如滿(mǎn)FIFO狀態(tài)下突然停止讀、寫(xiě)地址跨頁(yè)、burst長(zhǎng)度跨配置最大邊界等。仿真通過(guò)后再上板聯(lián)調(diào)問(wèn)題定位會(huì)容易很多。5.3 從DDR4用戶(hù)接口到系統(tǒng)性能調(diào)優(yōu)的思路系統(tǒng)性能調(diào)優(yōu)不能只看用戶(hù)接口但用戶(hù)接口是數(shù)據(jù)進(jìn)入DDR控制器的咽喉。一個(gè)常見(jiàn)調(diào)優(yōu)路徑是先確認(rèn)DDR控制器的寫(xiě)響應(yīng)時(shí)延如果寫(xiě)響應(yīng)過(guò)慢可能意味著控制器內(nèi)部隊(duì)列已經(jīng)堆滿(mǎn)此時(shí)查一下用戶(hù)的訪(fǎng)問(wèn)請(qǐng)求是否過(guò)于碎片化。碎片化問(wèn)題通常表現(xiàn)為大量小長(zhǎng)度的突發(fā)、頻繁切換讀寫(xiě)。系統(tǒng)層面可以做兩件事一是用DMA或者緩存模塊把小請(qǐng)求合并成大請(qǐng)求二是把讀和寫(xiě)分區(qū)減少讀寫(xiě)切換。比如視頻系統(tǒng)建議把采集寫(xiě)入和顯示讀取分到不同地址區(qū)間分配訪(fǎng)問(wèn)時(shí)段這樣DDR控制器能持續(xù)工作在同一“方向”帶寬利用率會(huì)顯著提升。我接手過(guò)一個(gè)4K視頻處理項(xiàng)目初版開(kāi)發(fā)時(shí)讀寫(xiě)交織頻繁實(shí)測(cè)DDR4帶寬利用率只有57%。后來(lái)把所有采集寫(xiě)入和顯示讀取的調(diào)度剝離開(kāi)利用幀緩存機(jī)制錯(cuò)峰訪(fǎng)問(wèn)利用率提升到了82%。這個(gè)改善在用戶(hù)接口層面幾乎不用改代碼純粹靠業(yè)務(wù)調(diào)度就能獲得足以說(shuō)明系統(tǒng)調(diào)配的重要性。5.4 關(guān)于DDR4用戶(hù)接口的幾點(diǎn)避坑心得調(diào)試DDR4的過(guò)程其實(shí)很大一部分時(shí)間是在和“不確定性”做斗爭(zhēng)。有的問(wèn)題很隱蔽比如板子剛上電時(shí)偶爾初始化失敗熱機(jī)后一切正常有的問(wèn)題在單板表現(xiàn)非常好但量產(chǎn)若干板后開(kāi)始頻發(fā)。我的體會(huì)是不要一上來(lái)就懷疑用戶(hù)邏輯先確認(rèn)顆粒配置、電源、時(shí)鐘、SI這些“物理基礎(chǔ)”再回頭看代碼邏輯。見(jiàn)過(guò)不少同事在代碼里為了兼容某次偶發(fā)錯(cuò)誤加了一堆重試邏輯結(jié)果把DDR控制器時(shí)序打亂反而造成更多問(wèn)題。有時(shí)候把問(wèn)題往源頭追溯比如去檢查是不是DDR顆粒的VREF設(shè)置偏低比在用戶(hù)接口層打補(bǔ)丁要有效得多。另外要養(yǎng)成用腳本自動(dòng)化跑壓力測(cè)試的習(xí)慣。手點(diǎn)按鈕測(cè)十次不如寫(xiě)個(gè)腳本連續(xù)測(cè)一晚上。測(cè)試腳本最好記錄運(yùn)行時(shí)間、錯(cuò)誤地址、錯(cuò)誤數(shù)據(jù)出現(xiàn)一次錯(cuò)誤也能完整復(fù)現(xiàn)。這個(gè)習(xí)慣幫我節(jié)省了大量排查時(shí)間強(qiáng)烈推薦。6. 結(jié)語(yǔ)中的最后一件事到最后還是想掏心窩子說(shuō)一句DDR4 IP的用戶(hù)接口設(shè)計(jì)真正難的地方其實(shí)不在信號(hào)列表本身而在于你能否把底層存儲(chǔ)顆粒行為、控制器調(diào)度邏輯、上層訪(fǎng)問(wèn)模型完全串起來(lái)。多看看IP的時(shí)序圖多寫(xiě)幾輪仿真多跑幾次壓力測(cè)試經(jīng)驗(yàn)就是這么一點(diǎn)一點(diǎn)攢出來(lái)的。如果你手頭也正在為cal fail發(fā)愁或者被讀寫(xiě)效率問(wèn)題困擾不妨回頭想想我今天提到的這些細(xì)節(jié)復(fù)位有沒(méi)有做對(duì)、突發(fā)長(zhǎng)度有沒(méi)有對(duì)齊、wstrb有沒(méi)有置好、控制器切換開(kāi)銷(xiāo)有沒(méi)有計(jì)入帶寬估算。把這些看得見(jiàn)摸得著的點(diǎn)都檢查一遍很多疑難問(wèn)題往往就迎刃而解了。