FPGA中频数字化处理与检波算法工程实战
2026/9/5 4:09:05 网站建设 项目流程

先说明一下:我把这个标题的产线定位为“一篇面向数字信号处理与FPGA开发者的实战技术总结”。类似的工程在通信设备、雷达、仪器仪表、软件无线电里都能见到,属于比较典型的中频数字化处理链路。下面我会按照一个真实的FPGA项目从方案论证到调试落地的顺序来写,涉及的算法会给出原理、参数选择思路和具体实现示例,内容尽量贴近实际工程操作。

1. 项目概述与总体方案选型

1.1 基带与中频处理在FPGA里的常见形态

这几年FPGA在信号处理领域的地位越来越高,尤其是基带和中频这一段。所谓基带,一般指没有经过载波调制的原始信号,频带从直流附近开始;中频则是经过一次或多次频谱搬移之后落在某个固定频率的带通信号,比如常见的70MHz、140MHz中频。FPGA的优势在于天然适合做并行、低延迟的实时处理,所以ADC采样进来的中频信号、或者DAC要发射出去的基带波形,都能在FPGA内部完成。

这个项目围绕的核心是:在FPGA内部实现基带和中频信号的处理算法链路,包括数字下变频(DDC)、数字上变频(DUC)、抽取/插值滤波、载波同步、符号同步、AGC、以及一些测量类的检波算法。对应的应用场景大致分两类:一类是通信收发机的物理层处理,另一类是频谱仪、示波器、信号分析仪这类仪器里的中频数字化模块。两类场景对算法的侧重不一样,通信更看重调制解调的性能和时延,仪器更看重测量精度和动态范围。

1.2 为什么用FPGA而不是DSP或ARM

搞嵌入式的人都清楚,ARM做控制、DSP做复杂算法、FPGA做高速并行处理,这是典型的组合拳。但放到基带和中频处理这个位置,FPGA几乎是绕不开的。一个100MHz采样率、256点FIR滤波器,在DSP里每秒钟要跑几千万次乘加,实时性压力很大;而在FPGA里可以通过流水线和并行结构,用一片不是很大的芯片就能轻松跑下来,延迟还稳定。

另一个容易被忽略的点是接口的灵活性。FPGA可以直接对接高速ADC和DAC的LVDS/JESD204B接口,省掉中间一级转换芯片,还能在FPGA内部做数据格式的预处理,减少PCB设计的复杂度。现在的FPGA还内置了DSP Slice,比如Xilinx 7系器件的DSP48E1,就是专门为乘加运算设计的硬核单元,一个时钟周期可以完成一次25x18的乘加,做滤波器和混频器非常合适。

1.3 方案定位与开发环境

我自己在做的这块属于仪器方向的中频数字化模块,先把方案说清楚,后面讲算法和代码就不悬空。

系统的主要规格如下:

  • ADC输入中频信号频率为140MHz,带宽20MHz,采样率122.88MHz(带通采样)
  • FPGA选用Xilinx Artix-7系列,具体型号为XC7A200T
  • 处理链路依次为:数字下变频(NCO混频+抽取滤波)-> 基带I/Q补偿 -> 基带FIR整形滤波 -> 检波和测量算法 -> 结果通过AXI总线送到嵌入式处理器
  • 同时实现一路数字上变频,把FPGA内部产生的基带信号搬移到中频输出给DAC

这个方案选Artix-7的原因很直接:成本适中,逻辑资源够用(200T有74万逻辑单元),内置了740个DSP48E1硬核,支持GTP高速串行收发器,以后想扩展PCIe或者万兆网接口也不受限。实际做下来资源占用在60%左右,余量比较充足,方便后续迭代。

2. 基带与中频算法的数学基础和编程风格

2.1 从模拟到数字:采样定理决定的处理边界

做中频数字化的第一个关键点是带通采样。直接对140MHz中频用奈奎斯特采样,至少需要280MHz以上的采样率,对ADC和FPGA的处理速率要求都高。实际工程里常用带通采样:信号带宽只有20MHz,用122.88MHz采样,信号频谱会被周期延拓,只要保证采样率大于2倍信号带宽,并且选择合适的采样频率,就能通过数字下变频把信号搬移到基带。

这里有个小心得:带通采样的频谱搬移方向不是固定的,取决于采样率是信号中心频率的几分之几。很多时候采进来之后频谱是反折的,I/Q解调出来后虚部和实部的正负关系要对调。如果你在调试中发现星座图镜像翻转,先检查这一步,比到处查代码高效得多。

2.2 定点化模型:从浮点到定点的损失控制

FPGA做算法不能直接跑浮点,所有数据都要量化成定点数。量化的带宽和精度直接决定了最终信噪比。我在项目里定了一条规则:ADC进来的数据统一用16bit定点,处理链路内部中间节点至少18bit,累加器用48bit。为什么这么定?

16bit输入对应大约96dB的动态范围,对于20MHz带宽的接收链路基本够用。但FIR滤波器卷积运算会带来溢出风险,每经过一级滤波,位宽增长大约log2(抽头数),所以内部节点要放宽到18或20bit。NCO产生的正弦余弦波形用18bit量化,可以保证混频后镜像抑制比优于80dBc。累加器用48bit则避免了CIC滤波器或FIR累加时的截位误差累积。

定点模型在写RTL之前必须先用MATLAB或Python建立浮点参考模型,然后逐级量化,对比定点浮点误差曲线。这一步跳过的话,后期在板上调试会非常痛苦:出了问题根本不知道是算法问题、量化问题还是硬件问题。

2.3 RTL编程中容易被忽视的时延一致性

FPGA算法和C语言最大的区别在于“并行”和“流水”。C语言写一个滤波器,循环累加就行,时序无关;Verilog写滤波器则必须考虑每个数据在哪个时钟沿到达哪个寄存器。尤其是I/Q两路信号,如果两路滤波器的流水级数不一致,出来的I/Q会有时延差,反映到星座图上就是“圆环”而不是标准的“点阵”。

我的习惯是:所有多路并行处理模块,一律用统一的“数据有效”信号来对齐时序。具体做法是每个模块都带一个s_axis_tvalid和s_axis_tready握手信号,数据在多路之间流动时,valid信号跟着数据一起打拍,最后在模块输出端统一对齐。这样虽然多花一点逻辑资源,但换来的是整个链路清晰可控,省去的调试时间远大于资源开销。

3. 核心算法实现:数字下变频与检波

3.1 数字下变频的经典三件套

数字下变频从功能上拆解是三件事:NCO产生本振混频、CIC抽取滤波、FIR整形滤波。这个结构从教科书到商业IP核都差不多,但工程实现里的细节差异可以很大。

NCO的实现通常用DDS(直接数字频率合成):一个相位累加器累加频率控制字,然后查表或通过CORDIC算法输出正弦余弦。查表法简单,但ROM容量和相位截断杂散是个矛盾。CORDIC则用迭代旋转计算三角函数,不需要大ROM,杂散更干净,但延迟稍大。我这边用的是Xilinx的DDS IP核,配置了30bit相位累加器、18bit输出,并行通道设为1。这个配置在122.88MHz时钟下,无杂散动态范围可以做到110dBc以上,完全满足仪器测量的需求。

混频器就是乘法器,Xilinx的DSP48E1可以直接完成。需要注意的一点是:混频后的数据位宽会增长,通常从18bit乘18bit变成36bit,需要合理截位。截位时不要直接截断,要带上舍入或抖动处理,否则会产生直流偏置和杂散,影响后续测量精度。

3.2 CIC滤波器与补偿滤波器的配合

CIC滤波器是数字下变频里性价比最高的抽取滤波器,因为它不需要乘法器,只用积分器和梳状器,适合做高倍率抽取。但它的通带衰减比较大,所以后面必须接一个补偿FIR来拉平通带。

我用的参数:抽取因子R=8,差分延迟M=1,级数N=3。对应的CIC增益是(R*M)^N = 512倍,这个增益在工程上要注意,防止溢出,必须在每一级积分配置足够的位宽。

CIC的幅频响应近似于sinc函数,在信号带宽边缘大概有0.9dB左右的衰减。为了补偿这个衰减,后面的FIR滤波器需要设计成反sinc形状。用MATLAB的fdesign.pulseshaping工具可以很方便地生成这种补偿滤波器系数,然后在FPGA里用FIR Compiler IP实现。这个组合做下来,整个链路的通带纹波可以控制在0.05dB以内。

3.3 中频检波的几种方法和选型

标题里的热搜词提到“中频检波有几种方法”,这里重点展开。检波的本质是从已调信号中提取包络或相位信息,常见的方法有四种:

  • 包络检波:最简单,利用二极管或整流加低通得到包络,模拟电路常用;数字化后则把取绝对值或平方再低通即可。
  • 同步检波(相干检波):需要一个和载波同频同相的本地参考信号,与输入信号相乘后低通,能完整恢复幅度和相位信息,抗噪声能力最强。
  • 平方律检波:把中频信号平方后低通,输出正比于功率,适合做功率测量。
  • 数字检波:通过数字下变频到基带后,计算I^2+Q^2再开方,得到信号的瞬时幅度包络;或者用CORDIC计算幅值和相位。

在FPGA里我最常用的是数字检波和同步检波。机械仪器里做一个简单的功率计,I^2+Q^2之后做一个滑动平均就能出稳定读数;但如果是做矢量信号分析,就要走到同步检波甚至是载波恢复环,对算法复杂度的要求完全不同。

3.4 基带I/Q解调的完整Verilog思路示例

先给一个简化版的下变频加检波的核心逻辑骨架,便于理解整个链路的节奏。实际工程的代码比这个长很多,而且会包含AXI接口、跨时钟域等,但核心算法部分就是这个形状。

module ddc_core ( input wire clk, input wire rst_n, input wire [15:0] adc_data, input wire adc_valid, output reg [31:0] i_data, output reg [31:0] q_data, output reg data_valid ); // NCO 输出 18bit sin/cos wire [17:0] sin_out; wire [17:0] cos_out; dds_nco u_nco ( .aclk(clk), .s_axis_phase_tvalid(1'b1), .s_axis_phase_tdata(FREQ_CTRL_WORD), .m_axis_data_tdata({cos_out, sin_out}) ); // 混频 wire signed [33:0] i_mix = $signed(adc_data) * $signed(cos_out); wire signed [33:0] q_mix = $signed(adc_data) * $signed(sin_out); // 抽取 + 低通滤波(这里用 FIR Compiler IP,这里以例化示意) wire [31:0] i_fir, q_fir; wire i_fir_valid, q_fir_valid; fir_decimate u_fir_i ( .aclk(clk), .s_axis_data_tvalid(adc_valid), .s_axis_data_tdata(i_mix[33:16]), .m_axis_data_tvalid(i_fir_valid), .m_axis_data_tdata(i_fir) ); fir_decimate u_fir_q ( .aclk(clk), .s_axis_data_tvalid(adc_valid), .s_axis_data_tdata(q_mix[33:16]), .m_axis_data_tvalid(q_fir_valid), .m_axis_data_tdata(q_fir) ); // 幅度检波:I^2 + Q^2,再做开方(用CORDIC IP) wire [63:0] mag_sq = $signed(i_fir) * $signed(i_fir) + $signed(q_fir) * $signed(q_fir); // cordic sqrt wire [31:0] magnitude; cordic_sqrt u_mag ( .aclk(clk), .s_axis_cartesian_tvalid(i_fir_valid), .s_axis_cartesian_tdata(mag_sq[47:16]), .m_axis_dout_tdata(magnitude) ); always @(posedge clk) begin if (!rst_n) begin i_data <= 0; q_data <= 0; data_valid <= 0; end else begin i_data <= i_fir; q_data <= q_fir; data_valid <= i_fir_valid; end end endmodule

这个小示例把混频、滤波、检波的链条串起来展示了。实际项目里我还会把AGC放在混频之前,防止ADC输出幅度波动导致后端饱和。AGC的实现一般是功率检测加反馈环路,反应时间常数要根据信号带宽来定,不能太灵敏也不能太迟钝。

4. 工程实现技巧与优化策略

4.1 滤波器设计:系数优化与资源权衡

FIR滤波器是基带中频处理里最占资源的部分,设计时主要权衡三个指标:通带纹波、阻带抑制、抽头数量。我用MATLAB的fdatool(现在叫filterDesigner)设计等纹波FIR,采样率122.88MHz,通带20MHz,阻带起始26MHz,通带纹波0.01dB,阻带抑制80dB。算下来需要127个抽头,在200T上做两个通道(I/Q)的滤波器,每个通道并行度设为1,大概消耗128个DSP48E1,占总量的17%左右,可以接受。

抽头数量太大时可以考虑多相分解,把一个长FIR分解成L个并行短FIR,每个短FIR在低时钟下运行,可以显著降低对DSP资源的要求。尤其是做宽带信号处理的时候,采样率高、抽头多,多相结构几乎是必须的手段。我最后用多相并行4路的方式,把时钟从122.88MHz降到了30.72MHz,DSP消耗也降到了原来的1/4。

4.2 跨时钟域与数据同步

FPGA工程里最隐蔽的bug大多出在跨时钟域。ADC的采样时钟和FPGA的处理时钟即使频率一样,相位也不同步;处理时钟和嵌入式总线的时钟更是完全异步。我的处理原则是:所有跨时钟域的数据,一律用异步FIFO + 握手信号;所有控制寄存器,用两级同步器打两拍。

具体到中频模块,ADC数据进入FPGA后先进入一个异步FIFO,写时钟是ADC随路时钟,读时钟是FPGA处理时钟。FIFO的空满标志要留足余量,否则在极端相位关系下会出现偶发的空读或满写。调试的时候用ILA抓ADC数据和FIFO输出,对比延迟和连续性,基本能定位绝大多数同步问题。

4.3 卡尔曼滤波与FPGA的实际结合方式

热搜词里“卡尔曼滤波 fpga”出现频率不低,我多说一句。卡尔曼滤波本质是递推最小均方估计,在FPGA里实现的核心是矩阵运算和除法,矩阵维度越高,资源消耗越大。实际工程里FPGA更适合做维数低、迭代率高的标量卡尔曼或二维卡尔曼。

我做过的场景是用卡尔曼滤波对NCO的频率控制字做平滑。接收机捕获阶段的频偏估计值噪声很大,直接用来调NCO会导致本振频率抖动,影响解调。解决方案是把频偏估计送入一个二维卡尔曼滤波器,状态量是频率偏移和频率变化率,观测量是频偏估计值。状态转移矩阵很简单,F = [[1, T], [0, 1]],运算量不大,但平滑效果非常明显。用Verilog实现矩阵加乘、再调一个除法IP做增益计算,不到500行代码就能跑起来。

需要注意卡尔曼滤波的数值稳定性。FPGA里做矩阵运算位宽有限,长时间迭代可能出现协方差矩阵负定导致滤波发散。解决方法是在更新协方差时加一个很小的正定修正项,这个在工程里叫“协方差修正”,能有效防止死机。

5. 仿真与调试实录

5.1 仿真环境与测试激励设计

FPGA算法验证分三层:模型仿真、RTL仿真、板级实测。模型仿真在MATLAB里做,主要是验证算法思路和量化策略;RTL仿真用Vivado Simulator或ModelSim,是写代码阶段的主战场。

测试激励的设计很关键。正弦波单音、双音、调制信号、带噪声信号,每一种都要测。单音用来验证频响和杂散,双音用来验证线性度和互调,调制信号用来验证解调性能,加噪声则用来验证算法在极端环境下的稳定性。我在工程里用MATLAB生成一个量化后的测试数据文件(.coe或.hex),在testbench里读入做仿真,这样能完全复现板上的输入条件,调试问题的时候能排除激励不一致的干扰。

5.2 板级调试时的ILA抓取技巧

板级调试我用Vivado的ILA(集成逻辑分析仪)。ILA的采样深度和触发条件是调试效率的关键。建议把关键节点都拉出来观测,包括ADC原始数据、混频后I/Q、滤波后I/Q、检波输出。触发条件设置为ADC数据超过某个阈值,这样能稳定抓到突发信号。

有个不太容易察觉的坑:ILA本身会占用BRAM和布线资源,如果工程本身资源紧张,插入ILA后时序会发生微妙变化,甚至导致原本正常的链路出现时序违例。所以我的建议是,先做成一个调试版本,把ILA加好,功能验证完、确认没问题之后,再做干净版本把ILA去掉,重新跑一遍全链路验证。

5.3 一次杂散问题的完整排查过程

印象最深的一次问题是中频输出端出现了一个-60dBc左右的固定杂散,频率偏移正好是1MHz。第一反应是电源纹波,用频谱仪看电源确实有1MHz的开关纹波,但幅度都很低,不像是直接耦合进来的。后来用ILA抓NCO的输出频谱,发现DDS输出的杂散里就有一个-75dBc的1MHz分量,到这里基本定位了。

查到最后是NCO的相位截断位数设置问题。DDS的相位累加器是30bit,但查表的地址只有12bit,相位截断误差以1MHz的速率周期性出现,形成了杂散。解决办法是把查找表深度增加到15bit,同时开启抖动注入,杂散直接从-75dBc降到了-90dBc以下。这个案例说明,FPGA算法调试必须结合仿真和板测,单独看时域波形很容易漏掉频域里的细节。

6. 常见问题速查与避坑清单

6.1 几个高频问题及解决方向

问题可能原因检查思路
IQ解调后星座图是一圈圆I/Q时延不一致检查两路FIR流水级数、握手信号是否对齐
输出频谱有周期性杂散DDS相位截断、电源纹波先抓DDS输出频谱,再检查电源
接收灵敏度偏低ADC采样相位不佳、前端增益不足检查采样时钟jitter、AGC阈值
滤波器通带纹波大抽取倍数和补偿滤波器不匹配重新设计补偿滤波器响应
AXI总线读数据时偶发错误跨时钟域不同步检查异步FIFO空满标志余量

6.2 工程实践中总结的避坑清单

第一,NCO输出位宽和混频器位宽要统一规划好。经常看到有人NCO输出18bit,混频乘法时却只用到高16bit,结果白白损失了2bit精度。正确做法是把数据对齐到同一尺度再相乘,或者用DSP48E1的原生位宽做截位。

第二,CIC滤波器的增益补偿要放在补偿FIR里一起做,不要在CIC后面加一个独立增益模块,白白增加逻辑。MATLAB设计补偿滤波器时,直接把1/(R*M)^N的增益倒数乘进系数里就行。

第三,AGC环路的时间常数要根据实际信号类型调整。匀速变化的连续波信号和突发模式的TDMA信号,对AGC响应速度要求完全不同,前者可以慢一点换取稳定性,后者必须快速收敛。建议把AGC的上升和下降时间做成可配置寄存器,方便现场调参。

第四,中频数字化系统中,采样时钟的抖动直接影响接收信噪比。如果ADC支持同步时钟输入,尽量用FPGA输出的干净时钟,而不是外部自由振荡时钟。板上时钟芯片的电源要单独处理,最好用LDO加磁珠隔离,避免数字电路开关噪声耦合到时钟上。

7. 工具链选型与不同需求下的扩展建议

7.1 FPGA开发板与芯片选型的实际建议

对刚入门的读者来说,选对开发板能省一半的学习时间。我自己的建议分三档:

基础学习用Xilinx Artix-7系列的国产开发板,比如黑金或正点原子的AX7系列,价格相对便宜,资料全,可以把Vivado的流程和基本算法跑熟。注意选板卡时要看有没有板载ADC/DAC,最好带FMC接口,以后可以扩展高速数据采集模块。

进阶做通信或软件无线电方向的,建议选Zynq系列,比如ZYNQ-7020或ZYNQ-7035。Zynq自带ARM Cortex-A9处理器,FPGA和ARM可以协同处理:FPGA做实时物理层算法,ARM跑LINUX做协议栈和控制面。这个架构非常接近工业产品的形态,学完可以直接过渡到实际项目。

高阶做仪器、雷达方向,可以考虑Kintex-7或UltraScale+。这类芯片的高速收发器数量更多,支持JESD204B接口,适合对接最新一代的高速ADC/DAC。缺点是成本和布线难度都高,不适合新手第一块板子。

7.2 与STM32H743的FMC通信设计要点

很多嵌入式的项目会用STM32做控制,FPGA做算法,两者之间用FMC总线(Flexible Memory Controller)通信。STM32H743的FMC接口支持SRAM/NOR Flash时序,FPGA端可以做成类异步SRAM从设备接口。设计的关键是把FPGA内部的AXI寄存器和外部FMC地址建立映射关系。

我的做法是:在FPGA里用Verilog实现一个简易的地址译码和读写时序转换模块。FMC的片选信号拉低、写使能拉低时,STM32将地址总线和数据总线上的数据写入对应寄存器;读操作同理。为了简化时序,我把数据位宽定为16bit,地址映射成一个基地址加偏移的二维表,ARM侧只需定义一个结构体指针就能访问FPGA内部的所有参数。要注意FMC的建立时间和保持时间配置,太快会导致数据采样不稳定,太慢则影响吞吐。STM32端的BANK配置一般用FSMC_SRAM的Bank1,具体参数需要根据FPGA端的时序约束来定。

7.3 图像处理和BISS-C等混合场景

至少一半的FPGA项目不止做射频通信。比如图像处理,FPGA擅长的是采集、缩放、滤波和视频时序生成,和基带处理的思路其实是相通的:都是对数据流做实时变换。图像里常用的均值滤波、高斯滤波、边缘检测,本质上就是二维卷积,和FIR滤波器的实现方式高度类似,只是数据的行列关系和行缓存稍有不同。

BISS-C是工业编码器常用的高速串行接口,可以用FPGA的LVDS接口直接接收。BISS-C协议的时序比较简单,核心是Master发送时钟和请求帧,Slave返回数据帧。FPGA实现时用状态机控制单向通信,数据按位拼装后通过AXI存到内存。这个场景的技术难点已经不是协议本身,而是如何在不同时钟域间可靠采集串行数据,以及如何检测错误帧和超时。

7.4 国密算法等加解密算法的FPGA实现参考

热搜词里有SM2、SM3、SM4,这些国密算法也有不少FPGA实现的场景,尤其是需要高速数据加密的板卡上。SM4是分组加密,串行实现比较容易,FPGA里主要是做一个轮函数,共32轮迭代,用流水线结构可以把一轮吞吐做到很高。SM3是哈希算法,计算过程以32bit字的布尔函数为主,FPGA实现也不难。SM2是非对称算法,涉及椭圆曲线点乘,运算很复杂,FPGA实现需要设计有限域运算单元,工程量大很多。

我的建议是,非对称算法如果对吞吐要求不高,放在配合的嵌入式处理器上用软件实现就行了;FPGA侧重做好对称加密和哈希,这样可以平衡开发成本和性能。软件开发包方面,JS和Java版本的国密算法库在开源社区里很丰富,用于上位机和数据中心联调比较方便,FPGA端只需要保证和标准测试向量一致即可。

8. 最后再分享一点个人的弯路经验

在做数据同步处理的初期,我也走过不少弯路。印象最深的是有一次滤波器输出数据看着波形正常,但是解调出来的星座图满天乱飞,花了两天时间反复调参数都无解。后来用MATLAB对比定点模型,才发现是I/Q两路滤波器用了不同的优化选项,导致其中一路自动合并了某些系数,两路群时延差了整整一个时钟周期。这种问题在浮点模型里根本不存在,只有落到RTL以后才会冒出来,所以凡是I/Q双路结构,一定要在代码里显式约束两路完全对称,绝不能靠工具的自动优化。

另一个心得是,仿真做得再充分,板上调试也一定会出现仿真里没见过的现象。仿真环境往往是理想的时钟和理想的电压,而真实芯片有温度漂移、有电源噪声、有片内工艺偏差。所以我现在的习惯是,在代码里尽量多留可读寄存器,把关键节点的信号值实时回读到ARM侧,这样即使不做在线逻辑分析,也能快速判断问题出在哪一级。

如果你也是从零开始做基带中频这条链路,我的建议是不要一上来就追求高指标。先把最简单的DDC、FIR、检波跑通,再一步一步加AGC、载波恢复和各种补偿算法,每一步都验证没问题再往下走。算法的坑大多藏在地基里,前面歪一寸,后面歪一丈。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询