利用Vivado的 FFT IP 核估计信号的幅度和频率

1 IP说明

1.1 Configuration Channel

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 管脚描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 例化IP

2.1 DDS IP

输出频率字的计算公式:deta_theta=f_out*(2^B)/f_clk
B:相位位宽,这里为32
f_clk :250M。注意,例化DDS IP时,外部模块例化端口的输入信号频率也必须为250M。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 FFT IP

第一页配置(Configuration):
(1)Number of Channels:多通道输入。FFT转换通道的个数,这里为1。
(2)Transform Length :FFT的处理点数
(3)Architecture Configuration:
  Target Clock Frequency:工作时钟;
  Architecture choice:选择一种FFT结构。包括自动匹配、流水线Streaming、基4 Burst、基2 Burst和轻量级基2 Burst,它们的计算速度和消耗的资源依次减少,可根据工程实际进行选择。
(4)Run Time Configurable Transform Length:实时更改FFT的点数。

在这里插入图片描述

第二页配置(Implementation):
(1)Data Format:设置FFT的数据格式为定点Fixed Point或浮点Float Point;
(2)Scaling Option:输出截位方式选择。不截位(Unscaled),截位(Scaled),块浮点(Block Floating Point);
(3)Precision Option:设置输入数据的位宽和相位因子位宽;
(4)Control Signals:时钟使能(ACLKEN),复位信号(ARESETn,低有效);
(5)Output Ordering Option:用以选择FFT计算结果以自然顺序(Nature Order)或位倒序(Bit/Digit Reversed Order)输出。
在这里插入图片描述

第三页配置(Detailed Implementation):
可设置优化方式、存储的类型。
存储类型分为两种:Block RAM(块RAM)和Distributed RAM(分布式RAM)
优化方式可选择资源最优或者速度最优。

在这里插入图片描述
在这里插入图片描述

3 程序

3.1 程序结构

在这里插入图片描述

3.2 Use_FFT_SignalAmp模块

`timescale 1ns / 1ps


/*
    幅度结果说明:
    1、估计出来的信号幅度估计值是正弦波的有效幅度,所以是正弦波最大值的1/sqrt(2)
    由于信号只有实部,所以信号幅度还要减少一半
    所以幅度估计值=正弦波的峰值/(2*sqrt(2))

    频率估计结果说明:
    计算公式:m_axis_data_tuser* fs/COUNT=82*250M/1024= 20.0195MHz
    

    易错点:
    1、如果连续估计信号幅度时,每次出来的幅度结果不一样,检查IP 的信号频率是否和clk相等
    2、tdata_i_valid 必须是一个脉冲信号
    3、FFT IP核里面设置的信号频率必须和外部信号频率一致
*/


// I:实部  Q:虚部
// fft_m_data_tuser)  输出频谱的索引该值* fs/N,即为输入信号的频点
//fs(200M)和N(Transform Length)的值见FFT  IP 设置 


/*
3、NFFT:可选,本次设计没有使用
位宽为5 
Point size of the transform,NFFT can be the size of the maximum transform or any smaller point size.
For example, a 1024-point FFT can compute point sizes 1024, 512, 256, and so on. 
The value of NFFT is log2 (point size). 如果是2048个采样点  NFFT=log2(2048)=11=01011
The transform point size can be set through the NFFT field in the Configuration channel if 
the run time configurable transform length option is selected.
只有选中run time configurable transform length这个选项,NFFT才可以配置。


2、CP_LEN:(Cyclic prefix length)  可选,本次设计没有使用
位宽为log2(采样点数)
The number of samples from the end of the transform that are initially output as a cyclic prefix, 
before the whole transform is output. CP_LEN can be any number from zero to one less than
the point size. 
This field is only present with cyclic prefix insertion.
只有选中 cyclic prefix insertion这个选项时,CP_LEN才可以配置。。


1、FWD_INV:   必须
位宽为:1 bit per FFT data channel
Indicates if a forward FFT transform or an inverse FFT transform is performed. 
FWD_INV = 1, a forward transform is computed. FFT变换
FWD_INV = 0, an inverse transform is computed. IFFT变换
Each FFT data channel has an assigned FWD_INV field


0、SCALE_SCH:  可选,本次设计没有使用
for Pipelined Streaming I/O and Radix-4 Burst I/O architectures,位宽为:2*ceil(NFFT/2)
for Radix-2, Burst I/O and Radix-2 Lite Burst I/O architectures 位宽为:2*NFFT
 This field is only available with scaled arithmetic 
 (not unscaled, block floating-point or singleprecision floating-point).
只有选中scalingOption的 scaled 这个选项时,SCALE_SCH才可以配置。


All fields with padding should be extended to the next 8-bit boundary 
if they do not already finish on an 8-bit boundary.
 */


//模块功能:1024个点的FFT变换,并得到幅度谱
module Use_FFT_SignalAmp(
    input               clk               ,// 工作时钟信号             
    input               rst_n             ,// 复位信号               
    input               tdata_i_valid     ,// 输入数据有效信号  // 是一个脉冲信号(上升沿有效)         
    input  [31:0]       tdata_i           ,// 待估计信号幅度的数据  32bit  
    
    
    output [15:0]       data_max        ,//信号幅度
    output              DataMax_Vaild    //信号幅度有效信号 //是一个脉冲信号(上升沿有效)      
);

/*
    step 1: 保证 tdata_i_valid 是一个脉冲信号
        为了避免它不是脉冲信号,所以我们用 tdata_i_valid 的上升沿来作为整个模块的触发信号
*/
reg tdata_i_valid_delay;
reg tdata_i_valid_syn;
always@(posedge clk)
begin
    tdata_i_valid_delay<=tdata_i_valid;
    if(tdata_i_valid_delay==1'b0 && tdata_i_valid==1'b1)//抓取信号的上升沿
        tdata_i_valid_syn<=1'b1;
    else
        tdata_i_valid_syn<=1'b0;   
end




/*
    step 2: FFT IP从端的输入数据和valid信号控制
           控制什么时候给FFT IP给输入数据以及输入给FFT IP的数据是否有效
*/
reg         fft_s_data_tvalid;
reg  [31:0] fft_s_data_tdata;
reg         fft_s_data_tlast;
reg [10:0]  count;
reg [3:0]   ST_FFT;
always @(posedge clk)  
begin
    if(!rst_n) 
        begin
            ST_FFT<=4'd0;
            fft_s_data_tvalid<=1'b0;
            fft_s_data_tdata<=32'dx;
            fft_s_data_tlast<=1'b0;
            count<=11'd0;
        end
     case(ST_FFT) 
           0:
               begin
                    fft_s_data_tvalid<=1'b0;   // FFT IP的从端数据无效
                    fft_s_data_tdata<=32'dx;   //数据保持,不给新数据
                    fft_s_data_tlast<=1'b0;
                    count<=11'd0;     
                    if (tdata_i_valid_syn)// 输入数据有效  是一个脉冲信号(上升沿有效)
                        begin
                            ST_FFT<=4'd1;                            
                        end   
                    else//外界数据无效或者且FFT IP没有准备好接收数据,握手不成功
                        begin
                            ST_FFT<=4'd0;                     
                        end       
                end
            1:           
                begin
                    fft_s_data_tvalid<=1'b1;    //输入给FFT IP的数据有效
                    fft_s_data_tdata<=tdata_i;  //给FFT IP灌数据   
                    if(count==11'd1023)  //最后一个数据
                        begin            
                            fft_s_data_tlast<=1'b1; //last信号拉高
                            count<=11'd0;           //计数器清零
                            ST_FFT<=4'd2;
                        end
                    else 
                        begin             
                           fft_s_data_tlast<=1'b0;//last信号为低   
                           count<=count+1;         //计数器的值+1
                           ST_FFT<=4'd1;
                        end
                end              
           2:
                begin
//                  fft_s_data_tdata<=tdata_i;  //给FFT IP灌数据   
                    fft_s_data_tdata<=32'dx;  
                    fft_s_data_tlast<=1'b0;     //last信号拉低   
                    fft_s_data_tvalid<=1'b0;   // FFT IP的从端数据无效
                    count <=11'd0;              //计数器清零
                    ST_FFT<=4'd0;                     
                end               
           default :
                ST_FFT<= 4'd0;
          endcase 
end                            




/*
        step 3:例化FFT IP 
*/
// output 
wire         fft_s_data_tready ;// 表示FFT IP核准备好接受数据  

wire  [63:0] fft_m_data_tdata  ;// FFT 变换后的结果(频域信号)
wire         fft_m_data_valid     ;
wire         fft_m_data_tlast  ;
wire [15:0]  fft_m_data_tuser  ;
xfft_1 Inst_xfft_1(
    //FFT的时钟、时钟使能、复位信号(注意复位信号要多给几个时钟)
    .aclk(clk),                        // input wire aclk
    .aresetn(rst_n),                   // input wire aresetn
   
    // FFT的重配置接口
    .s_axis_config_tdata(8'd1),        // input wire [7 : 0] s_axis_config_tdata
    .s_axis_config_tvalid(1'b1),       // input wire s_axis_config_tvalid
    .s_axis_config_tready(),           // output wire s_axis_config_tready
    
    //FFT的数据输入接口,遵循AXI-Stream协议
    .s_axis_data_tdata(fft_s_data_tdata),     // input wire [31 : 0] s_axis_data_tdata
    .s_axis_data_tvalid(fft_s_data_tvalid),   // input wire s_axis_data_tvalid
    .s_axis_data_tready(fft_s_data_tready),   // output wire s_axis_data_tready
    .s_axis_data_tlast(fft_s_data_tlast),     // input wire s_axis_data_tlast
  
  
    //FFT的数据输出接口,遵循AXI-Stream协议
    .m_axis_data_tdata(fft_m_data_tdata),     // output wire [63 : 0] m_axis_data_tdata
    .m_axis_data_tuser(fft_m_data_tuser),     // output wire [15 : 0] m_axis_data_tuser
    .m_axis_data_tvalid(fft_m_data_valid),       // output wire m_axis_data_tvalid
    .m_axis_data_tready(1'b1),                // input wire m_axis_data_tready
    .m_axis_data_tlast(fft_m_data_tlast),     // output wire m_axis_data_tlast
   
   //可以输出一些FFT的错误信息,比如输入的last未知不正确或没有,数据溢出等等
    .event_frame_started(),           // output wire event_frame_started
    .event_tlast_unexpected(),        // output wire event_tlast_unexpected
    .event_tlast_missing(),           // output wire event_tlast_missing
    .event_status_channel_halt(),     // output wire event_status_channel_halt
    .event_data_in_channel_halt(),    // output wire event_data_in_channel_halt
    .event_data_out_channel_halt()    // output wire event_data_out_channel_halt
  );

wire [26:0]  dati_out;//27bit
wire [26:0]  datq_out;
assign datq_out=fft_m_data_tdata[58:32];//虚部   结果只有[26:0]的数据才是有效的  
assign dati_out=fft_m_data_tdata[26:0];//实部     ---IP总结表明需要这样截位   
 
 
 /*
        step 3:得到信号的幅度谱
*/
//step 3.1 :得到信号的功率
wire [31:0]  dati_out_32bit;
wire [31:0]  datq_out_32bit;
assign dati_out_32bit={dati_out[26],dati_out[26],dati_out[26],
                       dati_out[26],dati_out[26],dati_out};
assign datq_out_32bit={datq_out[26],datq_out[26],datq_out[26],
                       datq_out[26],datq_out[26],datq_out};
wire  [127 : 0] fft_m_axis_dout_tdata;
wire fft_P_Vaild;
wire [63:0]  fft_P;// 信号的频谱(幅度值的平方)
cpMult32x32 Inst_cpMult32x32 (
  .aclk(clk),                              // input wire aclk
  .s_axis_a_tvalid(fft_m_data_valid),        // input wire s_axis_a_tvalid
  .s_axis_a_tdata({datq_out_32bit,dati_out_32bit}),   // input wire [63 : 0] s_axis_a_tdata
  .s_axis_b_tvalid(fft_m_data_valid),        // input wire s_axis_b_tvalid
  .s_axis_b_tdata({~datq_out_32bit+1,dati_out_32bit}),    // input wire [63 : 0]  s_axis_b_tdata
  
  .m_axis_dout_tvalid(fft_P_Vaild),  // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(fft_m_axis_dout_tdata)    // output wire [127 : 0] m_axis_dout_tdata
);
assign fft_P=fft_m_axis_dout_tdata[63:0]; //  幅度谱的实部---IP总结表明需要这样截位 



/*
    step 3.2 :得到信号的幅度 Amp
    Amp的计算方法
    Amp = abs(fft_P)/L 
        = sqrt(实部的平方+虚部的平方)/L   % 即表示先求模值,再除以采样点数   
        = sqrt(实部的平方+虚部的平方)/sqrt(L^2)
        = sqrt(  (实部的平方+虚部的平方)/L^2 )
        = sqrt(  fft_P /L^2 )
*/
wire [34 : 0] fft_P_44bit;
wire [47 : 0] fft_P_48bit;
assign fft_P_44bit=fft_P[63:20];//  [19:0]= 缩小2^20倍=  L^2 =(2^10)^2
assign fft_P_48bit={fft_P_44bit[44],fft_P_44bit[44],fft_P_44bit[44],fft_P_44bit[44],fft_P_44bit };

wire [31:0] fft_Amp    ;  // 开方结果,得到信号的幅度谱
wire fft_Amp_vaild;
CORDICROOT Inst_CORDICROOT (
  .aclk(clk),                                        // input wire aclk
  .s_axis_cartesian_tvalid(fft_P_Vaild),  // input wire s_axis_cartesian_tvalid
  .s_axis_cartesian_tdata(fft_P_48bit),    // input wire [47 : 0] s_axis_cartesian_tdata
  
  .m_axis_dout_tvalid(fft_Amp_vaild),            // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(fft_Amp)              // output wire [31 : 0] m_axis_dout_tdata
);
wire [24:0] fft_Amp_25bit;
assign fft_Amp_25bit=fft_Amp[24 : 0] ;//--IP总结表明需要这样截位 



/* 
    step 5:找到一组数据的最大值,得到信号的幅度
*/
wire [24:0]  data_max_25bit;
Max_Get Inst_Max_Get(
    //input
    .clk           (clk           ),
    .reset         (!rst_n        ),
    .En            (fft_Amp_vaild ), //是一个脉冲信号(上升沿有效)      
    .data_in       (fft_Amp_25bit ),//信号的幅度谱  [24:0] 
   
   
     //output
    .data_max      (data_max_25bit) ,//[24:0]  幅度谱的最大值,即为时域信号的幅度
    .DataMax_Vaild (DataMax_Vaild )  // 信号幅度有效信号 //是一个脉冲信号(上升沿有效)      
 );
assign data_max=data_max_25bit[15:0];




endmodule
  

3.3 Max_Get模块

`timescale 1ns / 1ps

// 找一串序列(深度为1024)的最大值

module Max_Get(
    input               clk             ,
    input               reset           ,
    input               En              ,//模块开始工作的使能信号  是一个脉冲信号(上升沿有效)      
    input [24:0]        data_in         ,// 输入数据
    
    output reg [24:0]   data_max        ,//搜索得到的最大值
    output reg          DataMax_Vaild    //是一个脉冲信号(上升沿有效)      
 );
 
 
  
reg [24:0]   data_max_temp;  
reg [3:0]    ST_max_search;
reg [15:0]   count;
   
always@(posedge clk)
begin
   if(reset)
        begin
             data_max_temp<=25'd0;    
             ST_max_search<=4'd0;
             count<=16'd0;
             DataMax_Vaild<=1'b0;
        end
   else
        begin
           case(ST_max_search)
              0:
                    begin
                        if(En==1'b1) //是一个脉冲信号(上升沿有效)      
                            begin
                                ST_max_search<=4'd1;
                                data_max_temp<=25'd0;//数据归零,避免影响下一次寻求最大值
                                count<=16'd0;//计数器清零
                            end
                       else
                            begin
                                ST_max_search<=4'd0;
                            end                             
                    end
              1:
                    begin
                        if(count<16'd1024)
                            begin
                                count<=count+1'd1;
                                ST_max_search<=4'd1;
                                
                                if(data_in>data_max_temp)
                                    data_max_temp<=data_in;
                                else
                                    data_max_temp<=data_max_temp;
                            end
                        else//if(count==16'd1024)  
                             begin
                                data_max<= data_max_temp;//输出最大值
                                DataMax_Vaild<=1'b1; //是一个脉冲信号(上升沿有效)                                 
                                ST_max_search<=4'd2;                           
                            end                                    
                    end 
              2:    
                    begin
                        ST_max_search<=4'd0;                                              
                        data_max_temp<=25'd0;//数据归零,避免影响下一次寻求最大值
                        count<=16'd0;
                        DataMax_Vaild<=1'b0;                   
                    end             
              default:         
                  ST_max_search<= 4'd0;//跳到状态0   
           endcase    
        end  
end      
      
     
     
     
        
endmodule

3.4 testbench

`timescale 1ns / 1ps

/*
    1、出一次结果需要40us,整个仿真需要250us
    2、信号幅度是32767,估计出来的结果是有效信号幅度 即23767/sqrt(2)=22925
   易错点:1、tdata_i_valid必须是一个脉冲信号
            2、FFT IP核里面设置的信号频率必须和外部信号频率一致
*/
module sim_Use_FFT_SignalAmp;

reg clk_250M;
reg reset;


//  step 1:DDS产生参考信号 RtI,RtQ
wire [31:0] Calib_Freq;//频率字 
wire Calib_Freq_vaild;//频率字有效信号 
assign Calib_Freq=32'h147AE148;//20M      dec2hex(round(20/250*2^32))
assign Calib_Freq_vaild=1'b1; 

wire [31:0]  m_axis_data_PINC;
wire m_axis_data_PINC_vaild;
wire [15:0]    RtI;
wire [15:0]    RtQ ;    
dds_data Inst_dds_data (
  .aclk(clk_250M),                           // input wire aclk
  .s_axis_config_tvalid(Calib_Freq_vaild), // input wire s_axis_config_tvalid
  .s_axis_config_tdata(Calib_Freq),     // input wire [31 : 0] s_axis_config_tdata
  
  .m_axis_data_tvalid(m_axis_data_PINC_vaild),       // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_data_PINC),  // output wire [31 : 0] m_axis_data_tdata
  .m_axis_phase_tvalid(),    // output wire m_axis_phase_tvalid
  .m_axis_phase_tdata()      // output wire [31 : 0] m_axis_phase_tdata
);
assign RtI=m_axis_data_PINC[15:0];  //I:实部   Q:虚部  
assign RtQ=m_axis_data_PINC[31:16]; 


 

wire [15:0]   Beam1_Amp        ;//子波束1的接收信号的幅度
wire          Beam1_Amp_Vaild  ;
reg tdata_i_valid;
reg [15:0]    tdata_i_I;
reg [15:0]    tdata_i_Q ;
reg [1:0] Signal_num;
always @(posedge clk_250M )
begin
    if(Signal_num==2'd0)//第一种信号
        begin
            tdata_i_I<=RtI;
            tdata_i_Q<=RtQ;   
        end
    else if(Signal_num==2'd1)//第二种信号
        begin
            tdata_i_I<={RtI[15],RtI[15:1]};
            tdata_i_Q<={RtQ[15],RtQ[15:1]};   
        end
end



Use_FFT_SignalAmp  Inst_Use_FFT_SignalAmp(
    //input
    .clk               (clk_250M        ), // 工作时钟信号             
    .rst_n             (~reset          ), // 复位信号               
    .tdata_i_valid     (tdata_i_valid   ), // 必须是一个脉冲信号(上升沿有效)                
    .tdata_i           ({tdata_i_Q,tdata_i_I}), // 复信号  32bit 
   // .tdata_i           ({tdata_i_Q,16'd0}), // 只有实部信号  16bit   

    //output
    .data_max          (Beam1_Amp        ),//子波束1的信号幅度           
    .DataMax_Vaild     (Beam1_Amp_Vaild  ) //子波束的信号幅度有效信号                        
); 

 

initial
begin
    clk_250M<=1'b0;
    reset<=1'b1;
    tdata_i_valid<=1'b0;
    Signal_num<=2'dx;
  #30
    reset<=1'b0;


    
  //   第一次循环:第1种目标信号数据有效 
  #100  
    tdata_i_valid<=1'b1;
    Signal_num<=2'd0;
  #10  
    tdata_i_valid<=1'b0;       
    
  //   第2个目标信号数据有效 
  #40_000  
    tdata_i_valid<=1'b1;  
    Signal_num<=2'd1;
  #10  
    tdata_i_valid<=1'b0;  
  
 
    
  //  第2次循环: 第1个目标信号数据有效 
  #40_000  
    tdata_i_valid<=1'b1;
    Signal_num<=2'd0; 
  #10  
    tdata_i_valid<=1'b0;       
    
  //   第2个目标信号数据有效 
  #40_000  
    tdata_i_valid<=1'b1;  
    Signal_num<=2'd1;       
  #10  
    tdata_i_valid<=1'b0;  
 
 
  
  //  第3次循环: 第1个目标信号数据有效 
  #40_000  
    tdata_i_valid<=1'b1;
    Signal_num<=2'd0; 
  #10  
    tdata_i_valid<=1'b0;       
    
  //   第2个目标信号数据有效 
  #40_000  
    tdata_i_valid<=1'b1;  
    Signal_num<=2'd1;       
  #10  
    tdata_i_valid<=1'b0;    
  
                
end


always  #2  clk_250M = ~ clk_250M;   





endmodule

4 仿真结果分析

4.1 信号频率估计

DDS产生的信号频率为20M,T=50ns
在这里插入图片描述

FFT IP的信号频率估计值:
计算公式:m_axis_data_tuser* fs/COUNT=82*250M/1024= 20.0195MHz
频率估计值和真实值很接近;且任意时刻的频率估计值是一致的。
在这里插入图片描述
在这里插入图片描述

4.2 信号幅度估计

在这里插入图片描述
分析:
  1、信号幅度是31736,理论上的幅度有效值为31736/sqrt(2)=22441;FFT IP估计出来的信号幅度有效值为22925 ,误差较小。
  2、同一个目标信号,多次调用FFT IP,理论上幅度估计值应该是一致的。上图也证明了这一点,如第一次、第三次和第五次的幅度估计值。

4.3 小结

  1、频率估计:计算公式:m_axis_data_tuser* fs/COUNT=82*250M/1024= 20.0195MHz
  2、幅度估计:如果输入的是复信号,最后输出的值是信号幅度的有效值。如果输入的是实信号,最后输出的值是信号幅度的有效值的一半。

5 工程文件链接

完整工程文件

  • 17
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值