差不多整整三年前,即2020年5月12日時(shí),我們分享了一篇有關(guān)100G開(kāi)源網(wǎng)卡的文章《業(yè)界第一個(gè)真正意義上開(kāi)源100 Gbps NIC Corundum介紹》。隨后又陸陸續(xù)續(xù)分享了多次有關(guān)Corundum上板測(cè)試及仿真環(huán)境搭建的文章:《揭秘:普通電腦換上Xilinx Alveo U50 100G網(wǎng)卡傳文件會(huì)有多快?》,《開(kāi)源100 Gbps NIC Corundum環(huán)境搭建介紹(一)》和《開(kāi)源100 Gbps NIC Corundum環(huán)境搭建介紹(二)仿真及工程恢復(fù)》。三年來(lái),學(xué)術(shù)界發(fā)表了多篇依據(jù)該開(kāi)源架構(gòu)的論文,工業(yè)界也在該開(kāi)源架構(gòu)的基礎(chǔ)上開(kāi)發(fā)多種產(chǎn)品。我們?cè)谑褂迷撻_(kāi)源項(xiàng)目的過(guò)程中,發(fā)現(xiàn)了該開(kāi)源工程中也存在不少的問(wèn)題,本著開(kāi)源的精神,本文就把發(fā)現(xiàn)Corundum中部分問(wèn)題的過(guò)程以及問(wèn)題解決思路分享給大家,希望能夠給大家?guī)?lái)幫助。本文作者是李釗同學(xué),前面提到本公眾號(hào)有關(guān)Corundum環(huán)境搭建文章的作者也是李釗。
Corundum是一個(gè)基于FPGA的開(kāi)源NIC原型平臺(tái),用于高達(dá)100Gbps及更高的網(wǎng)絡(luò)接口開(kāi)發(fā)。Corundum平臺(tái)包括一些用于實(shí)現(xiàn)實(shí)時(shí),高線速操作的核心功能,包括:高性能數(shù)據(jù)路徑,10G/25G/100G以太網(wǎng)MAC,PCIe gen3,自定義PCIe DMA引擎以及本機(jī)高精確的IEEE 1588 PTP時(shí)間戳。
背景
我們基于Corundum這個(gè)靈活且強(qiáng)大的平臺(tái)開(kāi)發(fā)了一款網(wǎng)絡(luò)加速器。如圖所示,我們的設(shè)計(jì)(位于APP黃色部分)需要和原生數(shù)據(jù)流量共用一套DMA IF和主機(jī)HOST進(jìn)行通信。紫色數(shù)據(jù)路徑為用于DMA操作的自定義分段內(nèi)存接口,連接著Corundum的高性能DMA引擎和分段存儲(chǔ)器。
在我們的設(shè)計(jì)中,需要頻繁的進(jìn)行DMA讀寫(xiě)操作。DMA操作大致有兩種類型,一種是小數(shù)據(jù)包的DMA,長(zhǎng)度在16B-64B之間,內(nèi)容為硬件和主機(jī)通信的描述符,存放于Desc fetch和Cpl write模塊內(nèi)的描述符分段存儲(chǔ)器;一種是大數(shù)據(jù)包的DMA,長(zhǎng)度大于1kB,內(nèi)容為傳輸數(shù)據(jù),存放于APP下的數(shù)據(jù)分段存儲(chǔ)器。
我們的設(shè)計(jì)通過(guò)了大量仿真,確認(rèn)開(kāi)發(fā)設(shè)計(jì)功能無(wú)誤,隨后開(kāi)始上板測(cè)試。
問(wèn)題
我們的PCIe鏈路速率gen3 * 8,PCIe理論帶寬為64G。在上板時(shí)出現(xiàn)了很奇怪的現(xiàn)象。
每次測(cè)試發(fā)送小數(shù)量級(jí)的請(qǐng)求時(shí)(<10000次,每次代表一次小數(shù)據(jù)包的DAM和一次大數(shù)據(jù)包的DMA),估算PCIe帶寬在25G-35G左右。這種測(cè)試情況下不會(huì)發(fā)生錯(cuò)誤,測(cè)試順利通過(guò)。
當(dāng)測(cè)試請(qǐng)求數(shù)量進(jìn)一步增大時(shí)(>10萬(wàn)次),預(yù)估PCIe帶寬在35G~40G。此時(shí)測(cè)試就會(huì)發(fā)生問(wèn)題,整個(gè)系統(tǒng)癱瘓。
定位
進(jìn)一步定位問(wèn)題,主機(jī)和FPGA還可以通過(guò)MMIO進(jìn)行寄存器訪問(wèn),但是DMA已經(jīng)完全卡死。
在DMA IF下設(shè)置計(jì)數(shù)器用于統(tǒng)計(jì)DMA Read請(qǐng)求的個(gè)數(shù)和響應(yīng):
if(s_axis_read_desc_valid&&s_axis_read_desc_ready)begin dma_read_req_cnt<=?dma_read_req_cnt?+?1'b1; end if(?m_axis_read_desc_status_valid?)?begin ????dma_read_status_cnt?<=?dma_read_status_cnt?+?1'b1; end
上板debug信號(hào),每次測(cè)試癱瘓后請(qǐng)求的個(gè)數(shù)總是大于響應(yīng)的個(gè)數(shù)。
在Corundum中,DMA引擎是這樣工作的:
DMA引擎接收來(lái)自用戶的DMA Read請(qǐng)求,然后將DMA Read請(qǐng)求轉(zhuǎn)換為PCIe Non-Posted 類型的mem read請(qǐng)求TLP包,然后等待mem read的Cpld返回報(bào)文,解析Cpld報(bào)文,將數(shù)據(jù)通過(guò)自定義分段內(nèi)存接口存儲(chǔ)至各級(jí)模塊下的分段存儲(chǔ)器中,存儲(chǔ)完成后將回復(fù)DMA Read響應(yīng)至用戶。
結(jié)合對(duì)DMA 引擎的理解,可以推測(cè)有以下幾個(gè)可能導(dǎo)致響應(yīng)個(gè)數(shù)不夠
PCIe IP錯(cuò)誤導(dǎo)致rd tlp或cpld丟失;
DMA引擎因返回cpld攜帶錯(cuò)誤而丟棄cpld;
DMA引擎因PCIe流控發(fā)生不可預(yù)知的錯(cuò)誤,隨機(jī)丟棄數(shù)據(jù)包;
其他原因;
分析原因:
PCIe IP為Xilinx UltraScale+ Integrated Block(PCIE4C) for PCI Express,成熟的商用IP經(jīng)過(guò)了大規(guī)模驗(yàn)證和使用一般不會(huì)出現(xiàn)問(wèn)題,可以暫且排除因素1。
抓取相關(guān)的錯(cuò)誤信號(hào)(rx_cpl_tlp_error、status_error_cor、status_error_uncor),結(jié)果表明PCIe并未發(fā)生可糾正/不可糾正的錯(cuò)誤類型,表示通信雙方鏈路正常,并沒(méi)有因?yàn)殄e(cuò)誤而丟棄報(bào)文導(dǎo)致響應(yīng)的數(shù)量不夠,可以排除因素2。
首先Corundum是一款100Gbps速率的網(wǎng)卡設(shè)計(jì),經(jīng)過(guò)了大量驗(yàn)證和上板測(cè)試;其次測(cè)試的PCIe理論帶寬為64Gbps。而此時(shí)的測(cè)試帶寬35G~40G遠(yuǎn)遠(yuǎn)不足s設(shè)計(jì)帶寬或是鏈路帶寬上限,應(yīng)該不會(huì)觸發(fā)流控機(jī)制。這里因素三也需要被排除。
求助Alex Forencich
最了解自己設(shè)計(jì)的一定是作者本人。我隨后在Zulip上求助了作者,希望他可以幫我想一想導(dǎo)致此問(wèn)題其他有可能的因素。
Alex很熱心,對(duì)每個(gè)問(wèn)題都會(huì)做出回復(fù)。
他表示這種問(wèn)題很難定位,需要精確的定位“案發(fā)現(xiàn)場(chǎng)”才有可能定位問(wèn)題。
我可以確保每次DMA操作的地址和長(zhǎng)度都是合理的,并且由于mem read tlp為Non-Posted類型,因此需要等待一定的PCIe鏈路時(shí)延才能得到返回報(bào)文。這兩項(xiàng)特質(zhì)決定了很難定位至發(fā)生錯(cuò)誤的DMA操作,無(wú)法恢復(fù)到錯(cuò)誤發(fā)生的那一時(shí)刻。
他告訴了我一種定位的手段:
設(shè)置足夠深的ILA緩沖,用計(jì)數(shù)器擴(kuò)展以檢測(cè)掛起:基本上,在每個(gè)周期遞增,但如果計(jì)數(shù)一致或有響應(yīng),則重置;然后將該計(jì)數(shù)器輸入ILA;并觸發(fā)各種計(jì)數(shù)器值。注意:觸發(fā)器位置靠近緩沖器末端。
基于此思路,我做出了以下設(shè)置:
設(shè)置ILA深度至8192,并且調(diào)整每次DMA Read的長(zhǎng)度為4K,確保能夠在ILA深度內(nèi)抓到完整的DMA操作。
dma_read_cycle_cnt作為需要觸發(fā)的計(jì)數(shù)/時(shí)器,沒(méi)收到一個(gè)DMA Read請(qǐng)求開(kāi)始隨時(shí)鐘周期自增,收到DMA Read請(qǐng)求響應(yīng)清零;dma_read_cycle_max_cnt用來(lái)記錄正常情況下Non-Posted完成的最大計(jì)數(shù)值。將觸發(fā)條件設(shè)置為dma_read_cycle_cnt > dma_read_cycle_max_cnt,這樣就可以觸發(fā)到模塊出錯(cuò)的時(shí)刻。調(diào)整dma_read_cycle_max_cnt觸發(fā)條件,逐步逼近出錯(cuò)時(shí)刻。
if(m_axis_read_desc_status_valid)begin dma_read_cycle_cnt<=?32'b0; end?else?if(s_axis_read_desc_valid?&&?s_axis_read_desc_ready)begin ????dma_read_cycle_cnt?<=?32'b1; end?else?if(dma_read_cycle_cnt?>=1'b1)begin dma_read_cycle_cnt<=?dma_read_cycle_cnt?+?1'b1; end?else?begin ????dma_read_cycle_cnt?<=?dma_read_cycle_cnt; end if(m_axis_read_desc_status_valid?&&?(dma_read_cycle_cnt[23:0]?>dma_read_cycle_max_cnt))begin dma_read_cycle_max_cnt<=?dma_read_cycle_cnt; end?else?begin ????dma_read_cycle_max_cnt?<=?dma_read_cycle_max_cnt; end
同時(shí),在ILA內(nèi)抓取一些相關(guān)的重要信號(hào):基本的握手信號(hào)和相關(guān)流控信號(hào)。
外加統(tǒng)計(jì)read tlp的請(qǐng)求數(shù)據(jù)長(zhǎng)度累計(jì)和返回cpld的數(shù)據(jù)長(zhǎng)度累計(jì)。
定位“案發(fā)現(xiàn)場(chǎng)”
按照觸發(fā)設(shè)置,終于在ILA中捕獲到“案發(fā)現(xiàn)場(chǎng)”。
某個(gè)時(shí)刻,握手信號(hào)rx_cpl_tlp_ready不再置1,但rx_cpl_tlp_valid還存在。表明DMA引擎出現(xiàn)了問(wèn)題,無(wú)法再接收返回的cpld報(bào)文。
也就是從此刻開(kāi)始,DMA Read請(qǐng)求的個(gè)數(shù)在一直增加,但是DMA Read響應(yīng)的個(gè)數(shù)卻不再增長(zhǎng)。
定位到了出錯(cuò)時(shí)刻,此時(shí)便可以對(duì)模塊內(nèi)的重點(diǎn)信號(hào)展開(kāi)分析。
原因分析
前文已經(jīng)去除掉了面向HOST的種種因素,因此最有可能出問(wèn)題的地方只能是面向用戶的用于DMA操作的自定義分段內(nèi)存接口。
這里的分段內(nèi)存接口是Corundum獨(dú)特的體系結(jié)構(gòu)的一種,對(duì)于PCIe上的高性能DMA,Corundum使用自定義分段存儲(chǔ)器接口。該接口被分成最大512位的段,并且整體寬度是PCIe硬IP內(nèi)核的AXI流接口寬度的兩倍。例如,將PCIe Gen 3 x16與PCIe硬核中的512位AXI流接口一起使用的設(shè)計(jì)將使用1024位分段接口,該接口分成2個(gè)段,每個(gè)段512位。與使用單個(gè)AXI接口相比,該接口提供了改進(jìn)的“阻抗匹配”,從而消除了DMA引擎中的對(duì)齊和互連邏輯中的仲裁,從而消除了背壓,從而提高了PCIe鏈路利用率。具體地說(shuō),該接口保證DMA接口可以在每個(gè)時(shí)鐘周期執(zhí)行全寬度,未對(duì)齊的讀取或?qū)懭搿4送猓褂煤?jiǎn)單的雙端口RAM(專用于在單個(gè)方向上移動(dòng)的流量)消除了讀寫(xiě)路徑之間的爭(zhēng)用。
在自定義分段內(nèi)存接口中,使用ram_wr_cmd_sel路由和復(fù)用。基于單獨(dú)的選擇信號(hào)而不是通過(guò)地址解碼進(jìn)行路由的。此功能消除了分配地址的需要,并允許使用可參數(shù)化的互連組件,這些組件以最少的配置適當(dāng)?shù)芈酚刹僮鳌?/p>
outputwire[RAM_SEG_COUNT*RAM_SEL_WIDTH-1:0]ram_wr_cmd_sel, outputwire[RAM_SEG_COUNT*RAM_SEG_BE_WIDTH-1:0]ram_wr_cmd_be, outputwire[RAM_SEG_COUNT*RAM_SEG_ADDR_WIDTH-1:0]ram_wr_cmd_addr, outputwire[RAM_SEG_COUNT*RAM_SEG_DATA_WIDTH-1:0]ram_wr_cmd_data, outputwire[RAM_SEG_COUNT-1:0]ram_wr_cmd_valid, inputwire[RAM_SEG_COUNT-1:0]ram_wr_cmd_ready, inputwire[RAM_SEG_COUNT-1:0]ram_wr_done,
DMA Read引擎使用分段內(nèi)存的寫(xiě)入接口,將獲取的cpld數(shù)據(jù)寫(xiě)入分段存儲(chǔ)器中。使用ram_wr_cmd_sel決定寫(xiě)入哪個(gè)分段存儲(chǔ)器,使用ram_wr_cmd_addr決定寫(xiě)入分段存儲(chǔ)器的地址,使用ram_wr_done判斷寫(xiě)入操作完成。
同樣為分段內(nèi)存接口設(shè)置計(jì)數(shù)器,用于統(tǒng)計(jì)分段內(nèi)存接口的寫(xiě)入和寫(xiě)入完成操作。
if(ram_wr_cmd_valid[0]&&ram_wr_cmd_ready[0])begin dpram_wr_cmd_cnt0<=?dpram_wr_cmd_cnt0?+?1'b1; end if(ram_wr_cmd_valid[1]?&&?ram_wr_cmd_ready[1])begin ????dpram_wr_cmd_cnt1?<=?dpram_wr_cmd_cnt1?+?1'b1; end??? if(ram_wr_done[0])begin ????dpram_wr_done_cnt0?<=?dpram_wr_done_cnt0?+?1'b1; end if(ram_wr_done[1])begin ????dpram_wr_done_cnt1?<=?dpram_wr_done_cnt1?+?1'b1; end???
問(wèn)題就出現(xiàn)在這里:
在“案發(fā)時(shí)刻”之后,分段內(nèi)存接口處不再有寫(xiě)入完成信號(hào)ram_wr_done??梢宰C明問(wèn)題就出在這里,不再有內(nèi)存寫(xiě)入完成信號(hào),導(dǎo)致DMA IF遲遲無(wú)法確認(rèn)數(shù)據(jù)操作完成,進(jìn)而導(dǎo)致無(wú)法接收新的Cpld報(bào)文數(shù)據(jù)(防止數(shù)據(jù)覆蓋)。
進(jìn)一步分析“案發(fā)時(shí)刻”之前分段內(nèi)存接口的所有波形,終于在某個(gè)時(shí)刻找到了異樣。在此時(shí)刻,分段存儲(chǔ)器的地址不再是線性增加,發(fā)生了跳變,執(zhí)行完0地址的寫(xiě)入之后再恢復(fù)正常。
與DMA Read操作關(guān)聯(lián)起來(lái),并且充分考慮到Non-Posted操作的PCIe鏈路時(shí)延,從“案發(fā)時(shí)刻”之前的13次DMA Read刪選出出錯(cuò)的那一次DMA Read操作。
首先進(jìn)行了一個(gè)4kB的大DMA Read操作;
隨后便是一個(gè)64B的小DMA Read操作:
所以按理來(lái)說(shuō),分段內(nèi)存應(yīng)該先執(zhí)行完4kB的數(shù)據(jù)寫(xiě)入操作之后,再進(jìn)行64B的數(shù)據(jù)寫(xiě)入操作。
但抓取到的波形卻顯示數(shù)據(jù)寫(xiě)入操作地址發(fā)生了跳變,0x5fc -> 0x000 -> 0x5fd。
而這正是觸發(fā)測(cè)試錯(cuò)誤的條件:兩次mem rd tlp的cpld碰巧發(fā)生了交織,或者說(shuō)發(fā)生了亂序。小數(shù)據(jù)包的cpld超越了部分大數(shù)據(jù)包的cpld,先一步通過(guò)PCIe鏈路傳送至硬件。這種完成報(bào)文之間的亂序恰巧是PCIe協(xié)議所允許的,用于保證生產(chǎn)者/消費(fèi)者模型的正確運(yùn)轉(zhuǎn),防止死鎖的發(fā)生。
如果完成報(bào)文與之前的完成報(bào)文的Transaction ID不同,該報(bào)文可以超越之前的完成報(bào)文;如果相同,則不能超越。這里恰巧是兩個(gè)連續(xù)的不同DMA Read操作,Transaction ID的tag必不相同,所以亂序是會(huì)發(fā)生的。
在仿真中恢復(fù)案發(fā)現(xiàn)場(chǎng)
在仿真中,將“案發(fā)時(shí)刻”前后的波形文件csv導(dǎo)入仿真環(huán)境,或者直接使用force-release強(qiáng)制激勵(lì)即可恢復(fù)案發(fā)現(xiàn)場(chǎng)。
通過(guò)在仿真中檢查,很快便定位到是最近一級(jí)的dma_mux模塊出現(xiàn)了問(wèn)題,它會(huì)在這種情況下丟棄1-2個(gè)ram_wr_done。這有可能是每個(gè)分段存儲(chǔ)器的寫(xiě)入路徑時(shí)延不同,導(dǎo)致done信號(hào)同時(shí)到達(dá)mux模塊或者是mux模塊因?yàn)楸徽加枚鵁o(wú)法處理某一個(gè)分段存儲(chǔ)器的寫(xiě)入完成,導(dǎo)致done信號(hào)丟失。
修改BUG
隨后詢問(wèn)了Alex我的想法是否正確:
他表示編寫(xiě)mux時(shí)使用fifo和計(jì)數(shù)器來(lái)捕獲所有的done脈沖信號(hào),并按正確的順序轉(zhuǎn)發(fā),但似乎存在一個(gè)bug。
隨后也證明確實(shí)是dma_ram_demux_wr這個(gè)模塊存在問(wèn)題:
Alex隨后進(jìn)行了BUG修改。對(duì)輸入dma_ram_demux_wr的完成的done信號(hào)加入雙向確認(rèn)機(jī)制,確保不會(huì)遺漏任何done信號(hào)。
將修改完的代碼進(jìn)行了仿真測(cè)試,隨后也通過(guò)了FPGA上板測(cè)試。
總結(jié)
錯(cuò)誤的觸發(fā)條件:兩次mem rd tlp的cpld發(fā)生亂序。小數(shù)據(jù)包的cpld超越了部分大數(shù)據(jù)包的cpld,先一步通過(guò)PCIe鏈路傳送至硬件。
錯(cuò)誤原因:dma_ram_demux_wr因?yàn)閞am_wr_done輸入擁塞而丟失了分段內(nèi)存接口的寫(xiě)入完成信號(hào)ram_wr_done。
進(jìn)一步分析:為什么Corundum跑到100G帶寬下都不會(huì)出現(xiàn)此問(wèn)題?
Corundum下DMA的自定義分段內(nèi)存接口拓?fù)淙鐖D所示:
在Corundum的架構(gòu)下,DMA IF 到所有分段存儲(chǔ)器的寫(xiě)入路徑延遲是一致的,因此即使發(fā)生了cpld亂序,dma_ram_demux_wr也不會(huì)因?yàn)閞am_wr_done輸入擁塞而丟失done個(gè)數(shù)。
在我們的設(shè)計(jì)中,在APP下仍會(huì)進(jìn)行若干級(jí)的DAM MUX將DMA引擎進(jìn)一步復(fù)用。
這樣就會(huì)導(dǎo)致存在兩個(gè)分段存儲(chǔ)器的寫(xiě)入路徑延遲不一致,進(jìn)一步導(dǎo)致在dma_ram_demux_wr寫(xiě)入完成接口處發(fā)生爭(zhēng)用導(dǎo)致ram_wr_done丟失。
補(bǔ)充:修改完此BUG之后,每級(jí)dma_if_mux會(huì)引入一拍額外的時(shí)延(用于雙向確認(rèn)),因此會(huì)對(duì)DMA引擎的性能造成一定影響。為消除此影響,可以將dma_if_pcie_rd模塊下的參數(shù)STATUS_FIFO_ADDR_WIDTH和OUTPUT_FIFO_ADDR_WIDTH由5擴(kuò)大為6,使用更多資源來(lái)抵消額外的時(shí)延影響。
審核編輯 :李倩
-
FPGA
+關(guān)注
關(guān)注
1629文章
21738瀏覽量
603461 -
存儲(chǔ)器
+關(guān)注
關(guān)注
38文章
7492瀏覽量
163853 -
加速器
+關(guān)注
關(guān)注
2文章
799瀏覽量
37876
原文標(biāo)題:【干貨】尋找開(kāi)源100G NIC Corundum中的隱藏BUG
文章出處:【微信號(hào):gh_cb8502189068,微信公眾號(hào):網(wǎng)絡(luò)交換FPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論