
兩年前我第一次在 Xilinx FPGA 上把 FIR 濾波器跑起來那時還是一個通道拿著 MATLAB 算好的系數往 FIR Compiler IP 里面一扔仿真一跑波形對了就覺得萬事大吉。等到后來項目變成四通道同步采集我才發(fā)現(xiàn)事情沒那么簡單通道數從 1 改成 4 只是最表面的操作背后牽涉到數據時序、硬件過采樣、多通道 AXI4-Stream 交織格式、濾波延遲對齊甚至資源優(yōu)化和時序收斂。這篇文章就圍繞“Xilinx FIR IP 核的多通道濾波器設計”這個主題把我實際踩過坑、反復看手冊才搞清楚的內容從零到一完整過一遍。無論你是在做多路 ADC 預處理、通信基帶濾波還是軟件無線電的多通道分集接收這套思路基本都能直接套用。1. 多通道 FIR 濾波器需求與設計思路1.1 FIR 的基本結構和為什么多通道不是簡單的“復制粘貼”FIR 濾波器的本質就是一組帶延遲的乘加運算。對第 n 個輸出采樣點來說它等于輸入序列和濾波器系數序列做卷積y[n] Σ h[k] · x[n-k]其中 k 從 0 到 N-1N 就是濾波器的抽頭數。舉個容易理解的例子如果 N64那么每產生一個輸出樣本都要完成 64 次乘法和 63 次加法。用 FPGA 做實現(xiàn)時思路無非兩種一是把 64 個乘法器并行擺開一個時鐘周期出 64 次乘加結果二是用一個乘法器在時鐘節(jié)拍里循環(huán)使用N 個時鐘周期出一個輸出。前者吞吐率高但 DSP48 資源消耗直接就是 64 個起步后者資源省但對時鐘頻率要求更高。單通道的時候這個選擇比較簡單資源不夠就多花幾個周期時序緊張就多鋪幾個乘法器。多通道就不一樣了四路信號同時采樣通道之間還要保證齊套性。很多人第一反應是把 FIR IP 例化四次一個通道一個 IP。這種方法不是不能用但相當于把四個獨立的濾波器塞進芯片里DSP48、LUT、寄存器成本直接翻四倍。而 FIR Compiler IP 本身提供的多通道模式恰恰就是專門解決這種場景的它允許你在同一個濾波核內部用時分復用方式處理多個通道共享大量算術資源。所以我的觀點是多路 FIR 設計首先應該考慮用官方 IP 的多通道能力而不是簡單復制單通道模塊。這不是為了省事而是在面積、功耗、時序上都有明顯收益。當然也要清楚多通道模式依賴時鐘資源和通道順序管理后面我會詳細展開。1.2 時分復用FIR 多通道實現(xiàn)的核心機制FIR Compiler IP 的多通道模式靠的是時分復用也就是讓多個通道的數據輪流使用同一套乘加器。打個比方單通道 FIR 是一個柜臺只服務一個客戶多通道 FIR 是同一個柜臺四個客戶按順序輪流辦業(yè)務。只要每個人的業(yè)務都足夠快外部看起來就像四人同時被服務一樣。具體到 FPGA 里這個“業(yè)務速度”由系統(tǒng)時鐘和采樣頻率的比值決定。Xilinx 手冊里把這個比值叫“硬件過采樣率”Hardware Over-sample Rate。如果系統(tǒng)時鐘是 100MHz每個通道的采樣率是 1MHz四個通道合在一起每秒需要處理四百萬個樣本。用 100MHz 的時鐘去處理每個樣本的窗口期里有 25 個時鐘周期可用這個 25 就是做過采樣計算后得到的余量。FIR 要完成 64 次乘加在 25 個時鐘周期內做不完于是 IP 會適當增加乘法器并行度直到滿足時序要求。這里有一個實際工程里很關鍵的概念硬件過采樣率決定了共享程度和乘法器數量的折中。過采樣率越高意味著每個樣本可用的時鐘周期越多IP 越傾向于用一個或少數幾個乘法器串行完成累加DSP48 用得就少過采樣率不足那就只能堆并行乘法器。設置 IP 時你可以手動指定過采樣率也可以讓它根據“時鐘頻率”和“采樣頻率”自動計算。我建議大多數場景下先自動計算跑完綜合看資源報告再回頭微調。多通道和時分復用是一對天然的搭檔。每多一個通道需要處理的樣本量就多一倍留給每個樣本的時鐘周期就少一半乘法器并行度就得提上來。這也是為什么多通道 FIR 的資源消耗并不是“按通道數線性增長那么夸張”而是階梯式上漲只要樣本速率和系統(tǒng)時鐘之間還有富余通道增加可能不增加任何 DSP48。1.3 一個具體的設計指標四通道低通濾波器我把這次設計的初始需求定成一個比較典型的工程場景后面所有配置都圍繞它展開通道數4 通道輸入數據位寬16 bit帶符號補碼每個通道采樣率1 MHz系統(tǒng)時鐘100 MHz濾波需求低通濾波器截止頻率 100 kHz阻帶衰減不小于 60 dB目標實現(xiàn)器件Xilinx 7 系列或者 UltraScale 都行這里不限定具體型號用 MATLAB 的firls或fir1設計得到大約 64 階的濾波器系數量化到 16 bit保存成 COE 文件。為什么選 64 抽頭因為在這個指標下64 抽頭的阻帶衰減和過渡帶寬度已經比較合適再多抽頭就意味著更多 DSP48而在 1MHz 采樣率下 16 抽頭和 64 抽頭的實時性壓力其實差不多。有了這個明確的小目標之后接下來最值得花時間的是搞清楚 FIR Compiler IP 內部的各種配置項到底是怎么影響實現(xiàn)的。我們先把原理講透再去 Vivado 里操作。2. FIR Compiler IP 的架構與關鍵配置概念2.1 IP 支持哪幾種濾波器模式FIR Compiler IP 在 Filter Options 頁面里會先讓你選濾波器類型。常見的有 Single Rate、Interpolated、Decimation、Interpolation以及 Xilinx 手冊里專門提到的 Multi-phase 多相濾波器。Single Rate 是標準配置輸入輸出采樣率相同我們做四通道低通就屬于這一類。Interpolation 和 Decimation 則是在濾波同時改變采樣率比如把 48kHz 音頻插值到 192kHz或者在做數字下變頻時把 100MHz 的數據抽取到 10MHz。這類場景下你可能需要配合多相結構來降低運算壓力。我見過不少新手在做一個帶抽取的濾波器時直接在 FIR IP 里選 Decimation然后把系數文件一股腦塞進去結果輸出數據總是不對。原因是抽取模式下IP 對輸入數據的時間槽和輸出樣本的對應關系有嚴格規(guī)定而且通常會要求你先做多相分解再配置。換個更省心的做法是先用一個普通單速率 FIR 濾波再單獨用另一個模塊做抽取兩個 IP 分開設計。雖然資源多一點但調試復雜度低很多。Multi-phase模式才是 FIR IP 真正體現(xiàn)“多相濾波”優(yōu)勢的地方。所謂多相分解就是把一個大濾波器拆成若干個小濾波器并行處理。例如把一個 64 抽頭的低通濾波器按 4 相位分解變成 4 組每組 16 抽頭的子濾波器每個子濾波器在多相輸入序列上分別運行再把結果重新交織回原始速率。這樣做的直接好處是每個乘法器的工作時鐘可以降低到原來的 1/4 或者資源占用大幅下降。Xilinx 的 FIR Compiler 在處理多通道和高速率濾波器時內部也會自動采用類似多相展開的結構所以你在 IP 配置界面看到“multiphase”時不必慌張它只是把你要求的濾波器和時鐘關系在底層拆成了更聰明的實現(xiàn)。2.2 多通道模式下的系數與通道關系FIR Compiler 里關于系數的配置通常有幾層概念系數集合、每個集合里的系數個數、通道如何使用這些集合。對于多數標準應用濾波器抽頭數就是系數集合的個數比如 64 個系數算一組。配置里允許你有多個 Coefficient Set并且可以給不同通道分配不同集合。也就是說通道和系數不是必須一一對應的。你可以讓四個通道共用同一組低通系數也可以讓通道 0 和通道 1 用低通系數通道 2 和通道 3 用高通系數甚至可以給四個通道分別配置四組完全不同的系數。這個能力在做多頻段、多速率系統(tǒng)時非常有用。但代價也很明顯多組系數意味著 IP 內部要有額外的存儲和切換邏輯資源占用會上升而且對接口控制時序的要求更高。我的建議是如果所有通道的濾波特性一致那就老老實實用一組系數讓所有通道共享。這樣既簡單又省資源。只有當指標明確要求每個通道獨立濾波時才去碰多系數集合。還有一個容易被忽略的選項Reloadable Coefficients也就是系數可動態(tài)更新。打開這個選項后軟件處理器可以通過 AXI4-Lite 接口在運行中改寫濾波器系數適合做自適應濾波或頻率切換。但開啟后IP 的面積和時序約束都會變復雜。如果產品階段不需要重新配系數我建議關閉這個選項把系數固定死在 COE 文件里。2.3 多相濾波架構與高速濾波場景“多相濾波”這個詞在網絡論壇和 Xilinx 資料里出現(xiàn)頻率很高但它不是 FIR Compiler 特有的新特性而是一種信號處理優(yōu)化方法。它的核心思想是濾波器系數不是從頭到尾串行處理而是按相位重新排列成多個子濾波器。舉一個具體數字某系統(tǒng)需要采樣率 200MSPS64 抽頭 FIR系統(tǒng)時鐘剛好也是 200MHz。如果直接做每 5ns 內必須完成 64 次乘加硬件壓力很大。如果做 4 相分解每一相子濾波器只處理 16 個抽頭乘法器的數據率其實不需要一下子高到原始采樣率只要最后把四個子濾波器輸出交織起來就能還原出 200MSPS 的濾波結果。這種技術也經常配合插值/抽取使用所以叫多相插值、多相抽取。對 FIR Compiler IP 來說你不需要手動寫多相分解的代碼只需要在配置里給出采樣率、系統(tǒng)時鐘和濾波器系數它會自動判斷是否采用多相結構。但在多通道項目里我還是建議你腦子里有一張“相位圖”當采樣率很高而系統(tǒng)時鐘不夠快時IP 內部必然會把運算拆到多個并行數據路徑上這時通道交織順序、輸出對齊關系都會發(fā)生變化調試時不能用單通道的思維去看波形。如果你非要手動做多相濾波通常需要先把系數分解成多組然后例化多個 FIR Compiler 并自己做輸出交織。這種事情只在極端定制場景下才有必要平常直接用 IP 自帶的優(yōu)化就行。這里我主要是提醒大家看到“多相濾波”不要頭大它反而是你處理高速 FIR 的救星。3. Vivado 實操從創(chuàng)建工程到跑通 FIR IP 核3.1 創(chuàng)建 IP 并導入濾波器系數實際操作是從 Vivado 的 IP Catalog 開始的。在項目管理器里搜索 FIR選擇 FIR Compiler雙擊創(chuàng)建 IP命名成fir_multi_ch。打開配置界面后第一頁是 Filter Options。Filter Type 選 Single RateNumber of Channels 設成 4。這一頁還會要求你選擇過采樣率來源默認是“Determined by Frequency Specification”。我們暫時不改它跳到后面填時鐘頻率和采樣頻率。再往下是系數部分。你可以手動一個個填但工程上一般不這么干。先用 MATLAB 生成系數然后導出成.coe文件。COE 文件的格式很簡單radix16; coefdata04A3, F501, 0032, FF06, 0B5F, ...上面只是示意實際系數要根據濾波器設計結果填寫。在 FIR Compiler 界面選擇 Load Coefficients載入這個 COE 文件后IP 會自動識別抽頭數并在界面上畫出幅度響應預覽。這一步最好養(yǎng)成習慣配置完后先看一眼幅度響應曲線確認帶內平坦、阻帶衰減符合指標再去生成例化代碼。我遇到過同事載入系數后沒檢查結果系數導出時本來就有格式問題到板子上才折騰半天才發(fā)現(xiàn)是系數反了。如果需要讓多個通道使用不同系數就在系數頁面里把 Coefficient Sets 數量改成通道數再分別加載不同的 COE 文件。我前面建議過初期盡量一組系數。3.2 通道規(guī)格、位寬與輸出量化設置接下來是 Channel Specification 和 Sample Specification。這里有幾個選項必須認真選Input Sample Type選 Signed絕大多數 ADC 輸出的模擬采樣值是帶符號的。Input Width16。如果輸入是 14 bit 或 12 bit我建議仍然按 16 bit 接進 IP然后在高位對齊低位補零。Coefficient Width選 16。Output Width默認 FP 全精度會給出一個較寬的位寬。如果后面接的模塊不需要這么多位可以改成自定義位寬。輸出位寬導致“數據看起來不對”是最常見的坑之一尤其是當你選擇了 Truncation 而不是 Round 時輸出會比全精度結果在幅度上低一些波形形狀沒有大變化但和 MATLAB 仿真結果對不上的時候往往就是量化策略造成的。仿真驗證階段我建議直接選 Full Precision先保證算法鏈路正確再根據最終位寬需求做截斷或舍入。Sample Frequency 填 1MHzClock Frequency 填 100MHzIP 會自動算出每通道的硬件過采樣率。四通道模式下總樣本率為 4MHz100MHz 系統(tǒng)時鐘對應每個樣本窗口有 25 個周期所以對于 64 抽頭濾波器它會在內部用 3 到 4 個乘法器把 64 次乘加分攤完。如果你在這里發(fā)現(xiàn) IP 給的 DSP48 估算數量遠超預期可以加大系統(tǒng)時鐘頻率或者減小抽頭數也可以在硬件過采樣率上做合并調整。反過來如果資源很寬裕但時序吃緊可以降低過采樣率讓更多 DSP 并行工作換取更短的組合邏輯鏈。3.3 AXI4-Stream 接口配置與連接完整數據通路FIR Compiler IP 外部接口默認采用 AXI4-Stream這在現(xiàn)代 Xilinx 設計里是事實標準前端數據源、后端的 DMA 或 FFT IP 核全都用這套協(xié)議。AXI4-Stream 的四個基礎信號是s_axis_data_tvalid主設備告訴從設備本次送的數據有效。s_axis_data_tready從設備告訴主設備當前可以接收數據。s_axis_data_tdata實際數據總線。s_axis_data_tlast幀結束標志用于多通道多幀場景。需要特別注意FIR Compiler 的多通道數據不是把四個通道分別放在tdata的不同 bit 段里而是在多個時鐘周期里按通道順序依次送出。這是很多人最容易搞錯的地方。正確格式是第一個有效周期里tdata是通道 0 的樣本第二個周期是通道 1 的樣本第三個是通道 2第四個是通道 3然后tlast拉高表示一幀結束下一組樣本又從通道 0 開始。整個過程中tvalid可以一直保持高電平tready由 IP 決定。在頂層代碼里例化 IP 時復位信號通常叫s_axis_aresetn低電平有效。如果你用的是高有效復位一定自己取反。時鐘信號是s_axis_aclk和整個邏輯域的時鐘接在一起即可。輸出端的m_axis_data_tdata同樣按通道順序交織m_axis_data_tvalid拉高時表示輸出數據有效。此外 IP 還會給出m_axis_data_sync信號這個信號在每幀的第一個有效輸出周期拉高用來標記通道 0 對齊位置后面做多通道校驗時特別好用。如果你的數據源是某個 ADC 接口 IP那大概率它已經按 AXI4-Stream 輸出你只需要確保它的通道排列順序和 FIR IP 期望的通道排列順序一致。如果順序不一致在 FIR 前面加一個重新排序的小模塊比在 FIR 后面補救要簡單得多因為 FIR 有延遲通道錯位在輸出端不太好查。4. 多通道仿真驗證讓每一路數據都對齊4.1 Testbench 怎么生成四通道交錯數據仿真驗證是整個多通道 FIR 設計里最見功力的一步。單通道可以隨便給個正弦波看看幅度幅度對不對就完事。四通道則要先在 Testbench 里正確生成按通道交織的數據序列。簡單來說Testbench 要模擬一個數據源每四個時鐘周期組成一個樣本幀依次發(fā)送通道 0 到通道 3 的樣本。下面是一段很常用的行為級驅動代碼可以直接拿去改reg [15:0] sample_mem [0:3]; reg [15:0] sample_axis_tdata; reg sample_axis_tvalid; reg sample_axis_tlast; integer ch_index; always (posedge clk) begin if (!rst_n) begin ch_index 0; sample_axis_tvalid 1b0; sample_axis_tlast 1b0; end else if (s_axis_tready) begin sample_axis_tvalid 1b1; sample_axis_tdata sample_mem[ch_index]; sample_axis_tlast (ch_index 3); if (ch_index 3) ch_index 0; else ch_index ch_index 1; end end我這個寫法是用一個計數器在 0、1、2、3 之間循環(huán)tlast在通道 3 時拉高。你每次更新sample_mem的內容模擬四路 ADC 分別送新樣本就行。實際工程中 ADC 的采樣時鐘如果和 FIR 的系統(tǒng)時鐘不一致你還需要做異步 FIFO 跨時鐘不能簡單用寄存器的時序邏輯驅動。有了正確的激勵后仿真里最先看的是s_axis_tready。如果這個信號始終為低說明 IP 沒有進入可接收狀態(tài)常見原因是復位沒有正確釋放或者是時鐘沒有跑起來。先把這兩個基本問題排除再去分析濾波結果。4.2 輸出檢查延遲、通道順序和 sync 信號FIR 濾波器本身有加法器樹和流水線延遲再加上多通道時分復用輸出相對輸入會有明顯延遲。調試的第一步不是拿第一個輸出和第一個輸入對上而是先找m_axis_data_sync信號。這個信號拉高對應的輸出樣本就是通道 0 當前幀的第一個樣本。對四通道設計建議同時抓四路解交織后的通道數據和 sync 信號的相對關系。在波形窗口里用一個 generate 或手寫一個解交織寄存器組把四個通道分別緩存起來。然后檢查每個通道的波形形狀是否正常低通濾波后不應出現(xiàn)高頻毛刺。四個通道的基帶信號相位是否和預期一致。如果輸入是同一信號源四路輸出應該幾乎同步只有固定群延遲。通道 0 的延遲就是 IP 的固有延遲可以在 vivado 的 IP 配置摘要里查到 latency 估算用它來校準你的解交織邏輯。如果發(fā)現(xiàn)通道 1 的輸出和通道 0 一樣但通道 2、通道 3 沒波形八成不是 FIR IP 的問題而是你 Testbench 的通道數據本來就沒送全。先檢查sample_mem四個地址是否有更新再看tlast是否按幀結束。4.3 仿真中常見的三個“假故障”我總結過仿真多通道 FIR 時最容易遇到的三個異常它們表面上像是濾波壞了其實都出在接口或測試環(huán)境上第一個是“輸出全是常量”。原因通常是s_axis_data_tvalid一直為低IP 內部沒有新的樣本進來輸出自然保持不變。這種問題可以抓 IP 的s_axis_tready和tvalid如果握手一直沒有成立就是數據源沒發(fā)對。第二個是“輸出波形有跳變但不平滑”。這種一般發(fā)生在輸入樣本隨機數生成或者數據位寬沒有對齊時。重點檢查發(fā)送端的tdata是否按補碼格式送數。如果本來是帶符號的 ADC 采樣值你用無符號整數去驅動波形可能就出現(xiàn)一條類似“削頂”的跳變。第三個是“通道順序錯亂”。你以為通道 0 的輸出在tdata的第一段于是從低位解交織結果取出來的是通道 1 的數據。這種問題很難通過波形輪廓看出來必須用四個不同的正弦波頻率做激勵再在接收端分別檢查頻率成分。只要每個通道拿到的信號頻率和激勵聲明相符就能確認通道順序正確。5. 資源、時序與工程化落地經驗5.1 位寬選擇和 DSP48 資源估算FIR 濾波器的硬件開銷大頭永遠是 DSP48 乘法器。一個 16bit×16bit 的乘加需要一個 DSP48這個基本是鐵的定律。64 抽頭的單通道 FIR理想情況下至少需要 64 個乘法器。如果濾波器系數對稱即 h[k]h[N-1-k]IP 會做系數折疊把乘法器需求砍掉近一半只需要約 33 個乘法器。多通道不代表乘法器數量乘以通道數。四通道 64 抽頭如果系統(tǒng)時鐘足夠快使每個通道都能時分復用同一套乘加器DSP48 可能只比單通道多一點點甚至持平。你可以通過調整硬件過采樣率來控制這個比例。過采樣率高資源少但時鐘周期緊過采樣率低資源多但組合邏輯輕松一些。輸出位寬對面積的影響沒有 DSP48 那么強但也不容小覷。如果選擇 32bit 全精度輸出后面接一級更寬的累加邏輯面積會明顯上升。我的習慣是在系統(tǒng)鏈路允許的前提下盡早截位但要在截位之前先保留足夠位數避免帶內信號的動態(tài)范圍被壓縮。具體截到多少位要結合 ADC 有效位數和最終信噪比指標來決定。5.2 時序收斂與跨時鐘域處理多通道 FIR 設計本身比較規(guī)矩時序問題更多出在它和數據源、數據宿的接口邊界上。系統(tǒng)時鐘設為 100MHzFIR 內部是干凈的同步時序加上寄存器級數很多一般不會成為關鍵路徑。反而是在多路 ADC 采樣數據和 FIR 的時鐘域交匯處如果直接拿異步信號打拍綜合工具會報告一堆時序違例。常見解決辦法是在 FIR 之前加異步 FIFO 或使用 Xilinx 原語做跨時鐘域處理。如果 ADC 數據是 4 通道并行總線先把四路信號各自同步到系統(tǒng)時鐘域再組裝成 AXI4-Stream 交織格式送進 FIR。這一步無論如何不能省省了不僅時序難收斂板級調試時還會看到隨機毛刺。后級如果接的是 DMA 或 PCIe 之類的接口 IP你還要注意 AXI4-Stream 的帶寬匹配。4 通道 × 1MHz × 32bit 輸出大約是 16MB/s 左右PCIe DMA 完全沒壓力。但如果你把采樣率提高幾十倍那 DMA 吞吐和 FIFO 深度就要重新評估FIR 本身反而不會成為瓶頸。5.3 前后端接口、DMA/高速收發(fā)器聯(lián)調的實際體會在實際項目里多通道 FIR 很少是獨立存在的。前面可能接 JESD204B 的高速 ADC后面可能接 FFT IP 或 PCIe DMA。你的 FIR 設計最終要能塞進這條大鏈里。我吃過一個虧把 FIR IP 的 AXI4-Stream 輸出直接接到 DMA 的輸入通道順序沒仔細查結果上位機里看到的四個通道數據其實是錯位的。后來修改 FIR 輸出后的解交織模塊才解決。所以在你把 FIR 集成進整體系統(tǒng)前一定先單獨做多通道仿真把通道序號在硬件上驗證明白。等到和 DMA 聯(lián)調之后再發(fā)現(xiàn)問題定位成本就高多了。另外如果項目里有 Aurora、SGMII 這類高速收發(fā)器把數據遠距離傳輸通道交織格式往往會受到傳輸層幀格式的影響。比如你的傳輸協(xié)議每次傳一個 AXI4-Stream 幀而幀里剛好包含四通道樣本那 FIR 的 tlast 就要和傳輸層的幀邊界對齊。這種對齊問題在系統(tǒng)調試階段特別煩人但只要仿真階段把 tlast 的時序校準好上板后就只是接線的活了。最后再分享一個經驗做多通道 FIR 項目無論多忙我都會先把四路輸入用四種不同頻率的測試信號跑一遍行為仿真再跑一遍綜合后仿真最后才上板。前兩步能解決絕大多數邏輯問題第三步往往只驗證接口電氣信號和真實噪聲。這套流程看起來繁瑣但比板級示波器現(xiàn)查波形快得多。等你真的遇到了又玄又難查的通道錯亂問題就會感謝當時這個小小的習慣。