Verilog 串行 FIR 滤波器设计
串行 FIR 滤波器设计参数不变,与并行 FIR 滤波器参数一致。即,输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号,经过 FIR 滤波器后,高频信号 7.5MHz 被滤除,只保留 250KMHz 的信号。
输入频率: 7.5MHz 和 250KHz 采样频率: 50MHz 阻带: 1MHz-6MHz 阶数: 15 (N=15)
串行设计,就是在 16 个时钟周期内对 16 个延时数据分时依次进行乘法、加法运算,然后在时钟驱动下输出滤波值。考虑到 FIR 滤波器系数的对称性,计算一个滤波输出值的周期可以减少到 8 个。串行设计时每个周期只进行一次乘法运算,所以设计中只需一个乘法器即可。此时数据需要每 8 个时钟周期有效输入一次,但是为了保证输出信号频率的正确性,工作时钟需要为采样频率的 8 倍,即 400MHz。这种方法的优点是资源耗费少,但是工作频率要求高,数据不能持续输出。
串行设计
设计中使用到的乘法器模块代码,可参考之前流水线式设计的乘法器。
为方便快速仿真,也可以直接使用乘号 * 完成乘法运算,设计中加入宏定义 SAFE_DESIGN 来选择使用哪种乘法器。
FIR 滤波器系数可由 matlab 生成,具体见附录。
/********************************************************** >> Description : fir study with serial tech >> V190403 : Fs:50Mhz, fstop:1-6Mhz, order:16, sys clk:400MHz ***********************************************************/ `define SAFE_DESIGN module fir_serial_low( input rstn, input clk, // 系统工作时钟,400MHz input en , // 输入数据有效信号 input [11:0] xin, // 输入混合频率的信号数据 output valid, // 输出数据有效信号 output [28:0] yout // 输出数据 ); //delay of input data enable reg [11:0] en_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin en_r[11:0] <= 'b0 ; end else begin en_r[11:0] <= {en_r[10:0], en} ; end end //fir coeficient wire [11:0] coe[7:0] ; assign coe[0] = 12'd11 ; assign coe[1] = 12'd31 ; assign coe[2] = 12'd63 ; assign coe[3] = 12'd104 ; assign coe[4] = 12'd152 ; assign coe[5] = 12'd198 ; assign coe[6] = 12'd235 ; assign coe[7] = 12'd255 ; //(1) 输入数据移位部分 reg [2:0] cnt ; integer i, j ; always @(posedge clk or negedge rstn) begin if (!rstn) begin cnt <= 3'b0 ; end else if (en || cnt != 0) begin cnt <= cnt + 1'b1 ; //8个周期计数 end end reg [11:0] xin_reg[15:0]; always @(posedge clk or negedge rstn) begin if (!rstn) begin for (i=0; i<16; i=i+1) begin xin_reg[i] <= 12'b0; end end else if (cnt == 3'd0 && en) begin //每8个周期读入一次有效数据 xin_reg[0] <= xin ; for (j=0; j<15; j=j+1) begin xin_reg[j+1] <= xin_reg[j] ; // 数据移位 end end end //(2) 系数对称,16个移位寄存器数据进行首位相加 reg [11:0] add_a, add_b ; reg [11:0] coe_s ; wire [12:0] add_s ; wire [2:0] xin_index = cnt>=1 ? cnt-1 : 3'd7 ; always @(posedge clk or negedge rstn) begin if (!rstn) begin add_a <= 13'b0 ; add_b <= 13'b0 ; coe_s <= 12'b0 ; end else if (en_r[xin_index]) begin //from en_r[1] add_a <= xin_reg[xin_index] ; add_b <= xin_reg[15-xin_index] ; coe_s <= coe[xin_index] ; end end assign add_s = {add_a} + {add_b} ; //(3) 乘法运算,只用一个乘法 reg [24:0] mout ; `ifdef SAFE_DESIGN wire en_mult ; wire [3:0] index_mult = cnt>=2 ? cnt-1 : 4'd7 + cnt[0] ; mult_man #(13, 12) u_mult_single //例化自己设计的流水线乘法器 (.clk (clk), .rstn (rstn), .data_rdy (en_r[index_mult]), //注意数据时序对应 .mult1 (add_s), .mult2 (coe_s), .res_rdy (en_mult), .res (mout) ); `else always @(posedge clk or negedge rstn) begin if (!rstn) begin mout <= 25'b0 ; end else if (|en_r[8:1]) begin mout <= coe_s * add_s ; //直接乘 end end wire en_mult = en_r[2]; `endif //(4) 积分累加,8组25bit数据 -> 1组 29bit 数据 reg [28:0] sum ; reg valid_r ; //mult output en counter reg [4:0] cnt_acc_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin cnt_acc_r <= 'b0 ; end else if (cnt_acc_r == 5'd7) begin //计时8个周期 cnt_acc_r <= 'b0 ; end else if (en_mult || cnt_acc_r != 0) begin //只要en有效,计时不停 cnt_acc_r <= cnt_acc_r + 1'b1 ; end end always @(posedge clk or negedge rstn) begin if (!rstn) begin sum <= 29'd0 ; valid_r <= 1'b0 ; end else if (cnt_acc_r == 5'd7) begin //在第8个累加周期输出滤波值 sum <= sum + mout; valid_r <= 1'b1 ; end else if (en_mult && cnt_acc_r == 0) begin //初始化 sum <= mout ; valid_r <= 1'b0 ; end else if (cnt_acc_r != 0) begin //acculating between cycles sum <= sum + mout ; valid_r <= 1'b0 ; end end //时钟锁存有效的输出数据,为了让输出信号不是那么频繁的变化 reg [28:0] yout_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin yout_r <= 'b0 ; end else if (valid_r) begin yout_r <= sum ; end end assign yout = yout_r ; //(5) 输出数据有效延迟,即滤波数据丢掉前15个滤波值 reg [4:0] cnt_valid ; always @(posedge clk or negedge rstn) begin if (!rstn) begin cnt_valid <= 'b0 ; end else if (valid_r && cnt_valid != 5'd16) begin cnt_valid <= cnt_valid + 1'b1 ; end end assign valid = (cnt_valid == 5'd16) & valid_r ; endmodule
testbench
testbench 编写如下,主要功能就是不间断连续的输入 250KHz 与 7.5MHz 的正弦波混合信号数据。输入的混合信号数据也可由 matlab 生成,具体见附录。
其中,工作频率为 400MHz,但输入数据和输入数据有效信号应当都保持 50MHz 的频率输入。
module test ; //input reg clk ; reg rst_n ; reg en ; reg [11:0] xin ; //output wire [28:0] yout ; wire valid ; parameter SIMU_CYCLE = 64'd1000 ; parameter SIN_DATA_NUM = 200 ; //===================================== // 8*50MHz clk generating localparam TCLK_HALF = (10_000 >>3); initial begin clk = 1'b0 ; forever begin # TCLK_HALF clk = ~clk ; end end //============================ // reset and finish initial begin rst_n = 1'b0 ; # 30 rst_n = 1'b1 ; # (TCLK_HALF * 2 * 8 * SIMU_CYCLE) ; $finish ; end //======================================= // read cos data into register reg [11:0] stimulus [0: SIN_DATA_NUM-1] ; integer i ; initial begin $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ; en = 0 ; i = 0 ; xin = 0 ; # 200 ; forever begin repeat(7) @(negedge clk) ; //空置7个周期,第8个周期给数据 en = 1 ; xin = stimulus[i] ; @(negedge clk) ; en = 0 ; //输入数据有效信号只持续一个周期即可 if (i == SIN_DATA_NUM-1) i = 0 ; else i = i + 1 ; end end fir_serial_low u_fir_serial ( .clk (clk), .rstn (rst_n), .en (en), .xin (xin), .valid (valid), .yout (yout)); endmodule
仿真结果
由下图仿真结果可知,经过 FIR 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。为了波形更加的美观,取 16 个之后的滤波数据作为有效输出。
波形局部放大后如下图所示,此时输入数据有效信号 en 与输出数据有效信号 valid 是周期(50MHz)相同的脉冲信号,不是持续有效的。但工作时钟为 400MHz,所以输出也会呈现为 50MHz 采样频率下的 250KHz 频率的正弦波信号。
附录:matlab 使用(与《并行 FIR 滤波器设计》一致)
生成 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 CIC 滤波器设计
积分梳状滤波器(CIC,Cascaded Integrator Comb),一般用于数字下变频(DDC)和数字上变频(DUC)系统。CIC 滤波器结构简单,没有乘法器,只有加法器、积分器和寄存器,资源消耗少,运算速率高,可实现高速滤波,常用在输入采样率最高的第一级,在多速率信号处理系统中具有着广泛应用。