FIR数字滤波器的FPGA实现

目录

1.FIR数字滤波器实现原理

2.FPGA实现

2.1全串行FIR滤波器

2.1.1 原理图 

2.1.2 Verilog代码

2.1.3 仿真测试代码

2.1.4 仿真结果图


1.FIR数字滤波器实现原理

一个 N 阶的 FIR 滤波器输出公式 y(n) 如下:

式1中 h(k)为滤波器的系数,x(n-k)为x(n)延时k个周期。系统的传输函数H(z)可表示成公式2:

 从式1看出:滤波过程主要是一组特定的系数与信号完成卷积的过程。从式2看出,在有限的Z平面内它有N-1个零点,同时其 N-1个极点全部位于 z=0 中,因此 FIR 滤波器也被称为全零点滤波器,是一个单位脉冲响应有限长的稳定系统
FIR滤波器在系数满足一定条件的情况下,它的相频特性是线性的,可以有效的保留信号的相位信息,因此线性相位的 FIR 滤波器在实际工程中有着较为广泛的应用。

2.FPGA实现

Fir滤波器的结构形式分为直接型、级联型、频率取样型、快速卷积型四种,其中最常用,最简单的是直接型。FPGA实现直接型Fir滤波器时,可有采用串行结构、并行结构、分布式结构,以及直接使用器件提供的ip核,下面对每种结构的实现方法进行介绍,代码实现以及仿真测试。 

2.1全串行FIR滤波器

2.1.1 原理图 

2.1.2 Verilog代码

//这是FirFullSerial.v文件的程序清单
module fir_filter(
	rst,clk,Xin,
	Yout);
	
	input		rst;   //复位信号,高电平有效
	input		clk;   //FPGA系统时钟,频率为16kHz
	input	 signed [11:0]	Xin;  //数据输入频率为2khZ
	output signed [28:0]	Yout; //滤波后的输出数据


	//实例化有符号数乘法器IP核mult
   reg  signed [11:0] coe;   //滤波器为12比特量化数据
	wire signed [12:0] add_s; //输入为12比特量化数据,两个对称系数相加需要13比特存储
	wire signed [24:0] Mout;  
	mult_ip	Umult (
		.clock (clk),
		.dataa (coe),
		.datab (add_s),
		.result (Mout));
		

	//实例化有符号数加法器IP核,对输入数据进行1位符号位扩展,输出结果为13比特数据
	reg signed [12:0] add_a;
	reg signed [12:0] add_b;
	add_ip Uadder (
		.dataa (add_a),
		.datab (add_b),
		.result (add_s));				
		
	//3位计数器,计数周期为8,为输入数据速率
	reg [2:0] count;
	always @(posedge clk or posedge rst)
		if (rst)
			count = 3'd0;
		else
			count = count + 1;
	
	//将数据存入移位寄存器Xin_Reg中
	reg [11:0] Xin_Reg[15:0];
	reg [3:0] i,j; 
	always @(posedge clk or posedge rst)
		if (rst)
			//初始化寄存器值为0
			begin 
				for (i=0; i<15; i=i+1)
					Xin_Reg[i]=12'd0;
			end
		else
			begin
				if (count==7)
					begin
						for (j=0; j<15; j=j+1)
							Xin_Reg[j+1] <= Xin_Reg[j];
						Xin_Reg[0] <= Xin;
					end
			end
			
	//将对称系数的输入数据相加,同时将对应的滤波器系数送入乘法器
	//需要注意的是,下面程序只使用了一个加法器及一个乘法器资源
	//以8倍数据速率调用乘法器IP核,由于滤波器长度为16,系数具有对称性,故可在一个数据
	//周期内完成所有8个滤波器系数与数据的乘法运算
	//为了保证加法运算不溢出,输入输出数据均扩展为13比特。
	always @(posedge clk or posedge rst)
		if (rst)
			begin
				add_a <= 13'd0;
				add_b <= 13'd0;
				coe <= 12'd0;
			end
		else
			begin
				if (count==3'd0)
					begin
						add_a <= {Xin_Reg[0][11],Xin_Reg[0]};
						add_b <= {Xin_Reg[15][11],Xin_Reg[15]};
						coe <= 12'h000;//c0
					end
				else if (count==3'd1)
					begin
						add_a <= {Xin_Reg[1][11],Xin_Reg[1]};
						add_b <= {Xin_Reg[14][11],Xin_Reg[14]};					
						coe <= 12'hffd; //c1
					end
				else if (count==3'd2)
					begin
						add_a <= {Xin_Reg[2][11],Xin_Reg[2]};
						add_b <= {Xin_Reg[13][11],Xin_Reg[13]};						
						coe <= 12'h00f; //c2
					end
				else if (count==3'd3)
					begin
						add_a <= {Xin_Reg[3][11],Xin_Reg[3]};
						add_b <= {Xin_Reg[12][11],Xin_Reg[12]};
						coe <= 12'h02e; //c3
					end
				else if (count==3'd4)
					begin
						add_a <= {Xin_Reg[4][11],Xin_Reg[4]};
						add_b <= {Xin_Reg[11][11],Xin_Reg[11]};						
						coe <= 12'hf8b; //c4
					end
				else if (count==3'd5)
					begin
						add_a <= {Xin_Reg[5][11],Xin_Reg[5]};
						add_b <= {Xin_Reg[10][11],Xin_Reg[10]};				
						coe <= 12'hef9; //c5
					end					
				else if (count==3'd6)
					begin
						add_a <= {Xin_Reg[6][11],Xin_Reg[6]};
						add_b <= {Xin_Reg[9][11],Xin_Reg[9]};						
						coe <= 12'h24e; //c6
					end
				else
					begin
						add_a <= {Xin_Reg[7][11],Xin_Reg[7]};
						add_b <= {Xin_Reg[8][11],Xin_Reg[8]};						
						coe <= 12'h7ff; //c7
					end
			end

	//对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据
	//考虑到乘法器及累加器的延时,需要计数器为2时对累加器清零,同时输出滤波器结果数据。
	//类似的时延长度一方面可通过精确计算获取,但更好的方法是通过行为仿真查看
	reg signed [28:0] sum;
	reg signed [28:0] yout;
	always @(posedge clk or posedge rst)
		if (rst)
			begin 
				sum = 29'd0; 
				yout <= 29'd0;
			end
		else
			begin
				if (count==2)
					begin
						yout <= sum;
						sum = 29'd0;
						sum =sum + Mout;
					end
				else
				   sum = sum + Mout;
			end
	
	assign Yout = yout;
			
endmodule

2.1.3 仿真测试代码

`timescale 1 ns/ 1 ns //设置仿真时间单位:ns
module fir_filter_tb();
// constants                                           
// general purpose registers
reg eachvec;
// test vector input registers
reg [11:0] Xin;
reg clk;
reg rst;
wire clk_data; //数据时钟,速率为系统时钟clk的1/8;
// wires                                               
wire [28:0]  Yout;

// assign statements (if any)                          
fir_filter i1 (
// port map - connection between master ports and signals/registers   
	.Xin(Xin),
	.Yout(Yout),
	.clk(clk),
	.rst(rst)
);

parameter clk_period=62500; //设置时钟信号周期(频率):16kHz
parameter clk_period_data=clk_period*8;
parameter clk_half_period=clk_period/2;
parameter clk_half_period_data=clk_half_period*8;
parameter data_num=2000;  //仿真数据长度
parameter time_sim=data_num*clk_period; //仿真时间

initial
begin
	//设置时钟信号初值
	clk=1;
	//clk_data=1;
	//设置复位信号
	rst=1;
	#200000 rst=0;
	//设置仿真时间
	#time_sim $finish;
	//设置输入信号初值
	Xin=12'd10;
end

//产生时钟信号
always                                                 
	#clk_half_period clk=~clk;

reg [2:0] cn_clk=3'd0;
always @(posedge clk) cn_clk <= cn_clk + 3'd1;
assign clk_data = cn_clk[2];


//从外部TX文件(SinIn.txt)读入数据作为测试激励
integer Pattern;
reg [11:0] stimulus[1:data_num];
initial
	begin
		//文件必须放置在"工程目录\simulation\modelsim"路径下
		//$readmemb("E4_7_Bin_noise.txt",stimulus);
		$readmemb("E4_7_Bin_s.txt",stimulus);
		Pattern=0;
		repeat(data_num)
			begin 
				@(posedge clk_data);
				Pattern=Pattern+1;
				Xin=stimulus[Pattern];
			end
	end

//将仿真数据dout写入外部TXT文件中(out.txt)
integer file_out;
initial 
begin
   //文件放置在"工程目录\simulation\modelsim"路径下                                                  
	//file_out = $fopen("E4_7_Noiseout.txt");
	file_out = $fopen("E4_7_Sout.txt");
	if(!file_out)
		begin
			$display("could not open file!");
			$finish;
		end
end
wire rst_write;
wire signed [28:0] dout_s;
assign dout_s = Yout;                //将dout转换成有符号数据
assign rst_write = clk_data & (!rst);//产生写入时钟信号,复位状态时不写入数据
always @(posedge rst_write )
	$fdisplay(file_out,"%d",dout_s);
	
endmodule

2.1.4 仿真结果图

可见,输出结果dout_a,与输入数据的时钟上升沿clk_data差两个时钟周期(输入数据的时钟上升沿输入数据),正如代码中所写,考虑到加法器和乘法器的延时,所以延迟两个时钟周期读输出数据,及cn_clk=2时输出。

 持续更新中

  • 6
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值