FPGA基带与中频信号处理实战:从DDC到定点量化的完整链路解析
2026/9/7 1:10:59 网站建设 项目流程

上个月帮别人调一块双通道中频采集板,算法在仿真软件里跑得干干净净,一上板输出就乱跳,排查了三天,最后发现问题出在DDC后级抽取滤波器的截位策略上——为了省一个DSP48,把本该保留的2bit保护位直接砍掉了。这种事情在FPGA通信信号处理里太常见了:不是算法原理错,而是对FPGA上的位宽、时序、接口细节理解不够。这篇文章就围绕基带与中频的FPGA算法实现展开,把从信号链定位、核心算法到定点量化、接口调试的完整链路讲透,适合正在做FPGA通信/信号处理项目,或者在学FPGA但不知道基带中频到底该做什么的读者参考。

1. 基带与中频的边界感:先搞清楚FPGA在信号链里的位置

1.1 基带、中频、射频各自负责什么

一个典型的超外差收发链路里,射频、中频、基带三者各管一段。射频是天线口的信号,频率最高,比如2.4GHz或者5.8GHz;中频是射频通过混频器搬移下来的中间频率,常见的有70MHz、140MHz、450MHz这些值;基带则是最终承载调制信息的低速信号,频率最低,带宽也最窄。

FPGA在中间的定位很有意思。它既能处理中频采样后的高速数据流,也能做基带的调制解调和同步。前端ADC把中频模拟信号变成数字信号后,FPGA通过数字下变频(DDC)把信号从中频搬到基带;反过来,发射方向则是通过数字上变频(DUC)把基带信号搬移到中频,再交给DAC。这种"数字化中频+软件化基带"的架构,其实就是软件无线电的核心思路,而FPGA是这套架构里最灵活的载体。

1.2 哪些算法适合放FPGA,哪些该留给ARM和DSP

很多刚接触FPGA的人会问:我是不是应该把整个通信协议栈都写进FPGA?答案是否定的。我自己的判断标准有三条:

  • 数据率极高,比如ADC工作在250MSPS,16bit采样,一路数据就是4Gbps,这种吞吐CPU根本接不住;
  • 算法结构天然并行,比如FIR滤波、FFT、CIC抽取、相关运算,这些本质是乘累加,特别适合FPGA的DSP48和流水线;
  • 对时延要求极其严格,比如锁相环、功率控制环、同步环路,延迟抖动会直接导致系统发散。

反过来,低速率、控制逻辑复杂、协议栈厚重的任务,比如TCP/IP协议栈、文件系统、复杂的调度策略,这些更适合交给ARM或者DSP。Zynq这类异构SoC之所以流行,就是因为它把ARM和FPGA放在同一颗芯片里,各干各擅长的事。

1.3 一个典型收发链路里FPGA承担的任务清单

接收方向,FPGA接到ADC数据后依次做:DDC混频、CIC抽取、FIR补偿滤波、匹配滤波、AGC增益控制、定时同步、载波同步、均衡、解映射。发射方向则反过来:信源比特映射成IQ符号、脉冲成形滤波、内插、DUC数字上变频、送DAC。

这个任务链里每一步都对应着明确的资源消耗。比如一个32阶FIR滤波器,如果数据和系数都是16bit,做全并行实现需要32个DSP48;如果改用多相分解加时分复用,可以把DSP48数量降下来,但代价是控制逻辑变复杂。做系统设计时,先把这个任务链写清楚,再估算资源和时序,比直接打开EDA工具写代码要靠谱得多。

2. 基带算法上板:成形滤波、同步环路和调制解调的工程化思路

2.1 脉冲成形与匹配滤波为什么要放在一起设计

基带的脉冲成形滤波器通常用根升余弦(RRC),接收端也要一个匹配的RRC,两个级联等效成升余弦滤波器,保证抽样点上没有码间串扰。这个问题在原理上很干净,但工程实现有几个坑。

第一个坑是阶数选择。RRC滤波器的阶数越高,频带越干净,但延迟也越大,DSP48消耗成正比上升。我的做法是先定滚降系数α,α越小频谱利用率越高,但时域拖尾越长,对定时误差越敏感。0.2到0.35是常用区间。

第二个坑是滤波器的实现结构。FPGA里FIR最常用的是转置型结构,因为流水线天然打散,时序更好收敛。如果是多符号并行处理,比如一个时钟出4个符号,那就需要多相分解,把单个滤波器拆成4个子滤波器并行跑,每个子滤波器工作在1/4时钟频率下,DSP48数量不变,但对时钟要求大幅下降。

第三个坑是系数定点化。Matlab设计出的浮点系数直接塞进FPGA必然性能劣化。一般先做系数定点化仿真,观察EVM或者误码率劣化程度,我以前习惯把系数放大到16bit整数定点,再做归一化处理,这样在FPGA里乘法器直接有符号数相乘,不需要额外处理小数点的偏移。

2.2 Costas环与Gardner定时同步的FPGA落地

载波同步在FPGA里最常见的是Costas环,它对BPSK、QPSK这种抑制载波的信号尤其有效。Costas环的组成是:误差鉴相器、环路滤波器、NCO。误差计算有几种形式,最简单的BPSK用I*Q,QPSK用sign(I)*Q + sign(Q)*I。环路滤波器是典型的比例积分结构,两个系数Kp和Ki决定了环路的带宽和捕获速度。

这里的工程细节很值得说:NCO的相位累加器位宽直接决定频率分辨率。假设系统时钟100MHz,NCO累加器32bit,那么频率分辨率是100MHz除以2的32次方,约0.023Hz,这对基带同步完全够用。但如果为了省资源把累加器砍到16bit,分辨率变成1.5kHz,环路就可能永远锁不到准确频率上。

定时同步方面,Gardner算法是我用得最多的,因为它对载波相位不敏感,可以在载波同步之前先做。它的误差公式是(x(n) - x(n-2)) * x(n-1)的符号相关形式,抽取点在两个符号中间。FPGA实现时要注意,这个误差计算本身是组合逻辑,需要做流水线寄存,否则可能成为时序瓶颈。

2.3 调制映射与解调判决的细节坑

调制映射本质是查表,QPSK就是根据2bit查I、Q两路的符号和幅度,16QAM则是查4bit对应到16个星座点。这个模块逻辑很简单,但两个细节值得注意:比特序的定义必须和协议一致,大端小端搞反了,整个系统的星座图会发生镜像或者旋转;I/Q两路的极性定义也要统一,否则解调出来I路变成Q路,符号全乱。

解调判决的边界问题常被忽视。ADC进来的信号幅度会随信道变化,如果不做AGC归一化,固定判决门限就会失效。AGC在FPGA里的实现通常是:计算I/Q幅度或者功率,和参考值比较,通过环路调整前级可变增益。工程上有个经验:AGC环路带宽要远低于符号速率,通常取符号速率的1/100到1/1000,否则会把信号自身的幅度波动也压掉,导致星座点畸变。

3. 中频检波算法:同步检波、正交检波和包络检波怎么选

3.1 三种检波方法的原理差异

中频检波是把中频信号里的幅度、相位信息提取出来的过程,常见方法包括包络检波、同步检波和正交检波,三者适用场景差别很大。

包络检波只提取幅度信息。FPGA里最简单的做法是对信号取绝对值,再做低通滤波;也可以用CORDIC直接计算瞬时幅度。它的优点是不需要恢复载波,实现资源极少,一个ABS加一个FIR就能跑,适合AM解调、信号检测这类场景。缺点是拿不到相位信息,QPSK、QAM这类相位调制的信号没法解。

同步检波也叫相干检波,要求本地参考载波与信号载波同频同相,相乘后经过低通滤波得到基带信号。这种方案必须配一个载波恢复电路,工程上常用PLL或者Costas环。它的好处是输出信噪比高,适合低信噪比环境下的弱信号解调。

正交检波是最通用的方案,本地NCO同时产生cos和-sin两路正交本振,分别和输入相乘,得到I、Q两路基带信号。它本质上也是相干检波,但把信号分解为正交分量,后续做幅度、相位、频谱分析都非常方便。软件无线电里的DDC核心就是这个结构。

3.2 正交检波的数学推导和FPGA实现结构

这里我把正交检波的数学过程展开写一下。设中频信号为:

s(t) = A(t)cos[2πf_IF t + φ(t)]

本地NCO产生两路正交本振:

LO_I = cos(2πf_IF t) LO_Q = -sin(2πf_IF t)

中频信号分别与两路本振相乘,得到:

I_raw = s(t)cos(2πf_IF t) = 0.5A(t)[cosφ(t) + cos(4πf_IF t + φ(t))] Q_raw = -s(t)sin(2πf_IF t) = 0.5A(t)[sinφ(t) - sin(4πf_IF t + φ(t))]

经过低通滤波器把2f_IF的高频分量滤除后,得到:

I = 0.5A(t)cosφ(t) Q = 0.5A(t)sinφ(t)

幅度和相位分别是:

A(t) = 2√(I² + Q²) φ(t) = atan2(Q, I)

FPGA实现时,混频器就是两个有符号乘法器,低通滤波器是两级级联的抽取滤波。幅度计算里开根号用CORDIC的SQRT模式,反正切用CORDIC的atan2模式。Xilinx的CORDIC IP核直接支持这两种功能,配置的时候注意输入输出位宽要和前级匹配,迭代次数多一档,精度好一点,但延迟也会变大。

3.3 中频采样率选择与带通采样的实际权衡

中频信号采样有一个带通采样定理:对于中心频率f0、带宽B的带通信号,采样率不需要高于2f0,只要保证频谱混叠后不重叠就可以。具体计算要满足:

2f_H / n ≤ fs ≤ 2f_L / (n-1)

其中f_L和f_H是信号频带的上下边界,n是正整数。

举个例子,70MHz中频、10MHz带宽的信号,理论最低采样率可以做到20MSPS附近,但实际工程我很少卡着理论值做。原因有两个:一是卡边采样对模拟抗混叠滤波器要求极高,滚降不够陡的话边缘频谱会折叠进来;二是后续DDC的抽取倍数和滤波器阶数需要综合考虑。工程上更常见的做法是取80MSPS或者140MSPS,留足频谱保护带,ADC前端的抗混叠滤波器就很好设计。

3.4 检波方案选型对照表

检波方法是否需要载波恢复输出信息FPGA资源消耗典型应用
包络检波不需要幅度AM解调、信号检测、AGC辅助
同步检波需要PLL/Costas基带I/Q弱信号相干解调
正交检波需要NCO本振I/Q及幅相中低DDC、软件无线电、通用架构

实际项目里,如果是做一个通用的中频采集板,我建议直接上正交检波,因为硬件结构不变,后续通过改NCO频率就能适配不同中频,灵活很多。如果只是做一个简单的AM信号检测器,包络检波就够了,没必要把CORDIC和NCO都用上,降低成本也降低调试难度。

4. 绕不开的定点量化:所有FPGA信号处理成败的关键

4.1 为什么FPGA上不主用浮点

FPGA完全可以实现浮点运算,Xilinx和Intel都有浮点IP核,但代价很大:一个浮点乘法器占用的DSP和LUT资源数倍于定点乘法器,延迟也高很多。在基带和中频处理这种深流水线、高吞吐的场景里,浮点的面积和功耗开销很难接受。

更关键的是,定点并不是牺牲精度。一个16bit定点数的信噪比大约96dB,这对绝大多数通信链路已经足够了。真正的问题在于:定点数运算位数有限,乘法加法之后位宽增长,必须设计截位和饱和策略。如果这个策略设计错了,EVM劣化、误码率上升、环路失锁就会接踵而至。

4.2 Q格式与位宽计算的核心逻辑

定点数一般用Q格式表示,比如Q1.15表示1位整数位加15位小数位,数值范围在-1到1之间。12bit ADC的输出可以看成Q1.11,16bit滤波器系数可以看成Q1.15。两个Q格式数相乘,整数位数相加,小数位数相加,结果位宽是两者之和。

实际处理链路里,位宽增长是逐级累积的。乘一次,两个原始位宽相加;加一次,多1bit。比如12bit数据和16bit系数相乘,乘积需要28bit;一个128阶FIR做全并行累加,累加结果还需要多出约7bit来容纳128个数的和。这就是为什么DSP处理的中间级位宽动辄35bit起步,最终再根据输出精度要求统一截位。

4.3 CIC滤波器的位宽增长预估算例

CIC滤波器在DDC里应用极广,因为不需要乘法器,只做加减法,资源特别省。但CIC有个特性让新手非常容易翻车:积分器内部的位宽会持续增长,如果不预留足够位宽就会溢出。

工程上有一个明确的计算公式:

B_out ≥ B_in + N × log2(R × M)

其中B_in是输入位宽,N是CIC级数,R是抽取因子,M是微分延迟(通常取1或2)。

举个例子:输入12bit,抽取因子R=16,级数N=4,M=1,那么位宽至少需要12 + 4×log2(16) = 12 + 16 = 28bit。如果只看输出以为16bit就够了,直接在CIC中间截断,那信号必然被削顶或者产生严重失真。

4.4 截位饱和策略的经验总结

我的习惯是:每个处理级之间保留2到4bit保护位,全部运算完成后再做饱和截位到目标位宽。饱和的意思是,如果截位后数值超出范围,就钳制到最大值或最小值,而不是简单丢弃高位。这个逻辑需要用有符号数仔细设计,写不好的话,正数溢出会变成负数,产生剧烈的非线性失真。

另外强烈建议在做RTL之前,先用Python或者Matlab把定点量化行为建模出来,和浮点模型做对比。我在项目里常用的流程是:浮点模型验证算法正确性,定点模型验证位宽和截位策略,最后才写Verilog/SystemVerilog。定点模型的仿真结果可以作为后续FPGA板级调试的黄金对比参考值,这个价值在排错时无可替代。

5. 接口与时序:FMC、LVDS、PCIE和跨时钟域实战

5.1 FMC总线通信:STM32与FPGA的数据交换细节

FMC这个词有两层含义,一个是STM32的Flexible Memory Controller,一个是FPGA板卡上连接AD/DA子卡的高速连接器。工程里经常看到两者的组合:STM32通过FMC总线挂一个FPGA作为外部存储器,实现寄存器读写和数据交换。

FPGA端在这种架构下要模拟一个SRAM-like从设备。总线的核心逻辑是:STM32发起片选CS、写使能WE、读使能OE和地址A,FPGA用状态机解析这些信号,把写时序变成内部寄存器写入,把读请求变成数据输出。STM32的FMC时序寄存器需要配置地址建立时间、数据建立时间、地址保持时间,这几个参数要和FPGA内部状态机的时序对齐。

一个常见问题是FPGA采样FMC控制信号引入亚稳态。我的做法是:控制信号先通过两级同步器打拍,但数据总线不能简单打拍,因为数据采样的时间窗口要求更严格。最好用FPGA内部时钟对FMC时序做约束,通过IO delay或者其他方式保证在数据有效的窗口中心采样。

5.2 LVDS、MIPI、Biss-C这类协议的正确打开方式

FPGA经常要处理各种高速串行接口协议,LVDS接收、MIPI摄像头、Biss-C绝对值编码器都是典型场景。这些协议的共同逻辑非常相似:物理层用FPGA的高速IO和专用硬核(比如ISERDES/OSERDES),逻辑层通过移位寄存器解析比特流,协议层用状态机完成组帧、去帧和CRC校验。

我的经验是:这类协议调试时,一定要把"时钟恢复与字节对齐"作为一个独立阶段验证,不要一上来就调整条链路。比如MIPI CSI-2的接收,如果LP/HS状态切换、SoT包起始码对齐没做对,后面解析数据没有任何意义。Biss-C编码器更是如此,它的时钟和数据方向会切换,时序参数和CRC多项式都要逐项核对,每项都可能成为链路不通的原因。

5.3 跨时钟域处理与基带数据缓存

基带和中频链路天然是多时钟域系统:ADC采样时钟、DDC处理时钟、PCIE/DDR总线时钟、ARM侧时钟,每个时钟域之间都要安全传递数据。跨时钟域的三板斧:单bit脉冲用两级触发器同步,多bit数据用异步FIFO,流控用AXI-Stream的ready/valid握手。

涉及基带数据的连续性缓存时,乒乓缓存是经典方案。两块缓冲区交替工作,一块被写入数据的同时,另一块可以被DMA读出。这样数据流不会中断,代价是缓冲区需要两倍容量。如果缓存量很大,比如多通道长时间IQ数据缓冲,BRAM就不够了,必须用DDR3/DDR4,FPGA只实现DMA读写引擎和地址管理。这种场景下,仲裁逻辑和突发传输效率往往比缓存本身更值得优化。

5.4 时序收敛的几个实际问题

基带中频设计里时序不收敛是家常便饭,但多数违例的原因高度集中。最常见的是乘法器后直接跟长累加链,组合逻辑路径过长;其次是扇出过大,一个复位信号接了上千个触发器;还有IP核之间握手信号没有正确流水,导致数据通路上出现组合逻辑环。

解决办法也相对套路:把长累加链切成各级流水,用retiming来自动平衡寄存器位置;复位信号做全局同步,按模块分级复位;关键握手路径上插入寄存器级,通过AXI-Stream协议本身的valid/ready不限制延迟的特性,可以灵活插入流水。时序分析工具报违例时,先看是跨时钟域路径还是纯组合逻辑路径,两者对策完全不同,不要拿到报告就盲目插寄存器。

6. 进阶玩法:卡尔曼滤波、自适应算法和Zynq动态重配置

6.1 卡尔曼滤波在FPGA上到底能干什么

卡尔曼滤波在FPGA上应用越来越多,场景包括目标跟踪、传感器融合、信道估计、参数辨识等。很多人的第一反应是卡尔曼滤波涉及矩阵运算和求逆,FPGA实现会不会很难。其实分情况看。

对于一维标量的卡尔曼滤波,公式非常简洁,比如对距离测量值做滤波:状态预测、协方差预测、增益计算、状态更新。FPGA实现时真正麻烦的是除法,增益K的计算里有一个除法,可以用CORDIC除法器或者查表近似。如果状态量只有一两个维度,硬件资源占用很小,流水线设计也不复杂。

多维状态卡尔曼滤波就复杂多了,涉及矩阵乘法和矩阵求逆。工程上能用解析公式求逆就尽量用解析公式,避免在FPGA里做通用矩阵求逆。3x3以下的矩阵求逆可以直接写出公式展开,再用定点运算实现。

6.2 LMS自适应滤波的流水线化实现

自适应滤波在均衡、噪声对消、回声消除里都用得上。LMS算法的核心是两步:滤波器输出,权值更新。FLMS的问题在于权值更新依赖当前误差,而误差又依赖滤波器输出,这个反馈环路天然难以深度流水。

工程上常用延迟LMS(Delayed LMS)来破解:允许权值更新延迟几个时钟周期,把反馈路径切断,让乘累加链路可以流水化。延迟一定周期后,算法收敛性能略有下降,但换来的是时钟频率大幅提升。FPGA里做实时自适应滤波,这个取舍几乎总是值得的。

NLMS算法相比LMS增加了能量归一化,性能更稳定,但多了求倒数除法。FPGA里可以用查找表做倒数近似,或者使用CORDIC除法。如果收敛速度要求高而LMS步长难以确定,NLMS往往是更省心的选择。

6.3 Zynq软硬件协同与动态加载bitstream

Zynq是ARM加FPGA的异构平台,典型的软件无线电、雷达信号处理、运动控制方案都会用到。ARM侧可以跑Linux系统,负责网络通信、人机交互、任务调度;FPGA侧做高速信号处理,两边通过AXI总线交换数据。

更进阶的玩法是动态部分重配置:Linux运行期间,通过FPGA Manager框架或者直接操作devcfg寄存器,把新的bitstream加载到FPGA里,实现运行时切换算法。同一块板卡,早上跑DDC加解调,下午切换成频谱感知或者新的调制方式,只需要重新加载bitstream,不需要重新启动系统。

使用时需要注意:动态重配置过程中FPGA内部的状态会全部丢失,DDR、DMA、中断这些共享资源必须事先干净地暂停。还有一个容易忽略的点,部分重配置的bitstream生成流程和全量配置不一样,设计时必须提前划分动态区和静态区,不是随便一个工程都能在运行时重配。

6.4 这类系统适合的业务场景

卡尔曼滤波加自适应算法加动态重配置,这套组合最典型的场景是软件无线电和智能仪器:信号带宽、调制方式、工作频点经常切换,并且需要高实时性处理。另外一个典型场景是精密运动控制和机器人,FPGA处理编码器反馈和电流环,卡尔曼滤波做传感器融合,ARM做轨迹规划和交互。这些场景的共同点是算法维度不固定、实时性要求高、资源受限,FPGA几乎是唯一能同时满足的载体。

7. FPGA选型与调试:从仿真通过到上板跑稳的经验

7.1 选型先算资源,再对型号

做基带中频项目选FPGA型号,我的建议是不要从"哪个型号比较高级"开始,而是先把自己的资源需求算清楚。下面这个清单基本够用:

  • 乘加运算量:滤波器阶数乘通道数,对应DSP48/乘法器单元数量;
  • 存储需求:FIFO深度、系数表、数据缓存,对应BRAM/URAM容量;
  • 高速接口:PCIE、SRIO、千兆网需要的高速收发器数量;
  • 普通IO:LVDS通道数量、ADC/DAC接口位宽、Bank电压分组;
  • 时钟资源:MMCM/PLL数量是否够多时钟域使用。

把这五个方面算出来,再去主流厂商官网按资源筛选型号,效率高很多。入门级项目用Artix-7或者国产同等规模芯片就够了,Kintex级别主要用于多通道高速收发器或者大容量DDR缓存场景,Zynq则适合需要ARM跑Linux做上层任务的项目。

7.2 IP核、自研RTL和仿真验证的关系

基带中频链路里的通用模块,比如FIR、CORDIC、FFT、DDC/DUC,我建议尽量使用IP核而不是自己写RTL。IP核经过充分验证,接口规范,时序也优化过,自己写RTL反而容易踩坑。关键是要先跑IP核的官方示例工程,把接口时序摸清楚,再接入自己的设计。

自己写RTL的部分,推荐统一采用AXI-Stream接口风格。这套协议就两个核心信号:valid表示数据有效,ready表示接收端准备好,数据在两者同时为高时传输。模块之间用AXI-Stream连接,以后要插入DMA、FIFO、跨时钟域模块都非常方便,不需要频繁改接口逻辑。

调试基带中频链路时,ILA逻辑分析仪抓取的信号点选择很有讲究。我通常优先在DDC输出、同步环路误差信号、定时同步的抽样点、FIFO计数这几个位置放探针。这些点能把链路的健康状态直接反映出来,比漫无目的地刷所有中间信号高效得多。

7.3 上板调试的关键检查项

最后分享一份检查清单,是我反复踩坑之后整理出来的,每次项目上板之前走一遍,能省下大量排查时间:

  1. 逐级确认数据位宽和饱和策略,特别关注CIC这类位宽增长极大的模块;
  2. 确认所有IP核的时钟、复位是否同源且连接正确;
  3. 检查AD采样时钟到处理时钟的跨时钟域FIFO是否有溢出或空读;
  4. NCO混频频率的计算要重新核对,是否考虑了采样率和相位累加器位宽;
  5. 板级抓取关键节点数据,与定点模型的输出做逐点对比;
  6. 同步环路的初值、环路带宽、锁定指示信号是否正常;
  7. 上电后等待锁相环锁定、复位释放顺序正确后再开数据流;
  8. 大资源设计注意功耗和散热,高速收发器区域温度往往比预想高。

我自己的体会是,大多数"仿真通过但上板失败"的问题,都能在上述清单里找到对应项。尤其是截位策略和跨时钟域这两类,看起来很基础,但它们才是基带中频FPGA项目里最难排查、也最影响成败的环节。把基础环节做扎实,算法本身反而很少成为瓶颈。

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

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

立即咨询