動(dòng)ZYNQ 7020實(shí)現(xiàn)UDP通信:從RGMII到AXI DMA鏈路搭建)
簡(jiǎn)介ZYNQ 7020以太網(wǎng)UDP通信資源包面向FPGA開(kāi)發(fā)者和嵌入式網(wǎng)絡(luò)工程師聚焦ZYNQ SoC上UDP通信的FPGA驅(qū)動(dòng)與軟硬件協(xié)同設(shè)計(jì)系統(tǒng)梳理了從以太網(wǎng)幀收發(fā)包、UDP數(shù)據(jù)報(bào)封裝解析到ARM側(cè)協(xié)議棧處理的完整鏈路。壓縮包共610個(gè)文件、約49.5MB涵蓋Verilog/VHDL邏輯源碼、XDC管腳約束、DCP綜合網(wǎng)表、Tcl/Shell/DO仿真腳本、ARM側(cè)C驅(qū)動(dòng)、示例應(yīng)用及配置文檔其中HDL文件負(fù)責(zé)UDP引擎與MAC接口邏輯XDC定義時(shí)序約束腳本用于工程編譯和仿真C驅(qū)動(dòng)處理中斷與DMATXT/RST文檔說(shuō)明協(xié)議原理目錄按IP核、仿真、約束和驅(qū)動(dòng)分層組織。已有996人學(xué)習(xí)下載。資源對(duì)UDP校驗(yàn)和計(jì)算、GMII/RGMII接口適配、DMA環(huán)形緩沖管理、中斷觸發(fā)以及ARP/IP/UDP協(xié)議棧協(xié)作等關(guān)鍵技術(shù)均提供了可綜合的HDL實(shí)現(xiàn)和C驅(qū)動(dòng)代碼并帶有比特流文件與一鍵編譯腳本可結(jié)合Vivado工程直接運(yùn)行直觀驗(yàn)證ZYNQ平臺(tái)上的高速以太網(wǎng)通信也可作為二次開(kāi)發(fā)的底層驅(qū)動(dòng)參考。1. 用FPGA驅(qū)動(dòng)ZYNQ 7020的UDP先搞清楚哪條鏈路才叫驅(qū)動(dòng)在Zynq 7020上進(jìn)UDP通信新手最容易掉進(jìn)坑里配好Linux IP、socket一開(kāi)、數(shù)據(jù)發(fā)出去了就以為UDP通了。其實(shí)這條路全部在PS自帶的千兆以太網(wǎng)MACGEM和Linux協(xié)議棧里走PL側(cè)的FPGA邏輯根本沒(méi)參與。標(biāo)題里說(shuō)的“FPGA驅(qū)動(dòng)”是指由PL側(cè)的MAC接PHY芯片把以太幀通過(guò)AXI DMA搬到DDR再由設(shè)備驅(qū)動(dòng)交給系統(tǒng)——網(wǎng)絡(luò)接口從FPGA里長(zhǎng)出來(lái)CPU只在控制面干預(yù)。這個(gè)方案解決兩類典型問(wèn)題PS網(wǎng)口被業(yè)務(wù)占滿時(shí)開(kāi)第二路UDP或者FPGA內(nèi)部信號(hào)直接封裝成UDP幀繞開(kāi)CPU逐層拷貝。高速數(shù)據(jù)采集、FPGA把ADC數(shù)據(jù)打包上傳的場(chǎng)景尤為常用。適合的讀者是調(diào)過(guò)RGMII時(shí)序、能看懂DMA描述符的嵌入式工程師以及準(zhǔn)備讓自定義總線上送以太網(wǎng)的算法工程師。2. 從RGMII到AXI Stream搭建ZYNQ 7020的UDP物理鏈路2.1 為什么選RGMII引腳、電平和PHY選型如果從頭在PL側(cè)開(kāi)千兆以太網(wǎng)第一選擇就是RGMII。原因很樸素GMII接口的TX/RX各8位數(shù)據(jù)線加上時(shí)鐘、控制、MDIO整體超過(guò)30根信號(hào)Zynq 7020的PL Bank引腳本來(lái)就不富裕還要給DDR、LVDS圖像接口留位置。RGMII把收發(fā)各壓到4根數(shù)據(jù)線時(shí)鐘和數(shù)據(jù)都走DDR雙沿12根引腳就能完成一個(gè)千兆口。SGMII更省引腳但需要走PL的SerDes通道。7020上MGT Bank常常被PCIe、SATA或其它高速串口占用為一個(gè)輔助網(wǎng)口讓出SerDes資源并不劃算。下表是三種PL側(cè)千兆接口的常規(guī)對(duì)比接口數(shù)據(jù)線寬度時(shí)鐘方式引腳消耗7020 PL側(cè)使用場(chǎng)景GMII8位并行125MHz SDR約30根較少Bank資源寬裕且PCB允許大量走線時(shí)RGMII4位DDR125MHz DDR約12根AXI Ethernet IP常規(guī)選項(xiàng)第二網(wǎng)口首選SGMII串行1.25Gbps SerDes4根MGT Bank空閑時(shí)可用但常被PCIe搶走PHY選型跟著接口走88E1512、88E1518、RTL8211系列都支持RGMII到銅纜的轉(zhuǎn)換。選型重點(diǎn)有三個(gè)RGMII電平是否匹配PL Bank的VCCOZynq 7020的HR Bank常用2.5V供電MDIO地址引腳是否有硬件拉拔關(guān)系到設(shè)備樹里PHY地址是0x0還是0x10以及PHY的125MHz時(shí)鐘輸出是否要反送回FPGA當(dāng)作GTX參考時(shí)鐘這會(huì)直接改變XDC里時(shí)鐘約束的寫法。上電后第一步用MDIO讀PHY的標(biāo)識(shí)寄存器寄存器2和3確認(rèn)MDIO連接和PHY地址正確再進(jìn)時(shí)序調(diào)試。這一步能省下好幾個(gè)小時(shí)的“網(wǎng)口死活不link”排查時(shí)間。2.2 RGMII時(shí)序約束與RXC相移用Vivado的AXI Ethernet IP時(shí)RGMII引腳約束看著簡(jiǎn)單把端口名對(duì)上就行真正影響收包率的是時(shí)鐘與數(shù)據(jù)的相位關(guān)系。RGMII 2.0規(guī)范中PHY送出的RXC與RXD之間存在最大1納秒左右的Skew到MAC側(cè)時(shí)數(shù)據(jù)沿已經(jīng)不在時(shí)鐘采樣窗口中心。常見(jiàn)做法是讓FPGA內(nèi)部對(duì)RXC經(jīng)過(guò)IDELAY后再進(jìn)IDDR采樣同時(shí)用XDC里的set_input_delay約束數(shù)據(jù)相對(duì)時(shí)鐘的窗口set_property -dict {PACKAGE_PIN L16 IOSTANDARD LVCMOS25} [get_ports rgmii_rxd[0]] create_clock -period 8.000 -name rgmii_rxc_i [get_ports rgmii_rxc] set_input_delay -clock rgmii_rxc_i -max 1.000 [get_ports rgmii_rxd[*]]邏輯說(shuō)明第一行把引腳綁定到具體封裝位置并聲明IO電平第二行創(chuàng)建125MHz輸入時(shí)鐘對(duì)象8ns周期對(duì)應(yīng)千兆DDR第三行聲明RXD相對(duì)RXC的最大到達(dá)延遲為1ns。實(shí)際調(diào)試中大多數(shù)PHY要求RXD比RXC晚0.5到1ns到達(dá)但PCB走線長(zhǎng)度會(huì)改變這個(gè)窗口因此XDC里的具體數(shù)值要以PHY數(shù)據(jù)手冊(cè)和實(shí)際誤包率為準(zhǔn)。調(diào)這個(gè)延時(shí)時(shí)最有效的驗(yàn)證方式是發(fā)固定長(zhǎng)度幀同時(shí)觀察MAC側(cè)CRC錯(cuò)誤計(jì)數(shù)。需要注意CRC錯(cuò)誤計(jì)數(shù)上漲不一定表現(xiàn)為丟包而是表現(xiàn)為接收幀被MAC丟棄從用戶側(cè)看就是“偶爾少幾個(gè)UDP包”。PCB布局把同組RGMII信號(hào)等長(zhǎng)控制在50mil以內(nèi)能顯著降低這類調(diào)試成本。2.3 AXI Stream與以太網(wǎng)幀格式的映射AXI Ethernet接收側(cè)的AXI Stream輸出是完整以太網(wǎng)幀前導(dǎo)碼和SFD已被MAC剝離數(shù)據(jù)從目的MAC地址開(kāi)始到TLAST拉高表示幀結(jié)束。CRC在默認(rèn)配置下也被MAC移除不會(huì)出現(xiàn)在Stream里。搞清這一點(diǎn)很重要很多從裸PL邏輯接手的人會(huì)在DMA緩沖區(qū)里看到幀尾多出4字節(jié)那不是錯(cuò)誤而是TX_INCLUDE_FCS配置被打開(kāi)了。以太網(wǎng)幀格式對(duì)UDP解析的影響體現(xiàn)在三個(gè)固定偏移幀頭第12、13字節(jié)是EtherType0x0800代表IPv40x0806代表ARPIPv4頭從偏移14開(kāi)始標(biāo)準(zhǔn)長(zhǎng)度20字節(jié)UDP頭在IP頭之后源端口與目的端口的偏移由此計(jì)算。FPGA驅(qū)動(dòng)層面的數(shù)據(jù)通路本質(zhì)上就是把RGMII上串行到達(dá)的字節(jié)按這個(gè)布局寫入AXI Stream再由DMA搬到內(nèi)存。如果你在調(diào)試中看到PC端能抓到板子發(fā)出的ARP卻收不到UDP數(shù)據(jù)回頭查這一層的EtherType和IP頭長(zhǎng)度字段往往比抓協(xié)議棧更有價(jià)值。3. 在Vivado里搭建AXI Ethernet加AXI DMA的UDP收發(fā)鏈路3.1 帶PS還是純PL先定數(shù)據(jù)處理歸屬做UDP通信前需要先回答一個(gè)問(wèn)題要不要把協(xié)議棧放進(jìn)FPGA邏輯。純PL方案的思路是在PL里用RTL實(shí)現(xiàn)MAC、ARP、IP、UDP校驗(yàn)和所有包處理都在硬件里完成帶PS方案則讓PL只做MAC和數(shù)據(jù)搬運(yùn)IP/UDP協(xié)議棧交給ARM上的Linux。一線工程里帶PS方式更常見(jiàn)因?yàn)閆ynq的CPU本來(lái)就有富余算力Linux協(xié)議棧的穩(wěn)定性遠(yuǎn)超自研RTL網(wǎng)絡(luò)棧而且調(diào)試工具豐富。純PL適合極低延遲或要求零CPU介入的數(shù)據(jù)采集前端但ARP、ICMP、分片重組都要自己寫調(diào)試周期往往按周計(jì)算。從“FPGA驅(qū)動(dòng)”這個(gè)標(biāo)題看兩條路都說(shuō)得通。下面以帶PS方案為主線展開(kāi)因?yàn)锳XI Ethernet、AXI DMA、設(shè)備樹、Linux驅(qū)動(dòng)這一整套組合是Zynq平臺(tái)最成熟可靠的UDP落地方案踩坑最少。3.2 塊設(shè)計(jì)最小組合IP選型與關(guān)鍵配置在Vivado Block Design里一個(gè)最小可跑通UDP的工程包含以下IPAXI Ethernet選擇1000Mbps、RGMII、使能MDIOAXI DMAS2MM和MM2S兩個(gè)通道都打開(kāi)用于收發(fā)兩個(gè)方向AXI Interconnect把DMA的存儲(chǔ)映射接口連到PS的HP口Processing System 7啟用HP0、GP0、PL中斷AXI BRAM Controller可選只在不用DDR做DMA緩沖時(shí)使用創(chuàng)建AXI Ethernet并設(shè)置參數(shù)的TCL腳本示例如下create_bd_cell -type ip -vlnv xilinx.com:ip:axi_ethernet:7.2 axi_eth_0 set_property -dict [list \ CONFIG.PHY_TYPE {RGMII} \ CONFIG.RX_INCLUDE_FCS {false} \ CONFIG.TX_INCLUDE_FCS {false} \ CONFIG.ENABLE_MDIO {true}] [get_bd_cells axi_eth_0]參數(shù)說(shuō)明PHY_TYPE選RGMII對(duì)應(yīng)外部PHY芯片接口RX_INCLUDE_FCS和TX_INCLUDE_FCS都設(shè)為false讓MAC自動(dòng)剝離和追加CRC避免DMA緩沖區(qū)里出現(xiàn)多余字節(jié)ENABLE_MDIO開(kāi)啟管理通道驅(qū)動(dòng)才能通過(guò)MDIO讀取PHY狀態(tài)和協(xié)商結(jié)果。連線層面AXI Ethernet的s_axi配置口接GP口或HP口均可中斷接PS的PL中斷AXI DMA的S2MM從以太網(wǎng)接收AXI StreamMM2S向以太網(wǎng)發(fā)送流DMA的m_axi接AXI Interconnect后掛到HP0。有一個(gè)容易忽略的點(diǎn)是DMA中斷和Ethernet中斷要在Block Design里同時(shí)引出Linux驅(qū)動(dòng)通常同時(shí)依賴這兩個(gè)中斷源設(shè)備樹里也要對(duì)應(yīng)配兩個(gè)中斷號(hào)。3.3 DMA描述符與緩沖區(qū)對(duì)齊的硬約束AXI DMA在PS側(cè)看來(lái)就是一組描述符加數(shù)據(jù)緩沖區(qū)。描述符由驅(qū)動(dòng)維護(hù)數(shù)據(jù)緩沖區(qū)要求物理地址連續(xù)且基地址和長(zhǎng)度都滿足對(duì)齊要求。在裸機(jī)裸金屬程序中Xil_DCacheFlush()必須在CPU寫完數(shù)據(jù)后調(diào)用否則DMA讀到的可能是Cache里的舊數(shù)據(jù)Linux下用dma_alloc_coherent()分配緩沖區(qū)才能保證設(shè)備地址和CPU虛擬地址在同一個(gè)映射視圖里。UDP包進(jìn)入DMA緩沖后描述符的環(huán)回順序決定接收連續(xù)性。如果環(huán)緩沖區(qū)只配置一個(gè)描述符上一幀還沒(méi)被用戶程序取走下一幀就會(huì)覆蓋同一塊內(nèi)存表現(xiàn)為“UDP數(shù)據(jù)時(shí)而新時(shí)而舊”。常規(guī)設(shè)計(jì)至少配置4個(gè)獨(dú)立描述符并在驅(qū)動(dòng)里維護(hù)一個(gè)“生產(chǎn)者-消費(fèi)者”索引。調(diào)試時(shí)如果發(fā)現(xiàn)高流量下丟幀先看描述符數(shù)量而不是先去調(diào)RGMII延時(shí)這往往是緩沖區(qū)深度不足造成的。4. Linux側(cè)驅(qū)動(dòng)與UDP應(yīng)用讓FPGA網(wǎng)卡出現(xiàn)在eth14.1 設(shè)備樹與驅(qū)動(dòng)匹配AXI Ethernet在Linux內(nèi)核里有現(xiàn)成驅(qū)動(dòng)設(shè)備樹要做的是把硬件資源告訴驅(qū)動(dòng)。一個(gè)典型節(jié)點(diǎn)如下etherneta4010000 { compatible xlnx,axi-ethernet-1.00.a; reg 0x0 0xa4010000 0x0 0x40000; interrupts 0 57 4; local-mac-address [00 0a 35 00 01 00]; phy-handle phy0; mdio { #address-cells 1; #size-cells 0; phy0: ethernet-phy0 { reg 0; }; }; };字段說(shuō)明compatible決定內(nèi)核用哪個(gè)驅(qū)動(dòng)匹配reg必須是Vivado地址向?qū)Х峙浣o該IP的基地址要和Block Design里的地址一致interrupts里的57是PL中斷經(jīng)GIC映射后的SPI編號(hào)具體值以Vivado向?qū)Т蛴闇?zhǔn)phy-handle指向MDIO子節(jié)點(diǎn)而子節(jié)點(diǎn)中的reg填寫PHY芯片的MDIO地址。最常踩的坑是PHY實(shí)際地址與reg不一致驅(qū)動(dòng)初始化時(shí)找不到PHY直接導(dǎo)致link狀態(tài)永遠(yuǎn)為down。修改設(shè)備樹后重新編譯啟動(dòng)ifconfig -a能看到新網(wǎng)卡接口通常命名為eth1。如果設(shè)備樹沒(méi)生效先檢查/proc/device-tree里對(duì)應(yīng)節(jié)點(diǎn)是否存在再確認(rèn)內(nèi)核里AXI Ethernet驅(qū)動(dòng)是否編譯進(jìn)內(nèi)核。4.2 用戶態(tài)UDP應(yīng)用的兩條通道網(wǎng)卡注冊(cè)成功后最直接的方式是把它當(dāng)普通網(wǎng)卡用綁定IP后通過(guò)socket收發(fā)UDP。這種方式的優(yōu)點(diǎn)是完全復(fù)用Linux協(xié)議棧代碼和PC端沒(méi)有區(qū)別。另一種方式是用戶態(tài)直接讀取DMA緩沖區(qū)適合PL側(cè)硬件已經(jīng)完成了IP/UDP解析、只想拿凈荷的場(chǎng)景典型實(shí)現(xiàn)是用UIO框架int fd open(/dev/uio0, O_RDWR); void *buf mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); uint16_t ethertype (buf[12] 8) | buf[13]; if (ethertype 0x0800 buf[23] 17) { uint16_t dport (buf[36] 8) | buf[37]; printf(UDP dport%u\n, dport); }代碼里的偏移量對(duì)應(yīng)標(biāo)準(zhǔn)以太網(wǎng)幀buf[12]和buf[13]是EtherType0x0800表示IPv4buf[23]是IP頭中的協(xié)議字段17表示UDPbuf[36]和buf[37]是UDP目的端口。這個(gè)片段用于驗(yàn)證PL側(cè)DMA搬運(yùn)過(guò)來(lái)的數(shù)據(jù)排列是否符合預(yù)期特別是網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序的差異調(diào)RGMII時(shí)也經(jīng)常用它快速確認(rèn)鏈路。需要說(shuō)明的是UIO方式要求驅(qū)動(dòng)把DMA緩沖區(qū)通過(guò)mmap暴露給用戶態(tài)并保證中斷到達(dá)后用戶態(tài)能感知。相比socket方式UIO省去了兩次數(shù)據(jù)拷貝但把協(xié)議解析工作推給了應(yīng)用。如果FPGA里有UDP卸載引擎選UIO如果PL只做MAC直接用socket更省事。4.3 沒(méi)有ARP就沒(méi)有UDP排查還是要從協(xié)議??雌餖inux協(xié)議棧發(fā)送IP包前會(huì)先發(fā)ARP請(qǐng)求只有拿到目的IP對(duì)應(yīng)的MAC地址UDP數(shù)據(jù)幀才會(huì)真正發(fā)出。因此“網(wǎng)線插著、ifconfig正常、socket卻沒(méi)數(shù)據(jù)”時(shí)第一步查ARP而不是查UDP代碼本身。PC端用Wireshark抓包判斷依據(jù)如下表現(xiàn)象問(wèn)題所在PC抓不到板子發(fā)出的ARP請(qǐng)求RGMII TX方向、PHY link、MAC使能狀態(tài)ARP能看到但沒(méi)有回應(yīng)DMA接收描述符、中斷配置、設(shè)備樹phy-handleARP通了但UDP收發(fā)失敗應(yīng)用層端口綁定、防火墻、網(wǎng)絡(luò)調(diào)試助手設(shè)置第三條在Linux里很常見(jiàn)綁定端口時(shí)不指定IP或錯(cuò)誤指定IPUDP包發(fā)到了別的接口上或者PC防火墻攔截了來(lái)自FPGA網(wǎng)卡的包。把防火墻關(guān)掉再測(cè)通常能立刻區(qū)分是驅(qū)動(dòng)問(wèn)題還是應(yīng)用問(wèn)題。5. 用MDIO環(huán)回和Wireshark確認(rèn)FPGA側(cè)包處理正確5.1 MDIO數(shù)字環(huán)回是最快的問(wèn)題分割手段UDP發(fā)不出去時(shí)第一步不是換網(wǎng)線而是讓PHY進(jìn)入數(shù)字環(huán)回模式。在這種模式下PHY在芯片內(nèi)部把發(fā)送端的RGMII數(shù)據(jù)直接送回接收端不經(jīng)過(guò)網(wǎng)線物理鏈路。FPGA發(fā)出一個(gè)UDP包如果接收方向能看到同樣的幀就說(shuō)明FPGA側(cè)的TX/RX數(shù)據(jù)通路、AXI DMA和驅(qū)動(dòng)邏輯都成立問(wèn)題被隔離到PHY與線纜側(cè)反之環(huán)回不通則說(shuō)明問(wèn)題在FPGA內(nèi)部鏈路。88E1518等PHY的數(shù)字環(huán)回控制通常位于寄存器0的bit14通過(guò)MDIO寫入即可開(kāi)啟mdio-tool eth1 phy_write 0x0 0x14 0x4000參數(shù)說(shuō)明第一條0x0是MDIO地址第二條0x14是寄存器地址0x4000對(duì)應(yīng)bit14置1。不同PHY芯片寄存器定義不同這里只是常用示例。開(kāi)啟環(huán)回后要關(guān)閉自適應(yīng)并固定1000Mbps全雙工否則PHY在環(huán)回條件下無(wú)法完成正常協(xié)商現(xiàn)象反而更奇怪。5.2 Wireshark對(duì)拍時(shí)看幀格式不只是看UDP載荷PC端抓包時(shí)過(guò)濾條件直接鎖定FPGA的MAC和UDP端口eth.addr00:0a:35:00:01:00 udp.port5000抓到的包要檢查三處EtherType是否為0x0800確認(rèn)IPv4幀被正確遞交IPv4頭校驗(yàn)和是否錯(cuò)誤錯(cuò)誤說(shuō)明FPGA側(cè)做了IP層處理但實(shí)現(xiàn)有缺陷UDP校驗(yàn)和是否為0。不少自研PL協(xié)議棧為省邏輯把UDP校驗(yàn)和寫成0IPv4允許這個(gè)做法但部分硬交換芯片會(huì)直接丟棄零校驗(yàn)和UDP幀。因此商用驗(yàn)證時(shí)最好讓PL把UDP校驗(yàn)和計(jì)算出來(lái)別省這個(gè)邏輯。5.3 收尾技巧用兩個(gè)計(jì)數(shù)定位丟包點(diǎn)網(wǎng)絡(luò)基本跑通后再做一輪壓測(cè)同時(shí)盯兩個(gè)數(shù)值FPGA內(nèi)部AXI Stream的TLAST脈沖計(jì)數(shù)以及PC端Wireshark收到的幀計(jì)數(shù)。兩個(gè)數(shù)字應(yīng)接近1:1。如果TLAST很多但Wireshark收得少問(wèn)題在RGMII發(fā)送側(cè)或PHY調(diào)IDELAY和發(fā)送時(shí)序如果Wireshark收得多但板子側(cè)面TLAST少問(wèn)題在接收方向。Zynq 7020的UDP鏈路每一層都有清晰的特征順著這個(gè)兩計(jì)數(shù)法能比盲改代碼更快收窄問(wèn)題。下一回遇到UDP丟包先跑一次PHY環(huán)回確認(rèn)內(nèi)部基線再動(dòng)RGMII延時(shí)省下的是按天計(jì)算的調(diào)試時(shí)間。本文還有配套的精品資源點(diǎn)擊獲取