赞
踩
目录
一个 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 滤波器在实际工程中有着较为广泛的应用。
Fir滤波器的结构形式分为直接型、级联型、频率取样型、快速卷积型四种,其中最常用,最简单的是直接型。FPGA实现直接型Fir滤波器时,可有采用串行结构、并行结构、分布式结构,以及直接使用器件提供的ip核,下面对每种结构的实现方法进行介绍,代码实现以及仿真测试。
- //这是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
- `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
可见,输出结果dout_a,与输入数据的时钟上升沿clk_data差两个时钟周期(输入数据的时钟上升沿输入数据),正如代码中所写,考虑到加法器和乘法器的延时,所以延迟两个时钟周期读输出数据,及cn_clk=2时输出。
持续更新中
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。