Written by @hzj
//JinXing Project
#2021.11.2 V1.0
#2021.11.3 V1.1
8bit的FIR滤波器的FPGA实现
为了让滤波器的处理速度更快,可以使用8bit的同时输入,那么此文就将改写前文写的FIR滤波器进行改写,原来8个clk_sig只能处理一个信号,现在同时传输8个信号进行处理。但是由于输出信号的局限(只能一个一个clk周期输出一个滤波后的信号),因此是每8个周期统一一次性输入8个信号,然后一个clk出去一个。注意 :后期是可以实现一个周期输入一个8bit信号,然后同时在这个周期输出8bit滤波后的信号,只需要改变成为8个卷积单元即可。(相当于增加更多的器件,让整个系统的处理速度更快),但由于本文的实现方式的局限性(需要根据每一个clk输出一个滤波信号),没有采用这样的方式。
前文的实现: FPGA学习记录(5)<低通&带通FIR滤波器FPGA实现>.
`timescale 1ns / 1ps
module FIR_low8(
input clk , //输入的系统时钟,时钟驱动
input clk_sig , //计数信号
input rst_n , //定义复位信号,供里面所有的参量进行复位,复位信号使用的是低电平有效
input signed [15:0] data_in_1 , //一个clk_sig信号,输入第一位数据位
input signed [15:0] data_in_2 , //一个clk_sig信号,输入第二位数据位
input signed [15:0] data_in_3 , //一个clk_sig信号,输入第三位数据位
input signed [15:0] data_in_4 , //一个clk_sig信号,输入第四位数据位
input signed [15:0] data_in_5 , //一个clk_sig信号,输入第五位数据位
input signed [15:0] data_in_6 , //一个clk_sig信号,输入第六位数据位
input signed [15:0] data_in_7 , //一个clk_sig信号,输入第七位数据位
input signed [15:0] data_in_8 , //一个clk_sig信号,输入第八位数据位
output reg signed [15:0] data_out , //定义滤波之后的数据,使用reg变量进行保存,有效位为16bit
output reg signed [15:0] input_wave
);
parameter U_DELAY = 1;//时序仿真信号,加上u_delay模拟信号的电路中传输的延迟
/*定义16个数据寄存器,用来存储待滤波信号的数据,每8个clk_sig信号输入8个数据进去,然后将源8个地位搬移到高8位上去
再将data_in_*中的数据打入到x1-x8这个低八位中去*/
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;
reg [2:0] count;
/* 根据时钟信号进行切换卷积器mul中的数据值,依次切换进行卷积。本次移位使用的是系统时钟,采用的下降沿rst_n进行复位,低有效位生效*/
always @ (posedge clk_sig or negedge rst_n) begin
if (rst_n == 1'b0) begin
x1 <= #U_DELAY 16'b0 ;
x2 <= #U_DELAY 16'b0 ;
x3 <= #U_DELAY 16'b0 ;
x4 <= #U_DELAY 16'b0 ;
x5 <= #U_DELAY 16'b0 ;
x6 <= #U_DELAY 16'b0 ;
x7 <= #U_DELAY 16'b0 ;
x8 <= #U_DELAY 16'b0 ;
x9 <= #U_DELAY 16'b0 ;
x10 <= #U_DELAY 16'b0 ;
x11 <= #U_DELAY 16'b0 ;
x12 <= #U_DELAY 16'b0 ;
x13 <= #U_DELAY 16'b0 ;
x14 <= #U_DELAY 16'b0 ;
x15 <= #U_DELAY 16'b0 ;
x16 <= #U_DELAY 16'b0 ;
end
else begin
x9 <= #U_DELAY x1 ;//数据的搬移,以及新数据的打入
x10 <= #U_DELAY x2 ;
x11 <= #U_DELAY x3 ;
x12 <= #U_DELAY x4 ;
x13 <= #U_DELAY x5 ;
x14 <= #U_DELAY x6 ;
x15 <= #U_DELAY x7 ;
x16 <= #U_DELAY x8 ;
x1 <= #U_DELAY data_in_8 ;
x2 <= #U_DELAY data_in_7 ;
x3 <= #U_DELAY data_in_6 ;
x4 <= #U_DELAY data_in_5 ;
x5 <= #U_DELAY data_in_4 ;
x6 <= #U_DELAY data_in_3 ;
x7 <= #U_DELAY data_in_2 ;
x8 <= #U_DELAY data_in_1 ;
end
end
/*定义一个位宽大小位3的参数变量,这里的3位宽是由我们的滤波器系数决定的,由于参数个数的量=7,因此这个地方就应该有能够表示7种可能的大小即2^3=8*/
/*定义count的参数大小;每一个计数周期来临时自加一,该参数可以控制该时钟周期与何进行乘积;遇到下降沿的rst_n时,进行count初值恢复*/
always@(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin
count = 3'd0;
end
else begin
count <= count + 1'b1;
end
end
/* 加法器,根据量化系数的对称性,进行两者的加法,得到三个变量add2_7, add3_6, add4_5 */
wire signed [16:0] add2_7;
wire signed [16:0] add3_6;
wire signed [16:0] add4_5;
reg [15:0] num_x_2;
reg [15:0] num_x_3;
reg [15:0] num_x_4;
reg [15:0] num_x_5;
reg [15:0] num_x_6;
reg [15:0] num_x_7;
assign add2_7 = {num_x_2[15] ,num_x_2} + {num_x_7[15], num_x_7};
assign add3_6 = {num_x_3[15], num_x_3} + {num_x_6[15], num_x_6};
assign add4_5 = {num_x_4[15], num_x_4} + {num_x_5[15], num_x_5};
wire signed [24:0] out_temp;
//例化调用模块,卷积层
mul mul_conv
(
.add_2_7 (add2_7[16:0]) ,
.add_3_6 (add3_6[16:0]) ,
.add_4_5 (add4_5[16:0]) ,
.mul_out (out_temp [24:0])
);
/*根据上述的count的值,轮流切换mul_a以及mul_b的值*/
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
num_x_2 <= #U_DELAY 0 ;
num_x_3 <= #U_DELAY 0 ;
num_x_4 <= #U_DELAY 0 ;
num_x_5 <= #U_DELAY 0 ;
num_x_6 <= #U_DELAY 0 ;
num_x_7 <= #U_DELAY 0 ;
input_wave <= 16'b0 ;
end
else begin
case(count)
3'b000 : begin
num_x_2 <= #U_DELAY x10 ;
num_x_3 <= #U_DELAY x11 ;
num_x_4 <= #U_DELAY x12 ;
num_x_5 <= #U_DELAY x13 ;
num_x_6 <= #U_DELAY x14 ;
num_x_7 <= #U_DELAY x15 ;
input_wave <= x8 ;
end
3'b001 : begin
num_x_2 <= #U_DELAY x9 ;
num_x_3 <= #U_DELAY x10 ;
num_x_4 <= #U_DELAY x11 ;
num_x_5 <= #U_DELAY x12 ;
num_x_6 <= #U_DELAY x13 ;
num_x_7 <= #U_DELAY x14 ;
input_wave <= x7 ;
end
3'b010 : begin
num_x_2 <= #U_DELAY x8 ;
num_x_3 <= #U_DELAY x9 ;
num_x_4 <= #U_DELAY x10 ;
num_x_5 <= #U_DELAY x11 ;
num_x_6 <= #U_DELAY x12 ;
num_x_7 <= #U_DELAY x13 ;
input_wave <= x6 ;
end
3'b011 : begin
num_x_2 <= #U_DELAY x7 ;
num_x_3 <= #U_DELAY x8 ;
num_x_4 <= #U_DELAY x9 ;
num_x_5 <= #U_DELAY x10 ;
num_x_6 <= #U_DELAY x11 ;
num_x_7 <= #U_DELAY x12 ;
input_wave <= x5 ;
end
3'b100 : begin
num_x_2 <= #U_DELAY x6 ;
num_x_3 <= #U_DELAY x7 ;
num_x_4 <= #U_DELAY x8 ;
num_x_5 <= #U_DELAY x9 ;
num_x_6 <= #U_DELAY x10 ;
num_x_7 <= #U_DELAY x11 ;
input_wave <= x4 ;
end
3'b101 : begin
num_x_2 <= #U_DELAY x5 ;
num_x_3 <= #U_DELAY x6 ;
num_x_4 <= #U_DELAY x7 ;
num_x_5 <= #U_DELAY x8 ;
num_x_6 <= #U_DELAY x9 ;
num_x_7 <= #U_DELAY x10 ;
input_wave <= x3 ;
end
3'b110 : begin
num_x_2 <= #U_DELAY x4 ;
num_x_3 <= #U_DELAY x5 ;
num_x_4 <= #U_DELAY x6 ;
num_x_5 <= #U_DELAY x7 ;
num_x_6 <= #U_DELAY x8 ;
num_x_7 <= #U_DELAY x9 ;
input_wave <= x2 ;
end
default : begin
num_x_2 <= #U_DELAY x3 ;
num_x_3 <= #U_DELAY x4 ;
num_x_4 <= #U_DELAY x5 ;
num_x_5 <= #U_DELAY x6 ;
num_x_6 <= #U_DELAY x7 ;
num_x_7 <= #U_DELAY x8 ;
input_wave <= x1 ;
end
endcase
end
end
/*定义一个输出的中间寄存器,out_temp,用来存储中间的变量值*/
always @ (posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin
data_out <= #U_DELAY 16'b0 ;
end
else begin
data_out <= #U_DELAY out_temp[24:9] ;
end
end
endmodule
mul卷积层实现如下所示,里面特定了固定数的乘法:
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
//
// Company:
// Engineer:
//
// Create Date: 2021/10/16 14:50:22
// Design Name:
// Module Name: tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
`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 ;
integer i;
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
forever begin
clk_sig = ~clk_sig;
#(PERIOD);
clk_sig = ~clk_sig;
#(PERIOD*7);
end
end
initial
begin
#(PERIOD*2) rst_n = 1; //复位信号
end
parameter data_num = 32'd10000;
reg [15:0] data_men[data_num:1];
initial begin
$readmemb("C:/路径/tb/sin.txt",data_men); //注意斜杠的方向,不能反<<<<<<<
end
always @(posedge clk_sig) 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
FIR_low8 u_FIR_low8 (
.clk ( clk ),
.clk_sig ( clk_sig ),
.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 ( data_out [15:0] )
);
integer w_file;
initial w_file = $fopen("C:/路径/tb/simulation_out.txt");
always @(i)
begin
$fdisplay(w_file,"%d",data_out);
if(i == 32'd1000000) //共写入个数据
$stop;
end
endmodule
滤波后的结果如下所示,可以看出与前文的结果相符。达到了滤波效果。