FPGA 实现 CIC 抽取滤波器
什么是 CIC 滤波器
CIC(级联积分梳状)滤波器是一种高效的多速率信号处理结构,本质上属于无乘法器的线性相位 FIR 滤波器。在数字下变频(DDC)和数字上变频(DUC)场景中,它凭借结构简单、仅依赖加法器和寄存器而成为首选方案。
CIC 滤波器主要有三种工作模式:
- 抽取滤波器:将高速输入数据流降为低速输出,常用于降低采样率或 DDC 前端。
- 插值滤波器:将低速输入升为高速输出,用于提升采样率或 DUC 后端。
- 单纯滤波器:输入输出速率一致,主要用于移动平均滤波。
其核心由积分器(Integrator)和梳状器(Comb)组成。积分器执行累加运算,状态方程为 $y[n] = y[n-1] + x[n]$;梳状器执行差分运算,状态方程为 $y[n] = x[n] - x[n-R]$,其中 R 为抽取/插值因子。由于单级 CIC 的第一旁瓣阻带衰减固定为 13.46dB,抑制能力有限,实际工程中常通过级联多级来提升性能。可以将级联想象成多层过滤系统:一级像纱网,二级加滤布,五级则相当于经过多重净化。通常 N=5 或 N=6 能在性能和资源间取得较好平衡。
以抽取为例,假设输入采样率 Fs 为 100MHz,抽取因子为 10,则输出速率为 10MHz。数据先经过多级积分器累加,随后在降采样点进入梳状器进行微分运算,最终得到抽取后的数值。
最大位宽计算
CIC 滤波器内部全是加减法,但积分器的累加效应会导致数据位宽随级数线性增长,这是硬件实现中最关键的资源考量点。
最大位宽计算公式如下:
$$B_{max} = B_{in} + N \times \lceil \log_2(R \times M) \rceil$$
其中:
- $B_{in}$:输入位宽
- $R$:抽取/插值因子
- $M$:差分延迟(通常为 1)
- $N$:级数
举例说明:若输入位宽 16 位,抽取因子 64,级数分别为 3、5、6 时:
- N=3:16 + 3×6 = 34 位
- N=5:16 + 5×6 = 46 位
- N=6:16 + 6×6 = 52 位
可见级数增加会显著消耗寄存器和逻辑资源。增加级数虽然提升了阻带抑制(抗混叠/抗镜像),但也带来了通带衰减的副作用,尤其是通带边缘信号会被削弱,因此后续往往需要级联补偿 FIR 滤波器来校正幅频响应。
Verilog 代码实现
下面给出一个具体的 FPGA 实现案例,参数设定为 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 else if (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 else if (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 else if (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 else if (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

