要求:设计一个15阶(长度为16)的低通线性相位FIR滤波器,采用blackman窗,截止频率500Hz,采样频率2KHz,采用FPGA并行结构滤波器实现,滤波器系数量化位宽12bit,输入数据位宽12bit,输出数据位宽29bit,系统时钟2KHz。
1.首先采用Matlab求出符合要求的滤波器的系数,并观察12bit量化后的滤波器幅频响应曲线(代码如下)。
matlab代码与上节串行FIR滤波器设计基本一样
运行结果为
可以发现12bit量化符合要求
2.采用Matlab仿真产生待测试数据并通过设计的滤波器验证理论上的滤波效果同时将生成的测试数据写入txt文件中供后面Modelsim仿真时读取
运行结果:
我们可以发现当输入信号为白噪声时输出800Hz以后的频率衰减已经达到-40dB了
当输入信号为200Hz和800Hz的叠加信号时,滤波后的输出只剩200Hz的信号了,800Hz被滤除了。达到了预期的效果。
3.编写Verilog代码实现FPGA设计滤波器结构
/*
*
*@Author: X-Z
*@Date:2023-09-18 20:57:28
*@Function:采用并行结构实现FIR滤波器
*/
/*
设计一个15阶(长度2为16)的低通线性相位的FIR滤波器,采用布莱克曼窗,截止频率500Hz,采样频率2KHZ,
采用FPGA实现并行结构的滤波器,系数量化位数12Bit,输入数据位宽12bit,输出数据位宽29bit
系统时钟2KHz
*/
//乘法器使用了1级流水线,加法器没有使用流水线会立即输出相加结果
module FirParaller(
input clk ,//系统时钟2KHz
input rst_n ,
input signed [11:0] Xin ,//输入信号12bit
output signed [28:0] Yout //输出信号
);
//将数据存入移位寄存器Xin_Reg中
reg signed [11:0] Xin_Reg[15:0];
reg [3:0] i,j;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin//初始化寄存器
for(i=0;i<15;i=i+1)
Xin_Reg[i] <= 12'd0;
end
else begin
for(j=0;j<15;j=j+1)//此处与串行结构不同,不需要判断计数器状态
Xin_Reg[j+1] <= Xin_Reg[j];
Xin_Reg[0] <= Xin;
end
end
//将对称系数的输入数据相加,同时将对应滤波器系数送人乘法器
//为进一步提高运行速度,另外增加一级寄存器
reg signed [12:0] Add_Reg[7:0];
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
for(i=0;i<8;i=i+1)
Add_Reg[i] <= 13'd0;
end
else begin
for(j=0;j<8;j=j+1)
Add_Reg[j] <= {Xin_Reg[j][11],Xin_Reg[j]} + {Xin_Reg[15-j][11],Xin_Reg[15-j]};
end
end
//与串行结构不同,另外需要实例化8个乘法器IP核
//实例化有符号数乘法器IP核
wire signed [11:0] coe [7:0];//滤波器12bit量化滤波器系数
wire signed [24:0] Mout [7:0];//乘法器输出为25bit的数据
//滤波器系数
/*
'000' 'FFD''00F''02E' 'F8B''EF9' '24E' '7FF'
'7FF''24E''EF9''F8B''02E''00F''FFD''000'
ans =
列 1 至 10
0 -3 15 46 -117 -263 590 2047 2047 590
列 11 至 16
-263 -117 46 15 -3 0
*/
assign coe[0] = 12'h000;
assign coe[1] = 12'hffd;
assign coe[2] = 12'h00f;
assign coe[3] = 12'h02e;
assign coe[4] = 12'hf8b;
assign coe[5] = 12'hef9;
assign coe[6] = 12'h24e;
assign coe[7] = 12'h7ff;
// localparam coe[0] = 12'h000,
// coe[1] = 12'hffd,
// coe[2] = 12'h00f,
// coe[3] = 12'h02e,
// coe[4] = 12'hf8b,
// coe[5] = 12'hef9,
// coe[6] = 12'h24e,
// coe[7] = 12'h7ff;
//例化8个并行的乘法器
mul u_mult0(
.clock (clk ),
.dataa (coe[0] ),
.datab (Add_Reg[0] ),
.result (Mout[0] )
);
mul u_mult1(
.clock (clk ),
.dataa (coe[1] ),
.datab (Add_Reg[1] ),
.result (Mout[1] )
);
mul u_mult2(
.clock (clk ),
.dataa (coe[2] ),
.datab (Add_Reg[2] ),
.result (Mout[2] )
);
mul u_mult3(
.clock (clk ),
.dataa (coe[3] ),
.datab (Add_Reg[3] ),
.result (Mout[3] )
);
mul u_mult4(
.clock (clk ),
.dataa (coe[4] ),
.datab (Add_Reg[4] ),
.result (Mout[4] )
);
mul u_mult5(
.clock (clk ),
.dataa (coe[5] ),
.datab (Add_Reg[5] ),
.result (Mout[5] )
);
mul u_mult6(
.clock (clk ),
.dataa (coe[6] ),
.datab (Add_Reg[6] ),
.result (Mout[6] )
);
mul u_mult7(
.clock (clk ),
.dataa (coe[7] ),
.datab (Add_Reg[7] ),
.result (Mout[7] )
);
//对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据
//与串行结构不同,此处在一个时钟周期内直接将所有乘法器结果相加
reg signed [28:0] sum1,sum2;
reg signed [28:0] yout;
//reg [3:0] k;
// always @(posedge clk or negedge rst_n)begin
// if(!rst_n)begin
// sum = 29'd0;
// yout <= 29'd0;
// end
// else begin
// yout <= sum;
// sum = 0;
// for(k=0;k<8;k=k+1)
// sum = sum + Mout[k];
// end
// end
//采用两级流水线实现累加运算
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sum1 <= 29'd0;
sum2 <= 29'd0;
yout <= 29'd0;
end
else begin
sum1 <= Mout[0]+Mout[1]+Mout[2]+Mout[3];
sum2 <= Mout[4]+Mout[5]+Mout[6]+Mout[7];
yout <= sum1 + sum2;
end
end
assign Yout = yout;
endmodule
4.编写测试激励文件(略)
5.将仿真滤波器滤波后输出的数据写入txt文件中。
Modelsim仿真结果:
通过仿真我们可以发现大致实现了滤波性能,但还是不够直观,因此我们采用Matlab进行分析滤波性能。
6.采用Matlab分析经过滤波后的数据,分析滤波性能。代码(略)
运行结果为:
我们发现采用FPGA滤波前后的波形和Matlab仿真波形图基本一致,说明程序代码没问题,并行FIR滤波器运行速度大大提高,但消耗了大量的面积资源,用面积换取速度。最终设计满足要求。(昨晚就学完了并行滤波器,没来得及写抓紧补上)。
注:自学完杜勇的FIR滤波器所写。