FPGA实现8点FFT

这篇具有很好参考价值的文章主要介绍了FPGA实现8点FFT。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前面我们讲了FFT的原理以及其在C++上的实现,可以参考我的博客:

快速傅里叶变换学习(超详细,附代码实现)_Patarw_Li的博客-CSDN博客

C++实现FFT算法(迭代版本)_Patarw_Li的博客-CSDN博客

下面我们会在FPGA上用Verilog实现8点FFT,下面是需要注意的几点:

1. 旋转因子

在FPGA中直接计算旋转因子是一件比较麻烦的事,因此我们使用MATLAB将旋转因子计算好后直接在verilog中赋值即可:

8点fft,数字信号处理,FPGA学习,fpga开发

 生成旋转因子实部和虚部的matlab代码:

%fft旋转因子生成表
%w代表返回值,n代表运算点数
%这里将w放大,是因为浮点运算比较消耗时间,因此将其化为整数
clear all;
clc;

n = 8;
for i=0:n/2
    wr=cos(-2*pi*i/n)*256 %实部
    wi=sin(-2*pi*i/n)*256 %虚部
end

8点fft,数字信号处理,FPGA学习,fpga开发

之所以要乘以256是因为浮点运算比较消耗时间,因此将其化为整数。

在verilog中的赋值代码:

wire signed [23:0] wndatareal[0:3];    //旋转因子实部数据
wire signed [23:0] wndataimg[0:3];     //旋转因子虚部数据
//通过matlab计算出旋转因子w0、w1、w2、w3,再乘以256,然后直接赋值
assign wndatareal[0] = 24'd256;
assign wndataimg[0] = 24'd0;
assign wndatareal[1] = 24'd181;
assign wndataimg[1] = -24'd181;
assign wndatareal[2] = 24'd0;
assign wndataimg[2] = -24'd256;
assign wndatareal[3] = -24'd181;
assign wndataimg[3] = -24'd181;

2. 码位倒置

8点fft,数字信号处理,FPGA学习,fpga开发

代码实现:

5'd1:begin  //码位倒置
            dft_oridata[data_cnt] <= input_data[{data_cnt[0],data_cnt[1],data_cnt[2]}];
            
            data_cnt <= data_cnt+1;
            if(data_cnt == N-1) begin
                state <= state+1;
                data_cnt <= 0;
            end
end

3. 蝶形运算

8点fft,数字信号处理,FPGA学习,fpga开发

设z1=a+bi,z2=c+di(a、b、c、d∈R)是任意两个复数,那么它们的积(a+bi)(c+di)=(ac-bd)+(bc+ad)i。 

代码实现:

//(a+bi)*(c+di)=a*c-b*d + (a*d+b*c)i
cache_real = dft_firoutreal[data_cnt+cal_stage]*wndatareal[wndata_cnt]-
             dft_firoutimg[data_cnt+cal_stage]*wndataimg[wndata_cnt];
cache_img = dft_firoutreal[data_cnt+cal_stage]*wndataimg[wndata_cnt]+
            dft_firoutimg[data_cnt+cal_stage]*wndatareal[wndata_cnt];
            
cache_realres[31:0] = (dft_firoutreal[data_cnt]<<8) + cache_real;
cache_imgres[31:0] = (dft_firoutimg[data_cnt]<<8) + cache_img;
dft_secoutreal[data_cnt] = cache_realres[31:8];
dft_secoutimg[data_cnt] = cache_imgres[31:8];
            
cache_realres[31:0] = (dft_firoutreal[data_cnt]<<8) - cache_real;
cache_imgres[31:0] = (dft_firoutimg[data_cnt]<<8) - cache_img;
dft_secoutreal[data_cnt+cal_stage] = cache_realres[31:8];
dft_secoutimg[data_cnt+cal_stage] = cache_imgres[31:8];

代码的上面4行是乘以旋转因子,下面8行是两个序列相加减,之所以dft_firoutreal要往左移8位是因为旋转因子被放大了256倍,后面在加减完成后,再缩小256倍。

总体Verilog代码(包含testbench):

//2023/4/18 lzp 8点fft
`timescale 1ns/1ps


module fft
(
    input clk_100m,             //时钟
    input rst_n,                //复位
    input signed [23:0] data,   //输入数据
    output [23:0] data_r,  //输出数据的实部
    output [23:0] data_i   //输出数据的虚部
);

//常数定义
parameter N = 4'd8;

//数组和标志位定义
wire signed [23:0] wndatareal[0:3];    //旋转因子实部数据
wire signed [23:0] wndataimg[0:3];     //旋转因子虚部数据
//通过matlab计算出旋转因子w0、w1、w2、w3,再乘以256,然后直接赋值
assign wndatareal[0] = 24'd256;
assign wndataimg[0] = 24'd0;
assign wndatareal[1] = 24'd181;
assign wndataimg[1] = -24'd181;
assign wndatareal[2] = 24'd0;
assign wndataimg[2] = -24'd256;
assign wndatareal[3] = -24'd181;
assign wndataimg[3] = -24'd181;

reg signed [23:0] input_data [0:N-1];    //原始输入数据,最高位为符号位
reg signed [23:0] dft_oridata [0:N-1];   //码位倒置后的输入数据,最高位为符号位

//第一级输出
reg signed [23:0] dft_firoutreal [0:N-1];  //第一级DFT输出数据实部,最高位为符号位
reg signed [23:0] dft_firoutimg [0:N-1];   //第一级DFT输出数据虚部,最高位为符号位
//第二级输出
reg signed [23:0] dft_secoutreal [0:N-1];  //第二级DFT输出数据实部,最高位为符号位
reg signed [23:0] dft_secoutimg [0:N-1];   //第二级DFT输出数据虚部,最高位为符号位
//第三级输出
reg signed [23:0] dft_trdoutreal [0:N-1];  //第三级DFT输出数据实部,最高位为符号位
reg signed [23:0] dft_trdoutimg [0:N-1];   //第三级DFT输出数据虚部,最高位为符号位

//fft流水线
reg [4:0]   state;        //状态机
reg [7:0]   fft_stage;    //fft运算阶数
reg [3:0]  cal_stage;    //每组做几次蝶形运算
reg [3:0]   data_cnt;     //数据计数
reg [3:0]  wndata_cnt;   //旋转因子计数
reg [3:0]  group_cnt;    //每一阶计算时都会数据进行不同的分组,该寄存器计数已计算的分组

reg [31:0]  cache_real;   //数据实部缓存(用于计算)
reg [31:0]  cache_img;    //数据虚部缓存(用于计算)
reg [31:0]  cache_realres;//实部计算结果
reg [31:0]  cache_imgres; //虚部计算结果

reg [23:0] fft_data_r;
reg [23:0] fft_data_i;

always@(posedge clk_100m or negedge rst_n)
if(!rst_n) begin
    state <= 0;
    fft_stage <= 1;
    cal_stage <= 1;
    data_cnt <= 0;
    wndata_cnt <= 0;
    group_cnt <= 0;
    
    cache_real <= 0;
    cache_img <= 0;
    cache_realres <= 0;
    cache_imgres <= 0;
    
    fft_data_r <= 0;
    fft_data_i <= 0;
end
else begin
    case(state)
        5'd0:begin  //将输入的data填入input_data中
            input_data[data_cnt] <= data;
            
            data_cnt <= data_cnt+1;
            if(data_cnt == N-1) begin
                state <= state+1;
                data_cnt <= 0;
            end
        end
        5'd1:begin  //码位倒置
            dft_oridata[data_cnt] <= input_data[{data_cnt[0],data_cnt[1],data_cnt[2]}];
            
            data_cnt <= data_cnt+1;
            if(data_cnt == N-1) begin
                state <= state+1;
                data_cnt <= 0;
            end
        end
        5'd2:begin  //第一级蝶形运算,N/2点DFT
            cache_real = dft_oridata[data_cnt+cal_stage]*wndatareal[wndata_cnt];
            cache_img = dft_oridata[data_cnt+cal_stage]*wndataimg[wndata_cnt];
            
            cache_realres[31:0] = (dft_oridata[data_cnt]<<8) + cache_real;
            cache_imgres[31:0] = cache_img;
            dft_firoutreal[data_cnt] = cache_realres[31:8];
            dft_firoutimg[data_cnt] = cache_imgres[31:8];
            
            cache_realres[31:0] = (dft_oridata[data_cnt]<<8) - cache_real;
            cache_imgres[31:0] = 0-cache_img;
            dft_firoutreal[data_cnt+cal_stage] = cache_realres[31:8];
            dft_firoutimg[data_cnt+cal_stage] = cache_imgres[31:8];
            
            data_cnt = data_cnt+1;
            wndata_cnt = wndata_cnt+1;
            if(wndata_cnt == cal_stage) begin //代表这组计算完毕,开始下一组计算
                group_cnt = group_cnt+1;
                data_cnt = data_cnt+cal_stage; //将data_cnt指向下一组的起始位置
                wndata_cnt = 0;
                if(group_cnt == (N>>fft_stage)) begin //代表所有组都计算完毕,开始下一级蝶形运算
                    state <= state+1;
                    group_cnt <= 0;
                    data_cnt <= 0;
                    fft_stage <= fft_stage+1;
                    cal_stage <= cal_stage<<1;
                end
            end
        end
        5'd3:begin  //第二级蝶形运算,N/4点DFT
            //(a+bi)*(c+di)=a*c-b*d + (a*d+b*c)i
            cache_real = dft_firoutreal[data_cnt+cal_stage]*wndatareal[wndata_cnt]-
                         dft_firoutimg[data_cnt+cal_stage]*wndataimg[wndata_cnt];
            cache_img = dft_firoutreal[data_cnt+cal_stage]*wndataimg[wndata_cnt]+
                        dft_firoutimg[data_cnt+cal_stage]*wndatareal[wndata_cnt];
            
            cache_realres[31:0] = (dft_firoutreal[data_cnt]<<8) + cache_real;
            cache_imgres[31:0] = (dft_firoutimg[data_cnt]<<8) + cache_img;
            dft_secoutreal[data_cnt] = cache_realres[31:8];
            dft_secoutimg[data_cnt] = cache_imgres[31:8];
            
            cache_realres[31:0] = (dft_firoutreal[data_cnt]<<8) - cache_real;
            cache_imgres[31:0] = (dft_firoutimg[data_cnt]<<8) - cache_img;
            dft_secoutreal[data_cnt+cal_stage] = cache_realres[31:8];
            dft_secoutimg[data_cnt+cal_stage] = cache_imgres[31:8];
            
            data_cnt = data_cnt+1;
            wndata_cnt = wndata_cnt+1;
            if(wndata_cnt == cal_stage) begin //代表这组计算完毕,开始下一组计算
                group_cnt = group_cnt+1;
                data_cnt = data_cnt+cal_stage;//将data_cnt指向下一组的起始位置
                wndata_cnt = 0;
                if(group_cnt == (N>>fft_stage)) begin //代表所有组都计算完毕,开始下一级蝶形运算
                    state <= state+1;
                    group_cnt <= 0;
                    data_cnt <= 0;
                    fft_stage <= fft_stage+1;
                    cal_stage <= cal_stage<<1;
                end
            end
        end
        5'd4:begin  //第三级蝶形运算,N/8点DFT
            //(a+bi)*(c+di)=a*c-b*d + (a*d+b*c)i
            cache_real = dft_secoutreal[data_cnt+cal_stage]*wndatareal[wndata_cnt]-
                         dft_secoutimg[data_cnt+cal_stage]*wndataimg[wndata_cnt];
            cache_img = dft_secoutreal[data_cnt+cal_stage]*wndataimg[wndata_cnt]+
                        dft_secoutimg[data_cnt+cal_stage]*wndatareal[wndata_cnt];
            
            cache_realres[31:0] = (dft_secoutreal[data_cnt]<<8) + cache_real;
            cache_imgres[31:0] = (dft_secoutimg[data_cnt]<<8) + cache_img;
            dft_trdoutreal[data_cnt] = cache_realres[31:8];
            dft_trdoutimg[data_cnt] = cache_imgres[31:8];
            
            cache_realres[31:0] = (dft_secoutreal[data_cnt]<<8) - cache_real;
            cache_imgres[31:0] = (dft_secoutimg[data_cnt]<<8) - cache_img;
            dft_trdoutreal[data_cnt+cal_stage] = cache_realres[31:8];
            dft_trdoutimg[data_cnt+cal_stage] = cache_imgres[31:8];
            
            data_cnt = data_cnt+1;
            wndata_cnt = wndata_cnt+1;
            if(wndata_cnt == cal_stage) begin //代表这组计算完毕,开始下一组计算
                group_cnt = group_cnt+1;
                data_cnt = data_cnt+cal_stage;//将data_cnt指向下一组的起始位置
                wndata_cnt = 0;
                if(group_cnt == (N>>fft_stage)) begin //最后一级蝶形运算
                    state <= state+1;
                    group_cnt <= 0;
                    data_cnt <= 0;
                    fft_stage <= fft_stage+1;
                    cal_stage <= cal_stage<<1;
                end
            end
        end
        5'd5:begin  
            fft_data_r <= dft_trdoutreal[data_cnt];
            fft_data_i <= dft_trdoutimg[data_cnt];
            
            data_cnt <= data_cnt+1; 
            if(data_cnt == N-1) begin
                state <= state+1; 
                data_cnt <= 0;
            end
        end
        default:begin 
            cal_stage <= 1;
            fft_stage <= 1; 
            data_cnt <= 0; 
        end
    endcase
end

//连接到模块输出
assign data_r = fft_data_r;
assign data_i = fft_data_i;

endmodule

//testbench
module fft_tb;

parameter N = 4'd8;

reg clk_100m;
reg rst_n;
reg signed [23:0] data;
wire [23:0] data_r;
wire [23:0] data_i;

reg [23:0] input_data [0:N-1];
reg [3:0]   data_cnt;     //数据计数

initial begin
    clk_100m=0;
    rst_n<=0;
    data<=0;
    data_cnt<=0;
    //用于输入的数据1 0 4 3 1 0 4 3
    input_data[0]<=1;
    input_data[1]<=0;
    input_data[2]<=4;
    input_data[3]<=3;
    input_data[4]<=1;
    input_data[5]<=0;
    input_data[6]<=4;
    input_data[7]<=3;
    
    #15 rst_n<=1;
end

//每隔一个时钟周期填入一个数据
always#10 begin
    data <= input_data[data_cnt];
    data_cnt <= data_cnt+1;
    if(data_cnt == N-1) begin
        data_cnt <= 0;
    end
end

//同名例化
fft fft1(
    clk_100m,
    rst_n,
    data,
    data_r,
    data_i
);

always#5 clk_100m<=~clk_100m;

endmodule

测试

输入:

8点fft,数字信号处理,FPGA学习,fpga开发

matlab中fft输出结果:

8点fft,数字信号处理,FPGA学习,fpga开发

fpga仿真:

将如下三个参数加入观察:input_data是输入数据,dft_trdoutreal和dft_trdoutimg对应输出的实部和虚部:

8点fft,数字信号处理,FPGA学习,fpga开发

仿真波形图:

8点fft,数字信号处理,FPGA学习,fpga开发

可以看到和matlab输出结果一致。

参考材料

1024点fft原理及fpga实现_雷凌峻毅的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-742259.html

到了这里,关于FPGA实现8点FFT的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA 的数字信号处理:Verilog 实现简单的 FIR 滤波器

    该项目介绍了如何使用 Verilog 实现具有预生成系数的简单 FIR 滤波器。 不起眼的 FIR 滤波器是 FPGA 数字信号处理中最基本的模块之一,因此了解如何将具有给定抽头数及其相应系数值的基本模块组合在一起非常重要。因此,在这个关于 FPGA 上 DSP 基础实用入门的教程中,将从一

    2024年02月09日
    浏览(50)
  • 数字信号处理-10-并行FIR滤波器MATLAB与FPGA实现

    本文介绍了设计滤波器的FPGA实现步骤,并结合杜勇老师的书籍中的并行FIR滤波器部分进行一步步实现硬件设计,对书中的架构做了复现以及解读,并进行了仿真验证。 FIR滤波器的结构形式时,介绍了直接型、级联型、频率取样型和快速卷积型4种。在FPGA实现时,最常用的是最

    2023年04月09日
    浏览(49)
  • FPGA与LVDS:数字信号处理的完美组合

    FPGA与LVDS:数字信号处理的完美组合 FPGA(Field-Programmable Gate Array)是一种可编程逻辑器件,具有高度灵活性和可重构性,可实现各种数字逻辑和处理功能。而LVDS(Low-Voltage Differential Signaling)则是一种用于高速数据传输的技术,特点是传输距离长、抗干扰能力强、带宽大。

    2024年01月23日
    浏览(52)
  • FPGA 的数字信号处理:重写 FIR 逻辑以满足时序要求

    在上一篇文章中(FPGA 的数字信号处理:Verilog 实现简单的 FIR 滤波器)演示了在 Verilog 中编写自定义 FIR 模块的初始demo。该项目在行为仿真中正常,但在布局和布线时未能满足时序要求。 所以今天的文章让我们来看看当设计不能满足时序要求时如何分析并解决它。 当在目标

    2024年02月09日
    浏览(45)
  • 【FPGA】FFT测量信号频率(Quartus IP核)

    ​​​​​​​ 一、前言 二、FFT是什么(原理)? 三、FFT IP核参数介绍 四、仿真 0、文件完整结构 1、设置IP核 2、例化FFT,并完善顶层文件 3、利用matlab生成正弦波信号 4、导出变量x生成的正弦波数据 5、编写testbench 6、RTL Simulation 五、上板 1、matlab生成正弦波信号并导出m

    2024年04月28日
    浏览(37)
  • 【数字信号处理课程设计】基于MATLAB实现语音信号的采集与处理(偏重滤波)

    目录 一、目标与任务 二、原理介绍 2.1 录音原理 2.2 滤波器的设计原理及设计方法 2.3 IIR 数字滤波器设计原理 2.4 双线性变换法 三、GUI界面设计与实现 四、基于MATLAB仿真 4.1实验过程 4.2 结果分析 五、总结 5.1 函数用法总结 5.2 心得体会 六、参考文献 这个项目在我的B站上有专

    2024年01月18日
    浏览(57)
  • 数字信号处理实验:IIR数字滤波器设计及软件实现

    目录 一、实验目的 二、实验原理 三、实验设备 四、实验内容及步骤 五、实验结果及分析 六、实验主程序框图及程序清单 七、实验总结 熟悉用双线性变换法设计IIR数字滤波器的原理与方法; 学会调用MATLAB信号处理工具箱中滤波器设计函数(或滤波器设计分析工具FDATool)设

    2024年02月12日
    浏览(43)
  • Simulink HDL Coder FPGA初级开发实践(五)FFT以及CORDIC算法进行信号相位校正

    前言: 本栏目除特别说明以外,均采用的黑金AX7103开发板,该开发板时钟频率为200M,并且是双端时钟,因此在每个项目中都有一段原语将双端时钟变成200MHz的单端时钟。文章仅作为学习记录,如有不足请在评论区指出,博主不会对各位的问题作出解答,请谅解。博主深知网

    2024年02月06日
    浏览(50)
  • 【数字化处理】仿生假体控制中肌电信号的数字化处理研究(Matlab代码实现)

     💥💥💞💞 欢迎来到本博客 ❤️❤️💥💥 🏆博主优势: 🌞🌞🌞 博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️ 座右铭: 行百里者,半于九十。 📋📋📋 本文目录如下: 🎁🎁🎁 目录 💥1 概述 📚2 运行结果 🎉3 参考文献 🌈4 Matlab代码及文献 文献

    2024年02月12日
    浏览(45)
  • 数字信号处理第四次试验:IIR数字滤波器设计及软件实现

    为了帮助同学们完成痛苦的实验课程设计,本作者将其作出的实验结果及代码贴至CSDN中,供同学们学习参考。如有不足或描述不完善之处,敬请各位指出,欢迎各位的斧正! (1)熟悉用双线性变换法设计IIR数字滤波器的原理与方法; (2)学会调用MATLAB信号处理工具箱中滤

    2024年02月08日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包