1. 为什么CIC滤波器值得单独拎出来聊
在数字信号处理这条线上摸爬滚打这些年,CIC滤波器一直是个绕不开的话题。它的全称是级联积分梳状滤波器,英文叫Cascaded Integrator-Comb Filter。名字听着挺唬人,但拆开看就三块东西:积分器、抽取器、梳状器。结构简单到用几行代码就能写出来,可偏偏在实际工程里,尤其是做多速率信号处理的时候,它的地位几乎不可替代。
CIC滤波器最典型的应用场景就是采样率转换。比如你从ADC拿到一个采样率很高的信号,想把它降下来做后续处理,直接抽取会混叠,那就得先低通滤波。但低通滤波器的阶数如果按常规FIR去设计,在采样率很高、过渡带很窄的情况下,系数会多到让人崩溃。CIC滤波器的妙处在于,它不需要乘法器,只需要加减法和延迟单元,就能实现一个等效的低通滤波效果。这个特性在FPGA和ASIC里简直是救命稻草,因为乘法器资源太宝贵了。
那为什么要在MATLAB里用FDA Tool来设计CIC滤波器呢?FDA Tool就是Filter Design and Analysis Tool,是MATLAB信号处理工具箱里一个图形化的滤波器设计工具。它把很多底层函数封装成了交互界面,让你可以拖拖拽拽、调调参数就能看到幅频响应。对于CIC这种参数不多但相互制约的滤波器来说,用FDA Tool可以快速试错,直观地看到不同参数组合下的频率响应变化。
但问题也恰恰出在这里。CIC滤波器的参数空间其实很窄,差分延迟、级数、抽取因子、采样率这几个参数之间有着强耦合关系。FDA Tool的界面设计更偏向于通用FIR和IIR滤波器,CIC只是其中一个选项。很多新手一上来就按设计普通FIR的思路去填参数,结果要么是响应曲线完全不对,要么是设计出来的滤波器根本没法用。我自己就踩过这个坑,当时调了一个下午,怎么改参数都得不到想要的阻带衰减,后来才发现是差分延迟设成了2,而我的抽取因子是8,两者不匹配导致梳状部分的零点位置全偏了。
所以这篇内容就是想把我在MATLAB里用FDA Tool设计CIC滤波器时遇到的那些坑,一个一个掰开来讲清楚。不管你是刚接触多速率信号处理的学生,还是已经在做FPGA实现的工程师,只要你在用MATLAB做CIC滤波器的前期设计,这些经验应该都能帮你省下不少调试时间。
2. CIC滤波器的核心参数与FDA Tool的对应关系
2.1 差分延迟、级数与抽取因子的三角关系
CIC滤波器的频率响应有个很简洁的闭式表达。对于一个N级、差分延迟为M、抽取因子为R的CIC滤波器,它的传递函数可以写成:
H(z) = [(1 - z^(-RM)) / (1 - z^(-1))]^N
这个公式里,R是抽取因子,M是差分延迟,N是级数。从频域上看,它的幅频响应在归一化频率的整数倍处会出现零点,零点的位置由R和M共同决定。具体来说,零点出现在f = k/(RM)处,k是整数。这意味着阻带的第一零点位置直接由RM的乘积决定。
在FDA Tool里,当你选择CIC滤波器类型后,界面会要求你输入几个关键参数:Decimation Factor(抽取因子)、Differential Delay(差分延迟)、Number of Sections(级数)。这三个参数就是CIC滤波器的全部自由度。但FDA Tool不会告诉你的是,这三个参数之间存在很强的约束关系。
差分延迟M的取值通常只有1和2两种。M=1时,CIC滤波器在归一化频率的整数倍处有零点,阻带衰减相对有限。M=2时,零点位置会偏移,阻带衰减会有所改善,但通带 droop 也会更严重。很多资料推荐M=1作为默认值,因为此时CIC的幅频响应有最宽的通带平坦度。但如果你需要更陡的过渡带,M=2是一个可选项,代价是通带边缘的衰减会增大。
级数N决定了阻带衰减的深度。每增加一级,阻带衰减大约增加6dB。但这个增长不是线性的,因为CIC的幅频响应是sinc函数的N次方。N=1时,第一旁瓣的衰减只有大约13dB,这个数值在工程上几乎不可用。N=3时,阻带衰减能到40dB左右。N=5时,能到65dB左右。但级数增加也会带来通带 droop 的加剧,以及积分器位宽的膨胀。
抽取因子R是最灵活的参数,它决定了采样率转换的比例。但R的取值会直接影响零点位置,进而影响阻带衰减。在FDA Tool里,当你改变R的时候,幅频响应图会实时更新,你可以直观地看到零点是怎么移动的。
2.2 FDA Tool中CIC设计界面的隐藏陷阱
FDA Tool的CIC设计界面看起来很简单,但有几个地方特别容易让人掉坑。
第一个坑是采样率的设置。FDA Tool默认的采样率是1,但CIC滤波器通常用在多速率系统里,输入采样率和输出采样率是不一样的。如果你在FDA Tool里把采样率设成了1,那看到的归一化频率响应虽然形状是对的,但实际频率的对应关系就全乱了。正确的做法是,把FDA Tool的采样率设成CIC滤波器的输入采样率,这样你看到的频率轴才是真实的频率。
第二个坑是差分延迟的默认值。FDA Tool里CIC滤波器的差分延迟默认是1,但有些版本的MATLAB里默认值是2。如果你没注意这个细节,设计出来的滤波器零点位置会和你预期的差一倍。我建议每次打开CIC设计界面,第一件事就是确认差分延迟的值。
第三个坑是级数的含义。FDA Tool里的Number of Sections指的是CIC滤波器的级数,也就是积分器和梳状器的对数。但有些文献里用“阶数”这个词,指的是滤波器的阶数,两者不是一回事。一个N级的CIC滤波器,它的阶数是N乘以RM。如果你在FDA Tool里填了级数5,但心里想的是阶数5,那设计出来的滤波器阻带衰减会比你预期的差很多。
第四个坑是幅频响应的显示范围。FDA Tool默认显示的频率范围是0到Fs/2,但CIC滤波器的关键特性往往在更低的频率范围内才能看清楚。比如通带 droop,如果你把频率轴拉到0到Fs/2,通带看起来几乎是平的。但如果你把频率轴缩放到0到Fs/(2R),就能看到通带边缘明显的衰减。这个衰减在后续的补偿滤波器设计里必须考虑进去。
2.3 参数选择背后的数学逻辑
为什么CIC滤波器的参数不能随便填?因为它的幅频响应是sinc函数的N次方,而sinc函数的形状是由RM决定的。具体来说,CIC滤波器的幅频响应可以写成:
|H(f)| = |sin(πRMf) / (R sin(πf))|^N
当f很小时,sin(πf) ≈ πf,sin(πRMf) ≈ πRMf,所以|H(f)| ≈ |RMf / (Rf)|^N = M^N。这就是CIC滤波器的直流增益。注意这个增益只和M和N有关,和R无关。这意味着如果你改变抽取因子R,直流增益不会变,但通带 droop 会变。
通带 droop 的定义是通带边缘的衰减量。对于CIC滤波器,通带边缘通常取f = 1/(2R)处的频率,也就是输出采样率的奈奎斯特频率。在这个频率上,|H(f)| = |sin(πM/2) / (R sin(π/(2R)))|^N。当R很大时,sin(π/(2R)) ≈ π/(2R),所以|H(f)| ≈ |sin(πM/2) / (πM/2)|^N * M^N。这个表达式说明,通带 droop 随着R的增大而增大,随着N的增大而增大。
阻带衰减的定义是第一旁瓣的衰减量。第一旁瓣出现在f = 3/(2RM)处。在这个频率上,|H(f)| = |sin(3π/2) / (R sin(3π/(2RM)))|^N。当R很大时,sin(3π/(2RM)) ≈ 3π/(2RM),所以|H(f)| ≈ |1 / (3π/2)|^N * M^N = (2/(3π))^N * M^N。相对于直流增益M^N,阻带衰减是(2/(3π))^N,换算成dB就是20N log10(2/(3π)) ≈ -13.46N dB。这就是为什么每增加一级,阻带衰减增加约13.5dB,而不是6dB。很多资料里说的6dB是指每增加一级,滤波器的阶数增加一倍,但CIC的阻带衰减增长是13.5dB每级。
这些数学关系在FDA Tool里不会直接告诉你,但它们是理解CIC滤波器行为的基础。如果你在设计时发现阻带衰减不够,增加级数是最直接的办法,但要注意通带 droop 也会同步恶化。
3. 在FDA Tool里一步步搭建CIC滤波器的实操流程
3.1 从零开始创建一个CIC设计会话
打开MATLAB,在命令行输入filterDesigner,FDA Tool的界面就弹出来了。如果你用的是比较新的MATLAB版本,这个命令可能会被重定向到filterDesigner函数,界面基本一样。
第一步是选择滤波器类型。在FDA Tool的界面上,点击“Create a new filter”或者直接在主界面里找到“Filter Type”下拉菜单。CIC滤波器不在默认的FIR和IIR分类里,你需要先选择“Multi-rate”或者“CIC”相关的选项。不同版本的MATLAB里,这个选项的位置可能不太一样。在R2020a之后的版本里,你可以在“Filter Type”里直接找到“CIC Decimator”和“CIC Interpolator”两个选项。如果你要做抽取,就选CIC Decimator;如果要做内插,就选CIC Interpolator。
选好类型后,界面会变成CIC专用的参数输入区。这里有几个关键字段:Decimation Factor(抽取因子)、Differential Delay(差分延迟)、Number of Sections(级数)、Input Sample Rate(输入采样率)。这四个参数就是CIC滤波器的全部设计变量。
我一般会先确定输入采样率和抽取因子,因为这两个参数是由系统需求决定的。比如我的ADC采样率是100MHz,我想把采样率降到25MHz,那抽取因子就是4,输入采样率就是100MHz。这两个参数填进去之后,再调差分延迟和级数。
差分延迟先保持默认值1,级数先填3。然后点击“Design Filter”按钮,FDA Tool就会计算出滤波器的系数并显示幅频响应。这时候你会看到一条曲线,在0到25MHz的范围内是通带,在25MHz到50MHz的范围内是阻带。注意,因为抽取因子是4,输出采样率是25MHz,所以奈奎斯特频率是12.5MHz。通带通常定义在0到12.5MHz之间。
3.2 读懂幅频响应图里的关键信息
FDA Tool画出来的幅频响应图里,有几个地方需要特别关注。
第一个是通带边缘的衰减。把鼠标移到频率轴上12.5MHz的位置,看看对应的幅度是多少。如果衰减超过了1dB,那通带 droop 就有点大了,后续可能需要加一个补偿滤波器。补偿滤波器的设计思路是让它的幅频响应在通带内有一个上升的斜率,正好抵消CIC的下降斜率。这个补偿滤波器通常是一个简单的FIR滤波器,阶数不用太高,因为CIC的通带 droop 是平滑的。
第二个是第一旁瓣的位置和衰减。在幅频响应图上找到第一个峰值,它通常出现在频率轴上比12.5MHz高一些的位置。看看这个峰值的幅度相对于直流增益衰减了多少。如果衰减不到40dB,那对于大多数通信系统来说是不够的。增加级数可以加深阻带衰减,但每增加一级,通带 droop 也会增加。
第三个是零点位置。CIC滤波器的零点出现在f = k/(RM)处。在幅频响应图上,你会看到一系列向下的尖峰,这些尖峰就是零点。零点的位置决定了哪些频率成分会被完全抑制。如果你发现某个干扰信号的频率正好落在零点之间,那CIC滤波器对它的抑制就不够。这时候可以考虑调整差分延迟M,让零点位置移动。
3.3 参数扫描与迭代优化的实操技巧
FDA Tool的一个强大之处是它可以做参数扫描。你不需要每次手动改参数再点Design,可以用MATLAB脚本批量生成不同参数组合下的滤波器,然后对比它们的性能。
比如你想看看级数从3增加到5时,阻带衰减和通带 droop 各变化了多少。你可以写一个简单的循环:
for N = 3:5 cicFilter = designfilt('cicdecimator', ... 'DecimationFactor', 4, ... 'DifferentialDelay', 1, ... 'NumSections', N, ... 'SampleRate', 100e6); % 然后分析这个滤波器的频率响应 [h, f] = freqz(cicFilter, 1024, 100e6); % 计算通带边缘衰减和阻带衰减 % ... end这段代码里,designfilt是MATLAB较新版本里推荐的滤波器设计函数,它比FDA Tool的图形界面更适合做批量分析。但如果你对designfilt的参数不熟悉,还是建议先在FDA Tool里手动调一遍,把每个参数的含义搞清楚,再用脚本批量跑。
参数扫描的时候,我一般会关注三个指标:通带边缘衰减、第一旁瓣衰减、以及积分器的位宽增长。前两个可以从幅频响应图上直接读出来,第三个需要根据CIC滤波器的增益来计算。CIC滤波器的直流增益是(M*R)^N,对于M=1、R=4、N=3的情况,增益是64,也就是6位。这意味着积分器的位宽至少要比输入信号多6位,否则会溢出。在实际FPGA实现里,这个位宽增长是必须考虑的,但在MATLAB设计阶段,你可以先忽略它,等确定了参数再算。
3.4 从FDA Tool导出滤波器系数的正确姿势
设计好CIC滤波器后,下一步通常是把它导出到FPGA或DSP里实现。FDA Tool提供了几种导出方式:导出到MATLAB工作区、导出到Simulink、导出到Xilinx或Altera的系数文件。
对于CIC滤波器,我强烈建议不要直接导出系数。因为CIC滤波器的“系数”其实只有几个整数,它的实现结构是固定的积分器-梳状器结构,不需要像FIR那样做乘累加。你真正需要导出的是参数:R、M、N,以及输入输出位宽。这些参数在FPGA代码里直接写成常量就行。
如果你非要从FDA Tool导出点什么,可以导出它的幅频响应数据,用来在MATLAB里做后续的补偿滤波器设计。具体操作是:在FDA Tool的菜单里选择“File”->“Export”,然后选择“Export to Workspace”,把频率响应导出成一个结构体。这个结构体里包含了频率向量和对应的幅度响应,你可以用它来拟合补偿滤波器的频率响应。
4. 那些年我在CIC设计里踩过的坑
4.1 差分延迟设错导致零点偏移
这是我踩过的第一个坑,也是最隐蔽的一个。当时我在做一个音频采样率转换的项目,输入采样率是48kHz,想降到8kHz,抽取因子是6。我在FDA Tool里填了R=6,M=1,N=3,然后看幅频响应,觉得阻带衰减还行,就导出了参数。结果在FPGA里实现后,发现输出信号里有一个明显的杂散,频率大概在4kHz左右。
排查了半天,最后发现是差分延迟的问题。我在FDA Tool里填的M=1,但FPGA代码里写的是M=2。为什么会有这个差异?因为我在FDA Tool里设计完之后,又参考了另一份资料,那份资料里推荐M=2,我就随手改了FPGA代码,但忘了在FDA Tool里重新验证。M=2时,零点位置从k/6变成了k/12,第一零点从8kHz移到了4kHz,正好落在了我的通带边缘附近,导致那个频率的成分被抑制了,但同时也引入了一个新的杂散。
这个坑的教训是:FDA Tool里的参数和FPGA代码里的参数必须严格一致,改任何一个都要重新验证。而且差分延迟M的取值不是随便定的,M=1和M=2各有适用场景。M=1时通带更平坦,M=2时阻带更陡,但通带 droop 更大。如果你不确定用哪个,先用M=1,因为大多数CIC滤波器的理论分析都是基于M=1的。
4.2 级数增加带来的位宽溢出
第二个坑是关于位宽的。CIC滤波器的直流增益是(M*R)^N,这个增益在积分器部分会累积。如果你在MATLAB里设计时只关注幅频响应,忽略了位宽,那在FPGA里实现时就会遇到溢出。
我当时做一个抽取因子为16、级数为5的CIC滤波器。输入信号是12位,我在MATLAB里看幅频响应,阻带衰减有65dB,很满意。但到了FPGA里,输出信号全是乱的。后来一算,直流增益是(1*16)^5 = 1048576,也就是20位。输入12位加上20位增益,总共需要32位才能不溢出。但我当时只用了16位的积分器,中间结果早就溢出了。
解决方法是:在积分器链的每一级都增加位宽,或者使用饱和算术。但饱和算术会引入非线性,影响滤波器的线性度。更常用的做法是让积分器的位宽足够大,然后在最后一级梳状器之后再做截断。截断会引入量化噪声,但CIC滤波器本身对量化噪声有一定的抑制能力,所以只要截断位数不是太多,影响可以接受。
在MATLAB里,你可以用fi对象来模拟定点运算,提前看看位宽够不够。但更简单的办法是记住一个经验公式:积分器位宽 = 输入位宽 + ceil(N * log2(M*R))。对于M=1、R=16、N=5的情况,ceil(5 * log2(16)) = ceil(5 * 4) = 20,所以积分器位宽至少是输入位宽加20。
4.3 通带 droop 被忽略导致后续补偿困难
第三个坑是通带 droop。CIC滤波器的通带不是平的,它在通带边缘有一个明显的衰减。这个衰减在幅频响应图上看起来可能不大,但如果你把频率轴放大,就会发现它其实挺严重的。
我当时做一个软件无线电的项目,CIC滤波器的抽取因子是8,级数是4。在FDA Tool里看幅频响应,通带边缘的衰减大概是2dB左右。我觉得2dB还能接受,就没加补偿滤波器。结果在后续的解调环节,信号的高频成分被衰减了,导致误码率偏高。
后来加了一个简单的补偿滤波器,就是一个3阶的FIR,它的频率响应在通带内有一个上升的斜率,正好抵消CIC的下降斜率。加了这个补偿滤波器之后,通带平坦度改善到了0.1dB以内,误码率也降下来了。
这个坑的教训是:CIC滤波器的通带 droop 是固有的,不能忽略。补偿滤波器的设计可以在MATLAB里用FDA Tool的“Design Filter”功能来做,选择FIR滤波器类型,然后让它的幅频响应在通带内有一个上升的斜率。具体做法是:先导出CIC滤波器的幅频响应,然后取它的倒数,再用FDA Tool拟合一个FIR滤波器来逼近这个倒数。拟合的阶数不用太高,3到5阶就够了,因为CIC的 droop 是平滑的。
4.4 FDA Tool版本差异导致的参数默认值变化
第四个坑是MATLAB版本差异。不同版本的MATLAB里,FDA Tool的CIC设计界面默认参数可能不一样。我在R2018a里设计的CIC滤波器,到了R2021a里打开,发现差分延迟的默认值从1变成了2,级数的默认值从3变成了4。如果你没注意这些变化,直接点Design,设计出来的滤波器可能和你之前的不一样。
这个坑的解决办法很简单:每次打开FDA Tool的CIC设计界面,先检查一遍所有参数的默认值,确认它们和你预期的一致。如果你经常在不同版本的MATLAB之间切换,建议把常用的参数组合保存成脚本,用designfilt函数来设计,这样就不受界面默认值的影响了。
5. 常见问题速查与排查思路
5.1 幅频响应曲线异常时的排查顺序
当你在FDA Tool里看到一条完全不对的幅频响应曲线时,不要慌,按下面的顺序排查:
| 排查步骤 | 检查内容 | 可能的问题 | 解决方法 |
|---|---|---|---|
| 1 | 滤波器类型 | 选成了FIR或IIR而不是CIC | 重新选择CIC Decimator或Interpolator |
| 2 | 抽取因子 | 填成了0或负数 | 确保R是大于1的整数 |
| 3 | 差分延迟 | 填成了0或非整数 | 确保M是1或2 |
| 4 | 级数 | 填成了0 | 确保N是大于等于1的整数 |
| 5 | 采样率 | 填成了1或与系统不符 | 改成实际的输入采样率 |
| 6 | 频率轴范围 | 显示范围太宽,看不清细节 | 缩放到0到Fs/(2R) |
这个表格里的顺序是我自己总结的,基本上90%的异常都能在前三步找到原因。如果前五步都检查过了还是不对,那可能是MATLAB的bug,重启一下FDA Tool或者换个版本试试。
5.2 阻带衰减不够时的参数调整策略
阻带衰减不够是CIC设计里最常见的问题。如果你发现第一旁瓣的衰减不到40dB,可以按下面的策略调整:
首先考虑增加级数N。每增加一级,阻带衰减增加约13.5dB。但级数增加也会带来通带 droop 的加剧和位宽的增长。一般来说,N=3能提供约40dB的阻带衰减,N=5能提供约67dB。如果你的系统要求阻带衰减大于60dB,那N至少要是5。
如果增加级数后通带 droop 太大,可以考虑减小差分延迟M。M=1时的通带 droop 比M=2时小。但M=1时的阻带衰减也比M=2时小。这是一个权衡。
如果级数和差分延迟都调过了还是不够,那可能是抽取因子R太大了。R越大,第一旁瓣离通带越近,阻带衰减越难做。这时候可以考虑把大的抽取因子拆成两级CIC,比如R=16拆成R=4和R=4,中间加一个补偿滤波器。这样每一级的R都小了,阻带衰减更容易做。
5.3 定点实现时积分器溢出的预防措施
积分器溢出是CIC滤波器定点实现时的头号杀手。预防措施有以下几条:
第一条是算清楚位宽。积分器位宽 = 输入位宽 + ceil(N * log2(M*R))。这个公式是保守估计,实际需要的位宽可能比这个少一点,但为了安全,建议按这个公式来。
第二条是在MATLAB里做定点仿真。用fi对象把输入信号量化成定点,然后跑一遍CIC滤波器,看看中间结果有没有溢出。MATLAB的定点工具箱可以自动检测溢出,并给出报告。
第三条是在FPGA里用饱和算术。如果位宽实在不够,可以在积分器的每一级加饱和逻辑。但饱和会引入非线性,所以只在万不得已的时候用。
第四条是在梳状器之后做截断。截断会引入量化噪声,但CIC滤波器对量化噪声有抑制能力。截断的位数不要太多,一般截掉log2(M*R)位就够了。
5.4 补偿滤波器设计时的频率响应拟合技巧
补偿滤波器的设计目标是在通带内提供一个上升的斜率,抵消CIC的下降斜率。在MATLAB里,你可以用firls函数来拟合这个斜率。
具体做法是:先导出CIC滤波器的幅频响应,取它的倒数,得到补偿滤波器的理想幅频响应。然后用firls函数拟合这个理想响应。拟合的频段选在通带内,比如0到Fs/(2R)。拟合的阶数选3到5阶就够了,因为CIC的 droop 是平滑的,不需要太高阶的滤波器来拟合。
拟合的时候要注意,补偿滤波器的直流增益应该和CIC的直流增益的倒数相等,这样级联后的总增益才是1。如果总增益不是1,可以在补偿滤波器后面再加一个增益因子。
6. 从MATLAB设计到硬件实现的衔接要点
6.1 参数传递时的位宽与格式约定
从MATLAB设计到FPGA实现,参数传递是最容易出问题的环节。CIC滤波器的参数只有R、M、N三个整数,看起来很简单,但位宽和格式的约定如果不一致,就会导致实现结果和仿真结果对不上。
我一般会在MATLAB里把这三个参数写成常量,然后在FPGA代码里用同样的常量。位宽方面,输入信号的位宽、积分器的位宽、梳状器的位宽、输出信号的位宽,这四个位宽要在MATLAB和FPGA里保持一致。我习惯在MATLAB里用fi对象定义这些位宽,然后把位宽值导出成一个头文件,FPGA代码里直接include这个头文件。
格式方面,CIC滤波器通常用整数运算,不需要浮点。但如果你在MATLAB里用的是浮点仿真,到了FPGA里改成定点,结果可能会有差异。这个差异主要来自量化噪声和截断误差。为了减小差异,建议在MATLAB里就用定点仿真,这样仿真结果和FPGA实现结果会更接近。
6.2 仿真结果与硬件实测不一致时的排查方向
如果你在MATLAB里仿真通过了,但FPGA实测结果不对,可以按下面的方向排查:
第一个方向是位宽。检查积分器的位宽是否足够,有没有溢出。检查截断的位数是否合理,有没有引入太大的量化噪声。
第二个方向是时序。CIC滤波器的积分器和梳状器之间有抽取器,抽取器的时序如果不对,会导致数据错位。检查抽取器的使能信号是否正确,数据有效信号的时序是否匹配。
第三个方向是复位。积分器在复位后的初始状态应该是0,如果复位不彻底,积分器里可能有残留值,导致输出异常。
第四个方向是时钟。CIC滤波器的输入和输出时钟频率不同,如果时钟域切换时没有做同步处理,会导致亚稳态和数据丢失。
6.3 资源占用与速度的平衡取舍
CIC滤波器的资源占用主要来自积分器和梳状器的加法器,以及延迟单元的寄存器。级数N越大,加法器和寄存器的数量越多。抽取因子R越大,积分器的位宽越大,加法器的位宽也越大。
在FPGA里,加法器的位宽直接影响资源占用。一个32位的加法器比一个16位的加法器占用的查找表和寄存器多一倍。所以如果资源紧张,可以考虑减小级数N,或者把大的抽取因子拆成多级。
速度方面,CIC滤波器的关键路径通常在积分器链上。积分器是递归结构,每一级的输出依赖上一级的输出,所以关键路径的长度和级数N成正比。如果时钟频率很高,可能需要把积分器链拆成流水线结构。但流水线会引入额外的延迟,需要在时序上做补偿。
7. 一些不太常见但很有用的经验
7.1 用CIC滤波器做分数倍采样率转换
CIC滤波器通常用于整数倍采样率转换,但通过组合多个CIC滤波器,也可以实现分数倍转换。比如你想把采样率从48kHz转到44.1kHz,比例是160/147。你可以先用一个抽取因子为160的CIC滤波器把采样率降到300Hz,然后再用一个内插因子为147的CIC滤波器把采样率升到44.1kHz。但这样做的代价是中间采样率太低,信号带宽受限。
更好的做法是用多相分解,把分数倍转换拆成多个整数倍转换的级联。但这就超出了FDA Tool的能力范围,需要自己写MATLAB脚本来实现。
7.2 CIC滤波器在Sigma-Delta ADC后的抽取应用
Sigma-Delta ADC的输出是高位率的1位或几位码流,需要用CIC滤波器做抽取。这种应用里,CIC滤波器的抽取因子通常很大,比如64或128。级数一般选3到5级。差分延迟通常选1,因为Sigma-Delta的噪声整形特性对通带 droop 不太敏感。
这种应用里需要注意的是,Sigma-Delta ADC的输出速率很高,CIC滤波器的积分器需要在很高的时钟频率下工作。如果FPGA的时钟频率不够,可以考虑用多个CIC滤波器并行处理,或者用多相结构来降低时钟频率。
7.3 用FDA Tool的批处理功能做参数优化
FDA Tool虽然是个图形界面工具,但它背后的函数是可以批处理的。你可以写一个MATLAB脚本,循环遍历不同的R、M、N组合,对每一种组合计算通带 droop 和阻带衰减,然后画成等高线图。这样你可以一眼看出哪些参数组合是可行的,哪些是不可行的。
这个批处理脚本的核心是designfilt函数和freqz函数。designfilt用来生成滤波器对象,freqz用来计算频率响应。计算完频率响应后,你可以用interp1函数在特定的频率点上插值,得到通带边缘衰减和第一旁瓣衰减。然后把这些指标存到一个矩阵里,用contourf函数画等高线图。
这个方法的优点是可以在几分钟内遍历几千种参数组合,比手动在FDA Tool里一个一个试快得多。缺点是需要对MATLAB编程比较熟悉,而且designfilt函数的参数名在不同版本里可能不一样,需要查文档。
7.4 关于CIC滤波器群延迟的测量与补偿
CIC滤波器的群延迟是频率的函数,不是常数。这意味着它会对信号造成相位失真。对于某些对相位敏感的应用,比如通信系统的解调,这个相位失真需要补偿。
群延迟的测量方法是在MATLAB里对CIC滤波器输入一个脉冲信号,然后观察输出脉冲的峰值位置。但CIC滤波器的脉冲响应不是对称的,峰值位置不容易确定。更准确的方法是用grpdelay函数计算群延迟曲线。
补偿群延迟的方法是在CIC滤波器后面加一个全通滤波器,让它的群延迟曲线和CIC的群延迟曲线相反。但全通滤波器的设计比较复杂,而且会引入额外的延迟。对于大多数应用来说,CIC的群延迟失真可以忽略,因为后续的补偿滤波器或者均衡器会把它一起补偿掉。
8. 我个人在实际操作中的几点体会
CIC滤波器这个东西,理论看起来简单,但实际用起来细节特别多。我在最开始接触的时候,觉得不就是几个参数嘛,能有多难。结果第一个项目就卡了三天,最后发现是差分延迟设错了。后来做的项目多了,慢慢总结出一些自己的习惯。
第一个习惯是:每次设计CIC滤波器之前,先拿纸笔把R、M、N三个参数写下来,然后手动算一遍直流增益、第一零点位置、第一旁瓣衰减。这三个数值算出来之后,心里就有底了,再去FDA Tool里填参数,就不会盲目。
第二个习惯是:在FDA Tool里设计完之后,一定要用designfilt函数再生成一遍,对比两者的频率响应。如果两者不一致,说明FDA Tool的界面参数和designfilt的参数有出入,需要查清楚是哪个参数的问题。
第三个习惯是:定点仿真一定要做。不要觉得浮点仿真通过了就万事大吉。CIC滤波器的位宽增长是实实在在的,浮点仿真看不出来。用fi对象做一遍定点仿真,看看中间结果有没有溢出,输出信号的量化噪声有多大。这一步花的时间不多,但能省下大量在FPGA上调试的时间。
第四个习惯是:补偿滤波器的设计不要追求完美。CIC的通带 droop 是平滑的,补偿滤波器只要能把 droop 压到1dB以内就够了。追求0.1dB的平坦度需要高阶滤波器,资源占用不划算。而且在实际系统里,后续的均衡器或者自动增益控制会进一步补偿通带的不平坦。
最后再分享一个小技巧:如果你在FDA Tool里怎么调都得不到满意的阻带衰减,试试把抽取因子拆成两个较小的因子,中间加一个简单的FIR滤波器。比如R=16拆成R=4和R=4,中间加一个5阶的FIR。这样每一级的CIC滤波器阻带衰减都更容易做,总的阻带衰减反而比单级R=16的CIC更好。这个技巧在资源受限的FPGA实现里特别有用,因为两级小CIC的加法器位宽比一级大CIC小得多。