当前位置:   article > 正文

《FPGA数字信号处理》基于FPGA的并行半带抽取滤波器设计_抽取滤波 fpga

抽取滤波 fpga

本人FPGA小白,只关注FPGA具体功能实现。如以下描述有误,望评论区指正!以下内容,纯手打,严禁未经过同意擅自转载,谢谢!

原理

半带滤波器是FIR滤波器的一种特殊形式。它是一种非常适合于实现2倍抽取的FIR滤波器。
相对于一般的FIR滤波器,半带滤波器可以在实现时可以节省将近一半的乘法器结构。

特点

如图所示是半带滤波器的幅频特性。
半带滤波器的频率响应半带滤波器系数示意图
半带滤波器结构

  1. 滤波器的通带阻带对称,宽度完全相同。即Ωp = Pi - Ωs,且δp = δs。
  2. 滤波器的系数具有偶对称性,且滤波器的长度为偶数(阶数为长度-1),滤波器的所有大于0的偶数序号的冲击响应值为0(这一特性决定了滤波器运算时所需的乘法器数量)。
  3. 根据滤波器的幅频特性图,使用半带滤波器进行2倍抽取时,可以满足信号通带内没有频谱混叠(阻带内有混叠)。

需求

下面以一个具体的工程实例介绍基于FPGA的并行半带滤波器实现的整个过程。

  1. 假设工程中使用的ADC采样率为2400MSPS,待采集的信号频率范围为100-1100MHz(中心频率为600MHz)。
  2. FPGA端系统数据时钟为150MHz,数据接口为16并行。
  3. 设计一个并行DDS,实现输出信号为600MHz, 将ADC数据与DDS数据进行混频。
  4. 通过半带滤波器完成数据滤波,并实现2倍抽取,最终输出DDC后的数据。

实现过程

Matlab滤波器设计

Matlab设计滤波器
通过Matlab设计一个31阶的半带滤波器,滤波器的Fs为2400MHz,通带截止频率为500MHz,经过量化后导出系数。

滤波器系数

FPGA程序设计

16并行DDS设计

    //******** DDS_16parallel
    wire [PARALLEL_NUM*32-1:0] DDS_sigOut; // S15,...,S0
    DDS_16parallel DDS_16parallel (
        .clock              ( clock      ),
        .rst                ( rst        ),
        .FreCode            ( DDC_LoFre  ),
        .out_Sig            ( DDS_sigOut )
    );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

此模块可以通过设置DDC_LoFre参数实现控制并行DDS输出信号的频率。

混频模块设计


    //******** Frequency Mixer
    genvar i,j;

    wire [(32*2-1):0] multDat [(PARALLEL_NUM-1):0]; // Real   S15,...,S0
    wire [(PARALLEL_NUM*32-1):0] multDat_Cut; //  IQ    S15,...,S0
    generate for(i=0; i<PARALLEL_NUM; i=i+1) begin
        Mult_16X16 Mult_sigIn_I (
            .CLK( clock                     ), // input wire CLK
            .A  ( DDS_sigOut[(32*i+0)+:16]  ), // input wire [15 : 0] A
            .B  ( sigIn     [(16*i)+:16]    ), // input wire [15 : 0] B
            .P  ( multDat[i][0+:32]         )  // output wire [31 : 0] P
        );
        Mult_16X16 Mult_sigIn_Q (
            .CLK( clock                     ), // input wire CLK
            .A  ( DDS_sigOut[(32*i+16)+:16] ), // input wire [15 : 0] A
            .B  ( sigIn     [(16*i)+:16]    ), // input wire [15 : 0] B
            .P  ( multDat[i][32+:32]        )  // output wire [31 : 0] P
        );

        assign multDat_Cut[(32*i+ 0)+:16] = multDat[i][30-:16];
        assign multDat_Cut[(32*i+16)+:16] = multDat[i][62-:16];

    end

    endgenerate


    integer dout_file;
    initial begin
        dout_file=$fopen(".../Matlab/Mixer_SigOut.csv","w+");    //打开路径 创建文件
        if(dout_file == 0) begin 
            $display ("can not open the file!");    //创建文件失败,显示can not open the file!
            $stop;
        end
    end

    always @(posedge clock) begin
        if(rst) begin

        end
        else begin
             $fdisplay(dout_file,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", // {I0,Q0,I1,Q1,I2,Q2,I3,Q3}
                        $signed(multDat_Cut[(32*00)+:16]),$signed(multDat_Cut[(32*00+16)+:16]),
                        $signed(multDat_Cut[(32*01)+:16]),$signed(multDat_Cut[(32*01+16)+:16]),
                        $signed(multDat_Cut[(32*02)+:16]),$signed(multDat_Cut[(32*02+16)+:16]),
                        $signed(multDat_Cut[(32*03)+:16]),$signed(multDat_Cut[(32*03+16)+:16]),
                        $signed(multDat_Cut[(32*04)+:16]),$signed(multDat_Cut[(32*04+16)+:16]),
                        $signed(multDat_Cut[(32*05)+:16]),$signed(multDat_Cut[(32*05+16)+:16]),
                        $signed(multDat_Cut[(32*06)+:16]),$signed(multDat_Cut[(32*06+16)+:16]),
                        $signed(multDat_Cut[(32*07)+:16]),$signed(multDat_Cut[(32*07+16)+:16]),
                        $signed(multDat_Cut[(32*08)+:16]),$signed(multDat_Cut[(32*08+16)+:16]),
                        $signed(multDat_Cut[(32*09)+:16]),$signed(multDat_Cut[(32*09+16)+:16]),
                        $signed(multDat_Cut[(32*10)+:16]),$signed(multDat_Cut[(32*10+16)+:16]),
                        $signed(multDat_Cut[(32*11)+:16]),$signed(multDat_Cut[(32*11+16)+:16]),
                        $signed(multDat_Cut[(32*12)+:16]),$signed(multDat_Cut[(32*12+16)+:16]),
                        $signed(multDat_Cut[(32*13)+:16]),$signed(multDat_Cut[(32*13+16)+:16]),
                        $signed(multDat_Cut[(32*14)+:16]),$signed(multDat_Cut[(32*14+16)+:16]),
                        $signed(multDat_Cut[(32*15)+:16]),$signed(multDat_Cut[(32*15+16)+:16]),

                        $signed(sigIn[(16*00)+:16]),
                        $signed(sigIn[(16*01)+:16]),
                        $signed(sigIn[(16*02)+:16]),
                        $signed(sigIn[(16*03)+:16]),
                        $signed(sigIn[(16*04)+:16]),
                        $signed(sigIn[(16*05)+:16]),
                        $signed(sigIn[(16*06)+:16]),
                        $signed(sigIn[(16*07)+:16]),
                        $signed(sigIn[(16*08)+:16]),
                        $signed(sigIn[(16*09)+:16]),
                        $signed(sigIn[(16*10)+:16]),
                        $signed(sigIn[(16*11)+:16]),
                        $signed(sigIn[(16*12)+:16]),
                        $signed(sigIn[(16*13)+:16]),
                        $signed(sigIn[(16*14)+:16]),
                        $signed(sigIn[(16*15)+:16])


                        ) ;
        end
    end 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

此模块实现将输入信号和DDS输出信号进行复乘操作,并将结果输出至指定的文件存储。

输入数据缓存

    //******** Parallel Data Regisiter

    reg  [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly0; //  IQ    S63,...,S48
    reg  [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly1; //  IQ    S47,...,S32
    reg  [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly2; //  IQ    S31,...,S16
    // reg  [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly3; //  IQ    S15 ,...,S0

    always @(posedge clock) begin
        if(rst) begin
            multDat_Cut_Dly0 <= {(PARALLEL_NUM*32){1'b0}};
            multDat_Cut_Dly1 <= {(PARALLEL_NUM*32){1'b0}};
            multDat_Cut_Dly2 <= {(PARALLEL_NUM*32){1'b0}};
            // multDat_Cut_Dly3 <= {(PARALLEL_NUM*32){1'b0}};
        end
        else begin
            multDat_Cut_Dly0 <= multDat_Cut     ;
            multDat_Cut_Dly1 <= multDat_Cut_Dly0; 
            multDat_Cut_Dly2 <= multDat_Cut_Dly1; 
            // multDat_Cut_Dly3 <= multDat_Cut_Dly2;
        end
    end

    reg [(PARALLEL_NUM*32*3-1):0] multDat_Cut_parallel; // S63,...,s16
    always @(posedge clock) begin
        if(rst) begin
            multDat_Cut_parallel <= {(PARALLEL_NUM*32*3){1'b0}};
        end
        else begin
            multDat_Cut_parallel <= {multDat_Cut_Dly0,multDat_Cut_Dly1,multDat_Cut_Dly2};
        end
    end
        reg [(32*33-1):0] FIR_sigIn [(PARALLEL_NUM/2-1):0]; // 33 Points IQ * 8 Parallel
                                // FIR_sigIn[0]  S63,...,s32,s31
                                // FIR_sigIn[1]  S62,...,s31,s30
                                // ......
                                // FIR_sigIn[6]  S57,...,s26,s25
                                // FIR_sigIn[7]  S56,...,s25,s24
    always @(posedge clock) begin
        if(rst) begin
            for(integer i=0; i<PARALLEL_NUM/2; i=i+1) begin
                FIR_sigIn[PARALLEL_NUM/2-1-i] <= {(32*33){1'b0}};
            end
        end
        else begin
            for(integer i=0; i<PARALLEL_NUM/2; i=i+1) begin
                FIR_sigIn[PARALLEL_NUM/2-1-i] <= multDat_Cut_parallel[((PARALLEL_NUM*3-2*i)*32-1)-:(33*32)];
            end
        end
    end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

此段代码用于寄存连续48个采样点数据,并通过移位操作寄存连续8组数据,用于并行计算同一个时钟节拍下的8并行数据。

结构优化

    reg [((PARALLEL_NUM/2+1)*34-1):0] FIR_sigIn_S[(PARALLEL_NUM/2-1):0]; // 33 -> 8*2+1 Points IQ * 8 Parallel  
        //FIR_sigIn_S[0]   y63 = (S62+S32)*C31+(S60+S34)*C29+...+(S48+S46)*C17+(S47+S47)*C16/2   Sum 9 Points   
        //FIR_sigIn_S[1]   y61 = (S60+S30)*C31+(S58+S32)*C29+...+(S46+S44)*C17+(S45+S45)*C16/2
        //  .......
        //FIR_sigIn_S[7]   y49 = (S48+S18)*C31+(S47+S20)*C29+...+(S34+S32)*C17+(S33+S33)*C16/2
    generate
        for(i=0; i<PARALLEL_NUM/2; i=i+1) begin
            for(j=0; j<PARALLEL_NUM/2+1; j=j+1) begin // 9 Points
                always @ (posedge clock) begin
                    if(rst) begin
                        FIR_sigIn_S[i][(34*j+17*0)+:17] <= {17{1'b0}};// I
                        FIR_sigIn_S[i][(34*j+17*1)+:17] <= {17{1'b0}};// Q
                    end
                    else begin
                        if(j==PARALLEL_NUM/2) begin
                            FIR_sigIn_S[i][(34*j+17*0)+:17] <= {FIR_sigIn[i][32*(32-2*j)    +15],FIR_sigIn[i][(32*(32-2*j)+0*16)    +:16]} + 17'b0;// I
                            FIR_sigIn_S[i][(34*j+17*1)+:17] <= {FIR_sigIn[i][32*(32-2*j)    +31],FIR_sigIn[i][(32*(32-2*j)+1*16)    +:16]} + 17'b0;// Q
                        end
                        else begin
                            FIR_sigIn_S[i][(34*j+17*0)+:17] <= {FIR_sigIn[i][32*(32-2*j-1)  +15],FIR_sigIn[i][(32*(32-2*j-1)+0*16)  +:16]} 
                                                             + {FIR_sigIn[i][32*(2*j+1)     +15],FIR_sigIn[i][(32*(2*j+1)+0*16)     +:16]};// I
                            FIR_sigIn_S[i][(34*j+17*1)+:17] <= {FIR_sigIn[i][32*(32-2*j-1)  +31],FIR_sigIn[i][(32*(32-2*j-1)+1*16)  +:16]} 
                                                             + {FIR_sigIn[i][32*(2*j+1)     +31],FIR_sigIn[i][(32*(2*j+1)+1*16)     +:16]};// Q
                        end
                    end
                end
            end
        end
    endgenerate 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

根据半带滤波器系数的偶对称特性,将对称的输入数据先进行求和,这样相比于直接实现,可以节省大约一半的乘法器

卷积运算

    wire [(9*32-1):0] ConvMult_Dat_I  [(PARALLEL_NUM/2-1):0]; // 9 Points I * 8 Parallel
    wire [(9*32-1):0] ConvMult_Dat_Q  [(PARALLEL_NUM/2-1):0]; // 9 Points Q * 8 Parallel
    wire [(32+4-1):0] polyFIR_SumOut_I[(PARALLEL_NUM/2-1):0];
    wire [(32+4-1):0] polyFIR_SumOut_Q[(PARALLEL_NUM/2-1):0];

    generate begin // 2X Decemation  16 -> 8 parallel
        for(i=0; i<PARALLEL_NUM/2; i=i+1) begin
            for(j=0; j<PARALLEL_NUM/2+1; j=j+1) begin
                if(j==PARALLEL_NUM/2) begin
                    Mult_16X16 Conv_Mult_I (
                        .CLK( clock                               ), // input wire CLK
                        .A  ( LPF_Coe          [(2*j)*16+:16]     ), // input wire [15 : 0] A
                        .B  ( FIR_sigIn_S   [i][(j*34+17*0+1)+:16]), // input wire [15 : 0] B
                        .P  ( ConvMult_Dat_I[i][(j*32)+:32]       )  // output wire [31 : 0] P
                    );
                    Mult_16X16 Conv_Mult_Q (
                        .CLK( clock                               ), // input wire CLK
                        .A  ( LPF_Coe          [(2*j)*16+:16]     ), // input wire [15 : 0] A
                        .B  ( FIR_sigIn_S   [i][(j*34+17*1+1)+:16]), // input wire [15 : 0] B
                        .P  ( ConvMult_Dat_Q[i][(j*32)+:32]       )  // output wire [31 : 0] P
                    );
                end
                else begin
                    Mult_16X16 Conv_Mult_I (
                        .CLK( clock                               ), // input wire CLK
                        .A  ( LPF_Coe          [(2*j+1)*16+:16]   ), // input wire [15 : 0] A
                        .B  ( FIR_sigIn_S   [i][(j*34+17*0+1)+:16]), // input wire [15 : 0] B
                        .P  ( ConvMult_Dat_I[i][(j*32)+:32]       )  // output wire [31 : 0] P
                    );
                    Mult_16X16 Conv_Mult_Q (
                        .CLK( clock                               ), // input wire CLK
                        .A  ( LPF_Coe          [(2*j+1)*16+:16]   ), // input wire [15 : 0] A
                        .B  ( FIR_sigIn_S   [i][(j*34+17*1+1)+:16]), // input wire [15 : 0] B
                        .P  ( ConvMult_Dat_Q[i][(j*32)+:32]       )  // output wire [31 : 0] P
                    );
                end

                SUM_16Point #(
                    .DATA_WIDTH( 32 ) 
                ) SUM_16Point_I (
                    .clock(clock                         ),
                    .rst  (rst                           ),
                    .din  ({{7{32'b0}},ConvMult_Dat_I[i]}),
                    .dout (polyFIR_SumOut_I[i]           )
                );
                SUM_16Point #(
                    .DATA_WIDTH( 32 ) 
                ) SUM_16Point_Q (
                    .clock(clock               ),
                    .rst  (rst                 ),
                    .din  ({{7{32'b0}},ConvMult_Dat_Q[i]} ),
                    .dout (polyFIR_SumOut_Q[i] )
                );
            end

            assign sigOut[32*i+:32] = {polyFIR_SumOut_Q[i][29-:16],polyFIR_SumOut_I[i][29-:16]};

        end
    end
    endgenerate


    integer dout_file1;
    initial begin
        dout_file1=$fopen(".../Matlab/polyFIR_SigOut.csv","w+");    //打开路径 创建文件
        if(dout_file1 == 0) begin 
            $display ("can not open the file!");    //创建文件失败,显示can not open the file!
            $stop;
        end
    end

    always @(posedge clock) begin
        if(rst) begin

        end
        else begin
             $fdisplay(dout_file1,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", // {I0,Q0,I1,Q1,I2,Q2,I3,Q3}
                        $signed(sigOut[(32*0)+:16]),$signed(sigOut[(32*0+16)+:16]),
                        $signed(sigOut[(32*1)+:16]),$signed(sigOut[(32*1+16)+:16]),
                        $signed(sigOut[(32*2)+:16]),$signed(sigOut[(32*2+16)+:16]),
                        $signed(sigOut[(32*3)+:16]),$signed(sigOut[(32*3+16)+:16]),
                        $signed(sigOut[(32*4)+:16]),$signed(sigOut[(32*4+16)+:16]),
                        $signed(sigOut[(32*5)+:16]),$signed(sigOut[(32*5+16)+:16]),
                        $signed(sigOut[(32*6)+:16]),$signed(sigOut[(32*6+16)+:16]),
                        $signed(sigOut[(32*7)+:16]),$signed(sigOut[(32*7+16)+:16]) 
                        ) ;
        end
    end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

实现滤波器的卷积运算,并将结果输出至指定文件存储。

Matlab联合仿真

产生采样点数据
close all;
clear;
clc;


fs = 2.4e9;
N = 32768;
n_index = (0:N-1);
t = N/fs;
t_index = (0:1/fs:t-1/fs);%时间间隔
f_index = (0:fs/N:fs-fs/N);
f_shfit_index = (-fs/2:fs/N:fs/2-fs/N);

f = 595e6;
sig = cos(2*pi*f*t_index);
sig = round(sig.*32767);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

通过matlab产生原始AD采样数据,并将数据写入文件,将其作为ADC的输出数据。

混频后数据仿真

混频后数据时域和频域
读取数据文件,通过matlab将混频后的数据时域和频域画出来,可以发现混频后数据正常。

半带滤波器输出仿真

半带滤波器输出信号的时域和频域
读取数据文件,通过matlab将半带滤波器输出的数据时域和频域画出来,可以发现滤波器输出数据正常。

更新

LFM信号抽取滤波前时频

LFM信号抽取滤波后时频

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/603195
推荐阅读
相关标签
  

闽ICP备14008679号