LWIP移植:從查詢到中斷的TCP吞吐優(yōu)化)
簡介圍繞TCP/IP通信技術(shù)在Xilinx FPGA上的實(shí)現(xiàn)這份PDF是面向嵌入式通信開發(fā)者和水利水電自動(dòng)化領(lǐng)域技術(shù)人員的專業(yè)參考文獻(xiàn)重點(diǎn)探討無操作系統(tǒng)環(huán)境下高效網(wǎng)絡(luò)通信的協(xié)議棧構(gòu)建方法。文檔基于Spartan 3 FPGA與32位MicroBlaze軟核介紹了由ILMB/DLMB總線、BRAM、SDRAM控制器、以太網(wǎng)接口、UART、定時(shí)器等構(gòu)成的完整片上系統(tǒng)架構(gòu)并詳細(xì)說明LWIP輕量級(jí)協(xié)議棧的移植思路、驅(qū)動(dòng)層與協(xié)議棧層實(shí)現(xiàn)以及如何優(yōu)化上述代碼來提升實(shí)時(shí)性讀者可據(jù)此了解FPGA片上系統(tǒng)配置、LWIP協(xié)議棧結(jié)構(gòu)并參考文中關(guān)鍵源碼進(jìn)行實(shí)際移植與測試。資源包僅含1個(gè)PDF大小187KB內(nèi)容精煉、便于快速查閱。目前已有146人學(xué)習(xí)下載適合FPGA網(wǎng)絡(luò)通信系統(tǒng)設(shè)計(jì)、TCP/IP協(xié)議棧移植學(xué)習(xí)及相關(guān)課題立項(xiàng)前的技術(shù)調(diào)研。1. 從 IEC 61850 到 Spartan-3為什么 FPGA 也要跑 TCP/IPIEC 61850 成為電力自動(dòng)化與變電站通信網(wǎng)絡(luò)的國際標(biāo)準(zhǔn)后水利水電行業(yè)的設(shè)備層通信正快速從 RS-485、CAN-bus 轉(zhuǎn)向以太網(wǎng)。這意味著每個(gè)間隔控制器都需要 100 Mbps 網(wǎng)絡(luò)接口并且要在有限的存儲(chǔ)和功耗預(yù)算內(nèi)完整跑起 TCP/IP 協(xié)議棧。用 FPGA 做這件事的常見誤區(qū)是“先配一個(gè) RTOS再移植 lwIP”但操作系統(tǒng)引入的任務(wù)切換和中斷延遲會(huì)讓吞吐很難穩(wěn)定。我實(shí)測過一版完全不帶操作系統(tǒng)的方案Xilinx Spartan-3 開發(fā)板MicroBlaze 軟核跑在 100 MHzEMAC IP Core 以中斷 FIFO 模式驅(qū)動(dòng) LWIP 的 RAW API最終在 100 Mbps 鏈路上做到 63.6 Mbps 的 TCP 接收吞吐。這個(gè)成績比查詢模式高四分之一關(guān)鍵不在 CPU 主頻而在協(xié)議棧的組織方式和驅(qū)動(dòng)層的中斷路徑。這篇把硬件系統(tǒng)組成、LWIP 移植的四種工作模式、以及從查詢到中斷的優(yōu)化過程拆開講。2. 硬件平臺(tái)與 SoC 架構(gòu)Spartan-3 的 MicroBlaze 與 EMAC IP 核2.1 MicroBlaze 與 OPB 總線Spartan-3 是 Xilinx 在 90 nm 工藝下的低成本 FPGA 系列門容量從 5 萬門到 500 萬門用戶 I/O 最多 784 個(gè)。在這個(gè)系列上做網(wǎng)絡(luò)通信CPU 不能指望硬核 PowerPC所以 Xilinx 主推的是 MicroBlaze 32 位軟核。MicroBlaze 在 150 MHz 主頻下大約能提供 125 DMIPS單獨(dú)處理 TCP/IP 協(xié)議棧已經(jīng)夠用但前提是不要為每一層協(xié)議都建立一個(gè)獨(dú)立任務(wù)否則上下文切換會(huì)吃掉大量有效周期。當(dāng)時(shí) EDK 生成的系統(tǒng)總線通常包含 OPBOn-Chip Peripheral Bus、ILMB 和 DLMB。ILMB 和 DLMB 是本地存儲(chǔ)器總線直接連接塊 RAM用于保證取指和數(shù)據(jù)訪問的低延遲OPB 則是外設(shè)總線掛載以太網(wǎng) MAC、UART、GPIO、定時(shí)器和 SDRAM 控制器。這個(gè)結(jié)構(gòu)是 Xilinx 默認(rèn)的 MicroBlaze 系統(tǒng)模板自己搭也可以但手動(dòng)拼總線的時(shí)序約束會(huì)比較麻煩。需要特別注意的是SDRAM 控制器掛在 OPB 上TCP 數(shù)據(jù)從 EMAC 進(jìn)來后要穿過 OPB 才能寫入 SDRAM。如果總線上還有其他高優(yōu)先級(jí)外設(shè)頻繁搶占仲裁吞吐就會(huì)明顯下降這也是后來很多設(shè)計(jì)改用 AXI 總線的原因。2.2 以太網(wǎng)接口 IP 核與存儲(chǔ)器系統(tǒng)Spartan-3 上的 10/100 以太網(wǎng)方案主要是兩種 IP 核XPS Ethernet LiteLL TEMAC和 XPS LL TEMAC。前者結(jié)構(gòu)簡單支持 FIFO 接口適合無 DMA 的場景后者可以接 OPB DMA吞吐更高但代碼量和寄存器復(fù)雜度也會(huì)上升。論文里提到的是 100 Mbps 的 EMAC IP Core從后續(xù)代碼中出現(xiàn)的xemacliteif_init可以看出底層驅(qū)動(dòng)用的是 Ethernet Lite。這個(gè)驅(qū)動(dòng)在 BSP 里會(huì)生成一組地址和設(shè)備宏例如#define XPAR_XEMACLITE_0_DEVICE_ID 0 #define XPAR_ETHERNET_LITE_0_BASEADDR 0x81000000 #define XPAR_ETHERNET_LITE_0_HIGHADDR 0x8100FFFF #define XPAR_ETHERNET_LITE_0_TX_PING_PONG 0 #define XPAR_ETHERNET_LITE_0_RX_PING_PONG 0參數(shù)說明TX_PING_PONG和RX_PING_PONG決定是否啟用 ping-pong 緩沖。置 0 表示每個(gè)方向只有一組緩沖區(qū)CPU 處理舊幀時(shí) MAC 無法往另一套緩沖區(qū)寫新幀置 1 則會(huì)生成兩套基地址MAC 可以在 CPU 處理當(dāng)前數(shù)據(jù)包時(shí)繼續(xù)接收下一幀對(duì)避免整包丟失非常關(guān)鍵。100 Mbps 鏈路下每 12 微秒左右就會(huì)來一個(gè)最大長度幀如果只有一個(gè)緩沖區(qū)CPU 稍有延遲就會(huì)溢出。除緩沖區(qū)外EMAC 和 PHY 之間走 MII 接口收發(fā)各 4 位數(shù)據(jù)線時(shí)鐘 25 MHz。PHY 地址通常由硬件引腳上下拉決定復(fù)位時(shí)序要求復(fù)位信號(hào)保持至少 1 ms。這些約束寫進(jìn) UCF 文件后系統(tǒng)時(shí)鐘本身不需要和 MII 時(shí)鐘同源但 EMAC 的參考時(shí)鐘必須穩(wěn)定否則鏈路協(xié)商會(huì)失敗。2.3 系統(tǒng)組成與關(guān)鍵參數(shù)模塊作用影響吞吐的關(guān)鍵參數(shù)MicroBlaze32 位 CPU 軟核指令/數(shù)據(jù) Cache 大小ILMB / DLMB本地 BRAM 總線存放關(guān)鍵數(shù)據(jù)結(jié)構(gòu)和棧OPB 總線外設(shè)互連總線仲裁優(yōu)先級(jí)EMAC Lite10/100 以太網(wǎng) MACFIFO 深度、ping-pongSDRAM MEMC外部存儲(chǔ)器控制器刷新周期、行策略UART 16550調(diào)試串口波特率、發(fā)送中斷實(shí)際的 EDK 配置里我一般會(huì)把 LWIP 的全局?jǐn)?shù)組、PBUF 池和 netif 結(jié)構(gòu)體放在 BRAM 段而不是默認(rèn)的 SDRAM。SDRAM 隨機(jī)訪問延遲在幾十納秒量級(jí)頻繁執(zhí)行memcpy和pbuf_alloc時(shí)這個(gè)延遲會(huì)被放大到足以拉低吞吐。論文里系統(tǒng)使用 64 MB SDRAM最終性能是依靠中斷驅(qū)動(dòng)優(yōu)化獲得的并沒有特別調(diào)存儲(chǔ)布局如果把 pbuf 放入 BRAM理論上還可以再往上追幾個(gè) Mbps。3. LWIP 協(xié)議棧的無操作系統(tǒng)移植從四種工作模式到 RAW API3.1 四種協(xié)議棧組織方式的取舍在嵌入式系統(tǒng)里移植 LWIP首先要決定協(xié)議棧以什么形態(tài)存在。論文給出了四種方式至今仍值得逐一比較。第一種是 TCP/IP 的每一層單獨(dú)建一個(gè)進(jìn)程。鏈路層一個(gè)進(jìn)程IP 層一個(gè)進(jìn)程TCP 層一個(gè)進(jìn)程。優(yōu)點(diǎn)是分層模型和教材完全一致調(diào)試時(shí)可以在每一層打日志缺點(diǎn)也最明顯收一個(gè) TCP 段要發(fā)生三次上下文切換。從網(wǎng)卡驅(qū)動(dòng)到鏈路層進(jìn)程一次鏈路層到 IP 層一次IP 層到 TCP 層一次。MicroBlaze 沒有 MMU上下文切換需要保存和恢復(fù)十幾個(gè)寄存器一百兆網(wǎng)絡(luò)下每秒要處理幾千包單是切換開銷就能讓 CPU 占用率升到 50% 以上。第二種是把 TCP/IP 協(xié)議棧放進(jìn)操作系統(tǒng)內(nèi)核。應(yīng)用程序通過系統(tǒng)調(diào)用訪問 socket所有數(shù)據(jù)包處理都在內(nèi)核空間完成。理論效率不錯(cuò)但代碼與內(nèi)核綁定太緊換一個(gè) RTOS 就要重新適配在資源有限的 FPGA 設(shè)計(jì)里維護(hù)成本過高。第三種是協(xié)議棧所有層放在同一個(gè)進(jìn)程中應(yīng)用層作為獨(dú)立任務(wù)通過郵箱、消息隊(duì)列和協(xié)議棧通信。移植量適中結(jié)構(gòu)清晰但消息傳遞會(huì)引入兩次拷貝一次從協(xié)議??截惖较⒁淮螐南⒖截惖綉?yīng)用緩沖區(qū)。雙倍拷貝對(duì) 100 Mbps 吞吐是明顯負(fù)擔(dān)。第四種是應(yīng)用層直接駐留在 TCP/IP 進(jìn)程中用 LWIP 的 RAW API 注冊(cè)回調(diào)函數(shù)。無操作系統(tǒng)也能運(yùn)行數(shù)據(jù)包從驅(qū)動(dòng)進(jìn)入?yún)f(xié)議棧后直接分發(fā)給應(yīng)用沒有跨任務(wù)拷貝也沒有上下文切換。論文選的就是這種方案實(shí)測性能也最好。下面是移植時(shí)最關(guān)鍵的一段初始化代碼。3.2 LWIP 初始化與內(nèi)存池分配無操作系統(tǒng)環(huán)境下LWIP 的初始化順序必須嚴(yán)格遵守依賴關(guān)系先底層后高層sys_init(); /* 系統(tǒng)抽象層NO_SYS1時(shí)為空實(shí)現(xiàn) */ mem_init(); /* 堆內(nèi)存管理用于非池化分配 */ memp_init(); /* 內(nèi)存池初始化TCP PCB、PBUF等 */ pbuf_init(); /* 包緩沖區(qū)分配器 */ netif_init(); /* 初始化netif鏈表 */ tcp_init(); /* TCP控制塊初始化 */邏輯說明memp_init()管理的池大小由lwipopts.h中的MEMP_NUM_TCP_PCB、MEMP_NUM_TCP_SEG等宏決定。內(nèi)存池固定大小避免堆碎片但總數(shù)必須覆蓋并發(fā)連接和分段數(shù)否則tcp_seg分配失敗會(huì)靜默丟包。pbuf_init()則負(fù)責(zé)建立 PBUF 池用于保存收到的網(wǎng)絡(luò)幀池深度不是越大越好太大會(huì)擠占其他模塊的 BRAM 空間。在無操作系統(tǒng)模式下必須把lwipopts.h里的NO_SYS置 1同時(shí)讓SYS_LIGHTWEIGHT_PROT保持為 0否則源碼會(huì)去調(diào)用sys_arch里不存在的信號(hào)量函數(shù)鏈接直接報(bào)錯(cuò)。sys_init()在無 OS 情況下是空函數(shù)但保留調(diào)用可以讓代碼后續(xù)移植到 RTOS 時(shí)不需要修改。3.3 網(wǎng)絡(luò)接口注冊(cè)與地址參數(shù)初始化完成后要把 EMAC 的驅(qū)動(dòng)掛到 LWIP 的 netif 上。原始代碼的核心調(diào)用如下struct netif *default_netif; ip_addr_t ipaddr, netmask, gw; IP4_ADDR(ipaddr, 192, 168, 1, 100); /* FPGA板卡IP */ IP4_ADDR(netmask, 255, 255, 255, 0); /* 子網(wǎng)掩碼 */ IP4_ADDR(gw, 192, 168, 1, 1); /* 網(wǎng)關(guān) */ default_netif mem_malloc(sizeof(struct netif)); if (default_netif NULL) { print(netif_add(): out of memory\r\n); return 1; } default_netif netif_add(default_netif, ipaddr, netmask, gw, XEmacLite_ConfigTable[0], xemacliteif_init, ip_input); netif_set_default(default_netif); netif_set_up(default_netif);參數(shù)說明netif_add的第四個(gè)參數(shù)是狀態(tài)指針這里傳 EMAC 驅(qū)動(dòng)的配置表地址驅(qū)動(dòng)初始化時(shí)會(huì)讀取里面的基地址和中斷號(hào)第五個(gè)參數(shù)xemacliteif_init負(fù)責(zé)復(fù)位 EMAC 寄存器并啟動(dòng) PHY 協(xié)商第六個(gè)參數(shù)ip_input是接收回調(diào)當(dāng) MAC 收到完整以太網(wǎng)幀驅(qū)動(dòng)會(huì)調(diào)用它進(jìn)入 IP 層處理流程。一個(gè)容易踩的坑是mem_malloc得到的 netif 內(nèi)存必須位于 CPU 可直接訪問的地址空間。如果放在 SDRAM而 EMAC 的 DMA 配置仍然指向本地 RAM數(shù)據(jù)路徑就會(huì)斷掉。這個(gè)資源場景下通常把 netif 結(jié)構(gòu)體聲明為全局變量比動(dòng)態(tài)分配更安全。3.4 用 RAW API 搭建最小 TCP 回聲服務(wù)LWIP 的 RAW API 靠回調(diào)驅(qū)動(dòng)典型過程是創(chuàng)建 PCB、綁定端口、進(jìn)入監(jiān)聽、注冊(cè)接受回調(diào)void echo_init(void) { struct tcp_pcb *pcb; pcb tcp_new(); if (pcb ! NULL) { tcp_bind(pcb, IP_ADDR_ANY, ECHO_PORT); pcb tcp_listen(pcb); tcp_accept(pcb, echo_accept); } }echo_accept會(huì)在新連接到達(dá)時(shí)被調(diào)用然后在里面通過tcp_recv注冊(cè)數(shù)據(jù)接收函數(shù)。RAW API 的回調(diào)全部運(yùn)行在同一個(gè)上下文回調(diào)內(nèi)部不能阻塞也不能做長時(shí)間循環(huán)否則整個(gè)協(xié)議棧停擺。大數(shù)據(jù)量接收時(shí)每處理完一段都要調(diào)用tcp_recved告訴協(xié)議棧釋放緩沖區(qū)并更新接收窗口漏掉這一步會(huì)表現(xiàn)為吞吐逐步下降最終停在很低的水平。下面用一個(gè)對(duì)比表來總結(jié)四種模式的差異方便在項(xiàng)目啟動(dòng)前做選型。工作模式是否依賴 OS數(shù)據(jù)拷貝次數(shù)上下文切換適用場景每層一個(gè)進(jìn)程是多次高教學(xué)、協(xié)議調(diào)試協(xié)議棧進(jìn)內(nèi)核是少中有成熟網(wǎng)絡(luò)棧的 RTOS單進(jìn)程應(yīng)用獨(dú)立是兩次中模塊化產(chǎn)品應(yīng)用駐留協(xié)議棧否零低高吞吐、無 OS 設(shè)備4. 查詢 FIFO 與中斷 FIFO性能差異、測試方法和優(yōu)化空間4.1 兩種驅(qū)動(dòng)模式的硬件數(shù)據(jù)路徑EMAC Lite 的 IP Core 內(nèi)部使用 FIFO 緩存收發(fā)數(shù)據(jù)。驅(qū)動(dòng)層可以通過兩種方式獲取數(shù)據(jù)到達(dá)事件查詢和中斷。查詢模式下主循環(huán)不斷讀取 EMAC 的中斷狀態(tài)寄存器判斷接收 FIFO 是否有新幀。雖然代碼最簡單但每次查詢都會(huì)占用 OPB 總線和寄存器接口。LWIP 處理一幀需要訪問多個(gè)內(nèi)存區(qū)域查詢動(dòng)作又在同一總線上插隊(duì)結(jié)果就是總線的有效帶寬被切碎吞吐上不去。中斷模式下EMAC 收到完整幀后拉高中斷信號(hào)MicroBlaze 進(jìn)入 ISR在中斷服務(wù)程序里調(diào)用ip_input把數(shù)據(jù)送入?yún)f(xié)議棧。CPU 不再浪費(fèi)周期去輪詢狀態(tài)吞吐自然更高。但中斷模式也帶來一個(gè)新問題如果每個(gè)幀都觸發(fā)一次中斷ISR 的現(xiàn)場保存和恢復(fù)開銷會(huì)累積。論文中的驅(qū)動(dòng)是分兩次測試一個(gè)查詢 FIFO一個(gè)中斷 FIFO中斷模式明顯勝出。4.2 BW Meter 實(shí)測方法論文的測試平臺(tái)為 Spartan-3 開發(fā)板CPU 是 MicroBlaze主頻 100 MHz內(nèi)存 64 MB SDRAM配置 100 Mbps EMAC IP Core。軟件側(cè)無操作系統(tǒng)bootrom 基于 Xilinx standaloneLWIP 使用 RAW API。測試方式RJ45 交叉以太網(wǎng)線直連 PC 和開發(fā)板。PC 作為 TCP 客戶端開發(fā)板作為 TCP 服務(wù)端。連接建立后PC 向服務(wù)端連續(xù)發(fā)送 TCP 包服務(wù)端只統(tǒng)計(jì)收到的數(shù)據(jù)量不做任何回顯。網(wǎng)絡(luò)測試工具 BW Meter 實(shí)時(shí)顯示下行和上行速率。這里有一個(gè)容易忽略的細(xì)節(jié)測試前要關(guān)閉 PC 網(wǎng)卡的協(xié)議卸載功能比如 TCP Checksum Offload 和 Large Send Offload。否則 PC 硬件會(huì)把部分負(fù)載接走測試結(jié)果虛高無法反映真實(shí)協(xié)議棧吞吐。4.3 性能對(duì)比結(jié)果與參數(shù)解讀FIFO 模式下載速率 DL上傳速率 UL說明查詢模式1.0 Mbps50.2 MbpsCPU 忙等接收路徑不穩(wěn)定中斷模式1.3 Mbps63.6 Mbps中斷驅(qū)動(dòng)吞吐提升約 26.7%這里的 DL 和 UL 是相對(duì)于 PC 的描述。FPGA 作為服務(wù)端主要壓力在接收方向也就是 PC 發(fā)送到 FPGA 的 UL 方向。查詢模式能穩(wěn)定跑到 50.2 Mbps中斷模式提升到 63.6 Mbps。這個(gè)差距的本質(zhì)在于中斷模式讓 CPU 從輪詢中解放出來把時(shí)間真正花在協(xié)議棧處理上。4.4 為什么中斷模式更快查詢模式的問題不只是忙等還破壞了協(xié)議棧處理數(shù)據(jù)的突發(fā)性。LWIP 接收一幀時(shí)需要把數(shù)據(jù)從 FIFO 拷貝到 pbuf再交給 IP 層和 TCP 層最后更新接收窗口。在拷貝期間如果有新幀到達(dá)FIFO 開始排隊(duì)當(dāng) FIFO 深度不足時(shí)MAC 硬件會(huì)直接丟棄新幀觸發(fā) TCP 重傳。重傳進(jìn)一步增加 CPU 負(fù)載形成惡性循環(huán)。中斷模式則按數(shù)據(jù)到達(dá)的節(jié)奏驅(qū)動(dòng)處理CPU 處理完當(dāng)前幀后回到主循環(huán)等待下一個(gè)中斷FIFO 中不會(huì)積壓過多數(shù)據(jù)。不過中斷也有閾值問題如果每來一個(gè)幀就中斷一次頻繁的現(xiàn)場切換會(huì)吃掉吞吐。2007 年那版驅(qū)動(dòng)沒有中斷合并能力能做到 63.6 Mbps 已經(jīng)很接近硬件極限。新一些的驅(qū)動(dòng)支持 interrupt coalescing可以設(shè)置當(dāng) FIFO 內(nèi)有 N 幀或超過 T 微秒才觸發(fā)一次中斷適合高吞吐場景。4.5 可落地的 LWIP 參數(shù)調(diào)整無操作系統(tǒng)方案中吞吐瓶頸通常集中在內(nèi)存池和窗口大小上。下面是一組針對(duì) 100 Mbps 接收壓力的建議值#define NO_SYS 1 #define MEM_SIZE (64 * 1024) #define PBUF_POOL_SIZE 32 #define PBUF_POOL_BUFSIZE 1518 #define MEMP_NUM_TCP_SEG 64 #define TCP_WND (16 * 1024) #define TCP_SND_BUF (16 * 1024)參數(shù)說明PBUF_POOL_SIZE決定接收緩沖區(qū)數(shù)量32 個(gè)可以緩沖大約 32 個(gè)幀在突發(fā)流量下不容易丟包TCP_WND是 TCP 接收窗口至少要比兩個(gè)最大段MSS大否則無法維持發(fā)送窗口滿速TCP_SND_BUF影響發(fā)送方向如果只是接收測試不需要調(diào)得太大。如果這兩個(gè)緩沖區(qū)被調(diào)得過大而 MicroBlaze 的本地 BRAM 又很緊張內(nèi)存分配失敗會(huì)以err計(jì)數(shù)方式反饋。下一章會(huì)給一個(gè)定位這類問題的具體排查順序。5. 進(jìn)階技巧無操作系統(tǒng)下 TCP 吞吐瓶頸的定位順序5.1 從 PHY 到協(xié)議棧的三步排查遇到吞吐上不去我一般按下面的順序看能快速縮小范圍。第一步確認(rèn) PHY 是否協(xié)商到 100 Mbps 全雙工。很多開發(fā)板默認(rèn)的 PHY 配置是自適應(yīng)但交叉線直連時(shí)如果 PHY 老化或抽頭錯(cuò)誤房間里可能掉到 10 Mbps。讀取 EMAC 的狀態(tài)寄存器看鏈路速度和雙工位即可。不要只看電腦端協(xié)商結(jié)果要以 FPGA 側(cè)實(shí)際讀到的鏈路狀態(tài)為準(zhǔn)。第二步查 FIFO 溢出。EMAC Lite 的 ISR 里通常有rx_overrun這類狀態(tài)位。如果溢出計(jì)數(shù)在每個(gè)測試周期都增長說明中斷響應(yīng)太慢或者 pbuf 池耗盡。響應(yīng)慢則考慮提高 MicroBlaze 中斷優(yōu)先級(jí)池耗盡則調(diào)大PBUF_POOL_SIZE。第三步打開 LWIP 的統(tǒng)計(jì)信息。在lwipopts.h中啟用LWIP_STATS和MEMP_STATS然后把分配失敗計(jì)數(shù)輸出到串口。5.2 用統(tǒng)計(jì)計(jì)數(shù)器驗(yàn)證丟包extern struct stats_mem memp_stats[]; u16_t last_err 0; void print_net_stats(void) { u16_t err memp_stats[PBUF_POOL].err; if (err ! last_err) { xil_printf(pbuf pool alloc err: %d\r\n, err); last_err err; } }邏輯說明memp_stats數(shù)組按池類型記錄分配次數(shù)和失敗次數(shù)err字段是累計(jì)值。主循環(huán)里每 1 秒調(diào)用一次print_net_stats如果失敗計(jì)數(shù)出現(xiàn)增長就說明協(xié)議棧層在丟包問題出在 LWIP 配置而不是 PHY。這里用xil_printf而不是標(biāo)準(zhǔn)printf是因?yàn)樗灰蕾嚩颜加玫拇a空間更小適合 MicroBlaze 的存儲(chǔ)環(huán)境。配合 PC 上的 Wireshark 抓包可以進(jìn)一步區(qū)分是協(xié)議棧丟包還是 MAC 丟包。抓包時(shí)如果看到大量 TCP Dup ACK 和重傳而 FIFO 溢出計(jì)數(shù)為零則瓶頸多半在TCP_WND太小如果溢出計(jì)數(shù)和重傳同時(shí)飆升則要優(yōu)先提升中斷響應(yīng)速度或加大PBUF_POOL_SIZE。這套定位方法不依賴操作系統(tǒng)稍微改一下變量名就能沿用到 Zynq 的裸機(jī) LWIP 工程里。本文還有配套的精品資源點(diǎn)擊獲取