Serial FIR Filter Design

Design Description

The design parameters are unchanged and consistent with the parallel FIR filter parameters. That is, for the mixed sine wave input signals with frequencies of 7.5 MHz and 250 KHz, after passing through the FIR filter, the high-frequency signal at 7.5 MHz is filtered out, leaving only the 250 KHz signal.

输入频率:    7.5MHz 和 250KHz
采样频率:    50MHz
阻带:           1MHz-6MHz
阶数:           15 (N=15)

In the serial design, the 16 delayed data samples are sequentially processed through multiplication and addition operations in a time-division manner within 16 clock cycles, and then the filtered value is output under the clock drive. Considering the symmetry of the FIR filter coefficients, the period for computing one filtered output value can be reduced to 8 cycles. In the serial design, only one multiplication operation is performed per cycle, so only one multiplier is needed in the design. At this point, data needs to be validly input once every 8 clock cycles. However, to ensure the correctness of the output signal frequency, the working clock must be 8 times the sampling frequency, i.e., 400 MHz. The advantage of this method is low resource consumption, but it requires a high working frequency and the data cannot be continuously output.

Serial Design

For the multiplier module code used in the design, refer to the multiplier from the previous pipeline design.

For convenient and fast simulation, the multiplication sign can also be used directly*to complete the multiplication operation. A macro definition SAFE_DESIGN is added in the design to select which multiplier to use.

The FIR filter coefficients can be generated by MATLAB; see the appendix for details.

Example

/**********************************************************
>> 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,   // System working clock, 400MHz
    input                en ,   // Input data valid signal
    input        [11:0]  xin,   // Input mixed-frequency signal data
    output               valid, // Output data valid signal
    output       [28:0]  yout   // Output data
    );
 
   //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) Input data shift section
    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-cycle count
        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    //Read valid data once every 8 cycles
            xin_reg[0] <= xin ;
            for (j=0; j<15; j=j+1) begin
                xin_reg[j+1] <= xin_reg[j] ; // Data shift
            end
        end
    end
 
    //(2) Coefficients are symmetric; add the first and last data of the 16 shift registers
    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) Multiplication operation, using only one multiplier
    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    //Instantiate the self-designed pipeline multiplier
        (.clk        (clk),
         .rstn       (rstn),
         .data_rdy   (en_r[index_mult]),  //Pay attention to the data timing correspondence
         .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 ;  //Direct multiplication
        end
    end
    wire                 en_mult = en_r[2];
`endif
 
    //(4) Accumulation, 8 groups of 25-bit data -> 1 group of 29-bit data
    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  //Count 8 cycles
            cnt_acc_r <= 'b0 ;
        end
        else if (en_mult || cnt_acc_r != 0) begin //As long as en is valid, the counting continues
            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 //Output the filtered value in the 8th accumulation cycle
            sum      <= sum + mout;
            valid_r  <= 1'b1 ;
        end
        else if (en_mult && cnt_acc_r == 0) begin //Initialization
            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
 
    //Latch valid output data on the clock edge so that the output signal does not change too frequently
    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) Output data valid delay; that is, discard the first 15 filtered values
    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

The testbench is written as follows. Its main function is to continuously input the mixed sine wave signal data of 250KHz and 7.5MHz without interruption. The input mixed signal data can also be generated by MATLAB; see the appendix for details.

Here, the working frequency is 400MHz, but the input data and the input data valid signal should both be input at a frequency of 50MHz.

Example

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) ; //Idle for 7 cycles, provide data in the 8th cycle
            en          = 1 ;
            xin         = stimulus[i] ;
            @(negedge clk) ;
            en          = 0 ;         //The input data valid signal only needs to last for one cycle
            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

Simulation Results

From the simulation results in the figure below, it can be seen that the signal after the FIR filter contains only one low-frequency signal (250KHz), and the high-frequency signal (7.5MHz) has been filtered out. For a more aesthetically pleasing waveform, the filtered data after the first 16 samples are taken as the valid output.

The locally enlarged waveform is shown in the figure below. At this time, the input data valid signal en and the output data valid signal valid are pulse signals with the same period (50MHz), and are not continuously valid. However, since the working clock is 400MHz, the output will also appear as a sine wave signal with a frequency of 250KHz at a sampling frequency of 50MHz.

Appendix: MATLAB Usage (same as"Parallel FIR Filter Design"consistent)

Generate FIR Filter Coefficients

Open MATLAB, enter the command in the command window: fdatool.

Then the following window will open. Set it according to the FIR filter parameters, as shown below.

The FIR implementation method selected here is the least-squares method (Least-squares). Different implementation methods also produce different filtering effects.

Click File -> Export

Export the filter parameters and store them in the variable coef, as shown in the figure below.

At this time, the coef variable should be floating-point data. Multiply it by a certain factor to scale it up, and then take its approximate fixed-point data as the FIR filter parameters in the design. Here the scaling factor is 2048, and the results are as follows.

Generate the Input Mixed Signal

The reference code for generating the mixed input signal using MATLAB is as follows.

The signal is unsigned fixed-point data with a bit width of 12 bits, stored in the filecosx0p25m7p5m12bit.txt 。

Example

clear all;close all;clc;
%=======================================================
% generating a cos wave data with txt hex format
%=======================================================

fc          = 0.25e6 ;      %Center frequency
fn          = 7.5e6 ;       %Clutter frequency
Fs          = 50e6 ;        %Sampling frequency
T           = 1/fc ;        %Signal period
Num         = Fs * T ;      %Number of signal sampling points per period
t           = (0:Num-1)/Fs ;      %Discrete time
cosx        = cos(2*pi*fc*t) ;    %Center frequency sine signal
cosn        = cos(2*pi*fn*t) ;    %Clutter signal
cosy        = mapminmax(cosx + cosn) ;     %Amplitude expanded to (-1,1) range
cosy_dig    = floor((2^11-1) * cosy + 2^11) ;     %Amplitude expanded to0~4095
fid         = fopen('cosx0p25m7p5m12bit.txt', 'wt') ;  %Write data file
fprintf(fid, '%x\n', cosy_dig) ;
fclose(fid) ;
 
%Time domain waveform
figure(1);
subplot(121);plot(t,cosx);hold on ;
plot(t,cosn) ;
subplot(122);plot(t,cosy_dig) ;
 
%Frequency domain waveform
fft_cosy    = fftshift(fft(cosy, Num)) ;
f_axis      = (-Num/2 : Num/2 - 1) * (Fs/Num) ;
figure(5) ;
plot(f_axis, abs(fft_cosy)) ;

Source Code Download

Download