资讯动态

FPGA点数受限下的正弦波发生器:从DDS到FIR滤波的完整设计

发布时间:2026/10/20 17:22:28 来源:尧图企业网站定制
前阵子调一个FPGA正弦信号发生器的小项目核心难点一直是DAC输出点数受限。最初我按教科书思路来把正弦一个周期的1024个采样点做成查找表相位累加器一驱动ROM里的数据依次送进DAC波形自然就出来了。可真到落板时片上存储被其他模块占走大半DAC接口的更新率也没那么充裕最后每个周期只能喂给DAC二三十个点。这时候示波器上看到的“正弦波”是一条条台阶频谱仪一扫二次三次谐波高得吓人。这篇文章就把这套“DAC输出点数受限条件下的完整正弦波产生器”的设计过程完整复盘一遍包括正弦数据怎么产生、相位累加器位宽怎么取、FPGA内部FIR低通怎么做、DAC后模拟重建滤波器怎么调以及联调时踩过的几个坑。适合正在做信号源类设计的同学也适合把FPGA当数字信号处理核心来用的朋友。1. 点数受限以后示波器上的台阶是怎么来的1.1 先搞清楚“点数”到底是谁决定的决定每个周期点数的不是查找表深度而是DAC更新率与输出频率的比值。比如DAC更新率是10Msps输出频率如果是10MHz一个周期只有一个点输出基本是一条直流在跳这时候你塞再多的ROM深度也没用。同理如果DAC接口是慢速串行方式哪怕内部时钟很快喂数据的速度就摆在那里每个周期的点数就是更新率除以输出频率。查找表只是提供“波形数据”实际把数据送出去的速率由DAC接口和采样时钟决定。在点数受限的系统里最常见的问题是大家把存储深度当成了输出点数这两个必须分开看。我在项目里一开始就把精力投在加大ROM上结果点数的问题一点没解决白折腾了两天。1.2 台阶和镜像到底从哪来当一个周期的采样点很少时理想序列经过DAC的零阶保持电路输出频谱变成原信号频谱的无数个副本每个副本以DAC更新率fs为中心幅度还沿着sinc频谱包络衰减。第一个镜像一般出现在fs减输出频率附近如果fs只比输出频率高几倍镜像会离基波非常近模拟滤波器很难在不伤基波的情况下把它压掉。这就是“点数少”真正麻烦的地方。台阶只是时域里的直观表现频域里那些镜像和次生谐波才是滤波器要对付的对象。做个简单估算输出1MHz、DAC更新率16Msps第一镜像在15MHz附近距离基波有15倍频程模拟端用两阶低通能压不少但若输出10MHz、更新率还是16Msps第一镜像可能跑到6MHz附近比基波还低这种情况下普通低通根本无法同时保留基波和压掉镜像只能提高更新率或者换带通架构。1.3 解决问题的整体路径到这里思路就清楚了点数受限时应该靠“数字端滤除带外 模拟端重建平滑波形”而不是靠硬加点数。数字端要做的事情是在DAC之前用FIR把镜像提前衰减降低模拟端负担模拟端则是用低通把最后一个台阶结构抹平同时兼顾DAC零阶保持带来的sinc滚降。后面几章的内容都沿着这条路径展开。2. 正弦数据从哪来完整查表、1/4压缩、CORDIC的取舍2.1 完整周期查找表最简单但最贵完整查表就是把一个周期均匀采M个点存成ROM相位累加器每时钟加一次把最高M位拿出来当地址。资源开销很直观M1024、16bit就是2KB。这个量看着不大但如果要同时支持多路输出、多种波形、多组幅值微调表几路下来就是几十KB。而且关键问题是在点数受限的场景里增加ROM深度解决的不是“DAC每个周期点位数”的瓶颈两者不是一个问题。适合完整查表的场景通常是单一固定波形、存储宽裕、要求DAC跑满速的系统。开发板例程喜欢这么干因为简单直观但到集成项目里就捉襟见肘。2.2 1/4周期对称压缩省3/4存储的思路利用正弦函数的对称性0到π/2存一个表其他象限通过地址反向和符号翻转映射过去。第一象限地址直接取相位低位第二象限对相位做折叠第三、四象限再处理符号。FPGA实现开销很小一个比较器、一个异或门、一个加法器基本就完事。存储直接变成原来的1/4等效点数相当于乘了4。同样是存储32个点1/4表和完整128点表在输出频率精度和杂散上几乎一样。我这次用的就是1/4压缩单路波形从8KB降到2KB给其他模块腾出了大量BRAM。经验之谈做1/4表时地址位数最好多留1到2位因为表尾和表头位置正弦变化的导数最大对量化误差最敏感多存几个点能让杂散更低。这个细节常规文档不会写但实测中确实明显。2.3 CORDIC实时计算一点存储都不用CORDIC算法把三角函数计算变成多次旋转迭代每步迭代只用移位和加减法逼近目标角度不占乘法器也不占BRAM。迭代N次结果精度大约对应N位二进制有效位。代价是一个输出点需要N个时钟周期。典型数据DAC更新率5MHz、迭代14次需要内部70MHz时钟中规模FPGA跑得很轻松如果更新率20MHz、迭代16次内部就要320MHz时序压力一下就上来了。所以CORDIC适合中低速场景优势是完全不吃存储劣势是输出点数速率受迭代次数卡脖子。2.4 我的最终选型我在这个项目里的选择是主生成路径用1/4压缩表保证DAC更新率跑满另写了一个CORDIC模块做特殊频点交叉验证两个模块共用同一条相位累加输出切换对比非常方便。如果让我给一个通用建议优先1/4压缩表既保速度又省存储只有当一块BRAM都挤不出来时才考虑CORDIC。速度要求极高、存储不是问题的场合上完整表也不是不行。实际项目不需要非黑即白双路生成往往更灵活。方案存储开销输出速度精度表现实现难度完整查表最大高高低1/4压缩表低高高中CORDIC几乎为零中受迭代限制与迭代次数强相关中高3. 相位累加器与频率控制字在点数受限下保留频率精度3.1 一上来就教大家算频率控制字DDS核心是一个反馈累加器每个时钟上升沿累加器加一次频率控制字FwordN位累加器溢出一次就代表输出完成一个完整周期。输出频率公式很直接f_out (Fword / 2^N) * f_clk反过来给定期望频率就能算FwordFword round((f_out / f_clk) * 2^N)举个例子f_clk 50MHzN 32想要1kHz输出Fword约等于85899频率分辨率约0.0116Hz。这个例子能说明一个容易被忽略的点点数受限不影响频率分辨率因为相位累加器位宽和每周期采样深度是两个维度。我见过有同学只把相位累加器做到16位结果想输出1kHz却出了0.99kHz频率误差一大截但每周期点数还是被DAC更新率卡着两个问题根本没有关联。3.2 相位截断、杂散以及注水抖动那点事相位累加器位宽很容易做宽但查找表不可能真存2^N个点所以只能截高位当地址把低位丢弃。这个操作叫相位截断会在输出频谱中产生离散杂散。杂散位置跟输出频率和截断位数有明确关系不是你随便挪一下滤波截止就能消掉的。工程上常用的改善办法有两个。一是加相位抖动在累加器最低位注入一个几bit的随机数把集中杂散的能量打散成噪声底SFDR观感能好很多。二是合理分配表深度在相位截断主导的区域表深度每增加1位相位量化带来的噪声可以下降约6dB但最终上限受DAC位数限制。所以表深度和DAC位宽要匹配不是越深越好。我自己的配置经验是相位累加器至少24到32位ROM地址位宽按DAC位宽加2到3位去分配。12位DAC配14位ROM地址每个周期16384点这时候波形精度已经很充裕低通做起来非常轻松。当然点数受限时未必有这么多资源具体按预算取舍。3.3 输出幅值位宽该留多少计算好的正弦值送到DAC之前通常还要经过FIR所以中间数据位宽最好比DAC位数多出2到4位避免滤波后峰值溢出或截位导致毛刺。这是个容易被忽略的中间环节。我的习惯是内部正弦表按16bit存储FIR输出先保留20bit最后再按DAC的实际位宽取高位并做饱和处理。点数越少越要保留中间余量因为量化噪声经过后续数字处理会被推来推去余量不足时波形顶端会出现很规律的毛刺。这个现象后面联调篇会详细讲。4. 先在FPGA内部做数字FIR低通把带外杂散压下去4.1 数字和模拟两级滤波到底怎么分工如果不加数字FIRDAC零阶保持产生的镜像和台阶谐波全让模拟滤波器扛。一个典型二阶低通在2倍截止频率处大约只能衰减24dB而镜像往往离基波不远还要压低到60dB模拟部分会非常痛苦。FIR在数字域里用几十个乘加器就能实现很陡的过渡带而且对温度、元件公差完全免疫。所以我坚持把FIR放在DAC之前让数字端先把最麻烦的镜像处理掉模拟端只负责把最后一点台阶和残余高频清干净。需要注意FIR并不能解决DAC固有的sinc滚降输出频率接近fs/4以后幅度就开始下跌这需要数字侧预加重或者干脆把模拟低通截止放宽别指望包络帮你做事。4.2 滤波器参数设计阶数、过渡带、阻带衰减FIR系数用设计工具生成关键参数是采样率、通带边缘、阻带边缘和阻带衰减。阶数不是拍脑袋定的工程上常用凯泽窗的估算式N ≈ (-20lg(δ_s) - 13) / (14.6 * Δf / fs)其中δ_s是阻带纹波Δf是过渡带宽度。举个例子fs50MHz通带边缘10MHz阻带边缘15MHz阻带衰减60dBΔf/fs0.1算出来N约33阶。如果过渡带改窄成12到13MHzΔf/fs只有0.02N直接跳到160阶。这个对比很说明问题FIR的阶数本质上是过渡带宽度决定的。点数受限系统里通常采样率不算高过渡带相对可以放宽一点阶数反而不会太夸张。不要一上来就设计一个极窄过渡带的滤波器资源上往往接不住。4.3 串行MAC实现和资源估算采样率不高时FIR不必并行实现。串行MAC的结构是输入数据打拍到移位寄存器系数按顺序从ROM读出一个乘累加器完成整个卷积。每个输出样本需要M个时钟周期所以内部工作时钟是采样率的M倍。举个例子33阶FIR、采样率10Msps内部只需要330MHz的时钟中规模FPGA还能跑如果160阶、采样率10Msps内部就要1.6GHz基本不可能实现。所以选阶数之前先算算内部时钟能不能满足这个顺序不能反。资源方面串行MAC只占一个DSP乘法器、一个加法器和一些RAM非常轻。延迟是M个时钟周期对信号源系统几乎无感。综合时给乘累加路径做好约束时序收敛并不难。4.4 FIR系数量化和累加溢出的坑设计工具默认输出浮点系数FPGA里要转定点。我一般先把所有系数归一化到DC增益为1再选Q格式量化系数幅度不能超过当前位宽能表示的最大值。累加器位宽必须比输入数据位宽多出log2(M)位否则中间过程就会溢出。这个坑我踩过FIR滤波后输出波形在峰值附近出现“削顶感”第一反应以为是模拟电路饱和查了半天发现是累加器溢出。把累加器位宽扩上去之后问题立刻消失。经验就是凡是看到波形顶部异常先在数字仿真里单独观察滤波器的中间节点别急着动硬件。5. DAC之后的重建滤波器RC升级到Sallen-Key的调试记录5.1 别忽略零阶保持那根sinc包络DAC零阶保持的频响是sinc形状幅度在接近fs时跌到很低。这个特性有时会给人“滤波器自带衰减”的错觉但实际测量时sinc包络和高频镜像混在一起经常出现“某个频点看起来被压住旁边又翘起来”的奇怪曲线。设计模拟低通时不能只看滤波器本身的曲线要拿完整的DAC输出频谱作为激励来源。低频输出时sinc包络平缓模拟滤波主导输出频率接近fs/4以后sinc本身就开始明显滚降波形幅度也随频率下跌。如果系统要求幅度平坦就得在数字侧做预加重不要指望靠调整模拟滤波器的截止频率来补。5.2 为什么直接上Sallen-Key而不是继续加RCRC低通虽然简单但一阶RC滚降太慢带外抑制不够。两级RC无源串联又会互相影响阻抗设计起来很别扭。Sallen-Key是有源二阶滤波运放起缓冲作用输入输出阻抗容易控制元件取值也比较灵活。工程上常用的单位增益Sallen-Key简化设计R1R2RC1取C2的两倍左右截止频率估算为f_c ≈ 1 / (2π R sqrt(2 C2^2))举个例子R10kΩC21nFC12.2nF算下来f_c约11kHz。需要更高截止频率就按同比例缩小R或C非常好调整。截止频率选择有个原则至少是输出最高频率的1.5到2倍否则你会看到正弦顶端变圆、幅值下降。但压镜像又希望截止低一点这本身就是矛盾。所以要在FIR设计时就规划好FIR把镜像处理得越干净模拟端截止就可以放得越宽波形反而更漂亮。5.3 Q值微调巴特沃斯还是贝塞尔不要为了追求带外衰减而把Q值调太高。对正弦波重建来说巴特沃斯最平坦是最省心的如果后续要做调制或者保持脉冲形状贝塞尔的群延迟更平。切比雪夫虽然带外衰减快但通带有纹波会把正弦顶部弄出周期抖动不建议用在信号源输出级。调试中要注意元件参数偏离会导致实际Q值升高频响在截止处隆起靠近截止频点的谐波不但没被压下去反而被放大。我在焊接后都会简单扫一下频率响应至少量一下转折点的电压有没有超过3dB的峰值有异常就及时换元件。5.4 实测结果的一个参考我连续对比了几组方案只靠二阶Sallen-Key在最高输出频率处谐波大约压到-40dB上下前面加33阶FIR后同样的模拟滤波器整体THD能再往下走十几dB。这不是精确指标不同DAC更新率、不同截止设计都不一样但方向很明确数字端每多压20dB模拟端的压力就小一个量级。没有专业频谱仪时可以用高分辨率示波器的FFT功能做粗测把采样窗口设置好也能看出谐波峰值。重点看二次、三次谐波和基波的差值这个最直观。6. 联调阶段最容易翻车的三个地方6.1 FIR输出位宽截断波形顶部出现规律毛刺现象很典型正弦波顶部出现等间距的小毛刺频谱上有额外的高次谐波看起来像滤波不足但又比滤波不足更规律。排查过程我先把FIR输出导入软件回放发现毛刺依然存在说明问题在数字段而不是模拟段。再检查中间数据位宽发现累加器输出截到16位时发生溢出峰值附近被削掉一部分。把FIR输出保留到20位并做饱和截位之后毛刺消失。建议遇到这类波形先做数字域回放把数字和模拟分开定位别一上来就改模拟滤波器很容易绕远路。6.2 DAC时序没约束好基波旁边长裙边现象是输出频率虽然稳定但频谱基波两侧出现小“裙边”听起来像频率在轻微抖动。一开始我以为是电源干扰后来看时序报告才发现DAC数据信号和时钟信号之间没有建立明确约束布线随意几个bit的路径延迟差太多等效成相位抖动。解决方式是在工程里把DAC数据总线、同步时钟归到同一个时钟组设置输出延迟约束然后在DAC端调整驱动强度和摆率配置。处理完以后基波旁边的裙边降了3到5dB。这个问题在高速DAC接口上特别值得提前做约束别等联调再补。6.3 模拟地没处理好低频段底噪高现象是示波器看到的噪声毛刺明显比频谱仪测出来的底噪大波形很“脏”。这个问题经常不是电路本身而是测量方法和地线布局。示波器探头如果夹着很长一段地线就相当于一个环路天线会感应共模噪声。换成弹簧接地针直接点在DAC输出座附近的地上噪声立刻小很多。板子设计上也要注意模拟地和数字地最好有一段铜皮隔离只在一点汇合避免数字开关电流窜到模拟输出。另外一个实用操作在滤波输出端并联一个几十皮法的小电容能把探头感应的高频分量压掉。这个小技巧在调试高频信号时非常管用。我个人的最终选型是1/4压缩表做主流生成、CORDIC做校验相位累加器32位FIR用33阶串行MACDAC后接二阶Sallen-Key整套系统在中规模FPGA上跑得很稳。回头再看点数受限不是“这方案没法做”的信号它只是帮我把方案逼到了更合理的位置——把工作尽量往前移让数字端先清场模拟端才有机会轻松。最后补一个小技巧如果接下来要做一个多频点可切换的信号源可以把不同频点对应的查找表偏移、FIR系数、滤波器配置做成一张配置表运行中只切换配置指针不需要实时重新计算频率控制字。听起来简单但我在实际项目里靠这个思路省掉了大量切换毛刺比现场重算DDS参数要稳定得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑