FPGA CIC抽取滤波器设计 - 教程

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

R为抽取率,M为延迟因子,fs为输入采样率,fs/R为输出采样率,当阶数为N时,只需要将N个积分器串联在一起,N个梳状器串联在一起即可

cic抽取滤波器的z域表达式如下

令z=e^{j2\pi f}可得频域表达式

将f=0可得直流增益

因此相比于IIR、FIR数字滤波器,cic的直流增益随着M、R、N的增加指数级上升,因此在数字电路设计位宽时cic输出位宽可根据直流增益的位宽与输入位宽来确定。

cic抽取滤波器截止频率无法精确计算,但可以获取近似值,假设对其进行了直流增益补偿,即整体输出除以直流增益,幅频特性-3db为截止频率点

可见归一化截止频率\Omega _{cic}始终小于归一化那奎斯特频率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。

posted @ 2025-09-04 20:29  yjbjingcha  阅读(255)  评论(0)    收藏  举报