據(jù)傳輸:基于CYUSB3014的USB 3.0 Slave FIFO設(shè)計實戰(zhàn))
前陣子幫客戶做一套高速數(shù)據(jù)采集系統(tǒng)FPGA 端要實時把 ADC 數(shù)據(jù)灌到 PC 上做處理。評估了幾種 USB 3.0 方案之后最后定了FPGA CYUSB3014EZ-USB FX3的 Slave FIFO 模式。調(diào)通之后用 Streamer 和自寫上位機(jī)分別測速持續(xù)傳輸穩(wěn)定在338MB/s這個數(shù)字在 USB 3.0 bulk 傳輸里已經(jīng)非常接近鏈路極限了。整個過程踩了不少坑固件側(cè)、FPGA 側(cè)、主機(jī)側(cè)都有很多問題不是看 datasheet 就能立刻想明白的。這篇文章把完整的設(shè)計思路、固件與源碼要點、實測數(shù)據(jù)和避坑記錄整理出來給正在做或者準(zhǔn)備做 FPGA 高速數(shù)據(jù)傳輸?shù)呐笥岩粋€參考。如果你也是用 FPGA 做數(shù)據(jù)采集、圖像傳輸、軟件無線電這類需要大帶寬上位的項目這篇內(nèi)容應(yīng)該能幫你省下至少兩周的調(diào)板時間。文章會講清楚為什么選 FX3、Slave FIFO 模式到底怎么工作、固件里哪些配置必須改、FPGA 側(cè)狀態(tài)機(jī)怎么寫不會出錯還有 338MB/s 這個數(shù)字是怎么來的、還能不能更高。1. 方案選型與整體設(shè)計思路1.1 為什么是 CYUSB3014先說選型。FPGA 和 PC 之間高速傳輸常見的路子就那幾條PCIe、千兆/萬兆以太網(wǎng)、USB 3.0。PCIe 帶寬最好但 FPGA 要帶 PCIe 硬核PC 端還要寫驅(qū)動工程量大。以太網(wǎng)方案比較通用但協(xié)議棧和 FPGA 邏輯資源開銷不小TCP 吞吐率調(diào)優(yōu)也是無底洞。USB 3.0 最大的優(yōu)勢是上位機(jī)生態(tài)成熟——Windows 下有 CyUSB3.sys 驅(qū)動Linux 下有現(xiàn)成的內(nèi)核驅(qū)動不需要自己碰驅(qū)動開發(fā)的深水區(qū)。USB 3.0 橋接芯片里市面上主要就是 Cypress現(xiàn)在是 Infineon的 FX3 和 FTDI 的 FT60x。FT60x 用起來簡單FIFO 接口好接但它的文檔和社區(qū)積累不如 FX3而且 FT60x 的 USB 協(xié)議層是把數(shù)據(jù)包封裝好之后直接往主機(jī)發(fā)的靈活性差一些。FX3 內(nèi)置了一顆 200MHz 的 ARM926EJ-S 核可以跑固件GPIF II 接口可編程既能做 Slave FIFO 又能做 Master端點、DMA 通道、Buffer 大小全都可以按需配置。說白了FT60x 是一個定死的橋FX3 是一個半定制的橋后者給了你足夠的調(diào)整空間去優(yōu)化吞吐率。另外 FX3 的參考資料非常多官方有 AN75779 這個經(jīng)典的 Slave FIFO 示例工程網(wǎng)上也有大量中文資料。做硬件最怕的就是芯片資料少、出了問題沒地方查FX3 在這方面的優(yōu)勢是實打?qū)嵉摹?.2 Slave FIFO 模式到底是怎么工作的FX3 的 GPIF II 是一個可編程狀態(tài)機(jī)接口可以配置成 Master 模式由 FX3 主動產(chǎn)生讀寫時序去訪問外部器件也可以配置成 Slave FIFO 模式由外部器件當(dāng)主控FX3 的 FIFO 就是一個被動的存儲池。我們項目里用的就是 Slave FIFO 同步模式。FPGA 作為主控向外提供 100MHz 的 PCLK 給 FX3同時控制片選、讀寫使能、數(shù)據(jù)總線FX3 通過 FLAGA/FLAGB 這些水位標(biāo)志告訴 FPGA 現(xiàn)在能不能寫/讀。數(shù)據(jù)流是單向大帶寬的FPGA 內(nèi)部邏輯把數(shù)據(jù)寫入緩存 FIFO然后寫狀態(tài)機(jī)檢測 FX3 的 FLAGA若水位允許就往 32bit 數(shù)據(jù)總線上丟數(shù)據(jù)并拉低 SLWRPCLK 上升沿采樣數(shù)據(jù)進(jìn)入 FX3 的 DMA 緩沖固件再把緩沖搬運到 USB 端點最終通過 USB 3.0 送到主機(jī)。這里有個常見誤區(qū)很多人以為 Slave FIFO 模式下 FX3 就是一塊內(nèi)存直接映射到 USB其實不是。FX3 內(nèi)部有 DMA 通道和 USB 端點數(shù)據(jù)是肩并肩搬運的GPIF II 寫入 FIFO 的速度和 USB 上傳速度互相制約水往低處流慢的那一方?jīng)Q定實際速率。所以要讓 FPGA 側(cè)接口帶寬遠(yuǎn)大于 USB 側(cè)才能讓 USB 變成瓶頸我們配置的 32bit 100MHz 理論帶寬是 400MB/s比 USB 3.0 實際有效帶寬高出一截這樣 USB 鏈路才是天花板。2. 硬件板卡設(shè)計與接口要點2.1 最小系統(tǒng)與啟動方式FX3 的最小系統(tǒng)說簡單也簡單說麻煩也麻煩。供電方面它需要 1.2V 內(nèi)核電壓、1.8V/2.5V/3.3V 的 I/O 電壓自己內(nèi)部還有 USB PHY 的模擬供電。我見過不少板子死在供電上——上電順序不對導(dǎo)致枚舉不穩(wěn)定或者 VIO 電壓和 FPGA bank 電壓不匹配導(dǎo)致接口時序異常。建議 VIO 和 FPGA 側(cè) bank 電壓保持一致比如都用 3.3V這樣不用加電平轉(zhuǎn)換芯片少一層延遲時序也更好控。啟動方式要特別注意。FX3 可以從 USB 啟動、I2C EEPROM 啟動、或者 SPI Flash 啟動。開發(fā)調(diào)試階段用 USB 啟動最方便也就是每次上電后通過 USB Control Center 手動下載固件。但是一旦進(jìn)入聯(lián)調(diào)階段如果不想每次斷電后都重新下載就要在板上放一顆 I2C EEPROM比如 24LC128 或 24LC256把固件燒進(jìn)去。這個點坑過很多人EEPROM 里固件版本和當(dāng)前工程不匹配板子上電后枚舉出來一個老固件然后你對新固件的修改完全不生效還以為是代碼問題。所以建議 Solid State 版的燒錄和版本管理從一開始就規(guī)范化我在工程里會寫一個版本號常量枚舉后通過廠商字符串讀出來上位機(jī)也能顯示避免這種烏龍。2.2 關(guān)鍵信號與時序裕量Slave FIFO 同步模式下 FPGA 和 FX3 之間的關(guān)鍵信號不多列一下信號方向相對 FPGA功能PCLK輸出給 FX3接口時鐘通常 100MHzSLCS#輸出片選低有效SLWR#輸出寫使能低有效SLRD#輸出讀使能低有效SLOE#輸出輸出使能讀數(shù)據(jù)時使用PKTEND#輸出短包結(jié)束信號A[1:0]輸出線程選擇地址DQ[31:0]雙向數(shù)據(jù)總線FLAGA/FLAGB輸入FIFO 狀態(tài)標(biāo)志這里的時序關(guān)系是高速接口的關(guān)鍵。PCLK 是 100MHz一個周期 10ns。FX3 手冊里對 DQ 相對 PCLK 的建立時間和保持時間都有明確要求不同批次芯片也許有細(xì)微差異但大致在幾個納秒量級。FPGA 側(cè)約束要跟上不然即使功能正確時序裕量不足也可能在溫度變化或不同板卡間隨機(jī)出錯。我的建議是在 XDC/FDC 中顯式約束 PCLK并用 set_input_delay、set_output_delay 約束數(shù)據(jù)總線與 PCLK 的相對關(guān)系。你不需要把每個信號的 delay 卡到幾個皮秒但至少要讓 Vivado/Quartus 知道 DQ 是同步于 PCLK 的否則工具默認(rèn)按異步路徑處理布局布線出來的結(jié)果很隨機(jī)。2.3 PCB 布線經(jīng)驗PCB 布線是容易忽略但決定成敗的一環(huán)。FPGA 與 FX3 之間的數(shù)據(jù)總線 DQ[31:0] 要盡量等長組內(nèi)偏差控制在 ±5mil 以內(nèi)PCLK 到 FX3 的走線要比數(shù)據(jù)線短一些因為 FPGA 輸出的時鐘沿和數(shù)據(jù)的相對關(guān)系受內(nèi)部延遲影響PCB 上留出余量有利于時序收斂。USB 3.0 的 TX/RX 差分對要做 90Ω 差分阻抗控制走線遠(yuǎn)離其他高速信號連接器附近的參考平面不能挖斷。我的經(jīng)驗是第一版 PCB 如果條件允許把 FLAG 信號和 DQ 信號都引出測試點方便示波器或邏輯分析儀測量。調(diào)試高速接口時能直接看到波形比什么都強(qiáng)。另外 FX3 的 USB 3.0 引腳和 USB 2.0 引腳需要同時接出來因為 FX3 啟動時先靠 USB 2.0 枚舉再協(xié)商到 SuperSpeed。只接了 USB 3.0 的線沒接 USB 2.0板子會一直枚舉失敗。3. FX3 固件側(cè)配置與實現(xiàn)3.1 從官方示例工程改起FX3 固件開發(fā)強(qiáng)烈建議直接從 SDK 里的 cyfxgpiftousb 示例工程開始而不是從零搭。這個工程本身就是 GPIF II 到 USB 的橋接 demoSlave FIFO 模式的所有基礎(chǔ)代碼都在你要改的主要是GPIF 狀態(tài)機(jī)配置、DMA 通道參數(shù)、端點配置、線程與水位的映射關(guān)系。固件主流程大致是調(diào)用 CyU3PDeviceInit 初始化芯片調(diào)用 CyU3PKernelEntry 啟動 RTOS 內(nèi)核創(chuàng)建 DMA 通道P2U 和 U2P加載 GPIF 狀態(tài)機(jī)配置Slave FIFO 同步模式配置 USB 端點并連接 USB啟動 GPIF 引擎每一步都有對應(yīng)的 APISDK 里也有完整注釋關(guān)鍵是理解每個參數(shù)的含義尤其是 DMA 通道的 buffer 大小和 buffer count這兩個參數(shù)直接決定吞吐率。3.2 端點、DMA 通道與線程配置這是固件里最重要的部分。我們的數(shù)據(jù)通路是 FPGA 把數(shù)據(jù)寫進(jìn) FX3所以核心是 P2UPeripheral to USB通道外部傳入的數(shù)據(jù)先落入 GPIF II 的 FIFODMA 引擎再把數(shù)據(jù)搬到 USB 端點。USB 側(cè)我用了一個 Bulk IN 端點 0x81以及一個 Bulk OUT 端點 0x01用來接收上位機(jī)下發(fā)的控制命令。創(chuàng)建一個可靠的 DMA 通道核心參數(shù)有幾個傳輸方向、事件類型、buffer 大小、buffer count。示例代碼里常見的是 16KB buffer、8 個 buffer。我實際測試下來buffer count 太少會導(dǎo)致 DMA 搬完數(shù)據(jù)后 CPU 介入太頻繁速率上不去。后來把 buffer count 加大到 16加上合理的水位配置吞吐率明顯改善。FX3 內(nèi)部是 ARM 核跑 RTOS 的DMA 中斷太多會擠掉主循環(huán)的時間相當(dāng)于拖慢吞吐。端點配置也不復(fù)雜但有一點要注意Bulk 端點的最大包大小要設(shè)成 1024 字節(jié)這是 USB 3.0 超速 bulk 的標(biāo)準(zhǔn)。如果固件里忘了改還是默認(rèn)的 512 字節(jié)那帶寬會直接砍半。下面是固件初始化中關(guān)鍵部分的一個簡化示例/* DMA 通道創(chuàng)建P2U自動模式從 GPIF 到 USB */ CyU3PDmaChannelCreate(glChHandleSlFifoP2U, CY_U3P_DMA_TYPE_MANUAL, dmaConfig); /* USB 端點配置Bulk IN 0x81PacketSize 1024 */ CyU3PUsbSetEpConfig(0x81, CY_U3P_USB_EP_BULK, 1024); CyU3PUsbSetEpConfig(0x01, CY_U3P_USB_EP_BULK, 1024); /* 啟動 GPIF 引擎加載 Slave FIFO 狀態(tài)機(jī)表 */ CyU3PGpifLoad(CyFxGpifConfig); CyU3PGpifStart();到底用自動模式還是手動模式取決于是否需要 CPU 參與數(shù)據(jù)搬運。純高速流式傳輸用自動模式即可CPU 不碰數(shù)據(jù)DMA 直接搬如果需要做協(xié)議解析、包格式轉(zhuǎn)換才需要手動模式讓 CPU 介入。3.3 水位配置與水印標(biāo)志FLAG 信號是 FPGA 判斷能不能寫的依據(jù)它背后靠的是水位Watermark機(jī)制。FX3 的每個線程都有自己的水位配置當(dāng) FIFO 剩余空間大于某個閾值時FLAG 就有效表示可以寫小于閾值時FLAG 無效表示不要再寫了否則數(shù)據(jù)可能溢出。水印閾值設(shè)得太小FIFO 快滿了才通知 FPGAFPGA 的寫入狀態(tài)機(jī)來不及停就會丟數(shù)據(jù)閾值設(shè)得太大明明還有很多空間但 FLAG 已經(jīng)無效利用率上不去吞吐率受損。我一開始用默認(rèn)閾值實測只有 200MB/s 左右后來把 P2U 方向的水印適當(dāng)調(diào)低給 FIFO多留一點緩沖速率馬上上來了。具體數(shù)值和你的 DMA buffer 配置有關(guān)需要實測微調(diào)但這里記住一個原則寫方向的水印要保證 FIFO 里有足夠的空間余量讓 FPGA 從檢測到 FLAG 無效到停止寫入這段時間里數(shù)據(jù)不會溢出。3.4 固件側(cè)的幾個坑先講 PKTEND。在 Slave FIFO 模式下如果傳輸?shù)氖沁B續(xù)大流量數(shù)據(jù)FPGA 側(cè)需要周期性地拉一下 PKTEND#告訴 FX3這一段數(shù)據(jù)結(jié)束了發(fā)給主機(jī)吧。不拉 PKTEND 的后果是數(shù)據(jù)一直攢在 FX3 內(nèi)部主機(jī)收不到表現(xiàn)就是上位機(jī)讀到的數(shù)據(jù)卡住或者吞吐率忽高忽低。很多人在跑短包測試時沒發(fā)現(xiàn)問題一旦跑持續(xù)大流量就掉鏈子多半是這個原因。再講 EEPROM 啟動的坑。上文提過固件版本管理這里再補(bǔ)充一點EEPROM 里的固件如果和當(dāng)前硬件不匹配比如 GPIO 配置不同輕則功能異常重則設(shè)備枚舉都過不了。而且 USB 啟動和 EEPROM 啟動的優(yōu)先級是 USB 優(yōu)先還是 EEPROM 優(yōu)先和 boot 引腳配置有關(guān)設(shè)計時要明確你要的啟動方式別讓兩種方式互相干擾。固件側(cè)調(diào)優(yōu)經(jīng)驗一句話總結(jié)先把默認(rèn)示例跑通再一項一項改參數(shù)每次只改一個變量記錄吞吐率變化不要一次改一堆參數(shù)然后不知道是誰起作用。4. FPGA 側(cè)邏輯實現(xiàn)4.1 模塊劃分與數(shù)據(jù)通路FPGA 側(cè)的邏輯我是按模塊劃分的避免把所有邏輯堆在頂層。主要分為時鐘管理模塊、數(shù)據(jù)源模塊ADC 或 DDR 讀出、寫通道狀態(tài)機(jī)、讀通道狀態(tài)機(jī)、以及跨時鐘域緩沖 FIFO。數(shù)據(jù)通路的頂層邏輯是這樣的數(shù)據(jù)源以較高速率把數(shù)據(jù)寫入一個異步 FIFO這個 FIFO 隔離了源時鐘域和 GPIF 時鐘域?qū)憼顟B(tài)機(jī)輪詢 FX3 的 FLAGA當(dāng) FIFO 非空且 FX3 允許寫入時從 FIFO 讀數(shù)據(jù)并驅(qū)動 DQ 總線和 SLWR讀狀態(tài)機(jī)相對簡單主要處理上位機(jī)通過 OUT 端點下發(fā)的命令流用 SLOE 和 SLRD 從 FX3 讀回數(shù)據(jù)。使用異步 FIFO 這個點非常重要。FPGA 內(nèi)部邏輯可能跑在 200MHz 甚至更高而 GPIF 接口是 100MHz兩個時鐘域如果不做隔離數(shù)據(jù)采樣很容易出問題。跨時鐘域不是靠大概對齊就能解決的一定要用 FIFO 或至少雙寄存器同步。4.2 寫通道狀態(tài)機(jī)寫狀態(tài)機(jī)是 FPGA 側(cè)的核心。它要做的事情是檢測 FLAGA 有效然后把 32bit 數(shù)據(jù)放到 DQ 總線上拉低 SLWR等待一個 PCLK 上升沿完成寫入重復(fù)直到 FIFO 空或者 FLAGA 無效。有一個細(xì)節(jié)必須注意FLAGA 是異步信號進(jìn)入 FPGA 內(nèi)部要先做兩級寄存器同步否則可能采到亞穩(wěn)態(tài)狀態(tài)機(jī)誤判。我在代碼里專門做了同步模塊同步后還要考慮到同步延遲——從 FLAGA 有效到 FPGA 真正采樣中間有幾個時鐘周期的延遲所以要保證 FX3 返回 FLAG 無效時水位的余量能覆蓋這個延遲。寫狀態(tài)機(jī)的簡化 Verilog 示例// 同步后的 FLAGflag_a_sync // 狀態(tài)機(jī)核心跳轉(zhuǎn) always (posedge pclk or negedge rst_n) begin if (!rst_n) state IDLE; else begin case (state) IDLE: begin if (fifo_empty 1b0 flag_a_sync 1b1) state WRITE; end WRITE: begin if (flag_a_sync 1b0) state IDLE; else if (fifo_empty 1b1) state IDLE; end endcase end end // 數(shù)據(jù)與寫使能輸出 always (posedge pclk or negedge rst_n) begin if (!rst_n) begin slwr_n 1b1; dq 32h0; end else if (state WRITE) begin dq fifo_dout; // 從緩存 FIFO 讀出數(shù)據(jù) slwr_n 1b0; // 拉低寫使能 end else begin slwr_n 1b1; end end這段邏輯看著簡單實際調(diào)試時最折磨人的是時序收斂。DQ 總線在 PCLK 上升沿之前要穩(wěn)定SLWR 也要保證和 DQ 滿足 FX3 的建立保持時間。如果你的設(shè)計在仿真里一切正常上板就隨機(jī)出錯十有八九是時序約束沒做好。4.3 讀通道與回環(huán)驗證讀通道用來接收上位機(jī)的命令或者做回環(huán)驗證。它的邏輯比寫通道簡單檢測 FLAGB 是否表示有數(shù)據(jù)可讀拉低 SLRD#讀取 DQ 總線。需要注意的是 SLOE# 信號在 Slave FIFO 模式下讀數(shù)據(jù)時分兩種情況——如果 SLOE 有效DQ 總線由 FX3 驅(qū)動如果 SLOE 無效DQ 總線可能處于高阻。所以讀通道要同時控制 SLOE 和 SLRD不能只拉一個。建議從最開始就做一個回環(huán)測試FPGA 從 FX3 讀回數(shù)據(jù)然后原樣寫回。這樣固件和 FPGA 的讀寫通道可以分開驗證哪邊有問題一目了然。調(diào)試完回環(huán)通路了再接入真實數(shù)據(jù)源。4.4 時序約束實戰(zhàn)FPGA 側(cè)時序約束是整個工程能否穩(wěn)定跑滿速率的隱藏決定因素。我以 Vivado 為例說一下做法create_clock -period 10.000 -name pclk [get_ports pclk] # 輸入延遲FX3 的 FLAG 信號相對 PCLK set_input_delay -clock pclk -max 5.0 [get_ports {flag_a flag_b}] set_input_delay -clock pclk -min 1.0 [get_ports {flag_a flag_b}] # 輸出延遲DQ、SLWR 等相對 PCLK set_output_delay -clock pclk -max 6.0 [get_ports {dq[*] slwr_n slrd_n}] set_output_delay -clock pclk -min 1.0 [get_ports {dq[*] slwr_n slrd_n}]這里的具體 delay 值僅供參考要根據(jù) FX3 手冊的時序參數(shù)計算。如果你的工程里沒有類似約束工具會默認(rèn)把 FLAG 當(dāng)成異步輸入布局布線時不會保證它滿足時序上板出錯概率很高。我見過太多人仿真沒問題但上板隨機(jī)錯最后發(fā)現(xiàn)約束文件幾乎是空的。5. 實測數(shù)據(jù)、性能分析與優(yōu)化5.1 338MB/s 是怎么測出來的實測環(huán)節(jié)我用兩種方法互相驗證。第一種是 Cypress SDK 自帶的 Streamer 上位機(jī)它支持 FX3 的 bulk 傳輸測速簡單粗暴界面直接顯示吞吐率。第二種是自己寫了一個 C# 上位機(jī)基于 CyUSB.NET 庫開一個 4MB 的接收緩沖循環(huán)調(diào)用異步讀接口統(tǒng)計每秒收到的字節(jié)數(shù)。測試條件是FPGA 側(cè)用計數(shù)器產(chǎn)生偽隨機(jī)數(shù)據(jù)流DDR 讀出的數(shù)據(jù)也可以反正不關(guān)心內(nèi)容只關(guān)心速度。上位機(jī)持續(xù)讀取 60 秒統(tǒng)計總字節(jié)數(shù)換算成平均速率。多次測試取平均值最終結(jié)果是 338MB/s。測試模式實測速率說明默認(rèn)配置DMA buffer 8 個約 200MB/s水印不匹配DMA 中斷頻繁加大 buffer調(diào)整水印約 300MB/s明顯改善最終調(diào)優(yōu)主機(jī)端大緩沖338MB/s接近 USB 3.0 bulk 極限主機(jī)端緩沖 64KB約 260MB/s緩沖區(qū)太小丟速嚴(yán)重看到 200MB/s 到 338MB/s 的差距了吧這幾個變量——DMA 通道 buffer count、水印閾值、主機(jī)端接收緩沖——任何一個不匹配都會讓你誤以為方案不行其實是參數(shù)沒調(diào)到位。5.2 338MB/s 離 USB 3.0 的理論極限還有多遠(yuǎn)來算一筆賬。USB 3.0 的物理層線速率是 5Gbps但因為采用了 8b/10b 編碼實際有效數(shù)據(jù)速率是 4Gbps也就是 500MB/s。這是物理層的上限。但是 bulk 傳輸在協(xié)議層有開銷每個包有包頭、CRC、握手還有包與包之間的間隔。實際能達(dá)到的最大 payload 帶寬通常只有 400MB/s 左右這還是在最理想的情況下。如果你用 USB 分析儀抓包會看到總線上的數(shù)據(jù)并不會 100% 時間都在傳數(shù)據(jù)總有一些 idle 間隔。再回到接口側(cè)FPGA 與 FX3 之間是 32bit 100MHz理論帶寬 400MB/s。FPGA 側(cè)供給的上限是 400MB/sUSB 側(cè)實際能吸收的不到 400MB/s所以瓶頸在 USB 鏈路。338MB/s 大約是 400MB/s 理論最大 payload 的 84.5%這個數(shù)字已經(jīng)很健康了說明 USB 協(xié)議開銷和 FIFO 切換間隙已經(jīng)壓得比較低。所以如果有人問怎么才能測到 400MB/s 以上答案很簡單換 USB 3.1/3.2 方案或者用 PCIe。FX3 跑 USB 3.0338MB/s 已經(jīng)接近它的天花板。5.3 影響吞吐率的幾個隱形因素先講主機(jī)端。很多人把注意力全部放在 FPGA 和 FX3 上忽略了上位機(jī)代碼。如果你用同步方式讀 USB每次讀 4KB讀完再發(fā)起下一次讀取中間的開銷非常大吞吐率能掉到 200MB/s 以下。要跑滿速率必須用異步讀并且緩沖區(qū)要足夠大至少 4MB。原理很簡單USB 控制器需要足夠的緩沖來容忍系統(tǒng)的調(diào)度延遲緩沖太小系統(tǒng)一卡USB 總線就空等了。固件線程優(yōu)先級也有影響。FX3 的 RTOS 里USB 中斷線程和 DMA 事件線程的優(yōu)先級如果設(shè)置不當(dāng)數(shù)據(jù)搬運鏈條上某一環(huán)會周期性阻塞。我試過把 DMA 事件線程優(yōu)先級調(diào)低速率暴跌調(diào)回合適優(yōu)先級后恢復(fù)正常。這類問題沒有統(tǒng)一標(biāo)準(zhǔn)要結(jié)合自己的數(shù)據(jù)流來驗證。還有一個容易被忽略的因素是數(shù)據(jù)對齊。Bulk 傳輸對包大小敏感如果 FPGA 側(cè)送入 FX3 的數(shù)據(jù)總是湊不滿 1024 字節(jié)的整數(shù)倍就會頻繁產(chǎn)生短包短包每一次都需要 PKTEND 和額外的總線處理長期跑下來吞吐率損失不小。所以設(shè)計數(shù)據(jù)源時盡量保證單次寫入的數(shù)據(jù)量是 1024 字節(jié)的整數(shù)倍。6. 常見問題排查與避坑速查6.1 問題速查表癥狀可能原因解決方案設(shè)備無法枚舉USB 2.0 線沒接、EEPROM 固件損壞、供電異常先測 USB 2.0 枚舉再排查供電枚舉成功但上位機(jī)打不開設(shè)備驅(qū)動沖突或驅(qū)動版本不對卸載舊驅(qū)動重裝 CyUSB3.sys速率只有 100-200MB/s主機(jī)緩沖太小、DMA 配置不當(dāng)調(diào)整主機(jī)緩沖到 4MB 以上加大 DMA buffer count跑幾分鐘后速率驟降上位機(jī)內(nèi)存碎片或緩存回收不及時改用異步讀反復(fù)使用預(yù)分配緩沖數(shù)據(jù)內(nèi)容錯位、出現(xiàn)亂碼字節(jié)序不對或數(shù)據(jù)位寬映射錯誤檢查 DQ 總線高低字節(jié)映射32bit 打包順序FLAG 信號誤觸發(fā)導(dǎo)致丟數(shù)跨時鐘域亞穩(wěn)態(tài)未處理增加兩級同步寄存器檢查水位余量短包模式正常連續(xù)流卡死PKTEND 沒周期性拉低在 FPGA 邏輯中按包周期拉低 PKTEND#這張表我每次做新項目都會打開看一遍。很多問題看起來是硬件問題實際是軟件配置問題很多問題看起來是 FPGA 問題實際是上位機(jī)問題。調(diào)試時不要急著懷疑一個方向按數(shù)據(jù)流從源頭到終點一級一級排查。6.2 一次卡在 200MB/s的排查實錄這個案例挺典型。有一次我把代碼從舊工程移植到新工程速率從 338MB/s 掉到 200MB/s而且怎么調(diào) DMA 參數(shù)都沒用。當(dāng)時排查步驟是第一步先用 Streamer 測速確認(rèn)掉速現(xiàn)象在官方工具上也存在排除上位機(jī)因素。第二步用示波器看 FPGA 和 FX3 之間的 FLAGA 和 SLWR發(fā)現(xiàn) SLWR 的有效占空比不到 50%說明 FPGA 經(jīng)常處于想寫但 FX3 不接收的狀態(tài)問題在 FX3 側(cè)水位配置。第三步查固件里 DMA 通道配置發(fā)現(xiàn)新工程里我復(fù)制了一份舊代碼但 DMA buffer count 被初始化成了 4和之前調(diào)優(yōu)后的 16 不一致。改回 16 之后速率立刻恢復(fù)到 330MB/s 以上。這個案例的教訓(xùn)是參數(shù)不是多就好或者少就好而是要和數(shù)據(jù)流匹配。舊工程的配置在新工程里不一定適用每次粘貼代碼都要認(rèn)真核對每個參數(shù)。后來我在工程里加了一個配置頭文件把 DMA buffer、水印、端點大小全部集中管理再也沒出過這類問題。6.3 調(diào)試工具組合拳最后分享一套調(diào)試工具用法是我在聯(lián)調(diào)時固定使用的組合。第一個是 USB 分析儀式的抓包工具。Windows 下可以用 Wireshark 加 USBPcap插上 USB 3.0 設(shè)備后就可以抓取總線上的 URBs雖然看不到每個包的物理層細(xì)節(jié)但足夠分析吞吐率瓶頸和錯誤包。第二個是邏輯分析儀或示波器。耐心等觸發(fā)條件抓 FLAGA、SLWR、DQ 之間的關(guān)系看看是 FLAG 一直無效還是有效但 FPGA 不寫。第三個是 Cypress 的 USB Control Center方便查看端點信息、廠商描述符以及手動下載固件。第四個是 UsbTreeView 這類 USB 設(shè)備查看工具排查枚舉失敗問題。我的調(diào)試順序一般是先用 UsbTreeView 確認(rèn)枚舉正常再用 Control Center 下載固件并確認(rèn)版本然后用 Streamer 測速測速不達(dá)標(biāo)就上示波器抓接口時序接口時序沒問題就上 Wireshark 抓總線包。按這個順序走完一般都能定位到問題所在。我個人在實際操作中的體會是FPGA 和 FX3 聯(lián)調(diào)最需要的不是靈光一現(xiàn)而是系統(tǒng)性的排查順序和耐心的參數(shù)微調(diào)。338MB/s 這個數(shù)字不是一次就調(diào)出來的中間經(jīng)歷了從 200 到 300 再到 338 的逐步提升每一步改動都很小但每個改動背后都有明確的依據(jù)。這套方案后續(xù)還可以繼續(xù)擴(kuò)展比如把 FPGA 側(cè)搬到 PCIe 上做更高帶寬或者在 FX3 上做多通道采集但 USB 3.0 這個門檻上FX3 的 Slave FIFO 模式基本是繞不開的經(jīng)典路數(shù)。希望這篇文章能幫你少踩幾個我踩過的坑一次就跑到 300MB/s 以上。