FPGA学习记录(10)<滤波器的定点定位以及Beyond compare的使用>

本文详细介绍了FPGA滤波器设计中的定点定位概念及其重要性,解释了如何使用BeyondCompare工具进行数据对比验证。通过8bit并行FIR滤波器与MATLAB仿真数据的对比,以及巴特沃斯滤波器实现的IIR滤波器验证,展示了定点运算和滤波效果的一致性。
摘要由CSDN通过智能技术生成

Written by @hzj
//JinXing Project
#2021.11.21 V1.0

1、滤波器的定点定位以及Beyond compare的使用

(1)为什么要进行定点定位?

1.定点:定点的存在,是由于FPGA内部是二进制的操作的原因。而在实际运算中出现的小数点(浮点类型的数据),无法使用二进制数值进行精确表示。因此,要根据项目的实际需要,定下浮点数的保留位,根据量化误差设定保存浮点数据的数据。当然,如果使用二进制数的位数越多,越能够更加精确的表达一个浮点数;相反的,如果使用二进制数的位数较少,就不能够精确的表达一个浮点数。当然,这个精确程度是建立在一个需求之上,也不能过分要求精确,反而浪费了寄存器。下面简单说一个关于定点的例子:
—>对12.918这个浮点数进行无损定点化,需要的最小位宽是多少位;若是选用的是11位的话,量化误差又是多少?
①整数部分:12的整数,需要使用的数位大小位4位,也就是及2^4 = 16>12,因此整数部分需要用4位小数位来表示。
②小数部分:使用的是12位的二进制数位数来表示,由于整数部分的位数宽度位为4位,因此,小数部分的位数宽度位为8位,因此得到表示0.918小数位的数值大小位为:int(0.918 * 256)/256 = 235/256 = 0.91796875。也就是说,如果使用12位的二进制数来表示这个值。那么真实使用这个二进制数真实表示这个数值的大小为0.91796875,与实际的值0.918相差0.00003125,由于需要进行无损化的定点位,因此,考虑的有效位位数为小数点后的三位,而相差的位数在前三位的大小为0,因此可以称之为无损化定点。-------这个时候,要是我们使用的是十一位的二进制数来进行表示,会是什么样的呢?如果使用是11位,前者整数位用了4位。因此小数位这里使用的是7位二进制数进行表示,而:int(0.918 * 128)/128 = 118/128 = 0.921875,与实际的值0.918相差0.003875,这里可以清楚看出来,前三位小数并不完全是0,因此,这个地方我们断定使用11位的二进制数,不能进行无损量化,量化的误差为0.0039。
其实,浮点数的数用二进制数进行表示,实质上也就是通过二进制数来表示数的大小,因此,可以将浮点数进行同比例扩大,将需要进行保留的小数位扩大成整数,同时,拓展到需要进行保留的小数位数的时候,截取小数点后面的小数位,然后原数就完全当成了一个整数进行表示,也相当于实现了定点操作,同时也不需要对浮点数考虑过多,更为简便。其实两者的表示原理也是一样的
2.定位:这里指的定位,是指的是进行运算时候的寄存器位数。对于一个运算操作来说,如果预留的寄存器位数过多,那么就会出现器件浪费的现象,因此需要进行”抠“位。这个地方总结出了两个运算的原则:
①当m位的二进制数与n位的二进制数进行加法运算的时候,运算结果需要使用max(m, n)+1位的二进制数位的寄存器进行数据保存。
②当m位的二进制数与n位的二进制数进行乘法运算的时候,运算结果需要使用m+n位的二进制数位的寄存器进行数据保存。

(2)Beyond compare的使用

FPGA中设计的仿真,往往需要与matlab中的值一一对应,才能验证FPGA的仿真结果正确性。因此,需要在matlab中模拟FPGA的数据运算操作,然后通过FPGA中仿真出来的数据与FPGA仿真数据一一比对,来判断是否正确。采用的Beyond Compare就是一个数据比对软件,通过导入两个txt文档中的数据,将有差异的数据标红显示,可以明显看出有哪些的数据是一样的,哪些数据是不一样的。

2、8bit并行运算FIR滤波器与matlab仿真数据的Beyond Compare对比比较

8bit并行输入的FIR滤波器在前文的工作中已经实现了。因此需要对整个模块进行matlab仿真验证,验证滤波器是否功能实现功能。验证的数据量也选择的是百万级别的数据。
相关的代码如下所示,FPGA中8bit的并行运算FIR滤波器:
主程序

`timescale 1ns / 1ps
/*并行移位模块,根据clk信号依次打入8个待滤波信号,每个周期也依次出来8个滤波后信号,该模块利用了8个卷积模块进行操作,相当于在以前8个周期完成的滤波内容
现在在1个周期内就可以完成。*/
module fir_low8(
    input clk                            , //输入的系统时钟,时钟驱动
    input rst_n                          , //定义复位信号,供里面所有的参量进行复位,复位信号使用的是低电平有效  
    input signed [15:0] data_in_1        , //一个clk信号,输入第一位数据位
    input signed [15:0] data_in_2        , //一个clk信号,输入第二位数据位
    input signed [15:0] data_in_3        , //一个clk信号,输入第三位数据位
    input signed [15:0] data_in_4        , //一个clk信号,输入第四位数据位 
    input signed [15:0] data_in_5        , //一个clk信号,输入第五位数据位
    input signed [15:0] data_in_6        , //一个clk信号,输入第六位数据位
    input signed [15:0] data_in_7        , //一个clk信号,输入第七位数据位
    input signed [15:0] data_in_8        , //一个clk信号,输入第八位数据位

    output reg signed [15:0] data_out_1    , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
    output reg signed [15:0] data_out_2    , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
    output reg signed [15:0] data_out_3    , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
    output reg signed [15:0] data_out_4    , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
    output reg signed [15:0] data_out_5    , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
    output reg signed [15:0] data_out_6    , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
    output reg signed [15:0] data_out_7    , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
    output reg signed [15:0] data_out_8      //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
);

parameter U_DLY = 1;//时序仿真信号,加上U_DLY模拟信号的电路中传输的延迟

/*定义16个数据寄存器,用来存储待滤波信号的数据,每一个clk周期,输入8个待滤波数据进去,依次进入x8、x7、x6...x1这8个寄存器,也就是说
低8位的寄存器保存的是最新来的数据信息,而高8位的寄存器保存的是上一个clk周期的数据信息*/
reg signed [15:0] x1;   
reg signed [15:0] x2;
reg signed [15:0] x3;
reg signed [15:0] x4;
reg signed [15:0] x5;
reg signed [15:0] x6;
reg signed [15:0] x7;
reg signed [15:0] x8;
reg signed [15:0] x9;
reg signed [15:0] x10;
reg signed [15:0] x11;
reg signed [15:0] x12;
reg signed [15:0] x13;
reg signed [15:0] x14;
reg signed [15:0] x15;
reg signed [15:0] x16;


/* 根据时钟信号进行切换寄存器中的数据值。本次移位使用的是系统时钟,采用的下降沿rst_n进行复位,低有效位生效
先将低八位的数据,输入到高8位寄存器进行存储(x1~x8的寄存器信息,放入到x9~x16寄存器中去),在将本次周期打入的8位新的数据
位,依照先后的顺序,打入至x8~x1寄存器中去。*/
always @ (posedge clk or negedge rst_n) begin
    if (rst_n == 1'b0) begin
        x1 <= #U_DLY 16'b0    ;
        x2 <= #U_DLY 16'b0    ;
        x3 <= #U_DLY 16'b0    ;
        x4 <= #U_DLY 16'b0    ;
        x5 <= #U_DLY 16'b0    ;
        x6 <= #U_DLY 16'b0    ;
        x7 <= #U_DLY 16'b0    ;
        x8 <= #U_DLY 16'b0    ;
        x9 <= #U_DLY 16'b0    ;
        x10 <= #U_DLY 16'b0   ;
        x11 <= #U_DLY 16'b0   ;
        x12 <= #U_DLY 16'b0   ;
        x13 <= #U_DLY 16'b0   ;
        x14 <= #U_DLY 16'b0   ;
        x15 <= #U_DLY 16'b0   ;
        x16 <= #U_DLY 16'b0   ;
    end
    else begin
        x9  <= #U_DLY x1                  ;
        x10 <= #U_DLY x2                  ;
        x11 <= #U_DLY x3                  ;
        x12 <= #U_DLY x4                  ;
        x13 <= #U_DLY x5                  ;
        x14 <= #U_DLY x6                  ;
        x15 <= #U_DLY x7                  ;
        x16 <= #U_DLY x8                  ;
        x1  <= #U_DLY data_in_8           ;
        x2  <= #U_DLY data_in_7           ;
        x3  <= #U_DLY data_in_6           ;
        x4  <= #U_DLY data_in_5           ;   
        x5  <= #U_DLY data_in_4           ;
        x6  <= #U_DLY data_in_3           ;
        x7  <= #U_DLY data_in_2           ;
        x8  <= #U_DLY data_in_1           ;
    end
end


/* 加法器,根据量化系数的对称性,进行两者的加法,得到三个变量add2_7, add3_6, add4_5,由于要进行8bit的并行运算,因此需要准备8组
加法寄存器,每一组寄存器种有三个加法器,因此总共以下一共定义了3*8=24个寄存器 */
wire signed [16:0] add2_7_1;
wire signed [16:0] add3_6_1;
wire signed [16:0] add4_5_1;
wire signed [16:0] add2_7_2;
wire signed [16:0] add3_6_2;
wire signed [16:0] add4_5_2;
wire signed [16:0] add2_7_3;
wire signed [16:0] add3_6_3;
wire signed [16:0] add4_5_3;
wire signed [16:0] add2_7_4;
wire signed [16:0] add3_6_4;
wire signed [16:0] add4_5_4;
wire signed [16:0] add2_7_5;
wire signed [16:0] add3_6_5;
wire signed [16:0] add4_5_5;
wire signed [16:0] add2_7_6;
wire signed [16:0] add3_6_6;
wire signed [16:0] add4_5_6;
wire signed [16:0] add2_7_7;
wire signed [16:0] add3_6_7;
wire signed [16:0] add4_5_7;
wire signed [16:0] add2_7_8;
wire signed [16:0] add3_6_8;
wire signed [16:0] add4_5_8;

/*卷积输出值寄存器out_temp*/
wire signed [24:0] out_temp_1;
wire signed [24:0] out_temp_2;
wire signed [24:0] out_temp_3;
wire signed [24:0] out_temp_4;
wire signed [24:0] out_temp_5;
wire signed [24:0] out_temp_6;
wire signed [24:0] out_temp_7;
wire signed [24:0] out_temp_8;

/*待加法数存储寄存器,依次切换寄存器中的值,再打入到加法器中进行加法运算*/
reg [15:0] num_x_2_1;
reg [15:0] num_x_3_1;
reg [15:0] num_x_4_1; 
reg [15:0] num_x_5_1;
reg [15:0] num_x_6_1;
reg [15:0] num_x_7_1;

reg [15:0] num_x_2_2;
reg [15:0] num_x_3_2;
reg [15:0] num_x_4_2; 
reg [15:0] num_x_5_2;
reg [15:0] num_x_6_2;
reg [15:0] num_x_7_2;

reg [15:0] num_x_2_3;
reg [15:0] num_x_3_3;
reg [15:0] num_x_4_3; 
reg [15:0] num_x_5_3;
reg [15:0] num_x_6_3;
reg [15:0] num_x_7_3;

reg [15:0] num_x_2_4;
reg [15:0] num_x_3_4;
reg [15:0] num_x_4_4; 
reg [15:0] num_x_5_4;
reg [15:0] num_x_6_4;
reg [15:0] num_x_7_4;

reg [15:0] num_x_2_5;
reg [15:0] num_x_3_5;
reg [15:0] num_x_4_5; 
reg [15:0] num_x_5_5;
reg [15:0] num_x_6_5;
reg [15:0] num_x_7_5;

reg [15:0] num_x_2_6;
reg [15:0] num_x_3_6;
reg [15:0] num_x_4_6; 
reg [15:0] num_x_5_6;
reg [15:0] num_x_6_6;
reg [15:0] num_x_7_6;

reg [15:0] num_x_2_7;
reg [15:0] num_x_3_7;
reg [15:0] num_x_4_7; 
reg [15:0] num_x_5_7;
reg [15:0] num_x_6_7;
reg [15:0] num_x_7_7;

reg [15:0] num_x_2_8;
reg [15:0] num_x_3_8;
reg [15:0] num_x_4_8; 
reg [15:0] num_x_5_8;
reg [15:0] num_x_6_8;
reg [15:0] num_x_7_8;

/*开始进行8bit卷积的工作*/

/*第一个bit的数据*/
assign add2_7_1 = {num_x_2_1[15] ,num_x_2_1} + {num_x_7_1[15], num_x_7_1};
assign add3_6_1 = {num_x_3_1[15], num_x_3_1} + {num_x_6_1[15], num_x_6_1};
assign add4_5_1 = {num_x_4_1[15], num_x_4_1} + {num_x_5_1[15], num_x_5_1};
//例化调用模块,卷积层_1
mul mul_conv_1
(
    .add_2_7 (add2_7_1[16:0])   ,
    .add_3_6 (add3_6_1[16:0])   ,
    .add_4_5 (add4_5_1[16:0])   , 
    .mul_out (out_temp_1 [24:0])
);



/*第二个bit的数据*/
assign add2_7_2 = {num_x_2_2[15] ,num_x_2_2} + {num_x_7_2[15], num_x_7_2};
assign add3_6_2 = {num_x_3_2[15], num_x_3_2} + {num_x_6_2[15], num_x_6_2};
assign add4_5_2 = {num_x_4_2[15], num_x_4_2} + {num_x_5_2[15], num_x_5_2};
//例化调用模块,卷积层_2
mul mul_conv_2
(
    .add_2_7 (add2_7_2[16:0])   ,
    .add_3_6 (add3_6_2[16:0])   ,
    .add_4_5 (add4_5_2[16:0])   , 
    .mul_out (out_temp_2 [24:0])
);


/*第三个bit的数据*/
assign add2_7_3 = {num_x_2_3[15] ,num_x_2_3} + {num_x_7_3[15], num_x_7_3};
assign add3_6_3 = {num_x_3_3[15], num_x_3_3} + {num_x_6_3[15], num_x_6_3};
assign add4_5_3 = {num_x_4_3[15], num_x_4_3} + {num_x_5_3[15], num_x_5_3};

//例化调用模块,卷积层_3
mul mul_conv_3
(
    .add_2_7 (add2_7_3[16:0])   ,
    .add_3_6 (add3_6_3[16:0])   ,
    .add_4_5 (add4_5_3[16:0])   , 
    .mul_out (out_temp_3 [24:0])
);


/*第四个bit的数据*/
assign add2_7_4 = {num_x_2_4[15] ,num_x_2_4} + {num_x_7_4[15], num_x_7_4};
assign add3_6_4 = {num_x_3_4[15], num_x_3_4} + {num_x_6_4[15], num_x_6_4};
assign add4_5_4 = {num_x_4_4[15], num_x_4_4} + {num_x_5_4[15], num_x_5_4};
//例化调用模块,卷积层_4
mul mul_conv_4
(
    .add_2_7 (add2_7_4[16:0])   ,
    .add_3_6 (add3_6_4[16:0])   ,
    .add_4_5 (add4_5_4[16:0])   , 
    .mul_out (out_temp_4 [24:0])
);


/*第五个bit的数据*/
assign add2_7_5 = {num_x_2_5[15] ,num_x_2_5} + {num_x_7_5[15], num_x_7_5};
assign add3_6_5 = {num_x_3_5[15], num_x_3_5} + {num_x_6_5[15], num_x_6_5};
assign add4_5_5 = {num_x_4_5[15], num_x_4_5} + {num_x_5_5[15], num_x_5_5};
//例化调用模块,卷积层_5
mul mul_conv_5
(
    .add_2_7 (add2_7_5[16:0])   ,
    .add_3_6 (add3_6_5[16:0])   ,
    .add_4_5 (add4_5_5[16:0])   , 
    .mul_out (out_temp_5 [24:0])
);

/*第六个bit的数据*/
assign add2_7_6 = {num_x_2_6[15] ,num_x_2_6} + {num_x_7_6[15], num_x_7_6};
assign add3_6_6 = {num_x_3_6[15], num_x_3_6} + {num_x_6_6[15], num_x_6_6};
assign add4_5_6 = {num_x_4_6[15], num_x_4_6} + {num_x_5_6[15], num_x_5_6};
//例化调用模块,卷积层_6
mul mul_conv_6
(
    .add_2_7 (add2_7_6[16:0])   ,
    .add_3_6 (add3_6_6[16:0])   ,
    .add_4_5 (add4_5_6[16:0])   , 
    .mul_out (out_temp_6 [24:0])
);

/*第七个bit的数据*/
assign add2_7_7 = {num_x_2_7[15] ,num_x_2_7} + {num_x_7_7[15], num_x_7_7};
assign add3_6_7 = {num_x_3_7[15], num_x_3_7} + {num_x_6_7[15], num_x_6_7};
assign add4_5_7 = {num_x_4_7[15], num_x_4_7} + {num_x_5_7[15], num_x_5_7};
//例化调用模块,卷积层_7
mul mul_conv_7
(
    .add_2_7 (add2_7_7[16:0])   ,
    .add_3_6 (add3_6_7[16:0])   ,
    .add_4_5 (add4_5_7[16:0])   , 
    .mul_out (out_temp_7 [24:0])
);

/*第八个bit的数据*/
assign add2_7_8 = {num_x_2_8[15] ,num_x_2_8} + {num_x_7_8[15], num_x_7_8};
assign add3_6_8 = {num_x_3_8[15], num_x_3_8} + {num_x_6_8[15], num_x_6_8};
assign add4_5_8 = {num_x_4_8[15], num_x_4_8} + {num_x_5_8[15], num_x_5_8};
//例化调用模块,卷积层_8
mul mul_conv_8
(
    .add_2_7 (add2_7_8[16:0])   ,
    .add_3_6 (add3_6_8[16:0])   ,
    .add_4_5 (add4_5_8[16:0])   , 
    .mul_out (out_temp_8 [24:0])
);




/*根据clk信号的到来,每次重新打入8组大小位6位(原本是8位,但是由于量化系数的第一位以及第八位的值为0,因此给予省略)。
仿照串行输入的数据,首先输入的是9~16(8~15),接着依次是8~15(7~14)、7~14(6~13)...3~8(2~9)*/
always @ (posedge clk or negedge rst_n) begin
    if (rst_n == 1'b0) begin
        num_x_2_1 <= #U_DLY 0        ;
        num_x_3_1 <= #U_DLY 0        ;
        num_x_4_1 <= #U_DLY 0        ;
        num_x_5_1 <= #U_DLY 0        ;    
        num_x_6_1 <= #U_DLY 0        ;
        num_x_7_1 <= #U_DLY 0        ;

        num_x_2_2 <= #U_DLY 0        ;
        num_x_3_2 <= #U_DLY 0        ;
        num_x_4_2 <= #U_DLY 0        ;
        num_x_5_2 <= #U_DLY 0        ;    
        num_x_6_2 <= #U_DLY 0        ;
        num_x_7_2 <= #U_DLY 0        ;

        num_x_2_3 <= #U_DLY 0        ;
        num_x_3_3 <= #U_DLY 0        ;
        num_x_4_3 <= #U_DLY 0        ;
        num_x_5_3 <= #U_DLY 0        ;    
        num_x_6_3 <= #U_DLY 0        ;
        num_x_7_3 <= #U_DLY 0        ;

        num_x_2_4 <= #U_DLY 0        ;
        num_x_3_4 <= #U_DLY 0        ;
        num_x_4_4 <= #U_DLY 0        ;
        num_x_5_4 <= #U_DLY 0        ;    
        num_x_6_4 <= #U_DLY 0        ;
        num_x_7_4 <= #U_DLY 0        ;

        num_x_2_5 <= #U_DLY 0        ;
        num_x_3_5 <= #U_DLY 0        ;
        num_x_4_5 <= #U_DLY 0        ;
        num_x_5_5 <= #U_DLY 0        ;    
        num_x_6_5 <= #U_DLY 0        ;
        num_x_7_5 <= #U_DLY 0        ;

        num_x_2_6 <= #U_DLY 0        ;
        num_x_3_6 <= #U_DLY 0        ;
        num_x_4_6 <= #U_DLY 0        ;
        num_x_5_6 <= #U_DLY 0        ;    
        num_x_6_6 <= #U_DLY 0        ;
        num_x_7_6 <= #U_DLY 0        ;

        num_x_2_7 <= #U_DLY 0        ;
        num_x_3_7 <= #U_DLY 0        ;
        num_x_4_7 <= #U_DLY 0        ;
        num_x_5_7 <= #U_DLY 0        ;    
        num_x_6_7 <= #U_DLY 0        ;
        num_x_7_7 <= #U_DLY 0        ;

        num_x_2_8 <= #U_DLY 0        ;
        num_x_3_8 <= #U_DLY 0        ;
        num_x_4_8 <= #U_DLY 0        ;
        num_x_5_8 <= #U_DLY 0        ;    
        num_x_6_8 <= #U_DLY 0        ;
        num_x_7_8 <= #U_DLY 0        ;
    end
    else begin
        num_x_2_1 <= #U_DLY x10      ;
        num_x_3_1 <= #U_DLY x11      ;
        num_x_4_1 <= #U_DLY x12      ;
        num_x_5_1 <= #U_DLY x13      ;    
        num_x_6_1 <= #U_DLY x14      ;
        num_x_7_1 <= #U_DLY x15      ;

        num_x_2_2 <= #U_DLY x9       ;
        num_x_3_2 <= #U_DLY x10      ;
        num_x_4_2 <= #U_DLY x11      ;
        num_x_5_2 <= #U_DLY x12      ;    
        num_x_6_2 <= #U_DLY x13      ;
        num_x_7_2 <= #U_DLY x14      ;

        num_x_2_3 <= #U_DLY x8       ;
        num_x_3_3 <= #U_DLY x9       ;
        num_x_4_3 <= #U_DLY x10      ;
        num_x_5_3 <= #U_DLY x11      ;    
        num_x_6_3 <= #U_DLY x12      ;
        num_x_7_3 <= #U_DLY x13      ;
        
        num_x_2_4 <= #U_DLY x7       ;
        num_x_3_4 <= #U_DLY x8       ;
        num_x_4_4 <= #U_DLY x9       ;
        num_x_5_4 <= #U_DLY x10      ;    
        num_x_6_4 <= #U_DLY x11      ;
        num_x_7_4 <= #U_DLY x12      ;
    
        num_x_2_5 <= #U_DLY x6       ;
        num_x_3_5 <= #U_DLY x7       ;
        num_x_4_5 <= #U_DLY x8       ;
        num_x_5_5 <= #U_DLY x9       ;    
        num_x_6_5 <= #U_DLY x10      ;
        num_x_7_5 <= #U_DLY x11      ;
            
        num_x_2_6 <= #U_DLY x5       ;
        num_x_3_6 <= #U_DLY x6       ;
        num_x_4_6 <= #U_DLY x7       ;
        num_x_5_6 <= #U_DLY x8       ;    
        num_x_6_6 <= #U_DLY x9       ;
        num_x_7_6 <= #U_DLY x10      ;    
    
        num_x_2_7 <= #U_DLY x4       ;
        num_x_3_7 <= #U_DLY x5       ;
        num_x_4_7 <= #U_DLY x6       ;
        num_x_5_7 <= #U_DLY x7       ;    
        num_x_6_7 <= #U_DLY x8       ;
        num_x_7_7 <= #U_DLY x9       ;   
        
        num_x_2_8 <= #U_DLY x3       ;
        num_x_3_8 <= #U_DLY x4       ;
        num_x_4_8 <= #U_DLY x5       ;
        num_x_5_8 <= #U_DLY x6       ;  
        num_x_6_8 <= #U_DLY x7       ;
        num_x_7_8 <= #U_DLY x8       ;
    end
end

/*根据每一次的clk上升沿信号,进行卷积滤波的数据输出,输出的数据都需要进行移位的操作,8位的信号都要截取掉低9位的数据,以达到同时缩小的目的*/
always @ (posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        data_out_1 <= #U_DLY 0                     ;
        data_out_2 <= #U_DLY 0                     ;
        data_out_3 <= #U_DLY 0                     ;
        data_out_4 <= #U_DLY 0                     ;
        data_out_5 <= #U_DLY 0                     ;
        data_out_6 <= #U_DLY 0                     ;
        data_out_7 <= #U_DLY 0                     ;
        data_out_8 <= #U_DLY 0                     ;  
    end
    else begin
        data_out_1 <= #U_DLY out_temp_1[24:9]       ;
        data_out_2 <= #U_DLY out_temp_2[24:9]       ;
        data_out_3 <= #U_DLY out_temp_3[24:9]       ;
        data_out_4 <= #U_DLY out_temp_4[24:9]       ;
        data_out_5 <= #U_DLY out_temp_5[24:9]       ;
        data_out_6 <= #U_DLY out_temp_6[24:9]       ;
        data_out_7 <= #U_DLY out_temp_7[24:9]       ;
        data_out_8 <= #U_DLY out_temp_8[24:9]       ;
    end
end
endmodule

卷积例化模块程序


module  mul(
    input [16:0] add_2_7       ,
    input [16:0] add_3_6       ,
    input [16:0] add_4_5       ,

    output signed [24:0] mul_out 
);

wire [23:0] mul_2_d;//绝对值乘积后的值
wire [23:0] mul_2;//乘积后取补码后的结果
wire [23:0] mul_3;
wire [23:0] mul_4;

/*进行三个乘法进行相加,完成整个卷积运算,对于mul_1,由于量化系数的大小为0,因此这个地方给予省略*/
assign mul_2_d = {{6{add_2_7[16]}}, add_2_7, 1'b0}            ;//11111110,这个地方先进行绝对值相乘,再进行取反的操作
assign mul_2   = (~mul_2_d) + 1'b1                            ;//取反进行取反加一

assign mul_3   = {{{7{add_3_6[16]}}, add_3_6} 
                    + {{6{add_3_6[16]}}, add_3_6, 1'b0}
                    + {{3{add_3_6[16]}}, add_3_6, 4'b0}}       ;//1 0011

assign mul_4   = {{{3{add_4_5[16]}}, add_4_5, 4'b0}
                    + {{2{add_4_5[16]}}, add_4_5, 5'b0}
                    + {{1{add_4_5[16]}}, add_4_5, 6'b0}}        ;//111 0000

/*进行加法运算,将上述的三个乘法进行相加,完成整个卷积运算*/
assign mul_out = {mul_2[23], mul_2}
                 + {mul_3[23], mul_3}
                 + {mul_4[23], mul_4}                         ;  

endmodule

测试tb文件程序

`timescale  1ns / 1ps

module tb_fir_low8;

// FIR_low8 Parameters
parameter PERIOD  = 10;


// FIR_low8 Inputs
reg   clk                   ;
reg   clk_sig               ;
reg   rst_n                 ;
reg   signed [15:0]  data_in_1       ;
reg   signed [15:0]  data_in_2       ;
reg   signed [15:0]  data_in_3       ;
reg   signed [15:0]  data_in_4       ;
reg   signed [15:0]  data_in_5       ;
reg   signed [15:0]  data_in_6       ;
reg   signed [15:0]  data_in_7       ;
reg   signed [15:0]  data_in_8       ;
// FIR_low8 Outputs
wire  signed [15:0]  data_out_1      ;
wire  signed [15:0]  data_out_2      ;
wire  signed [15:0]  data_out_3      ;
wire  signed [15:0]  data_out_4      ;

wire  signed [15:0]  data_out_5      ;
wire  signed [15:0]  data_out_6      ;
wire  signed [15:0]  data_out_7      ;
wire  signed [15:0]  data_out_8      ;


integer i;
reg flag;

initial
begin
    clk                          = 0 ;
    clk_sig                      = 0 ;
    rst_n                        = 0 ;
    data_in_1                    = 0 ;
    data_in_2                    = 0 ;                    
    data_in_3                    = 0 ;
    data_in_4                    = 0 ;
    data_in_5                    = 0 ;
    data_in_6                    = 0 ;
    data_in_7                    = 0 ;
    data_in_8                    = 0 ;
    i                            = 1 ; //此处是从1开始计数,也就是说data_men中的第一个数的下标为[1],而非[0]
end

initial
begin
    forever #(PERIOD/2)  clk =~clk; //clk的周期
end


initial
begin
    #(PERIOD*2) rst_n  =  1;  //复位信号
end

initial
begin
    flag = 2'b0;
    #(PERIOD*4) flag <= 1'b1; //
end

parameter data_num = 32'd1000000;
reg [15:0] data_men[data_num:1];


initial begin
    $readmemb("C:/Users/64441/Desktop/8bit_beyondcompare/8bit/tb/sin.txt",data_men);   //注意斜杠的方向,不能反<<<<<<<
end

always@(posedge clk)begin
    if(flag == 1'b1)
    begin //每1个clk_sig信号来临后,进行一次数据输入,将新的一个波形数据输入data_in中。
        data_in_1 <= data_men[i];
        data_in_2 <= data_men[i+1];
        data_in_3 <= data_men[i+2];
        data_in_4 <= data_men[i+3];
        data_in_5 <= data_men[i+4];
        data_in_6 <= data_men[i+5];
        data_in_7 <= data_men[i+6];
        data_in_8 <= data_men[i+7];
        i <= i+8;
    end
end

fir_low8  u_fir_low8 (
    .clk                     ( clk              ),
    .rst_n                   ( rst_n            ),
    .data_in_1               ( data_in_1   [15:0] ),
    .data_in_2               ( data_in_2   [15:0] ),
    .data_in_3               ( data_in_3   [15:0] ),
    .data_in_4               ( data_in_4   [15:0] ),
    .data_in_5               ( data_in_5   [15:0] ),
    .data_in_6               ( data_in_6   [15:0] ),
    .data_in_7               ( data_in_7   [15:0] ),
    .data_in_8               ( data_in_8   [15:0] ),

    .data_out_1                ( data_out_1  [15:0] ),
    .data_out_2                ( data_out_2  [15:0] ),
    .data_out_3                ( data_out_3  [15:0] ),
    .data_out_4                ( data_out_4  [15:0] ),
    .data_out_5                ( data_out_5  [15:0] ),
    .data_out_6                ( data_out_6  [15:0] ),
    .data_out_7                ( data_out_7  [15:0] ),
    .data_out_8                ( data_out_8  [15:0] )
);

integer w_file;
initial w_file = $fopen("C:/Users/64441/Desktop/8bit_beyondcompare/8bit/tb/simulation_out_8bit.txt");
always @(i)
begin
    $fdisplay(w_file,"%b",data_out_1);
    $fdisplay(w_file,"%b",data_out_2);
    $fdisplay(w_file,"%b",data_out_3);
    $fdisplay(w_file,"%b",data_out_4);
    $fdisplay(w_file,"%b",data_out_5);
    $fdisplay(w_file,"%b",data_out_6);
    $fdisplay(w_file,"%b",data_out_7);
    $fdisplay(w_file,"%b",data_out_8);
    if(i > 32'd1000000)    //共写入个数据
        $stop;
end  


endmodule

这里需要对程序做一下说明,因为设计到卷积操作,因此这个里面包含了乘法以及加法,所以,需要对每一个变量的位宽进行精确计算。首先对于输入的数据来说,是8个16位位宽的数据。通过对称性的分析,需要进行对称加法,加法后的寄存器位宽应该是需要17位来保存相加后的值。相加后的值当输入到卷积块mul.v中,进行卷积运算时,需要分析位宽的大小,由于量化系数分别为0,-2,19,112。通过对四位量化系数进行二进制处理,可以得到最大位宽数数为112,二进制表示为111 0000,因此,这个地方进行移位乘法的最高位应该移动6位,又因为是加法运算,因此这个地方还需要多一位符号位,以免加法运算的值溢出。又因为卷积块的输入位宽数位为17位,因此,乘积后的位宽数应该位17+6+1=24,因此乘法运算的输出位数应该是[23:0],而卷积运算的结果是由多个乘法运算的相加所构成,也就是即mul_out = {mul_2[23], mul_2} + {mul_3[23], mul_3} + {mul_4[23], mul_4},因此最终的卷积模块的输出位数应该是[24:0],也就是25位的输出。最终使用[24:9],截取最后的9位,相当于做一个整体的除法缩放。

Matlab测试仿真文件

%将二进制转化为十进制的数据
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
str_in = importdata('..\tb\sin.txt');
str_length = length(str_in); 
str_out = zeros(str_length, 1);
MAX_DATA = 2^15-1;
for number = 1 : str_length
    str_out(number) = bin2dec( num2str(str_in(number)) );
    if(str_out(number) > MAX_DATA)
           str_out(number) = bin2dec( num2str(str_in(number)) ) - 2^16;
    end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

x_in= str_out;

%定义一个输出信号y,代表的是卷积后的数据存储的地方
y=zeros(1,str_length);

%定义一个初值
x_in_2 = 0;
x_in_3 = 0;
x_in_4 = 0;
x_in_5 = 0;
x_in_6 = 0;
x_in_7 = 0;
x_in_8 = 0;

for i=1:1:str_length

    %卷积操作,将原来的寄存器中的数据进行卷积,卷积后的数据放入到y(i)中。
     y(i) = x_in(i)*0 + x_in_2 * (-2) + x_in_3 * 19 +  x_in_4 * 112 + x_in_5 * 112 + x_in_6 * 19 + x_in_7 * (-2) + x_in_8 * 0;

     %依次进行数据的搬移工作
     x_in_8 = x_in_7;
     x_in_7 = x_in_6;
     x_in_6 = x_in_5;
     x_in_5 = x_in_4;
     x_in_4 = x_in_3;    
     x_in_3 = x_in_2;     
     x_in_2 = x_in(i);     
end

output=zeros(1,str_length);
for i = 1:1:str_length
	output(i) = y(i);
end

%将卷积操作完成之后的数据,进行二进制化的操作,转化成二进制后,写入到txt文件中
%由于在FPGA之中,我们进行了截位的操作,因此,这个地方也需要进行截位
%这里采用的截位的方法就是控制写入,通过控制写入的数据位数,来达到截位的目的,
%比如这个地方,原来输出的output里面存储的应该是一个最大数据应该使用25位的二进制数来表示
%但是使用截取的是前16位的数据,因此达到了截位的目的
fid = fopen('..\tb\simulation_bin_matlab.txt','w');
 for k=1:length(output)
    B_s=dec2bin(output(k)+((output(k))<0)*2^25,25);
    for j=1:16
       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);

这个地方需要注意,由于输出的值是一个25位宽的数据,还需要做到FPGA里面的截位除法,因此要实现截位的工作,就直接在保存数据文件中的时候,只保存写入前16位的数据,相当于把末尾9位的数据进行了截除。
Verilog的测试tb文件将数据保存进了txt文档,同时使用matlab也保存进了txt文档,写几行简单的代码将两者的波形进行显示:
测试文件代码

module image(
    input clk       , 
    input rst_n     ,
    input data_8bit   ,
    input data_in_in,
    input data_4m,
    input data_matlab
);

/*8bit运算数据*/
    reg [15:0]  data_out_1;
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)begin
            data_out_1 <= 16'b0;
        end
        else begin
            data_out_1 <= data_8bit; 
        end
    end

/*原始数据*/
    reg [15:0]  data_out_2;
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)begin
            data_out_2 <= 16'b0;
        end
        else begin
            data_out_2 <= data_in_in; 
        end
    end

/*4MFIR串行处理数据*/
    reg [15:0]  data_out_3;
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)begin
            data_out_3 <= 16'b0;
        end
        else begin
            data_out_3 <= data_4m; 
        end
    end

/*matlab仿真数据*/
    reg [15:0]  data_out_4;
    always@(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)begin
            data_out_4 <= 16'b0;
        end
        else begin
            data_out_4 <= data_matlab; 
        end
    end
endmodule

对应的tb文件

`timescale 1ns / 1ps
module test_bench;

// FIR_low8 Parameters
parameter PERIOD  = 10;


// FIR_low8 Inputs
reg   clk                   ;
reg   rst_n                 ;
reg   [15:0] data_in               ;
reg   [15:0] data_in_in               ;
reg   [15:0] data_4m         ;
reg   [15:0] data_matlab;


integer i,j,k;
reg flag;

initial
begin
    i                            = 0 ;
    j                            = 0 ;
    k                            = 0 ;
    clk                          = 0 ;
    rst_n                        = 0 ;
    data_in                      = 0 ;
    data_in_in                   = 0 ;
    data_4m = 0                      ;  
    data_matlab                  = 0 ;
end

initial
begin
    forever #(PERIOD/2)  clk =~clk; //clk的周期
end


initial
begin
    #(PERIOD*2) rst_n  =  1;  //复位信号
end

initial
begin
    flag = 1'b0;
    #(PERIOD*4) flag <= 1'b1; //
end



parameter data_num = 32'd10000;
reg [15:0] data_men_in[data_num:1];
initial begin
    $readmemb("C:/Users/64441/Desktop/8bit_beyondcompare/8bit/tb/sin.txt",data_men_in);   //注意斜杠的方向,不能反<<<<<<<
end

always@(posedge clk)begin
    if(flag == 1'b1)
    begin //每1个clk_sig信号来临后,进行一次数据输入,将新的一个波形数据输入data_in中。
        data_in_in <= data_men_in[i];
        i = i+1;
    end
end


reg [15:0] data_men[data_num:1];
initial begin
    $readmemb("C:/Users/64441/Desktop/8bit_beyondcompare/8bit/tb/simulation_out_8bit.txt",data_men);   //注意斜杠的方向,不能反<<<<<<<
end

always@(posedge clk)begin
    if(flag == 1'b1)
    begin //每1个clk_sig信号来临后,进行一次数据输入,将新的一个波形数据输入data_in中。
        data_in <= data_men[j];
        j <= j+1;
    end
end




reg [15:0] data_men_4M[data_num:1];
initial begin
    $readmemb("C:/Users/64441/Desktop/Verilog_FIR_4M/tb/simulation_out_1bit.txt",data_men_4M);   //注意斜杠的方向,不能反<<<<<<<
end

always@(posedge clk)begin
    if(flag == 1'b1)
    begin //每1个clk_sig信号来临后,进行一次数据输入,将新的一个波形数据输入data_in中。
        data_4m <= data_men_4M[k];
        k <= k+1;
    end
end



reg [15:0] data_men_matlab[data_num:1];
initial begin
    $readmemb("C:/Users/64441/Desktop/Verilog_FIR_4M/tb/simulation_bin_matlab.txt",data_men_matlab);   //注意斜杠的方向,不能反<<<<<<<
end

always@(posedge clk)begin
    if(flag == 1'b1)
    begin //每1个clk_sig信号来临后,进行一次数据输入,将新的一个波形数据输入data_in中。
        data_matlab <= data_men_matlab[k];
        k <= k+1;
    end
end


image  u_image(
    .clk                     ( clk              ),
    .rst_n                   ( rst_n            ),
    .data_8bit                 ( data_in           ),
    .data_in_in              ( data_in_in       ),
    .data_4m                 (data_4m ),
    .data_matlab             (data_matlab)           
);
endmodule

得到的波形显示结果如下所示,从上到下依次是输入波形、8bit并行运算的输出数据、1bit串行输入数据、matlab仿真输出数据的结果。可以清楚看出,波形大致一致,滤波效果良好。
在这里插入图片描述
为了精确验证数据的完备性,准备了一百万组数据进行仿真数据输出对比测试,使用beyond compare进行对比,选择差异选项,滤除无差别的数据,显示的结果如下:
在这里插入图片描述
可以证明,只有首位部分数据不同(由于输入的数据数量不一样,这种情况出现是正常的),因此,两者的滤波数值完全一致。

3、使用巴特沃斯滤波器实现的IIR滤波器与matlab仿真数据的Beyond Compare对比比较

巴特沃斯滤波器实现的IIR滤波器与matlab仿真数据的Beyond Compare对比比较方式与上述的做法相似,但是由于IIR属于无限数据的滤波器,前面的滤波输出结果会一致影响这后面的数据位输出,因此定位操作格外重要。老规矩,先放代码:
顶层模块

`timescale 1ns/1ps
//-------------------------------------------------------
//   IIR滤波器顶层模块
//-------------------------------------------------------
module iir_top(
    input clk                           ,
    input rst_n                         ,
    input signed [11:0] iir_in       ,
    output reg signed [11:0] iir_out
);

parameter U_DLY = 1;//时序仿真信号,加上U_DLY模拟信号的电路中传输的延迟

//-------------------------------------------------------
//   调用零点模块
//-------------------------------------------------------
wire signed [20:0] zero_out  ;
wire signed [11:0] pole_in   ;
wire signed [19:0] feedback  ;

//-------------------------------------------------------
//   根据时钟信号,来控制波形的输入值wave_in;
//-------------------------------------------------------
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)begin
        iir_out <= #U_DLY 12'b0;    //复位信号,下降沿有效
    end
    else begin
        iir_out <= #U_DLY pole_in;  //正常周期信号clk状态下依次进行输入波形数据
    end

zero_module zero
(
    .rst_n (rst_n)             ,
    .clk (clk)                 ,
    .zero_in (iir_in)    ,
    .zero_out (zero_out)
);

//-------------------------------------------------------
//   调用极点模块
//-------------------------------------------------------

pole_module pole
(
    .clk (clk)                  ,
    .rst_n (rst_n)              ,
    .pole_in (pole_in)       ,
    .pole_out(feedback)         
);

//-------------------------------------------------------
//   顶层模块进行求差值以及乘积的工作,构成IIR模块
//-------------------------------------------------------
wire signed [20:0] sub_module;

assign sub_module = {zero_out - {feedback[19], feedback}};//零点的输出与极点的输出进行相减,21位

// assign pole_in = {sub_module[20:9]}  ;//求出的差值进行移位除法
                                      //输入信号的位数是12位。输出信号的位数也是12位

assign pole_in = {sub_module[20:9]}  ;//求出的差值进行移位除法


endmodule 

零点模块

`timescale 1ns/1ps
//-------------------------------------------------------
//   IIR滤波器零点系数模块
//-------------------------------------------------------

//-------------------------------------------------------
//   引脚模块定义
//-------------------------------------------------------
module zero_module(
    input clk                         , //时钟信号clk
    input rst_n                       , //定义一个复位信号,复位信号按照下降沿有效
    input signed [11:0] zero_in    , //零点模块的输入
 
    output signed [20:0] zero_out    //零点模块的输出
);

parameter U_DLY = 1;//时序仿真信号,加上U_DLY模拟信号的电路中传输的延迟

//-------------------------------------------------------
//   IIR滤波器中进行移位的部分,按照clk时钟周期,依次输入波
//   IIR形数据,同时也应该在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] <= #U_DLY 12'b0   ;
        data_reg[1] <= #U_DLY 12'b0   ;
        data_reg[2] <= #U_DLY 12'b0   ;
        data_reg[3] <= #U_DLY 12'b0   ;
        data_reg[4] <= #U_DLY 12'b0   ;
        data_reg[5] <= #U_DLY 12'b0   ;
        data_reg[6] <= #U_DLY 12'b0   ;
    end
    else 
    begin                       //普通时钟周期信号,依次进行移位操作,将新的数据进行送入
        data_reg[6] <= #U_DLY data_reg[5]  ;
        data_reg[5] <= #U_DLY data_reg[4]  ; 
        data_reg[4] <= #U_DLY data_reg[3]  ;
        data_reg[3] <= #U_DLY data_reg[2]  ;
        data_reg[2] <= #U_DLY data_reg[1]  ;
        data_reg[1] <= #U_DLY data_reg[0]  ;
        data_reg[0] <= #U_DLY zero_in   ;
    end
end
//-------------------------------------------------------
//   IIR滤波器的零点由于系数都是对称的,因此可以通过这样子减
//   少器件,先进行加法运算
//-------------------------------------------------------
wire signed [12:0] add_reg [3:0];

assign add_reg[0] = {zero_in[11], zero_in} + {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_out = mult_reg[0] 
                    + mult_reg[1] 
                    + mult_reg[2] 
                    + mult_reg[3];

endmodule

极点模块

`timescale 1ns/1ps
//-------------------------------------------------------
//   IIR滤波器极点系数模块
//-------------------------------------------------------

//-------------------------------------------------------
//   引脚模块定义
//-------------------------------------------------------

module pole_module(
    input clk                          ,//时钟信号
    input rst_n                        ,//复位信号,使能高电平有效
    input signed [11:0] pole_in     ,//输入信号

    output signed [19:0] pole_out     //输出信号
);

//-------------------------------------------------------
//   还是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_in    ;         
    end

//-------------------------------------------------------
//   滤波器的系数,量化之后,量化2^6
//   使用组合逻辑进行乘法的操作。
//-------------------------------------------------------
wire [19:0] mult_pole [6:0];

assign mult_pole[0] = {{{1{data_reg[1][11]}}, data_reg[1], 7'b0} 
                        + {{4{data_reg[1][11]}}, data_reg[1], 4'b0} 
                        + {{6{data_reg[1][11]}}, data_reg[1], 2'b0}};//*148_

assign mult_pole[1] = {{{1{data_reg[2][11]}}, data_reg[2], 7'b0} 
                        + {{2{data_reg[2][11]}}, data_reg[2], 6'b0} 
                        + {{7{data_reg[2][11]}}, data_reg[2], 1'b0}};//*194_

assign mult_pole[2] = {{{1{data_reg[3][11]}}, data_reg[3], 7'b0} 
                        + {{4{data_reg[3][11]}}, data_reg[3], 4'b0} 
                        + {{5{data_reg[3][11]}}, data_reg[3], 3'b0} 
                        + {{8{data_reg[3][11]}}, data_reg[3]}};//*153_

assign mult_pole[3] = {{{2{data_reg[4][11]}}, data_reg[4], 6'b0} 
                        + {{5{data_reg[4][11]}}, data_reg[4], 3'b0} 
                        + {{6{data_reg[4][11]}}, data_reg[4], 2'b0} 
                        + {{7{data_reg[4][11]}}, data_reg[4], 1'b0} 
                        + {{8{data_reg[4][11]}}, data_reg[4]}};//*79_

assign mult_pole[4] = {{{4{data_reg[5][11]}}, data_reg[5], 4'b0} 
                        + {{5{data_reg[5][11]}}, data_reg[5], 3'b0} 
                        + {{7{data_reg[5][11]}}, data_reg[5], 1'b0}};//*26_

assign mult_pole[5] = {{{6{data_reg[6][11]}}, data_reg[6], 2'b0} 
                        + {{8{data_reg[6][11]}}, data_reg[6]}};//*5_

assign mult_pole[6] = 20'b0;

//-------------------------------------------------------
//   求卷积和;将前面所有的乘积进行求和,并且输出置Yout
//   将前面7个已经完成乘法的寄存器中的数据进行加法运算
//-------------------------------------------------------
assign pole_out =    {mult_pole[0] + 
                        mult_pole[1] + 
                        mult_pole[2] + 
                        mult_pole[3] + 
                        mult_pole[4] + 
                        mult_pole[5] + 
                        mult_pole[6]};

endmodule

测试文件tb

//~ `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              = 12'd0          ;
    i                    = 0          ;
end

initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2) rst_n  =  1;
end


parameter DATA_NUM = 32'd1000000;
reg [11:0]  data_men[DATA_NUM:1];


initial begin
    $readmemb("C:/Users/64441/Desktop/Verilog_IIR_Butterworth/tb/sin.txt",data_men);   //注意斜杠的方向,不能反<<<<<<<
end

always @(posedge clk or negedge rst_n) begin //每1个clk_sig信号来临后,进行一次数据输入,将新的一个波形数据输入Din中。
    wave_in <= data_men[i]; 
    i <= i+1;
end



iir_top  u_IIR_top (
    .clk                     ( clk              ),
    .rst_n                   ( rst_n            ),
    .iir_in               ( wave_in   [11:0] ),
    .iir_out              ( wave_out  [11:0] )
);

integer w_file;
initial w_file = $fopen("C:/Users/64441/Desktop/Verilog_IIR_Butterworth/tb/simulation_after_IIR.txt");
always @(i)
begin
    $fdisplay(w_file,"%b",wave_out);
end  

endmodule


这个地方直接放仿真出来的图片:
在这里插入图片描述

滤波器的matlab仿真代码

%将二进制转化为十进制的数据,所有的数据都是从sin.txt原始文件中导出
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
str_in = importdata('..\tb\sin.txt');
str_length = length(str_in); 
str_out = zeros(str_length, 1);
MAX_DATA = 2^11-1;
for number = 1 : str_length
    str_out(number) = bin2dec( num2str(str_in(number)) );
    if(str_out(number) > MAX_DATA)
           str_out(number) = bin2dec( num2str(str_in(number)) ) - 2^12;
    end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

x_in= str_out;

pole_out = 0;

%定义每一个变量的初值,按照电路中复位的原则,所有的卷积器中的变量,都给予初值
%初值的大小设置位为0
x_in_2 = 0;
x_in_3 = 0;
x_in_4 = 0;
x_in_5 = 0;
x_in_6 = 0;
x_in_7 = 0;
x_in_8 = 0;

div = 0;

x_in_6_pole = 0;
x_in_5_pole = 0;
x_in_4_pole = 0;    
x_in_3_pole = 0;     
x_in_2_pole = 0;   
x_in_1_pole = 0;

%定义一个矩阵数组,用来存放我们的输出值
output = zeros(1, str_length);

%定义一个二进制数组存储单元
binary_tem = zeros(1, 12);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for i=1:1:str_length
    %进行第一次卷积操作,第一次卷积操作是进行零点运算
     %卷积操作,将原来的寄存器中的数据进行卷积,卷积后的数据放入到y中。
     y = x_in(i)*5 + x_in_2 * 37 + x_in_3 * 110 +  x_in_4 * 183 + x_in_5 * 183 + x_in_6 * 110 + x_in_7 * 37 + x_in_8 * 5;

     %依次进行数据的搬移工作
     x_in_8 = x_in_7;
     x_in_7 = x_in_6;
     x_in_6 = x_in_5;
     x_in_5 = x_in_4;
     x_in_4 = x_in_3;    
     x_in_3 = x_in_2;     
     x_in_2 = x_in(i);
     
     %进行第二次卷积操作,第二次卷积操作是进行极点运算
     %卷积操作,将卷积后的值运算得出,放在pole中
     pole = x_in_1_pole * 148 + x_in_2_pole * 194 + x_in_3_pole * 153 +  x_in_4_pole * 79 + x_in_5_pole * 26 + x_in_6_pole * 5;
     
     %依次进行数据的搬移工作
     x_in_6_pole = x_in_5_pole;
     x_in_5_pole = x_in_4_pole;
     x_in_4_pole = x_in_3_pole;    
     x_in_3_pole = x_in_2_pole;     
     x_in_2_pole = x_in_1_pole;
     x_in_1_pole = div;
     
     %进行数据的相减,相当于零点的运算值与极点的运算值进行相差
     sub = y - pole;
     %相当于数据的截位运算,由于需要进行截位除法,因此,模拟一次数据的截位操作,原来的21位的数据,最终只需要得到12位的值
     %因此,只需要读取前十二位的数据,便相当于对后9位的数据进行一个截取
        B_s=dec2bin(sub+((sub)<0)*2^21,21);
        for j=1:12
          if B_s(j)=='1'
              binary_tem(1, j) = 1;
          else
              binary_tem(1, j) = 0;
          end
        end
        
        if(binary_tem(1, 1) == 0)
            div = binary_tem(1, 1) * 2048 + binary_tem(1, 2) * 1024 + binary_tem(1, 3) * 512 + binary_tem(1, 4) * 256 + binary_tem(1, 5) * 128 + binary_tem(1, 6) * 64 + binary_tem(1, 7) * 32 + binary_tem(1, 8) * 16 + binary_tem(1, 9) * 8 + binary_tem(1, 10) * 4 + binary_tem(1, 11) * 2 + binary_tem(1, 12) * 1;
        else
            div =  ~(binary_tem(1, 1)) * 2048 +  ~(binary_tem(1, 2)) * 1024 + ~(binary_tem(1, 3)) * 512 + ~(binary_tem(1, 4)) * 256 + ~(binary_tem(1, 5)) * 128 + ~(binary_tem(1, 6)) * 64 + ~(binary_tem(1, 7)) * 32 + ~(binary_tem(1, 8)) * 16 + ~(binary_tem(1, 9)) * 8 + ~(binary_tem(1, 10)) * 4 + ~(binary_tem(1, 11)) * 2 + ~(binary_tem(1, 12)) * 1;
            div = div + 1;
            div = -div;
        end

    
    %获得div截位之后的值,得到的结果放入输出矩阵output(1, i)中去
    output(1, i) = div;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%波形数据的数据的写入,将截位之后的数据,也就是即输出的滤波波形数据进行输出
fid = fopen('..\tb\simulation_MATLAB_IIR.txt','w');
for k=1:length(output)
    B_s=dec2bin(output(k)+((output(k))<0)*2^12,12);
    for j=1:12
       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);

利用matalb仿真出来的数据,与FPGA仿真数据进行对比,验证巴特沃斯滤波器的滤波结果的正确性。经过Beyond Compare的比对,选择差别选项,可以看出,除了首尾的差异之外,其余的数据都是相同的。因此数据匹配。
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值