Como todos sabemos, el Diseñador de filtros en Matlab puede generar directamente el código verilog del filtro FIR y puede generar fácilmente filtros de paso alto, paso bajo y paso de banda con un orden específico y parámetros de filtro específicos, y el verilog generado. El código también puede especificar el tipo de señales de entrada y salida y el ancho de bits. Sin embargo, el código generado no es realmente hermoso y la reutilización también es muy pobre. Para realizar el cambio de diferentes características de filtrado, es necesario generar códigos para múltiples filtros.
Por las consideraciones anteriores, diseñé e implementé el código verilog general del filtro FIR, y sus parámetros de filtro se ingresan a través de la interfaz, de modo que los resultados de filtrado correspondientes se pueden obtener ingresando diferentes parámetros. El código verilog es el siguiente:
/*
* 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
Ejemplo de filtro de paso bajo
Cuando el orden del filtro es alto, cómo proporcionar los parámetros del filtro es sin duda un asunto problemático, por lo que se escribe el código matlab y se puede generar el archivo .v requerido con un clic para realizar la configuración de los parámetros:
%-----------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);
El banco de pruebas y los resultados de las pruebas son los siguientes.
`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
Ejemplo de Hilbert
Usando el código de filtro FIR anterior, también se pueden implementar muchas otras funciones de filtrado, como el cambio de fase de 90 grados comúnmente usado, que se puede implementar usando la transformada de Hilbert. El código generado por Matlab para los parámetros del filtro de Hilbert es el siguiente
%-----------------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);
Los resultados de la simulación son los siguientes.