目录
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的比对,选择差别选项,可以看出,除了首尾的差异之外,其余的数据都是相同的。因此数据匹配。