一、什么是 CIC 滤波器
(一)CIC 滤波器原理和结构
CIC(级联积分梳状)滤波器是一种高效的多速率信号处理滤波器,属于无乘法器的线性相位 FIR 滤波器。常用于数字下变频(DDC)和数字上变频(DUC)中。其主要优点是不需要乘法器,结构简单,仅由加法器、减法器和寄存器组成。
CIC 滤波器有三种工作模式:
- 抽取滤波器:数据流由高速输入变为低速输出,主要应用于数字下变频及降低采样率的系统。
- 插值滤波器:数据流由低速输入变为高速输出,主要应用于数字上变频及提升采样率的系统。
- 单纯滤波器:数据流速率不变,积分器和梳状器都工作在同一个采样率下,主要应用于移动平均滤波。
CIC 滤波器主要由积分器和梳状器组成。其中 N 为抽取倍数(抽取因子)。例如输入信号采样率为 100MHz,如果抽取因子为 10,那么降采样之后的信号速率就是 10MHz。即 F_out = F_in / 抽取因子。
由于单级 CIC 的第一旁瓣阻带衰减是固定的 13.46dB,无法很好地抑制旁瓣,因此可以通过级联的方式来提升抑制效果。级数 N 越高,滤波效果越好,但系统的资源消耗和位宽增长也会增加。在数字下变频等应用中,N=5 或 N=6 是非常常见的选择。
假设数据以 Fs 的频率输入滤波器,先通过多级积分滤波器进行累加。若希望得到 Fs 的 1/N 分频的采样数据,则将积分滤波器的最后一级输出以 Fs/N 的频率进入梳状滤波器。梳状滤波器进行微分运算,即当前值减去上一次的值。经过多级梳状滤波器后,最后一级结果即为抽取的数值。
(二)最大位宽计算
CIC 滤波器中存在加减法操作,需关注计算过程中的信号位宽问题。由于积分器的累加效应,内部数据位宽会随着级数 N 线性增长。
最大位宽计算公式如下: B_max = B_in + N * ceil(log2(R * M))
其中:
- B_in:输入位宽
- R:抽取/插值因子
- M:差分延迟(通常为 1)
- ceil():向上取整函数
例如:假设 B_in=16 位,R=64,M=1,ceil(log2(64)) = 6。
- 当 N=3 时,最大位宽 = 16 + 3*6 = 34 位
- 当 N=5 时,最大位宽 = 16 + 5*6 = 46 位
- 当 N=6 时,最大位宽 = 16 + 6*6 = 52 位
级数 N 对滤波器性能的影响:
- 阻带抑制:N 越高,阻带衰减越大,滤波器对带外信号的抑制能力越强,能更好地防止混叠或抑制镜像。
- 通带衰减:N 越高,通带内的衰减也越严重。为了补偿这种通带衰减,通常需要在 CIC 滤波器后面级联一个补偿 FIR 滤波器。
二、CIC 抽取滤波器 FPGA 实现
(一)Verilog 代码
以下 CIC 滤波器代码参数:级数 3,抽取因子 16。
`timescale 1ns /1ps
module cic_down_sample#(
parameter DATA_IN_WIDTH = 8,
parameter DATA_OUT_WIDTH = 8,
parameter CIC_FACTORS = 16, // 抽取因子
parameter MAX_WIDTH = 20 // 最大位宽
)(
input clk,
input rstn,
input signed [DATA_IN_WIDTH-1:0] data_in,
input data_in_valid,
output signed [DATA_OUT_WIDTH-1:0] data_out
);
wire signed [MAX_WIDTH-1:0] add_data_0;
wire signed [MAX_WIDTH-1:0] add_data_1;
wire signed [MAX_WIDTH-1:0] add_data_2;
reg signed [MAX_WIDTH-1:0] data_0;
reg signed [MAX_WIDTH-1:0] data_1;
reg signed [MAX_WIDTH-1:0] data_2;
reg [4:0] cnt_div;
reg sample_flag; // 采样标志
reg signed [MAX_WIDTH-1:0] comb_in;
wire signed [MAX_WIDTH-1:0] add_comb_0;
wire signed [MAX_WIDTH-1:0] add_comb_1;
wire signed [MAX_WIDTH-1:0] add_comb_2;
reg signed [MAX_WIDTH-1:0] comb_0;
reg signed [MAX_WIDTH-1:0] comb_1;
reg signed [MAX_WIDTH-1:0] comb_2;
// 积分器
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
data_0 <= 'd0;
data_1 <= 'd0;
data_2 <= 'd0;
end else begin
data_0 <= add_data_0;
data_1 <= add_data_1;
data_2 <= add_data_2;
end
end
assign add_data_0 = {{12{data_in[7]}}, data_in} + data_0;
assign add_data_1 = add_data_0 + data_1;
assign add_data_2 = add_data_1 + data_2;
// 降采样处理
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt_div <= 'd0;
end elseif (cnt_div == CIC_FACTORS - 1) begin
cnt_div <= 'd0;
end else begin
cnt_div <= cnt_div + 1;
end
end
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
sample_flag <= 1'b0;
end elseif (cnt_div == CIC_FACTORS - 1) begin
sample_flag <= 1'b1;
end else begin
sample_flag <= 1'b0;
end
end
// 梳状器
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
comb_in <= 'd0;
end elseif (sample_flag) begin
comb_in <= add_data_2;
end
end
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
comb_0 <= 'd0;
comb_1 <= 'd0;
comb_2 <= 'd0;
end elseif (sample_flag) begin
comb_0 <= comb_in;
comb_1 <= add_comb_0;
comb_2 <= add_comb_1;
end
end
assign add_comb_0 = comb_in - comb_0;
assign add_comb_1 = add_comb_0 - comb_1;
assign add_comb_2 = add_comb_1 - comb_2;
assign data_out = add_comb_2[19:12];
endmodule



