寻找薛定谔的猫

V1

2022/01/06阅读:69主题:默认主题

基于数字IC的下采样CIC滤波器设计(中)

CIC滤波器IP引脚介绍

本文设计了一个时钟频率是信号采样率两倍的降采样CIC滤波器。该CIC滤波器共有六类引脚。如下图所示,为CIC滤波器的引脚图。 对应的该引脚图的各项功能如下表所示。 该CIC滤波器IP同时起到产生采样脉冲及进行CIC滤波的作用。由于信号的采样率是时钟频率的一半,且采样脉冲宽度为一个时钟周期的宽度。则采样脉冲sample_p可以直接由时钟信号的二分频可到。如下图所示。

CIC滤波器IP核实现分析

在继续往下介绍之前,我们介绍一下采用加法器实现减法运算的实现方式。对于带符号数二进制整数a和b,若实现a-b运算,可以通过将b所有位取反加1后再与a相加实现,即表达式为a+(~b)+1。为此,对于N位的带符号数减法运算,我们可以采用N+1位的加法器实现,即{a,1'b1}+{~b,1'b1}的计算结果去掉最低位的数据。例如,a=01110,b=00110,则~b=11001,有{a,1'b1}+{~b,1'b1}=01110 +11001 =01000 ,去掉最低位后可以得到计算结果为01000。注意,前边~b表示b的所有位取反。 如上图所示,为CIC滤波器IP核加法器分配图。对于输入信号为12位,降采样倍数为16,且滤波器的级数为5的CIC滤波器,内部数据的位数应该为32位。则加法器A和加法器B的位数为32位;而加法器C同时实现加法和减法运算,则设定位数为33位。

为了实现加法器的复用,此处我们需要添加一个32位的寄存器I_tem和一个33位的寄存器C_temp。将采样脉冲sample_p用作加法器运算控制信号。对于CIC滤波器的积分器部分,当采样脉冲sample_p为低电平时(即sample_p=0),实现的加法运算如下: 其中I_1~I_5和I_temp表示32位寄存器。而当采样脉冲sample_p为高电平时(即𝑠𝑎𝑚𝑝𝑙𝑒_𝑝=1),CIC滤波器的积分器部分实现的加法运算如下: 在说明微分器的运算之前,先介绍降采样计数器down_cnt。降采样计数器down_cnt是通过对输入信号进行计数,实现对输入数据的降采样。该计数器down_cnt的起始值为0,,在采样脉冲sample_p为高电平时,实现加1计数。对于16倍降采样的CIC滤波器,计数器down_cnt计数到15之后,在下一个采样脉冲到来时,重新变为0。即当计数器down_cnt的值为15时,且采样脉冲sample_p为低电平时,我们将I_5 + I_4的计算值赋值给寄存器C_temp。

对于梳状滤波器部分的运算,这里复用加法器C,将计数器down_cnt用作状态机,实现加数寄存器的选择。为了避免与I_5 = I_5+I_4运算冲突,我们在采样脉冲sample_p为高电平(sample_p=1)时复用加法器C于梳状滤波器运算。计数器down_cnt与加法运算的关系如下: 在𝑑𝑜𝑤𝑛_𝑐𝑛𝑡=4时,给valid_out赋值高电平,表示输出信号有效,该电平持续一个时钟周期。

CIC滤波器IP核硬件实现细节及RTL代码

前面介绍了CIC滤波器的设计实现思路。接下来我们需要进行RTL设计,再RTL设计时,我们同样需要注意CIC滤波器RTL的优化,以期达到最小的设计的面积。如下所示,是关于加法器A和加法器B的复用细节。 由上图,对于加法器A,I_1可以直接用做加数,而对于被加数则需要通过选择器对I_2和 进行选择;同理,对于加法器B,I_3可以直接用做加数,而对于被加数则需要通过选择器对I_4和I_temp进行选择。对于在sample_p为高电平时,加法器A和加法器B的计算值分别赋值给寄存器I_1和寄存器I_3;在sample_p为低电平时,加法器A和加法器B的计算值分别赋值给寄存器I_2和寄存器I_4。 对于加法器C,在采样脉冲sample_p为低电平(sample_p=0)时复用加法器C于积分器的最后一个加法运算,此时加数和被加数分别是I_4和I_5;在采样脉冲sample_p为高电平(sample_p=1)时复用加法器C于梳状滤波器运算,此时加数和被加数分别是由down_cnt所决定的梳状滤波器的寄存器和C_temp。另外,下图所示红色线条表示添加位数,1'b0表示添加最低位0,其目的是实现本文“CIC滤波器IP核实现分析”小节中所介绍的“采用加法器实现加法器的运算”。 如下图所示,关于I_5寄存器,只需要在采样脉冲sample_p为低电平(sample_p=0)时,将加法器C的计算值寄存于寄存器I_5;而在采样脉冲sample_p为高电平(sample_p=1)时,寄存器I_5的值保持不变。(注意,加法器C计算结果对寄存器的赋值需要去掉最低位) 寄存器C_temp用来寄存梳状滤波器运行的中间数据。对于十六倍降采样的CIC滤波器,降采样计数器down_cnt每计数到15,且采样脉冲sample_p为低电平时,将加法器C的计算值赋值给寄存器C_temp。而后在降采样计数器down_cnt计数在0、1、2、3和4期间,每当采样脉冲sample_p为高电平时,将加法器C的计算值赋值给寄存器C_temp。(注意,加法器C计算结果对寄存器的赋值需要去掉最低位) 而对于梳状滤波器的五个微分计算寄存器。如下图所示,为了方便绘图,用不同颜色标示,表示不同的寄存器。在降采样计数器down_cnt计数在0且采样脉冲sample_p为高电平时,将加法器C的计算值赋值给寄存器C_0。相同的,在降采样计数器down_cnt计数在1且采样脉冲sample_p为高电平时,将加法器C的计算值赋值给寄存器C_1;在降采样计数器down_cnt计数在2且采样脉冲sample_p为高电平时,将加法器C的计算值赋值给寄存器C_2;在降采样计数器down_cnt计数在3且采样脉冲sample_p为高电平时,将加法器C的计算值赋值给寄存器C_3;在降采样计数器down_cnt计数在4且采样脉冲sample_p为高电平时,将加法器C的计算值赋值给寄存器C_4。 在降采样计数器down_cnt计数在4且采样脉冲sample_p为高电平时,加法器C完成一个待输出数据的最后一次运算,所以,可以在降采样计数器down_cnt为4且采样脉冲sample_p为高电平时,可以将运算结果赋值给数据输出寄存器,同时给数据输出一个时钟周期宽度的高电平信号。如下图所示,为CIC滤波器计算结果输出寄存器。 这里笔者提供了对应的CIC滤波器TRL代码,代码如下所示。其中,参数有如下几个:

  • 输入信号位宽参数(DATA_WIDTH_IN),通过该参数配置CIC滤波器输入信号的位宽;
  • 输出信号位宽参数(DATA_WIDTH_IN),通过该参数配置CIC滤波器输出信号的位宽,输出位宽应该不大于数据有效位宽(DATA_WIDTH_VALID);
  • 降采样倍数参数(DOWN_SAMPLE_CNT_NUMBER),通过该参数设定降采样的倍数,该module的降采样倍数不小于5;
  • 数据有效位宽(DATA_WIDTH_VALID),计算过程的防溢出位宽,该位宽的计算方程为DATA_WIDTH_IN + 5*log2(DOWN_SAMPLE_CNT_NUMBER);
  • 降采样计数器位宽参数(DOWN_SAMPLE_CNT_WIDTH),设定降采样计数器的位宽,该参数的计算方程为log2(DOWN_SAMPLE_CNT_NUMBER)
//======================================================================================
// Filename     :  down_sample_cic.v 
// Description  :  Downsampling CIC filter
// Version      :  1.0  
// Author       :  Scat Lin
// Data         :  2022/1/4
//======================================================================================
module  down_sample_cic (
    clk       ,  // clock
    rst_n     ,  // reset, Active low
    sample_p  ,  // Sampling pulse,Active high
    valid_out ,  // Output request pulse,Active high
    data_in   ,  // Input signal
    data_out     // Output signal
);

// parameters, Configurable
parameter     DATA_WIDTH_IN          =  12;  // Number of bits of input  data
parameter     DATA_WIDTH_OUT         =  32;  // Number of bits of output data
parameter     DATA_WIDTH_VALID       =  32;  // DATA_WIDTH_IN + 5*log2(DOWN_SAMPLE_CNT_NUMBER)
parameter     DOWN_SAMPLE_CNT_NUMBER =  16;  // Downsampling multiple, >=5
parameter     DOWN_SAMPLE_CNT_WIDTH  =  4;   // log2(DOWN_SAMPLE_CNT_NUMBER)

// localparam
parameter     DOWN_SAMPLE_COUNT_NUM  =  DOWN_SAMPLE_CNT_NUMBER-1;   // 16 - 1

/******************************************************************\
   Inputs and Outputs
\******************************************************************/

// inputs
input                          clk;
input                          rst_n;
input  [DATA_WIDTH_IN-1:0]     data_in;

// outputs
output                         sample_p ;
output                         valid_out;
output [DATA_WIDTH_OUT-1:0]    data_out ;

// wires
wire                           clk;
wire                           rst_n;
wire[DATA_WIDTH_IN-1:0]        data_in;

// registers
reg                            sample_p;
reg                            valid_out;
reg [DATA_WIDTH_OUT-1:0]       data_out ;

wire[DATA_WIDTH_VALID-1:0]     cic_out;

/******************************************************************\
   define wires and Registers
\******************************************************************/

// registers
// integrate
reg  [DATA_WIDTH_VALID-1:0]    inte_reg1;
reg  [DATA_WIDTH_VALID-1:0]    inte_reg2;
reg  [DATA_WIDTH_VALID-1:0]    inte_reg3;
reg  [DATA_WIDTH_VALID-1:0]    inte_reg4;
reg  [DATA_WIDTH_VALID-1:0]    inte_reg5;
reg  [DATA_WIDTH_VALID-1:0]    inte_temp;
// comb
reg  [DATA_WIDTH_VALID-1:0]    comb_reg1;
reg  [DATA_WIDTH_VALID-1:0]    comb_reg2;
reg  [DATA_WIDTH_VALID-1:0]    comb_reg3;
reg  [DATA_WIDTH_VALID-1:0]    comb_reg4;
reg  [DATA_WIDTH_VALID-1:0]    comb_reg5;
reg  [DATA_WIDTH_VALID-1:0]    comb_temp;

// down sample 
reg  [DOWN_SAMPLE_CNT_WIDTH-1:0]  down_cnt;

// wires
wire [DATA_WIDTH_VALID-1:0]    inte_add_1;
wire [DATA_WIDTH_VALID-1:0]    inte_add_2;
wire [DATA_WIDTH_VALID-1:0]    inte_sum_1;
wire [DATA_WIDTH_VALID-1:0]    inte_sum_2;

wire [DATA_WIDTH_VALID-1:0]    inte_add_3;
wire [DATA_WIDTH_VALID-1:0]    inte_add_4;
wire [DATA_WIDTH_VALID  :0]    inte_sum_3;

/******************************************************************\
  ----> Interview pulse generation through frequency divider
  ----> Realize down-sampling counting by counting sampling pulses
                _   _   _   _   _   _   _   _   _   _   _   _
         clk  _| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|
                ___     ___     ___     ___     ___     ___
    sample_p  _|   |___|   |___|   |___|   |___|   |___|   |_
              _ _______ _______ _______ _______ _______ _____
               |       |       |       |       |       |       
    down_cnt  0|   1   |   2   |   3   |   4   |   5   |  1  
              _|_______|_______|_______|_______|_______|_____
               
\******************************************************************/

// Generate sampling pulse
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        sample_p <= 1'b0;
    end else begin
        sample_p <= ~sample_p;
    end
end

// Down sample conter
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        down_cnt <= {DOWN_SAMPLE_CNT_WIDTH{1'b0}};
    end else if(sample_p) begin
        if(down_cnt == DOWN_SAMPLE_COUNT_NUM[DOWN_SAMPLE_CNT_WIDTH-1:0])begin
            down_cnt <= {DOWN_SAMPLE_CNT_WIDTH{1'b0}};
        end else begin
            down_cnt <= down_cnt + {{(DOWN_SAMPLE_CNT_WIDTH-1){1'b0}},{1'b1}};
        end
    end
end

/******************************************************************\
   Addition operation
   By three adders
\******************************************************************/

assign    inte_add_1 = ( sample_p    ) ? {{(DATA_WIDTH_VALID - DATA_WIDTH_IN){data_in[DATA_WIDTH_IN-1]}},data_in} : inte_reg2;

assign    inte_add_2 = ( sample_p    ) ?  inte_temp : inte_reg4;

assign    inte_add_3 = ( sample_p    ) ? ~{ (down_cnt == {{(DOWN_SAMPLE_CNT_WIDTH-2){1'b0}},{2'd0}}) ?  comb_reg1 :
                                            (down_cnt == {{(DOWN_SAMPLE_CNT_WIDTH-2){1'b0}},{2'd1}}) ?  comb_reg2 :
                                            (down_cnt == {{(DOWN_SAMPLE_CNT_WIDTH-2){1'b0}},{2'd2}}) ?  comb_reg3 :
                                            (down_cnt == {{(DOWN_SAMPLE_CNT_WIDTH-2){1'b0}},{2'd3}}) ?  comb_reg4 : comb_reg5 } : inte_reg4 ;

assign    inte_add_4 = ( sample_p    ) ?  comb_temp : inte_reg5 ;

assign    inte_sum_1 = inte_add_1 + inte_reg1 ;
assign    inte_sum_2 = inte_add_2 + inte_reg3 ;
assign    inte_sum_3 = {inte_add_3,{(sample_p)?1'b1:1'b0}} + {inte_add_4,{(sample_p)?1'b1:1'b0}};  // add && sub

/******************************************************************\
  Registers:
   ----> Integrator  Registers
   ----> Comb filter Registers
   ----> Temp Registers
   ----> Out registers
\******************************************************************/

// Integrator Registers
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
        inte_reg2 <= {DATA_WIDTH_VALID{1'b0}};
        inte_reg4 <= {DATA_WIDTH_VALID{1'b0}};
        inte_reg5 <= {DATA_WIDTH_VALID{1'b0}};
        inte_temp <= {DATA_WIDTH_VALID{1'b0}};
    end else if(~sample_p) begin
        inte_temp <= inte_reg2;
        inte_reg2 <= inte_sum_1;
        inte_reg4 <= inte_sum_2;
        inte_reg5 <= inte_sum_3[DATA_WIDTH_VALID:1];
    end
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
       inte_reg1 <= {DATA_WIDTH_VALID{1'b0}};
       inte_reg3 <= {DATA_WIDTH_VALID{1'b0}};
   end else if(sample_p) begin
       inte_reg1 <= inte_sum_1;
       inte_reg3 <= inte_sum_2;    
   end
end

// Comb filter registers
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        comb_reg1  <= {DATA_WIDTH_VALID{1'b0}};
        comb_reg2  <= {DATA_WIDTH_VALID{1'b0}};
        comb_reg3  <= {DATA_WIDTH_VALID{1'b0}};
        comb_reg4  <= {DATA_WIDTH_VALID{1'b0}};
        comb_reg5  <= {DATA_WIDTH_VALID{1'b0}};
    end else if (sample_p)begin
        case(down_cnt)
            {{(DOWN_SAMPLE_CNT_WIDTH-3){1'b0}},{3'd0}} : begin  comb_reg1 <=  comb_temp;  end
            {{(DOWN_SAMPLE_CNT_WIDTH-3){1'b0}},{3'd1}} : begin  comb_reg2 <=  comb_temp;  end
            {{(DOWN_SAMPLE_CNT_WIDTH-3){1'b0}},{3'd2}} : begin  comb_reg3 <=  comb_temp;  end
            {{(DOWN_SAMPLE_CNT_WIDTH-3){1'b0}},{3'd3}} : begin  comb_reg4 <=  comb_temp;  end
            {{(DOWN_SAMPLE_CNT_WIDTH-3){1'b0}},{3'd4}} : begin  comb_reg5 <=  comb_temp;  end
        endcase
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        comb_temp  <= {DATA_WIDTH_VALID{1'b0}};
 end else if(((~sample_p) && (down_cnt == DOWN_SAMPLE_COUNT_NUM[DOWN_SAMPLE_CNT_WIDTH-1:0])) || (sample_p && (~down_cnt[2]) && (~down_cnt[3])))begin
        comb_temp  <= inte_sum_3[DATA_WIDTH_VALID:1];
    end 
end

// out registers
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        valid_out  <= 1'b0;
    end else begin
        valid_out  <= (down_cnt == {{(DOWN_SAMPLE_CNT_WIDTH-3){1'b0}},{3'd4}}) && sample_p ;
    end 
end

assign cic_out  = inte_sum_3[DATA_WIDTH_VALID:1] ;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        data_out <= {DATA_WIDTH_OUT{1'b0}};
    end else if((down_cnt == {{(DOWN_SAMPLE_CNT_WIDTH-3){1'b0}},{3'd4}}) && sample_p)begin
        data_out <= cic_out[DATA_WIDTH_VALID-1 : DATA_WIDTH_VALID - DATA_WIDTH_OUT] ;
    end 
end

endmodule

笔者提供了对应的CIC滤波器testbench仿真代码,详细代码如下所示。关于该testbench仿真代码,通过读取matlab生成的原始数据并输送CIC滤波器module,然后将生成的降采样滤波数据与matlab产生的经过CIC滤波器模型的数据进行比较,如果所有生成的数据均对应相等,则表明设计的CIC滤波器RTL模块正确。否则存在错误。

`timescale 1 ns/ 100 ps
module down_sample_cic_vlg_tst();

// parameters
parameter     DATA_WIDTH_IN          =  12;      // The number of bits of the input  data
parameter     DATA_WIDTH_OUT         =  32;      // The number of bits of the output data
parameter     DATA_WIDTH_VALID       =  32;
parameter     DOWN_SAMPLE_CNT_WIDTH  =  4;
parameter     MEM_DEPTH_OUT          =  102400;  // Depth of memory : the number of input data
parameter     MEM_DEPTH_CMP          =  6400;    // Depth of memory : the number of compare data

// inputs 
reg                             clk;
reg                             rst_n;   
reg  [DATA_WIDTH_IN-1:0]        data_in;   

// outputs                                
wire [DATA_WIDTH_OUT-1:0]       data_out;
wire                            sample_p;
wire                            valid_out;

// Signal tracking
wire  [DATA_WIDTH_VALID-1:0]    inte_reg1;
wire  [DATA_WIDTH_VALID-1:0]    inte_reg2;
wire  [DATA_WIDTH_VALID-1:0]    inte_reg3;
wire  [DATA_WIDTH_VALID-1:0]    inte_reg4;
wire  [DATA_WIDTH_VALID-1:0]    inte_reg5;
wire  [DATA_WIDTH_VALID-1:0]    inte_temp;
wire  [DATA_WIDTH_VALID-1:0]    comb_reg1;
wire  [DATA_WIDTH_VALID-1:0]    comb_reg2;
wire  [DATA_WIDTH_VALID-1:0]    comb_reg3;
wire  [DATA_WIDTH_VALID-1:0]    comb_reg4;
wire  [DATA_WIDTH_VALID-1:0]    comb_reg5;
wire  [DATA_WIDTH_VALID-1:0]    comb_temp;
wire  [DATA_WIDTH_VALID-1:0]    inte_add_3;
wire  [DATA_WIDTH_VALID:0]      inte_sum_3;

wire  [DOWN_SAMPLE_CNT_WIDTH-1:0]      down_cnt;

reg   [DATA_WIDTH_OUT-1:0]       data_cmp;
reg                              temp_o;

assign  inte_reg1  = u_cic.inte_reg1;
assign  inte_reg2  = u_cic.inte_reg2;
assign  inte_reg3  = u_cic.inte_reg3;
assign  inte_reg4  = u_cic.inte_reg4;
assign  inte_reg5  = u_cic.inte_reg5;
assign  comb_reg1  = u_cic.comb_reg1;
assign  comb_reg2  = u_cic.comb_reg2;
assign  comb_reg3  = u_cic.comb_reg3;
assign  comb_reg4  = u_cic.comb_reg4;
assign  comb_reg5  = u_cic.comb_reg5;
assign  inte_temp  = u_cic.inte_temp;
assign  comb_temp  = u_cic.comb_temp;
assign  inte_add_3 = u_cic.inte_add_3;
assign  inte_sum_3 = u_cic.inte_sum_3;
assign  down_cnt   = u_cic.down_cnt;

// memory
reg [DATA_WIDTH_IN-1:0]        mem_d[MEM_DEPTH_OUT-1:0];
reg [DATA_WIDTH_OUT-1:0]       mem_c[MEM_DEPTH_CMP-1:0];

// integer
integer    i,j;
integer    cmp_result;

                         
down_sample_cic u_cic (  
    .clk              (clk               ),
    .rst_n            (rst_n             ),
    .data_in          (data_in           ),
    .data_out         (data_out          ), 
    .sample_p         (sample_p          ),
    .valid_out        (valid_out         )
);

// read txt
initial begin  $readmemh("D:/work/Study/CIC/signal_quantified.txt", mem_d); end // Pay attention to the direction of the '/'
initial begin  $readmemh("D:/work/Study/CIC/signal_aftercic.txt", mem_c); end // Pay attention to the direction of the '/'

initial begin   
    $display("Running testbench\n");                                                 
    clk   =  0;
    rst_n =  0
    #111;
    rst_n =  1;
    for (i = 0; i < MEM_DEPTH_OUT; i = i+1)begin
        @(posedge sample_p)
        #0.5;
        data_in = mem_d[i];
    end
 
    $display("testbench is over!\n"); 
    if (cmp_result > 0)begin
        $display("Module Has Errors!\n");
        $display("Errors numbers is %d\n",cmp_result);
    end else begin
        $display("Module simulation is correct!\n");
    end
    $stop;                        
end

initial begin
    j = 0;
    cmp_result = 0;
    temp_o  = 0;
    forever begin
        wait(valid_out);
        if (temp_o)begin
            data_cmp = mem_c[j];
        end else begin
            data_cmp = 0;
        end
        if (data_cmp == data_out)begin
            cmp_result = cmp_result;
        end  else begin
            cmp_result = cmp_result+1;
        end
        #17;
        if (temp_o)begin
            j = j+1;
        end
        temp_o = 1;
        wait(~valid_out);
    end
end

// clock
always  #10 clk = ~clk;

endmodule

如下图所示,为生成的仿真结果。仿真结果表明,该CIC滤波器RTL代码是正确的。

本文PDF格式文件及代码下载链接如下 链接:https://pan.baidu.com/s/19S2hieKwj620-tphwFTjNQ 提取码:0000

若喜欢DSP相关话题,还请关注本人公众号

分类:

前端

标签:

前端

作者介绍

寻找薛定谔的猫
V1