處理:從DDC到同步的完整實(shí)戰(zhàn)指南)
1. 從通信系統(tǒng)到FPGA基帶與中頻到底在做什么做了這么多年FPGA被問(wèn)得最多的一句話是“基帶和中頻到底有什么區(qū)別”不少剛?cè)胄械呐笥寻堰@兩個(gè)詞掛在嘴邊但真到了寫(xiě)代碼、定架構(gòu)的時(shí)候又開(kāi)始犯迷糊。我先用自己的理解把這件事兒講清楚——基帶和中頻不是兩個(gè)孤立的概念它們是通信收發(fā)鏈路里兩個(gè)相對(duì)的信號(hào)處理階段?;鶐盘?hào)本質(zhì)上是原始信息所在的頻段。它可能是一個(gè)碼流、一組符號(hào)也可能是一串經(jīng)過(guò)調(diào)制映射后的復(fù)數(shù)數(shù)據(jù)?;鶐幚淼闹攸c(diǎn)是“信息”本身——怎么編碼、怎么映射、怎么擴(kuò)頻、怎么均衡、怎么判決。中頻信號(hào)則是把基帶信號(hào)搬到某個(gè)固定的載波頻率上方便后續(xù)變頻、濾波、放大和發(fā)射。中頻存在的意義在于直接在天線頻率上做處理難度太大而零中頻方案又有直流偏置、本振泄漏這些麻煩事兒所以先到一個(gè)“中間頻率”把信號(hào)理順再交給射頻前端。FPGA之所以在基帶和中頻處理里這么吃香核心就三個(gè)字并行性。一個(gè)OFDM符號(hào)里有幾十上百個(gè)子載波每路子載波都要同時(shí)做乘法累加這種天然并行的結(jié)構(gòu)ARM和DSP跑起來(lái)要循環(huán)幾百上千次FPGA一拍就搞定。另一個(gè)關(guān)鍵點(diǎn)是接口靈活性——FPGA可以直接對(duì)接ADC/DAC的高速JESD204B接口也可以掛一片STM32做控制面交互還能用FMC口擴(kuò)展各種子卡這種“我要什么接口就可以自己拼”的能力是ASIC和通用處理器都替代不了的。這篇文章的內(nèi)容會(huì)比較具體適合兩類人看一類是做通信物理層開(kāi)發(fā)的工程師想系統(tǒng)梳理基帶和中頻鏈路在FPGA上的算法落地方案另一類是剛接觸FPGA信號(hào)處理的在校學(xué)生或者轉(zhuǎn)行入門(mén)者想搞清楚FIR濾波器、數(shù)字下變頻、CIC抽取、同步檢波這些東西到底該怎么設(shè)計(jì)和調(diào)試。我盡量少講空洞的理論多給能直接用的架構(gòu)方案、參數(shù)計(jì)算過(guò)程和踩坑記錄。2. 整體設(shè)計(jì)與思路拆解為什么FPGA方案這樣搭2.1 基帶和中頻鏈路的典型功能劃分先看一個(gè)典型的數(shù)字接收機(jī)鏈路結(jié)構(gòu)這也是FPGA工程里最常見(jiàn)的一種劃分方式前端ADC采樣進(jìn)來(lái)的是中頻模擬信號(hào)數(shù)字化后的數(shù)據(jù)頻率通常在幾十MHz到幾百M(fèi)Hz范圍。FPGA這邊第一步做數(shù)字下變頻DDC把中頻搬到基帶第二步做抽取濾波把高采樣率降到符號(hào)速率等級(jí)的較低速率接下來(lái)是匹配濾波、同步、均衡、解映射這些就是基帶處理的內(nèi)容了。發(fā)射方向剛好反過(guò)來(lái)——基帶側(cè)完成編碼、調(diào)制、脈沖成形然后插值、數(shù)字上變頻DUC最后送給DAC。這個(gè)架構(gòu)設(shè)計(jì)的核心思路是“高速在前、低速在后”。中頻側(cè)數(shù)據(jù)率往住是幾百M(fèi)sps的級(jí)別每一次乘加都要消耗真實(shí)的硬件資源所以這里盡量少做復(fù)雜運(yùn)算只做必要的搬頻和濾波?;鶐?cè)數(shù)據(jù)率降下來(lái)了做均衡、做同步就可以用更精細(xì)的算法因?yàn)橐粋€(gè)符號(hào)周期內(nèi)的時(shí)鐘周期多了可以分時(shí)復(fù)用運(yùn)算單元。這個(gè)“降速換取算法復(fù)雜度”的思路是整個(gè)通信FPGA設(shè)計(jì)里最核心的工程理念。2.2 為什么用FPGA而不是DSP或ARM很多人在方案選型的時(shí)候糾結(jié)FPGA、DSP、ARM到底怎么選我的經(jīng)驗(yàn)是這么判斷的——當(dāng)你的算法里存在大量逐樣本級(jí)別的乘加運(yùn)算、并行處理、流水線結(jié)構(gòu)時(shí)選FPGA當(dāng)你的算法是塊級(jí)別的矩陣操作、浮點(diǎn)遞歸迭代、復(fù)雜的C代碼流程控制時(shí)選DSP當(dāng)你只是做控制、配置、協(xié)議棧時(shí)選ARM/STM32就足夠了。實(shí)際項(xiàng)目里往往是混合架構(gòu)。我做過(guò)一個(gè)SDR平臺(tái)用STM32H743做控制面負(fù)責(zé)網(wǎng)口通信、參數(shù)配置、狀態(tài)監(jiān)控FPGA負(fù)責(zé)數(shù)據(jù)面的DDC、DUC、濾波、同步兩片芯片之間就用FMC總線通信。很多人問(wèn)FMC怎么實(shí)現(xiàn)其實(shí)FPGA側(cè)就是掛一塊雙口RAMSTM32側(cè)配置FSMC/FMC控制器的時(shí)序參數(shù)把FPGA的BRAM映射到STM32的地址空間里兩邊讀寫(xiě)就像操作自己的數(shù)組一樣簡(jiǎn)單。這個(gè)方案的好處是控制面和數(shù)據(jù)面徹底隔離調(diào)試的時(shí)候互不干擾。2.3 方案選型的三條經(jīng)驗(yàn)法則做基帶中頻FPGA方案時(shí)我總結(jié)了三條經(jīng)驗(yàn)法則也算是我被反復(fù)毒打之后的教訓(xùn)第一先定數(shù)據(jù)率再定算法復(fù)雜度。系統(tǒng)采樣率定了ADC位數(shù)定了FPGA的資源消耗基本就定了大概。如果采樣率太高、算法太重優(yōu)先考慮把部分處理用模擬前端來(lái)實(shí)現(xiàn)而不是全推給FPGA硬扛。第二能用定點(diǎn)絕不用浮點(diǎn)。很多工程師從MATLAB過(guò)渡到FPGA習(xí)慣性想拿浮點(diǎn)IP核直接做結(jié)果DSP48E1被吃光、LUT被吃光、時(shí)序還跑不過(guò)。基帶和中頻算法里大多數(shù)場(chǎng)景使用16位定點(diǎn)甚至更低位寬性能損失很小資源節(jié)省巨大。第三成熟IP核優(yōu)先不要什么都自己造。Xilinx和Intel都提供了免費(fèi)的FIR Compiler、CIC Compiler、NCO、FFT這些IP核性能和資源都優(yōu)化過(guò)。自己用Verilog手寫(xiě)FIR不是不行但一般來(lái)說(shuō)性能還不如IP核調(diào)試周期卻要長(zhǎng)好幾倍。3. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)基帶中頻常用算法怎么落地3.1 數(shù)字下變頻DDC的實(shí)現(xiàn)要點(diǎn)DDC的標(biāo)準(zhǔn)結(jié)構(gòu)是NCO產(chǎn)生本振信號(hào)然后分別和輸入的I/Q兩路信號(hào)做混頻混頻之后過(guò)低通濾波器再抽取降速。FPGA實(shí)現(xiàn)的時(shí)候NCO用Xilinx的DDS IP核或者自己寫(xiě)一個(gè)相位累加器都可以但要注意幾個(gè)關(guān)鍵細(xì)節(jié)。首先是本振頻率的精度。NCO的輸出頻率公式是fout fclk * 相位增量 / 2^相位位寬這個(gè)概念看起來(lái)很簡(jiǎn)單但很多新手在這里翻車。比如系統(tǒng)時(shí)鐘100MHz想產(chǎn)生30MHz的本振NCO位寬32位相位增量就是30M / 100M * 2^32 1288490188.8取整后實(shí)際頻率為29.99999996MHz還是30.00000001MHz取決于取整方式。除非你有特殊要求否則這個(gè)誤差完全可以忽略但如果相位增量取整錯(cuò)了頻率誤差就可能到幾kHz甚至幾百Hz。其次是混頻器的位寬管理?;祛l是乘法運(yùn)算兩個(gè)16位數(shù)相乘的結(jié)果是32位如果直接送給濾波器資源立刻翻倍。我的習(xí)慣是混頻之后立刻截位到18到20位再進(jìn)濾波器。截位帶來(lái)的量化噪聲可以接受因?yàn)楹罄m(xù)濾波器本身就會(huì)把帶外噪聲干掉。最后是抽取的順序問(wèn)題。正確的順序是“先濾波再抽取”因?yàn)橹苯映槿?huì)導(dǎo)致頻譜混疊——原來(lái)鏡像在fs/2附近的頻譜會(huì)被折疊到基帶。FIR Compiler IP核里有內(nèi)置的抽取功能直接把抽取因子填進(jìn)去就行它內(nèi)部會(huì)自動(dòng)處理防混疊濾波和抽取的關(guān)系。3.2 FIR濾波器設(shè)計(jì)參數(shù)計(jì)算怎么不踩坑在做中頻濾波和基帶成形濾波時(shí)FIR濾波器是繞不開(kāi)的基礎(chǔ)模塊。設(shè)計(jì)一個(gè)FIR濾波器真正折磨人的不是調(diào)IP核而是把指標(biāo)定清楚。以中頻信號(hào)帶寬1.2MHz、采樣率61.44MHz的常見(jiàn)配置為例。如果我只想留出中心頻率±600kHz的信號(hào)其他全部濾掉濾波器的通帶邊界大約在500kHz阻帶邊界在800kHz。通帶紋波要求小于0.1dB阻帶抑制大于60dB。這些要求扔給MATLAB用fdatool或者filterDesigner工具自動(dòng)生成系數(shù)再計(jì)算需要的階數(shù)——大概需要128到256階取決于過(guò)渡帶的陡峭程度。階數(shù)確定后有幾個(gè)和FPGA實(shí)現(xiàn)直接相關(guān)的坑要注意。第一系數(shù)個(gè)數(shù)和DSP48資源的關(guān)系。一個(gè)128階對(duì)稱FIR利用對(duì)稱性只需要64個(gè)乘法器每個(gè)DSP48可以做到一個(gè)乘法器資源評(píng)估的時(shí)候別按128來(lái)計(jì)算。第二并行度問(wèn)題。在61.44MHz采樣率下128階濾波器對(duì)DSP48的消耗是128個(gè)但如果我把4個(gè)并行輸入合并成1個(gè)做時(shí)分復(fù)用DSP48就能降到32個(gè)代價(jià)是流水線深度增加、邏輯延遲變大。這里沒(méi)有標(biāo)準(zhǔn)答案全看你的資源預(yù)算是多少。從工程實(shí)踐來(lái)看把FIR Compiler IP核配置成“多通道時(shí)分復(fù)用”模式在低速率基帶處理中經(jīng)常能省下一半以上的DSP資源效果非常顯著。3.3 CIC濾波器抽取倍數(shù)大時(shí)的好幫手當(dāng)抽取倍數(shù)超過(guò)8倍甚至16倍時(shí)FIR濾波器需要的階數(shù)會(huì)高到不合理。舉個(gè)例子從61.44MHz降到1.92MHz抽取32倍如果用FIR做主抽取濾波器過(guò)渡帶要非常窄濾波器階數(shù)輕松上千DSP資源會(huì)爆炸。這時(shí)就該CIC濾波器登場(chǎng)了。CIC濾波器的原理是級(jí)聯(lián)積分器和梳狀濾波器它不需要乘法器全用加法器實(shí)現(xiàn)結(jié)構(gòu)極其規(guī)整天然適合大倍數(shù)抽取。代價(jià)是它的通帶不是完全平坦的有落降。以32倍抽取為例CIC的通帶衰減可能達(dá)到3dB以上必須在后面級(jí)聯(lián)一個(gè)等化FIR濾波器來(lái)做補(bǔ)償。FPGA里做CIC抽取用Xilinx的CIC Compiler IP核最方便。配置的時(shí)候注意幾點(diǎn)微分延遲一般選1最大微分延遲選2就行級(jí)聯(lián)階數(shù)Number Of Stages通常在3到6之間階數(shù)越高阻帶抑制越好但通帶落降越大輸入輸出位寬要仔細(xì)算防止中間級(jí)溢出。CIC內(nèi)部增益公式是Gain (抽取倍數(shù) * 微分延遲)的級(jí)聯(lián)階數(shù)次方這個(gè)增益在抽取后會(huì)體現(xiàn)在輸出數(shù)據(jù)的字長(zhǎng)增長(zhǎng)上配置IP核時(shí)它會(huì)自動(dòng)幫你算好但給后端模塊傳數(shù)據(jù)時(shí)位寬千萬(wàn)別截太多。3.4 中頻檢波算法包絡(luò)檢波、同步檢波和正交檢波中頻信號(hào)處理還有一個(gè)經(jīng)典場(chǎng)景是檢波也就是從已調(diào)中頻信號(hào)里恢復(fù)出原始的調(diào)制信息。網(wǎng)絡(luò)上經(jīng)常有人問(wèn)“中頻檢波有幾種方法”我在項(xiàng)目中實(shí)際用過(guò)的有三種包絡(luò)檢波、同步檢波、正交檢波。包絡(luò)檢波最簡(jiǎn)單——取信號(hào)的絕對(duì)值然后低通濾波就能得到包絡(luò)。本質(zhì)上這是非相干解調(diào)抗噪聲性能一般但勝在結(jié)構(gòu)簡(jiǎn)單、穩(wěn)健尤其適合調(diào)幅信號(hào)或者FSK信號(hào)的幅度比較應(yīng)用。FPGA實(shí)現(xiàn)包絡(luò)檢波就是一個(gè)ABS運(yùn)算加一個(gè)FIR低通資源消耗極低。同步檢波需要恢復(fù)本地載波對(duì)載波相位同步有要求適用于抑制載波的雙邊帶信號(hào)、殘留邊帶信號(hào)。它的輸出比包絡(luò)檢波干凈信噪比也更好但實(shí)現(xiàn)復(fù)雜度上了一個(gè)臺(tái)階——要有載波恢復(fù)環(huán)路或至少要有導(dǎo)頻輔助的相位校準(zhǔn)模塊。正交檢波則是把輸入的實(shí)信號(hào)分成I/Q兩路分別檢波最后求出平方根恢復(fù)幅度。在FPGA里做這個(gè)就有點(diǎn)講究了求平方根可以直接調(diào)CORDIC IP核或者用查表法近似。效果方面正交檢波對(duì)相位不敏感、抗衰落能力強(qiáng)在衰落信道下表現(xiàn)最好。項(xiàng)目里如果要求高直接上正交檢波如果只是為了信號(hào)強(qiáng)度指示包絡(luò)檢波完全夠用。3.5 FPGA里的同步與捕獲從相關(guān)運(yùn)算到幀同步基帶處理中對(duì)性能影響最大的模塊我認(rèn)為是同步。它包含載波同步、符號(hào)同步、幀同步每一層都有單獨(dú)的算法鏈路。FPGA因?yàn)榭梢宰尪鄠€(gè)相關(guān)器并行工作做同步捕獲的優(yōu)勢(shì)非常明顯。以擴(kuò)頻通信為例。接收端先要做偽碼捕獲本質(zhì)是一個(gè)滑動(dòng)相關(guān)過(guò)程——接收信號(hào)和本地偽碼做互相關(guān)峰值超過(guò)門(mén)限就認(rèn)為捕獲完成。在FPGA里我們不會(huì)用“滑”的笨辦法而是用匹配濾波器結(jié)構(gòu)把接收信號(hào)延遲線存儲(chǔ)本地偽碼的所有相位同時(shí)做相關(guān)一次并行輸出所有相關(guān)峰。這種并行相關(guān)的速度比串行滑動(dòng)快了N倍N等于偽碼長(zhǎng)度代價(jià)是資源開(kāi)銷線性增加。符號(hào)同步在FPGA里最常用的方案是Gardner算法——它不需要額外的導(dǎo)頻符號(hào)利用相鄰符號(hào)的差分來(lái)做定時(shí)誤差檢測(cè)。Gardner算法在低信噪比下仍能穩(wěn)定收斂實(shí)現(xiàn)也簡(jiǎn)單就是兩路插值濾波器加一個(gè)環(huán)路濾波器。環(huán)路濾波器的參數(shù)設(shè)計(jì)直接決定同步環(huán)路的收斂速度和抖動(dòng)增益系數(shù)太大收斂快但噪聲大太小則收斂太慢——這塊沒(méi)有捷徑只能通過(guò)仿真調(diào)參。幀同步相對(duì)簡(jiǎn)單一些一般是用已知的幀頭序列和接收序列做相關(guān)相關(guān)值超過(guò)門(mén)限就對(duì)齊輸出。在OFDM系統(tǒng)里幀同步和符號(hào)同步往往要聯(lián)合起來(lái)做把前導(dǎo)序列的相關(guān)峰位置當(dāng)作FFT窗口定位的依據(jù)這一塊在IP核里一般沒(méi)有現(xiàn)成方案需要自己寫(xiě)Verilog或者用System Generator搭配HDL。我實(shí)際做的時(shí)候更多是先用MATLAB把算法模型跑通、把最佳門(mén)限和窗口位置定下來(lái)再在FPGA里用定點(diǎn)數(shù)模擬同樣的邏輯——這樣至少能把BUG留在仿真階段而不是板卡調(diào)試時(shí)候。4. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)一個(gè)可復(fù)現(xiàn)的中頻DDC工程示例4.1 需求與指標(biāo)先定清楚光講概念不如給一個(gè)完整的工程示例。我?guī)н^(guò)很多學(xué)生上手FPGA信號(hào)處理第一課從來(lái)不是寫(xiě)代碼而是先讓他們把指標(biāo)定下來(lái)“你的一段代碼到底要滿足什么指標(biāo)”指標(biāo)不清后面全都是白干。就以一個(gè)通用的中頻數(shù)字化接收DDC模塊為例。假設(shè)ADC采樣率fs 61.44MHzADC位寬14位中頻載波fIF 15.36MHz信號(hào)帶寬BW 1.2MHz要求輸出基帶I/Q數(shù)據(jù)數(shù)據(jù)率降低到1.92MHz。先用筆算一下設(shè)計(jì)要點(diǎn)。NCO本振頻率就設(shè)為15.36MHz——?jiǎng)偤檬遣蓸勇实乃姆种?。這里有個(gè)偷懶的做法當(dāng)本振頻率等于fs/4時(shí)NCO輸出序列正好是[1, 0, -1, 0, 1…]I路和[0, -1, 0, 1, 0…]Q路換句話說(shuō)混頻這個(gè)乘法直接就變成了“取反、置零、透?jìng)鳌钡慕M合邏輯壓根不需要真正的乘法器。這種運(yùn)算量幾乎為零的混頻沒(méi)有理由不用。如果你算出來(lái)的中頻值剛好是fs/4那真是神仙配置。不過(guò)實(shí)際工程里中頻不總是fs/4比如中頻14.64MHz這種值就不好化簡(jiǎn)了這時(shí)老老實(shí)實(shí)做法就是DDS IP核產(chǎn)生正弦余弦序列然后做復(fù)數(shù)乘法混頻。復(fù)數(shù)乘法的FPGA實(shí)現(xiàn)是4個(gè)乘法器加2個(gè)加法器位寬控制在合適范圍內(nèi)資源也是可控的。4.2 工程模塊劃分與端口規(guī)劃把DDC工程按功能拆成幾個(gè)模塊每個(gè)模塊單獨(dú)仿真、單獨(dú)測(cè)試這是避免調(diào)試地獄的關(guān)鍵方法。頂層ddc_top ├── nco_instDDS IP核產(chǎn)生cos和sin本振 ├── mixer_inst復(fù)數(shù)混頻輸出18位I/Q數(shù)據(jù) ├── cic_instCIC抽取濾波抽取8倍抑制混疊 ├── fir_inst等化FIR濾波補(bǔ)償CIC通帶落降并進(jìn)一步濾除帶外噪聲 └── data_align_inst位寬截取和輸出對(duì)齊輸出16位I/Q基帶數(shù)據(jù)在這個(gè)架構(gòu)里NCO的相位增量配置是關(guān)鍵計(jì)算。fpga的DDS IP核如果配置的是頻率分辨率模式需要在界面里填System Clock61.44MHz、Desired Output Frequency15.36MHzIP核會(huì)自動(dòng)算出相位增量。配置完成之后仿真看下頻譜如果看到輸出頻率有稍微偏差考慮一下是否因?yàn)榻匚粚?dǎo)致的相位抖動(dòng)屬于正常現(xiàn)象不影響整體性能。CIC這里配置為CIC抽取8倍微分延遲1級(jí)聯(lián)階數(shù)4輸出位寬自動(dòng)增長(zhǎng)。從61.44MHz抽取8倍后是7.68MHz還沒(méi)到最終的1.92MHz所以后面再用一個(gè)抽取倍數(shù)為4的FIR濾波器。這個(gè)FIR除了抽取還負(fù)責(zé)補(bǔ)償CIC的通帶落降——CIC的頻響形狀近似于sinc函數(shù)需要在通帶內(nèi)做反向整形。用MATLAB的fdatool可以設(shè)計(jì)出這個(gè)補(bǔ)償濾波器系數(shù)通帶截止約600kHz阻帶抑制約50dB階數(shù)32到64階足夠。這里切忌直接FIR抽取倍數(shù)設(shè)置過(guò)大否則階數(shù)會(huì)爆炸。4.3 關(guān)鍵Verilog代碼片段和綜合實(shí)現(xiàn)混頻器模塊的核心代碼實(shí)際上非常簡(jiǎn)單。四分之一中頻采樣的場(chǎng)景下I/Q混頻沒(méi)有乘法器直接按采樣時(shí)鐘的相位n % 4來(lái)做選擇。// 四分之一樣率混頻fs4fIF // 相位索引: 0,1,2,3 分別對(duì)應(yīng) cos: 1,0,-1,0 ; sin: 0,-1,0,1 always (posedge clk) begin if (phase 2d0) begin i_out adc_in; q_out 16d0; end else if (phase 2d1) begin i_out 16d0; q_out -adc_in; end else if (phase 2d2) begin i_out -adc_in; q_out 16d0; end else begin i_out 16d0; q_out adc_in; end phase phase 2d1; end這段代碼沒(méi)有乘法器綜合出來(lái)就是幾組多路選擇器和取反邏輯。如果你用的是非四分之一中頻那就老老實(shí)實(shí)調(diào)用DDS IP核再加兩個(gè)乘法器做混頻。乘法器輸出位寬我習(xí)慣做到輸入位寬加本振位寬再溢出個(gè)一兩比特之后立即截位避免后面濾波器資源爆炸。FIR等化抽取濾波器直接調(diào)Xilinx FIR Compiler IP核配置4倍抽取系數(shù)來(lái)自MATLAB生成的.coe文件。注意FIR Compiler在抽取模式下輸出的數(shù)據(jù)速率是輸入的四分之一接口上會(huì)有一個(gè)DVData Valid信號(hào)標(biāo)示有效數(shù)據(jù)——這一點(diǎn)新手經(jīng)常忽略直接把連續(xù)數(shù)據(jù)往FIFO里寫(xiě)結(jié)果FIFO寫(xiě)滿數(shù)據(jù)錯(cuò)位。一定記住FIR Compiler抽取之后不再是每個(gè)時(shí)鐘都有效必須用它的DV信號(hào)去驅(qū)動(dòng)后面的存儲(chǔ)和運(yùn)算模塊。4.4 仿真協(xié)同驗(yàn)證與板級(jí)實(shí)測(cè)很多工程問(wèn)題根源在于MATLAB模型和FPGA實(shí)現(xiàn)不一致。我有幾條實(shí)操紀(jì)律第一算法模型在MATLAB里先跑通量化指標(biāo)誤碼率、EVM、頻譜泄漏先達(dá)標(biāo)再動(dòng)手寫(xiě)RTL。第二RTL仿真時(shí)用到MATLAB導(dǎo)出的激勵(lì)作為輸入然后用$fwrite寫(xiě)入文件再拿回MATLAB對(duì)比分析。寫(xiě)一個(gè)簡(jiǎn)單的testbench直接喂數(shù)據(jù)、捕獲輸出做回來(lái)MATLAB繪制頻譜和星座圖這個(gè)方法幾乎是我每做一個(gè)通信模塊都會(huì)用的“標(biāo)配”。第三別急著上板。先把DDC模塊在Vivado或Quartus里跑行為仿真看看混頻后頻譜是不是對(duì)了、CIC抽取后幅頻響應(yīng)的落降和預(yù)算是接近、FIR補(bǔ)償之后帶外雜散是否符合要求。確認(rèn)沒(méi)問(wèn)題再去接板子的ADC數(shù)據(jù)。上板實(shí)測(cè)時(shí)用信號(hào)發(fā)生器產(chǎn)生一個(gè)單音信號(hào)頻率設(shè)到(15.36MHz 10kHz)偏離量。FPGA內(nèi)部抓數(shù)據(jù)用ILA集成邏輯分析儀觀察I/Q輸出波形。此時(shí)I/Q兩路的輸出應(yīng)該是一個(gè)10kHz的正弦波頻率剛好是輸入偏移量幅度恒定。如果看到波形有周期性閃爍或者頻率不對(duì)先查NCO頻率配置再查混頻的符號(hào)位處理最后查CIC和FIR的位寬截位。這幾乎是排查DDC模塊問(wèn)題的三斧頭。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄調(diào)試基帶中頻模塊的九大經(jīng)典坑5.1 頻譜不對(duì)、雜散大先查哪里我第一次獨(dú)立調(diào)DDC模塊的時(shí)候抓回來(lái)的頻譜和MATLAB仿真差了十萬(wàn)八千里期望的基帶信號(hào)附近全是雜散帶寬外抑制只有30dB左右怎么調(diào)參數(shù)都不管用。排查到最后才發(fā)現(xiàn)是FIR Compiler的系數(shù)格式配錯(cuò)了——我導(dǎo)出的系數(shù)是十進(jìn)制小數(shù)但I(xiàn)P核默認(rèn)用的是無(wú)符號(hào)定點(diǎn)數(shù)符號(hào)格式?jīng)]選對(duì)結(jié)果整個(gè)濾波器頻響全亂了帶外抑制當(dāng)然做不上去。這個(gè)問(wèn)題的排查方法很有通用性先把NCO配置成輸出一個(gè)直流或者單音不連濾波器看混頻輸出的頻譜是不是干凈再一級(jí)一級(jí)把模塊接回來(lái)。每一級(jí)都驗(yàn)證完好再接下一級(jí)可以最大程度地縮短定位問(wèn)題的范圍。另外混頻器的符號(hào)位處理也很常見(jiàn)——有符號(hào)數(shù)和無(wú)符號(hào)數(shù)混在一起做加減法直接讓頻譜在0頻附近產(chǎn)生一個(gè)巨大的鏡像分量。檢查方法很簡(jiǎn)單把ILA抓到的原始數(shù)據(jù)做FFT看看頻譜和理論值差在哪。5.2 數(shù)據(jù)不同步、時(shí)序不收斂的問(wèn)題FPGA時(shí)序不收斂是多模塊系統(tǒng)里最常見(jiàn)的頭痛問(wèn)題。我碰到過(guò)ODDR翻轉(zhuǎn)一個(gè)關(guān)鍵路徑導(dǎo)致不收斂的情況也遇到過(guò)CIC和FIR級(jí)聯(lián)之后組合邏輯過(guò)長(zhǎng)導(dǎo)致保持時(shí)間不夠的情況。排查的基本思路是先看時(shí)序報(bào)告把超差的路徑找出來(lái)然后逐條看是組合邏輯太長(zhǎng)還是扇出太大。通信鏈路數(shù)據(jù)通路大多數(shù)延時(shí)是乘法器級(jí)聯(lián)造成的可以考慮在這之間插入多級(jí)流水寄存器。處理時(shí)序問(wèn)題有一個(gè)技巧把關(guān)鍵數(shù)據(jù)通路按“架構(gòu)上可容忍的延遲”切流水。比如FIR濾波器一個(gè)時(shí)鐘出不來(lái)就把算法拆成多個(gè)周期流水結(jié)果延遲固定即可對(duì)通信系統(tǒng)來(lái)說(shuō)沒(méi)影響。說(shuō)白了通信信號(hào)本來(lái)就是流式的延遲幾拍換時(shí)序收斂性價(jià)比極高。5.3 資源不足DSP48被吃光怎么辦在資源受限的FPGA芯片上做基帶中頻處理最尷尬的就是綜合實(shí)現(xiàn)之后發(fā)現(xiàn)DSP48E1用量超過(guò)100%。這時(shí)候有幾種快速優(yōu)化手段第一大抽取比應(yīng)用先做CIC再做FIR別讓FIR承受所有抽取倍數(shù)。之前已經(jīng)強(qiáng)調(diào)過(guò)了FIR的階數(shù)會(huì)隨抽取比顯著上升CIC無(wú)乘法器的結(jié)構(gòu)在處理大抽取比時(shí)遠(yuǎn)比FIR劃算。第二FIR Compiler開(kāi)時(shí)分復(fù)用模式。一個(gè)DSP48可以時(shí)分復(fù)用給多個(gè)輸入通道或者多個(gè)系數(shù)分組代價(jià)是吞吐率降低但很多低速基帶場(chǎng)景不需要每時(shí)鐘都出結(jié)果這種模式就是白賺的資源。第三把能降位寬的地方果斷降位寬。信號(hào)鏈路每經(jīng)過(guò)一級(jí)處理噪聲基底的貢獻(xiàn)主要由截位誤差決定保持?jǐn)?shù)據(jù)位16位左右足夠完全沒(méi)有必要從頭到尾全是32位。5.4 同步環(huán)路不收斂問(wèn)題在下游做同步環(huán)路比如Gardner、Costas時(shí)很多人的第一反應(yīng)是調(diào)環(huán)路濾波器帶寬、調(diào)環(huán)路增益但我踩過(guò)幾次坑后發(fā)現(xiàn)問(wèn)題往往不在環(huán)路本身而是出現(xiàn)在上游的濾波器和數(shù)據(jù)位寬上。當(dāng)輸入數(shù)據(jù)存在大的直流偏置或者幅度不均衡時(shí)定時(shí)誤差檢測(cè)器的輸出會(huì)被污染環(huán)路當(dāng)然無(wú)法穩(wěn)定收斂。排查這個(gè)問(wèn)題的辦法也很直觀在環(huán)路前加一個(gè)高通濾波器把直流過(guò)濾掉或者先用自動(dòng)增益控制AGC把幅度歸一化到標(biāo)準(zhǔn)范圍。還有一點(diǎn)經(jīng)常被忽略同步環(huán)路是閉環(huán)系統(tǒng)仿真的激勵(lì)和板級(jí)的真實(shí)信號(hào)在幅度分布上可能有差異所以在板級(jí)調(diào)試之前先在MATLAB里對(duì)定點(diǎn)模型做閉環(huán)跑位錯(cuò)誤碼率這樣至少能排除“環(huán)路自身原理就錯(cuò)了”這種情況。6. 工具鏈與開(kāi)發(fā)環(huán)境的實(shí)戰(zhàn)經(jīng)驗(yàn)6.1 Vivado和Quartus不同廠商工具的選擇建議Xilinx和Intel在FPGA信號(hào)處理方面的工具鏈我都用過(guò)說(shuō)下實(shí)際感受。Vivado在IP核生態(tài)、System Generator和RFSoC支持上更成熟尤其做通信基帶Xilinx的DDS IP核、FIR Compiler和FFT IP核的性能和文檔都很不錯(cuò)。Intel的Quartus則在低端器件的性價(jià)比方面有優(yōu)勢(shì)Cyclone系列做中頻處理非常便宜但I(xiàn)P核的配置流程相對(duì)繁瑣一些。如果是從學(xué)習(xí)角度出發(fā)我建議先從Xilinx的Artix-7系列開(kāi)始。原因是學(xué)習(xí)資料多、例程多、遇到問(wèn)題容易搜到答案很多開(kāi)源項(xiàng)目也都是Artix-7平臺(tái)。等把DDC、DUC、同步這些核心模塊練熟了再根據(jù)公司項(xiàng)目需求切到其他平臺(tái)基本可以無(wú)縫轉(zhuǎn)移思路。6.2 System Generator還是手寫(xiě)Verilog這個(gè)問(wèn)題每隔一段時(shí)間就會(huì)被人翻出來(lái)討論。我的建議是分階段算法探索階段用System Generator或者M(jìn)ATLAB的HDL Coder快速搭建、快速驗(yàn)證產(chǎn)品交付階段用手寫(xiě)Verilog/VHDL做精細(xì)化資源優(yōu)化和時(shí)序調(diào)優(yōu)。工具生成的代碼往往不像手寫(xiě)代碼那樣對(duì)關(guān)鍵路徑做到極致優(yōu)化尤其是資源可控性和時(shí)序收斂性全自動(dòng)生成的代碼還是比不了多年工程經(jīng)驗(yàn)調(diào)校過(guò)的手寫(xiě)代碼。但如果你做的是一次性原型驗(yàn)證System Generator真的能幫你省出數(shù)周時(shí)間。6.3 調(diào)試驗(yàn)證工具ILA、邏輯分析儀和頻譜儀搭配在板級(jí)調(diào)試時(shí)ILA幾乎是我最常用的工具。但必須注意ILA會(huì)占用BRAM資源而且抓取深度有限。我的經(jīng)驗(yàn)是抓取深度優(yōu)先保證連續(xù)幾個(gè)調(diào)制符號(hào)的長(zhǎng)度采樣時(shí)鐘用模塊的主時(shí)鐘觸發(fā)條件設(shè)為同步模塊的鎖定信號(hào)——這樣能捕捉到同步建立和穩(wěn)定后的完整波形。除了ILA有條件的話真的建議配一臺(tái)頻譜儀。FPGA用DAC輸出中頻信號(hào)的時(shí)候頻譜儀能看到真實(shí)的頻譜形狀、雜散、諧波這些信息對(duì)排查濾波器和混頻問(wèn)題非常重要。沒(méi)有頻譜儀的話退而求其次用采樣示波器抓時(shí)域波形也能發(fā)現(xiàn)不少問(wèn)題。7. 如何持續(xù)精進(jìn)一個(gè)老工程師的學(xué)習(xí)路線建議基帶與中頻的FPGA算法實(shí)現(xiàn)入門(mén)容易精通難。我給新人的建議是先吃透數(shù)字信號(hào)處理基礎(chǔ)再精通FPGA工具鏈最后做足夠多的工程實(shí)踐。很多人在第一步就跳過(guò)去了直接上手寫(xiě)代碼結(jié)果濾波器系數(shù)來(lái)自ChatGPT、NCO配置瞎填、頻譜亂成一片還不知道為什么。學(xué)習(xí)路徑方面推薦按這個(gè)順序走先從MATLAB把傅里葉變換、數(shù)字濾波、采樣定理這些基礎(chǔ)概念玩明白再用FPGA的IP核做FIR濾波器、NCO、CIC抽取這三樣?xùn)|西接著搭一個(gè)完整的DDC鏈路從ADC數(shù)據(jù)輸入到I/Q基帶輸出完整調(diào)通然后做發(fā)射方向的DUC鏈路之后再做同步環(huán)路。把這條路走通你在通信基帶中頻FPGA領(lǐng)域的基本盤(pán)就建立了。開(kāi)源項(xiàng)目方面GitHub上搜索“SDR FPGA”或者“DDC FPGA”能找到不少好項(xiàng)目。我比較推薦看那些帶有MATLAB模型和Verilog代碼對(duì)照的項(xiàng)目這種資源才是真正能學(xué)到東西的。需要特別提醒的是很多開(kāi)源項(xiàng)目的代碼質(zhì)量和工程規(guī)范并不可靠看的時(shí)候多想一想“為什么這樣設(shè)計(jì)”而不是直接搬到自己的工程里??柭鼮V波在FPGA上的實(shí)現(xiàn)是另一個(gè)值得深入的方向。雖然它通常被用在導(dǎo)航、跟蹤、目標(biāo)預(yù)測(cè)領(lǐng)域但在通信信道估計(jì)中也有很多變體和應(yīng)用。FPGA實(shí)現(xiàn)卡爾曼濾波的本質(zhì)是處理矩陣乘法和求逆二維到四維的矩陣運(yùn)算用HDL寫(xiě)起來(lái)會(huì)有點(diǎn)痛苦但在資源充足的平臺(tái)上往往能獲得比DSP更低的延遲和更穩(wěn)定的確定性——畢竟迭代循環(huán)在FPGA上是展開(kāi)的流水線而不是馮諾依曼式的取指執(zhí)行。最后想強(qiáng)調(diào)的是基帶和中頻的算法實(shí)現(xiàn)不是一個(gè)“照著書(shū)抄就能成功”的事情參數(shù)的影響是全局性的——濾波器抽頭多一個(gè)少一個(gè)、NCO位寬差幾位、抽取倍數(shù)安排得不合理都會(huì)在最后聯(lián)調(diào)的時(shí)候給你各種意想不到的驚喜。我自己也是在這些坑里滾了這么多年才逐漸積累起一套“先算后做、分級(jí)驗(yàn)證”的穩(wěn)定流程。希望大家看完這篇之后至少能在動(dòng)手前先花一個(gè)小時(shí)把指標(biāo)和架構(gòu)想清楚少走一點(diǎn)我當(dāng)年走過(guò)的彎路。