verilog实现FIR滤波系数生成(阶数,FIR滤波器类型及窗函数可调)

  在以往采用 FPGA 实现的 FIR 滤波功能,滤波器系数是通过 matlab 计算生成,然后作为固定参数导入到 verilog 程序中,这尽管简单,但灵活性不足。在某些需求下(例如捕获任意给定台站信号)需要随时修改滤波器的中心频率、带宽等信息,这要么通过上位机计算系数后更新到 FPGA 端(但并非所有设备都具备配套的上位机),要么直接在 FPGA 端计算并更新滤波器系数。本文对后者进行实现。

  计算 FIR 滤波器系数,主要包括两个方面的计算:窗函数计算,滤波器系数计算

窗函数生成

几种常用窗函数

  首先给出几种常用的窗函数的表达式,这里不对窗函数细节进行讨论:

  • 矩形窗

w ( n ) = 1.0 , n = 0 , 1 , . . . , N − 1 w(n) = 1.0,\ n=0,1,...,N-1 w(n)=1.0, n=0,1,...,N1

  • 三角窗

w ( n ) = 1 − ∣ 1 − 2 n N − 1 ∣ , n = 0 , 1 , . . . , N − 1 w(n)=1 - |1 - \frac{2n}{N - 1}|,\ n=0,1,...,N-1 w(n)=1∣1N12n, n=0,1,...,N1

  • 图基窗 Tukey

w ( n ) = { 0.5 − 0.5 cos ⁡ ( n π k + 1 ) , 0 ≤ n ≤ k 1.0 , k < n ≤ N − k − 2 0.5 − 0.5 cos ⁡ ( π ( N − n − 1 ) k + 1 ) , N − k − 2 < n ≤ N − 1 , where  k = N − 2 10 w(n)= \left\{ \begin{aligned} 0.5 - 0.5\cos(\frac{n\pi}{k + 1}),&\ 0\le n\le k\\ 1.0,&\ k<n\le N-k-2\\ 0.5 - 0.5\cos(\frac{\pi(N - n - 1)}{k + 1}),&\ N-k-2<n\le N-1\ \end{aligned} \right. ,\ \text{where}\ k=\frac{N-2}{10} w(n)= 0.50.5cos(k+1),1.0,0.50.5cos(k+1π(Nn1)), 0nk k<nNk2 Nk2<nN1 , where k=10N2

  • 汉宁窗 Hann

w ( n ) = 0.5 × [ 1.0 − cos ⁡ ( 2 π n N − 1 ) ] , n = 0 , 1 , . . . , N − 1 w(n)=0.5 \times [1.0 - \cos(\frac{2\pi n}{N - 1})],\ n=0,1,...,N-1 w(n)=0.5×[1.0cos(N12πn)], n=0,1,...,N1

  • 汉明窗 Hamming

w ( n ) = 0.54 − 0.46 cos ⁡ ( 2 π n N − 1 ) , n = 0 , 1 , . . . , N − 1 w(n)=0.54 - 0.46\cos(\frac{2\pi n}{N - 1}),\ n=0,1,...,N-1 w(n)=0.540.46cos(N12πn), n=0,1,...,N1

  • 布莱克曼窗 Blackman

w ( n ) = 0.42 − 0.5 cos ⁡ ( 2 π n N − 1 ) + 0.08 cos ⁡ ( 4 π n N − 1 ) , n = 0 , 1 , . . . , N − 1 w(n)=0.42 - 0.5\cos(\frac{2\pi n}{N - 1}) + 0.08\cos(\frac{4\pi n}{N-1}),\ n=0,1,...,N-1 w(n)=0.420.5cos(N12πn)+0.08cos(N14πn), n=0,1,...,N1

verilog 实现

  可以观察到,Tukey、Hann、Hamming 和 Blackman 窗都用到了余弦函数,这可以用正余弦查找表实现,下面代码中会用到这一模块,可参考我这篇博文;窗函数生成器代码如下

/* * file			: FIR_windows_generator.v* author		: 今朝无言* lab			: WHU-EIS-LMSWE* date			: 2024-09-26* version		: v1.0* description	: 生成指定阶数、指定类型的窗函数*/
`default_nettype none
module FIR_windows_generator(
input	wire					clk,
input	wire					rst_n,input	wire					en,			//上升沿触发窗口计算
input	wire			[3:0]	win_type,	//窗口类型,1:矩形窗,2:图基窗Tukey,3:三角窗,4:汉宁窗Hann,5:海明窗Hamming,6:布莱克曼窗Blackman,(7:凯塞窗kaiser, 暂未实现)
input	wire			[15:0]	n,			//窗口长度
input	wire			[15:0]	i,			//窗口索引值,0 ~ n-1
//input	wire	signed	[15:0]	beta,		//kaiser窗的参数beta,win_type=7时需要这个参数,其他情况可任意给值output	wire					busy,		//指示模块是否计算完成
output	wire	signed	[15:0]	win
);reg		signed	[15:0]	win_buf		= 16'sd256;		//8-8有符号定点数
reg		signed	[15:0]	win_buf_d0	= 16'sd256;
reg						busy_buf	= 1'b0;assign	win		= win_buf_d0;
assign	busy	= busy_buf;localparam	S_IDLE	= 4'h1;
localparam	S_CAL	= 4'h2;
localparam	S_END	= 4'h4;reg		[3:0]	state	= S_IDLE;
reg		[3:0]	next_state;always @(posedge clk) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(en_pe) beginnext_state	<= S_CAL;endelse beginnext_state	<= S_IDLE;endendS_CAL: begincase(win_type)4'd1: begin		//矩形窗next_state	<= S_END;end4'd2: begin		//图基窗if(cnt >= 4'd4) beginnext_state	<= S_END;endelse beginnext_state	<= S_CAL;endend4'd3: begin		//三角窗if(cnt >= 4'd1) beginnext_state	<= S_END;endelse beginnext_state	<= S_CAL;endend4'd4: begin		//汉宁窗if(cnt >= 4'd3) beginnext_state	<= S_END;endelse beginnext_state	<= S_CAL;endend4'd5: begin		//海明窗if(cnt >= 4'd3) beginnext_state	<= S_END;endelse beginnext_state	<= S_CAL;endend4'd6: begin		//布莱克曼窗if(cnt >= 4'd5) beginnext_state	<= S_END;endelse beginnext_state	<= S_CAL;endend// 4'd7: begin		//凯塞窗		这个涉及到循环逼近bessel函数和sqrt计算,FPGA比较麻烦,就先不实现这个窗口类型了// 	next_state	<= S_END;// enddefault: beginnext_state	<= S_END;endendcaseendS_END: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
end//en 边沿检测
wire	en_pe;
detect_sig_edge detect_sig_edge_inst(.clk		(clk),		//工作时钟.sig		(en),		//待检测信号.sig_pe		(en_pe),	//信号上升沿.sig_ne		(),			//下降沿.sig_de		()			//双边沿
);//cnt 控制读取cosin结果,以计算窗口值
reg		[3:0]	cnt	= 4'd0;
always @(posedge clk) begincase(state)S_IDLE: begincnt		<= 4'd0;endS_CAL: begincnt		<= cnt + 1'b1;enddefault: begincnt		<= 4'd0;endendcase
end//win_buf
reg		signed	[31:0]	multi_tmp	= 32'sd0;
always @(posedge clk) beginif(~rst_n) beginwin_buf		<= 16'sd256;	//1.0endelse case(state)S_CAL: begincase(win_type)4'd1: begin								//矩形窗win_buf		<= 16'sd256;end4'd2: begin								//图基窗if(cnt == 4'd4) beginif(i <= k) beginwin_buf		<= (16'sd256 - (cos_val_s >>> 7)) >>> 1;endelse if(i > n - k - 4'd2) beginwin_buf		<= (16'sd256 - (cos_val_s >>> 7)) >>> 1;endelse beginwin_buf		<= 16'sd256;endendelse beginwin_buf		<= win_buf;endend4'd3: begin								//三角窗if(cnt == 4'd0) beginmulti_tmp	<= 16'sd512 * i / (n - 1'b1);endelse if(cnt == 4'd1) beginwin_buf 	<= 16'sd256 - abs(16'sd256 - multi_tmp[15:0]);endelse beginwin_buf		<= win_buf;endend4'd4: begin								//汉宁窗if(cnt == 4'd3) beginwin_buf		<= 16'sd128 - (cos_val_s >>> 8);	//0.5 * (1.0 - cos(2 * i * pi / (n - 1)));endelse beginwin_buf		<= win_buf;endend4'd5: begin								//海明窗if(cnt == 4'd3) beginwin_buf		<= 16'sd138 - ((16'sd118 * (cos_val_s >>> 7)) >>> 8);	//0.54 - 0.46 * cos(2 * i * pi / (n - 1));endelse beginwin_buf		<= win_buf;endend4'd6: begin								//布莱克曼窗if(cnt == 4'd3) beginwin_buf		<= 16'sd108 - (cos_val_s >>> 8);endelse if(cnt == 4'd5) beginwin_buf		<= win_buf + ((16'sd82 * (cos_val_s >>> 7)) >>> 10);endelse beginwin_buf		<= win_buf;endend// 4'd7: begin							//凯塞窗// 	win_buf		<= 16'sd0;// enddefault: beginwin_buf		<= 16'sd256;endendcaseenddefault: beginwin_buf		<= win_buf;endendcase
end//busy_buf
always @(*) begincase(state)S_IDLE: beginbusy_buf	<= 1'b0;enddefault: beginbusy_buf	<= 1'b1;endendcase
end//cos_phase
reg		[15:0]	k	= 16'd0;
always @(posedge clk) begincase(state)S_CAL: begincase(win_type)4'd1: begin								//矩形窗cos_phase	<= 16'd0;end4'd2: begin								//图基窗if(cnt == 4'd0) begink 			<= (n - 2'd2) / 4'd10;cos_phase	<= cos_phase;endelse if(cnt == 4'd1) begink			<= k;if(i <= k) begincos_phase	<= i * (16'd32768 / (k + 1'b1)) + 16'd16384;endelse if(i > n - k - 2'd2) begincos_phase	<= (n - i - 1'b1) * (16'd32768 / (k + 1'b1)) + 16'd16384;endelse begincos_phase	<= 16'd0;endendelse begincos_phase	<= cos_phase;k			<= k;endend4'd3: begin								//三角窗cos_phase	<= 16'd0;end4'd4: begin								//汉宁窗cos_phase	<= 2'd2 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;end4'd5: begin								//海明窗cos_phase	<= 2'd2 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;end4'd6: begin								//布莱克曼窗if(cnt == 4'd0) begincos_phase	<= 2'd2 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;endelse if(cnt == 4'd2) begincos_phase	<= 4'd4 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;endelse begincos_phase	<= cos_phase;endend// 4'd7: begin							//凯塞窗// 	cos_phase	<= 16'd0;// enddefault: begincos_phase	<= 16'd0;endendcaseenddefault: begincos_phase	<= cos_phase;endendcase
end//sin_rom
reg		[15:0]	cos_phase	= 16'd0;
wire	[15:0]	cos_out;
sin_gen sin_gen_inst(.clk		(clk),.phase		(cos_phase),		//相位,0~65535对应[0~2pi).sin_out	(cos_out)			//0~65535
);wire	signed	[15:0]	cos_val_s;
assign	cos_val_s	= {~cos_out[15], cos_out[14:0]};//win_buf_d0
always @(posedge clk) begincase(state)S_END: beginwin_buf_d0	<= win_buf;enddefault: beginwin_buf_d0	<= win_buf_d0;endendcase
end//------------------func------------------------------
function signed [15:0] abs(input signed [15:0] a);beginabs = (a >= 16'sd0)? a : -a;end
endfunctionendmodule

测试

  testbench 如下

`timescale 1ns/100psmodule FIR_windows_generate_tb();reg		clk_100M	= 1'b1;
always #5 beginclk_100M	<= ~clk_100M;
endreg				rst_n 				= 1'b1;reg						en;			//上升沿触发窗口计算
reg				[3:0]	win_type;	//窗口类型,1:矩形窗,2:图基窗,3:三角窗,4:汉宁窗,5:海明窗,6:布莱克曼窗
reg				[15:0]	n;			//滤波器阶数
reg				[15:0]	i;			//窗口索引值,0 ~ n-1wire					busy;
wire	signed	[15:0]	win;FIR_windows_generator FIR_windows_generator_inst(.clk			(clk_100M),.rst_n			(rst_n),.en				(en),			//上升沿触发窗口计算.win_type		(win_type),		//窗口类型,1:矩形窗,2:图基窗,3:三角窗,4:汉宁窗,5:海明窗,6:布莱克曼窗.n				(n),			//滤波器阶数.i				(i),			//窗口索引值,0 ~ n-1.busy			(busy),			//指示模块是否计算完成.win			(win)
);//进行一组FIR_win的计算
task cal_win;input	[3:0]	WIN_TYPE;input	[15:0]	N;integer			k;beginn			= N;win_type	= WIN_TYPE;#10;for (k = 0; k < N; k = k + 1'b1) begini	= k;en	= 1'b1;wait(busy);#10;en	= 1'b0;wait(~busy);#10;endend
endtaskinitial beginrst_n		<= 1'b0;en			<= 1'b0;win_type	<= 1'b1;n			<= 16'd16;i			<= 16'd0;#100;rst_n		<= 1'b1;#100;cal_win(1, 64);		//矩形窗#100;cal_win(3, 64);		//三角窗#100;cal_win(2, 64);		//图基窗#100;cal_win(4, 64);		//汉宁窗#100;cal_win(5, 64);		//海明窗#100;cal_win(6, 64);		//布莱克曼窗#200;$stop;
endendmodule

  仿真结果如下

在这里插入图片描述

滤波器系数计算

FIR 滤波器冲激响应

  这里给出理想低通 FIR 滤波器,理想高通 FIR 滤波器、理想带通 FIR 滤波器、理想带阻 FIR 滤波器的冲激响应函数表达式:

  • 低通

h L P ( n ) = sin ⁡ ( 2 π f c f s s ) π s , where  s = ∣ n − N 2 ∣ , n = 0 , 1 , . . . , N h_{LP}(n)=\frac{\sin(\frac{2\pi f_{c}}{f_s}s)}{\pi s},\ \text{where}\ s=|n-\frac{N}{2}|,\ n=0,1,...,N hLP(n)=πssin(fs2πfcs), where s=n2N, n=0,1,...,N

其中 f s f_s fs 为采样率, f c f_c fc 为截止频率。

  • 高通

h H P ( n ) = sin ⁡ ( π s ) − sin ⁡ ( 2 π f c f s s ) π s , where  s = ∣ n − N 2 ∣ , n = 0 , 1 , . . . , N h_{HP}(n)=\frac{\sin(\pi s)-\sin(\frac{2\pi f_{c}}{f_s}s)}{\pi s},\ \text{where}\ s=|n-\frac{N}{2}|,\ n=0,1,...,N hHP(n)=πssin(πs)sin(fs2πfcs), where s=n2N, n=0,1,...,N

  • 带通

h B P ( n ) = sin ⁡ ( 2 π f c 2 f s s ) − sin ⁡ ( 2 π f c 1 f s s ) π s , where  s = ∣ n − N 2 ∣ , n = 0 , 1 , . . . , N h_{BP}(n)=\frac{\sin(\frac{2\pi f_{c2}}{f_s}s)-\sin(\frac{2\pi f_{c1}}{f_s}s)}{\pi s},\ \text{where}\ s=|n-\frac{N}{2}|,\ n=0,1,...,N hBP(n)=πssin(fs2πfc2s)sin(fs2πfc1s), where s=n2N, n=0,1,...,N

其中 f c 1 f_{c1} fc1 为下截止频率, f c 2 f_{c2} fc2 为上截止频率。

  • 带阻

h B S ( n ) = sin ⁡ ( 2 π f c 1 f s s ) + sin ⁡ ( π s ) − sin ⁡ ( 2 π f c 2 f s s ) π s , where  s = ∣ n − N 2 ∣ , n = 0 , 1 , . . . , N h_{BS}(n)=\frac{\sin(\frac{2\pi f_{c1}}{f_s}s)+\sin(\pi s)-\sin(\frac{2\pi f_{c2}}{f_s}s)}{\pi s},\ \text{where}\ s=|n-\frac{N}{2}|,\ n=0,1,...,N hBS(n)=πssin(fs2πfc1s)+sin(πs)sin(fs2πfc2s), where s=n2N, n=0,1,...,N

  在实际设计中,FIR 滤波的数据要加窗以将无限冲激的 sinc 函数截断为有限长(即窗函数法 FIR 滤波器设计),因此将以上冲激响应与窗函数相乘即可。在计算以上冲激函数时,当阶数 N 为偶数时,则会在 n = N / 2 n=N/2 n=N/2 时出现除零的问题,此时利用洛必达法则进行计算即可。

verilog 实现

/* * file			: FIR_firwin_generator.v* author		: 今朝无言* lab			: WHU-EIS-LMSWE* date			: 2024-09-26* version		: v1.0* description	: 生成指定阶数、指定类型的FIR滤波窗口*/
`default_nettype none
module FIR_firwin_generator(
input	wire					clk,
input	wire					rst_n,input	wire					en,			//上升沿触发窗口计算
input	wire			[1:0]	band_type,	//滤波器类型,0:低通LP,1:高通HP,2:带通BP,3:带阻BS
input	wire	signed	[15:0]	fs,			//采样率,注意fln,fhn均应小于fs/2
input	wire	signed	[15:0]	fln,		//滤波器下频点,LP,HP,BP,BS均会用到
input	wire	signed	[15:0]	fhn,		//滤波器上频点,BP,BS用到
input	wire			[3:0]	win_type,	//窗函数类型,1:矩形窗,2:图基窗Tukey,3:三角窗,4:汉宁窗Hann,5:海明窗Hamming,6:布莱克曼窗Blackman
input	wire	signed	[15:0]	n,			//滤波器阶数   注意,HP/BS的阶数应为偶数,奇数阶的系数不可靠
input	wire	signed	[15:0]	i,			//0~n,共n+1个值output	wire					busy,		//指示模块是否计算完成
output	wire	signed	[15:0]	firwin
);reg		signed	[31:0]	firwin_buf		= 32'sd256;
reg		signed	[15:0]	firwin_buf_d0	= 16'sd256;		//8-8有符号定点数
reg						busy_buf		= 1'b0;assign	firwin	= firwin_buf_d0;
assign	busy	= busy_buf;localparam	S_IDLE	= 4'h1;
localparam	S_CAL	= 4'h2;
localparam	S_END	= 4'h4;reg		[3:0]	state	= S_IDLE;
reg		[3:0]	next_state;always @(posedge clk) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(en_pe) beginnext_state	<= S_CAL;endelse beginnext_state	<= S_IDLE;endendS_CAL: beginif(cnt >= 4'd12) begin		//最迟在cnt=7可以读取窗函数值并计算firwin,随后本模块可计算firwinnext_state	<= S_END;endelse beginnext_state	<= S_CAL;endendS_END: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
end//firwin_buf
always @(posedge clk) begincase(state)S_CAL: begincase(band_type)2'd0: begin			//LPif(cnt == 4'd3) beginif((~n[0]) && (i_buf == n/4'sd2)) begin	//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值firwin_buf		<= (({fln, 16'b0} / fs) * 16'sd804) >>> 8;		//3.1415 = 804/256endelse beginfirwin_buf		<= sin_val_s / s_mlti2 * 4'sd2;endendelse if(cnt == 4'd7) beginfirwin_buf		<= (firwin_buf * win) >>> 16;endelse beginfirwin_buf		<= firwin_buf;endend2'd1: begin			//HPif(cnt == 4'd3) beginif((~n[0]) && (i_buf == n/4'sd2)) begin	//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值firwin_buf		<= ((32'sd32768 - {fln, 16'b0} / fs) * 16'sd804) >>> 8;endelse beginfirwin_buf		<= sin_val_s;endendelse if(cnt == 4'd7) beginif((~n[0]) && (i_buf == n/4'sd2)) beginfirwin_buf		<= firwin_buf;endelse beginfirwin_buf		<= (firwin_buf - sin_val_s) / s_mlti2 * 4'sd2;endendelse if(cnt == 4'd8) beginfirwin_buf		<= (firwin_buf * win) >>> 16;endelse beginfirwin_buf		<= firwin_buf;endend2'd2: begin			//BPif(cnt == 4'd3) beginif((~n[0]) && (i_buf == n/4'sd2)) begin	//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值firwin_buf		<= ((({fhn, 16'b0} - {fln, 16'b0}) / fs) * 16'sd804) >>> 8;endelse beginfirwin_buf		<= sin_val_s;endendelse if(cnt == 4'd7) beginif((~n[0]) && (i_buf == n/4'sd2)) beginfirwin_buf		<= firwin_buf;endelse beginfirwin_buf		<= (firwin_buf - sin_val_s) / s_mlti2 * 4'sd2;endendelse if(cnt == 4'd8) beginfirwin_buf		<= (firwin_buf * win) >>> 16;endelse beginfirwin_buf		<= firwin_buf;endend2'd3: begin			//BSif(cnt == 4'd3) beginif((~n[0]) && (i_buf == n/4'sd2)) begin	//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值firwin_buf		<= (({fln, 16'b0} / fs + 32'sd32768 - {fhn, 16'b0} / fs) * 16'sd804) >>> 8;endelse beginfirwin_buf		<= sin_val_s;endendelse if(cnt == 4'd7) beginif((~n[0]) && (i_buf == n/4'sd2)) beginfirwin_buf		<= firwin_buf;endelse beginfirwin_buf		<= firwin_buf + sin_val_s;endendelse if(cnt == 4'd11) beginif((~n[0]) && (i_buf == n/4'sd2)) beginfirwin_buf		<= firwin_buf;endelse beginfirwin_buf		<= (firwin_buf - sin_val_s) / s_mlti2 * 4'sd2;endendelse if(cnt == 4'd12) beginfirwin_buf		<= (firwin_buf * win) >>> 16;endelse beginfirwin_buf		<= firwin_buf;endenddefault: beginfirwin_buf	<= firwin_buf;endendcaseenddefault: beginfirwin_buf		<= firwin_buf;endendcase
end//i_buf
reg		signed	[15:0]	i_buf;
always @(posedge clk) beginif(i > (n >>> 1)) begini_buf	<= n - i;	//滤波器是对称的,这里处理后利用i_buf计算滤波器系数endelse begini_buf	<= i;end
end//sin_phase
reg		[31:0]	multi_tmp;
always @(posedge clk) begincase(state)S_CAL: beginsin_phase	<= multi_tmp[15:0];enddefault: beginsin_phase	<= sin_phase;endendcase
endreg		signed	[15:0]	s_mlti2;
always @(*) begins_mlti2	<= n - i_buf * 4'sd2;
endalways @(*) begincase(state)S_CAL: begincase(band_type)2'd0: begin			//LPmulti_tmp	<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;end2'd1: begin			//HPif(cnt <= 4'd3) beginmulti_tmp	<= {s_mlti2, 14'b0};endelse beginmulti_tmp	<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;endend2'd2: begin			//BPif(cnt <= 4'd3) beginmulti_tmp	<= (({fhn, 16'b0} / fs) * s_mlti2) >> 1;endelse beginmulti_tmp	<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;endend2'd3: begin			//BSif(cnt <= 4'd3) beginmulti_tmp	<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;endelse if(cnt <= 4'd7) beginmulti_tmp	<= {s_mlti2, 14'b0};endelse beginmulti_tmp	<= (({fhn, 16'b0} / fs) * s_mlti2) >> 1;endenddefault: beginmulti_tmp	<= 32'd0;endendcaseenddefault: beginmulti_tmp	<= 32'd0;endendcase
end//窗函数
wire	signed	[15:0]	win;
FIR_windows_generator FIR_windows_generator_inst(.clk		(clk),.rst_n		(rst_n),.en			(en),			//上升沿触发窗口计算.win_type	(win_type),		//窗口类型,1:矩形窗,2:图基窗Tukey,3:三角窗,4:汉宁窗Hann,5:海明窗Hamming,6:布莱克曼窗Blackman.n			(n + 1'b1),		//窗口长度,=滤波器阶数+1.i			(i),			//窗口索引值.busy		(),				//指示模块是否计算完成.win		(win)
);//en 边沿检测
wire	en_pe;
detect_sig_edge detect_sig_edge_inst(.clk		(clk),		//工作时钟.sig		(en),		//待检测信号.sig_pe		(en_pe),	//信号上升沿.sig_ne		(),			//下降沿.sig_de		()			//双边沿
);//cnt 控制计算流程
reg		[3:0]	cnt	= 4'd0;
always @(posedge clk) begincase(state)S_IDLE: begincnt		<= 4'd0;endS_CAL: begincnt		<= cnt + 1'b1;enddefault: begincnt		<= 4'd0;endendcase
end//busy_buf
always @(*) begincase(state)S_IDLE: beginbusy_buf	<= 1'b0;enddefault: beginbusy_buf	<= 1'b1;endendcase
end//sin_rom
reg		[15:0]	sin_phase	= 16'd0;
wire	[15:0]	sin_out;
sin_gen sin_gen_inst(.clk		(clk),.phase		(sin_phase),		//相位,0~65535对应[0~2pi).sin_out	(sin_out)			//0~65535
);wire	signed	[15:0]	sin_val_s;
assign	sin_val_s	= {~sin_out[15], sin_out[14:0]};//firwin_buf_d0
always @(posedge clk) begincase(state)S_END: beginfirwin_buf_d0	<= firwin_buf[15:0];enddefault: beginfirwin_buf_d0	<= firwin_buf_d0;endendcase
endendmodule

测试

  testbench 如下

`timescale 1ns/100psmodule FIR_firwin_generate_tb();reg		clk_100M	= 1'b1;
always #5 beginclk_100M	<= ~clk_100M;
endreg						rst_n 	= 1'b1;reg						en;			//上升沿触发窗口计算reg				[1:0]	band_type;	//滤波器类型,0:低通LP,1:高通HP,2:带通BP,3:带阻BS
reg				[15:0]	fs;			//采样率,注意fln,fhn均应小于fs/2
reg				[15:0]	fln;		//滤波器下频点,LP,HP,BP,BS均会用到
reg				[15:0]	fhn;		//滤波器上频点,BP,BS用到
reg				[3:0]	win_type;	//窗口类型,1:矩形窗,2:图基窗,3:三角窗,4:汉宁窗,5:海明窗,6:布莱克曼窗
reg				[15:0]	n;			//滤波器阶数
reg				[15:0]	i;			//窗口索引值,0 ~ nwire					busy;
wire	signed	[15:0]	firwin;FIR_firwin_generator FIR_firwin_generator_inst(.clk			(clk_100M),.rst_n			(rst_n),.en				(en),			//上升沿触发窗口计算.band_type		(band_type),	//滤波器类型,0:低通LP,1:高通HP,2:带通BP,3:带阻BS.fs				(fs),			//采样率,注意fln,fhn均应小于fs/2.fln			(fln),			//滤波器下频点,LP,HP,BP,BS均会用到.fhn			(fhn),			//滤波器上频点,BP,BS用到.win_type		(win_type),		//窗函数类型,1:矩形窗,2:图基窗Tukey,3:三角窗,4:汉宁窗Hann,5:海明窗Hamming,6:布莱克曼窗Blackman.n				(n),			//滤波器阶数.i				(i),			//0~n,共n+1个值.busy			(busy),.firwin			(firwin)
);//进行一组FIR_win的计算
task cal_firwin;input	[1:0]	BAND_TYPE;input	[15:0]	Fs;input	[15:0]	Fln;input	[15:0]	Fhn;input	[3:0]	WIN_TYPE;input	[15:0]	N;integer			k;beginband_type	= BAND_TYPE;fs			= Fs;fln			= Fln;fhn			= Fhn;win_type	= WIN_TYPE;n			= N;#10;for (k = 0; k <= N; k = k + 1'b1) begini	= k;en	= 1'b1;wait(busy);#10;en	= 1'b0;wait(~busy);#10;endend
endtaskinitial beginrst_n		<= 1'b0;en			<= 1'b0;band_type	<= 2'd0;fs			<= 16'd100;fln			<= 16'd10;fhn			<= 16'd60;win_type	<= 4'd1;n			<= 16'd64;i			<= 16'd0;#100;rst_n		<= 1'b1;#100;//可与matlab函数fir1(fir_N, Wn, band_type, win)的结果比对   注意其中的Wn是 [fln/f_ny, fhn/f_ny],其中f_ny=fs/2cal_firwin(2'd0, 16'd1000, 16'd50, 16'd100, 4'd1, 16'd64);	//LP,矩形窗#200;cal_firwin(2'd0, 16'd1000, 16'd50, 16'd100, 4'd5, 16'd64);	//LP,海明窗#200;cal_firwin(2'd0, 16'd1000, 16'd50, 16'd100, 4'd6, 16'd64);	//LP,布莱克曼窗#200;cal_firwin(2'd0, 16'd1000, 16'd50, 16'd100, 4'd6, 16'd63);	//LP,布莱克曼窗#200;cal_firwin(2'd1, 16'd1000, 16'd50, 16'd100, 4'd1, 16'd64);	//HP,矩形窗#200;cal_firwin(2'd1, 16'd1000, 16'd50, 16'd100, 4'd1, 16'd63);	//HP,矩形窗	HP的阶数应为偶数,否则系数不可靠,matlab fir1也是只能生成偶数阶的HP#200;cal_firwin(2'd2, 16'd1000, 16'd50, 16'd100, 4'd1, 16'd64);	//BP,矩形窗#200;cal_firwin(2'd2, 16'd1000, 16'd50, 16'd100, 4'd1, 16'd63);	//BP,矩形窗#200;cal_firwin(2'd2, 16'd1000, 16'd50, 16'd100, 4'd5, 16'd63);	//BP,海明窗#200;cal_firwin(2'd3, 16'd1000, 16'd50, 16'd100, 4'd1, 16'd64);	//BS,矩形窗#200;cal_firwin(2'd3, 16'd1000, 16'd50, 16'd100, 4'd1, 16'd63);	//BS,矩形窗	BS的阶数也应为偶数#200;cal_firwin(2'd3, 16'd1000, 16'd50, 16'd100, 4'd5, 16'd64);	//BS,海明窗#200;#200;$stop;
endendmodule

  仿真结果如下

在这里插入图片描述

读者可与 Matlab 的 fir1 函数结果进行比对。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1550480.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【教程】57帧! Mac电脑流畅运行黑神话悟空

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、先安装CrossOver。网上有许多和谐版&#xff0c;可自行搜索。&#xff08;pd虚拟机里运行黑神话估计够呛的&#xff09; 2、运行CrossOver&#xf…

12、echarts 没有显示折线图

一、问题描述 echarts 没有显示折线图&#xff0c;但是&#xff0c;有数据显示&#xff1a; 看图表展示&#xff0c;y轴数据全部没有显示&#xff0c;直接可以判定是数据结构出问题了。 检查 series.data[] 数据结构&#xff1a; dataList [{"dateStr":"202…

鸿蒙开发(NEXT/API 12)【硬件(传感器开发3)】传感器服务

场景介绍 当设备需要获取传感器数据时&#xff0c;可以使用sensor模块&#xff0c;例如&#xff1a;通过订阅方向传感器数据感知用户设备当前的朝向&#xff0c;通过订阅计步传感器数据统计用户的步数等。 函数说明 名称描述OH_Sensor_GetInfos(Sensor_Info **infos, uint32…

GAMES101(作业8)

作业8 题目&#xff1a; 模拟绳子动画&#xff0c;包括基于物理的&#xff0c;和非物理的&#xff0c;应该修改的函数是:rope.cpp 中的void Rope::simulateEuler(... Rope::rope(...)&#xff0c;&#xff0c;void Rope::simulateVerlet(...) 代码框架&#xff1a; main:负…

调试分析:[跳数度量]更改为[距离度量]后的 routing_bellmanford 算法

回顾复习2023年8月的《★修改Exata6.2源码&#xff1a;〔修改Bellmanford最短路径路由的衡量标准从【路由跳数】改为【“路由器节点间的物理距离”】&#xff0c;并动画演示〕》&#xff0c;VS2015调试Exata&#xff0c;跟踪调试修改后的[ routing_bellmanford.cpp ]源码&#…

Redis-常见数据类型(修改ing)

1. 预备知识 redis按照键值对的方式存储数据 1.1 基本全局命令 KEYS 返回所有满⾜样式&#xff08;pattern&#xff09;的key,⽀持如下统配样式: h?llo 匹配hello,hallo,hxlloh*llo 匹配hllo,heeeelloh[ae]llo 只匹配hallo helloh[^e]llo 匹配除hello,heee..llo以外的h[a…

Java-数据结构-Map和Set-(二)-哈希表 |ू・ω・` )

文本目录&#xff1a; ❄️一、哈希表&#xff1a; ☑ 1、概念&#xff1a; ☑ 2、冲突-概念&#xff1a; ☑ 3、冲突-避免&#xff1a; ☞ 1&#xff09;、避免冲突-哈希函数的设计&#xff1a; ☞ 2&#xff09;、避免冲突-负载因子调节(重点)&#xff1a; ☑ 4、冲突-解决&…

宠物空气净化器该怎么选?希喂、美的、有哈这三款有推荐的吗?

终于要到国庆了&#xff0c;这可是打工人除春节外最长的假期&#xff01;在外上班后&#xff0c;回家的次数越来越少了&#xff0c;这次国庆肯定要回去陪陪父母。这票是真难买啊&#xff0c;候补了我一个多星期才买到。本来以为最困难的问题已经解决了&#xff0c;又想到我家猫…

有通话质量更好的蓝牙耳机推荐吗?高品质的平价开放式耳机推荐

个人认为开放式耳机在通话方面还是表现不错的&#xff0c;主要有以下几个原因&#xff1a; 首先&#xff0c;在麦克风设计与配置方面&#xff1a; 拥有高品质麦克风硬件。优质的开放式耳机往往会配备高性能的麦克风&#xff0c;这些麦克风灵敏度较高&#xff0c;能够精准地捕捉…

情感短视频素材下载推荐

在制作热门的情感短视频时&#xff0c;优质的素材是不可或缺的。作为一名资深视频剪辑师&#xff0c;今天我将为你推荐几个可以下载高清无水印情感视频素材的网站&#xff0c;助你轻松找到创作灵感。 蛙学网 蛙学网是国内领先的视频素材平台&#xff0c;专注于情感和治愈类视频…

TypeScript是基于LLM上层研发的受益者

TypeScript优在哪里 TypeScript是一种由微软开发的开源编程语言&#xff0c;它是JavaScript的一个超集&#xff0c;添加了类型系统和一些其他特性。TypeScript的优势在于&#xff1a; 静态类型检查&#xff1a;TypeScript的最大卖点是它的静态类型系统。这允许开发者在编写代码…

11.C++程序中的常用函数

我们将程序中反复执行的代码封装到一个代码块中&#xff0c;这个代码块就被称为函数&#xff0c;它类似于数学中的函数&#xff0c;在C程序中&#xff0c;有许多由编译器定义好的函数&#xff0c;供大家使用。下面就简单说一下&#xff0c;C中常用的函数。 1.sizeof sizeof函…

AI预测福彩3D采取888=3策略+和值012路或胆码测试9月28日新模型预测第101弹

经过100多期的测试&#xff0c;当然有很多彩友也一直在观察我每天发的预测结果&#xff0c;得到了一个非常有价值的信息&#xff0c;那就是9码定位的命中率非常高&#xff0c;100多期一共只错了12次&#xff0c;这给喜欢打私房菜的朋友提供了极高价值的预测结果~当然了&#xf…

Learn OpenGL In Qt之炫酷进度条

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~ 公众号&#xff1a; C学习与探索 | 个人主页&#xff1a; rainInSunny | 个人专栏&#xff1a; Learn OpenGL In Qt 文章目录 设计实现目录结构需要哪些类接口设计关键函数 实现效果Shader解析GLSL基本函数clampsmoothstep 实现分析效…

2.点位管理——帝可得后台管理系统

目录 前言点位管理菜单模块1.需求说明2.库表设计3.生成基础代码0 .使用若依代码生成器最终目标1.创建点位管理2.添加数据字典3.配置代码生成信息4.下载代码并导入项目 4.优化菜单——点位管理1.优化区域管理2.增加点位数3. 合作商 前言 提示&#xff1a;本篇介绍点位管理模块&…

[论文精读]AI-Guardian: Defeating Adversarial Attacks using Backdoors

会议名称&#xff1a;2023 IEEE Symposium on Security and Privacy (SP) 发布链接&#xff1a;AI-Guardian: Defeating Adversarial Attacks using Backdoors | IEEE Conference Publication | IEEE Xplore 中文译名&#xff1a;AI-Guardian:利用后门防御对抗攻击 阅读原因…

记一次教学版内网渗透流程

信息收集 如果觉得文章写的不错可以共同交流 http://aertyxqdp1.target.yijinglab.com/dirsearch dirsearch -u "http://aertyxqdp1.target.yijinglab.com/"发现 http://aertyxqdp1.target.yijinglab.com/joomla/http://aertyxqdp1.target.yijinglab.com/phpMyA…

IT基础监控范围和对象

监控易作为一款由美信时代独立自主研发的分布式一体化集中监控平台&#xff0c;其监控范围极为广泛&#xff0c;几乎涵盖了所有主流的IT基础设施以及相关的设备和系统。以下是对监控易监控范围的详细介绍&#xff1a; 一、IT基础资源监控 服务器硬件监控&#xff1a;监控易支…

fmql之Linux阻塞和非阻塞IO

继续学习正点原子吧。 概念简介 什么是阻塞、非阻塞IO 阻塞/非阻塞访问的代码示例 等待队列&#xff08;阻塞访问使用&#xff09; 轮询&#xff08;非阻塞访问使用&#xff09; poll操作函数的使用&#xff08;轮询的一种&#xff09; 阻塞IO的实验 blockio.c blockioAPP.c 运…

大模型能力扩展之——提示词(Prompt),知识库,思维链(CoT)和Agent(代理)

前言 “大模型的推理能力配合外部工具才能真正发挥大模型的作用” 在学习和使用大模型的过程中&#xff0c;我们会发现大模型只能用来进行一下简单的问答&#xff1b;一旦涉及到复杂的问题&#xff0c;大模型就无能为力了。 其原因就在于我们并不会使用大模型&#xff0c;或…