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

  众所周知,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
  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 26
    评论
对于FIR(有限冲激响应)滤波器Verilog 实现,以下是一个简单的例子: ```verilog module fir_filter ( input clk, input reset, input signed [15:0] data_in, output reg signed [15:0] data_out ); parameter N = 8; // 滤波器阶数 reg signed [15:0] taps [0:N-1]; // FIR滤波器系数 reg signed [15:0] delay_line [0:N-1]; // 延迟线 always @(posedge clk or posedge reset) begin if (reset) begin for (int i = 0; i < N; i = i + 1) delay_line[i] <= 0; end else begin // 更新延迟线 for (int i = N-1; i > 0; i = i - 1) delay_line[i] <= delay_line[i-1]; delay_line[0] <= data_in; // 计算输出 data_out = 0; for (int i = 0; i < N; i = i + 1) data_out = data_out + taps[i] * delay_line[i]; end end endmodule ``` 在上述代码中,我们使用了一个带有时钟和复位信号的 FIR 滤波器模块。输入数据以有符号的 16 位整数表示,输出数据也是相同的格式。滤波器的阶数由参数 `N`定义。 在时钟的上升沿或复位信号的上升沿触发的 `always` 块内,我们首先处理复位情况,在复位时将延迟线中的所有值设置为 0。然后,在非复位情况下,我们更新延迟线并计算输出值。 延迟线的更新是通过循环将每个元素向后移动一个位置,最后将新的输入数据存储在第一个位置。输出值则是通过将每个延迟线元素与对应的系数相乘,并将所有结果相加得到的。 请注意,这只是一个基本的 FIR 滤波器实现示例,你可以根据具体需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今朝无言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值