FPGA:verilog实现直接型巴特沃斯高通IIR滤波器

目录

前言

1、matlab设计滤波器

1.1、通过FDATOOL设计滤波器

1.2、对滤波器系数进行量化

2、verilog设计IIR滤波器

2.1 零点模块

2.2 极点模块

2.3 顶层文件

3、vivado仿真

3.1 matlab生成测试数据

3.2vivado添加测试数据

3.3添加tb文件运行仿真

总结


前言

在matlab设计7阶(8级)高通IIR巴特沃斯滤波器,并实现verilog代码,并在vivado中进行仿真


1、matlab设计滤波器

1.1、通过FDATOOL设计滤波器

        打开matlab,在APP这一栏的搜索框中搜索 ’filter‘ 或直接搜索 ’滤波器‘ ,单击打开(matlab版本为2022a)

        我们要设计的滤波器的相应类型为高通,设计方法为IIR 巴特沃斯,采样频率Fs设置为1M(1000k),Fc设置为200k。在下面的界面填好这些参数,并点击设计滤波器。

         我们可以从当前滤波器信息窗口看到,我们设计的滤波器目前的结构是级联型,七阶(8)级的滤波器被设计为4个节,其中三个节是2阶(3级),一个节是1阶(2级),2*3+1 = 7,组成了我们的七阶滤波器。

        为了设计成直接型,我们对滤波器的结构进行转换

         打开编辑-转换结构

在这里插入图片描述

        选择Direct-Form Ⅰ

         转化为单节

在这里插入图片描述

       到这里就完成了到直接型的转化,下一步我们可以看看量化效应,对滤波器效果的影响,根据下图操作,可以看到幅值响应区域出现了量化前后的幅频响应曲线。

        可以看到16位量化后,滤波器的性能产生了恶化,但在可接受范围内,实际工程中可以通过级联型的滤波器来减小量化带来的误差。

        进行如下操作,将滤波器系数导出到matlab工作区

        Den Num这里分别是分母和分子

1.2、对滤波器系数进行量化

         在matlab中运行下面代码(来自杜勇老师的《数字滤波器的matlab与fpga实现》)

Qcoe = 16
b = Num;
a = Den;

%对滤波器系数进行量化,四舍五入截尾
m=max(max(abs(a),abs(b)));
Qm = floor(log2(m/a(1)))

if Qm<log2(m/a(1))
    Qm = Qm + 1;
end
Qm = 2^Qm;

Qb16=round(b/Qm*(2^(Qcoe-1)-1))
Qa16=round(a/Qm*(2^(Qcoe-1)-1))

        运行结果如下:

Qb16 =

  列 1 至 5

         742       -5197       15590      -25982       25982

  列 6 至 8

      -15590        5197        -742


Qa16 =

  列 1 至 5

       16384      -22761       27443      -17266        8332

  列 6 至 8

       -2373         430         -33

        可以看到零点系数Qb16具有对称性

2、verilog设计IIR滤波器

2.1 零点模块

module Zero(
	input		            rst,  
	input		            clk,
	input	 signed [11:0]	Xin,
	output   signed [30:0]	Xout
);

//-------------------------------------------------------
//   将数据移位寄存
//-------------------------------------------------------
	reg signed [11:0] Xin_Reg[6:0];
	reg        [3:0]  i,j; 
	
	always @(posedge clk or posedge rst)
		if (rst)
			begin 
				for (i=0; i<7; i=i+1)
					Xin_Reg[i]=12'd0;
			end
		else
			begin
				for (j=0; j<6; j=j+1)
				    Xin_Reg[j+1] <= Xin_Reg[j];
				Xin_Reg[0] <= Xin;
			end
			
//-------------------------------------------------------
//   将对称数据相加,结果存储在数组中  2个12bit数据相加,结果为13bit
//-------------------------------------------------------		
	wire signed [12:0] Add_Reg[3:0];
	
	assign Add_Reg[0] = {Xin[11],Xin}               - {Xin_Reg[6][11],Xin_Reg[6]};
	assign Add_Reg[1] = {Xin_Reg[0][11],Xin_Reg[0]} - {Xin_Reg[5][11],Xin_Reg[5]};
	assign Add_Reg[2] = {Xin_Reg[1][11],Xin_Reg[1]} - {Xin_Reg[4][11],Xin_Reg[4]};
	assign Add_Reg[3] = {Xin_Reg[2][11],Xin_Reg[2]} - {Xin_Reg[3][11],Xin_Reg[3]};	
	
//-------------------------------------------------------
//   将数据与系数相乘,结果存储在数组中   13bit与16bit相乘,结果为28bit
//-------------------------------------------------------		
	wire signed [27:0] Mult_Reg[3:0];
	
	wire signed [15:0] coe [3:0];
	
	assign coe[0] =  16'd742;
	assign coe[1] =  -16'd5196;
	assign coe[2] =  16'd15589;
	assign coe[3] =  -16'd25982;
//-------------------------------------------------------
//   调用4个乘法器,设置为13bit有符号数和16bit有符号数相乘
//-------------------------------------------------------	
	mult_gen_0	Umultz0 (
		.A (Add_Reg[0]),
		.B (coe[0]),
		.P (Mult_Reg[0])
		);
	mult_gen_0	Umultz1 (
		.A (Add_Reg[1]),
		.B (coe[1]),
		.P (Mult_Reg[1])
		);
	mult_gen_0	Umultz2 (
		.A (Add_Reg[2]),
		.B (coe[2]),
		.P (Mult_Reg[2])
		);
	mult_gen_0	Umultz3 (
		.A (Add_Reg[3]),
		.B (coe[3]),
		.P (Mult_Reg[3])
		);

	assign Xout =  {{3{Mult_Reg[0][27]}},Mult_Reg[0]}+ 
	               {{3{Mult_Reg[1][27]}},Mult_Reg[1]}+
	               {{3{Mult_Reg[2][27]}},Mult_Reg[2]}+
	               {{3{Mult_Reg[3][27]}},Mult_Reg[3]};
	
endmodule

          这里的乘法器调用了vivado的乘法IP,设置为13bit有符号数和16bit有符号数相乘,输出结果设置为28bit,因为输入数据不可能同时出现最大负值。流水线级数设置为0。

        这种做法的缺点是比较浪费资源,在资源比较宝贵的项目中不要采取这种方法。可以采取移位相加法来节省资源,也可以对不同的大小的系数,用不同输入位宽的乘法器,比方说乘以742可以设置IP核的输入为11位,相比于16位,可以节省资源。

        但这样做的好处是,移植起来很方便,调用的时候把参数填进coe[]数组就好了,可以节省很多时间    

        可以按照下图进行设置,如果仿真结果有问题,很有可能就是乘法IP设置出错了。

2.2 极点模块

module Pole(
	input		                rst  ,   
	input		                clk  ,   
	input	   signed [11:0]	Yin  ,  
	output     signed [30:0]	Yout
);

	reg signed[11:0] Yin_Reg[6:0];
	reg [3:0] i,j; 
	
//-------------------------------------------------------
//   将数据移位寄存
//-------------------------------------------------------
	always @(posedge clk or posedge rst)
		if (rst)
			begin 
				for (i=0; i<7; i=i+1)
					Yin_Reg[i]=12'd0;
			end
		else
			begin
				for (j=0; j<6; j=j+1)
					Yin_Reg[j+1] <= Yin_Reg[j];
				Yin_Reg[0] <= Yin;
			end
//-------------------------------------------------------
//   将数据与系数相乘,结果存储在数组中   12bit与16bit相乘,结果为27bit
//-------------------------------------------------------		
    wire signed [15:0] coe[6:0] ;       
	wire signed [26:0] Mult_Reg[7:0];   
	
	assign coe[0]=  -16'd22761 ;
	assign coe[1]=   16'd27443 ;
	assign coe[2]=  -16'd17266 ; 
	assign coe[3]=   16'd8332 ;	
	assign coe[4]=  -16'd2373  ;
	assign coe[5]=   16'd430  ; 	
	assign coe[6]=  -16'd33   ;		
		
//-------------------------------------------------------
//   调用七个乘法器,设置为12bit有符号数和16bit有符号数相乘
//-------------------------------------------------------			
	mult_gen_1	Umult1 (
		.A (Yin_Reg[0]),
		.B (coe[0]),
		.P (Mult_Reg[0])
		);	
	mult_gen_1	Umult2 (
		.A (Yin_Reg[1]),
		.B (coe[1]),
		.P (Mult_Reg[1])
		);
	mult_gen_1	Umult3 (
		.A (Yin_Reg[2]),
		.B (coe[2]),
		.P (Mult_Reg[2])
		);
	mult_gen_1	Umult4 (
		.A (Yin_Reg[3]),
		.B (coe[3]),
		.P (Mult_Reg[3])
		);	
	mult_gen_1	Umult5 (
		.A (Yin_Reg[4]),
		.B (coe[4]),
		.P (Mult_Reg[4])
		);	
    mult_gen_1	Umult6 (
		.A (Yin_Reg[5]),
		.B (coe[5]),
		.P (Mult_Reg[5])
		);	
	mult_gen_1	Umult7 (
		.A (Yin_Reg[6]),
		.B (coe[6]),
		.P (Mult_Reg[6])
		);

		
	assign Yout =  {{4{Mult_Reg[0][26]}},Mult_Reg[0]}+
	               {{4{Mult_Reg[1][26]}},Mult_Reg[1]}+
	               {{4{Mult_Reg[2][26]}},Mult_Reg[2]}+
	               {{4{Mult_Reg[3][26]}},Mult_Reg[3]}+
				   {{4{Mult_Reg[4][26]}},Mult_Reg[4]}+
				   {{4{Mult_Reg[5][26]}},Mult_Reg[5]}+
				   {{4{Mult_Reg[6][26]}},Mult_Reg[6]};
	
endmodule

        这里的乘法IP设计跟零点模块基本相同,就不重复了

2.3 顶层文件

module IIR_Direct (
	input						rst,
	input						clk,
	input	   signed [11:0]	din,
	output     signed [11:0]	dout
	);
	
	wire signed [30:0] Xout;
	
	Zero U0 (
		.rst (rst),
		.clk (clk),
		.Xin (din),
		.Xout (Xout)
		);
		
	wire signed [11:0] Yin;
	wire signed [30:0] Yout;
	
	Pole U1 (
		.rst (rst),
		.clk (clk),
		.Yin (Yin),
		.Yout (Yout)
		);

    wire signed [30:0] Ysum;
    assign Ysum = Xout - Yout;
	
	//将Ydiv右移14位,相当于除了16384
	wire signed [30:0] Ydiv;
	assign Ydiv = {{14{Ysum[30]}},Ysum[30:14]};
	
	//直接对结果进行截尾
	assign Yin = (rst ? 12'd0 : Ydiv[11:0]);
	assign dout = Yin;

endmodule

3、vivado仿真

        vivado仿真需要测试数据,以及仿真tb文件,给出代码如下:

3.1 matlab生成测试数据

        为了验证高通滤波器,我们生成的测试信号由50k和250k的正弦信号叠加而成。生成的二进制文件为Bin_s.txt


f1=50000;         %信号1频率为50KHz
f2=250000;        %信号2频率为250KHz

Fs=1000000;       %采样频率为1MHz
N=12;             %量化位数

%产生信号
t=0:(1/Fs):0.0012;
c1=2*pi*f1*t;
c2=2*pi*f2*t;


s1=sin(c1);%产生正弦波
s2=sin(c2);%产生正弦波


s=0.5*s1+0.5*s2;
plot(t,s1,t,s);
Q_s=round(s*(2^(N-1)-1));

%% 生成txt文件们
%将生成的数据以十进制数据格式写入txt文件中

fid=fopen('Int_s.txt','w');
fprintf(fid,'%8d\r\n',Q_s);
fprintf(fid,';'); 
fclose(fid);

%将生成的数据以二进制数据格式写入txt文件中

fid=fopen('Bin_s.txt','w');
for i=1:length(Q_s)
    B_s=dec2bin(Q_s(i)+(Q_s(i)<0)*2^N,N)
    for j=1:N
       if B_s(j)=='1'
           tb=1;
       else
           tb=0;
       end
       fprintf(fid,'%d',tb);  
    end
    fprintf(fid,'\r\n');
end
fprintf(fid,';'); 
fclose(fid);

3.2vivado添加测试数据

        需要将测试数据添加到vivado环境中,仿真才能正常运行,我的做法如下

3.3添加tb文件运行仿真

        tb文件如下

`timescale 1ns / 1ps

module IIR_tb();

reg clk;
reg [11:0] din;
reg rst;                                             
wire [11:0]  dout;
                    
IIR_Direct i1 (
	.clk(clk),
	.din(din),
	.dout(dout),
	.rst(rst)
);

parameter clk_period=626; 
parameter clk_half_period=clk_period/2;
parameter data_num=2000;  
parameter time_sim=data_num*clk_period/2; 

initial
begin
	clk=1;
	rst=1;
	#10000 rst=0;
	#time_sim $finish;
	din=12'd10;
end

always                                                 
	#clk_half_period clk=~clk;

integer Pattern;
reg [11:0] stimulus[1:data_num];
initial
begin
	$readmemb("Bin_s.txt",stimulus);
	Pattern=0;
	repeat(data_num)
		begin
			Pattern=Pattern+1;
			din=stimulus[Pattern];
			#clk_period;
		end
end

integer file_out;
initial 
begin                                                
	file_out = $fopen("Bin_s_out.txt");
	if(!file_out)
		begin
			$display("could not open file!");
			$finish;
		end
end
wire rst_write;
wire signed [11:0] dout_s;
assign dout_s = dout;                  
assign rst_write = clk& (!rst);
always @(posedge rst_write )
	$fdisplay(file_out,"%d",dout_s);
	
endmodule

 局部放大如下:

总结

        本篇文章在matlab设计7阶(8级)高通IIR巴特沃斯滤波器,并实现verilog代码,并在vivado中进行仿真,结果符合预期。

  • 14
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值