FPGA基础——并行FIR滤波器
FIR(Finite Impulse Response)滤波器是一种有限长单位冲激响应滤波器,又称为非递归型滤波器。
FIR 滤波器具有严格的线性相频特性,同时其单位响应是有限长的,因而是稳定的系统,在数字通信、图像处理等领域都有着广泛的应用。
一、FIR 滤波器原理
FIR 滤波器是有限长单位冲击响应滤波器。直接型结构如下:
FIR 滤波器本质上就是输入信号与单位冲击响应函数的卷积,表达式如下:
FIR 滤波器有如下几个特性:
- 响应是有限长序列。
- 系统函数在 |z| > 0 处收敛,极点全部在 z=0 处,属于因果系统。
- 结构上是非递归的,没有输出到输入的反馈。
- 输入信号相位响应是线性的,因为响应函数 h(n) 系数是对称的。
- 输入信号的各频率之间,相对相位差也是固定不变的。
- 时域卷积等于频域相乘,因此该卷积相当于筛选频谱中各频率分量的增益倍数。某些频率分量保留,某些频率分量衰减,从而实现滤波的效果。
二、并行 FIR 滤波器设计
设计说明
输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号,经过 FIR 滤波器后,高频信号 7.5MHz 被滤除,只保留 250KHz 的信号。设计参数如下:
输入频率:7.5MHz 和 250KHz 采样频率:50MHz 阻带: 1MHz ~ 6MHz 阶数: 15(N-1=15)
由 FIR 滤波器结构可知,阶数为 15 时,FIR 的实现需要 16 个乘法器,15 个加法器和 15 组延时寄存器。为了稳定第一拍的数据,可以再多用一组延时寄存器,即共用 16 组延时寄存器。由于 FIR 滤波器系数的对称性,乘法器可以少用一半,即共使用 8 个乘法器。
并行设计,就是在一个时钟周期内对 16 个延时数据同时进行乘法、加法运算,然后在时钟驱动下输出滤波值。这种方法的优点是滤波延时短,但是对时序要求比较高。
并行设计
设计中使用到的乘法器模块代码,可参考之前流水线式设计的乘法器。
为方便快速仿真,也可以直接使用乘号 * 完成乘法运算,设计中加入宏定义 SAFE_DESIGN 来选择使用哪种乘法器。
FIR 滤波器系数可由 matlab 生成,具体见附录。
1 /***********************************************************
2 >> V201001 : Fs:50Mhz, fstop:1Mhz-6Mhz, order: 15
3 ************************************************************/
4 `define SAFE_DESIGN
5
6 module fir_guide (
7 input rstn, //复位,低有效
8 input clk, //工作频率,即采样频率
9 input en, //输入数据有效信号
10 input [11:0] xin, //输入混合频率的信号数据
11 output valid, //输出数据有效信号
12 output [28:0] yout //输出数据,低频信号,即250KHz
13 );
14
15 //data en delay
16 reg [3:0] en_r ;
17 always @(posedge clk or negedge rstn) begin
18 if (!rstn) begin
19 en_r[3:0] <= 'b0 ;
20 end
21 else begin
22 en_r[3:0] <= {en_r[2:0], en} ;
23 end
24 end
25
26 //(1) 16 组移位寄存器
27 reg [11:0] xin_reg[15:0];
28 reg [3:0] i, j ;
29 always @(posedge clk or negedge rstn) begin
30 if (!rstn) begin
31 for (i=0; i<15; i=i+1) begin
32 xin_reg[i] <= 12'b0;
33 end
34 end
35 else if (en) begin
36 xin_reg[0] <= xin ;
37 for (j=0; j<15; j=j+1) begin
38 xin_reg[j+1] <= xin_reg[j] ; //周期性移位操作
39 end
40 end
41 end
42
43 //Only 8 multipliers needed because of the symmetry of FIR filter coefficient
44 //(2) 系数对称,16个移位寄存器数据进行首位相加
45 reg [12:0] add_reg[7:0];
46 always @(posedge clk or negedge rstn) begin
47 if (!rstn) begin
48 for (i=0; i<8; i=i+1) begin
49 add_reg[i] <= 13'd0 ;
50 end
51 end
52 else if (en_r[0]) begin
53 for (i=0; i<8; i=i+1) begin
54 add_reg[i] <= xin_reg[i] + xin_reg[15-i] ;
55 end
56 end
57 end
58
59 //(3) 8个乘法器
60 // 滤波器系数,已经过一定倍数的放大
61 wire [11:0] coe[7:0] ;
62 assign coe[0] = 12'd11 ;
63 assign coe[1] = 12'd31 ;
64 assign coe[2] = 12'd63 ;
65 assign coe[3] = 12'd104 ;
66 assign coe[4] = 12'd152 ;
67 assign coe[5] = 12'd198 ;
68 assign coe[6] = 12'd235 ;
69 assign coe[7] = 12'd255 ;
70 reg [24:0] mout[7:0];
71
72 `ifdef SAFE_DESIGN
73 //流水线式乘法器
74 wire [7:0] valid_mult ;
75 genvar k ;
76 generate
77 for (k=0; k<8; k=k+1) begin
78 mult_man #(13, 12)
79 u_mult_paral (
80 .clk (clk),
81 .rstn (rstn),
82 .data_rdy (en_r[1]),
83 .mult1 (add_reg[k]),
84 .mult2 (coe[k]),
85 .res_rdy (valid_mult[k]), //所有输出使能完全一致
86 .res (mout[k])
87 );
88 end
89 endgenerate
90 wire valid_mult7 = valid_mult[7] ;
91
92 `else
93 //如果对时序要求不高,可以直接用乘号
94 always @(posedge clk or negedge rstn) begin
95 if (!rstn) begin
96 for (i=0 ; i<8; i=i+1) begin
97 mout[i] <= 25'b0 ;
98 end
99 end
100 else if (en_r[1]) begin
101 for (i=0 ; i<8; i=i+1) begin
102 mout[i] <= coe[i] * add_reg[i] ;
103 end
104 end
105 end
106 wire valid_mult7 = en_r[2];
107 `endif
108
109 //(4) 积分累加,8组25bit数据 -> 1组 29bit 数据
110 //数据有效延时
111 reg [3:0] valid_mult_r ;
112 always @(posedge clk or negedge rstn) begin
113 if (!rstn) begin
114 valid_mult_r[3:0] <= 'b0 ;
115 end
116 else begin
117 valid_mult_r[3:0] <= {valid_mult_r[2:0], valid_mult7} ;
118 end
119 end
120
121 `ifdef SAFE_DESIGN
122 //加法运算时,分多个周期进行流水,优化时序
123 reg [28:0] sum1 ;
124 reg [28:0] sum2 ;
125 reg [28:0] yout_t ;
126 always @(posedge clk or negedge rstn) begin
127 if (!rstn) begin
128 sum1 <= 29'd0 ;
129 sum2 <= 29'd0 ;
130 yout_t <= 29'd0 ;
131 end
132 else if(valid_mult7) begin
133 sum1 <= mout[0] + mout[1] + mout[2] + mout[3] ;
134 sum2 <= mout[4] + mout[5] + mout[6] + mout[7] ;
135 yout_t <= sum1 + sum2 ;
136 end
137 end
138
139 `else
140 //一步计算累加结果,但是实际中时序非常危险
141 reg signed [28:0] sum ;
142 reg signed [28:0] yout_t ;
143 always @(posedge clk or negedge rstn) begin
144 if (!rstn) begin
145 sum <= 29'd0 ;
146 yout_t <= 29'd0 ;
147 end
148 else if (valid_mult7) begin
149 sum <= mout[0] + mout[1] + mout[2] + mout[3] + mout[4] + mout[5] + mout[6] + mout[7];
150 yout_t <= sum ;
151 end
152 end
153 `endif
154 assign yout = yout_t ;
155 assign valid = valid_mult_r[0];
156
157 endmodule
testbench
testbench 编写如下,主要功能就是不间断连续的输入 250KHz 与 7.5MHz 的正弦波混合信号数据。输入的混合信号数据也可由 matlab 生成,具体见附录。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 `timescale 1ps/1ps 2 3 module test ; 4 //input 5 reg clk ; 6 reg rst_n ; 7 reg en ; 8 reg [11:0] xin ; 9 //output 10 wire valid ; 11 wire [28:0] yout ; 12 13 parameter SIMU_CYCLE = 64'd2000 ; //50MHz 采样频率 14 parameter SIN_DATA_NUM = 200 ; //仿真周期 15 16 //===================================== 17 // 50MHz clk generating 18 localparam TCLK_HALF = 10_000; 19 initial begin 20 clk = 1'b0 ; 21 forever begin 22 # TCLK_HALF ; 23 clk = ~clk ; 24 end 25 end 26 27 //============================ 28 // reset and finish 29 initial begin 30 rst_n = 1'b0 ; 31 # 30 rst_n = 1'b1 ; 32 # (TCLK_HALF * 2 * SIMU_CYCLE) ; 33 $finish ; 34 end 35 36 //======================================= 37 // read signal data into register 38 reg [11:0] stimulus [0: SIN_DATA_NUM-1] ; 39 integer i ; 40 initial begin 41 $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ; 42 i = 0 ; 43 en = 0 ; 44 xin = 0 ; 45 # 200 ; 46 forever begin 47 @(negedge clk) begin 48 en = 1'b1 ; 49 xin = stimulus[i] ; 50 if (i == SIN_DATA_NUM-1) begin //周期送入数据控制 51 i = 0 ; 52 end 53 else begin 54 i = i + 1 ; 55 end 56 end 57 end 58 end 59 60 fir_guide u_fir_paral ( 61 .xin (xin), 62 .clk (clk), 63 .en (en), 64 .rstn (rst_n), 65 .valid (valid), 66 .yout (yout)); 67 68 endmodule
仿真结果
由下图仿真结果可知,经过 FIR 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。而且输出波形是连续的,能够持续输出。
但是,如红圈所示,波形起始部分呈不规则状态,对此进行放大。
波形起始端放大后如下图所示,可见不规则波形的时间段,即两根竖线之间的时间间隔是 16 个时钟周期。
因为数据是串行输入,设计中使用了 16 组延时寄存器,所以滤波后的第一个正常点应该较第一个滤波数据输出时刻延迟 16 个时钟周期。即数据输出有效信号 valid 应该再延迟 16 个时钟周期,则会使输出波形更加完美。
三、多项分解FIR
待补充。。。
附录:matlab 使用
生成 FIR 滤波器系数
打开 matlab,在命令窗口输入命令: fdatool。
然后会打开如下窗口,按照 FIR 滤波器参数进行设置。
这里选择的 FIR 实现方法是最小二乘法(Least-squares),不同的实现方式滤波效果也不同。
点击 File -> Export
将滤波器参数输出,存到变量 coef 中,如下图所示。
此时 coef 变量应该是浮点型数据。对其进行一定倍数的相乘扩大,然后取其近似的定点型数据作为设计中的 FIR 滤波器参数。这里取扩大倍数为 2048,结果如下所示。
生成输入的混合信号
利用 matlab 生成混合的输入信号参考代码如下。
信号为无符号定点型数据,位宽宽度为 12bit,存于文件 cosx0p25m7p5m12bit.txt。
clear all;close all;clc;
%=======================================================
% generating a cos wave data with txt hex format
%=======================================================
fc = 0.25e6 ; % 中心频率
fn = 7.5e6 ; % 杂波频率
Fs = 50e6 ; % 采样频率
T = 1/fc ; % 信号周期
Num = Fs * T ; % 周期内信号采样点数
t = (0:Num-1)/Fs ; % 离散时间
cosx = cos(2*pi*fc*t) ; % 中心频率正弦信号
cosn = cos(2*pi*fn*t) ; % 杂波信号
cosy = mapminmax(cosx + cosn) ; %幅值扩展到(-1,1) 之间
cosy_dig = floor((2^11-1) * cosy + 2^11) ; %幅值扩展到 0~4095
fid = fopen('cosx0p25m7p5m12bit.txt', 'wt') ; %写数据文件
fprintf(fid, '%x\n', cosy_dig) ;
fclose(fid) ;
%时域波形
figure(1);
subplot(121);plot(t,cosx);hold on ;
plot(t,cosn) ;
subplot(122);plot(t,cosy_dig) ;
%频域波形
fft_cosy = fftshift(fft(cosy, Num)) ;
f_axis = (-Num/2 : Num/2 - 1) * (Fs/Num) ;
figure(5) ;
plot(f_axis, abs(fft_cosy)) ;
原文链接:Verilog 并行 FIR 滤波器设计