[FPGA] Vivado IP核:FFT实验

[复制链接]
2214|5
 楼主| gaochy1126 发表于 2022-11-30 20:58 | 显示全部楼层 |阅读模式
第一步:使用matlab产生正弦波,并且导入vivado中作为待FFT处理的信号
需要注意的地方:
  • 浮点数运算复杂,因此需要对正弦波进行量化
使用floor函数进行四舍五入取整,考虑到有符号数,只量化为15位,留一位作为符号位。

2. vivado不能直接读取负数,需要将负数转变为对应的有符号数
对于正负数的转化:是在原本15位数据的基础上加上符号位
以3位有符号数据为例,最高位是符号,那么-1就是 111。但如果把111看为无符号数,那么他就是7。
也即,三位符号数中-1就是无符号数中的7(计算过程为-1+2^3)
那么转到16位有符号数来说,也即为原负数加上2的16次方(代码中for循环可以体现)。

3. 如何根据时钟周期,将读取到的数据串行输出?
在testbench文件中读取数据,并且在for循环中依次串行输出
产生正弦波的matlab代码如下:
  1. clear all;

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

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

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

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

  16.     if( y_16bit_a(i) < 0 )
  17.        y_16bit_a(i) = 2^16+y_16bit_a(i);
  18.     end

  19. end

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

  23. y2 = fft(y_16bit_a,512);

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

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


图:matlab产生正弦波,进行符号转换并做fft
在这第一行第二个图是使用MATLAB进行FFT输出的结果。
后续 会使用vivado进行FFT运算,把二者的数据进行对比,如果不出意外的话,二者的数据应该是大致相同的!


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| gaochy1126 发表于 2022-11-30 20:58 | 显示全部楼层
第二步,对vivado FFT IP核进行配置
以下是我的配置界面
配置好IP核后,可以在下图的文件中直接复制代码方便例化。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| gaochy1126 发表于 2022-11-30 20:59 | 显示全部楼层
第三步,对FFT的IP核进行封装
说实话,第一次看到fft模块居然有那么多行参数需要设置,我整个人都麻了。。。。。。。。。。
相比较matlab只需要设置输入即可,vivado的FFT核模块要复杂的多。
我们庖丁解牛,把他们分开来分析,就会很好理解。

图:整个模块分为四大部分
  • 第一个方框的是配置FFT的信息,我们通过控制这些输入信号,来控制FFT的运作方式。
(1)s_axis_config_tdata:最后一位如果为1则是FFT模式,为0则是IFFT模式,这里我们设置为1
(2)s_axis_config_tvalid:配置信息有效位,恒为1即可
(3)s_axis_config_tready:配置完成标志,不需要的信号我在这里直接设置为空
2. 第二个方框:待FFT信号输入模块
(1)s_axis_data_tdata:待fft信号,需要注意的是,高16位为虚部,低16为是实部。这里我的输入数据全是实数,需要令高16位为0,再把它们拼接起来
(2)s_axis_data_tvalid:输入数据有效位,令该位和输入数据的第一位对齐。当输入信号结束时把它置0即可结束运算。
(3)s_axis_data_tready: 用不到,空置即可
(4)s_axis_data_tlast:当fft计算即将结束(到最后一位数据时),该标志位置1
3. 第三个方框:FFT计算后输出模块
(1)m_axis_data_tdata:这就是我们需要的FFT输出后的信号,仍然是高n位虚部,低n位实部
(2)m_axis_data_tvalid:当FFT开始输出时,该标志位一直置1。计算结束后,该位置0
(3)m_axis_data_tready:一直置1即可

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| gaochy1126 发表于 2022-11-30 20:59 | 显示全部楼层
4. 第四个方框:事件模块
由于事件对本次实验参考价值不大,为了简便就不再介绍
这里的bpsk信号就是之前的正弦波(名字忘了改了。。。)
对fft进行模块实例的veirlog代码如下:
  1. `timescale 1ns / 1ps
  2. module bidesign_top(
  3.     input [15:0]bpsk_in,
  4.     input clk,
  5.     input dat_last,
  6.     input dat_valid,

  7.     output out_valid,
  8.     output [31:0]out_im,
  9.     output [31:0]out_re
  10.     );

  11. wire [63:0]fft_out;

  12. xfft_0 test (
  13.   .aclk(clk),                                                 // input wire aclk                         (输入时钟)
  14.   .s_axis_config_tdata(16'd1),                                // input wire [15 : 0] s_axis_config_tdata(配置数据,1为FFT,0为IFFT)
  15.   .s_axis_config_tvalid(1'd1),                                // input wire s_axis_config_tvalid        (1则开始进行FFT配置,0停止)
  16.   .s_axis_config_tready(),                                    // output wire s_axis_config_tready       (输出:当FFT配置好时,会给标志)

  17.   .s_axis_data_tdata({16'd0,bpsk_in}),                        // input wire [31 : 0] s_axis_data_tdata   (输入数据,高n位为虚部,低n位为实部)
  18.   .s_axis_data_tvalid(dat_valid),                             // input wire s_axis_data_tvalid          (1则开始进行FFT计算,0停止)
  19.   .s_axis_data_tready(),                                      // output wire s_axis_data_tready         
  20.   .s_axis_data_tlast(dat_last),                               // input wire s_axis_data_tlast           (最后一个数据标志位,便于结束FFT)

  21.   .m_axis_data_tdata(fft_out),                                // output wire [63 : 0] m_axis_data_tdata  (FFT的输出值)
  22.   .m_axis_data_tvalid(out_valid),                             // output wire m_axis_data_tvalid
  23.   .m_axis_data_tready(1'd1),                                  // input wire m_axis_data_tready            
  24.   .m_axis_data_tlast(),                                       // output wire m_axis_data_tlast

  25.   .event_frame_started(),                                     // output wire event_frame_started
  26.   .event_tlast_unexpected(),                                  // output wire event_tlast_unexpected
  27.   .event_tlast_missing(),                                     // output wire event_tlast_missing
  28.   .event_status_channel_halt(),                               // output wire event_status_channel_halt
  29.   .event_data_in_channel_halt(),                              // output wire event_data_in_channel_halt
  30.   .event_data_out_channel_halt()                              // output wire event_data_out_channel_halt
  31. );
  32.     assign out_im = fft_out[63:32];
  33.     assign out_re = fft_out[31:0];

  34. endmodule


 楼主| gaochy1126 发表于 2022-11-30 20:59 | 显示全部楼层
第四步:编写 testbench文件
  1. `timescale 1ns / 1ps
  2. module bidesign_tb(
  3.     );
  4.     reg  [15:0] bpsk_in [0:511];
  5.     reg  [15:0] bpsk_out;

  6.     reg clk;
  7.     reg dat_last;
  8.     reg dat_valid;
  9.     wire [31:0]out_im;
  10.     wire [31:0]out_re;

  11.     wire out_valid;

  12.     reg [31:0]reg_out_im;
  13.     reg [31:0]reg_out_re;

  14.     integer i;
  15.     integer fid_out_re;
  16.     integer fid_out_im;

  17.     always #2 clk <= ~clk; //生成时钟
  18. ///////////////////例化代码
  19.     bidesign_top bidesign_top1(
  20.     .bpsk_in(bpsk_out),
  21.     .clk(clk),
  22.     .dat_last(dat_last),
  23.     .dat_valid(dat_valid),
  24.     .out_valid(out_valid),
  25.     .out_im(out_im),
  26.     .out_re(out_re)
  27.     );
  28. ///////////////////////////////////////////////////////////////////////////////////////////////
  29.     initial begin
  30.         clk        <= 1'd1;
  31.         bpsk_out   <= 16'd0;
  32.         dat_valid  <= 1'b0;
  33.         dat_last   <= 1'b0;
  34.         $readmemh("D:/code_vivado/bidesign/sinwave.txt",bpsk_in); //读文件
  35.         fid_out_re = $fopen("D:/code_vivado/bidesign/out_re.txt","w");
  36.         fid_out_im = $fopen("D:/code_vivado/bidesign/out_im.txt","w");
  37.          #10;      

  38.         for(i = 0;i<=511;i = i+1)begin
  39.             dat_valid <= 1;
  40.             bpsk_out  <= bpsk_in[i];
  41.             #4;
  42.         end

  43.         dat_last  <= 1;
  44.         dat_valid <= 0;
  45.         bpsk_out <= 16'd0;      
  46.         #2000;
  47.     end


  48.      always [url=home.php?mod=space&uid=72445]@[/url] (posedge clk) begin   
  49.                 $fwrite(fid_out_re,"%d\n",$signed(reg_out_re[31:0]));
  50.                 $fwrite(fid_out_im,"%d\n",$signed(reg_out_im[31:0]));               
  51.     end

  52.      always @ (posedge clk) begin                //fft输出结果到寄存器中保存,使数据更稳定
  53.                reg_out_im <= out_im;
  54.                reg_out_re <= out_re;
  55.     end

  56.      always @ (posedge clk) begin
  57.        if(!out_valid) begin   
  58.                   reg_out_re <= 32'd0;
  59.                   reg_out_im <= 32'd0;
  60.        end
  61.     end
  62. endmodule



 楼主| gaochy1126 发表于 2022-11-30 21:00 | 显示全部楼层
第五步:观察结果1. 观察vivado是否正确读取正弦波形,符号转换是否正确

将格式调整为unsigned下的曲线
将格式调整为signed下的曲线2. 观察输入部分波形
可以看出,当正弦波正常输出时(fft输入数据时),dat_valid置1,说明输入有效。当输入最后一个数据后,dat_last置1.
3. 观察输出部分波形
可以看出,当fft输出时,out_valid信号置1,当输出结束后归0。
4. 将reg_out_re,reg_out_im从vivado输出到txt文件中,再使用MATLAB画图,进行对比。
观察二者数据是否大致相同
可以看出,上面是IP核计算的输出,下面是MATLAB计算的输出。波形走向大致相同

5. 观察二者数据是否大致相同
可以看出,第一个都是3094019,第二个数据都是51开头的,后面也都对应的上。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1205

主题

11937

帖子

26

粉丝
快速回复 在线客服 返回列表 返回顶部