我们上一篇博客对CIC滤波器的理论与实现进行了详细的介绍,相信同学们从前一篇博客可以进行相应的学习。但是CIC滤波器本质上就是 低通滤波器。为什么如此常用,只是因为CIC滤波器构成简单不需要乘法器,运行速度快,在通信中的上下变频中非常常用。当然CIC滤波器可是使用低通滤波器来实现,但是资源利用率不高。
本次项目我们将使用多级CIC滤波器进行抽取与插值操作,这样做的目的主要是为了提高阻带衰减,是有用信号的占比更大。但是提高阻带衰减的同时会降低通带衰减,这些特点也就决定了CIC滤波器在数字上下变频中的应用,因为该应用中信号的抽样频率远大于有用信号的频率。学习这一篇博客的时候一定要先学习上一篇博客,否则会导致概念不明白。
根据上一篇文章我们关于CIC滤波器严格的计算,可以发现单级CIC滤波器的第一旁瓣电平衰减固定为 13.46dB,且与滤波器的阶数无关。然而,这个 阻带衰减通常不满足实际中的要求,所以会把多个简单的CIC滤波器进行级联,所以称为 多级CIC滤波器。
不过增加CIC滤波器的级数也有不利的影响:通带衰减也随着增加。换句话说,对于给定的通带衰减要求,多 级CIC滤波器的通带范围会随着级数的增加而不断变窄,通带衰减也会随之增加。滤波器的设计不仅要考虑阻带误差容限,还要考虑通带误差容限,设计多级CIC滤波器时要注意考虑这个问题。这里再次强调CIC滤波器就是低通滤波器,至于为什么不使用其他的低通滤波器,而使用CIC滤波器,主要是因为结构简单。
其实,CIC滤波器大多应用于 抗混叠抽取/内插滤波器,也是因为上述的原因。因为在抗混叠应用中,有效信号频带往往远小于采样率,因此总可以设计出同时满足通带和阻带要求的CIC滤波器。
常见信号的抽取操作如下图:
从上面我们可以看出一般先进行抗混叠滤波再进行抽取。这事抗混叠滤波就可以使用如下CIC多级滤波器进行级联。
上面的图是一级CIC,如果感觉阻带衰减不满足要求,那么可以进行多级CIC滤波器的级联。
理论上多级CIC滤波器可以直接由多个单级CIC滤波器级联得到,但根据 Noble恒等式(“先进行抽取或者插值,再进行线性滤波”与“先进行线性滤波,再进行抽取或者插值”这两者是可以等价的) 。这个公式特别重要也是CIC滤波器在抗混叠滤波器应用如此广泛的另一个原因。所以根据这个原理,当抽取时,我们把抽取操作放到CIC滤波器积分与梳状之间。假设抽取个数与CIC滤波器的阶数相同,那么级联的梳状滤波器正好变成一阶,如下图:
在这里插入图片描述
多级CIC抽取的整个级联图倍简化成上面图形,所以CIC滤波器在抽取中才会那么常用。
上面我们我们已经介绍了CIC滤波器在抽取中的优点以及特殊结构。接下来我们将介绍CIC滤波器在内插中的优点及特殊结构。
常见的内插操作如下:
从上面我们可以看出一般先进行内插再进行抗混叠滤波。这时抗混叠滤波就可以使用如下CIC多级滤波器进行级联。
上面的图是一级CIC,如果感觉阻带衰减不满足要求,那么可以进行多级CIC滤波器的级联。
同样,利用 Noble恒等式,我们可以将内插操作放到梳状滤波器与积分滤波器的中间。假设内插的个数与梳状滤波器的阶数相同,那么将内插操作插入到梳状滤波器之后,便可以将梳状滤波器化简为1阶滤波器。从而减少硬件资源量,变化之后的图形如下图:
我们实现的功能是,采样率1MHz,信号2.5KHz的正弦波进行采样,然后将采样之后的信号进行4倍抽取。在这个程序中,我们使用的是3级4阶CIC滤波器级联进行滤波操作。最后对比两个结果。我们严格按照变形之后的图形,即经过Noble恒等式变形之后的图形进行编写MATLAB代码:
close all
clear all
clc
%set system parameter
fs = 2500; %The frequency of the local oscillator signal
Fs = 1000000; %sampling frequency
N = 24; %Quantitative bits
L = 50000000;
%Generating an input signal
t =0:1/Fs:(1/Fs)*(L-1); %Generating the time series of sampling frequencies
sc =sin(2*pi*fs*t); %a sinusoidal input signal that produces a random starting phase
b =[1,-1];%integerator
a =[1,-1];%comb
%comb
c1=filter(1,b,sc);
c2=filter(1,b,c1);
c3=filter(1,b,c2);
y = downsample(c3,4);
%integerater
i1 =filter(a,1,y);
i2 =filter(a,1,i1);
i3 =filter(a,1,i2);
sf = i3;
figure(1),
subplot(211),stem(t(1:1600),sc(1:1600));
xlabel('时间(t)','fontsize',8);
ylabel('幅度(dB)','fontsize',8);
title('sc','fontsize',8);
subplot(212),stem(t(1:400),sf(1:400));
xlabel('时间/4(t)','fontsize',8);
ylabel('幅度(dB)','fontsize',8);
title('sf','fontsize',8);
相信大家经过MATLAB代码与博客理论之间的学习,可以很容易学会CIC滤波器在信号抽取上面的实现。
我们将上面的MATLAB文件进行运行,结果如下:
在这里插入图片描述
从上面的结果可以看出我们成功实现了CIC抽取操作。
我们实现的功能是,采样率0.25MHz,信号2.5KHz的正弦波进行采样,然后将采样之后的信号进行4倍内插。在这个程序中,我们使用的是3级4阶CIC滤波器级联进行滤波操作。我们的内插操作也是按照前面Noble恒等式变形之后的图形进行的程序**。这里需要注意一下,上一篇博客关于单级CIC滤波器的内插操作,我们没有按照变形之后的图像操作,只是把CIC当成了一个简单的低通滤波器来进行信号处理。但是抽取部分吗是严格按照变形之后的图形进行的。**,代码如下:
close all
clear all
clc
%set system parameter
fs = 2500; %The frequency of the local oscillator signal
Fs = 250000; %sampling frequency
L = 81920;
%Generating an input signal
t =0:1/Fs:(1/Fs)*(L-1); %Generating the time series of sampling frequencies
sc =sin(2*pi*fs*t); %a sinusoidal input signal that produces a random starting phase
b =[1,-1];%comb
a =[1,-1];%integerator
%comb
c1=filter(b,1,sc);
c2=filter(b,1,c1);
c3=filter(b,1,c2);
y = upsample(c3,4);
%integerater
i1 =filter(1,a,y);
i2 =filter(1,a,i1);
i3 =filter(1,a,i2);
sf = i3;
figure(1),
subplot(211),stem(t(1:320),sc(1:320));
xlabel('时间(t)','fontsize',8);
ylabel('幅度(dB)','fontsize',8);
title('sc','fontsize',8);
subplot(212),stem(t(1:1280),sf(1:1280));
xlabel('时间*4(t)','fontsize',8);
ylabel('幅度(dB)','fontsize',8);
title('sf','fontsize',8);
同样从上面的MATLAB代码,相信大家可以很容易的学会基于CIC滤波器的内插操作。
从上面的结果可以看出,我们实现的基于CIC滤波器的内插操作成功实现。
由于积分运算会导致数据位宽扩展,首先需要确定积分器的输出数据位宽。可以借助如下公式,当输入信号为Bin位时,积分器最大可能输出位数为:
R为抽取/插值倍数,D为滤波器级数,N为滤波器阶数。
梳状滤波器模块设计与积分模块很相似,只不过是由寄存器和减法器组成的。该模块的输出即为整个CIC抽取滤波器的输出数据,位宽可由如下公式确定:
当然我们程序中使用的位宽稍大。
我们此代码得书写严格按照我们博客上面给得结构,也与MATLAB的流程一摸一样,但是输出可以极大的缩小位宽,为了方便原因,我们不再调整。
我们实现的功能是,采样率1MHz,信号2.5KHz的正弦波进行采样,然后将采样之后的信号进行4倍抽取。在这个程序中,我们使用的是3级4阶CIC滤波器级联进行滤波操作
cic模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : cic.v
// Create Time : 2020-04-24 15:08:16
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module cic(
//System Interfaces
input sclk ,
input rst_n ,
//Communication Interfaces
input rvalid ,
input signed [ 9:0] din ,
output reg tvalid ,
output reg signed [33:0] dout
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
wire signed [33:0] din_x ;
//积分器
wire signed [33:0] int_in1 ;
wire signed [33:0] int_in2 ;
wire signed [33:0] int_in3 ;
reg signed [33:0] int_r1 ;
reg signed [33:0] int_r2 ;
reg signed [33:0] int_r3 ;
wire signed [33:0] int_data ;
//抽取
reg [ 2:0] cnt ;
reg ext_valid ;
//梳状滤波器
wire signed [33:0] comb_din ;
reg signed [33:0] comb_r1 ;
reg signed [33:0] comb_r2 ;
reg signed [33:0] comb_r3 ;
wire signed [33:0] comb_in1 ;
wire signed [33:0] comb_in2 ;
wire signed [33:0] comb_in3 ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign din_x = rvalid == 1'b1 ? {{24{din[9]}},din}: din_x;
assign int_in1 = int_r1 + din_x;
assign int_in2 = int_r2 + int_in1;
assign int_in3 = int_r3 + int_in2;
assign comb_din = ext_valid == 1'b1 ? int_in3 : comb_din;
assign comb_in1 = comb_din - comb_r1;
assign comb_in2 = comb_in1 - comb_r2;
assign comb_in3 = comb_in2 - comb_r3;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
int_r1 <= 34'd0;
else if(rvalid == 1'b1)
int_r1 <= int_in1;
else
int_r1 <= int_r1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
int_r2 <= 34'd0;
else if(rvalid == 1'b1)
int_r2 <= int_in2;
else
int_r2 <= int_r2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
int_r3 <= 34'd0;
else if(rvalid == 1'b1)
int_r3 <= int_in3;
else
int_r3 <= int_r3;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt <= 3'd0;
else if(rvalid == 1'b1 && cnt == 'd3)
cnt <= 3'd0;
else if(rvalid == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
always @(*)
if(rvalid == 1'b1 && cnt == 'd3)
ext_valid <= 1'b1;
else
ext_valid <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
comb_r1 <= 34'd0;
else if(ext_valid == 1'b1)
comb_r1 <= int_in3;
else
comb_r1 <= comb_r1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
comb_r2 <= 34'd0;
else if(ext_valid == 1'b1)
comb_r2 <= comb_in1;
else
comb_r2 <= comb_r2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
comb_r3 <= 34'd0;
else if(ext_valid == 1'b1)
comb_r3 <= comb_in2;
else
comb_r3 <= comb_r3;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
dout <= 'd0;
else if(ext_valid == 1'b1)
dout <= comb_in3;
else
dout <= dout;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
tvalid <= 1'b0;
else
tvalid <= ext_valid;
endmodule
tb模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : tb.v
// Create Time : 2020-04-24 16:00:12
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module tb();
reg sclk ;
reg rst_n ;
wire rvalid ;
wire [ 7:0] din ;
wire tvalid ;
wire [12:0] dout ;
initial begin
sclk = 1'b0;
rst_n <= 1'b0;
#(1000);
rst_n <= 1'b1;
end
always #(500) sclk = ~sclk;
dds_compiler_0 dds_compiler_0_inst (
.aclk (sclk ), // input wire aclk
.m_axis_data_tvalid (rvalid ), // output wire m_axis_data_tvalid
.m_axis_data_tdata (din ) // output wire [7 : 0] m_axis_data_tdata
);
cic cic_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//Communication Interfaces
.rvalid (rvalid ),
.din ({{2{din[7]}},din} ),
.tvalid (tvalid ),
.dout (dout )
);
endmodule
上面我们为了方便起见使用了DDS IP来产生正弦波信号,详细的IP定制步骤可以查看我们前面的DDS的文章。
进行运行结果如下:
放大的局部信息:
从上面的运行结果中可以看出,我们成功实现了原始信号的四抽取。如果要实现更高倍数的抽取,只需要对代码中稍作修改即可。
同样这里梳状滤波器、积分滤波器的输入输出位宽满足多级CIC滤波器的抽取时候的条件。本次实验选取的位宽依旧有很大剩余,这是为了计算的简洁性,但是做学术的时候绝对不可以出现这样的情况。**
我们实现的功能是:采样率0.25MHz,信号2.5KHz的正弦波进行采样,然后将采样之后的信号进行4倍内插。在这个程序中,我们使用的是3级4阶CIC滤波器级联进行滤波操作。
这里废话不多说,直接给出相应的代码供大家学习,这里需要注意我们使用的结构是经过Noble恒等式优化之后的结构,整个过程与MATLAB仿真完全一摸一样。
CIC_inter模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : CIC_inter.v
// Create Time : 2020-04-24 20:20:56
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module CIC_inter(
//System Interfaces
input sclk ,
input rst_n ,
//Communication Interfaces
input rvalid ,
input [ 9:0] din ,
output wire tvalid ,
output wire [33:0] dout
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
wire signed [33:0] din_x ;
//积分器
wire signed [33:0] int_in1 ;
wire signed [33:0] int_in2 ;
wire signed [33:0] int_in3 ;
reg signed [33:0] int_r1 ;
reg signed [33:0] int_r2 ;
reg signed [33:0] int_r3 ;
wire signed [33:0] int_data ;
wire signed [33:0] int_din ;
//抽取
reg [ 2:0] cnt ;
reg intr_valid ;
//梳状滤波器
reg signed [33:0] comb_r1 ;
reg signed [33:0] comb_r2 ;
reg signed [33:0] comb_r3 ;
wire signed [33:0] comb_in1 ;
wire signed [33:0] comb_in2 ;
wire signed [33:0] comb_in3 ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign din_x = rvalid == 1'b1 ? {{24{din[9]}},din} : 34'd0;
assign int_in1 = int_r1 + int_din;
assign int_in2 = int_r2 + int_in1;
assign int_in3 = int_r3 + int_in2;
assign comb_in1 = din_x - comb_r1;
assign comb_in2 = comb_in1 - comb_r2;
assign comb_in3 = comb_in2 - comb_r3;
assign int_din = rvalid == 1'b1 ? comb_in3 : 34'd0;
assign dout = int_in3;
assign tvalid = intr_valid;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
comb_r1 <= 34'd0;
else if(rvalid == 1'b1)
comb_r1 <= din_x;
else
comb_r1 <= comb_r1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
comb_r2 <= 34'd0;
else if(rvalid == 1'b1)
comb_r2 <= comb_in1;
else
comb_r2 <= comb_r2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
comb_r3 <= 34'd0;
else if(rvalid == 1'b1)
comb_r3 <= comb_in2;
else
comb_r3 <= comb_r3;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 'd3)
cnt <= 3'd0;
else if(rvalid == 1'b1)
cnt <= 3'd1;
else
cnt <= cnt + 1'b1;
always @(*)
if(rvalid == 1'b1)
intr_valid <= 1'b1;
else if(cnt > 0)
intr_valid <= 1'b1;
else
intr_valid <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
int_r1 <= 34'd0;
else if(intr_valid == 1'b1)
int_r1 <= int_in1;
else
int_r1 <= int_r1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
int_r2 <= 34'd0;
else if(intr_valid == 1'b1)
int_r2 <= int_in2;
else
int_r2 <= int_r2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
int_r3 <= 34'd0;
else if(intr_valid == 1'b1)
int_r3 <= int_in3;
else
int_r3 <= int_r3;
endmodule
仔细读一下代码再结合前面的理论相信大家可以学会CIC滤波器在内插时候的操作。
tb模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : tb.v
// Create Time : 2020-04-24 16:00:12
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module tb();
reg sclk ;
reg rst_n ;
wire rvalid ;
wire [ 7:0] din ;
reg clk_250k ;
reg clk_1m ;
reg [ 8:0] cnt_250k ;
reg [ 8:0] cnt_1m ;
wire tvalid ;
wire [33:0] dout ;
reg CIC_inter_valid ;
reg [ 2:0] cnt ;
initial begin
sclk = 1'b0;
rst_n <= 1'b0;
#(1000);
rst_n <= 1'b1;
end
always #(10) sclk = ~sclk;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_250k <= 9'd0;
else if(cnt_250k == 'd99)
cnt_250k <= 9'd0;
else
cnt_250k <= cnt_250k + 1'b1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
clk_250k <= 1'b0;
else if(cnt_250k == 'd99)
clk_250k <= ~clk_250k;
else
clk_250k <= clk_250k;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_1m <= 9'd0;
else if(cnt_1m == 'd24)
cnt_1m <= 9'd0;
else
cnt_1m <= cnt_1m + 1'b1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
clk_1m <= 1'b0;
else if(cnt_1m == 'd24)
clk_1m <= ~clk_1m;
else
clk_1m <= clk_1m;
always @(posedge clk_1m or negedge rst_n)
if(rst_n == 1'b0)
cnt <= 3'd0;
else if(cnt == 'd3 && rvalid == 1'b1)
cnt <= 3'd0;
else if(rvalid == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
always @(posedge clk_1m or negedge rst_n)
if(rst_n == 1'b0)
CIC_inter_valid <= 1'b0;
else if(cnt == 'd3 && rvalid == 1'b1)
CIC_inter_valid <= 1'b1;
else
CIC_inter_valid <= 1'b0;
dds_compiler_0 dds_compiler_0_inst (
.aclk (clk_250k ), // input wire aclk
.m_axis_data_tvalid (rvalid ), // output wire m_axis_data_tvalid
.m_axis_data_tdata (din ) // output wire [7 : 0] m_axis_data_tdata
);
CIC_inter CIC_inter_inst(
//System Interfaces
.sclk (clk_1m ),
.rst_n (rst_n ),
//Communication Interfaces
.rvalid (CIC_inter_valid ),
.din ({{2{din[7]}},din} ),
.tvalid (tvalid ),
.dout (dout )
);
endmodule
正弦波的产生我们同样使用了DDS IP核。
CIC滤波器内插结果如下:
放大后的细节如下:
由上图可知,我们成功实现了基于CIC滤波器的内插操作。
[1]、FPGADesigner——CSDN博主
[2]、长弓的坚持——CSDN博主
[3]、行州人——CSDN博主
[4]、FPGA开源工作室——CSDN博主
在查找一些资料的时候,发现一些博主只给出部分代码,其实这样别人根本看不懂,只有给出整个工程代码才易于知识的传播。创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: