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 阶数: 15N-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 生成,具体见附录。

 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
testbench

仿真结果

由下图仿真结果可知,经过 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 滤波器设计

posted on 2021-07-07 20:34  一曲挽歌  阅读(1918)  评论(0编辑  收藏  举报

导航