Verilog CIC 滤波器设计(代码自取)

前言:积分梳状滤波器(CIC,Cascaded Integrator Comb),一般用于数字下变频(DDC)和数字上变频(DUC)系统。CIC 滤波器结构简单,没有乘法器,只有加法器、积分器和寄存器,资源消耗少,运算速率高,可实现高速滤波,常用在输入采样率最高的第一级,在多速率信号处理系统中具有着广泛应用。

1. DDC 原理

DDC 主要由本地振荡器(NCO) 、混频器、滤波器等组成,如下图所示。
在这里插入图片描述
DDC 将中频信号与振荡器产生的载波信号进行混频,信号中心频率被搬移,再经过抽取滤波,恢复原始信号,实现了下变频功能。

中频数据采样时,需要很高的采样频率来确保 ADC(模数转换器)采集到信号的信噪比。经过数字下变频后,得到的基带信号采样频率仍然是 ADC 采样频率,所以数据率很高。此时基带信号的有效带宽往往已经远小于采样频率,所以利用抽取、滤波进行数据速率的转换,使采样率降低,避免资源的浪费和设计的困难,就成为 DDC 不可缺少的一部分。

而采用 CIC 滤波器进行数据处理,是 DDC 抽取滤波部分最常用的方法。

2.带通采样定理

在这里插入图片描述

3. DDC 频谱搬移

例如一个带宽信号中心频率为 60MHz,带宽为 8MHz, 则频率范围为 56MHz ~ 64MHz,m 的可取值范围为 0 ~ 7。取 m=1, 则采样频率范围为 64MHz ~ 112MHz。

取采样频率为 80MHz,设 NCO 中心频率为 20 MHz,下面讨论复信号频谱搬移示意图。

(1)考虑频谱的对称性,输入复信号的频谱示意图如下:
在这里插入图片描述
(2)80MHz 采样频率采样后,56~64MHz 的频带被搬移到了 -24~ -16MHz 与 136 ~ 144MHz(高于采样频率被滤除)的频带处,-64~ -56MHz 的频带被搬移到 -144~ -136MHz(高于采样频率被滤除)与 16~24MHz 的频带处。

采样后频带分布如下:
在这里插入图片描述
(3)信号经过 20MHz NCO 的正交电路后, -24~ -16MHz 的频带被搬移到 -4~4MHz 与 -44~ -36MHz 的频带处,16~24MHz 的频带被搬移到 -4~4MHz 与 36~44MHz 的频带处,如下所示。
在这里插入图片描述
(4)此时中频输入的信号已经被搬移到零中频基带处。

-44~ -36MHz 和 36~44MHz 的带宽信号是不需要的,可以滤除;-4~4MHz 的零中频信号数据速率仍然是 80MHz,可以进行抽取降低数据速率。而 CIC 滤波,就是要完成这个过程。

上述复习了很多数字信号处理的内容,权当抛 DDC 的砖,引 CIC 的玉。

4.CIC 滤波器原理

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
多级 CIC 滤波器

单级 CIC 滤波器的阻带衰减较差,为了提高滤波效果,抽取滤波时往往会采用多级 CIC 滤波器级联的结构。

实现多级直接级联的 CIC 滤波器在设计和资源上并不是最优的方式,需要对其结构进行调整。如下所示,将积分器和梳状滤波器分别移至一组,并将抽取器移到梳状滤波器之前。先抽取再进行滤波,可以减少数据处理的长度,节约硬件资源。
在这里插入图片描述
当然,级联数越大,旁瓣抑制越好,但是通带内的平坦度也会变差。所以级联数不宜过多,一般最多 5 级。

5.CIC 滤波器设计

设计说明

CIC 滤波器本质上就是一个简单的低通滤波器,截止频率为采样频率除以抽取倍数后的一半。输入数据信号仍然是 7.5MHz 和 250KHz,采样频率 50MHz。抽取倍数设置为 5,则截止频率为 5MHz,小于 7.5MHz,可以滤除 7.5MHz 的频率成分。设计参数如下:
输入频率: 7.5MHz 和 250KHz
采样频率: 50MHz
阻带: 5MHz
阶数: 1(M=1)
级数: 3(N=3)
在这里插入图片描述

//3 stages integrator
module integrator
    #(parameter NIN     = 12,
      parameter NOUT    = 21)
    (
      input               clk ,
      input               rstn ,
      input               en ,
      input [NIN-1:0]     din ,
      output              valid ,
      output [NOUT-1:0]   dout) ;

    reg [NOUT-1:0]         int_d0  ;
    reg [NOUT-1:0]         int_d1  ;
    reg [NOUT-1:0]         int_d2  ;
    wire [NOUT-1:0]        sxtx = {{(NOUT-NIN){1'b0}}, din} ;

    //data input enable delay
    reg [2:0]              en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            en_r   <= 'b0 ;
        end
        else begin
            en_r   <= {en_r[1:0], en};
        end
    end

    //integrator
    //stage1
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            int_d0        <= 'b0 ;
        end
        else if (en) begin
            int_d0        <= int_d0 + sxtx ;
        end
    end

    //stage2
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            int_d1        <= 'b0 ;
        end
        else if (en_r[0]) begin
            int_d1        <= int_d1 + int_d0 ;
        end
    end

   //stage3
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            int_d2        <= 'b0 ;
        end
        else if (en_r[1]) begin
            int_d2        <= int_d2 + int_d1 ;
        end
    end
    assign dout  = int_d2 ;
    assign valid = en_r[2];

endmodule

抽取器设计

抽取器设计时,对积分器输出的数据进行计数,然后间隔 5 个数据进行抽取即可。

module  decimation
    #(parameter NDEC = 21)
    (
     input                clk,
     input                rstn,
     input                en,
     input [NDEC-1:0]     din,
     output               valid,
     output [NDEC-1:0]    dout);

    reg                  valid_r ;
    reg [2:0]            cnt ;
    reg [NDEC-1:0]       dout_r ;

    //counter
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt <= 3'b0;
        end
        else if (en) begin
            if (cnt==4) begin
                cnt <= 'b0 ;
            end
            else begin
                cnt <= cnt + 1'b1 ;
            end
        end
    end

    //data, valid
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            valid_r        <= 1'b0 ;
            dout_r         <= 'b0 ;
        end
        else if (en) begin
            if (cnt==4) begin
                valid_r     <= 1'b1 ;
                dout_r      <= din;
            end
            else begin
                valid_r     <= 1'b0 ;
            end
        end
    end
    assign dout          = dout_r ;
    assign valid         = valid_r ;

endmodule

梳状器设计

梳状滤波器就是简单的一阶 FIR 滤波器,每一级的 FIR 滤波器对数据进行一个时钟延时,然后做相减即可。因为系数为 ±1,所以不需要乘法器。

module comb
    #(parameter NIN  = 21,
      parameter NOUT = 17)
    (
     input               clk,
     input               rstn,
     input               en,
     input [NIN-1:0]     din,
     input               valid,
     output [NOUT-1:0]   dout);

    //en delay
    reg [5:0]                 en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            en_r <= 'b0 ;
        end
        else if (en) begin
            en_r <= {en_r[5:0], en} ;
        end
    end
 
    reg [NOUT-1:0]            d1, d1_d, d2, d2_d, d3, d3_d ;
    //stage 1, as fir filter, shift and add(sub), 
    //no need for multiplier
    always @(posedge clk or negedge rstn) begin
        if (!rstn)        d1     <= 'b0 ;
        else if (en)      d1     <= din ;
    end
    always @(posedge clk or negedge rstn) begin
        if (!rstn)        d1_d   <= 'b0 ;
        else if (en)      d1_d   <= d1 ;
    end
    wire [NOUT-1:0]      s1_out = d1 - d1_d ;

    //stage 2
    always @(posedge clk or negedge rstn) begin
        if (!rstn)        d2     <= 'b0 ;
        else if (en)      d2     <= s1_out ;
    end
    always @(posedge clk or negedge rstn) begin
        if (!rstn)        d2_d   <= 'b0 ;
        else if (en)      d2_d   <= d2 ;
    end
    wire [NOUT-1:0]      s2_out = d2 - d2_d ;

    //stage 3
    always @(posedge clk or negedge rstn) begin
        if (!rstn)        d3     <= 'b0 ;
        else if (en)      d3     <= s2_out ;
    end
    always @(posedge clk or negedge rstn) begin
        if (!rstn)        d3_d   <= 'b0 ;
        else if (en)      d3_d   <= d3 ;
    end
    wire [NOUT-1:0]      s3_out = d3 - d3_d ;

    //tap the output data for better display
    reg [NOUT-1:0]       dout_r ;
    reg                  valid_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            dout_r         <= 'b0 ;
            valid_r        <= 'b0 ;
        end
        else if (en) begin
            dout_r         <= s3_out ;
            valid_r        <= 1'b1 ;
        end
        else begin
            valid_r        <= 1'b0 ;
        end
    end
    assign       dout    = dout_r ;
    assign       valid   = valid_r ;

endmodule

顶层例化

按信号的流向将积分器、抽取器、梳状器分别例化,即可组成最后的 CIC 滤波器模块。

梳状滤波器的最终输出位宽一般会比输入信号小一些,这里取 17bit。当然输出位宽完全可以与输入数据的位宽一致。

module cic
    #(parameter NIN  = 12,
      parameter NMAX = 21,
      parameter NOUT = 17)
    (
     input               clk,
     input               rstn,
     input               en,
     input [NIN-1:0]     din,
     input               valid,
     output [NOUT-1:0]   dout);

    wire [NMAX-1:0]      itg_out ;
    wire [NMAX-1:0]      dec_out ;
    wire [1:0]           en_r ;

    integrator   #(.NIN(NIN), .NOUT(NMAX))
    u_integrator (
       .clk         (clk),
       .rstn        (rstn),
       .en          (en),
       .din         (din),
       .valid       (en_r[0]),
       .dout        (itg_out));

    decimation   #(.NDEC(NMAX))
    u_decimator (
       .clk         (clk),
       .rstn        (rstn),
       .en          (en_r[0]),
       .din         (itg_out),
       .dout        (dec_out),
       .valid       (en_r[1]));

    comb         #(.NIN(NMAX), .NOUT(NOUT))
    u_comb (
       .clk         (clk),
       .rstn        (rstn),
       .en          (en_r[1]),
       .din         (dec_out),
       .valid       (valid),
       .dout        (dout));

endmodule

testbench

testbench 编写如下,主要功能就是不间断连续的输入 250KHz 与 7.5MHz 的正弦波混合信号数据。输入的混合信号数据也可由 matlab 生成,具体过程参考《并行 FIR 滤波器设计》一节。

module test ;
    parameter    NIN  = 12 ;
    parameter    NMAX = 21 ;
    parameter    NOUT = NMAX ;

    reg                  clk ;
    reg                  rstn ;
    reg                  en ;
    reg  [NIN-1:0]       din ;
    wire                 valid ;
    wire [NOUT-1:0]      dout ;

    //=====================================
    // 50MHz clk generating
    localparam   T50M_HALF    = 10000;
    initial begin
        clk = 1'b0 ;
        forever begin
            # T50M_HALF clk = ~clk ;
        end
    end

    //============================
    //  reset and finish
    initial begin
        rstn = 1'b0 ;
        # 30 ;
        rstn = 1'b1 ;
        # (T50M_HALF * 2 * 2000) ;
        $finish ;
    end

    //=======================================
    // read cos data into register
    parameter    SIN_DATA_NUM = 200 ;
    reg          [NIN-1:0] stimulus [0: SIN_DATA_NUM-1] ;
    integer      i ;
    initial begin
        $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ;
        i         = 0 ;
        en        = 0 ;
        din       = 0 ;
        # 200 ;
        forever begin
            @(negedge clk) begin
                en          = 1 ;
                din         = stimulus[i] ;
                if (i == SIN_DATA_NUM-1) begin
                    i = 0 ;
                end
                else begin
                    i = i + 1 ;
                end
            end
        end
    end

    cic #(.NIN(NIN), .NMAX(NMAX), .NOUT(NOUT))
    u_cic (
     .clk         (clk),
     .rstn        (rstn),
     .en          (en),
     .din         (din),
     .valid       (valid),
     .dout        (dout));

endmodule // test

6.仿真结果

由下图仿真结果可知,经过 CIC 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。

但是波形不是非常完美,这与设计的截止频率、数据不是持续输出等有一定关系。

此时发现,积分器输出的数据信号也非常的不规则,这与其位宽有关系。
在这里插入图片描述
为了更好的观察积分器输出的数据,将其位宽由 21bit 改为 34bit,仿真结果如下。

此时发现,CIC 滤波器的数据输出并没有实质性的变化,但是积分器输出的数据信号呈现锯齿状,也称之为梳状。这也是梳状滤波器名字的由来。
在这里插入图片描述

  • 21
    点赞
  • 180
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
CIC(Cascaded Integrator-Comb)滤波器是一种数字滤波器,常用于信号采样率降低的应用中。下面是一个简单的 Verilog 实现。 首先,我们需要定义 CIC 滤波器的参数。假设输入采样率为 $f_s$,输出采样率为 $f_{out}$,CIC 滤波器有 $M$ 级,每级有 $N$ 个积分器和差分器。则: $$R = \frac{f_s}{f_{out}}$$ $$H(z) = \frac{1 - z^{-R}}{1 - z^{-1}}$$ $$H(z)^M = \frac{1 - z^{-RM}}{(1 - z^{-1})^M}$$ $$N = \frac{R^M - 1}{R - 1}$$ 接下来,我们可以开始编写 Verilog 代码。首先定义模块的输入和输出: ```verilog module cic_filter ( input clk, input rst, input signed [31:0] data_in, output signed [31:0] data_out ); ``` 其中,`clk` 是时钟输入,`rst` 是重置输入,`data_in` 是输入数据,`data_out` 是输出数据。注意,这里使用了 `signed` 声明输入和输出数据是有符号整数。 接着,定义 CIC 滤波器的参数: ```verilog parameter M = 3; // 滤波器级数 parameter R = 8; // 降采样比 parameter N = 85; // 积分器和差分器的个数 ``` 其中,`M` 是级数,`R` 是降采样比,`N` 是积分器和差分器的个数。这里我们使用了上面的公式计算出了 `N` 的值。 接着,定义 CIC 滤波器的中间变量: ```verilog reg signed [31:0] integrator_reg [0:M-1][0:N-1]; // 积分器输出寄存器 reg signed [31:0] comb_reg [0:M-1][0:N-1]; // 差分器输出寄存器 reg signed [31:0] delay_reg [0:R-1]; // 降采样延迟寄存器 reg signed [31:0] accumulator_reg; // 累加器输出寄存器 reg signed [31:0] diff_reg; // 差分器输出寄存器 ``` 其中,`integrator_reg` 是每级积分器的输出寄存器,`comb_reg` 是每级差分器的输出寄存器,`delay_reg` 是用于降采样的延迟寄存器,`accumulator_reg` 是累加器的输出寄存器,`diff_reg` 是差分器的输出寄存器。 接着,实现 CIC 滤波器的主要逻辑: ```verilog // 积分器 always @(posedge clk or negedge rst) begin if (!rst) begin integrator_reg <= '{default:'h0}; end else begin for (int i = 0; i < M; i = i + 1) begin for (int j = 0; j < N; j = j + 1) begin if (j == 0) begin integrator_reg[i][j] <= data_in + integrator_reg[i][N-1]; end else begin integrator_reg[i][j] <= integrator_reg[i][j-1] + integrator_reg[i][N-1]; end end end end end // 差分器 always @(posedge clk or negedge rst) begin if (!rst) begin comb_reg <= '{default:'h0}; end else begin for (int i = 0; i < M; i = i + 1) begin for (int j = 0; j < N; j = j + 1) begin if (j == 0) begin comb_reg[i][j] <= integrator_reg[i][0] - delay_reg[0]; end else begin comb_reg[i][j] <= comb_reg[i][j-1] - delay_reg[0]; end end end end end // 降采样 always @(posedge clk or negedge rst) begin if (!rst) begin delay_reg <= '{default:'h0}; end else begin delay_reg <= {data_in, delay_reg[R-1:1]}; end end // 累加器 always @(posedge clk or negedge rst) begin if (!rst) begin accumulator_reg <= 0; end else begin accumulator_reg <= accumulator_reg + comb_reg[M-1][N-1]; end end // 差分器 always @(posedge clk or negedge rst) begin if (!rst) begin diff_reg <= 0; end else begin diff_reg <= accumulator_reg - diff_reg; end end // 输出 assign data_out = diff_reg; ``` 其中,`integrator_reg` 和 `comb_reg` 的更新使用了双重循环。注意,当 $j=0$ 时,需要使用输入数据更新积分器的输出;当 $j\neq0$ 时,需要使用上一个积分器的输出更新当前积分器的输出。对于差分器和降采样,也是类似的。 最后,还需要添加时钟和重置的声明: ```verilog reg [31:0] cnt = 0; always @(posedge clk) begin if (cnt == 0) begin rst <= 0; end else if (cnt == 1) begin rst <= 1; end cnt <= cnt + 1; end ``` 这里使用了一个计数器 `cnt` 来控制时钟上电时的重置,以保证 CIC 滤波器的正确启动。 完整的 Verilog 代码如下: ```verilog module cic_filter ( input clk, input rst, input signed [31:0] data_in, output signed [31:0] data_out ); parameter M = 3; parameter R = 8; parameter N = 85; reg signed [31:0] integrator_reg [0:M-1][0:N-1]; reg signed [31:0] comb_reg [0:M-1][0:N-1]; reg signed [31:0] delay_reg [0:R-1]; reg signed [31:0] accumulator_reg; reg signed [31:0] diff_reg; always @(posedge clk or negedge rst) begin if (!rst) begin integrator_reg <= '{default:'h0}; end else begin for (int i = 0; i < M; i = i + 1) begin for (int j = 0; j < N; j = j + 1) begin if (j == 0) begin integrator_reg[i][j] <= data_in + integrator_reg[i][N-1]; end else begin integrator_reg[i][j] <= integrator_reg[i][j-1] + integrator_reg[i][N-1]; end end end end end always @(posedge clk or negedge rst) begin if (!rst) begin comb_reg <= '{default:'h0}; end else begin for (int i = 0; i < M; i = i + 1) begin for (int j = 0; j < N; j = j + 1) begin if (j == 0) begin comb_reg[i][j] <= integrator_reg[i][0] - delay_reg[0]; end else begin comb_reg[i][j] <= comb_reg[i][j-1] - delay_reg[0]; end end end end end always @(posedge clk or negedge rst) begin if (!rst) begin delay_reg <= '{default:'h0}; end else begin delay_reg <= {data_in, delay_reg[R-1:1]}; end end always @(posedge clk or negedge rst) begin if (!rst) begin accumulator_reg <= 0; end else begin accumulator_reg <= accumulator_reg + comb_reg[M-1][N-1]; end end always @(posedge clk or negedge rst) begin if (!rst) begin diff_reg <= 0; end else begin diff_reg <= accumulator_reg - diff_reg; end end assign data_out = diff_reg; reg [31:0] cnt = 0; always @(posedge clk) begin if (cnt == 0) begin rst <= 0; end else if (cnt == 1) begin rst <= 1; end cnt <= cnt + 1; end endmodule ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值