一、FIR滤波器模块介绍
设计该模块作用是,可通过MATLAB产生h(n)序列的系数获得的coe文件(注意该系数需要处理成整数,后面以16bit的数据有符号数为例)。将数据保存rom中,信号输入该模块,该模块可以实现输入信号与h(n)序列的实时卷积输出。
二、该模块用法介绍
1、 用MATLAB生成滤波器系数
首先用MATLAB滤波器设计模块导出滤波器的mat文件,untitled.mat
然后用MATLAB产生COE文件
Hd=load('untitled.mat');
filtercoeff=Hd.LF.Numerator*80000;
% 将数据量化为8位整数(如果需要)
figure;
filter_coe_quantized_data = int16(fliplr(filtercoeff));
plot(filter_coe_quantized_data)
% 保存为COE文件
coe_file = fopen('filter.coe', 'w');
fprintf(coe_file, 'memory_initialization_radix=10;\n');
fprintf(coe_file, 'memory_initialization_vector=\n');
for i = 1:length(filter_coe_quantized_data)
fprintf(coe_file, '%d,\n', filter_coe_quantized_data(i));
end
fclose(coe_file);
2、VIVADO RAM IP核设置
这里IP核大小设置的其实比较大,可以实现1000阶的实时卷积,可以根据实际情况设置小些,比如这里MATLAB生成30阶滤波器,64的深度就够了。
![](https://img-blog.csdnimg.cn/direct/761240bac59c4546a744494b74ac2a11.png)
3、rom IP核设置
rom 导入COE文件
![](https://img-blog.csdnimg.cn/direct/c8996255b9b84d2a809266b0ad93e179.png)
![](https://img-blog.csdnimg.cn/direct/c52465516e34457e862c512aa938ebb6.png)
4、模块使用,输入接口时序。
sys_clk应该是比,数据变换更快的一个时钟,比如Data是由96KHZ的ADC采样的数据,sys_clk使用100MHZ的时钟计算,Data_flag在数据到来时保持多个100MHZ的时钟周期。
这个模块卷积模块兼容WM8731音频采样模块,通常WM8731音频采样模块(采样率96KHZ)的时序就是就会在数据到来时,给一个有12MHZ时钟产生的一个高电平。(之前设计时候就是基于这个音频采样模块设计)
该模块会实现实时的每个数据到来时实现实时得卷积运算,并以和输入数据一样的采样率的rela_data输出。
注意这里用rela_data表示的原因是因为,当rom放置需要一段波形,它也会实现实时的相关运算。
所以注意放置rom中的序列应当是h(n)的倒置,MATLAB中用fliplr函数实现h(n)序列的倒置。
三、verilog代码
`timescale 1ns/1ns
// Author : 悠志
// Create Date : 2023/10/10
module low_filter
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire data_flag ,
input wire signed [15:0] data ,
output reg signed [31:0] rela_data
);
parameter RELA_NUM= 'd30;//31-1
reg [10:0] ccl_cnt;
reg judge_en;//卷积运算结果判断
reg ccl_en ;
reg ccl_en_d1;
reg signed [31:0] mult_data ;
// sum_ram
wire signed [31:0] sum_rela_rom_data ;
reg signed [31:0] sum_data ;
reg sum_ram_wr_en ;
reg sum_ram_rd_en ;
reg [9:0] sum_ram_wr_addr ;
reg [9:0] sum_ram_rd_addr ;
wire signed [15:0] rela_rom_data ;
reg [9:0] rela_rom_addr ;
//ccl_cnt
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
ccl_cnt<=10'd0;
else if(ccl_en==1'b1)
ccl_cnt<=ccl_cnt+1'b1;
else if(ccl_cnt==10'd0||ccl_cnt==RELA_NUM+10'd3)
ccl_cnt<=10'd0;
else
ccl_cnt<=ccl_cnt+1'b1;
//judge_en
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
judge_en<=1'b0;
else if(ccl_cnt==RELA_NUM+10'd2)
judge_en<=1'b1;
else
judge_en<=1'b0;
//ccl_en
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
ccl_en<=1'b0;
else if(data_flag==1'b1)
ccl_en<=1'b1;
else if(ccl_cnt==RELA_NUM)
ccl_en<=1'b0;
else
ccl_en<=ccl_en;
//ccl_en_d1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
ccl_en_d1<=1'b0;
else
ccl_en_d1<=ccl_en;
//rela_rom_addr
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rela_rom_addr<=10'd0;
else if(data_flag==1'b1)
rela_rom_addr<=rela_rom_addr+1'b1;
else if(rela_rom_addr==RELA_NUM||rela_rom_addr==10'd0)
rela_rom_addr<=10'd0;
else
rela_rom_addr<=rela_rom_addr+1'b1;
//mult_data
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
mult_data<=32'd0;
else if(ccl_en_d1==1'b1)
mult_data<=rela_rom_data*data;
else
mult_data<=mult_data;
//sum_ram_rd_en
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
sum_ram_rd_en<=1'b0;
else
sum_ram_rd_en<=ccl_en_d1;
//sum_ram_rd_addr
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
sum_ram_rd_addr<=10'd0;
else if(sum_ram_rd_en==1'b0)
sum_ram_rd_addr<=10'd0;
else
sum_ram_rd_addr<=sum_ram_rd_addr+1'b1;
//sum_data
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
sum_data<=16'd0;
else if(sum_ram_rd_en==1'b1)
begin
if(sum_ram_rd_addr==10'd0)
sum_data<=mult_data;
else
sum_data<=sum_rela_rom_data+mult_data;
end
else
sum_data<=sum_data;
//sum_ram_wr_en
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
sum_ram_wr_en<=1'b0;
else
sum_ram_wr_en<=sum_ram_rd_en;
//sum_ram_wr_addr
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
sum_ram_wr_addr<=10'd0;
else
sum_ram_wr_addr<=sum_ram_rd_addr;
//rela_data
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
rela_data<='d0;
else if(judge_en==1'b1)
rela_data<=sum_data;
else
rela_data<=rela_data;
sum_ram sum_ram_inst (
.clka(sys_clk), // input wire clka
.ena(sum_ram_wr_en), // input wire ena
.wea(1'b1), // input wire [0 : 0] wea
.addra(sum_ram_wr_addr), // input wire [9 : 0] addra
.dina(sum_data), // input wire [31 : 0] dina
.douta(), // output wire [31 : 0] douta
.clkb(sys_clk), // input wire clkb
.enb(sum_ram_rd_en), // input wire enb
.web(1'b0), // input wire [0 : 0] web
.addrb(sum_ram_rd_addr), // input wire [9 : 0] addrb
.dinb(), // input wire [31 : 0] dinb
.doutb(sum_rela_rom_data) // output wire [31 : 0] doutb
);
filter_rom filter_rom_inst (
.clka(sys_clk), // input wire clka
.addra(rela_rom_addr), // input wire [4 : 0] addra
.douta(rela_rom_data) // output wire [15 : 0] douta
);
endmodule
总结
本文教大家如何用FPGA实现实时地卷积运算,以实现FIR滤波器,若大家使用该模块过程遇到问题可以私信我。