測(cè)試工程:RTL、相干采樣與SNR/ENOB復(fù)現(xiàn))
簡(jiǎn)介面向FPGA數(shù)字信號(hào)處理學(xué)習(xí)者的DDS與ADDA自環(huán)測(cè)試工程圍繞DDS IP數(shù)字波形合成、DAC輸出與ADC回采驗(yàn)證展開適合已具備Verilog/VHDL基礎(chǔ)、希望打通從波形生成到頻譜分析鏈路的開發(fā)者。工程采用50MHz時(shí)鐘驅(qū)動(dòng)DAC輸出正弦波將模擬輸出自環(huán)至ADC輸入并以MMCM分頻得到25MHz采樣時(shí)鐘配合ILA采集不少于2048個(gè)樣點(diǎn)再由Matlab完成頻譜分析同時(shí)可通過(guò)VIO修改頻率字生成1MHz與3MHz信號(hào)驗(yàn)證頻譜結(jié)果。壓縮包共613個(gè)文件、約46.11MB包含Verilog/VHDL源碼與仿真文件、XDC約束、Tcl與批處理腳本、比特流、ILA/CSV采樣數(shù)據(jù)及工程配置等兼顧綜合實(shí)現(xiàn)與數(shù)據(jù)分析。已有321人學(xué)習(xí)可用于復(fù)現(xiàn)實(shí)驗(yàn)、核對(duì)DDS配置與ADC采樣時(shí)序并參考AN108 34針插頭安裝位置等注意事項(xiàng)減少排錯(cuò)成本。1. DDS 驅(qū)動(dòng) ADDA 的閉環(huán)測(cè)試這套工程文件省掉的不是寫代碼的時(shí)間把 DAC 的輸出用一根短線直接接回 ADC 的輸入中間只留一級(jí)重建濾波器、一級(jí)抗混疊和必要的衰減網(wǎng)絡(luò)就能用一套 DDS 把整條模擬鏈路的指標(biāo)量出來(lái)。原理上只有三句話DDS 發(fā)正弦、DAC 打出去、ADC 收回來(lái)做 FFT。真正動(dòng)手時(shí)多數(shù)人卡在“發(fā)的”和“收的”對(duì)不齊DAC 側(cè)時(shí)鐘怎么轉(zhuǎn)發(fā)、ADC 側(cè)數(shù)據(jù)位序怎么拼、抓到的 8192 點(diǎn)到底是不是完整周期。這份 DDS ADDA 測(cè)試工程文件價(jià)值就在把這條閉環(huán)固化成可復(fù)現(xiàn)的 RTL 加腳本。它適合兩類人正在調(diào) DDS 波形相位累加器、查找表、頻率控制字的和要給一塊自制采集板做首輪 SNR、ENOB 摸底的。下面按“先讀懂 RTL再定參數(shù)最后排坑”的順序拆。2. 拆工程目錄DDS 相位累加器到 DAC 打拍的三級(jí)流水拿到這類工程最忌諱從第一個(gè) .v 文件順著往下讀。DDS 加 ADDA 的工程文件通常按功能切得很干凈波形生成、發(fā)送接口、接收緩存、上位機(jī)腳本四塊剩下的是約束和測(cè)試記錄。先把 top 層的連線看清楚再把信號(hào)從“數(shù)字域”到“模擬域”再到“數(shù)字域”的整條路徑畫在紙上后面所有參數(shù)才有落點(diǎn)。2.1 目錄骨架先找 top再看四個(gè)子模塊這類工程的文件組織大同小異常見結(jié)構(gòu)如下表。你手里的工程文件如果目錄名不同按用途對(duì)號(hào)入座即可不要被命名帶偏。目錄通常放什么閱讀優(yōu)先級(jí)rtl/top、dds_core、dac_if、adc_cap 四個(gè)模塊第一優(yōu)先先看 top 的端口rtl/lut/ 或 sim/正弦查找表 .hex/.coe 與生成腳本第二優(yōu)先確認(rèn)表點(diǎn)數(shù)和位寬constr/時(shí)鐘約束、輸出延遲約束、引腳分配第三優(yōu)先出問題時(shí)回來(lái)看sw/上位機(jī)取數(shù)與 FFT 分析腳本第四優(yōu)先決定你怎么看數(shù)據(jù)doc/測(cè)試記錄表、頻點(diǎn)與碼值對(duì)照有空再看別當(dāng)說(shuō)明書讀閱讀順序我一般這么走先看 top 里clk、rst_n、fcw這幾個(gè)端口誰(shuí)驅(qū)動(dòng)誰(shuí)確認(rèn) DDS 和 DAC 是不是同一個(gè)時(shí)鐘源再看 dds_core 的位寬參數(shù)這決定了你的頻率分辨率然后看 dac_if 里時(shí)鐘是怎么送到管腳的這里是第一個(gè)翻車高發(fā)區(qū)最后看 adc_cap 的觸發(fā)條件它決定了你抓到的是不是一段“干凈”的數(shù)據(jù)。提示工程里如果同時(shí)存在仿真用的 .hex 和綜合用的 .coe務(wù)必確認(rèn)綜合實(shí)際讀的是哪一個(gè)。改了一個(gè)忘了另一個(gè)是最常見的“波形怎么都不對(duì)”的源頭。2.2 相位累加器與查找表DDS 的數(shù)學(xué)怎么落成 RTLDDS 的核心就一個(gè)公式輸出頻率f_out FCW × f_clk / 2^N其中 N 是相位累加器位寬FCW 是頻率控制字。它的直接推論是頻率分辨率f_clk / 2^N——125 MHz 時(shí)鐘配 32 位累加器分辨率約 0.029 Hz這個(gè)數(shù)字決定了你能不能“卡”到嚴(yán)格相干的頻點(diǎn)上后面第 4 章會(huì)反復(fù)用到。工程里的 dds_core 一般長(zhǎng)這樣我按可綜合的寫法整理過(guò)module dds_core #( parameter PHASE_W 32, // 相位累加器位寬決定頻率分辨率 parameter LUT_AW 12, // 查找表地址位寬決定相位截?cái)嚯s散 parameter DATA_W 14 // 幅度位寬對(duì)齊 DAC 位寬 )( input wire clk, input wire rst_n, input wire [PHASE_W-1:0] fcw, // 頻率控制字運(yùn)行時(shí)可變 input wire [PHASE_W-1:0] pinc, // 相位偏移雙通道正交/雙音時(shí)用 output wire [DATA_W-1:0] dac_data, output wire dac_valid ); reg [PHASE_W-1:0] phase_acc; wire [PHASE_W-1:0] phase_sum phase_acc pinc; wire [LUT_AW-1:0] lut_addr phase_sum[PHASE_W-1 -: LUT_AW]; // 取高位做截?cái)?reg [DATA_W-1:0] sine_lut [0:(1LUT_AW)-1]; reg [DATA_W-1:0] lut_q; reg [1:0] vld_sr; initial $readmemh(sine_lut.hex, sine_lut); // 綜合前務(wù)必確認(rèn)路徑有效 always (posedge clk or negedge rst_n) begin if (!rst_n) phase_acc {PHASE_W{1b0}}; else phase_acc phase_sum; // 每拍累加一次溢出即一個(gè)周期 end always (posedge clk or negedge rst_n) begin if (!rst_n) begin lut_q {DATA_W{1b0}}; vld_sr 2b00; end else begin lut_q sine_lut[lut_addr]; // BRAM 讀固定 1 拍延遲 vld_sr {vld_sr[0], 1b1}; // valid 跟著數(shù)據(jù)一起打拍 end end assign dac_data lut_q; assign dac_valid vld_sr[1]; // 與相位→地址→數(shù)據(jù)的總延遲對(duì)齊 endmodule這段代碼里有三個(gè)必須理解的點(diǎn)。第一phase_sum[PHASE_W-1 -: LUT_AW]是取相位的高 LUT_AW 位做地址低位被丟掉這就是“相位截?cái)唷?。?jīng)驗(yàn)上地址每多 1 bit截?cái)嘁氲淖顗碾s散改善約 6 dB但同時(shí) BRAM 用量翻倍12 bit 地址配 14 bit 數(shù)據(jù)在很多板子上是資源和指標(biāo)折中的甜點(diǎn)。第二vld_sr打了兩拍是因?yàn)閺南辔坏?BRAM 輸出一共兩拍延遲如果你的 LUT 用組合讀分布式 RAM實(shí)現(xiàn)valid 只打一拍就夠多打一拍反而會(huì)讓第一個(gè)數(shù)據(jù)點(diǎn)錯(cuò)位一格看上去就是“波形整體挪了一點(diǎn)”。第三pinc不是擺設(shè)做雙音互調(diào)測(cè)試時(shí)用兩路 DDS 各給一個(gè) FCW把兩路數(shù)據(jù)相加再送 DAC做 IQ 正交時(shí)讓第二路pinc 2^(N-2)就是 90 度相移。參數(shù)怎么算舉個(gè)例子f_clk 125 MHz、N 32、目標(biāo)f_out 1 MHz則FCW 1e6 × 2^32 / 125e6 34359738.368取整 34359738實(shí)際輸出 999999.98 Hz偏差 0.02 Hz。這個(gè)偏差看起來(lái)微不足道但當(dāng)你要求嚴(yán)格相干采樣時(shí)它就是頻譜泄漏的根源。2.3 DAC 側(cè)發(fā)送時(shí)鐘轉(zhuǎn)發(fā)、數(shù)據(jù)對(duì)齊和 valid 數(shù)拍DDS 算得再對(duì)DAC 接口接錯(cuò)了也是白搭。并行高速 DAC 的標(biāo)準(zhǔn)接法是“源同步”把內(nèi)部時(shí)鐘經(jīng) ODDR 轉(zhuǎn)發(fā)到管腳作為 DAC 的采樣時(shí)鐘數(shù)據(jù)用另一組輸出寄存器同時(shí)送出靠 PCB 上等長(zhǎng)布線保證建立/保持。FPGA 內(nèi)部千萬(wàn)不要讓工具把時(shí)鐘和數(shù)據(jù)走兩條不同的路徑出去否則偏斜隨溫度漂。// 時(shí)鐘轉(zhuǎn)發(fā)用 ODDR 把系統(tǒng)時(shí)鐘送到管腳避免時(shí)鐘走全局緩沖后偏斜失控 ODDR #( .DDR_CLK_EDGE(SAME_EDGE), .INIT(1b0), .SRTYPE(SYNC) ) u_dac_clk_fwd ( .Q (dac_clk_p), // 直接接 OBUFDS 或單端輸出 .C (clk_dac), .CE(1b1), .D1(1b1), .D2(1b0), // 雙沿都翻轉(zhuǎn)得到與 clk_dac 同頻的方波 .R (1b0), .S(1b0) );數(shù)據(jù)側(cè)一般再打一拍輸出寄存器讓數(shù)據(jù)和轉(zhuǎn)發(fā)時(shí)鐘從同一個(gè) IOB 出發(fā)# 輸出方向按源同步寫具體數(shù)值必須按 DAC 手冊(cè)的建立/保持時(shí)間換算不能照抄 set_output_delay -clock [get_clocks dac_clk_tx] -max 1.2 [get_ports dac_data*] set_output_delay -clock [get_clocks dac_clk_tx] -min -0.8 [get_ports dac_data*]還有兩個(gè)容易忽略的細(xì)節(jié)。一個(gè)是數(shù)據(jù)格式多數(shù)高速 DAC 吃的是偏移二進(jìn)制offset binary而 DDS 里累加器自然產(chǎn)生的是二進(jìn)制補(bǔ)碼中間要么加半量程偏置要么翻轉(zhuǎn)最高位少做這一步輸出就是一個(gè)單極性半波看上去“頻率對(duì)了但只有半個(gè)正弦”。另一個(gè)是更新率串行接口的 DAC 建立時(shí)間通常在微秒量級(jí)用它做 1 MHz 以上的正弦幅度會(huì)被壓得很慘這不是 DDS 的問題是接口帶寬的問題。3. 把波形收回來(lái)ADC 采樣、觸發(fā)抓數(shù)與上位機(jī)取數(shù)發(fā)送端通了接著是接收端。這一段是整個(gè)閉環(huán)里最容易被低估的部分很多人以為 ADC 只要時(shí)鐘給上、數(shù)據(jù)接上就能出數(shù)實(shí)際上位序、眼圖中心、跨時(shí)鐘域這三件事里任何一件沒處理你看到的頻譜都會(huì)是一團(tuán)糊。判斷自己有沒有做對(duì)有個(gè)簡(jiǎn)單標(biāo)準(zhǔn)——輸入一個(gè)干凈的正弦頻譜上除了基波和已知諧波底噪應(yīng)該是平的如果底噪有明顯鼓包或者成對(duì)出現(xiàn)的小峰八成是采樣側(cè)的問題。3.1 ADC 接口與采樣時(shí)鐘先算抖動(dòng)再談位寬采樣時(shí)鐘的質(zhì)量直接決定 SNR 上限公式是SNR_jitter -20log10(2π × f_in × t_jitter)。代入兩個(gè)數(shù)感受一下輸入 1 MHz、時(shí)鐘抖動(dòng) 1 ps理論上限約 104 dB輸入 100 MHz、同樣 1 ps上限掉到 64 dB。這條曲線說(shuō)明一件事——評(píng)估一塊 14 位 ADC理論 SNR 約 86 dB時(shí)如果你在 100 MHz 輸入下測(cè)出 64 dB那不一定是板子壞了可能是時(shí)鐘或信號(hào)源的抖動(dòng)已經(jīng)把你限死了。選測(cè)試頻點(diǎn)時(shí)必須先做這個(gè)估算。并行 CMOS 輸出的 ADC 一般隨路送一個(gè) DCO 時(shí)鐘接收側(cè)用 IDDR 在雙沿采樣再把兩拍數(shù)據(jù)拼成 2×N 位// DCO 反相后作為采樣時(shí)鐘保證采在數(shù)據(jù)眼圖中心 IDDR #( .DDR_CLK_EDGE(SAME_EDGE_PIPELINED), .INIT_Q1(1b0), .INIT_Q2(1b0), .SRTYPE(SYNC) ) u_adc_iddr ( .Q1(adc_d0), .Q2(adc_d1), // 同一時(shí)鐘沿采到的兩個(gè)相鄰樣本 .C (adc_dco_buf), .CE(1b1), .D (adc_data_in), .R(1b0), .S(1b0) );拼接時(shí)位序錯(cuò)了波形會(huì)“像被洗過(guò)一樣”頻譜上出現(xiàn)對(duì)稱的鏡像雜散這是血淚經(jīng)驗(yàn)里最典型的一種。處理辦法是打開工程里的 IDELAY 掃描邏輯或者手工改 tap 值把延遲從 0 掃到最大每檔算一次 SNR取 SNR 最高那一檔的中間值作為工作點(diǎn)——眼圖中心找到了位序問題通常也一并暴露。串行 LVDS 輸出的 ADC 則是另一套用 ISERDES 做串并轉(zhuǎn)換注意 bitslip 對(duì)齊字邊界幀頭對(duì)不上時(shí)數(shù)據(jù)會(huì)整體循環(huán)移位表現(xiàn)是“波形對(duì)但幅度亂跳”。這類接口在工程里一般有獨(dú)立的狀態(tài)機(jī)先把 lock 信號(hào)拉高再開始采集。3.2 觸發(fā)與深存儲(chǔ)怎么保證抓到的 8192 點(diǎn)不是半截自由跑采集的問題在于你無(wú)法控制起點(diǎn)。做 FFT 雖然不要求從正弦的零點(diǎn)開始但要求這段數(shù)據(jù)是“連續(xù)、不丟點(diǎn)、不含復(fù)位毛刺”的。工程里通常的做法是內(nèi)部用一塊 8192×16 bit 的 BRAM 做深存儲(chǔ)寫入由 ADC 采樣使能驅(qū)動(dòng)觸發(fā)條件可以是軟件下發(fā)的一個(gè)脈沖也可以是電平跨越閾值預(yù)觸發(fā)深度設(shè) 50%這樣觸發(fā)點(diǎn)前后的波形都能看到。跨時(shí)鐘域是這一步的關(guān)鍵。ADC 數(shù)據(jù)在采樣時(shí)鐘域BRAM 和上傳邏輯在系統(tǒng)時(shí)鐘域中間必須過(guò)異步 FIFO。FIFO 深度不夠、或者讀側(cè)在 FIFO 空的時(shí)候硬讀結(jié)果就是數(shù)據(jù)里隨機(jī)丟點(diǎn)——丟點(diǎn)在時(shí)域上幾乎看不出來(lái)在頻域上表現(xiàn)為底噪整體抬高 3 到 6 dB非常像“ADC 本身性能不行”。判斷方法把一個(gè)已知頻率的相干信號(hào)灌進(jìn)去看基波峰值是否穩(wěn)定如果峰值每次采集抖動(dòng)超過(guò) 0.5 dB先查丟點(diǎn)別急著換芯片。存滿之后停止寫入、置一個(gè) ready 標(biāo)志等上位機(jī)把數(shù)據(jù)搬走再清標(biāo)志重新開始。這個(gè)握手如果做成“邊寫邊讀”很容易讀到半新半舊的兩段數(shù)據(jù)拼在一起相位不連續(xù)頻譜上會(huì)出現(xiàn)寬裙邊。3.3 取數(shù)到上位機(jī)把 8192 點(diǎn)搬進(jìn) Python傳輸鏈路的選擇取決于你的工程里已經(jīng)有什么。常見的是 UART8192 點(diǎn) × 2 字節(jié) 16 KB115200 波特率下 8N1 每字節(jié)占 10 bit滿打滿算要 1.4 秒換成 921600 就只要 0.18 秒。這個(gè)時(shí)間對(duì)調(diào)試體驗(yàn)影響很大——每次改參數(shù)等一秒半一天下來(lái)耐心就磨沒了。有條件的話優(yōu)先走 USB 或網(wǎng)口串口留給兜底。Python 側(cè)收到的是裸二進(jìn)制直接按 16 位小端解析然后進(jìn) FFTimport numpy as np FS 100e6 # ADC 實(shí)際采樣率必須與你配置的時(shí)鐘一致 N 8192 # 單次分析點(diǎn)數(shù)取 2 的冪 def load_samples(path): raw np.fromfile(path, dtypeu2) # 無(wú)符號(hào) 16 位小端 return raw.astype(np.float64) def tone_metrics(x, fsFS, harmonics5, half_lobe3): n len(x) x x - x.mean() # 去直流否則 DC 泄漏會(huì)污染鄰近 bin w np.blackman(n) # 非嚴(yán)格相干時(shí)用窗壓泄漏 spec np.abs(np.fft.rfft(x * w)) ** 2 k int(np.argmax(spec[1:]) 1) # 跳過(guò) DC 找基波 lo, hi max(k - half_lobe, 1), k half_lobe psig spec[lo:hi 1].sum() # 基波主瓣功率 mask np.ones_like(spec, dtypebool) mask[0:2] False # DC 與 1 號(hào) bin 不計(jì)入噪聲 mask[lo:hi 1] False hpow 0.0 for h in range(2, harmonics 1): c int(round(k * h)) if c half_lobe len(spec): break hpow spec[c - half_lobe:c half_lobe 1].sum() mask[c - half_lobe:c half_lobe 1] False # 諧波單獨(dú)統(tǒng)計(jì) pnoise spec[mask].sum() snr 10 * np.log10(psig / pnoise) sinad 10 * np.log10(psig / (pnoise hpow)) sfdr 10 * np.log10(psig / spec[1:][mask[1:]].max()) enob (sinad - 1.76) / 6.02 return dict(bink, fk * fs / n, snrsnr, sinadsinad, sfdrsfdr, enobenob) if __name__ __main__: print(tone_metrics(load_samples(cap_8192.bin)))幾個(gè)參數(shù)要盯住。half_lobe3表示基波主瓣按 ±3 個(gè) bin 統(tǒng)計(jì)用 Blackman 窗時(shí)主瓣寬度約 6 個(gè) bin取 3 正好覆蓋一半如果你換成 Hann 窗可以收到 2。harmonics5是排除前 5 次諧波測(cè) ADC 時(shí)一般排到 5 次足夠測(cè) DAC 的鏡像時(shí)則要單獨(dú)處理。mask[0:2] False把 DC 和 1 號(hào) bin 排除掉是因?yàn)榧恿舜爸笾绷鲿?huì)向鄰近泄漏不排的話低頻噪聲會(huì)被算高。注意加窗是為了在非相干條件下看趨勢(shì)帶窗算出來(lái)的 SNR 會(huì)偏低 1 到 2 dB。要報(bào)正式指標(biāo)必須先讓相干采樣條件嚴(yán)格成立再把窗換成全 1 重算一遍。4. 參數(shù)怎么定頻點(diǎn)、采樣率、點(diǎn)數(shù)與窗函數(shù)的取值對(duì)照表同一塊板子換一組測(cè)試參數(shù)SNR 能差出 10 dB 以上這是很多“指標(biāo)對(duì)不上”爭(zhēng)議的來(lái)源。所以動(dòng)手之前先把頻率控制字、相干條件、點(diǎn)數(shù)、窗函數(shù)這四件事一次性定死寫進(jìn)測(cè)試記錄表后面所有數(shù)據(jù)都基于同一口徑。4.1 頻率控制字與實(shí)際頻點(diǎn)先算清楚再談指標(biāo)DDS 的頻率分辨率是f_clk / 2^N這意味著你想要的任何頻點(diǎn)都會(huì)被量化到最近的格點(diǎn)上。做單音測(cè)試時(shí)這沒什么問題但當(dāng)你同時(shí)用 ADC 的采樣率去卡相干條件時(shí)量化誤差就變成了頻譜泄漏。所以正確的順序是先由 FCW 反算實(shí)際頻點(diǎn)再拿這個(gè)實(shí)際頻點(diǎn)去設(shè)計(jì)相干采樣。CLK_DDS 125e6 # DDS/DAC 系統(tǒng)時(shí)鐘 PHASE_W 32 # 相位累加器位寬 FS_ADC 100e6 # ADC 采樣率 N_FFT 8192 # 分析點(diǎn)數(shù) def fcw_for(f_target, clkCLK_DDS, wPHASE_W): fcw int(round(f_target * (1 w) / clk)) f_real fcw * clk / (1 w) # 量化后的真實(shí)頻點(diǎn) return fcw, f_real def coherent_pick(n_fftN_FFT, fsFS_ADC, m_min60): # 選一個(gè)與 n_fft 互質(zhì)的 m落在合適頻段減少諧波折疊到基波附近 for m in range(m_min, m_min 200): if np.gcd(m, n_fft) 1: yield m, m * fs / n_fft這里m和n_fft互質(zhì)是相干采樣的核心條件一個(gè)周期內(nèi)采到的 N 個(gè)點(diǎn)剛好覆蓋信號(hào)的整數(shù)個(gè)周期且采樣點(diǎn)在相位上均勻鋪開不重復(fù)。若m與n_fft有公因子采樣點(diǎn)會(huì)在相位上“扎堆”等效于降低了有效分辨率。我一般會(huì)固定N_FFT 8192、在m60 到 260 之間掃挑一個(gè)既落在關(guān)心的頻段、諧波又不會(huì)折回基波附近的。折回這件事很容易被忽略如果 3 次諧波的頻率超過(guò)fs/2它會(huì)鏡像回來(lái)落在某個(gè)位置萬(wàn)一正好壓在基波或 2 次諧波上你算出來(lái)的 SINAD 就是錯(cuò)的。4.2 相干采樣與窗函數(shù)頻譜泄漏該由誰(shuí)背理想情況下相干采樣成立就不需要窗函數(shù)矩形窗的能量最集中SNR 也最準(zhǔn)。但現(xiàn)實(shí)是 DDS 的 FCW 量化、ADC 時(shí)鐘與 DDS 時(shí)鐘不同源帶來(lái)的微小頻差、以及時(shí)鐘本身的漂移都會(huì)讓相干條件“差不多但不嚴(yán)格”。這時(shí)候泄漏就會(huì)發(fā)生基波能量向鄰近 bin 擴(kuò)散主瓣變寬噪聲統(tǒng)計(jì)被污染。判斷方法很直接把采集到的數(shù)據(jù)截兩段各做 FFT比較基波峰值位置。如果兩次的峰值落在不同 bin說(shuō)明相干沒鎖住如果落在同一 bin 但主瓣明顯比理論寬說(shuō)明有頻率漂移。前者換參數(shù)后者只能加窗。場(chǎng)景建議窗函數(shù)主瓣寬度bin代價(jià)嚴(yán)格相干用于報(bào)指標(biāo)矩形窗2對(duì)頻偏零容忍相干但有時(shí)鐘漂移Hann4SNR 偏低約 1.5 dB非相干日??蹿厔?shì)Blackman6SNR 偏低約 2 dB動(dòng)態(tài)范圍好小信號(hào) 大信號(hào)共存Blackman-Harris8主瓣最寬適合找雜散選窗的原則是你要看的是“底噪有多低”就用矩形或 Hann你要看的是“有沒有小雜散”就用 Blackman 系列。兩者不要混著報(bào)數(shù)更不要拿帶窗的結(jié)果去和芯片手冊(cè)的典型值直接對(duì)比。4.3 SNR、SINAD、ENOB、SFDR 的口徑必須統(tǒng)一這四個(gè)指標(biāo)經(jīng)常被混用但它們回答的是不同問題。SNR 只算噪聲把諧波也當(dāng)成信號(hào)的一部分排除在外SINAD 把諧波算進(jìn)“非理想成分”所以 SINAD 恒小于等于 SNR兩者之差反映了諧波失真有多嚴(yán)重ENOB 由 SINAD 換算ENOB (SINAD - 1.76) / 6.02它是最常被引用也最容易被濫用的數(shù)字SFDR 是最差的那根雜散相對(duì)基波的高度跟噪聲無(wú)關(guān)純粹看有沒有“尖峰”。實(shí)操上有個(gè)坑不同工具對(duì)“諧波排除窗口”的定義不同。有的按 ±1 bin有的按 ±5 bin同一組數(shù)據(jù)算出來(lái) SINAD 能差 1 dB 以上。解決辦法是在測(cè)試記錄表里寫清口徑——主瓣 ±3 bin、諧波排到 5 次、DC 與 1 號(hào) bin 排除、窗函數(shù)類型——后面所有數(shù)據(jù)都照這個(gè)來(lái)。這份工程文件里的 sw 目錄通常就帶這樣一個(gè)配置頭別嫌它啰嗦它是唯一能讓三個(gè)月后的你復(fù)現(xiàn)出同樣數(shù)字的東西。還有一點(diǎn)DAC 側(cè)的指標(biāo)和 ADC 側(cè)要分開測(cè)。DAC 的關(guān)鍵是 SFDR 和通帶平坦度因?yàn)橹亟V波器和 sin(x)/x 滾降A(chǔ)DC 的關(guān)鍵是 SNR 和 ENOB。把兩者混在一個(gè)閉環(huán)里測(cè)出來(lái)的數(shù)字既不是 DAC 的也不是 ADC 的只能叫“系統(tǒng)指標(biāo)”對(duì)外報(bào)的時(shí)候必須說(shuō)清楚。5. 聯(lián)調(diào)避坑DDS 到 ADDA 閉環(huán)上最容易翻車的五個(gè)點(diǎn)下面這五條是我在這類工程上反復(fù)見過(guò)的。每條都按“現(xiàn)象、原因、解決”寫遇到問題時(shí)可以直接對(duì)號(hào)入座。5.1 波形側(cè)出不來(lái)、出歪了、幅度不對(duì)一、輸出恒定在量程中點(diǎn)一點(diǎn)波形都沒有?,F(xiàn)象示波器上看 DAC 輸出是一條平直的直流測(cè)出來(lái)正好是半量程電壓。 原因查找表沒被正確初始化。$readmemh的相對(duì)路徑在仿真時(shí)有效、綜合時(shí)失效或者綜合工具把 BRAM 優(yōu)化成了全零初值。此時(shí)地址怎么變讀出來(lái)的都是 0再經(jīng)過(guò)偏移二進(jìn)制轉(zhuǎn)換就成了中點(diǎn)。 解決檢查綜合日志里$readmemh是否被識(shí)別把 hex 文件轉(zhuǎn)成 .coe 或用initial塊加綜合屬性最穩(wěn)的辦法是加一個(gè)自檢計(jì)數(shù)器上電后順序讀 16 個(gè)地址通過(guò)串口打印出來(lái)看是不是正弦的前 16 個(gè)點(diǎn)。二、頻率完全正確幅度只有一半波形有明顯臺(tái)階?,F(xiàn)象頻譜上基波頻率分毫不差但幅度比預(yù)期低 6 dB時(shí)域看波形上下不對(duì)稱。 原因位寬沒對(duì)齊。DDS 輸出 14 位DAC 也是 14 位但中間某處只接了 13 根線或者把補(bǔ)碼直接當(dāng)偏移二進(jìn)制送了出去導(dǎo)致負(fù)半周被截掉。 解決逐級(jí)核對(duì)位寬和格式轉(zhuǎn)換點(diǎn)補(bǔ)碼轉(zhuǎn)偏移二進(jìn)制就是最高位取反或加半量程轉(zhuǎn)換點(diǎn)只能有一處多了會(huì)翻兩次等于沒翻。三、低頻正常頻率一上去幅度就塌?,F(xiàn)象1 MHz 時(shí)幅度正常10 MHz 時(shí)掉了 3 dB30 MHz 時(shí)幾乎看不到波形。 原因三個(gè)可能——DAC 重建濾波器截止頻率太低輸出變壓器或運(yùn)放的壓擺率不夠DAC 更新率本身不足高頻點(diǎn)每個(gè)周期只有幾個(gè)采樣點(diǎn)。另外 sin(x)/x 滾降在0.4×f_clk附近已經(jīng)有約 2.7 dB 衰減這是物理規(guī)律不是故障。 解決先把濾波器旁路測(cè)一次排除濾波器因素再算 DAC 的建立時(shí)間是否夠最后接受 sin(x)/x 的存在在軟件側(cè)做預(yù)補(bǔ)償把 LUT 值乘上 1/sinc 的倒數(shù)或者干脆把工作頻段限制在0.1×f_clk以內(nèi)。5.2 數(shù)據(jù)側(cè)抓不全、算不準(zhǔn)四、抓回來(lái)的波形“像被洗過(guò)”SNR 只有 30 dB 左右?,F(xiàn)象時(shí)域波形毛刺多、臺(tái)階亂頻譜底噪整體抬高還伴隨對(duì)稱的鏡像雜散。 原因IDDR 拼接位序反了或者采樣時(shí)鐘相位不對(duì)采在了數(shù)據(jù)跳變沿上。這兩種情況的表現(xiàn)極為相似都是“數(shù)據(jù)大體對(duì)但細(xì)節(jié)全亂”。 解決先做靜態(tài)測(cè)試——讓 DDS 輸出一個(gè)極低頻的鋸齒或直接讓 DAC 輸出遞增碼這樣每次采樣值應(yīng)該是確定的位序錯(cuò)了會(huì)立刻看出規(guī)律性的跳變確認(rèn)位序沒問題后再掃 IDELAY按 SNR 曲線找眼圖中心。這個(gè)流程走一遍大概二十分鐘比盲猜快得多。五、ENOB 每次算都不一樣前后能差 1 bit 以上?,F(xiàn)象同一組硬件配置連續(xù)采十次ENOB 在 10.2 到 11.5 之間跳。 原因采集不是嚴(yán)格相干的每次觸發(fā)點(diǎn)落在不同相位上加窗后殘余泄漏量隨機(jī)變化或者分析腳本里諧波排除窗口、DC 排除范圍沒固定也可能是丟點(diǎn)導(dǎo)致的隨機(jī)底噪抬升。 解決固定三件事——采集點(diǎn)數(shù)必須是 2 的冪、觸發(fā)后先檢查數(shù)據(jù)連續(xù)性相鄰點(diǎn)差分不應(yīng)出現(xiàn)異常大跳變、分析腳本的參數(shù)寫死在配置文件里。做完這三步重復(fù)性通常能收到 ±0.1 bit 以內(nèi)。如果還跳回去查 FIFO 有沒有溢出標(biāo)志被忽略。提示把每次測(cè)試的 FCW、實(shí)際頻點(diǎn)、采樣率、點(diǎn)數(shù)、窗函數(shù)、腳本版本號(hào)一起記進(jìn) csv出問題時(shí)按時(shí)間倒查比翻聊天記錄靠譜。6. 掃頻與雙音把工程的可信度從“能出波形”推到“指標(biāo)可復(fù)現(xiàn)”單點(diǎn)測(cè)試通過(guò)只說(shuō)明這套工程在那個(gè)頻點(diǎn)上能跑。要判斷它值不值得作為長(zhǎng)期使用的測(cè)試平臺(tái)還得做兩件事掃頻和雙音。掃頻的做法是把 FCW 從一個(gè)下限步進(jìn)到上限每一步采一組數(shù)據(jù)、算一次 SNR 和基波幅度最后畫兩條曲線幅度平坦度和 SNR 隨頻率的變化。這兩條曲線能暴露很多單點(diǎn)測(cè)試看不見的東西——重建濾波器的實(shí)際截止點(diǎn)、運(yùn)放開始?jí)簲[的頻點(diǎn)、以及 ADC 的 SNR 隨輸入頻率下降的斜率是否和抖動(dòng)估算吻合。我一般會(huì)取 1 MHz 到 40 MHz、每 1 MHz 一個(gè)點(diǎn)40 個(gè)點(diǎn)跑完大約五分鐘得到的曲線比任何手冊(cè)都貼合你手上這塊板子。掃頻時(shí)記得把每步的 FCW 和實(shí)際頻點(diǎn)都存下來(lái)DDS 的量化會(huì)讓實(shí)際頻點(diǎn)略低于目標(biāo)值畫圖時(shí)用實(shí)際值否則曲線會(huì)有細(xì)小的鋸齒。雙音測(cè)試用來(lái)量互調(diào)。做法是例化兩路 DDSFCW 分別對(duì)應(yīng) f1 和 f2兩路數(shù)據(jù)相加后送 DAC注意加完后要飽和或右移一位防止溢出FFT 里重點(diǎn)看 2f1-f2 和 2f2-f1 兩個(gè)三階產(chǎn)物相對(duì)基波的高度那就是 IMD3。兩個(gè)頻點(diǎn)的間隔有講究太近了落在同一個(gè)主瓣里分不開太遠(yuǎn)了又可能被濾波器滾降影響幅度我一般取間隔為采樣率的 1/40 左右并且讓 f1、f2 都滿足與采樣率互質(zhì)的前提。雙音對(duì) DAC 的要求比單音高得多很多在單音下 SFDR 漂亮的板子一上雙音三階產(chǎn)物就冒出來(lái)了這不是工程錯(cuò)了是它本來(lái)就該暴露。最后一個(gè)我踩過(guò)的坑掃頻跑完發(fā)現(xiàn) 20 MHz 附近 SNR 突然掉 4 dB查了半天硬件最后發(fā)現(xiàn)是那個(gè)頻點(diǎn)的 FCW 量化后剛好和采樣率構(gòu)成了近似整數(shù)比相干條件被破壞泄漏進(jìn)了噪聲統(tǒng)計(jì)。從那以后我每次做掃頻之前都強(qiáng)制先用第 4 章那個(gè)腳本把每個(gè)頻點(diǎn)的實(shí)際值和互質(zhì)條件過(guò)一遍不滿足就跳到下一個(gè)格點(diǎn)寧可曲線少一個(gè)點(diǎn)也不留一個(gè)假坑。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取