目录
Written by @hzj
//JinXing Project
#2021.10.24 V1.0
#2021.10.25 V1.1
#2021.10.27 V1.2
#2021.10.28 V1.3
Matlab仿真IIR巴特沃斯低通滤波器并使用FPGA实现
(1)IIR实现原理分析
- IIR滤波器:IIR滤波器的系统函数和差分方程如下所示:
H ( z ) = ∑ i = 0 M b i z − i / ( 1 − ∑ l = 1 N a l z − l ) H(z)= \sum_{i=0}^Mb_iz^{-i} /(1-\sum_{l=1}^Na_lz^{-l}) H(z)=∑i=0Mbiz−i/(1−∑l=1Nalz−l)
y ( n ) = ∑ i = 0 M x ( n − i ) b ( i ) + ∑ l = 1 N y ( n − l ) a ( l ) y(n)= \sum_{i=0}^Mx(n-i)b(i)+ \sum_{l=1}^Ny(n-l)a(l) y(n)=∑i=0Mx(n−i)b(i)+∑l=1Ny(n−l)a(l)
- 这里进行设计一个直接Ⅱ型的IIR滤波器 。还是以设计一个低通滤波器为目的,选择巴特沃斯滤波器为设计的主要目的。下午的为结构显示图,右边为零点部分,左边为极点部分。相对于直接Ⅰ型的滤波器,消耗的资源相对较小。根据我们上述的理论分析,其实IIR实质之上就是两个FIR进行一个叠加。零点部分的模块进行一个N位的移位,极点部分的模块进行一个N-1位的移位。
- 由上述的理论分析可以知道,由于IIR的模块设计,那么可以使用一个顶层设计的方案,分别设计两个子模块进行调用。最终达到滤波的效果,下面开始进行设计IIR滤波器。
(2)IIR滤波器的设计
①采用Matlab中的FDA设计软件进行参数设计
设计一个低通滤波器,滤波器的采样频率为3000Hz,截至频率Fc为1000Hz,采用的IIR设计方法为巴特沃斯滤波器(Butterworth),指定阶数为7阶。由此构建的滤波器如下图所示。
下面开始导出系数:
选择Direct-Form Ⅰ, SOS
使用Ctrl +e快捷键进行导出系数
在命令行窗口进行输出,如下所示
>> Num
Num =
列 1 至 6
0.0817953182464095 0.572567227724866 1.7177016831746 2.86283613862433 2.86283613862433 1.7177016831746
列 7 至 8
0.572567227724866 0.0817953182464095
>> Den
Den =
列 1 至 6
1 2.31746027736733 3.02835285045499 2.39176248790061 1.24027554135094 0.407050515894209
列 7 至 8
0.0782116476869789 0.00668741488534553
进行8bit的放大,放大后的结果如下,format long g这个参数就是可是让原来科学技术法的结果不按照科学计数法显示。
因此整理之后,可以获得:
>> round(Den * 2^6)
ans =
64 148 194 153 79 26 5 0
>> round(Num * 2^6)
ans =
5 37 110 183 183 110 37 5
②zero零点模块书写:
根据前面仿真得出的量化系数 5 37 110 183 183 110 37 5,可以推断出,由于系数有对称性,那么这里只需使用到8/2=4个乘法器,现将data_reg[m]与data_reg[n-m]求和之后,再进行乘积。例如(data_reg[0] + data_reg[7]) * 5 = mul_reg[0]。因此只需要再在程序中进行二级制乘法转换即可。
`timescale 1ns/1ps
//-------------------------------------------------------
// IIR滤波器零点系数模块
//-------------------------------------------------------
//-------------------------------------------------------
// 引脚模块定义
//-------------------------------------------------------
module zero_module(
input clk , //时钟信号clk
input rst_n , //定义一个复位信号,复位信号按照下降沿有效
input signed [11:0] zero_input , //零点模块的输入
output signed [20:0] zero_output //零点模块的输出
);
//-------------------------------------------------------
// IIR滤波器中进行移位的部分,按照clk时钟周期,依次输入波
// 形数据,同时也应该在rst_n为1'b0的时候进行置零
//-------------------------------------------------------
reg signed [11:0] data_reg [6:0];
always@(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin //复位信号,系统进行置零操作,复位信号低电平有效
data_reg[0] <= 12'b0 ;
data_reg[1] <= 12'b0 ;
data_reg[2] <= 12'b0 ;
data_reg[3] <= 12'b0 ;
data_reg[4] <= 12'b0 ;
data_reg[5] <= 12'b0 ;
data_reg[6] <= 12'b0 ;
end
else
begin //普通时钟周期信号,依次进行移位操作,将新的数据进行送入
data_reg[6] <= data_reg[5] ;
data_reg[5] <= data_reg[4] ;
data_reg[4] <= data_reg[3] ;
data_reg[3] <= data_reg[2] ;
data_reg[2] <= data_reg[1] ;
data_reg[1] <= data_reg[0] ;
data_reg[0] <= zero_input ;
end
end
//-------------------------------------------------------
// IIR滤波器的零点由于系数都是对称的,因此可以通过这样子减
// 少器件,先进行加法运算
//-------------------------------------------------------
wire signed [12:0] add_reg [3:0];
assign add_reg[0] = {zero_input[11], zero_input} + {data_reg[6][11], data_reg[6]} ;
assign add_reg[1] = {data_reg[0][11], data_reg[0]} + {data_reg[5][11], data_reg[5]} ;
assign add_reg[2] = {data_reg[1][11], data_reg[1]} + {data_reg[4][11], data_reg[4]} ;
assign add_reg[3] = {data_reg[2][11], data_reg[2]} + {data_reg[3][11], data_reg[3]} ;
//-------------------------------------------------------
// IIR滤波器使用已经加好的数进行卷积
//-------------------------------------------------------
wire signed [20:0] mult_reg [3:0];
assign mult_reg[0] = {{{6{add_reg[0][12]}}, add_reg[0], 2'b0}
+ {{8{add_reg[0][12]}}, add_reg[0]}} ;//*5_101
assign mult_reg[1] = {{{3{add_reg[1][12]}}, add_reg[1], 5'b0}
+ {{6{add_reg[1][12]}}, add_reg[1], 2'b0}
+ {{8{add_reg[1][12]}}, add_reg[1]}} ;//*37_100101
assign mult_reg[2] = {{{2{add_reg[2][12]}}, add_reg[2], 6'b0}
+ {{3{add_reg[2][12]}}, add_reg[2], 5'b0}
+ {{5{add_reg[2][12]}}, add_reg[2], 3'b0}
+ {{6{add_reg[2][12]}}, add_reg[2], 2'b0}
+ {{7{add_reg[2][12]}}, add_reg[2], 1'b0}} ;//*110_1101110
assign mult_reg[3] = {{{1{add_reg[3][12]}}, add_reg[3], 7'b0}
+ {{3{add_reg[3][12]}}, add_reg[3], 5'b0}
+ {{4{add_reg[3][12]}}, add_reg[3], 4'b0}
+ {{6{add_reg[3][12]}}, add_reg[3], 2'b0}
+ {{7{add_reg[3][12]}}, add_reg[3], 1'b0}
+ {{8{add_reg[3][12]}}, add_reg[3]}} ;//*183_10110111
//-------------------------------------------------------
// 卷积运算之后进行求和输出
//-------------------------------------------------------
assign zero_output = mult_reg[0]
+ mult_reg[1]
+ mult_reg[2]
+ mult_reg[3];
endmodule
③pole极点模块书写:
与零点一样,当成一个FIR滤波器进行操作,但是由于这里的系数不再具有对称性,因此,我们使用时就不能再进行相加再乘。不能偷懒就很烦…只能依次进行移位乘法(注意一下,若是这个里面有负数,我们可以先乘这个数的绝对值,在进行取补码的操作,但是一定要保证符号位多一位,不然可能会出现问题,解决方案也很简单,扩充一个符号位就可以了)。根据前文求出来的系数: 64 148 194 153 79 26 5 0,按照原理中的要求,从第一位开始(这里要舍弃第0位),依次进行与波形数据的乘法,如果有疑问,可以再在前文看看公式。
`timescale 1ns/1ps
//-------------------------------------------------------
// IIR滤波器极点系数模块
//-------------------------------------------------------
//-------------------------------------------------------
// 引脚模块定义
//-------------------------------------------------------
module pole_module(
input clk ,//时钟信号
input rst_n ,//复位信号,使能高电平有效
input signed [11:0] pole_input ,//输入信号
output signed [25:0] pole_output //输出信号
);
//-------------------------------------------------------
// 还是pole模块由于是N-1位,因此这个地方我们只需要8-1个寄存器进行存储
// 因此该处定义的是7位的reg信号,信号的带宽与输入的带宽相同,也就是即
// 12位。复位信号清零;clk信号进行移位;
//-------------------------------------------------------
reg signed [11:0] data_reg [6:0];
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0) begin //复位信号,系统进行置零操作,复位信号低电平有效
data_reg[0] <= 12'd0 ;
data_reg[1] <= 12'd0 ;
data_reg[2] <= 12'd0 ;
data_reg[3] <= 12'd0 ;
data_reg[4] <= 12'd0 ;
data_reg[5] <= 12'd0 ;
data_reg[6] <= 12'd0 ;
end
else begin //普通时钟周期信号,依次进行移位操作,将新的数据进行送入
data_reg[6] <= data_reg[5] ;
data_reg[5] <= data_reg[4] ;
data_reg[4] <= data_reg[3] ;
data_reg[3] <= data_reg[2] ;
data_reg[2] <= data_reg[1] ;
data_reg[1] <= data_reg[0] ;
data_reg[0] <= pole_input ;
end
//-------------------------------------------------------
// 滤波器的系数,量化之后,量化2^6
// 使用组合逻辑进行乘法的操作。
//-------------------------------------------------------
wire [25:0] mult_pole [6:0];
assign mult_pole[0] = {{{7{data_reg[1][11]}}, data_reg[1], 7'b0}
+ {{10{data_reg[1][11]}}, data_reg[1], 4'b0}
+ {{12{data_reg[1][11]}}, data_reg[1], 2'b0}};//*148_10010100
assign mult_pole[1] = {{{7{data_reg[2][11]}}, data_reg[2], 7'b0}
+ {{8{data_reg[2][11]}}, data_reg[2], 6'b0}
+ {{13{data_reg[2][11]}}, data_reg[2], 1'b0}};//*194_11000010
assign mult_pole[2] = {{{7{data_reg[3][11]}}, data_reg[3], 7'b0}
+ {{10{data_reg[3][11]}}, data_reg[3], 4'b0}
+ {{11{data_reg[3][11]}}, data_reg[3], 3'b0}
+ {{14{data_reg[3][11]}}, data_reg[3]}};//*153_10011001
assign mult_pole[3] = {{{8{data_reg[4][11]}}, data_reg[4], 6'b0}
+ {{11{data_reg[4][11]}}, data_reg[4], 3'b0}
+ {{12{data_reg[4][11]}}, data_reg[4], 2'b0}
+ {{13{data_reg[4][11]}}, data_reg[4], 1'b0}
+ {{14{data_reg[4][11]}}, data_reg[4]}};//*79_1001111
assign mult_pole[4] = {{{10{data_reg[5][11]}}, data_reg[5], 4'b0}
+ {{11{data_reg[5][11]}}, data_reg[5], 3'b0}
+ {{13{data_reg[5][11]}}, data_reg[5], 1'b0}};//*26_11010
assign mult_pole[5] = {{{12{data_reg[6][11]}}, data_reg[6], 2'b0}
+ {{14{data_reg[6][11]}}, data_reg[6]}};//*5_101
assign mult_pole[6] = 26'b0;
//-------------------------------------------------------
// 求卷积和;将前面所有的乘积进行求和,并且输出置Yout
// 将前面7个已经完成乘法的寄存器中的数据进行加法运算
//-------------------------------------------------------
assign pole_output = {mult_pole[0] +
mult_pole[1] +
mult_pole[2] +
mult_pole[3] +
mult_pole[4] +
mult_pole[5] +
mult_pole[6]};
endmodule
④顶层模块书写:
顶层模块的目的就是要构造成为一个IIR,前文相当于可以看成是两个FIR模块。因此,程序中调用前面的两个子模块module_zero以及module_pole。然后将零极点的输出值进行相减,除法的操作,最终输出我们滤波后的波形数据。输入波形的数据位数位12位,输出波形数据的数据位也为12位。
`timescale 1ns/1ps
//-------------------------------------------------------
// IIR滤波器顶层模块
//-------------------------------------------------------
module IIR_top(
input clk ,
input rst_n ,
input signed [11:0] IIR_input ,
output reg signed [11:0] IIR_output
);
//-------------------------------------------------------
// 调用零点模块
//-------------------------------------------------------
wire signed [20:0] zero_out ;
wire signed [11:0] pole_in ;
wire signed [25:0] feedback ;
//-------------------------------------------------------
// 根据时钟信号,来控制波形的输入值wave_in;
//-------------------------------------------------------
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)begin
IIR_output <= 12'b0; //复位信号,下降沿有效
end
else begin
IIR_output <= pole_in; //正常周期信号clk状态下依次进行输入波形数据
end
zero_module zero
(
.rst_n (rst_n) ,
.clk (clk) ,
.zero_input (IIR_input) ,
.zero_output(zero_out)
);
//-------------------------------------------------------
// 调用极点模块
//-------------------------------------------------------
pole_module pole
(
.clk (clk) ,
.rst_n (rst_n) ,
.pole_input (pole_in) ,
.pole_output(feedback)
);
//-------------------------------------------------------
// 顶层模块进行求差值以及乘积的工作,构成IIR模块
//-------------------------------------------------------
wire signed [25:0] sub_module;
wire signed [25:0] div_module;
assign sub_module = {{5{zero_out[20]}}, zero_out} - feedback ;//零点的输出与极点的输出进行相减
assign div_module = {{9{sub_module[25]}}, sub_module[25:9]} ;//求出的差值进行移位除法
assign pole_in = div_module[11:0] ;//输入信号的位数是12位。输出信号的位数也是12位
endmodule
⑤testbench测试文件的书写:
//~ `New testbench
`timescale 1ns / 1ps
module tb_IIR_top;
// IIR_top Parameters
parameter PERIOD = 10;
// IIR_top Inputs
reg clk ;
reg rst_n ;
reg [11:0] wave_in ;
// IIR_top Outputs
wire [11:0] wave_out ;
integer i ;
initial begin
clk = 0 ;
rst_n = 0 ;
wave_in = 0 ;
i = 0 ;
end
initial
begin
forever #(PERIOD/2) clk=~clk;
end
initial
begin
#(PERIOD*2) rst_n = 1;
end
parameter DATA_NUM = 32'd10000;
reg [11:0] data_men[DATA_NUM:1];
initial begin
$readmemb("绝对路径/tb/sin.txt",data_men); //注意斜杠的方向,不能反<<<<<<<
end
always @(posedge clk or negedge rst_n) begin //每1个clk信号来临后,进行一次数据输入,将新的一个波形数据输入Din中。
wave_in <= data_men[i];
i <= i+1;
end
IIR_top u_IIR_top (
.clk ( clk ),
.rst_n ( rst_n ),
.IIR_input ( wave_in [11:0] ),
.IIR_output ( wave_out [11:0] )
);
endmodule
⑥Matlab生成待滤波的wave数据:
所有准备工作完成之后,就应该准备输入信号,因为这里是做的一个低通滤波器,因此,我们的输入信号中,有一个较高频率的信号,一个在通带里面的信号。根据采样频率为3000Hz,那么据采样定理最多可以采到的波形频率为1500Hz,这样的话,便设置一个杂波信号的频率为1400Hz的正弦波,源信号为500Hz,Fs采样频率为3000Hz,量化位数与Verilog代码相匹配,采用的是12位的量化。生成一组十进制采样的正弦信号以及一个二进制化的正弦信号。Matlab波形生成程序的名称命名为IIR_IN_DES.m
f1 = 500;
f2 = 1400;
Fs = 3000;
N = 12;
End = 100;
t = 0:1/Fs:End-1/Fs;
c1 = 2*pi*f1*t;
c2 = 2*pi*f2*t;
s1 = sin(c1);
s2 = 0.5*sin(c2);
s = s1+s2;
s = s/max(abs(s));
Q_s = round(s*(2^(N-1) - 1));
fid = fopen('..\tb\sin_ten.txt','w');
fprintf(fid, '%12d\r\n',Q_s);
fprintf(fid, ';');
fclose(fid);
fid = fopen('..\tb\sin.txt','w');
for k=1:length(Q_s)%二进制变换
B_s=dec2bin(Q_s(k)+((Q_s(k))<0)*2^N,N);
for j=1:N
if B_s(j)=='1'
tb=1;
else
tb=0;
end
fprintf(fid,'%d',tb);
end
fprintf(fid,'\r\n');
end
fprintf(fid,';');
fclose(fid);
⑦使用Modelsim仿真波形
书写sim.do文件,如下所示,同时加入modelsim_run.bat批处理文件,进行一键执行仿真。do文件如下所示。
vlib work
vmap work work
vlog -work work "../src/*.v"
vlog -work work "../tb/*.v"
vsim -voptargs=+acc work.tb_IIR_top
add wave -position insertpoint \
sim:/tb_IIR_top/*
do wave.do
run 10000ns
点击批处理文件进行一键执行,仿真后的波形如下所示