CIC滤波器通带凹陷补偿:firceqrip invsinc设计与定点落地 数字前端链路里CIC 滤波器几乎是免不了的一环结构简单、不需要乘法器、抽取比可以做得很大这些都是它的好。但它有个先天毛病——通带不平越往通带边缘走衰减越重。我最近做的一版链路CIC 抽完之后通带边缘掉了将近 3 dB后面直接接信道滤波器结果整个带内的幅频响应是斜的测下来指标一直卡在那儿上不去。这个问题最后是靠一级 FIR 通带补偿解决的用的就是 MATLAB 里firceqrip的invsinc通道。这篇就把整条路子完整走一遍droop 是怎么算出来的、补偿目标曲线怎么定、firceqrip的参数怎么填、系数怎么定点落地、联调时踩了哪几个坑。适合正在做抽取/插值链路、被 CIC 通带凹陷困扰的同行参考也适合刚接触多速率信号处理、想搞清楚反 sinc 补偿到底在补什么的朋友。1. 通带边缘那 3 dB 是怎么掉的CIC 幅频响应的固有凹陷1.1 从积分-抽取-梳状的零极点抵消说起CIC 滤波器的全称是级联积分梳状滤波器它的结构本质上是 N 级积分器、一个抽取器、N 级梳状器串起来。单级的传递函数写成 Z 域是H(z) (1 - z^(-R·M)) / (1 - z^(-1))其中 R 是抽取因子M 是差分延迟通常取 1。这个式子的分子在单位圆上有 R·M 个等间隔零点分母在 z1 处有一个极点两者恰好抵消剩下的就是一个梳状的幅频响应。从频域角度看单级 CIC 的幅度响应可以写成|sin(π·f·R·M/fs)| / |R·M·sin(π·f/fs)|的形式其中 fs 是抽取前的采样率。这个形状看起来像 sinc 函数但严格说它是离散 sinc——分子分母都是正弦原点处极限为 1随着频率升高单调下降。N 级级联就是把这条曲线自乘 N 次凹陷被放大 N 倍。这里有个容易被忽略的点很多人会把 CIC 的响应直接当成连续 sinc 来处理用sinc(x) sin(πx)/(πx)去近似。在小带宽、大抽取比的场景下这个近似误差不大但当通带取到输出奈奎斯特的 40% 以上时近似误差会开始显现补偿曲线就会对不上。我后面所有的计算都用精确的离散形式不用近似。把频率归一化到抽取后的采样率记x f / fs_outx 的取值范围是 0 到 0.5那么 N 级 CIC 的归一化幅度响应是H(x) [ sin(π·M·x) / (R·M·sin(π·x/R)) ]^N这个式子在 x 趋近 0 时分子约等于 π·M·x分母里的 sin(π·x/R) 约等于 π·x/R两者相乘正好也是 π·M·x比值为 1符合直流增益归一化的预期。这个形式比用绝对频率写起来清爽得多后面所有的推导和代码都基于它。1.2 把 droop 算出来一组真实参数下的定量结果光说会掉没用得知道掉多少才能决定补偿滤波器要多大力气。我拿手上正在调的一组参数做个完整演算抽取前采样率 100 MHz抽取因子 R8差分延迟 M1级数 N5抽取后采样率 12.5 MHz。代入上面的公式取几个关键频点算一下归一化频率 x相对 fs_out对应绝对频率CIC 幅度dB累计衰减0.000 MHz0.000.00 dB0.101.25 MHz-0.700.70 dB0.202.50 MHz-2.852.85 dB0.253.125 MHz-4.494.49 dB0.303.75 MHz-6.526.52 dB0.405.00 MHz-11.9411.94 dB0.506.25 MHz-19.3119.31 dB看这张表就能明白问题有多严重。如果我的有用信号带宽占输出奈奎斯特的 40%也就是 x 从 0 到 0.2边缘处就已经掉了 2.85 dB如果带宽占到 60%边缘掉 6.5 dB这个量级已经不是稍微不平而是会直接影响后端解调门限的。这里还有个经验值可以记一下同样的 R 和 M级数 N 每增加 1通带边缘的 droop 大致按比例放大。N4 时边缘 droop 约 2.28 dBN5 时 2.85 dBN6 时 3.42 dB基本是线性关系因为取对数之后是乘法变加法。所以如果链路里 CIC 级数选得比较高为了更好的混叠抑制通带补偿的必要性就更强。算 droop 这一步我强烈建议用脚本做不要手算也不要凭经验估。手算容易在归一化上出错而一旦算错补偿滤波器的目标曲线就整个偏了后面怎么调都调不平。写个十来行的脚本把整个通带扫一遍输出最大值、最小值和平坦度是最省事的做法。1.3 为什么不能指望在 CIC 内部把曲线拉平有人会想既然 CIC 的通带不平那能不能在 CIC 结构内部动手脚比如给积分器加个反馈系数、或者调整梳状器的延迟这条路我也试过结论是不划算。CIC 的核心优势就在于它没有乘法器——整个结构只有加法器、减法器和寄存器。一旦引入任何可调系数乘法器就回来了而且是在抽取前的高速侧工作资源和功耗的代价非常大。更麻烦的是CIC 的 droop 形状是 sinc 幂次的凸曲线用简单的反馈结构根本拟合不出来最多只能做点粗糙的预畸变。另一条思路是在 CIC 前面做预加重也就是在高速侧先把信号的高频部分抬起来让 CIC 的凹陷正好抵消。听起来很巧妙实际上更糟预加重是在抽取前做的那时候采样率还是 100 MHz任何 FIR 都要在这个速率下跑资源翻好几倍而且预加重会把带外噪声一起抬起来经过 CIC 抽取后混叠回带内反而恶化信噪比。所以业界的标准做法就是老老实实在 CIC 后面跟一级补偿 FIR。这一级工作在抽取后的低采样率上本例 12.5 MHz资源开销可以接受而且它的目标响应非常明确——就是把1/|H_cic(x)|这条曲线拟合出来。补偿器的增益曲线只在整个通带内有几个 dB 的起伏属于小信号整形用定点实现也很友好。2. 补偿滤波器的目标曲线1/|H_cic| 该怎么定边界2.1 目标幅度响应的表达式与采样点补偿滤波器的理想幅度响应就是 CIC 响应的倒数把上面那个公式翻过来即可|H_comp(x)| [ R·M·sin(π·x/R) / sin(π·M·x) ]^N需要注意这个式子在 x0 处是 0/0 型要用极限处理x 趋近 0 时整个式子趋近于 1。写代码的时候要么避开 x0 这个点要么显式地把第一个点置为 1。我第一次写的时候就忘了这一步结果firls直接返回了一堆 NaN查了半天才发现是原点除零。另外这个目标曲线是相对于已经做过增益归一化的 CIC定义的。也就是说补偿滤波器的直流增益应该是 1而不是 1/(R·M)^N。关于这个增益归一化放在哪一级后面 2.3 小节专门讲这里先记住结论。目标响应的采样密度也有讲究。如果用firls或者自己写最小二乘采样点太少会导致曲线拟合出现折线感尤其在通带边缘曲率最大的地方采样稀疏会直接让拟合误差超标。我的做法是在 0 到通带边缘之间至少取 200 个点并且在靠近边缘的 20% 区间内加密一倍。这个细节在文档里基本不会提但实测下来对最终平坦度的影响挺明显的。2.2 通带边界、过渡带与阶数的三方博弈补偿滤波器不是凭空设计的它要和前后的滤波器配合。这里有几个边界必须一起定通带边界 wo这是补偿有效的范围。一般取最终有用信号带宽再留 10%~20% 的余量因为后面的信道滤波器也有过渡带两者要衔接上。本例里我把 wo 定在 x0.2也就是输出奈奎斯特的 40%。过渡带起点补偿滤波器在 wo 之外就不能再往上抬了否则会把带外噪声放大。如果后面紧跟半带滤波器过渡带可以放到 x0.25 开始如果补偿器是链路的最后一级那过渡带必须收得更紧从 x0.22 就要开始往下压。阶数 n这是最直接的资源杠杆。用 Kaiser 的经验公式估算等波纹 FIR 的阶数大致是n ≈ (A - 8) / (2.285 · Δω)其中 A 是阻带衰减dBΔω 是过渡带宽度弧度相对采样率。本例中过渡带从 x0.2 到 x0.3宽度 0.1对应 Δω0.628 rad。代入目标阻带衰减估算阶数实际取用延迟12.5 MHz40 dB222211 样本 / 0.88 μs50 dB293015 样本 / 1.20 μs60 dB363618 样本 / 1.44 μs70 dB434422 样本 / 1.76 μs我最后选了 30 阶。理由是这个链路的补偿器后面还跟着两级半带滤波器本身就有 50 dB 以上的阻带抑制补偿器的阻带不需要做得太狠50 dB 足够而 30 阶带来的 1.2 μs 延迟在整体延迟预算里还扛得住。这里有个常被忽略的约束firceqrip要求阶数 n 为偶数。原因是它设计的是第一类线性相位滤波器长度 n1 必须是奇数这样系数才严格对称、中点系数不为零。如果你填了个奇数进去一般会直接报错或者被自动调整别以为是工具箱出问题了。2.3 归一化放在哪一级CIC 增益 2^15 的处理这一节是我认为整个设计里最容易出错、也最值得单独拎出来讲的地方。CIC 的直流增益是(R·M)^N。本例里 R8、M1、N5增益是 8^5 32768正好是 2 的 15 次方。这个数字不是巧合——很多工程链路会刻意挑选 R 和 N让(R·M)^N落在 2 的整数次幂附近这样归一化只需要一个右移不需要乘法器也不引入舍入误差。如果补偿滤波器把归一化也包进来它的系数就得以 1/32768 为基准通带增益在 3e-5 这个量级。用 16 位定点根本表示不了必须用 32 位甚至浮点完全失去工程意义。所以正确的分工是CIC 输出先做移位归一化把直流增益拉回 1 附近补偿 FIR 只负责几个 dB 的曲线整形直流增益约等于 1。具体做法是在 CIC 的最后一级梳状器输出上右移 15 位。如果(R·M)^N不是 2 的幂比如 R12、N4增益 20736那就得用乘法或者移位相加近似这时候要么接受一点点增益误差要么在补偿 FIR 的系数里顺手把这个常数吸收掉——因为补偿 FIR 本身就有乘法器多乘一个常数不增加成本。我见过一个挺典型的翻车案例某版设计里 CIC 输出没做归一化补偿 FIR 的系数又是按直流增益 1设计的结果整条链路的增益是 32768 倍一上电就饱和。查了两天才发现是两级之间的增益账没对上。所以这一步一定要在系统层面把增益链路画清楚每一级的直流增益标出来乘一遍看是不是 1。3. firceqrip 的 invsinc 通道参数填法与我踩过的偏差3.1 firceqrip 的等波纹思路与 invsinc 选项的语义firceqrip是 MATLAB 信号处理工具箱里一个比较老但很好用的函数它设计的是等波纹线性相位 FIR 滤波器——通带内波纹幅度相等、阻带内波纹幅度也相等本质上和firpm是同一类但参数化方式不同firpm用权重向量控制各频段的相对误差firceqrip直接指定通带波纹 rp 和阻带衰减 rs用起来更直观。它的基本调用形式是b firceqrip(n, wo, [rp rs])n 是阶数wo 是通带边缘归一化到奈奎斯特频率1 对应 fs/2rp 和 rs 分别是通带波纹和阻带衰减。关键在于它的第四个参数——滤波器类型选项。常见的有high高通、stop带阻、passedge把 wo 解释为通带边缘而非截止点以及本文要用的invsinc。invsinc的作用是在低通包络的基础上把通带内的幅度响应塑造成反 sinc 形状也就是(sin(x)/x)^(-k)这种往上翘的曲线。这正好就是 CIC 补偿需要的形状。参数形式上invsinc后面跟的是一个描述形状的向量。我这边的工具箱版本里写成[N, R*M]就能得到和1/|H_cic|几乎重合的结果——第一个元素对应反 sinc 的幂次正好是 CIC 的级数 N第二个元素对应 sinc 的形状尺度对应 R·M。注意不同版本的工具箱对这个向量的两位参数解释并不完全一致有的版本把第二位的含义定义成倒数或者缩放因子。第一次用的时候务必用fvtool或者freqz把设计结果的幅频响应和理论1/|H_cic|叠在一张图上比对对不上就调第二个元素试R*M和1/(R*M)两种一般五分钟内就能确定。这个先叠图再定参的习惯比死记文档参数含义靠谱得多。我现在的做法是任何一次补偿器设计第一件事都是把理论曲线和设计曲线画在一起肉眼扫一眼形状对不对再做定量误差统计。3.2 一次完整的设计脚本与结果核对把前面的东西串起来完整的设计脚本大概是这样% ---- 链路参数 ---- R 8; % 抽取因子 M 1; % 差分延迟 N 5; % CIC 级数 fs_out 12.5e6; % 抽取后采样率 % ---- 补偿滤波器参数 ---- n 30; % 阶数必须为偶数 wo 0.4; % 通带边缘归一化到 Nyquist0.2 相对 fs_out rp 0.02; % 通带波纹dB rs 50; % 阻带衰减dB % ---- 用 firceqrip 的 invsinc 模式设计 ---- b firceqrip(n, wo, [rp rs], invsinc, [N, R*M]); % ---- 理论目标曲线 ---- x linspace(1e-6, 0.5, 1024); % 归一化到 fs_out Hcic (sin(pi*M*x) ./ (R*M*sin(pi*x/R))).^N; Hideal 1 ./ Hcic; % ---- 实际响应 ---- [Hd, w] freqz(b, 1, 1024, 2); % w 归一化到 1 Nyquist w w / 2; % 换成相对 fs_out 的归一化 % ---- 误差统计只在通带内---- idx w 0.2; err 20*log10(abs(Hd(idx))) - 20*log10(Hideal(idx)); fprintf(通带内最大偏差 %.3f dB最小 %.3f dB\n, max(err), min(err)); % ---- 级联验证 ---- Htot abs(Hd) .* Hcic; fprintf(级联后通带边缘 %.3f dB\n, 20*log10(Htot(find(idx,1,last))));跑出来的结果通带内最大偏差在 ±0.05 dB 以内级联后的通带边缘从原来的 -2.85 dB 拉回到 -0.02 dB 附近。这个精度对绝大多数通信链路都够了。有一点要提醒freqz的第四个参数设成 2返回的w就归一化到 1 对应 Nyquist。而我在算理论曲线时用的x是归一化到采样率的x0.5 对应 Nyquist。这两套归一化差了一倍写脚本时必须手动除以 2否则两条曲线会错开一半看起来完全不重合。这个坑我在 5.1 里还会再展开说。3.3 invsinc 对不上时的替代路线firls 与 fir2invsinc模式虽然方便但它有个局限它只保证通带内是等波纹的反 sinc 形状对阻带的控制相对粗放而且反 sinc 的幂次只能取整数对应 CIC 级数。如果遇到 CIC 级数是分数的情况比如插值链路里的组合或者需要更精细的通带/阻带权重分配就得换方法。路线一firls最小二乘拟合。直接把目标幅度响应喂进去f linspace(0, 0.5, 400); A [1 ./ (sin(pi*M*f(2:end)) ./ (R*M*sin(pi*f(2:end)/R))).^N, zeros(1,100)]; ff [f, linspace(0.5, 1, 100)]; b2 firls(30, ff, A, [ones(1,400), 10*ones(1,100)]);第二行末尾的10*ones(...)是阻带权重把它调大能换来更深的阻带抑制代价是通带拟合精度下降。这个权重调参的过程有点磨人但换来的是对通带、阻带两边都能精确控制。路线二fir2频率采样加插值。对光滑的1/H曲线效果很好因为反 sinc 曲线本身没有突变频率采样加插值能很好地还原。我用 32 阶的fir2做过对比通带拟合误差大概 0.08 dB略差于firceqrip的 0.05 dB但胜在参数直观、调试方便。路线三解析方法直接算系数。反 sinc 曲线可以用泰勒展开近似把1/(sin(x)/x)展开成1 x²/6 7x⁴/360 ...的形式然后对多项式做逆 Z 变换得到近似系数。这个方法在算力受限、只能用很小的滤波器时有用但阶数一高误差就上来了现在基本不用。我把三条路线的特点整理成表方法通带拟合精度阻带可控性参数直观度适用场景firceqrip invsinc最好±0.05 dB中等高CIC 级数为整数、标准补偿firls好±0.1 dB最好低要调权重需要精确控制阻带fir2中±0.1 dB中等高快速原型、曲线光滑解析近似差差中极低阶数、算力受限实际工程里我的默认选择是firceqrip只有在阻带指标特别严的时候才切到firls。4. 系数落地定点量化、对称折叠与延迟预算4.1 系数位宽与累加器位宽的估算表MATLAB 里算出来的系数是双精度落到 FPGA 或 ASIC 里必须定点化。这一步的位宽选择有个常见的误区很多人凭感觉给个 16 位结果通带波纹一量化就反弹回头怀疑是算法有问题。正确的估算逻辑是这样的。系数位宽取决于两个因素一是补偿滤波器通带内的最大增益本例约 1.39即 2.85 dB需要 1 位整数位二是系数的最小有效值决定了需要多少小数位才能保住精度。我的经验是补偿增益通带峰值建议系数位宽量化后通带波纹恶化 1 dB12 bit 0.01 dB1 ~ 3 dB14 ~ 16 bit0.02 ~ 0.05 dB3 ~ 6 dB16 ~ 18 bit0.05 ~ 0.10 dB 6 dB20 bit 以上明显建议重新分配通带本例补偿增益 2.85 dB落在第二档16 位系数1 符号 1 整数 14 小数够用。累加器位宽的估算公式是Acc ≥ B_coef ceil(log2(L)) 1其中 L 是滤波器长度n1加 1 是保护位。本例 B_coef16L31ceil(log2(31))5所以165122取 24 位留足余量。再往上一层如果 CIC 用的是位增长bit growth结构累加器位宽 输入位宽 ceil(log2((R·M)^N))。本例输入 12 位增益 2^15所以 CIC 内部要 27 位。这个数字直接决定了 CIC 那部分寄存器的规模比补偿滤波器那边重要得多。4.2 对称结构、乘法器数量与放置位置由于firceqrip设计出的系数严格对称第一类和第二类线性相位滤波器的特性硬件实现时一定要用折叠结构把b[k]和b[n-k]先加再乘乘法器数量直接砍半。31 阶滤波器本来是 31 个乘法器折叠后是 16 个这个节省很实在。放置位置也有讲究。补偿 FIR 应该放在CIC 抽取之后也就是工作在低采样率侧。原因很直接在 12.5 MHz 下跑 31 阶 FIR每秒 31×12.5M 387.5M 次乘加如果放在抽取前的 100 MHz 侧就是 3.1G 次乘加差八倍。而且低采样率侧的时序余量宽松得多布线压力小。有人会问放在抽取后会不会影响补偿效果不会。因为 CIC 的 droop 是相对于输出采样率定义的幅频特性抽取之后看到的频响就是那条 droop 曲线在哪个速率上补都是一样的。唯一的区别是延迟——放在低速侧同样阶数对应的绝对时间延迟反而更小。还有一种情况是补偿器需要放在抽取前那就是补偿带宽接近输入奈奎斯特、需要多相分解的时候。但那种场景很少见一般意味着 CIC 抽取比选得太小了应该先重新审视链路参数。4.3 延迟与增益的一点账线性相位 FIR 的群延迟是n/2个采样点。30 阶对应 15 个输出样本在 12.5 MHz 下是 1.2 μs。这个数字要计入整条链路的延迟预算尤其对闭环控制、实时同步这类应用几个微秒都可能影响环路的稳定性。增益账也要算清楚。补偿滤波器的直流增益应该精确等于 1因为 CIC 已经归一化过了但它的通带峰值增益是 1.39。这意味着如果输入信号是一个满幅的低频正弦经过补偿器后峰值会超出满幅定点实现时必须预留 1.5 dB 以上的余量否则会在通带边缘削顶。这个余量要么在补偿器输出右移一位代价是损失 6 dB 动态范围要么在上游链路把信号压下来一点。我一般会把补偿器的输出位宽做宽 2 位保留小数部分把右移放到后级滤波器里一起做这样不会损失精度也不会有削顶风险。5. 联调阶段翻车的四种典型形态5.1 补偿带宽只有预期的一半wo 的归一化对象搞错了这是最高频的错误我自己也犯过。firceqrip的wo参数归一化到奈奎斯特频率也就是 1 对应 fs/2。但我算理论曲线的时候习惯用归一化到采样率的形式0.5 对应 fs/2。两套坐标差了整整一倍。具体的翻车过程是这样的我想让通带覆盖到输出采样率的 20%x0.2于是填了wo0.2。结果设计出来的补偿滤波器在 x0.1 就开始往下掉了补偿带宽只有预期的一半通带边缘 0.2 处完全没被抬起。当时第一反应是firceqrip 的 invsinc 参数填错了调了半天第二个元素越调越乱。正确的填法是wo 0.4因为 0.2 相对采样率 0.4 相对奈奎斯特。判断有没有搞错最简单的办法是看补偿曲线的拐点位置对不对——理论上的 droop 曲线在哪个频率开始明显下弯设计出来的补偿曲线就应该在同一个位置开始上翘两者错开就是归一化错了。排查路径我总结成三步先画理论1/|H_cic|曲线标出通带边缘再画设计结果的响应看它的上翘终点落在哪两个位置对不上先检查归一化再看 invsinc 参数最后才怀疑阶数不够。这个顺序很重要因为前两个问题的概率远高于