FPGA学习记录(7)<巴特沃斯低通IIR滤波器FPGA实现>

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实现原理分析

  1. 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=0Mbizi/(1l=1Nalzl)

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(ni)b(i)+l=1Ny(nl)a(l)

  1. 这里进行设计一个直接Ⅱ型的IIR滤波器 。还是以设计一个低通滤波器为目的,选择巴特沃斯滤波器为设计的主要目的。下午的为结构显示图,右边为零点部分,左边为极点部分。相对于直接Ⅰ型的滤波器,消耗的资源相对较小。在这里插入图片描述根据我们上述的理论分析,其实IIR实质之上就是两个FIR进行一个叠加。零点部分的模块进行一个N位的移位,极点部分的模块进行一个N-1位的移位。
  2. 由上述的理论分析可以知道,由于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

点击批处理文件进行一键执行,仿真后的波形如下所示
在这里插入图片描述

  • 9
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值