vivado FFT IP核使用

matlab生成正弦函数

采样点数为512,每个采样点位宽为16位,其中最高位为符号为(0正,1负)。换句话说,如果用ROM存储正弦函数的coe文件的话,ROM ip核的位宽设置为16,深度为512.

clear all;

Fs = 512;
t = 0:1/Fs:1-1/Fs;
y = sin(10*t);            %产生正弦波,总点数为512点
y_16bit = floor(y*2^15);  %把数据量化为15位,因为要留一个符号位

subplot(221);
plot(y_16bit);
xlabel('原始有符号数的正弦波形');

y1 = fft(y_16bit,512);
subplot(222);
plot(real(y1));
xlabel('原始有符号数的fft(实部)');

y_16bit_a = y_16bit;   %定义相同大小的变量,存储转变符号后的数据
%下面for循环的目的是将原本的负数变为正数(在最高位加上符号位),便于vivado读取
for i = 1:length(y_16bit)

    if( y_16bit_a(i) < 0 )
       y_16bit_a(i) = 2^16+y_16bit_a(i);%当信号小于零时,通过将其与 2^16 相加,可以确保最高位为1,从而表示该数字是一个负数
    end
 
end

subplot(223);
plot(y_16bit_a);
xlabel('将有符号数转化为无符号数的正弦波形');

y2 = fft(y_16bit_a,512);

subplot(224);
plot(real(y2));
xlabel('转化为无符号数后的fft(实部)');

fid = fopen('sinwave.txt','w');   %将转变符号后的数据写入文件中,等到vivado读取
fprintf(fid,'%x\r\n',y_16bit_a);

注意: 我们要确保输出给fft ip核的数据是16位,同时最高位是符号位,因此我们将原始数据乘上2的15次方填满低15位,在将负数加上2的16次方使得最高位为1。在我们写入txt时,我们是以%x(16进制写入的)因此正数最高位自动补0,负数保持不变,这样就确保了16bit输入,同时正数最高位为0,负数最高位为1

vivado FFT ip核使用

ip核的配置

 

此时我们看一下FFT ip核的信息

从图上可以知道,该FFT ip核在完成512次fft计算后会经过3476次时钟周期,一次时钟周期时4ns,因此一共延迟13868ns=13.868us

FFT ip核的例化 

顶层文件如下

`timescale 1ns / 1ps
module fft_top(
    input [15:0]data_in,
    input clk,
    input data_last,
    input data_valid,
    output out_valid,
    output [31:0]out_im,
    output [31:0]out_re
    );
 
 wire [63:0]fft_out;

 xfft_0 test (
  .aclk(clk),                                                 // input wire aclk                         (输入时钟)
  .s_axis_config_tdata(16'd1),                                // input wire [15 : 0] s_axis_config_tdata(配置数据,1为FFT,0为IFFT)
  .s_axis_config_tvalid(1'd1),                                // input wire s_axis_config_tvalid        (1则开始进行FFT配置,0停止)
  .s_axis_config_tready(),                                    // output wire s_axis_config_tready       (输出:当FFT配置好时,会给标志)
 
  .s_axis_data_tdata({16'd0,data_in}),                        // input wire [31 : 0] s_axis_data_tdata   (输入数据,高n位为虚部,低n位为实部)
  .s_axis_data_tvalid(data_valid),                             // input wire s_axis_data_tvalid          (1则开始进行FFT计算,0停止)
  .s_axis_data_tready(),                                      // output wire s_axis_data_tready         
  .s_axis_data_tlast(data_last),                               // input wire s_axis_data_tlast           (最后一个数据标志位,便于结束FFT)
 
  .m_axis_data_tdata(fft_out),                                // output wire [63 : 0] m_axis_data_tdata  (FFT的输出值)
  .m_axis_data_tvalid(out_valid),                             // output wire m_axis_data_tvalid
  .m_axis_data_tuser(),                                       // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tready(1'd1),                                  // input wire m_axis_data_tready 一直置1即可           
  .m_axis_data_tlast(),                                       // output wire m_axis_data_tlast
 
  .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
);
    assign out_im = fft_out[63:32];
    assign out_re = fft_out[31:0];
 
endmodule

FFT ip核的例化详见:Vivado IP核:FFT实验 - 知乎 (zhihu.com) 

TestBench文件编写

`timescale 1ns / 1ps
module fft_tb(
    );
    reg  [15:0] data_temp [0:511]; 
    reg  [15:0] data_input;

    reg clk;
    reg data_last;
    reg data_valid;
    wire [31:0]out_im;
    wire [31:0]out_re;
 
    wire out_valid;

    reg [31:0]reg_out_im;
    reg [31:0]reg_out_re;
 
    integer i;
    integer fid_out_re;
    integer fid_out_im;
 
    always #2 clk <= ~clk; //生成时钟
 ///例化代码
    fft_top bidesign_top1(
    .data_in(data_input),
    .clk(clk),
    .data_last(data_last),
    .data_valid(data_valid),
    .out_valid(out_valid),
    .out_im(out_im),
    .out_re(out_re)
    );
 ///
    initial begin
        clk        <= 1'd1;
        data_input   <= 16'd0;
        data_valid  <= 1'b0;
        data_last   <= 1'b0;
        $readmemh("E:/FPGA/book/vivado/FPGA_DSP/project_FFT_ip/matlab/sinwave.txt",data_temp); //读文件
        fid_out_re = $fopen("E:/FPGA/book/vivado/FPGA_DSP/project_FFT_ip/matlab/out_re.txt","w"); 
        fid_out_im = $fopen("E:/FPGA/book/vivado/FPGA_DSP/project_FFT_ip/matlab/out_im.txt","w");
         #10; //延迟2.5个时钟周期     
 
        for(i = 0;i<=511;i = i+1)begin
            data_valid <= 1;
            data_input  <= data_temp[i];
            #4;//延迟一个时钟周期
        end
 
        data_last  <= 1;
        data_valid <= 0;
        data_input <= 16'd0;       
        #2000;
    end
 
 
     always @ (posedge clk) begin    
                $fwrite(fid_out_re,"%d\n",$signed(reg_out_re[31:0])); 
                $fwrite(fid_out_im,"%d\n",$signed(reg_out_im[31:0]));               
    end
 
     always @ (posedge clk) begin                //fft输出结果到寄存器中保存,使数据更稳定
               reg_out_im <= out_im;
               reg_out_re <= out_re;
    end
 
     always @ (posedge clk) begin
       if(!out_valid) begin    
                  reg_out_re <= 32'd0;
                  reg_out_im <= 32'd0;
       end
    end
endmodule

值得注意的点:输入fft的数据是在 clk(系统时钟)的上升沿读取的,如果数据也是在clk上升沿更新的话可能会出现亚稳态,因此我们通过时延半个周期使得数据在中间位置(即 clk 的下降沿)被fft读取时才是相对稳定的。在代码中#10确保了data_input在下降沿更新,#4确保了数据更新周期与时钟周期保持一致,这样就确保了FFT读取data_input时data_input信号是稳定的。

时序分析

注意:data_input的格式调整为unsigned/analog下的曲线 

接下来我们分析每个信号的变化关系

注意:data_input也是延迟10ns后开始变化的,但是由于data_input初始值为0,所以在10~14ns的时间内任然保持0

接下来我们重点分析FFT的输出信号:out_re和out_im 

out_re是延迟了11828ns输出结果,out_im是延迟了11832ns输出结果,它们分别对应延迟了11828/4=2957和11832/4=2958个时钟周期。除此之外,可以发现reg_out_im和reg_out_re滞后out_im和out_re一个时钟周期,这也是时序逻辑赋值产生的延迟

out_re和out_im在延迟了13876ns后输出完成,一共延迟了13876/4=3469个时钟周期,减去初始化的2.5个之中周期后,延迟结果与FFT ip核的信息给出的结果保持一致。

Matlab 数据分析

我们将vivado输出的FFT ip核的fft 实部虚部结果的txt文件导入matlab

matlab导入txt代码如下:

clear all;
present_re=importdata('out_re.txt');
present_im=importdata('out_im.txt');


% 找到非零值的索引
nonzero_indices_re = find(present_re.data ~= 0);

% 提取非零值对应的元素
re = present_re.data(nonzero_indices_re);

plot(re);

我们只需要去其实部即可:

 与matlab结果保持一致。验证了我们的工作正确性

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值