十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基带与中频FPGA算法实现:从DDC到QPSK解调的完整工程指南

基带与中频FPGA算法实现:从DDC到QPSK解调的完整工程指南 通信FPGA这个方向我接触了快十年从刚毕业那会儿对着Xilinx文档一头雾水到现在能独立调通一发完整的收发链路中间踩过的坑比写过的代码还多。今天借“基带与中频的FPGA算法实现与应用”这个题目把那些真正值得沉淀的东西梳理一遍。这篇文章不是教科书式的原理复述而是围绕一个核心问题展开当你拿到一个信号处理任务该用什么思路在FPGA上把它落地并且做到资源可控、时序收敛、结果可靠。1. 先搞清楚基带和中频在FPGA里到底承担什么分工很多初学者最大的误区是一上来就翻IP核手册急着写RTL代码结果做了几个星期发现连需求都没理解透。实际上基带和中频在FPGA内部是两个性格完全不同的世界搞清楚它们的分工后面做算法才谈得上有的放矢。1.1 中频部分靠的是确定性的数学运算中频信号的本质是什么是带着载波频率的信号通常在几十MHz到几百MHz这个区间。FPGA在中频段干的事情可以归纳成五类数字下变频DDC、数字上变频DUC、滤波抽取、增益控制、以及各种检波处理。DDC的典型结构是混频器加CIC滤波器加FIR补偿滤波器再加抽取。混频器做的是把中频信号搬到零频附近。CIC滤波器负责大倍数抽取FIR负责补偿CIC的通带衰减和做精细滤波。这一套下来中频信号就变成了低速率的基带I/Q数据。DUC则是反着来内插、滤波、混频到中频。这里最容易出问题的地方是内插后的镜像抑制。做DUC的时候如果半带滤波器阶数不够、阻带衰减不足镜像分量会直接落在带外后端模拟电路怎么滤都滤不干净。中频部分的特点是运算类型非常固定基本都是乘加、延迟、累加这三板斧。它的难度不在于算法本身而在于时序和资源。一个128阶的FIR滤波器工作在250MHz采样率下如果用纯乘加器实现资源消耗相当可观如果用DSP48的时分复用结构就能把资源降下来。1.2 基带部分拼的是算法架构和状态控制基带信号通常是低速率的I/Q数据速率可能只有几MHz甚至几百kHz。到了基带处理的不再是波形层面的数学运算而是符号层面的解调映射、同步、均衡、编解码。基带部分的核心难点在于算法逻辑的复杂度上来了载波同步环里要分频偏估计和相偏跟踪两个阶段的切换符号同步里的定时误差检测TED有多种算法可选——早迟门、Gardner、MuellerMuller——每种适应的调制方式和滚降系数都不一样均衡器要决定是上LMS还是RLS抽头怎么定。这部分在FPGA里的工作方式和中频完全不同更多是状态机、查找表、反馈环路、缓存管理这一类的逻辑密集型设计。时序收敛反而相对好做一些因为速率通常不高真正考验人的是算法架构设计。这里给出一个核心建议中频部分优先考虑用IP核和成熟的DSP微架构基带部分优先自己设计控制通路和算法流水线。原因后面逐步展开。2. 中频检波到底有哪几种方法工程上该怎么选热搜词里有一个“中频检波有几种方法”这个关键词值得展开。检波就是从已调中频信号中提取出调制信息的过程。在FPGA工程里最常用的方法有三大类峰值/包络检波、同步检波、以及基于数字信号处理的I/Q正交检波。2.1 包络检波简单粗暴但要接受它的局限包络检波的原理是取中频信号的幅度包络。在FPGA里实现包络检波常见做法是先求绝对值再用低通滤波器平滑。比如用一个滑动平均窗或者一阶IIR低通把中频载波频率滤掉剩下的就是包络线。代码看起来很简单// 一阶IIR低通实现包络提取 always (posedge clk) begin if (rst) begin envelope 0; end else begin envelope envelope (abs_sample - envelope) * alpha; end end其中alpha是滤波系数决定包络跟踪的带宽。alpha太大会滤不干净载波分量alpha太小会导致包络失真。但包络检波有硬伤对信噪比要求高而且对于调相信号PSK无效——因为PSK信号的幅度没有变化包络里根本没有调制信息。所以在实际工程中包络检波主要用在AM解调、幅度检测和信号存在性判断这类场景比如AGC的功率测量、通道自检等。2.2 同步检波底噪好但对相位同步有要求同步检波就是让本地的参考载波与输入信号载波同频同相然后相乘取基带分量。从频域看同步检波相当于做了一次频谱搬移。它的优势是信噪比性能好AM信号同步检波比包络检波有大约3dB的改善对DSB/SSB信号更是必须用同步检波才能解调。FPGA里实现同步检波的基本结构是// 同步检波核心混频 低通 assign mixer_out if_in * nco_out; // NCO产生本地载波 // 低通提取基带 filtered_out fir_filter(mixer_out);难点在于“同频同相”这四个字。如果收发两端不是锁相的本地NCO的频率和相位必须靠载波恢复环路来跟踪。常见的载波恢复环路有Costas环和判决反馈环其中Costas环对BPSK/QPSK特别友好。2.3 I/Q正交检波FPGA里的主流方案真正在FPGA里大量使用的是I/Q正交检波。结构上输入中频信号分两路分别和NCO输出的cos/sin相乘得到I路和Q路然后各自低通滤波。为什么说正交检波是FPGA主流因为它的好处是信息完整。I/Q两路合起来幅度、相位信息一个不少无论后面是解调、测向、还是做频谱分析都能直接处理。而且I/Q方法和各种数字信号处理算法兼容性极好——FFT、数字下变频、均衡器全都基于I/Q工作。三种检波方式的对比如下检波方式硬件消耗可提取信息适用信号工程难度包络检波极低幅度AM、ASK最易同步检波中幅度、相位AM、DSB、SSB、PSK中I/Q正交检波较高幅度、相位、频率全类型较难工程选型时我的建议很直接除非是极简的幅度检测场景优先选I/Q正交检波。因为你现在省下的那几个乘加器将来做算法升级时一定会在时序收敛上加倍还回来。3. 频域那把尺子FPGA里的FFT工程化处理聊到基带和中频算法绕不开FFT。无论是做频谱监测、信道估计、还是基于频域的滤波FFT都是底层的基石。Xilinx和Intel都有现成的FFT IP核但直接用IP核不等于万事大吉实际工程里的坑一个不少。3.1 FFT点数、数据格式与时序的联调思路选FFT点数不是越大越好。点数N越大频率分辨率越高fs/N但处理延迟和资源消耗也线性增长。工程上要优先搞清楚你要分辨的最小频率间隔是多少你要处理的最短突发是多少这两个参数直接决定N。数据格式是另一个大坑。FFT IP核的输入输出都有多种定点格式可选。定点FFT的问题是字长增长——每一级蝶形运算都会引入舍入误差。如果中间截位策略不对噪声基底会明显抬升。我常用的做法是仿真时先跑一版浮点参考模型再把具体定点参数代入对比两者的频谱差异。FFT IP核里通常有配置缩放计划scaling schedule的选项这个必须根据输入信号最大幅度仔细算否则溢出溢出到怀疑人生。3.2 并行还是串行资源与时延的取舍FFT IP核一般提供四种架构选择流水线流式、基4突发、基2突发、以及轻量级流式。它们的对比如下架构资源消耗处理时延适用场景流水线流式高低连续数据流、实时性高基4突发中中突发数据、中等实时性基2突发低高非实时、资源受限轻量级流式最低高极低速率、极小面积在很多窄带接收机里数据是突发到达的两帧之间有明显空闲这时候用基4突发就够用省出来的资源可以让给后端的解调逻辑。在宽带实时处理场景里数据一直不断流那就老老实实上流水线流式。工程选型的核心逻辑不是选择最强架构而是选择刚好合适的架构。3.3 FFT之后那些容易被忽略的收尾工作FFT输出的是复数频谱数据很多人拿到幅度谱就完事了。但真实工程里后面还有谱峰搜索、去镜像、加窗补偿、以及fft leakage处理这些事。窗口补偿如果加了汉明窗或者海宁窗那幅度谱的峰值会偏低需要乘以窗口系数的倒数。谱峰搜索如果要做频率估计不能只取最大值那个频点更好用抛物线插值或者chirp-z变换在峰值附近做细化。去镜像实数信号的FFT结果正负频对称有些应用只需要一半的谱线处理的时候可以省掉一半的运算。有一次做宽带频谱监测我用1024点FFT加Blackman窗刚出来峰值偏了差不多1dB。排查半天才意识到是窗口增益补偿没做。这是非常基础但极容易忘的细节。4. 把算法放到可综合的层面去思考FPGA算法和PC上跑算法最大的区别是什么PC上是顺序执行的指令一条条过FPGA里是并发的所有模块一起动。这个根本差异决定了设计思路必须不一样。从算法公式到可综合RTL中间有一层很重要的思维转换简单说是三个关键词流水线、定点化、复用。4.1 乘法器资源是硬约束要学会“省着花”FPGA里最值钱的运算资源是DSP48也就是内嵌的硬乘加单元。一片中端型号的FPGA通常有几百到上千个DSP48听上去不少但用起来极快。一个复数乘法器要4个DSP48一个32阶复数FIR要32个DSP48做两路就翻倍三下两下资源就紧张了。控制DSP48消耗的几个招数乘法器复用通过时分复用用4个DSP48在一个时钟周期内分时完成两个乘法代价是吞吐减半。如果速率有余量这是非常划算的交换。转置结构FIR相比直接型FIR转置结构在滤波器抽头多时能够更好地利用DSP48的级联特性减少逻辑延迟。CORDIC替代乘法器做三角函数、极坐标转换这些运算时CORDIC算法只用移位和加减法不消耗DSP48特别适合DSP吃紧的设计。查找表换资源如果是固定的常数乘法比如某个固定的混频系数完全可以提前算好放成查找表用BRAM实现。4.2 从浮点到定点误差和位宽的拔河算法验证阶段大家用MATLAB或C浮点模型精度美得很。但到了FPGA里浮点运算要么消耗巨量资源做浮点IP核要么根本跑不到目标频率。所以绝大多数基带处理都走定点路线。定点的核心问题是位宽规划。位宽太小导致量化噪声大、甚至溢出位宽太大导致面积暴涨、时序恶化。一般的做法是先跑数据统计出各级信号的动态范围再逐级确定位宽。这个过程叫字长优化说法比较学术实际做起来就是从输入到输出逐级仿真观察各级信号的峰值和噪声底。一个实用的原则是对于通信接收链路从天线到解调输出整体增益和每级位宽要统一规划中频之前的数字增益保证信号到达解调器时幅度不过低也不溢出。定点化信号幂次不重要重要的是信噪比。在做定点仿真时盯着EVM误差矢量幅度或者SNR变化比盯着瞬时波形更有意义。4.3 时序收敛的常规检查和优化顺序写完RTL跑综合布线时序报红这是FPGA工程师最熟悉的日常。“时钟频率上不去”这个问题的排查顺序值得理理清楚因为很多人一上来就乱调。第一步查代码风格有没有在同一个always块里做太长的组合逻辑链有没有把异步信号直接打进同步逻辑有没有在时钟域边界忘记做同步处理第二步查关键路径打开时序报告找到最差路径看它从哪个寄存器到哪个寄存器路径延迟主要消耗在组合逻辑还是走线。如果是组合逻辑过长就拆流水线在中间多插几级寄存器。第三步查布局布线频繁报红的模块可能是布局太分散尝试在综合时加入模块级约束或者pblock让相关逻辑靠得更近。第四步查综合选项retiming、register duplication这些综合选项有时能带来立竿见影的效果但也会导致网表难读一般等代码优化失效时才用。5. 从算法到系统一次中频接收机的完整链路前面把各个模块单独展开讲了这一节串起来看一个完整的中频接收机是什么样的。这个案例是通用窄带接收机的中频基带处理链路涵盖一个很典型的需求10MHz中频输入、200kHz信号带宽、QPSK解调。5.1 链路级设计和高层指标拆解链路结构从输入到输出依次是ADC接口 → 数字混频NCO混频器 → CIC抽取滤波 → FIR补偿滤波 → AGC → 载波同步Costas环 → 符号同步Gardner环 → QPSK解映射 → 输出比特流。在设计之前先把核心指标定下来指标参数ADC采样率80MSPS中频频率10MHz信号带宽200kHz抽取倍数40输出符号速率100ksps调制方式QPSK这个实例里在混频后先是CIC抽取倍数20再是FIR抽取倍数2级联总共40。CIC负责大倍数抽取FIR做补偿和精细滤波。符号速率和采样率差800倍正好由抽取链路来消化。指标拆好后逐级参数的规划又回到前面几节讲的那些位宽、资源、时钟域、控制时序。这个整体思路有了具体干活的时候路就顺得多。5.2 完整链路中的几个控制要点实际跑链路时有一个点要特别提醒AQ借位问题和环路稳定时间。CIC抽取滤波器有一个著名的特性是寄存器位宽必须按公式严格计算否则会溢出。CIC的输出位宽公式为[ B_{out} B_{in} \lceil N \cdot \log_2(M\cdot R) \rceil ]其中N是级数M是微分延迟R是抽取倍数。比如输入16bit、4级CIC、抽取20倍、微分延迟1那么输出位宽至少要是 (16 \lceil 4 \times \log_2(20) \rceil 16 18 34) bit。很多人在这一步喜欢赌一把结果就是信号突变时溢出造成误码。AGC在启动阶段也值得留心。首发AGC和跟踪AGC是不同的工作模式。启动时AGC需要快速收敛把增益拉到一个合理范围跟踪时增益微调要慢避免把信号的调制信息给吞掉。这个慢快节奏如果没控制好接收机的误码性能会很难看。Costas环的环路带宽和环路滤波器参数也需要按照阻尼系数设计一般选阻尼系数0.707环路噪声带宽选取信号带宽的10%以内。环路带宽太宽相位噪声大带宽太窄捕获时间太长。具体值用仿真迭代比用理论硬算来得更快。5.3 实测中的效果与调优经验这版链路在Xilinx 7系列上跑资源占用大概是DSP48用了不到40%BRAM用了30%左右LUT用了50%左右。QPSK解调的EVM在信噪比20dB环境下大概做到3%以内误码率表现符合理论预期。调优过程中印象最深刻的两个地方第一个是CIC补偿滤波器的设计。直接用MATLAB的fdatool设计了一个通带补偿滤波器但因为CIC的幅度响应是 (\text{sinc}) 形状的补偿滤波器要用 (\text{sinc}) 函数的倒数来逼近。一开始设计阶数不够通带边缘补偿不足星图边缘发糊后来增加阶数才好。第二个是Gardner定时恢复环路的插值器用的是线性插值性能只能说勉强能用。后来换成立方插值器Farrow结构星图的聚类明显好了一个档次代价是多用了几个乘法器——非常划算。6. 工具链和调试验证从仿真到上板之后算法写得再漂亮仿真跑得再完美最后还是要落到板子上接受真实信号的检验。这个阶段遇到的问题往往是仿真环境里根本不会出现的。6.1 仿真验证从MATLAB模型到RTL的背靠背比对工程上最稳妥的验证流程是第一步用MATLAB或Python搭建浮点模型确认算法本身的正确性 第二步搭建定点模型确认量化误差在接收指标范围内 第三步写RTL用同样的测试向量把RTL仿真结果和定点模型结果一一对比。这个流程里的关键点在于测试向量必须是真实场景的不是随便给一段正弦波就完事。要拿一段真实采集的中频信号来做仿真并且要做信噪比扫描保证不同信噪比下行为都符合预期。6.2 在线调试三板斧ILA、VIO、还有逻辑分析仪板级调试时ILA集成逻辑分析仪是我用得最多的工具。抓数据有个技巧不要把ILA插到所有信号上那样不仅布线困难而且数据量太大没法看。正确做法是先在关键位置抓一小段数据确认基本走向没问题再逐级往后查。VIO虚拟输入输出则是调整参数的利器。环路带宽、AGC增益、导频相位这些都可以通过VIO实时改不用频繁重新编译。比起每调一次参数就重跑一遍综合布线这能省下大把时间。值得一提的是当算法逻辑出问题时不要急着怀疑FPGA逻辑本身先用示波器和频谱仪确认ADC输入的信号没有问题。我遇到过几次忙了半天最后发现是同轴线没接稳。6.3 高速接口容易忽略的坑LVDS和同步现在ADC和DAC大多走LVDS或者JESD204B接口。JESD204B这种高速串行接口的调通本身就是一个大工程要关注时钟同步和确定性延迟。相比之下LVDS接口简单一些但也要注意源同步时钟的约束和训练序列的对齐逻辑。很多中频算法的功能正确性最终取决于接口数据的对齐正确性。我在内联ADC接口时最常犯的错误就是bit顺序没对齐导致后面的频谱全乱了。所以拿到新板子的第一件事一定是先跑一个已知信号的接口测试确认数据落位正确再做算法调试。7. 给入门和进阶者的一些实在话文章写到这儿该聊的硬核内容基本都聊了。最后说几句这几年工作下来沉淀的体会也许对正在这条路上的读者更有参考价值。7.1 入门路线从工程角度切入比从理论角度高效很多朋友一上来抱着《数字信号处理》和《通信原理》啃试图先把数学吃透再动手。这个路径不能说错但效率确实不高。更好的切入方式是先搭一个能跑通的简单链路比如用开发板做一个DDS信号源加一个数字下变频在这个过程中理解每个模块的输入输出是什么再去回头看书上的公式那些公式一下就活了。基础工具方面建议优先把Vivado/Quartus、MATLAB/Simulink、以及Python的numpy/scipy用熟。这三样几乎覆盖了从算法到实现的全流程。学习顺序上建议先掌握Verilog/VHDL和基础时序约束再逐步深入DSP算法实现。很多调试技巧是在实际调试中自动积累的。7.2 进阶路线哪类项目积累的经验最有含金量在我的经验里扎实做完完整收发链路带来的成长远大于断断续续做各种零散模块。因为完整链路会让你面对很多模块级联才有的问题——增益规划、时钟同步、控制时序、定点误差传播这些都是面试和实际项目中区分深度的重要话题。如果条件允许选一个带真实ADC/DAC的项目从头到尾调一遍收获比做十个仿真项目都大。通信方向的读者可以从通用SDR平台入手图像方向的读者可以从MIPI接口的图像采集处理入手逻辑框架是相通的。7.3 遇到问题时的排查思路比代码本身更重要写代码是最容易的部分调试才是最见功力的。工程里遇到诡异问题先问三个问题时钟对不对复位释放时机对不对数据位宽和数据顺序对不对这三个问题排查干净能解决掉九成以上的表面问题。剩下那不到一成的深层问题往往才是真正让人成长的。碰到这类问题时把仿真波形、在线抓的信号、以及MATLAB重跑的结果三者放在一起对比通常能找到线索。这条“三源对比”的排查思路在多次实战里帮我节约了大量时间。FPGA上的基带与中频算法实现前期是数学、中期是工程、后期是经验。这篇内容没有覆盖所有细节但把最核心的思路和最常见的坑都点到了。希望大家都能在调试通过的那一刻觉得之前的折腾值得。
返回列表