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

资讯详情

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

FPGA实现数字上变频DUC:从插值滤波到NCO混频的完整设计指南

FPGA实现数字上变频DUC:从插值滤波到NCO混频的完整设计指南 简介数字上变频DUC是软件无线电、雷达及高速通信系统中的关键信号处理环节这套FPGA工程资料围绕Xilinx Zynq平台展示了从RTL设计、IP集成、仿真验证到综合实现、硬件调试的完整流程适合具备FPGA开发经验并希望深入理解数字上变频实现的工程师与相关专业学生。压缩包约140MB共828个文件主要包含167个Verilog源文件、117个VHDL源文件以及dcp网表、xci IP核配置、xdc约束文件等Vivado工程文件同时提供仿真波形、综合报告、运行脚本和硬件定义文件可辅助重建完整工程环境开展时序约束与频谱搬移分析各目录下还保留txt说明与log日志便于排查实现过程中的问题。资源与作者Zynq学习笔记七配套目录按设计输入、综合实现、仿真验证和硬件调试组织内附bit流、ILA调试核及逻辑分析仪波形方便对照笔记理解各模块接口时序与上变频前后信号变化显著减少环境搭建与配置时间让使用者能够集中精力研究DUC算法映射、CIC/FIR滤波插值等核心问题是一份理论结合实践、便于扩展调试的完整FPGA学习工程包目前已有411人学习下载。 做通信或者软件无线电的朋友肯定绕不开数字上变频DUCDigital Up Converter这个东西。它本质上是发射链路里从基带往中频/射频搬移的“搬运工”但FPGA里实现好它并不是简单调个IP完事涉及插值滤波、混频、NCO设计和位宽管理一堆细节。这篇博文就把我实际做过的DUC工程从头到尾拆开讲包括为什么这么设计、参数怎么算、RTL怎么组织、上板怎么调希望能给你一条能直接上手的路径。适合刚接触FPGA通信信号处理的同学也适合正在做发射链路想参考设计思路的工程师。1. 先弄明白DUC在发射链路里到底干什么1.1 从基带到天线中间到底少了什么我见过不少刚入门的朋友把DUC简单理解成“把信号频率变高一点”其实这个理解不够完整。在真正的发射链路里基带信号通常是低速率、复数形式的基带IQ数据比如来自DAC前的成型滤波输出速率可能只有几十兆。但天线端需要的是高载波频率上、满足频谱模板要求的模拟信号这中间隔着采样率提升和频谱搬移两步。数字上变频负责的第一步是插值。插值的目的是提高采样率因为后级DAC的采样率往往远高于基带数据率同时我们还要通过插值滤波把基带信号在频域里的镜像频谱滤掉。第二步是混频把插值后的数字基带信号乘以一个数字本振NCO产生的正弦/余弦把频谱从零中频搬移到目标中频或射频。如果你不理解为什么插值会增加镜像可以想象一下DAC的采样保持效应采样率太低时镜像会直接落在带内你的发射信号就“糊”了。所以严格来说DUC至少包含三部分插值滤波器、数字混频器、NCO。三者协同完成采样率变换和频带搬移这跟我之前做DDC数字下变频正好是逆过程。很多人只关心“我调一个IP核就行”其实核心难点不在于调IP而在于对整个信号链路指标的把控比如带外抑制、无杂散动态范围SFDR和资源占用。1.2 一个最小DUC系统的信号链分解我习惯把一个DUC拆成前端和后端前端是“采样率变换整形滤波”后端是“频谱搬移”。举个例子如果你基带IQ速率是30.72MHz本地振荡目标频率是70MHzDAC工作在122.88MHz那么前端需要把30.72MHz的IQ数据插值4倍到122.88MHz后端再用NCO产生的70MHz本振去混频最后得到一个中心频率70MHz、采样率122.88MHz的数字中频信号送给DAC。前端的4倍插值又可以拆成两级2倍插值的半带滤波器HB1HB2也可以一级CIC补偿滤波器具体看你的插值倍数和通带指标。后端混频一般直接用一个复数乘法器把I/Q两路和NCO的正弦/余弦相乘得到实数中频输出。这里有个容易忽略的点DUC的输出通常只需要实数信号因为DAC只有一路物理输出。所以混频时做的其实是 I×cos(ωt) - Q×sin(ωt) 把复基带变成实中频。很多同学在这里写错符号最后频谱方向反了还不知道这点后面调试部分会细说。1.3 为什么说多级插值比一次插值聪明插值倍数大的时候我一向建议用多级插值而不是一个高阶FIR滤波器一步到位。假设你要做16倍插值直接设计一个16倍插值的FIR滤波器阶数可能需要近千阶乘法器资源直接爆炸。但如果你把它拆成4级2倍插值的半带滤波器每一级只需要十几到几十阶资源开销有数量级的差异。更关键的是滤波器系数的量化噪声和多速率处理的计算效率。半带滤波器有一半的系数是0FIR实现时可以跳过一半乘法运算量天然减半采样率还提高了一倍性价比非常高。CIC滤波器更夸张完全不使用乘法器全是加法器和延迟器适合插值倍数很大的场景但带内容差需要外加补偿滤波器修正。这些选型问题都得在开始写RTL之前就定好。2. 关键参数怎么算才算得明白2.1 插值倍数的选择逻辑从DAC速率反推插值倍数的确定我认为最靠谱的办法是从后往前倒推。先定DAC的工作采样率Fdac和可用的模拟抗镜像滤波器指标再定基带IQ数据率Fbb两者的整数比或者有理数比就是总插值倍数。举个例子系统要求基带IQ数据率Fbb 30.72MHz我们选了一款DAC它的实际采样率Fdac 122.88MHz那总插值倍数就是4倍。为什么不定成3倍或者5倍从指标推导30.72MHz的信号带宽大约25MHz采样率如果只到92.16MHz镜像和带外抑制要求会非常苛刻而122.88MHz是4倍关系半带滤波器实现得既省资源又能满足指标所以选4倍是合理的。如果你的Fbb和Fdac之间不是整数倍比如Fbb40MHz、Fdac122.88MHz那就需要有理数重采样器做分数倍插值。FPGA里实现分数倍插值比整数倍麻烦得多会涉及多相滤波器结构和FIFO速率匹配。我通常在系统设计阶段就会建议调整Fbb或Fdac尽量避免有理数倍除非指标实在绕不开。2.2 半带滤波器和CIC的分工界限很多新手纠结“用半带还是CIC”其实关键看插值倍数和过渡带要求。我的经验是插值倍数在2到8倍之间优先用半带滤波器因为半带通带平坦、阻带抑制高而且硬件实现简单插值倍数在16倍以上比如从几MHz变到几百MHzCIC加补偿滤波器更合适因为CIC的阻带衰减确实不如FIR但它的资源消耗几乎可以忽略适合作为第一级粗插值。当然CIC的增益随插值倍数呈指数增长。CIC插值因子为R、微分延迟为M、级数为N时增益是(R×M)^N。比如R16、M1、N4增益就是65536也就是16位输入时内部至少要用32位总线否则直接溢出。这个数字一定要算好我见过有人没做位宽扩展结果输出波形全是削波噪声还以为是滤波器系数问题。如果指标要求带外抑制大于60dB半带2倍插值设计到60dB以上没有问题如果要80dB阶数会高一些但大部分FPGA逻辑资源还是放得下的。CIC要达到60dB以上就得增加级数代价是带内平坦度变差所以实际工程里CIC后面必加等波纹补偿FIR这是一种很经典的组合。2.3 NCO频率控制字的定点计算NCO设计的核心是相位累加器频率控制字FTW和输出频率的关系是FTW (F_out × 2^N) / F_clk其中N是相位累加器位宽。设N32位、F_clk122.88MHz、需要输出F_out30.72MHzFTW (30.72M × 2^32) / 122.88M 1073741824也就是十六进制的0x40000000。这里计算上要注意单位一致全部用Hz。相位累加器全32位精度很高但查表不可能用32位所以一般会截取高17位或高19位作为查找表的地址。相位截断会引入杂散信号SFDR大概6.02×B dB其中B是保留的相位位数。17位相位地址理论SFDR约102dB实际加上幅度量化后通常也能到85-90dB以上够大部分通信指标用了。如果你希望NCO的杂散更低可以考虑加抖动dithering代价是噪底抬升一点。Xilinx的DDS Compiler里有抖动选项我建议默认打开除非你对极低频的杂散有严格约束。混频部分用复数乘法器时乘累加位宽也要仔细设计否则截断噪声和溢出会破坏你的SFDR这点不要偷懒该扩位就扩位。3. FPGA工程实操从RTL到IP核3.1 官方IP还是手写RTL我的选择标准Xilinx Vivado里提供了FIR Compiler、CIC Compiler、DDS Compiler和Complex Multiplier几乎覆盖了DUC所有核心模块。我个人的习惯是标准滤波器、NCO这类算法成熟且参数繁多的模块直接用官方IP因为官方IP经过了严格验证时序收敛也更容易但多通道时分复用、速率匹配逻辑这类和系统强相关的胶水逻辑一定要自己写RTL不要指望IP帮你包办一切。比如FIR Compiler可以配置成多通道、多相插值模式半带系数可以直接导入系数文件。DDS Compiler可以配置相位累加器位宽、输出位宽和SFDR甚至可以直接帮你生成Sin/Cos两路输出。你如果把官方IP用熟练DUC的RTL工作量会减少很大一部分。但代价是IP核内部的位宽截断策略是黑盒出了问题不好查所以我一般在IP外围自己加饱和保护和位宽记录逻辑方便上板调试。还有一个现实问题如果你在某国产FPGA平台上做很多IP也是兼容Xilinx接口的但细节不同。比如有些国产FPGA的DDS IP核起步配置和Xilinx不完全一样FIR Compiler的通道数和系数位宽限制也有差异。跨平台移植时最稳妥的做法是核心算法模块自己写RTL只把厂商IP用在乘法器、DSP Slice、RAM原语这些低层原语上这样代码可移植性最好。3.2 多通道DUC的时分复用设计如果你要做4通道或者8通道的DUC一个非常高效的做法是让所有通道共享一个滤波器组通过时分复用来实现。具体来说把4路IQ数据按序送入FIR IP核的多通道模式FIR Compiler内部会时分复用乘法器资源。比如4通道、2倍插值半带滤波器在相同硬件资源下实际吞吐是单通道的4倍但逻辑只增加了一部分控制逻辑。这里有个需要特别注意的点滤波器IP核的多通道模式下通道间串扰和滤波器的建立时间必须算清楚。FIR Compiler的通道延迟会随通道数增加而增大而每通道的数据相对延迟必须和后续混频对齐。我自己踩过坑4通道DUC前级数据是按时分复用的顺序送的但FIR之后的输出里同一时刻的4个样本却不是原来4个通道的对应序号导致后面NCO混频时把通道顺序调乱了出来的4路频谱全部对不上。后来我在滤波输出加了一个同步FIFO用轮询通道序号重新对齐才解决。多通道NCO的混频也要注意NCO本身是单通道连续输出的多通道时分复用混频需要按通道序号从相同的相位起点生成对应频率或者直接把NCO输出也做成时分复用模式。Xilinx DDS Compiler支持多通道模式它会每通道独立生成相位我推荐直接配置成多通道省得自己写相位映射逻辑。3.3 位宽管理和溢出保护是第一优先级DUC工程里位宽管理最影响最终指标。从基带IQ到插值滤波再到混频输出每级数据的位宽都在变化。如果每一级都保留全精度位宽会从16位一路涨到40多位DAC根本接不了如果乱截位噪声和杂散又会被放大。正确做法是每级进行细致的定点化分析。一个实用的经验法则是滤波器内部累加位宽不小于输入位宽ceil(log2(通道数))ceil(log2(滤波器增益))输出截位时保留足够的有效位。比如16位输入、半带滤波器系数求和增益约1内部累加器用32位输出截到18位这样既保证不会溢出又不过度消耗DSP资源。你把每一步的实际位数变化记录在设计和测试文档里后续一旦需要排查频谱上的诡异杂散这个记录能帮你省几个通宵。混频输出到DAC之前一般也是截位到DAC位宽比如DAC是16位混频输出可以截到18位再做饱和截断到16位。这里的“饱和”一定要做不要只做简单的截断否则信号峰值处会产生很大的谐波失真这在频谱上会形成明显的毛刺。4. 仿真、调试和实测那些事4.1 用MATLAB模型做golden reference我每次写DUC的RTL之前都会先搭一个MATLAB定点模型并且让模型与RTL逐位对齐。你会问这不做也影响不大不是的。没有golden referenceRTL仿真里就算你看到波形“大概像”也无法确认SNR、SFDR是否达标。做法其实不复杂MATLAB里把输入IQ数据源固定成一组正弦波或PRBS经过插值滤波、混频后把中间量输出为十六进制文本或二进制文件。RTL testbench里读取同一份输入数据经过DUC模块把RTL结果导出再在MATLAB中对比两者差异。逐拍比对时用到的误差容忍度设为0通常就能把位宽截断和特殊时刻的饱和行为全部逼出来。我在实际对比中发现过几个典型差异一是半带滤波器的初始状态不同导致前几个时钟周期输出不对要在比对时跳过滤波器延迟二是CIC补偿滤波器的增益补偿系数在定点化后会有零点几个dB的偏差需要在MATLAB里修正补偿增益三是时隙对齐的问题RTL里的延迟跟MATLAB模型不一致时比对会全部对不上。这些差异只有逐位对比才能快速发现并修正。4.2 ILA抓信号先会抓IQ再谈看频谱上板调试时ILA集成逻辑分析仪是最常用的工具。DUC的调试难点在于你要观察的不是单一波形而是一组IQ数据和混频后的中频数据。我建议把ILA的采样深度设置得足够大至少要包含一个完整周期的低频调制信号否则你看到的频谱分辨率和实际情况差异很大。抓数据时有个小技巧把ILA触发条件设为NCO相位累加器的某个特定值作为同步标记这样每次抓到的数据帧都从同一个相位开始方便观察频谱相位是否稳定。如果你直接在主机端做FFT用这个同步标记的数据帧做平均频谱的杂散底噪会清楚得多。我实际调试DUC的时候最头疼的问题是信号看起来全对但频谱中出现了额外杂散。后来发现是NCO输出的正弦余弦两路存在相位偏差不是严格的90度导致镜像抑制比下降。你抓ILA时不光要检查波形幅度还要检查I/Q两路是不是严格正交这一点很容易被忽略。4.3 常见问题与排查技巧速查以下是我在DUC调试中遇到的高频问题和对应的排查思路整理成表格方便大家对照。现象可能原因排查方法输出频谱出现明显镜像插值滤波器阻带抑制不足用MATLAB导入滤波器系数做频响分析优化系数阶数中频信号频谱方向相反混频时COS/SIN符号反了跟golden reference逐拍比对符号带内平坦度差CIC补偿滤波器增益不对检查补偿FIR在通带边缘的实际响应微弱杂散伴随主信号NCO相位截断/幅度量化查DDS配置的SFDR必要时打开抖动信号时有时无概率性错误跨时钟域没有做同步或FIFO溢出抓ILA看FIFO满信号和数据有效信号时序多通道串扰FIR多通道时分复用对齐错误用单一通道加正弦激励观察其他通道输出关于跨时钟域我必须多说一句。DUC里通常存在两个时钟域前级基带处理时钟和DAC采样时钟当两者不同源时必然要经过异步FIFO。异步FIFO的深度按突发数据长度加上裕量设计不要用同步FIFO硬扛否则概率性丢数据会让你抓狂。5. 资源优化和后续扩展方向5.1 一块Artix-7到底能塞几个DUC很多人关心资源我直接给一个我在Artix-7 200T上做的4通道DUC实测数据作为参考。每个通道插值4倍半带滤波器2级输出122.88MHzNCO 32位相位累加器、17位相位地址混频后输出16位。单个DUC通道约消耗不到200个DSP48E1和约4000个LUT4通道时分复用后总LUT能压到12000左右DSP约500个在200T上完全没有任何压力。所以如果你需要8通道甚至16通道核心瓶颈往往不是逻辑资源而是数据带宽和DAC接口。每条通道每周期要产生一个中频样本16通道就需要DAC接口在同一个时钟周期吞吐16个样本单DAC通常做不到。此时常见的做法是把多通道DUC的输出直接做数字合路送给一个高速DAC或者输出到JESD204B接口用多lane传输。这种架构设计反而是系统层面的主要考量。如果你做单通道就往高端卡里塞那资源当然没有压力。但FPGA设计的工程价值往往体现在“同样的硬件能不能塞更多通道”这要求你在算法选型时就要考虑资源复用多通道时分复用设计是最有效的路径。5.2 从DUC出发可以往哪些方向扩展DUC做顺手之后你会发现很多发射链路的模块其实是相通的。最简单的扩展是DDC数字下变频正好是DUC的逆过程结构上复用NCO和滤波器只是把混频放在前端、抽取放在后端。你做一个DUC加一个DDC基本就能拼出一个完整的收发信机数字中频处理链路。再往深走DUC还能和削峰CFR和数字预失真DPD结合。CFR通常在DUC之后、DPD之前用来降低信号的峰均比DPD则利用反馈通路观察功放输出计算非线性模型进行预失真。这两块和DUC都属于发射链路的数字信号处理范畴逻辑上可以共用NCO和滤波资源。如果你有做基站或射频直连收发机的机会这些技术栈会非常有价值。我自己的经验是DUC不是孤立的一块把它放进整个信号链里看才能把资源、时序、指标这些系统层面的问题想清楚。这也是为什么我特别强调先做MATLAB模型验证再上板调试的原因——一旦整个链路通了后续扩展就是“套模板”的过程。最后再说个实际体会DUC这类模块真正难的从来不是某一个乘法器或滤波器而是每一个模块之间的位宽、时序、通道对齐这些“看不见的线”。我做过几个DUC工程后养成一个习惯——任何数据接口都用结构体信号把IQ分开定义任何跨时钟域都画时序图再编码任何位宽变化都留文档记录。这些习惯看着琐碎但在项目后期追查匪夷所思的bug时能救你一命。如果你也在调DUC或者准备开始做不妨从一个小插值倍数的单通道设计入手配合这篇博文里的参数计算方法和调试思路一步步来。本文还有配套的精品资源点击获取
返回列表