通用FIR滤波器的verilog实现(内有Lowpass、Hilbert参数生成示例)

avatar
作者
筋斗云
阅读量:0

  众所周知,Matlab 中的 Filter Designer 可以直接生成 FIR 滤波器的 verilog 代码,可以方便地生成指定阶数、指定滤波器参数的高通、低通、带通滤波器,生成的 verilog 代码也可以指定输入输出信号的类型和位宽。然而其生成的代码实在算不上美观,复用性也很差,要实现不同滤波特性的切换就要生成多个滤波器的代码。

  出于以上考虑,自己设计实现了 FIR 滤波器的通用 verilog 代码,其滤波器参数通过接口输入,从而可以通过输入不同的参数获得相应的滤波结果。verilog 代码如下:

/*   * file         : FIR_filter.v  * author       : 今朝无言  * date		    : 2023-07-03  * version      : v1.0  * description  : FIR 滤波器  */ module FIR_filter( input							clk, input							rst_n,  input				[16*N-1:0]	filter_params,  input		signed	[15:0]		data_in, output	reg	signed	[15:0]		data_out );  parameter	N		= 32;	//滤波器参数个数 parameter	div_N	= 16;	//sum结果除 2^div_N,作为 filter 的输出  //FIR 滤波器参数 reg	signed	[15:0] b[0:N-1];  integer	m; always @(*) begin 	for(m=0; m<N; m=m+1) begin 		b[m]	<= filter_params[(m << 4) +: 16]; 	end end  reg	signed	[15:0]	shift_reg[0:N-1];  integer	i; always @(posedge clk) begin 	if(~rst_n) begin 		for(i=N-1; i>=0; i=i-1) begin 			shift_reg[i]	<= 16'd0; 		end 	end 	else begin 		for(i=N-1; i>0; i=i-1) begin 			shift_reg[i]	<= shift_reg[i-1]; 		end 		shift_reg[0]		<= data_in; 	end end  reg		signed	[31:0]	multi[0:N-1];  integer	j; always @(*) begin 	for(j=0; j<N; j=j+1) begin 		multi[j]	<= shift_reg[j] * b[j]; 		//这里可以考虑使用multiplier IP核,使用LUT搭建(而这里直接乘使用的是DSP资源,一般的FPGA芯片只有几百个) 	end end  reg		signed	[47:0]	sum;  integer	k; always @(*) begin 	sum		= 0; 	for(k=0; k<N; k=k+1) begin 		sum	= sum + multi[k]; 	end end  always @(posedge clk) begin 	data_out	<= sum[47-div_N : 32-div_N]; end  endmodule 

Lowpass Filter示例

  当滤波器阶数较高时,滤波器参数如何给出无疑是个麻烦事,因此又编写了 matlab 代码,可以一键生成所需的 .v 文件以实现参数的配置:

%-----------FIR滤波器参数(生成.v)----------------- clc,clear,close all  fs=1e6;  N=20; Wn=0.1; b = fir1(N, Wn); % 默认Hamming窗  freqz(b,1,512)  %% pramas B=floor(b*65536); B=B';  %% test t=0:1/fs:1e-3; s=(mod(t,1e-4)<5e-5)*1.0;  %s_filt=filter(B,1,s)/65536; for i=1:size(s,2)-N-1     s_filt(i)=s(i:i+N)*double(B)/65536; end  figure subplot(2,1,1) plot(t,s) subplot(2,1,2) plot(t(1:end-N-1),s_filt)  %% 生成.v filename='FIR_params'; fid=fopen(['./v/',filename,'.v'],'w');  fprintf(fid,['/* ','\n',... ' * file\t\t\t: ',filename,'.v','\n',... ' * author\t\t: 今朝无言','\n',... ' * date\t\t\t: 2023-07-04','\n',... ' * version\t\t: v1.0','\n',... ' * description\t: FIR 滤波器','\n',... ' */','\n']);  fprintf(fid,['module ',filename,'(','\n',... 'output\t[',num2str(size(B,1)*16-1),':0]\tparams\n',... ');\n\n']);  for i=1:size(B,1)     if(B(i)>=0)         hex=dec2hex(B(i),4);     else         hex=dec2hex(65536+B(i),4);     end     fprintf(fid,['assign\t','params[',...         num2str(i*16-1),':',num2str((i-1)*16),...         ']\t= 16','''','h',hex,';\n']); end  fprintf(fid,'\nendmodule\n');  fclose(fid); 

  testbench与测试结果如下

`timescale 1ns/100ps  module FIR_filter_tb();  reg		clk_100M	= 1'b1; always #5 begin 	clk_100M	<= ~clk_100M; end  localparam	N = 20;	//FIR滤波器阶数  wire	[16*(N+1)-1:0]	filter_params; FIR_params_0d1 FIR_params_inst( 	.params		(filter_params) );  reg				[15:0]	data_in; wire	signed	[15:0]	data_out;  FIR_filter #(.N(N+1)) FIR_filter_inst2( 	.clk			(clk_100M), 	.filter_params	(filter_params),		//滤波器参数  	.data_in		(data_in), 	.data_out		(data_out) );  reg		[7:0]	cnt		= 8'd0;  always @(posedge clk_100M) begin 	cnt		<= cnt + 1'b1;  	if(cnt<100)	begin 		data_in		<= -10000; 	end 	else if(cnt<200)	begin 		data_in		<= 10000; 	end 	else begin 		data_in		<= 0; 	end end  initial begin 	#10000; 	$stop; end  endmodule 

在这里插入图片描述

Hilbert 示例

  使用以上 FIR 滤波器代码,还可以实现许多其他滤波功能,比如常用的 90 度相移,可以使用 Hilbert 变换实现,Hilbert 滤波器参数的 matlab 生成代码如下

%-----------------Hilbert---------------------- clc,clear,close all  %% Hilbert N=200;  % method 1		这种直接通过 h(n) 表达式生成的更为精确,推荐 n=(1:floor((N-1)/2)); b1=(1-(-1).^n)./(pi.*n); if mod(N,2)==0     b1=[0,b1,0,-b1(end:-1:1)]'; else     b1=[0,b1,-b1(end:-1:1)]'; end  % method 2		构造 Hilbert 的频域特性,经 IFFT 获得 H=[-1j*ones(1,floor((N+1)/2)),1j*ones(1,floor(N/2))]; b2=ifft(H); b2=real(b2)';  b=b1;  freqz(b,1,100)  %% Filter fs=1e3; t=0:1/fs:1; s=5*sin(2*pi*10*t); % f >= fs/N 时,可以由很好的90度移相  s2=filter(b,1,s);  figure hold on plot(t,s,'r-') plot(t,s2,'b--') hold off  %% 量化 B=floor(b*32768); s3=filter(B,1,s)/32768;  figure hold on plot(t,s,'r-') plot(t,s3,'b--') hold off  %% 生成params.v filename='Hilbert_params'; fid=fopen(['./v/',filename,'.v'],'w');  fprintf(fid,['/* ','\n',... ' * file\t\t\t: ',filename,'.v','\n',... ' * author\t\t: 今朝无言','\n',... ' * date\t\t\t: 2023-08-04','\n',... ' * version\t\t: v1.0','\n',... ' * description\t: FIR滤波器参数(Hilbert)',... '   N=',num2str(N),'\n',... ' */','\n']);  fprintf(fid,['module ',filename,'(','\n',... 'output\t[',num2str(size(B,1)*16-1),':0]\tparams\n',... ');\n\n']);  for i=1:size(B,1)     if(B(i)>=0)         hex=dec2hex(B(i),4);     else         hex=dec2hex(65536+B(i),4);     end     fprintf(fid,['assign\t','params[',...         num2str(i*16-1),':',num2str((i-1)*16),...         ']\t= 16','''','h',hex,';\n']); end  fprintf(fid,'\nendmodule\n');  fclose(fid); 

  仿真结果如下

在这里插入图片描述

针对运算溢出问题修正后的代码

  (2024/05/27)

  以上代码没有对可能的运算溢出问题进行处理,以下代码进行了修正

/*   * file         : FIR_filter.v  * author       : 今朝无言  * date		    : 2024-03-27  * version      : v2.0  * description  : FIR 滤波器  */ `default_nettype none module FIR_filter( input	wire						clk, input	wire			[16*N-1:0]	filter_params,  input	wire	signed	[15:0]		data_in, output	reg		signed	[15:0]		data_out );  parameter	N		= 32;			//滤波器参数个数 //m阶FIR滤波器对应m+1个参数,对称  parameter	shift_N	= 16;			//滤波器缩放系数(filter_params的定点数)  localparam	NN		= clogb2(N-1); //NN为N-1的位数  //FIR 滤波器参数 reg	signed	[15:0] b[0:N-1];  integer	m; always @(*) begin 	for(m=0; m<N; m=m+1) begin 		b[m]	<= filter_params[((N-1-m) << 4) +: 16]; 	end end  //移位寄存 reg	signed	[15:0]	shift_reg[0:N-1];  integer	i; always @(posedge clk) begin 	for(i=N-1; i>0; i=i-1) begin 		shift_reg[i]	<= shift_reg[i-1]; 	end 	shift_reg[0]		<= data_in; end  //卷积-乘法 reg		signed	[31:0]	multi[0:N-1];  integer	j; always @(posedge clk) begin 	for(j=0; j<N; j=j+1) begin 		multi[j]	<= shift_reg[j] * b[j]; 	end end  //卷积-加和 reg		signed	[32+NN-1:0]	sum;  integer	k; always @(posedge clk) begin 	sum		= 0; 	for(k=0; k<N; k=k+1) begin 		sum	= sum + multi[k]; 	end end  //缩放 wire	signed	[32+NN-1:0]	sum_shift_s; assign	sum_shift_s	= sum>>>shift_N;	//>>>是算术右移,会进行符号拓展  always @(posedge clk) begin	 	if(sum_shift_s >= 32'sd32767) begin	//防止结果算术溢出 		data_out	<= 16'sd32767; 	end 	else if(sum_shift_s <= -32'sd32768) begin 		data_out	<= -16'sd32768; 	end 	else begin 		data_out	<= sum_shift_s[15:0]; 	end end  //------------------------log2----------------------------- function integer clogb2 (input integer depth); begin     for (clogb2=0; depth>0; clogb2=clogb2+1) begin         depth = depth >> 1; 	end end endfunction  endmodule 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!