Verilog99题——38-42题(Fir滤波器)

前言

不忘出芯veriloig99题的38-42题是Fir滤波器的Verilog实现

FIR理论

线性时不变( linear time-invariant,LTI)滤波器是一种最为普通的数字滤波器。LTI通过如下的线性卷积过程与其输入信号相互作用。

y[n]=x[n]f[n]=kx[k]f[nk]=kf[k]x[nk]y[n]=x[n] * f[n]=\sum_{k} x[k] f[n-k]=\sum_{k} f[k] x[n-k]

直接形式的FIR滤波器

带有常系数的FIR滤波器是一种LTI滤波器。对于输入序列x[n]x[n],长度为L或N=L-1阶FIR的输出可通过上式的有限卷积和的形式给出,即:

y[n]=x[n]f[n]=k=0L1f[k]x[nk]y[n]=x[n] * f[n]=\sum_{k=0}^{L-1} f[k] x[n-k]

其中f[0]0f[0] \neq 0直到f[L1]0f[L-1] \neq 0都是FIR的L个系数,同时也对应FIR的脉冲响应。对于LTI系统,在z域描述上式更便捷:

Y(z)=F(z)X(z)Y(z)=F(z) X(z)

其中F(z)F(z)是FIR的传递函数,其在z域的定义:F(z)=k=0L1f[k]zkF(z)=\sum_{k=0}^{L-1} f[k] z^{-k}

下图给出长度为L的LTI型FIR滤波器的图解。FIR滤波器由“抽头延迟线”、加法器、乘法器混合构成。该图展示了直接形式的FIR滤波器。

直接形式的FIR滤波器

FIR滤波器的转置结构

FIR滤波器的卷积公式可以表示成另一种形式,即

y[n]=f[n]x[n]=k=0L1f[nk]x[k]y[n]=f[n] * x[n]=\sum_{k=0}^{L-1} f[n-k] x[k]

这种形式的FIR滤波器结构是直接FIR滤波器的转置结构。如下图所示,给出FIR滤波器的转置结构。

FIR滤波器的转置结构结构

两种结构的粗略对比

直接FIR滤波器结构需要在两个加法器之间插入额外的流水线寄存器,来降低加法器树的延迟,以实现高吞吐量。而转置结构在加法器之间都存在寄存器,因此在不插入额外寄存器的情况下,就可以达到和直接FIR滤波器相同的吞吐量。转置滤波器结构需要更多的延迟寄存器,转置FIR延迟寄存器的个数比直接FIR滤波器使用的寄存器的个数的2倍还要多一点。在某种程度上,这可能是转置FIR相比于标准FIR的一个缺点。但是,在FPGA中,由于可用的延迟/触发器的数量很多,所以可以不必太在意这个问题。

改进FIR硬件实现的方法

  1. 通过对系数采用CSD编码,如3.75=22223.75 = 2^2-2^{-2}
  2. 注意系数是否具有对称性,并加以利用。
  3. 采用流水线技术。

接下来看题目:

38.用verilog实现一个3-tap低通FIR滤波器,输入输出为8bit无符号数,滤波器系数[1/4 1/2 1/4]

module fir_lpf_3tap (
input clk,
input rst_n,
input [7:0] din,
output [7:0] dout
);
...
endmodule

verilog描述

本题采用直接结构的FIR,由于系数简单直接采用截位的方式实现,并为采用流水线和对称结构。

module fir_lpf_3tap(
    input               clk     , 
    input               rst_n   , 
    input   [7:0]       din     , 
    output  reg [7:0]   dout
    );

reg [7:0] tap [0:2];
integer i;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        for (i = 0; i<=2; i=i+1) begin
            tap[i] <= 0;
        end
        dout <= 8'd0;
    end else begin
        // dout <= (tap[0] >> 2) + (tap[1]  >> 1) + (tap[2] >> 2);
        dout <= tap[0][7:2] + tap[1][7:1] + tap[2][7:2];
        /* for (i = 2; i > 0; i=i-1) begin
            tap[i] <= tap[i-1];
        end */
        tap[2] <= tap[1];
        tap[1] <= tap[0];
        tap[0] <= din;
    end
end
endmodule

综合结果

直接结构的3-tapFIR

该综合结果与直接形式的FIR滤波器的结构图对比,可以很明显看出两种结构完全一致。

39. 用verilog实现一个3-tap低通FIR滤波器,输入输出为8bit无符号数,滤波器系数[1/4 1/2 1/4],支持bypass功能:fir_bypass为1时输出原始数据。

注:bypass即为全通

module fir_lpf_3tap (
input clk,
input rst_n,
input fir_bypass,
input [7:0] din,
output [7:0] dout
);
...
endmodule

verilog描述

module fir_lpf_3tap(
    input               clk         , 
    input               rst_n       , 
    input               fir_bypass  ,
    input       [7:0]   din         , 
    output  reg [7:0]   dout        
    );

reg [7:0] tap [0:2];
integer i;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        for (i = 0; i<=2; i=i+1) begin
            tap[i] <= 0;
        end
        dout <= 8'd0;
    end else if (!fir_bypass)begin
        // dout <= (tap[0] >> 2) + (tap[1]  >> 1) + (tap[2] >> 2);
        dout <= tap[0][7:2] + tap[1][7:1] + tap[2][7:2];
        /* for (i = 2; i > 0; i=i-1) begin
            tap[i] <= tap[i-1];
        end */
        tap[2] <= tap[1];
        tap[1] <= tap[0];
        tap[0] <= din;
    end else begin
        dout <= din;
    end
end
endmodule

40. 用verilog实现一个低通FIR滤波器,输入输出为8bit无符号数,滤波器系数根据mode选择:

mode 0:bypass
mode 1:[1 2 1]/4
mode 2:[1 2 2 2 1]/8
mode 3:[1 2 3 4 3 2 1]/16

module fir_lpf (
input clk,
input rst_n,
input [1:0] mode,
input [7:0] din,
output [7:0] dout
);
...
endmodule

简要分析

  1. 系数是关于中间对称的。
  2. 系数可以通过移位实现。
  3. 可以使用流水线:
    1. 延迟(获取输入采样)。
    2. 与系数相乘,获得乘积(乘法、移位)。
    3. 乘积作和(计算结果)。

verilog描述

仍然采用直接形式设计,但是使用了系数对称和流水线来改进设计。

module test40(
    input               clk     , 
    input               rst_n   , 
    input       [1:0]   mode    , 
    input       [7:0]   din     , 
    output      [7:0]   dout    
    );

reg [7:0] tap [6:0];
reg [8:0] product [3:0];
reg [7:0] dout_r;
//延迟(获取输入采样)
integer i;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        for (i = 0; i <= 6; i = i + 1) begin
            tap[i] <= 8'd0;
        end
    end else begin
        for (i = 6; i > 0; i = i - 1) begin
            tap[i] <= tap[i-1];
        end
        tap[0] <= din;
    end
end
//与系数相乘,获得乘积(乘法、移位)
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        for (i = 0; i <= 3; i = i + 1) begin
            product[i] <= 8'd0;
        end
    end else begin
        case (mode)
            2'd1: begin
                product[0] = (tap[0] + tap[2]) >> 2;
                product[1] = tap[1] >> 1;
            end
            2'd2: begin
                product[0] = (tap[0] + tap[4]) >> 3;
                product[1] = (tap[1] + tap[2] + tap[3]) >> 2;
            end
            2'd3: begin
                product[0] = (tap[0] + tap[6]) >> 4;
                product[1] = (tap[1] + tap[5]) >> 3;
                product[2] = ((tap[2] + tap[4]) * 3) >> 4;
                product[3] = tap[3] >> 2;
            end
            default: ;
        endcase
    end
end
//乘积作和(计算结果)
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        dout_r <= 8'd0;
    end else begin
        case (mode)
            2'd1: dout_r <= product[0] + product[1];
            2'd2: dout_r <= product[0] + product[1];
            2'd3: dout_r <= (product[0] + product[1]) + (product[2] + product[3]);
            default: dout_r <= din;
        endcase
    end
end

assign dout = dout_r;
endmodule

41. 用verilog实现一个参数化的FIR滤波器。

可配置参数包括 1. 输入输出数据位宽N 2. 滤波器阶数T 3. 滤波器系数位宽M
(输入数据与滤波器系数为无符号数)

verilog描述

//基于SOP(乘积和)的转置结构的FIR滤波器。
//load_sw为低时为缓存系数过程,load_sw为高时为计算和输出过程。
module fir
#(
    parameter   DIN_WIDTH    = 8    ,   //输入数据位宽
                FIR_TAP      = 4    ,   //滤波器阶数
                COEF_WIDTH   = 8    ,   //系数位宽
                DOUT_WIDTH   = 8       //输出数据位宽
)
(
    input           clk         ,
    input           rst_n       ,
    input           load_sw     ,               // load/run switch
    input       [ DIN_WIDTH-1:0]    data_in ,   //数据
    input       [COEF_WIDTH-1:0]    coff_in ,   //系数
    output      [DOUT_WIDTH-1:0]    data_out    //输出
    );

localparam  PROD_WIDTH   = DIN_WIDTH + COEF_WIDTH, //乘积位宽
            ADD_WIDTH    = PROD_WIDTH + clogb2(FIR_TAP) ;//乘积和位宽

reg [DIN_WIDTH-1:0] data_in_r;
wire [ADD_WIDTH-1:0] data_out_w;

reg [COEF_WIDTH-1:0] coff [FIR_TAP-1:0];    //系数
wire [PROD_WIDTH-1:0] prod [FIR_TAP-1:0];   //乘积
reg [ADD_WIDTH-1:0] sum [FIR_TAP-1:0];      //乘积和
//----> Load Data or Coefficient
always @(posedge clk or negedge rst_n) begin: LOAD
    integer i;
    if (!rst_n) begin
        for (i = 0; i <= FIR_TAP-1; i = i +1) begin
            coff[i] <= 'd0;
        end
        data_in_r <= 'd0;
    end else if(!load_sw) begin     //缓存系数
        coff[FIR_TAP-1] <= coff_in;
        for (i = FIR_TAP-1; i > 0; i = i -1) begin
            coff[i-1] <= coff[i];
        end
    end else begin
        data_in_r <= data_in;       //缓存数据
    end
end
//----> Compute products
genvar k;
generate
    for (k = 0; k < FIR_TAP; k = k + 1) begin
        assign prod[k] = data_in_r * coff[k];
    end
endgenerate
//----> Compute sum-of-products
always @(posedge clk or negedge rst_n) begin: SOP
    integer m;
    if (!rst_n) begin
        for (m = 0; m < FIR_TAP; m = m + 1) begin
            sum[m] <= 'd0;
        end
    end else begin
        for (m = 0; m < FIR_TAP - 1; m = m + 1) begin
            sum[m] <= prod[m] + sum[m+1];
        end
        sum[FIR_TAP-1] <= prod[FIR_TAP-1];
    end
end
assign data_out_w = sum[0];

assign data_out = data_out_w[ADD_WIDTH-1:ADD_WIDTH-DOUT_WIDTH];

function integer clogb2 (input integer depth);
    begin
        for(clogb2=0; depth>1; clogb2=clogb2+1)
        depth = depth >> 1;
    end
endfunction
endmodule

综合结果

基于SOP(乘积和)的转置结构的可编程FIR滤波器
请查看原图,并与FIR滤波器的转置结构图进行对比。

42. 用verilog实现一个3-tap低通FIR滤波器,Y通路输入输出为8bit无符号数,滤波器系数[1/4 1/2 1/4]。C通路bypass输出。

module fir_lpf_3tap_YC (
input clk,
input rst_n,
input [7:0] yin,
output [7:0] yout,
input [7:0] cin,
output [7:0] cout
);
...
endmodule

verilog描述

module fir_lpf_3tap(
    input               clk     , 
    input               rst_n   , 
    input       [7:0]   yin     , 
    output      [7:0]   yout    ,
    input       [7:0]   cin     ,
    output      [7:0]   cout    
    );

reg [7:0] tap [0:2];
reg [8:0] product [0:1];
reg [7:0] yout_r    ;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        tap[0] <= 8'd0;
        tap[1] <= 8'd0;
        tap[2] <= 8'd0;
    end else begin
        tap[2] <= tap[1];
        tap[1] <= tap[0];
        tap[0] <= yin;
    end
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        product[0] <= 9'd0;
        product[1] <= 9'd0;
    end else begin
        product[0] = (tap[0] + tap[2]) >> 2;
        product[1] = tap[1] >> 1;        
    end
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        yout_r <= 8'd0;
    end else begin
        yout_r <= product[0] + product[1];
    end
end

assign yout = yout_r;
assign cout = cin;

endmodule

即将入行的数字IC设计工程师