ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

FPGA工程师必看:FDA Tool中CIC滤波器设计的5个隐藏陷阱与避坑指南

FPGA工程师必看:FDA Tool中CIC滤波器设计的5个隐藏陷阱与避坑指南

1. 从一次采样率转换的翻车说起

前阵子帮一个做音频采集的朋友排查问题,他要在FPGA里做数字下变频,前端采样率跑到了 96 kHz,后端处理只需要 8 kHz,中间差了整整 12 倍。他图省事,直接在 MATLAB 里用 FDA Tool(Filter Designer)拖了一个 CIC 滤波器出来,参数看着挺漂亮,结果综合上板之后输出全是毛刺,频谱上混叠糊成一片。他拿着截图来问我:“明明仿真波形是干净的,怎么一上板就废了?”

这个问题其实特别典型。CIC(Cascaded Integrator-Comb,级联积分梳状)滤波器在多速率信号处理里几乎是绕不开的一环,尤其是做抽取(Decimation)和插值(Interpolation)的时候,它不需要乘法器,只用加减法和延迟单元就能实现,资源占用极低,特别适合高速数据流的前端处理。MATLAB 的FDA Tool里内置了 CIC 滤波器的设计入口,很多人第一次用觉得“点几下就出来了”,但真正踩过坑的人都知道,这个工具在 CIC 模式下有几个非常隐蔽的设定,稍不注意就会掉进去。

这篇内容我打算把我在实际项目里遇到的、以及帮别人排查过的 CIC 设计问题系统梳理一遍。适合两类人看:一类是刚接触多速率滤波、准备用 FDA Tool 做 CIC 设计的工程师;另一类是已经用过但被仿真和实测不一致折磨过的朋友。我会从工具入口的隐藏逻辑讲起,把位宽增长、差分延迟、频率掩码这些关键参数的来龙去脉说清楚,再给出可以直接复现的排查链路。全文基于 MATLAB 常见版本的通用行为,具体版本差异我会在涉及的地方标注出来。

2. FDA Tool 里 CIC 模式的入口逻辑与常见误操作

2.1 为什么你在 Filter Designer 里找不到 CIC 选项

很多人打开filterDesigner命令之后,在 Response Type 里翻来翻去,只看到 Lowpass、Highpass、Bandpass、Bandstop 这几个,压根没有 CIC 的影子。于是就开始怀疑是不是版本不对,或者是不是要装什么工具箱。其实 CIC 滤波器在 FDA Tool 里不是通过 Response Type 进入的,它的入口藏在Design Method这一层。

正确的操作路径是这样的:先把 Response Type 选成Lowpass,然后在 Design Method 区域里选择IIR或者FIR都行,关键是下面那个下拉框里要找到CIC Decimator或者CIC Interpolator。这两个选项只有在特定条件下才会出现——具体来说,当你在 Design Method 里选了 FIR,并且把 Response Type 限定为 Lowpass 时,CIC 相关的选项才会出现在方法列表里。如果你选的是 Highpass 或者 Bandpass,这个选项直接消失。

注意:不同 MATLAB 版本里这个入口的位置略有差异。R2015b 到 R2018a 之间,CIC 选项在 FIR 方法列表的靠下位置;R2019b 之后界面重构过一次,CIC 被归到了 “Multirate” 分类下。如果你死活找不到,先确认 Response Type 是不是 Lowpass。

这个设计逻辑其实是有道理的。CIC 滤波器本质上是一个低通滤波器,它的频率响应是 sinc 函数的形状,主瓣在低频,旁瓣衰减缓慢。所以工具强制你先把响应类型定成低通,才允许你进入 CIC 的设计流程。很多人卡在这一步,以为是软件 bug,其实只是没理解工具的设计哲学。

2.2 抽取和插值:两个入口不能混用

CIC Decimator 和 CIC Interpolator 是两个完全独立的入口,选错了后面全盘皆输。我见过有人要做 4 倍抽取,结果手滑点成了 Interpolator,仿真出来的采样率反而变成了原来的 4 倍,波形被拉伸得面目全非,还以为是滤波器系数算错了。

判断方法很简单:抽取是降采样率,插值是升采样率。如果你的系统是从高速 ADC 往低速处理链路走,那就是抽取;如果是从低速基带往高速 DAC 走,那就是插值。在 FDA Tool 里,Decimator 的参数面板会多出一个Decimation Factor的输入框,而 Interpolator 对应的是Interpolation Factor。这两个因子决定了 CIC 滤波器的核心结构——积分器的数量、梳状器的延迟,都和这个因子直接挂钩。

还有一个容易忽略的点:CIC 的差分延迟(Differential Delay)参数。默认值是 1,大多数场景下也确实用 1。但在某些需要更陡峭频率响应的场合,会把它设成 2。这个参数改变的是梳状器部分延迟单元的个数,直接影响频率响应的零点位置。如果你发现滤波器的阻带衰减不够,可以试着调这个值,但要注意它会让通带衰减变得更严重,后面我会详细说这个权衡。

2.3 频率掩码设置:最容易埋雷的地方

FDA Tool 在 CIC 模式下会要求你设置Frequency Specifications,包括采样率(Fs)、通带频率(Fpass)、阻带频率(Fstop)等。这里有一个非常隐蔽的坑:CIC 滤波器的频率响应是固定的 sinc 形状,你设的这些频率点并不会改变滤波器的系数,它们只影响工具给你画的掩码图和生成的报告。

换句话说,你在 FDA Tool 里把 Fpass 设成 1 kHz,Fstop 设成 2 kHz,工具会画出一个理想的矩形掩码,然后告诉你“设计满足/不满足”。但 CIC 的实际响应是由抽取因子、差分延迟和级数决定的,跟你设的 Fpass/Fstop 没有直接关系。很多人看到掩码图上一片红(不满足),就拼命调 Fpass/Fstop,结果怎么调都不对,因为方向错了。

正确的做法是:先根据系统需求确定抽取因子和级数,算出 CIC 的实际频率响应,然后再把 Fpass/Fstop 设成和实际响应匹配的值,用掩码图来验证而不是设计。这个思维转变很关键,我在第 4 节会给出具体的计算过程。

3. 位宽增长:仿真干净但上板溢出的根源

3.1 CIC 增益的数学推导

CIC 滤波器有一个非常反直觉的特性:它的增益会随着抽取因子和级数的增加而急剧增长。这个增益不是线性的,而是指数级的。具体公式是这样的:

对于 N 级 CIC 抽取滤波器,抽取因子为 R,差分延迟为 M,其直流增益为:

Gain = (R × M)^N

举个例子,R=12,M=1,N=5 的时候,增益是 12^5 = 248832。也就是说,输入信号如果幅度是 1,输出幅度会变成 24 万多。这个数字在浮点仿真里完全不是问题,MATLAB 会自动用 double 类型处理,波形看起来完美无缺。但一旦你要把系数或者中间结果定点化,这个增益就会直接把位宽撑爆。

我那个朋友的案例就是这个问题。他在 FDA Tool 里设了 R=12,级数用了默认的 5 级,仿真的时候用的是 double,输出幅度大但形状对。上板的时候他用了 16 位定点,结果中间积分器的累加结果直接溢出,波形被截断,频谱上全是混叠产物。他以为是滤波器设计错了,其实是位宽没算够。

3.2 用 FDA Tool 查看增益和位宽建议

FDA Tool 其实会给你提示。在 CIC 设计界面的下方,有一个Filter Arithmetic的选项,选成Fixed-point之后,工具会显示一个Quantization面板,里面有一栏叫Input Word Length和Output Word Length。如果你把 Input Word Length 设成 16,工具会自动算出一个推荐的 Output Word Length,这个值通常就是考虑了 CIC 增益之后的结果。

但这里有个细节:FDA Tool 给出的位宽建议是保守估计,它假设最坏情况下所有积分器都达到满量程。实际工程里,如果你能保证输入信号的动态范围有限,可以适当缩减位宽。我的经验是,在工具建议的基础上减 2 到 3 位,然后通过仿真验证是否溢出。如果仿真里没有出现削顶,那就可以用。

具体操作上,你可以这样验证:在 FDA Tool 里设计好 CIC 之后,点击Realize Model生成 Simulink 模型,然后在模型里加一个Scope观察中间节点的数据。如果看到积分器的输出接近饱和,就说明位宽不够;如果离饱和还有很大余量,就可以试着缩减。

3.3 定点化的实操步骤与避坑

定点化 CIC 的时候,我一般会按这个流程走:

  1. 确定输入位宽:根据 ADC 的实际输出位数来定,比如 14 位 ADC 就用 14 位输入。
  2. 计算理论增益:用上面的公式算出 (R×M)^N。
  3. 计算所需位宽:输入位宽 + ceil(log2(增益))。比如 14 + ceil(log2(248832)) = 14 + 18 = 32 位。
  4. 在 FDA Tool 里设置位宽:把 Output Word Length 设成算出来的值,或者用工具的建议值。
  5. 仿真验证:用实际信号跑一遍,看有没有溢出。
  6. 逐步缩减:如果余量很大,每次减 1 位,重新仿真,直到出现溢出为止,然后回退 1 位。

提示:CIC 的积分器部分最容易溢出,因为它是累加结构,误差会累积。梳状器部分因为是差分,反而相对安全。所以在分配位宽的时候,积分器要比梳状器多留 2 到 3 位余量。

还有一个坑:FDA Tool 生成的定点系数默认是舍入到最近偶数,这个在大多数情况下没问题,但如果你的 CIC 级数很高,舍入误差会累积。我遇到过 N=8 的时候,舍入误差导致阻带衰减比理论值差了 6 dB。解决办法是在 FDA Tool 的Quantization面板里把Round Mode改成Convergent或者Floor,然后对比一下哪个效果更好。这个没有绝对的最优解,得根据你的具体信号来试。

4. 频率响应验证:掩码图不达标怎么办

4.1 CIC 频率响应的固有缺陷

CIC 滤波器的频率响应是 sinc 函数的 N 次方,这个形状有两个天生的问题:通带衰减和阻带衰减不够。

通带衰减是指,在通带边缘,CIC 的幅度已经掉下来了。比如 R=12、N=5 的时候,在归一化频率 0.1 处,衰减可能已经有 3 dB 了。这意味着如果你直接把 CIC 的输出当作最终结果,通带内的信号已经被压低了。阻带衰减不够是指,sinc 函数的旁瓣衰减很慢,第一旁瓣只比主瓣低 13 dB 左右,N 级级联之后会改善,但改善速度是 20×N dB/decade,对于高抽取因子来说,往往达不到系统要求的 60 dB 或 80 dB。

FDA Tool 的掩码图会把这些缺陷暴露出来。你设一个理想的低通掩码,CIC 的实际响应肯定不满足,通带边缘掉下去了,阻带也没压够。这时候很多人就慌了,开始乱调参数。

4.2 用补偿滤波器拯救通带

正确的做法是在 CIC 后面级联一个补偿滤波器(Compensation Filter)。这个补偿滤波器的频率响应是 CIC 响应的倒数,在通带内把掉下去的幅度抬起来。FDA Tool 里可以直接设计这个补偿滤波器:在 CIC 设计完成后,点击Design Compensation Filter按钮,工具会自动根据当前 CIC 的响应生成一个 FIR 补偿滤波器。

补偿滤波器的阶数需要权衡。阶数越高,补偿越精确,但资源消耗也越大。我的经验是,对于 R=12、N=5 的 CIC,补偿滤波器用 15 到 20 阶就足够了,再高收益递减。在 FDA Tool 里,你可以手动指定补偿滤波器的阶数,然后观察通带平坦度的改善情况。

注意:补偿滤波器会引入额外的延迟,如果你的系统对延迟敏感,需要把这段延迟算进去。另外,补偿滤波器是在抽取之后工作的,所以它的采样率是降采样之后的速率,设计的时候要注意 Fs 的设置。

4.3 多级 CIC 级联的取舍

如果单级 CIC 的阻带衰减实在不够,可以考虑多级 CIC 级联。比如第一级 R=4,第二级 R=3,总抽取因子还是 12,但频率响应会比单级 R=12 好很多。这是因为每一级的 sinc 形状不同,级联之后旁瓣会互相抵消一部分。

在 FDA Tool 里做多级 CIC 稍微麻烦一点,需要分别设计每一级,然后手动级联。具体步骤是:先设计第一级 CIC,导出系数;再设计第二级,导出系数;然后在 Simulink 或者你自己的代码里把两级串起来。每一级的位宽要单独计算,因为增益是逐级累积的。

我一般会在以下情况考虑多级 CIC:单级 CIC 的阻带衰减比要求差 10 dB 以上,或者通带衰减超过 1 dB 且补偿滤波器搞不定。多级 CIC 的代价是控制逻辑更复杂,延迟更大,所以不是所有场景都值得。

5. 仿真与实测不一致的排查链路

5.1 第一步:确认仿真用的是不是定点

很多人说“仿真和实测不一致”,结果一问,仿真用的是 double,实测用的是定点。这俩根本不是一回事。CIC 的定点行为跟浮点差异很大,尤其是位宽不够的时候,浮点仿真完全看不出来。

排查方法:在 FDA Tool 里把 Filter Arithmetic 设成 Fixed-point,重新生成模型,再跑仿真。如果定点仿真和实测对不上,那才是真正的问题。如果定点仿真和实测一致,但和浮点仿真不一致,那说明是位宽或者舍入模式的问题,回到第 3 节去调。

5.2 第二步:检查采样率转换的位置

CIC 抽取滤波器的工作位置很关键。它应该放在高速侧,也就是抽取之前。如果你把它放在了抽取之后,那它的输入采样率已经降下来了,频率响应会完全不对。这个错误在 Simulink 里特别容易犯,因为 Rate Transition 模块的位置很容易放错。

检查方法:在 Simulink 里看 CIC 模块的输入端口采样率。如果输入采样率等于输出采样率,那说明你放错位置了。正确的应该是输入采样率是输出的 R 倍。

5.3 第三步:用实际信号做频谱对比

如果前两步都没问题,那就用实际采集的信号做频谱对比。具体做法是:把实测数据的频谱和定点仿真的频谱画在一起,看差异在哪里。如果差异主要在阻带,那是滤波器设计的问题;如果差异在通带,那是补偿滤波器或者位宽的问题;如果整个频谱都糊了,那可能是溢出或者采样率不匹配。

我一般会用这个表格来定位问题:

现象可能原因排查方向
通带幅度偏低补偿滤波器没加或阶数不够检查补偿滤波器设计
阻带衰减不够CIC 级数不够或抽取因子太大增加级数或改多级 CIC
频谱出现杂散定点溢出或舍入误差累积增加位宽或改舍入模式
整个频谱糊掉采样率不匹配或滤波器位置错误检查 Rate Transition
输出全是零位宽严重不足导致完全截断重新计算位宽

5.4 第四步:用 FDA Tool 的量化分析工具

FDA Tool 里有一个Quantization面板,可以显示定点化之后的频率响应。这个功能很多人不知道,但它非常有用。你可以对比浮点和定点的频率响应,看定点化引入了多少误差。如果误差主要在阻带,那问题不大;如果通带也出现了明显偏差,那就得重新调位宽。

具体操作:在 Filter Designer 里,点击Analysis菜单,选择Quantization,然后勾选Frequency Response。工具会同时画出浮点和定点的响应曲线,差异一目了然。

6. 几个我踩过的具体坑与应对技巧

6.1 差分延迟设成 2 之后通带塌了

有一次我需要更陡的阻带衰减,就把差分延迟 M 从 1 改成了 2。结果阻带确实好了,但通带在 0.05 归一化频率处就掉了 1.5 dB,补偿滤波器怎么调都补不回来。后来查资料才知道,M=2 的时候,CIC 的频率响应零点位置变了,通带边缘的衰减会显著增加。这个坑的教训是:差分延迟不要随便改,除非你确实需要更陡的阻带,并且能接受通带的额外衰减。

6.2 级数设成 6 之后资源爆了

CIC 的级数 N 每增加 1,积分器和梳状器的数量就增加一组,资源占用线性增长。我有个项目为了追求阻带衰减,把 N 设成了 6,结果 FPGA 的 LUT 用了 80% 以上,时序也跑不上去。后来降到 N=4,配合一个 20 阶的补偿滤波器,效果反而更好。所以级数不是越多越好,要综合考虑资源、延迟和补偿滤波器的设计难度。

6.3 FDA Tool 导出的系数和预期不符

FDA Tool 在 CIC 模式下导出的系数有时候看起来很奇怪,比如全是 1 或者全是 0。这是因为 CIC 的系数本质上就是 1 和 -1,工具导出的是结构参数而不是传统的滤波器系数。如果你要手动实现 CIC,不要直接拿导出的系数去做卷积,而是要按照积分器-梳状器的结构去搭。这个坑我见过不止一个人踩,导出了系数发现全是 1,以为工具坏了。

6.4 采样率设错导致掩码图完全对不上

FDA Tool 里的 Fs 是输入采样率,不是输出采样率。很多人把 Fs 设成了抽取之后的速率,结果掩码图上的频率轴全错了,怎么调都满足不了。正确的做法是:Fs 填抽取之前的采样率,然后 Fpass 和 Fstop 根据你的信号带宽来设。比如输入 96 kHz,抽取 12 倍,输出 8 kHz,那 Fs 填 96000,Fpass 填 3000,Fstop 填 4000。

7. 写在最后的几句实在话

CIC 滤波器在 FDA Tool 里的设计,说难不难,说简单也不简单。核心就三件事:位宽算够、位置放对、补偿跟上。位宽不够,仿真再漂亮上板也是废的;位置放错,频率响应完全不对;补偿不加,通带衰减没法看。这三件事搞定了,CIC 基本就不会出大问题。

我个人的习惯是,每次设计完 CIC 之后,一定会做三件事:第一,用定点仿真跑一遍实际信号;第二,把浮点和定点的频率响应画在一起对比;第三,在 Simulink 里检查采样率转换的位置。这三步做完,心里就有底了。

另外,FDA Tool 虽然方便,但它毕竟是个通用工具,对 CIC 的支持有一些限制。如果你要做非常定制化的 CIC,比如非整数倍抽取、或者多相 CIC,可能还是得自己写代码。工具能帮你快速验证思路,但最终的实现细节,还是得靠自己对原理的理解。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进