前言:本文参考了其他博客,自己做了一遍,只是用做记录。
设计目标:
设计一个低通滤波器,可以从1KHZ、3KHZ、4KHZ的叠加信号中,滤除掉3KHZ及以上的信号。
功能设计
总体架构:
总结一下,即:
首先由MATLAB生成一个由三个正弦波叠加的待滤波信号,三个正弦波的频率分别是1KHZ,3KHZ,4KHZ。
然后将待滤波信号送入Vivado,用Verilog编写的FIR滤波器进行仿真,观察滤波后的波形。
再利用MATLAB里面的conv函数将滤波器系数和待滤波信号卷积并观察滤波后的波形图。
将Vivado的仿真结果和MATLAB的仿真结果比较,验证在FPGA中滤波器算法的正确性。
设计步骤
matlab生成滤波器系数
如图所示,使用matlab中的filterDesigner设计滤波器,滤波器指标总结在表1:
滤波器指标 | |
采样频率(Fs) | 10000HZ |
通带截止频率(Fpass) | 1000HZ |
阻带截止频率(Fstop) | 3000HZ |
滤波器阶数(specify order) | 8阶 |
将滤波器系数export到matlab的workspace,使用图3所示的两行代码进行定点化处理,得到如图4所示的滤波器系数:
matlab生成待滤波信号
使用图5所示代码生成KHZ、3KHZ、4KHZ三个正弦波叠加信号,并将其保存成.txt文件:
Vivado进行FIR的verilog设计
原理框图:
代码实现:
`timescale 1ms / 1ps
module fir(
input[7:0] FIR_IN,
input CLK,
input RSTn,
output reg[15:0] FIR_OUT
);
reg[7:0] delay_pipeline1 ;
reg[7:0] delay_pipeline2 ;
reg[7:0] delay_pipeline3 ;
reg[7:0] delay_pipeline4 ;
reg[7:0] delay_pipeline5 ;
reg[7:0] delay_pipeline6 ;
reg[7:0] delay_pipeline7 ;
reg[7:0] delay_pipeline8 ;
reg[7:0] delay_pipeline9 ;
/*第一级流水,将输入信号进行延时,每到来一个时钟信号,
便将输入信号保存到delay_pipelin1中,然后将剩下的依次移动一位。*/
always@(posedge CLK or negedge RSTn)
if(!RSTn)
begin
delay_pipeline1 <= 8'b0 ;
delay_pipeline2 <= 8'b0 ;
delay_pipeline3 <= 8'b0 ;
delay_pipeline4 <= 8'b0 ;
delay_pipeline5 <= 8'b0 ;
delay_pipeline6 <= 8'b0 ;
delay_pipeline7 <= 8'b0 ;
delay_pipeline8<= 8'b0 ;
delay_pipeline9<= 8'b0 ;
end
else begin
delay_pipeline1 <= FIR_IN ;
delay_pipeline2 <= delay_pipeline1 ;
delay_pipeline3 <= delay_pipeline2 ;
delay_pipeline4 <= delay_pipeline3 ;
delay_pipeline5 <= delay_pipeline4 ;
delay_pipeline6 <= delay_pipeline5 ;
delay_pipeline7 <= delay_pipeline6 ;
delay_pipeline8 <=delay_pipeline7 ;
delay_pipeline9<= delay_pipeline8 ;
end
//滤波器系数
wire[7:0] coeff1 = 8'd7;
wire[7:0] coeff2 = 8'd5;
wire[7:0] coeff3 = 8'd51;
wire[7:0] coeff4 = 8'd135;
wire[7:0] coeff5 = 8'd179;
wire[7:0] coeff6 = 8'd135;
wire[7:0] coeff7 = 8'd51;
wire[7:0] coeff8 = 8'd5;
wire[7:0] coeff9 = 8'd7;
//乘积结果保存寄存器
reg signed [16:0] multi_data1 ;
reg signed [16:0] multi_data2 ;
reg signed [16:0] multi_data3 ;
reg signed [16:0] multi_data4 ;
reg signed [16:0] multi_data5 ;
reg signed [16:0] multi_data6 ;
reg signed [16:0] multi_data7 ;
reg signed [16:0] multi_data8 ;
reg signed [16:0] multi_data9 ;
//x(n) * h(n-k)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
begin
multi_data1 <= 17'b0 ;
else begin
multi_data1 <= delay_pipeline1*coeff1 ;
end
//x(1) * h(1)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data2 <= 16'b0 ;
else
multi_data2 <= delay_pipeline2*coeff2 ;
//x(2) * h(2)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data3 <= 16'b0 ;
else
multi_data3 <= delay_pipeline3*coeff3 ;
//x(3) * h(3)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data4 <= 16'b0 ;
else
multi_data4 <= delay_pipeline4*coeff4 ;
//x(4) * h(4)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data5 <= 16'b0 ;
else
multi_data5 <= delay_pipeline5*coeff5 ;
//x(5) * h(5)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data6 <= 16'b0 ;
else
multi_data6 <= delay_pipeline6*coeff6 ;
//x(6) * h(6)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data7 <= 16'b0 ;
else
multi_data7 <= delay_pipeline7*coeff7;
//x(7) * h(7)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data8 <= 16'b0 ;
else
multi_data8 <= delay_pipeline8*coeff8;
//x(8) * h(8)
always@(posedge CLK or negedge RSTn)
if(!RSTn)
multi_data9 <= 16'b0 ;
else
multi_data9 <= delay_pipeline9*coeff9 ;
将乘积累加,累加的结果就是滤波后的信号
always@(posedge CLK or negedge RSTn)
if(!RSTn)
FIR_OUT <= 16'b0 ;
else
FIR_OUT <= multi_data1 + multi_data2 + multi_data3 +
multi_data4 +multi_data5 + multi_data6 + multi_data7 +
multi_data8 + multi_data9 ;
endmodule
testbench:
module tb(
);
reg CLK;
reg [7:0] FIR_IN;
reg RSTn;
reg [7:0] mem[1:4096];
wire [15:0] FIR_OUT;
reg [12:0] i;
//例化FIR滤波器
fir i1 (
.CLK(CLK),
.FIR_IN(FIR_IN),
.FIR_OUT(FIR_OUT),
.RSTn(RSTn)
);
initial
begin
$readmemh("C:/Users/fuwen/Desktop/FPGA_final/FIR_EDA_VERSION/project_3/mem.txt",mem);//将待滤波信号读入mem
RSTn= 0;
CLK= 0;
#50;RSTn= 1;
#50000;
$stop;
end
initial
forever
#50 CLK = ~CLK;//时钟生成,注意与采样率一致
always@(posedge CLK or negedge RSTn)
if(!RSTn)
FIR_IN <= 8'b0 ;
else
FIR_IN <= mem[i]; //读入数据
always@(posedge CLK or negedge RSTn)
if(!RSTn)
i <= 12'd0;
else
i <= i + 1'd1;
endmodule
行为仿真波形结果如下图所示:
使用matlab自带conv对带滤波数据进行滤波,代码如下:
% Fs = 10000; %采样频率决定了两个正弦波点之间的间隔
% N = 4096; %采样点数
% N1 = 0 : 1/Fs : (N-1)/Fs;
% s = sin(1000*2*pi*N1) + sin(3000*2*pi*N1) +sin(4000*2*pi*N1);
% fidc = fopen('C:\Users\fuwen\Desktop\FPGA_final\FIR_EDA_VERSION\project_3\mem.txt','wt');
% for x = 1 : N
% fprintf(fidc,'%x\n',round((s(x)+2.12)*58));
% end
% fclose(fidc);
Fs = 10000; %采样频率决定了两个正弦波点之间的间隔
N = 4096; %采样点数
N1 = 0 : 1/Fs :(N-1)/Fs;
in =sin(1000*2*pi*N1) + sin(3000*2*pi*N1) + sin(4000*2*pi*N1);
coeff =[-0.0325,-0.0384,0.0784,0.2874,0.3984,0.2874,0.0784,-0.0384,-0.0325]; %滤波器系数
out =conv(in,coeff);%卷积滤波
subplot(2,1,1);
plot(in);
xlabel('滤波前');
axis([0 200 -3 3]);
subplot(2,1,2);
plot(out);
xlabel('滤波后');
axis([100 200 -2 2]);
输出的仿真波形如下图所示,显然,Verilog实现的滤波器与matlab实现的功能一致,故滤波器功能正确,可以进一步进行综合、实现以及布局布线。
综合出的原理图:
仅加时钟约束,无其他任何优化策略时的综合实现
综合:
添加时序约束10ns,检查时序报告:
Slack均为正值,时序没有违例情况:
资源利用率:
采取时序约束+综合约束retiming时的综合实现
综合时勾选Retiming选项:启用该功能,可以通过在组合门和LUT之间移动寄存器(达到寄存器平衡状态)提高内部时钟时序路径的电路性能。该功能会保留原来的功能和电路延迟,也不需要改变RTL源文件。
综合
利用率:可以看出lut数和寄存器数相比默认设置都有增长
采用时序约束+不同的优化策略时的综合实现:
优化面积
利用率:可以看到所用的LUT以及FF都减少很多,但是使用了较多的dsp(5个)。
还是上述的情况,但是在综合选项中添加约束,设置不允许使用DSP
综合时:利用率:可以看到所用的LUT以及FF都增加了,没有使用dsp。
还有很多种不同方式,这里不一一列举了。