自学内容网 自学内容网

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(
inputwireclk,
inputwirerst_n,

inputwireen,//上升沿触发窗口计算
inputwire[3:0]win_type,//窗口类型,1:矩形窗,2:图基窗Tukey,3:三角窗,4:汉宁窗Hann,5:海明窗Hamming,6:布莱克曼窗Blackman,(7:凯塞窗kaiser, 暂未实现)
inputwire[15:0]n,//窗口长度
inputwire[15:0]i,//窗口索引值,0 ~ n-1
//inputwiresigned[15:0]beta,//kaiser窗的参数beta,win_type=7时需要这个参数,其他情况可任意给值

outputwirebusy,//指示模块是否计算完成
outputwiresigned[15:0]win
);

regsigned[15:0]win_buf= 16'sd256;//8-8有符号定点数
regsigned[15:0]win_buf_d0= 16'sd256;
regbusy_buf= 1'b0;

assignwin= win_buf_d0;
assignbusy= busy_buf;

localparamS_IDLE= 4'h1;
localparamS_CAL= 4'h2;
localparamS_END= 4'h4;

reg[3:0]state= S_IDLE;
reg[3:0]next_state;

always @(posedge clk) begin
if(~rst_n) begin
state<= S_IDLE;
end
else begin
state<= next_state;
end
end

always @(*) begin
case(state)
S_IDLE: begin
if(en_pe) begin
next_state<= S_CAL;
end
else begin
next_state<= S_IDLE;
end
end
S_CAL: begin
case(win_type)
4'd1: begin//矩形窗
next_state<= S_END;
end
4'd2: begin//图基窗
if(cnt >= 4'd4) begin
next_state<= S_END;
end
else begin
next_state<= S_CAL;
end
end
4'd3: begin//三角窗
if(cnt >= 4'd1) begin
next_state<= S_END;
end
else begin
next_state<= S_CAL;
end
end
4'd4: begin//汉宁窗
if(cnt >= 4'd3) begin
next_state<= S_END;
end
else begin
next_state<= S_CAL;
end
end
4'd5: begin//海明窗
if(cnt >= 4'd3) begin
next_state<= S_END;
end
else begin
next_state<= S_CAL;
end
end
4'd6: begin//布莱克曼窗
if(cnt >= 4'd5) begin
next_state<= S_END;
end
else begin
next_state<= S_CAL;
end
end
// 4'd7: begin//凯塞窗这个涉及到循环逼近bessel函数和sqrt计算,FPGA比较麻烦,就先不实现这个窗口类型了
// next_state<= S_END;
// end
default: begin
next_state<= S_END;
end
endcase
end
S_END: begin
next_state<= S_IDLE;
end
default: begin
next_state<= S_IDLE;
end
endcase
end

//en 边沿检测
wireen_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) begin
case(state)
S_IDLE: begin
cnt<= 4'd0;
end
S_CAL: begin
cnt<= cnt + 1'b1;
end
default: begin
cnt<= 4'd0;
end
endcase
end

//win_buf
regsigned[31:0]multi_tmp= 32'sd0;
always @(posedge clk) begin
if(~rst_n) begin
win_buf<= 16'sd256;//1.0
end
else case(state)
S_CAL: begin
case(win_type)
4'd1: begin//矩形窗
win_buf<= 16'sd256;
end
4'd2: begin//图基窗
if(cnt == 4'd4) begin
if(i <= k) begin
win_buf<= (16'sd256 - (cos_val_s >>> 7)) >>> 1;
end
else if(i > n - k - 4'd2) begin
win_buf<= (16'sd256 - (cos_val_s >>> 7)) >>> 1;
end
else begin
win_buf<= 16'sd256;
end
end
else begin
win_buf<= win_buf;
end
end
4'd3: begin//三角窗
if(cnt == 4'd0) begin
multi_tmp<= 16'sd512 * i / (n - 1'b1);
end
else if(cnt == 4'd1) begin
win_buf <= 16'sd256 - abs(16'sd256 - multi_tmp[15:0]);
end
else begin
win_buf<= win_buf;
end
end
4'd4: begin//汉宁窗
if(cnt == 4'd3) begin
win_buf<= 16'sd128 - (cos_val_s >>> 8);//0.5 * (1.0 - cos(2 * i * pi / (n - 1)));
end
else begin
win_buf<= win_buf;
end
end
4'd5: begin//海明窗
if(cnt == 4'd3) begin
win_buf<= 16'sd138 - ((16'sd118 * (cos_val_s >>> 7)) >>> 8);//0.54 - 0.46 * cos(2 * i * pi / (n - 1));
end
else begin
win_buf<= win_buf;
end
end
4'd6: begin//布莱克曼窗
if(cnt == 4'd3) begin
win_buf<= 16'sd108 - (cos_val_s >>> 8);
end
else if(cnt == 4'd5) begin
win_buf<= win_buf + ((16'sd82 * (cos_val_s >>> 7)) >>> 10);
end
else begin
win_buf<= win_buf;
end
end
// 4'd7: begin//凯塞窗
// win_buf<= 16'sd0;
// end
default: begin
win_buf<= 16'sd256;
end
endcase
end
default: begin
win_buf<= win_buf;
end
endcase
end

//busy_buf
always @(*) begin
case(state)
S_IDLE: begin
busy_buf<= 1'b0;
end
default: begin
busy_buf<= 1'b1;
end
endcase
end

//cos_phase
reg[15:0]k= 16'd0;
always @(posedge clk) begin
case(state)
S_CAL: begin
case(win_type)
4'd1: begin//矩形窗
cos_phase<= 16'd0;
end
4'd2: begin//图基窗
if(cnt == 4'd0) begin
k <= (n - 2'd2) / 4'd10;
cos_phase<= cos_phase;
end
else if(cnt == 4'd1) begin
k<= k;

if(i <= k) begin
cos_phase<= i * (16'd32768 / (k + 1'b1)) + 16'd16384;
end
else if(i > n - k - 2'd2) begin
cos_phase<= (n - i - 1'b1) * (16'd32768 / (k + 1'b1)) + 16'd16384;
end
else begin
cos_phase<= 16'd0;
end
end
else begin
cos_phase<= cos_phase;
k<= k;
end
end
4'd3: begin//三角窗
cos_phase<= 16'd0;
end
4'd4: begin//汉宁窗
cos_phase<= 2'd2 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;
end
4'd5: begin//海明窗
cos_phase<= 2'd2 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;
end
4'd6: begin//布莱克曼窗
if(cnt == 4'd0) begin
cos_phase<= 2'd2 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;
end
else if(cnt == 4'd2) begin
cos_phase<= 4'd4 * i * (16'd32768 / (n - 1'b1)) + 16'd16384;
end
else begin
cos_phase<= cos_phase;
end
end
// 4'd7: begin//凯塞窗
// cos_phase<= 16'd0;
// end
default: begin
cos_phase<= 16'd0;
end
endcase
end
default: begin
cos_phase<= cos_phase;
end
endcase
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
);

wiresigned[15:0]cos_val_s;
assigncos_val_s= {~cos_out[15], cos_out[14:0]};

//win_buf_d0
always @(posedge clk) begin
case(state)
S_END: begin
win_buf_d0<= win_buf;
end
default: begin
win_buf_d0<= win_buf_d0;
end
endcase
end

//------------------func------------------------------
function signed [15:0] abs(input signed [15:0] a);
begin
abs = (a >= 16'sd0)? a : -a;
end
endfunction

endmodule

测试

  testbench 如下

`timescale 1ns/100ps

module FIR_windows_generate_tb();

regclk_100M= 1'b1;
always #5 begin
clk_100M<= ~clk_100M;
end

regrst_n = 1'b1;

regen;//上升沿触发窗口计算
reg[3:0]win_type;//窗口类型,1:矩形窗,2:图基窗,3:三角窗,4:汉宁窗,5:海明窗,6:布莱克曼窗
reg[15:0]n;//滤波器阶数
reg[15:0]i;//窗口索引值,0 ~ n-1

wirebusy;
wiresigned[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;

integerk;
begin
n= N;
win_type= WIN_TYPE;
#10;

for (k = 0; k < N; k = k + 1'b1) begin
i= k;
en= 1'b1;
wait(busy);
#10;
en= 1'b0;
wait(~busy);
#10;
end
end
endtask

initial begin
rst_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;
end

endmodule

  仿真结果如下

在这里插入图片描述

滤波器系数计算

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(
inputwireclk,
inputwirerst_n,

inputwireen,//上升沿触发窗口计算
inputwire[1:0]band_type,//滤波器类型,0:低通LP,1:高通HP,2:带通BP,3:带阻BS
inputwiresigned[15:0]fs,//采样率,注意fln,fhn均应小于fs/2
inputwiresigned[15:0]fln,//滤波器下频点,LP,HP,BP,BS均会用到
inputwiresigned[15:0]fhn,//滤波器上频点,BP,BS用到
inputwire[3:0]win_type,//窗函数类型,1:矩形窗,2:图基窗Tukey,3:三角窗,4:汉宁窗Hann,5:海明窗Hamming,6:布莱克曼窗Blackman
inputwiresigned[15:0]n,//滤波器阶数   注意,HP/BS的阶数应为偶数,奇数阶的系数不可靠
inputwiresigned[15:0]i,//0~n,共n+1个值

outputwirebusy,//指示模块是否计算完成
outputwiresigned[15:0]firwin
);

regsigned[31:0]firwin_buf= 32'sd256;
regsigned[15:0]firwin_buf_d0= 16'sd256;//8-8有符号定点数
regbusy_buf= 1'b0;

assignfirwin= firwin_buf_d0;
assignbusy= busy_buf;

localparamS_IDLE= 4'h1;
localparamS_CAL= 4'h2;
localparamS_END= 4'h4;

reg[3:0]state= S_IDLE;
reg[3:0]next_state;

always @(posedge clk) begin
if(~rst_n) begin
state<= S_IDLE;
end
else begin
state<= next_state;
end
end

always @(*) begin
case(state)
S_IDLE: begin
if(en_pe) begin
next_state<= S_CAL;
end
else begin
next_state<= S_IDLE;
end
end
S_CAL: begin
if(cnt >= 4'd12) begin//最迟在cnt=7可以读取窗函数值并计算firwin,随后本模块可计算firwin
next_state<= S_END;
end
else begin
next_state<= S_CAL;
end
end
S_END: begin
next_state<= S_IDLE;
end
default: begin
next_state<= S_IDLE;
end
endcase
end

//firwin_buf
always @(posedge clk) begin
case(state)
S_CAL: begin
case(band_type)
2'd0: begin//LP
if(cnt == 4'd3) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值
firwin_buf<= (({fln, 16'b0} / fs) * 16'sd804) >>> 8;//3.1415 = 804/256
end
else begin
firwin_buf<= sin_val_s / s_mlti2 * 4'sd2;
end
end
else if(cnt == 4'd7) begin
firwin_buf<= (firwin_buf * win) >>> 16;
end
else begin
firwin_buf<= firwin_buf;
end
end
2'd1: begin//HP
if(cnt == 4'd3) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值
firwin_buf<= ((32'sd32768 - {fln, 16'b0} / fs) * 16'sd804) >>> 8;
end
else begin
firwin_buf<= sin_val_s;
end
end
else if(cnt == 4'd7) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin
firwin_buf<= firwin_buf;
end
else begin
firwin_buf<= (firwin_buf - sin_val_s) / s_mlti2 * 4'sd2;
end
end
else if(cnt == 4'd8) begin
firwin_buf<= (firwin_buf * win) >>> 16;
end
else begin
firwin_buf<= firwin_buf;
end
end
2'd2: begin//BP
if(cnt == 4'd3) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值
firwin_buf<= ((({fhn, 16'b0} - {fln, 16'b0}) / fs) * 16'sd804) >>> 8;
end
else begin
firwin_buf<= sin_val_s;
end
end
else if(cnt == 4'd7) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin
firwin_buf<= firwin_buf;
end
else begin
firwin_buf<= (firwin_buf - sin_val_s) / s_mlti2 * 4'sd2;
end
end
else if(cnt == 4'd8) begin
firwin_buf<= (firwin_buf * win) >>> 16;
end
else begin
firwin_buf<= firwin_buf;
end
end
2'd3: begin//BS
if(cnt == 4'd3) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin//偶数阶滤波器,计算最中间的滤波器系数   即洛必达求=0时的值
firwin_buf<= (({fln, 16'b0} / fs + 32'sd32768 - {fhn, 16'b0} / fs) * 16'sd804) >>> 8;
end
else begin
firwin_buf<= sin_val_s;
end
end
else if(cnt == 4'd7) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin
firwin_buf<= firwin_buf;
end
else begin
firwin_buf<= firwin_buf + sin_val_s;
end
end
else if(cnt == 4'd11) begin
if((~n[0]) && (i_buf == n/4'sd2)) begin
firwin_buf<= firwin_buf;
end
else begin
firwin_buf<= (firwin_buf - sin_val_s) / s_mlti2 * 4'sd2;
end
end
else if(cnt == 4'd12) begin
firwin_buf<= (firwin_buf * win) >>> 16;
end
else begin
firwin_buf<= firwin_buf;
end
end
default: begin
firwin_buf<= firwin_buf;
end
endcase
end
default: begin
firwin_buf<= firwin_buf;
end
endcase
end

//i_buf
regsigned[15:0]i_buf;
always @(posedge clk) begin
if(i > (n >>> 1)) begin
i_buf<= n - i;//滤波器是对称的,这里处理后利用i_buf计算滤波器系数
end
else begin
i_buf<= i;
end
end

//sin_phase
reg[31:0]multi_tmp;
always @(posedge clk) begin
case(state)
S_CAL: begin
sin_phase<= multi_tmp[15:0];
end
default: begin
sin_phase<= sin_phase;
end
endcase
end

regsigned[15:0]s_mlti2;
always @(*) begin
s_mlti2<= n - i_buf * 4'sd2;
end

always @(*) begin
case(state)
S_CAL: begin
case(band_type)
2'd0: begin//LP
multi_tmp<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;
end
2'd1: begin//HP
if(cnt <= 4'd3) begin
multi_tmp<= {s_mlti2, 14'b0};
end
else begin
multi_tmp<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;
end
end
2'd2: begin//BP
if(cnt <= 4'd3) begin
multi_tmp<= (({fhn, 16'b0} / fs) * s_mlti2) >> 1;
end
else begin
multi_tmp<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;
end
end
2'd3: begin//BS
if(cnt <= 4'd3) begin
multi_tmp<= (({fln, 16'b0} / fs) * s_mlti2) >> 1;
end
else if(cnt <= 4'd7) begin
multi_tmp<= {s_mlti2, 14'b0};
end
else begin
multi_tmp<= (({fhn, 16'b0} / fs) * s_mlti2) >> 1;
end
end
default: begin
multi_tmp<= 32'd0;
end
endcase
end
default: begin
multi_tmp<= 32'd0;
end
endcase
end

//窗函数
wiresigned[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 边沿检测
wireen_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) begin
case(state)
S_IDLE: begin
cnt<= 4'd0;
end
S_CAL: begin
cnt<= cnt + 1'b1;
end
default: begin
cnt<= 4'd0;
end
endcase
end

//busy_buf
always @(*) begin
case(state)
S_IDLE: begin
busy_buf<= 1'b0;
end
default: begin
busy_buf<= 1'b1;
end
endcase
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
);

wiresigned[15:0]sin_val_s;
assignsin_val_s= {~sin_out[15], sin_out[14:0]};

//firwin_buf_d0
always @(posedge clk) begin
case(state)
S_END: begin
firwin_buf_d0<= firwin_buf[15:0];
end
default: begin
firwin_buf_d0<= firwin_buf_d0;
end
endcase
end

endmodule

测试

  testbench 如下

`timescale 1ns/100ps

module FIR_firwin_generate_tb();

regclk_100M= 1'b1;
always #5 begin
clk_100M<= ~clk_100M;
end

regrst_n = 1'b1;

regen;//上升沿触发窗口计算

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 ~ n

wirebusy;
wiresigned[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;

integerk;
begin
band_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) begin
i= k;
en= 1'b1;
wait(busy);
#10;
en= 1'b0;
wait(~busy);
#10;
end
end
endtask

initial begin
rst_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/2
cal_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;
end

endmodule

  仿真结果如下

在这里插入图片描述

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


原文地址:https://blog.csdn.net/qq_43557686/article/details/142643100

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!