阅读量: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