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

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

出于以上考虑,自己设计实现了 FIR 滤波器的通用 verilog 代码,其滤波器参数通过接口输入,从而可以通过输入不同的参数获得相应的滤波结果。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 文件以实现参数的配置:

matlab 复制代码
%-----------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与测试结果如下

verilog 复制代码
`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 生成代码如下

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);

仿真结果如下

相关推荐
Seraphina_Lily9 分钟前
CPU–FPGA–DSP 异构系统中的总线接口选型——为什么 CPU 用 eLBC,而 DSP 用 XINTF?
fpga开发
GateWorld13 分钟前
FPGA开发十年心路
fpga开发
ALINX技术博客16 小时前
【ALINX 教程】FPGA Multiboot 功能实现——基于 ALINX Artix US+ AXAU25 开发板
fpga开发·fpga
Genevieve_xiao18 小时前
【verilog】如何一小时成为verilog高手(并非
fpga开发
从此不归路19 小时前
FPGA 结构与 CAD 设计(第3章)上
ide·fpga开发
Aaron158821 小时前
基于VU13P在人工智能高速接口传输上的应用浅析
人工智能·算法·fpga开发·硬件架构·信息与通信·信号处理·基带工程
碎碎思21 小时前
在 FPGA 上实现并行脉冲神经网络(Spiking Neural Net)
人工智能·深度学习·神经网络·机器学习·fpga开发
集芯微电科技有限公司1 天前
替代HT6310/KP3310离线式AC-DC无感线性稳压器
数据结构·人工智能·单片机·嵌入式硬件·fpga开发
minglie11 天前
Zynq上UART/IIC/SPI的24个实验-第0课:目录
fpga开发
FPGA小c鸡1 天前
FPGA摄像头到屏幕完整链路:从OV5640采集到HDMI实时显示(附完整工程代码)
fpga开发