FPGA 实现 CIC 抽取滤波器设计与仿真
一、CIC 滤波器原理与结构
CIC(级联积分梳状)滤波器是一种高效的多速率信号处理结构,属于无乘法器的线性相位 FIR 滤波器。它主要由积分器和梳状器组成,仅依赖加法器、减法和寄存器,在资源受限的 FPGA 上极具优势,常用于数字下变频(DDC)和数字上变频(DUC)。
1. 工作模式
CIC 主要有三种工作模式:
- 抽取滤波器:将高速输入降为低速输出,适用于降低采样率系统。
- 插值滤波器:将低速输入升为高速输出,适用于提升采样率系统。
- 单纯滤波器:输入输出速率不变,主要用于移动平均滤波。
以抽取滤波器为例,数据先经过多级积分器累加,再进入梳状器进行差分运算。假设输入采样率为 Fs,抽取因子为 N,则输出速率为 Fs/N。单级 CIC 的第一旁瓣阻带衰减固定为 13.46dB,抑制能力有限,实际应用中常通过级联多段来提升性能。N=5 或 N=6 是常见的选择,能在性能和资源消耗间取得平衡。
2. 最大位宽计算
由于积分器的累加效应,内部数据位宽会随级数增长。设计时必须预留足够的位宽以防止溢出。
最大位宽计算公式如下:
B_max = B_in + N * ceil(log2(R * M))
其中:
B_in:输入位宽R:抽取/插值因子M:差分延迟(通常为 1)N:级数
示例:若输入 16 位,抽取因子 R=64,级数 N=3。
- log2(64) = 6
- 最大位宽 = 16 + 3 * 6 = 34 位
随着级数增加,阻带抑制能力提升,但通带衰减也会加剧。这通常需要在 CIC 后级联补偿 FIR 滤波器来修正通带响应。
二、Verilog 实现与仿真
下面展示一个具体的 FPGA 实现案例,参数设定为 3 级级联,抽取因子 16。
1. 核心模块代码
该模块包含积分器、降采样计数器和梳状器逻辑。
`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; // 采样使能标志
// 梳状器中间信号
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_in;
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


