IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基于FPGA对信号连续做FFT开发调试总结 -> 正文阅读

[嵌入式]基于FPGA对信号连续做FFT开发调试总结

一、概述

在实际工程中,往往需要对ADC(模数转换)数据不间断的做FFT,或者在基于OFDM数字通信中,需要对连续输入的数据不间断地处理,因为这里采用了xilinx的RAM、FIFO和FFT等IP核,实现对连续输入的数据做256点FFT,并插入32点CP(循环前缀),最后将FFT的结果连续输出,符合实际应用需求。其中:
1、RAM用于存储256点正弦信号并且连续不间断的输出;
2、FIFO的读写采用异步时钟;
3、FFT采用了基于pipeline的架构,确保FFT连续输入输出,并且将FFT结果前添加CP;
整个设计框图如下:
在这里插入图片描述

二、各个模块配置介绍

1、PLL时钟模块

PLL时钟模块IP所处位置如下图所示。
在这里插入图片描述
这里的时钟模块选择的是MMCM,输入时钟为50M,分别用于产生50M和25M两个输出时钟源,具体配置如下图。
在这里插入图片描述
在这里插入图片描述

2、RAM模块

RAM模块IP所处位置如下图所示。
在这里插入图片描述
RAM选择的单口RAM,内部存储了256个正弦信号,具体配置如下图。
在这里插入图片描述
在这里插入图片描述

3、FIFO模块

FIFO模块IP所处位置如下图所示。
在这里插入图片描述
这里我们选择的是异步FIFO,写FIFO时钟为25M,读FIFO的时钟为50M,FFT点数为256,由于输出需要插入32点CP,为了保证输入数据连续,因此将FIFO深度配置为512,有点乒乓RAM的意思。具体配置如下图。
在这里插入图片描述
如下图,这里需要特别注意,Read Mode选择的是standard FIFO模式
在这里插入图片描述
在这里插入图片描述

standard FIFO模式与First word Fall through模式的区别见时序图:
在这里插入图片描述
上图为在standard FIFO模式下的时序图,FIFO读出数据有效信号要比读使能信号晚1拍。在这里插入图片描述
上图为First word Fall through模式下的时序图,FIFO读出数据有效信号与读使能信号完全同步。

注意:在本项目中,我们没有选择First word Fall through模式,因为该模式下即使在读使能无效的情况下,读数据还是一直有效,影响到我们通过读使能信号判断输入数据个数,如下图,可能是在First word Fall through模式下,先将数据抛到FIFO输出数据总线一直保持,因此读出数据有效信号一直为高电平。
在这里插入图片描述
在使用FIFO时,还需要注意:
1、FIFO的读空信号empty要滞后零点几ns,如下图。
在这里插入图片描述
2、写FIFO反馈信号wr_ack信号要比写FIFO使能信号fifo_wr_en要晚1拍。
3、FIFO的复位信号为高电平有效

4、FFT模块

FFT配置界面如下图所示,为保证连续输入输出,这里选择pipelined Streaming IO模式。
在这里插入图片描述
本项目要自动完成插入CP,需要选择下图选项。
在这里插入图片描述

三、测试代码

下面是完整的测试代码,里面有注释,供参考。

`timescale 1ns / 1ns
module tb_fft;
//--------------------系统宏定义--------------------
parameter FFT_LEN=256;
parameter FFT_ADD_CP_LEN=288;

//--------------------FFT模块配置参数---------------
//基2的参数配置
//parameter FWD=1'b1;//选择是FFT还是IFFT
//parameter SCALE=16'b01_01_01_01_01_00_01_10;
//parameter PAD=7'b000_0000;

//基4的参数配置
//parameter FWD=1'b1;//选择是FFT还是IFFT
//parameter SCALE=8'b01_10_11_10;//每级右移位数
//parameter PAD=7'b000_0000;

//基4+CP的参数配置
parameter FWD=1'b1;//选择是FFT还是IFFT
parameter SCALE=8'b01_10_11_10;
parameter PAD=7'b000_0000;
parameter CP_LEN=8'b0010_0000;//32点CP

//基2的寄存器长度
//wire [23:0]  s_axis_config_tdata;
//基4的寄存器长度
//wire [15:0]  s_axis_config_tdata;
//基4+CP的寄存器长度
wire [23:0]  s_axis_config_tdata;
wire s_axis_config_tready;

reg sclk;
reg rst_n;
wire fifo_wr_en;
reg Ram_rd_en;
wire fifo_rd_en;
wire locked;
reg fifo_w_clk_25M;
reg fifo_r_clk_50M;

reg  [7:0] addra;
wire [7:0] sin_out;  

wire s_axis_data_tready;
wire[15:0] s_axis_data_tdata;
wire s_axis_data_tvalid;
wire[15:0] m_axis_data_tdata;
wire[15:0] m_axis_data_tuser;
wire m_axis_data_tvalid;
wire m_axis_data_tlast;
reg s_axis_data_tlast;
reg [8:0] tlast_cnt;

wire[7:0] out_re;
wire[7:0] out_im;

wire m_axis_status_tdata;
wire m_axis_status_tvalid;
wire event_frame_started;
wire event_tlast_unexpected;
wire event_tlast_missing;
wire event_fft_overflow;
wire event_status_channel_halt;
wire event_data_in_channel_halt;
wire event_data_out_channel_halt;

wire [7:0] fifo_out;// output wire [7 : 0] dout
wire full;                  // output wire full
wire almost_full;    // output wire almost_full
wire wr_ack;              // output wire wr_ack
wire overflow;          // output wire overflow
wire empty;                // output wire empty
wire almost_empty;  // output wire almost_empty
wire underflow;        // output wire underflow
wire wr_rst_busy;    // output wire wr_rst_busy
wire rd_rst_busy;    // output wire rd_rst_busy
wire [8:0] rd_data_count;
wire [8:0] wr_data_count;

reg [8:0] empty_cnt;

initial
begin
#0 rst_n=0;
#0 fifo_r_clk_50M=0;fifo_w_clk_25M=0;sclk=0;
#0 sclk=0;
#90 rst_n=1;
end

initial
begin
#0 Ram_rd_en=0;
#705 Ram_rd_en=1;
wire_data();
end

always #10 fifo_r_clk_50M=~fifo_r_clk_50M;
always #20 fifo_w_clk_25M=~fifo_w_clk_25M;
always #10 sclk=~sclk;

//配置FFT参数
//基4的参数配置
//assign s_axis_config_tdata={PAD,SCALE,FWD};
//基4+CP的寄存器长度
assign s_axis_config_tdata={PAD,SCALE,FWD,CP_LEN};
//------------------------------------产生2个时钟模块----------------------------------------
clk_wiz_0 clk_wiz_inst
   (
    // Clock out ports
    //.clk_out1(fifo_r_clk_50M),     // output clk_out1
    //.clk_out2(fifo_w_clk_25M),     // output clk_out2
    .clk_out1(),     // output clk_out1
    .clk_out2(),     // output clk_out2
    // Status and control signals
    .resetn(rst_n), // input reset
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(sclk)  // input clk_in1
    ); 

//------------------------------------产生输入信号模块----------------------------------------
//周期性产生256点正弦信号地址    
always@(posedge fifo_w_clk_25M or negedge rst_n)
begin
		if(rst_n==1'b0)
		    addra<=8'd0;
		else if(Ram_rd_en==1'b1)
		    addra<=addra+1'b1;
end

//生成单口RAM的IP模块,读使能信号Ram_rd_en由外部测试信号给出
blk_mem_gen_0 blk_mem_gen_0_inst (
  .clka(fifo_w_clk_25M),    // input wire clka
  .ena(Ram_rd_en),      // input wire ena
  .wea(1'b0),      // input wire [0 : 0] wea
  .addra(addra),  // input wire [7 : 0] addra
  .dina('d0),    // input wire [7 : 0] dina
  .douta(sin_out)  // output wire [7 : 0] douta
);

//----------------------------------------FIFO模块--------------------------------------------
//产生FIFO写使能信号fifo_wr_en,即将读RAM信号Ram_rd_en延迟1拍,保证输出数据与FIFO写使能信号同步
reg [1:0] delay; 
always@(posedge fifo_w_clk_25M or negedge rst_n)
begin
		if(rst_n==1'b0)
		    delay<=2'd0;
		else
		    delay[1:0]<={delay[0:0],Ram_rd_en};		    
end

assign fifo_wr_en=delay[1];

//对非空信号进行计数,做为FIFO读使能信号的判断条件
always@(posedge fifo_r_clk_50M or negedge rst_n )
begin
        if(rst_n==1'b0)
            empty_cnt<=9'd0;
        else if((empty_cnt==FFT_ADD_CP_LEN-1) && (empty==1'b0))
            empty_cnt<=9'd0;
        else if(empty==1'b0)
            empty_cnt<=empty_cnt+1'b1;        
end

//产生FIFO读使能信号fifo_rd_en,使能信号要拉低32点,用于添加32点CP
assign fifo_rd_en = ((s_axis_data_tready==1'b1) && (empty==1'b0) && (empty_cnt<FFT_LEN))?  1'b1 : 1'b0;

//生成FIFO的IP模块
fifo_generator_0 fifo_generator_0_inst (
  .rst(~rst_n),                    // input wire rst
  .wr_clk(fifo_w_clk_25M),              // input wire wr_clk
  .rd_clk(fifo_r_clk_50M),              // input wire rd_clk
  .din(sin_out),                    // input wire [7 : 0] din
  .wr_en(fifo_wr_en),                // input wire wr_en
  .rd_en(fifo_rd_en),                // input wire rd_en
  .dout(fifo_out),                  // output wire [7 : 0] dout
  .full(full),                  // output wire full
  .almost_full(almost_full),    // output wire almost_full
  .wr_ack(wr_ack),              // output wire wr_ack
  .overflow(overflow),          // output wire overflow
  .empty(empty),                // output wire empty
  .almost_empty(almost_empty),  // output wire almost_empty
  .valid(s_axis_data_tvalid),                // output wire valid
  .underflow(underflow),        // output wire underflow
  .rd_data_count(rd_data_count),  // output wire [8 : 0] rd_data_count
  .wr_data_count(wr_data_count),  // output wire [8 : 0] wr_data_count
  .wr_rst_busy(wr_rst_busy),    // output wire wr_rst_busy
  .rd_rst_busy(rd_rst_busy)    // output wire rd_rst_busy
);

//-----------------------------------------FFT模块----------------------------------------------
//输入的正弦信号为实信号放在实部,虚部全部补零
assign s_axis_data_tdata={8'd0,fifo_out};
//用于产生FFT模块每帧输入最后(第256个数据)一个数据的控制信息s_axis_data_tlast
always@(posedge fifo_r_clk_50M or negedge rst_n) 
begin
        if(rst_n==1'b0)
            tlast_cnt<=9'd0;
        else if((tlast_cnt==FFT_LEN-1)&&(s_axis_data_tvalid==1'b1))
            tlast_cnt<=9'd0;        
        else if(s_axis_data_tvalid==1'b1)
            tlast_cnt<=tlast_cnt+1'b1;
end

always@(posedge fifo_r_clk_50M or negedge rst_n)
begin
        if(rst_n==1'b0)
            s_axis_data_tlast<=1'b0;
        else if(tlast_cnt==FFT_LEN-1)
            s_axis_data_tlast<=1'b1;
        else
            s_axis_data_tlast<=1'b0;                    
end
//生成FFT的IP模块   
xfft_0 xfft_0_inst (
  .aclk(fifo_r_clk_50M),                                                // input wire aclk
  .aclken(1'b1),                                            // input wire aclken
  .aresetn(rst_n),                                          // input wire aresetn
  .s_axis_config_tdata(s_axis_config_tdata),                  // input wire [23 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                // input wire s_axis_config_tvalid
  .s_axis_config_tready(s_axis_config_tready),                // output wire s_axis_config_tready
  
  .s_axis_data_tdata(s_axis_data_tdata),                      // input wire [15 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(s_axis_data_tvalid),                    // input wire s_axis_data_tvalid
  .s_axis_data_tready(s_axis_data_tready),                    // output wire s_axis_data_tready
  //.s_axis_data_tlast(1'b0),                      // input wire s_axis_data_tlast
  .s_axis_data_tlast(s_axis_data_tlast),                      // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_data_tdata),                      // output wire [15 : 0] m_axis_data_tdata
  .m_axis_data_tuser(m_axis_data_tuser),                      // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(1'b1),                    // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_data_tlast),                      // output wire m_axis_data_tlast
  
  .m_axis_status_tdata(m_axis_status_tdata),                  // output wire [7 : 0] m_axis_status_tdata
  .m_axis_status_tvalid(m_axis_status_tvalid),                // output wire m_axis_status_tvalid
  .m_axis_status_tready(1'b1),                // input wire m_axis_status_tready
  .event_frame_started(event_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_tlast_missing),                  // output wire event_tlast_missing
  .event_fft_overflow(event_fft_overflow),                    // output wire event_fft_overflow
  .event_status_channel_halt(event_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_data_out_channel_halt)  // output wire event_data_out_channel_halt
);

//-----------------------------------------FFT输出结果存成文件----------------------------------------------
//查看FFT输出结果的实虚部
assign out_re=m_axis_data_tdata[7:0];
assign out_im=m_axis_data_tdata[15:8];
//将FFT输出结果存成文件
integer fp_w;
task wire_data();  
	integer i;
	begin
	    fp_w= $fopen("fft_data_out.txt","w+");//以写的方式打开文件
		i=0;
		while(1)
		begin
			@(posedge fifo_r_clk_50M);
			begin
			     if(m_axis_data_tvalid==1'b1)
			     begin
			         $fwrite(fp_w,"%d",out_im) ;//写入文件
			         i=i+1;
			         if((i%FFT_ADD_CP_LEN)==0)//256+32点CP=288点
			         begin
			              $fwrite(fp_w,"\n") ;//写入文件
			         end
			     end
			end
		end
		$fclose(fp_w);//以写的方式打开文件
	end
endtask

endmodule
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-02-24 15:27:34  更:2022-02-24 15:29:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:33:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码