FPGA CIC抽取滤波器设计 - 教程
cic抽取滤波器是一种兼具低通抗混叠与降采样作用的高速率滤波器,内部不需要系数计算也不涉及乘法运算,仅有梳状器comb和积分器integrator组成,一阶cic抽取滤波器结构如下

R为抽取率,M为延迟因子,fs为输入采样率,fs/R为输出采样率,当阶数为N时,只需要将N个积分器串联在一起,N个梳状器串联在一起即可
cic抽取滤波器的z域表达式如下

令z=可得频域表达式

将f=0可得直流增益

因此相比于IIR、FIR数字滤波器,cic的直流增益随着M、R、N的增加指数级上升,因此在数字电路设计位宽时cic输出位宽可根据直流增益的位宽与输入位宽来确定。
cic抽取滤波器截止频率无法精确计算,但可以获取近似值,假设对其进行了直流增益补偿,即整体输出除以直流增益,幅频特性-3db为截止频率点


可见归一化截止频率始终小于归一化那奎斯特频率0.5,同时随着MRN增加而减少,因此可知cic抽取滤波器具有良好的抗混叠能力
因此同时具备抗混叠与降采样功能的cic抽取滤波器广泛应用于数字抗混叠滤波器的设计
在matlab中使用fdatool设计如下



点击实现模型来在simulink中看其具体结构


从这来导出幅频特性图



使用Verilog语言编写如下
`timescale 1ns / 1ps
module CIC_Decimator#(
parameter BW_IN = 16 ,
parameter BW_OUT = 19 ,
parameter ORDER = 3 ,
parameter COMB_DLY= 2 ,
parameter DSAMP = 3
)(
input i_clk ,
input i_rst ,
input [BW_IN - 1:0] i_data ,
input i_vld ,
output reg [BW_OUT - 1:0] o_data ,
output reg o_vld
);
// integration
reg signed [BW_OUT - 1:0] r_intg_arr [0:ORDER - 1];
wire signed [BW_OUT - 1:0] w_din_sext;
bit_extension# (
.BW_IN (BW_IN ) ,
.BW_OUT (BW_OUT ) ,
.SIGNED (1 ))
bit_extension_U(
.i_data(i_data ),
.o_data(w_din_sext)
);
genvar i;
generate
for(i = 1; i < ORDER; i = i + 1) begin
always@(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_intg_arr[i] <= 'd0;
else if(i_vld)
r_intg_arr[i] <= r_intg_arr[i] + r_intg_arr[i - 1];
else
r_intg_arr[i] <= r_intg_arr[i];
end
end
endgenerate
always@(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_intg_arr[0] <= 'd0;
else if(i_vld)
r_intg_arr[0] <= w_din_sext + r_intg_arr[0];
else
r_intg_arr[0] <= r_intg_arr[0];
end
// downsampling
reg [7:0] r_dsamp_cnt ;
(*keep = "true"*)reg r_dsamp_vld_cp0 ;
(*keep = "true"*)reg r_dsamp_vld_cp1 ;
(*keep = "true"*)reg r_dsamp_vld_cp2 ;
(*keep = "true"*)reg r_dsamp_vld_cp3 ;
always@(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_dsamp_cnt <= 'd0;
else if(i_vld)
if(r_dsamp_cnt < DSAMP - 1)
r_dsamp_cnt <= r_dsamp_cnt + 1;
else
r_dsamp_cnt <= 'd0;
else
r_dsamp_cnt <= r_dsamp_cnt;
end
reg [BW_OUT - 1:0] r_dsamp_data ;
always@(posedge i_clk or posedge i_rst) begin
if(i_rst) begin
r_dsamp_vld_cp0 <= 'b0;
r_dsamp_vld_cp1 <= 'b0;
r_dsamp_vld_cp2 <= 'b0;
r_dsamp_vld_cp3 <= 'b0;
r_dsamp_data <= 'd0;
end
else if(i_vld & !r_dsamp_cnt) begin
r_dsamp_vld_cp0 <= 'b1;
r_dsamp_vld_cp1 <= 'b1;
r_dsamp_vld_cp2 <= 'b1;
r_dsamp_vld_cp3 <= 'b1;
r_dsamp_data <= r_intg_arr[ORDER - 1];
end
else begin
r_dsamp_vld_cp0 <= 'b0;
r_dsamp_vld_cp1 <= 'b0;
r_dsamp_vld_cp2 <= 'b0;
r_dsamp_vld_cp3 <= 'b0;
r_dsamp_data <= r_dsamp_data;
end
end
// comb
reg signed [BW_OUT - 1:0] r_diff_arr [0:ORDER - 1];
reg signed [BW_OUT - 1:0] r_comb_arr [0:ORDER - 1][0:COMB_DLY - 1];
generate
for(i = 1; i < ORDER; i = i + 1) begin
always@(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_diff_arr[i] <= 'd0;
else if(r_dsamp_vld_cp1)
r_diff_arr[i] <= r_diff_arr[i - 1] - r_comb_arr[i][COMB_DLY - 1];
else
r_diff_arr[i] <= r_diff_arr[i];
end
end
endgenerate
always@(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_diff_arr[0] <= 'd0;
else if(r_dsamp_vld_cp3)
r_diff_arr[0] <= r_dsamp_data - r_comb_arr[0][COMB_DLY - 1];
else
r_diff_arr[0] <= r_diff_arr[0];
end
integer j;
generate
for(i = 1; i < ORDER; i = i + 1) begin
always@(posedge i_clk or posedge i_rst) begin
if(i_rst)
for(j = 0; j < COMB_DLY; j = j + 1) begin
r_comb_arr[i][j] <= 'd0;
end
else if(r_dsamp_vld_cp2) begin
r_comb_arr[i][0] <= r_diff_arr[i - 1];
for(j = 1; j < COMB_DLY; j = j + 1) begin
r_comb_arr[i][j] <= r_comb_arr[i][j - 1];
end
end
else
for(j = 0; j < COMB_DLY; j = j + 1) begin
r_comb_arr[i][j] <= r_comb_arr[i][j];
end
end
end
endgenerate
always@(posedge i_clk or posedge i_rst) begin
if(i_rst)
for(j = 0; j < COMB_DLY; j = j + 1) begin
r_comb_arr[0][j] <= 'd0;
end
else if(r_dsamp_vld_cp3) begin
r_comb_arr[0][0] <= r_dsamp_data;
for(j = 1; j < COMB_DLY; j = j + 1) begin
r_comb_arr[0][j] <= r_comb_arr[0][j - 1];
end
end
else
for(j = 0; j < COMB_DLY; j = j + 1) begin
r_comb_arr[0][j] <= r_comb_arr[0][j];
end
end
always@(posedge i_clk or posedge i_rst) begin
if(i_rst) begin
o_data <= 'd0;
o_vld <= 'b0;
end
else if(r_dsamp_vld_cp3) begin
o_data <= r_diff_arr[ORDER - 1];
o_vld <= 'b1;
end
else begin
o_data <= o_data;
o_vld <= 'b0 ;
end
end
endmodule
tb文件将手写cic与cic ip对比
`timescale 1ns / 1ps
module dds_cic_tb();
reg i_clk ;
reg i_rst ;
reg [15:0] i_data ;
reg i_dvld ;
reg [31:0] addr ;
reg [15:0] sine [0:999];
localparam BW_IN = 16 ,
BW_OUT = 24 ,
ORDER = 4 ,
COMB_DLY= 1 ,
DSAMP = 4 ;
wire w_ip_ready ;
wire [BW_OUT - 1:0] w_cic_out ;
wire w_cic_ovld ;
always@(posedge i_clk) begin
if(i_rst) begin
addr <= 'd0;
i_data <= 'd0;
i_dvld <= 'b0;
end
else if(w_ip_ready & ~i_dvld) begin
addr <= addr + 1;
i_data <= sine[addr];
i_dvld <= 1'b1;
end
else begin
addr <= addr;
i_data <= i_data;
i_dvld <= 'b0;
end
end
CIC_Decimator#(
.BW_IN (BW_IN ) ,
.BW_OUT (BW_OUT ) ,
.ORDER (ORDER ) ,
.COMB_DLY (COMB_DLY ) ,
.DSAMP (DSAMP ))
CIC_Decimator_U(
.i_clk (i_clk ),
.i_rst (i_rst ),
.i_data (i_data ),
.i_vld (i_dvld ),
.o_data (w_cic_out ),
.o_vld (w_cic_ovld )
);
always#10 i_clk = ~i_clk;
initial begin
i_clk = 1;
i_rst = 1;
$readmemh("G:/FPGA_Prj2025/DDS/sig.txt", sine);
#100
i_rst = 0;
end
wire [23:0] w_ipout ;
wire w_ipvld ;
cic_d
cic_d (
.aclk (i_clk ), // input wire aclk
.aresetn (~i_rst ),
.s_axis_data_tdata (i_data ), // input wire [15 : 0] s_axis_data_tdata
.s_axis_data_tvalid (i_dvld ), // input wire s_axis_data_tvalid
.s_axis_data_tready (w_ip_ready ), // output wire s_axis_data_tready
.m_axis_data_tdata (w_ipout ), // output wire [23 : 0] m_axis_data_tdata
.m_axis_data_tvalid (w_ipvld ) // output wire m_axis_data_tvalid
);
endmodule
cic ip核配置


波形仿真如下:
其中蓝色为输入波形,白色为手写cic的输出黄色为cic ip核输出

还要注意一点,由于一般需要对直流增益进行缩放补偿,因此可将MR设为2的n倍,这样只需要对cic输出进行n个算数右移即可,即$signed(cic out) >>> n。
浙公网安备 33010602011771号