├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── butterfly.v ├── butterfly_tb.v ├── fft4.v ├── fft4_tb.v ├── ifft4.v ├── ifft4_tb.v ├── image ├── Butterfly.jpg ├── top_butterfly_tb.png └── top_ifft4_tb.png └── script └── project ├── .gitignore ├── CMakeLists.txt └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build/ 3 | *.vcd 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Neon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Encoding: UTF-8 2 | 3 | BUILD_DIR = build 4 | 5 | 6 | butterfly_tb: $(BUILD_DIR)/top_butterfly_tb.vcd 7 | gtkwave $< 8 | 9 | $(BUILD_DIR)/top_butterfly_tb.vcd: butterfly_tb.v butterfly.v | build 10 | iverilog -o $(BUILD_DIR)/top_butterfly_tb $^ 11 | vvp $(BUILD_DIR)/top_butterfly_tb 12 | 13 | 14 | fft4_tb: $(BUILD_DIR)/top_fft4_tb.vcd 15 | gtkwave $< 16 | 17 | $(BUILD_DIR)/top_fft4_tb.vcd: fft4_tb.v fft4.v butterfly.v | build 18 | iverilog -o $(BUILD_DIR)/top_fft4_tb $^ 19 | vvp $(BUILD_DIR)/top_fft4_tb 20 | 21 | 22 | ifft4_tb: $(BUILD_DIR)/top_ifft4_tb.vcd 23 | gtkwave $< 24 | 25 | $(BUILD_DIR)/top_ifft4_tb.vcd: ifft4_tb.v ifft4.v butterfly.v | build 26 | iverilog -o $(BUILD_DIR)/top_ifft4_tb $^ 27 | vvp $(BUILD_DIR)/top_ifft4_tb 28 | 29 | 30 | build: 31 | mkdir -p $(BUILD_DIR) 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fft_verilog 2 | 使用verilog实现FFT 3 | 4 | 代码**参考学习**自菜鸟教程的文章 5 | 6 | 原地址:https://www.runoob.com/w3cnote/verilog-fft.html 7 | 8 | --- 9 | 10 | `script/project` 使用C实现了生成旋转因子的代码。 11 | 12 | 目前实现了可通用的Butterfly和四点的FFT/IFFT,代码也配有非常完整的注释,方便理解。 13 | 14 | 非常适合入门verilog练手。😝 15 | 16 | 。 17 | 18 | 本工程使用iverlog和modelsim通过了仿真, 19 | 20 | --- 21 | 22 | 蝶形运算模块将运算分成了三级,过程如下 23 | 24 | ![Butterfly](./image/Butterfly.jpg) 25 | 26 | … 27 | 28 | --- 29 | 30 | ​ **推荐**:如果你只是想检查Verilog文件的语法是否有错误,然后进行一些基本的时序仿真,iverilog 是一个不错的选择。相比于各大FPGA厂商的IDE几个G的大小,iverilog 极其小巧,并且支持全平台:Windows + Linux + MacOS 。 31 | 32 | iverilog软件的下载在这里👉:https://bleyer.org/icarus/ > Download 33 | 34 | 下面这篇帖子介绍了如何使用Icarus Verilog来进行verilog文件的编译和仿真。非常简单轻便😁。 35 | 36 | 地址:https://zhuanlan.zhihu.com/p/95081329 37 | 38 | --- 39 | 40 | 至于如何使用iverilog+gtkwave仿真本工程,可参照下面的过程. 41 | 42 | 第一步:这个命令会将Verilog文件butterfly.v、ifft4.v和ifft4_tb.v编译,并生成一个名为test_ifft4的可执行文件。 43 | 44 | ```bash 45 | iverilog -o test_ifft4 ..\butterfly.v ..\ifft4.v ..\ifft4_tb.v 46 | ``` 47 | 48 | 第二步:这个命令会运行ifft4模块的testbench。 49 | 50 | ```bash 51 | vvp test_ifft4 52 | ``` 53 | 54 | 第三步:这个命令会打开波形查看器,以可视化保存在VCD文件wave_ifft4_tb.vcd中的仿真结果。 55 | 56 | ```bash 57 | gtkwave top_ifft4_tb.vcd 58 | ``` 59 | 60 | 61 | 62 | 使用Makefile可以简化命令行输入 63 | 64 | ```makefile 65 | # Encoding: UTF-8 66 | 67 | BUILD_DIR = build 68 | 69 | butterfly_tb: butterfly_tb.v butterfly.v | build 70 | iverilog -o $(BUILD_DIR)/top_butterfly_tb $^ 71 | vvp $(BUILD_DIR)/top_butterfly_tb 72 | 73 | fft4_tb: fft4_tb.v fft4.v butterfly.v | build 74 | iverilog -o $(BUILD_DIR)/top_fft4_tb $^ 75 | vvp $(BUILD_DIR)/top_fft4_tb 76 | 77 | ifft4_tb: ifft4_tb.v ifft4.v butterfly.v | build 78 | iverilog -o $(BUILD_DIR)/top_ifft4_tb $^ 79 | vvp $(BUILD_DIR)/top_ifft4_tb 80 | 81 | view: butterfly_tb fft4_tb ifft4_tb 82 | gtkwave top_butterfly_tb.vcd 83 | gtkwave top_fft4_tb.vcd 84 | gtkwave top_ifft4_tb.vcd 85 | 86 | build: 87 | mkdir $(BUILD_DIR) 88 | 89 | ``` 90 | 91 | 编译运行 92 | 93 | ```bash 94 | make view 95 | ``` 96 | 97 | 将会依次打开`butterfly_tb` `fft4_tb` `ifft4_tb` 98 | 99 | 100 | 101 | **top_butterfly_tb** 102 | 103 | ![top_butterfly_tb](image/top_butterfly_tb.png) 104 | 105 | **top_ifft4_tb** 106 | 107 | ![top_ifft4_tb](image/top_ifft4_tb.png) 108 | -------------------------------------------------------------------------------- /butterfly.v: -------------------------------------------------------------------------------- 1 | 2 | module Butterfly #( 3 | parameter DATA_WIDTH = 4,// 默认位宽为 4 | parameter EXPAND = 9 // 默认将旋转因子扩大1<<9=512倍 5 | )( 6 | input wire clk, 7 | input wire rst_n, 8 | input wire en, 9 | 10 | // Input 1 and Input 2 11 | input wire signed [DATA_WIDTH-1:0] in1_real, 12 | input wire signed [DATA_WIDTH-1:0] in1_imag, 13 | input wire signed [DATA_WIDTH-1:0] in2_real, 14 | input wire signed [DATA_WIDTH-1:0] in2_imag, 15 | // rotation factor 16 | // 多出的1位用于存放符号位 17 | input wire signed [EXPAND+1:0] ro_real, 18 | input wire signed [EXPAND+1:0] ro_imag, 19 | // Output 1 and Output 2 20 | // 输出要比输入多富余出一个位宽 21 | output wire signed [DATA_WIDTH:0] out1_real, 22 | output wire signed [DATA_WIDTH:0] out1_imag, 23 | output wire signed [DATA_WIDTH:0] out2_real, 24 | output wire signed [DATA_WIDTH:0] out2_imag, 25 | // 26 | output wire valid 27 | 28 | ); 29 | //因为有3级流水线,故为每一级流水线计算富余出一个位宽,确保精度 30 | localparam PRECISION = 3; 31 | 32 | // localparam EXPAND = 13; 33 | 34 | // initial begin 35 | // valid <= 'b0; 36 | // end 37 | 38 | 39 | reg [4:0] en_r; 40 | 41 | // 每次将en的值保存到en_r寄存器的最低位, 42 | // 保存en的值用于判断当流水线计算完毕输出时的数据是否是在en置位时的输入的, 43 | // 这样是为了判断输出是否合法. 44 | always @(posedge clk or negedge rst_n) begin 45 | if (!rst_n) begin 46 | en_r <= 'b0; 47 | end else begin 48 | en_r <= {en_r[3:0], en};// 保存en的值到en_r寄存器的最低位 49 | end 50 | end 51 | 52 | /* 53 | * 整个过程分为三级计算. 54 | * 55 | * 第一步当然是要先计算x2 和 ro 的乘积的系数. 56 | * 假设x2 = x2_r + i * x2_i;ro = ro_r + i * ro_i; 57 | * 假设这两个虚数计算的结果是 rod = rod_r + i * rod_i; 58 | * 计算过程如下: 59 | * rod = x2 * ro; 60 | * 即 rod = (x2_r + i * x2_i) * (ro_r + i * ro_i); 61 | * 整理后 rod = (x2_r * ro_r - x2_i * ro_i) + i * (x2_r * ro_i + x2_i * ro_r); 62 | * 63 | * 第一级就是 在给定x2和ro(旋转因子)后,通过上式计算四个乘积的结果; 64 | * 65 | * 第二级就是 将这四个乘积结果进行加减组合后得出rod = rod_r + i * rod_i的过程.具体过程如下: 66 | * rod_r = x2_r * ro_r - x2_i * ro_i; 67 | * rod_i = x2_r * ro_i + x2_i * ro_r; 68 | * 69 | * 第三级则是 将x1和rod进行加减计算的过程.假设结果是y1和y2,具体过程如下: 70 | * y1 = x1 + rod = (x1_r + rod_r) + i * (x1_i + rod_i) 71 | * y2 = x1 - rod = (x1_r - rod_r) + i * (x1_i - rod_i) 72 | */ 73 | 74 | //====================================// 75 | // 第一级 只计算in2和ro(旋转因子)的乘积系数;不计算in1,只将其保存下来。 76 | 77 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] rod_real_0; 78 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] rod_real_1; 79 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] rod_imag_0; 80 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] rod_imag_1; 81 | 82 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] in1_real_d; 83 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] in1_imag_d; 84 | 85 | always @(posedge clk or negedge rst_n) begin 86 | if (!rst_n) begin 87 | rod_real_0 <= 'b0; 88 | rod_real_1 <= 'b0; 89 | rod_imag_0 <= 'b0; 90 | rod_imag_1 <= 'b0; 91 | in1_real_d <= 'b0; 92 | in1_imag_d <= 'b0; 93 | end else if(en) begin 94 | // 这里计算in2 * ro(旋转因子) 的计算结果 95 | rod_real_0 <= in2_real * ro_real; 96 | rod_real_1 <= in2_imag * ro_imag; 97 | rod_imag_0 <= in2_real * ro_imag; 98 | rod_imag_1 <= in2_imag * ro_real; 99 | // 保存in1的值,并对其进行移位扩展 100 | in1_real_d <= in1_real <<< EXPAND; 101 | in1_imag_d <= in1_imag <<< EXPAND; 102 | end 103 | end 104 | 105 | //====================================// 106 | // 第二级 组合in2和ro(旋转因子)的乘积系数, 仍然不计算in1 107 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] rod_real; 108 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] rod_imag; 109 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] in1_real_d1; 110 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] in1_imag_d1; 111 | 112 | always @(posedge clk or negedge rst_n) begin 113 | if (!rst_n) begin 114 | in1_real_d1 <= 'b0; 115 | in1_imag_d1 <= 'b0; 116 | rod_real <= 'b0 ; 117 | rod_imag <= 'b0 ; 118 | end 119 | else if (en_r[0]) begin 120 | in1_real_d1 <= in1_real_d; // 再次保存in1的值 121 | in1_imag_d1 <= in1_imag_d; 122 | // (rod_real + i * rod_imag)就是in2和ro计算的值 123 | // 提前设置好位宽余量,防止数据溢出 124 | rod_real <= rod_real_0 - rod_real_1; 125 | rod_imag <= rod_imag_0 + rod_imag_1; 126 | end 127 | end 128 | 129 | //====================================// 130 | // 第三级 计算in1和(in2和ro))的和and差 131 | 132 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] out1_real_r; 133 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] out1_imag_r; 134 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] out2_real_r; 135 | reg signed [PRECISION+DATA_WIDTH+EXPAND-1:0] out2_imag_r; 136 | 137 | always @(posedge clk or negedge rst_n) begin 138 | if (!rst_n) begin 139 | out1_real_r <= 'b0; 140 | out1_imag_r <= 'b0; 141 | out2_real_r <= 'b0; 142 | out2_imag_r <= 'b0; 143 | end 144 | else if (en_r[1]) begin 145 | out1_real_r <= in1_real_d1 + rod_real; 146 | out1_imag_r <= in1_imag_d1 + rod_imag; 147 | out2_real_r <= in1_real_d1 - rod_real; 148 | out2_imag_r <= in1_imag_d1 - rod_imag; 149 | 150 | // valid <= en_r[2]; 151 | end 152 | end 153 | 154 | 155 | // 在计算出结果时丢弃之前扩展的ENPAND位 156 | // 将第三级计算出的结果取其可用之处,连接线出去到各个输出寄存器 157 | // 截取最高位 和 从扩展位开始加上原来带宽的位宽 组成新的数据 158 | assign out1_real = {out1_real_r[PRECISION+DATA_WIDTH+EXPAND-1], out1_real_r[DATA_WIDTH+EXPAND-1:EXPAND]}; 159 | assign out1_imag = {out1_imag_r[PRECISION+DATA_WIDTH+EXPAND-1], out1_imag_r[DATA_WIDTH+EXPAND-1:EXPAND]}; 160 | assign out2_real = {out2_real_r[PRECISION+DATA_WIDTH+EXPAND-1], out2_real_r[DATA_WIDTH+EXPAND-1:EXPAND]}; 161 | assign out2_imag = {out2_imag_r[PRECISION+DATA_WIDTH+EXPAND-1], out2_imag_r[DATA_WIDTH+EXPAND-1:EXPAND]}; 162 | 163 | assign valid = en_r[2];// 用来表示当前输出的结果是否是合法的. 164 | 165 | endmodule 166 | -------------------------------------------------------------------------------- /butterfly_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1ps/1ps 2 | 3 | module butterfly_tb; 4 | 5 | localparam DATA_WIDTH = 8; 6 | localparam EXPAND = 6; // 默认将旋转因子扩大1<<6=64倍 7 | 8 | 9 | // 定义输入信号 10 | reg signed [DATA_WIDTH-1:0] in1_real = {DATA_WIDTH{1'b0}}; 11 | reg signed [DATA_WIDTH-1:0] in1_imag = {DATA_WIDTH{1'b0}}; 12 | reg signed [DATA_WIDTH-1:0] in2_real = {DATA_WIDTH{1'b0}}; 13 | reg signed [DATA_WIDTH-1:0] in2_imag = {DATA_WIDTH{1'b0}}; 14 | // 旋转因子 15 | reg signed [EXPAND+1:0] ro_real = 'd64; 16 | reg signed [EXPAND+1:0] ro_imag = 'd0; 17 | 18 | // 定义输出信号 19 | wire signed [DATA_WIDTH:0] out1_real; 20 | wire signed [DATA_WIDTH:0] out1_imag; 21 | wire signed [DATA_WIDTH:0] out2_real; 22 | wire signed [DATA_WIDTH:0] out2_imag; 23 | // 有效性信号 24 | wire valid; 25 | 26 | // 定义模块的实例 27 | Butterfly #( 28 | .DATA_WIDTH(DATA_WIDTH), 29 | .EXPAND(EXPAND) 30 | ) 31 | dut ( 32 | .clk(clk), 33 | .rst_n(1'b1), 34 | .en(en), 35 | .in1_real(in1_real), 36 | .in1_imag(in1_imag), 37 | .in2_real(in2_real), 38 | .in2_imag(in2_imag), 39 | .ro_real(ro_real), 40 | .ro_imag(ro_imag), 41 | .out1_real(out1_real), 42 | .out1_imag(out1_imag), 43 | .out2_real(out2_real), 44 | .out2_imag(out2_imag), 45 | .valid(valid) 46 | ); 47 | 48 | // 时钟信号 49 | reg clk = 0; 50 | always #5 clk = ~clk; 51 | 52 | // 设定结束时间点 53 | initial begin 54 | #500; 55 | $finish; 56 | end 57 | 58 | reg en = 1; 59 | always #50 en = ~en; 60 | 61 | // 设置数据 62 | initial begin 63 | 64 | forever begin 65 | in1_real = $random; 66 | in1_imag = $random; 67 | in2_real = $random; 68 | in2_imag = $random; 69 | #10; 70 | end 71 | 72 | end 73 | 74 | initial begin 75 | $dumpfile("build/top_butterfly_tb.vcd"); // 指定用作dumpfile的文件 76 | $dumpvars; // dump all vars 77 | end 78 | 79 | endmodule -------------------------------------------------------------------------------- /fft4.v: -------------------------------------------------------------------------------- 1 | module fft4 #( 2 | parameter DATA_WIDTH = 8 3 | ) 4 | ( 5 | input wire clk, 6 | input wire rst_n, 7 | input wire en, 8 | 9 | // 4点的FFT,四个输入实部和虚部 10 | input wire signed [DATA_WIDTH-1:0] in0_real, 11 | input wire signed [DATA_WIDTH-1:0] in0_imag, 12 | input wire signed [DATA_WIDTH-1:0] in1_real, 13 | input wire signed [DATA_WIDTH-1:0] in1_imag, 14 | input wire signed [DATA_WIDTH-1:0] in2_real, 15 | input wire signed [DATA_WIDTH-1:0] in2_imag, 16 | input wire signed [DATA_WIDTH-1:0] in3_real, 17 | input wire signed [DATA_WIDTH-1:0] in3_imag, 18 | 19 | // 4点的FFT需要进行两层Butterfly, 因此为其多富余两个位宽 20 | output wire signed [DATA_WIDTH+1:0] out0_real, 21 | output wire signed [DATA_WIDTH+1:0] out0_imag, 22 | output wire signed [DATA_WIDTH+1:0] out1_real, 23 | output wire signed [DATA_WIDTH+1:0] out1_imag, 24 | output wire signed [DATA_WIDTH+1:0] out2_real, 25 | output wire signed [DATA_WIDTH+1:0] out2_imag, 26 | output wire signed [DATA_WIDTH+1:0] out3_real, 27 | output wire signed [DATA_WIDTH+1:0] out3_imag, 28 | 29 | output wire valid 30 | ); 31 | // 1 << 9 = 512 故下面的旋转因子参数也被扩大了512倍 32 | localparam EXPAND = 9; 33 | // 四点fft 34 | localparam POINTS = 4; 35 | 36 | // 旋转因子 37 | reg signed [EXPAND+1:0] RO_ARRAY[POINTS-1:0][1:0]; 38 | // 初始化旋转因子 39 | initial begin 40 | RO_ARRAY[0][0] <= 512; 41 | RO_ARRAY[0][1] <= 0; 42 | RO_ARRAY[1][0] <= 0; 43 | RO_ARRAY[1][1] <= -512; 44 | RO_ARRAY[2][0] <= -512; 45 | RO_ARRAY[2][1] <= 0; 46 | RO_ARRAY[3][0] <= 0; 47 | RO_ARRAY[3][1] <= 512; 48 | end 49 | 50 | 51 | // 四点的fft共需要三层连线:码位倒置一层,第一次蝶形运算一层,第二次蝶形运算一层 52 | // [DATA_WIDTH+EXPAND-1:0]表示每个数据的位宽 53 | // [2:0]表示 共需要三层 54 | // [3:0]表示每个层有四个数据,即四点的数据 55 | // wire signed [DATA_WIDTH+EXPAND-1:0] in_real[2:0][3:0]; 56 | // wire signed [DATA_WIDTH+EXPAND-1:0] in_imag[2:0][3:0]; 57 | 58 | // 用于原始数据层和第一计算层 59 | wire signed [DATA_WIDTH-1:0] in_real[3:0]; 60 | wire signed [DATA_WIDTH-1:0] in_imag[3:0]; 61 | // 用于第一计算层和第二计算层 62 | wire signed [DATA_WIDTH+0:0] in_real_step1[3:0]; 63 | wire signed [DATA_WIDTH+0:0] in_imag_step1[3:0]; 64 | // 用于第二计算层和输出层 65 | wire signed [DATA_WIDTH+1:0] in_real_step2[3:0]; 66 | wire signed [DATA_WIDTH+1:0] in_imag_step2[3:0]; 67 | // 用于连接各个模块的en引脚 68 | 69 | wire en_connect [3:0][1:0]; 70 | // 连接引脚第一层蝶形运算模块 71 | assign en_connect[0][0] = en; 72 | assign en_connect[1][0] = en; 73 | // 第一步: 码位倒置 74 | assign in_real[0] = in0_real; 75 | assign in_imag[0] = in0_imag; 76 | assign in_real[1] = in2_real; 77 | assign in_imag[1] = in2_imag; 78 | assign in_real[2] = in1_real; 79 | assign in_imag[2] = in1_imag; 80 | assign in_real[3] = in3_real; 81 | assign in_imag[3] = in3_imag; 82 | 83 | // 第二步: 连接倒置后的数据和第一层蝶形运算 84 | Butterfly #( 85 | .DATA_WIDTH(DATA_WIDTH), .EXPAND(EXPAND) 86 | ) butterfly_unit_0_0 ( 87 | // 控制信号 88 | .clk(clk), 89 | .rst_n(rst_n), 90 | .en(en_connect[0][0]), 91 | // 输入 92 | .in1_real(in_real[0]), 93 | .in1_imag(in_imag[0]), 94 | .in2_real(in_real[1]), 95 | .in2_imag(in_imag[1]), 96 | // 旋转因子 97 | .ro_real(RO_ARRAY[0][0]), 98 | .ro_imag(RO_ARRAY[0][1]), 99 | // 输出 100 | .out1_real(in_real_step1[0]), 101 | .out1_imag(in_imag_step1[0]), 102 | .out2_real(in_real_step1[1]), 103 | .out2_imag(in_imag_step1[1]), 104 | // 输出是否有效信号 105 | // 有效代表该数据可用,否则则不可用 106 | .valid(en_connect[0][1]) 107 | ); 108 | 109 | Butterfly #( 110 | .DATA_WIDTH(DATA_WIDTH), .EXPAND(EXPAND) 111 | ) butterfly_unit_0_1 ( 112 | // 控制信号 113 | .clk(clk), 114 | .rst_n(rst_n), 115 | .en(en_connect[1][0]), 116 | // 输入 117 | .in1_real(in_real[2]), 118 | .in1_imag(in_imag[2]), 119 | .in2_real(in_real[3]), 120 | .in2_imag(in_imag[3]), 121 | // 旋转因子 122 | .ro_real(RO_ARRAY[0][0]), 123 | .ro_imag(RO_ARRAY[0][1]), 124 | // 输出 125 | .out1_real(in_real_step1[2]), 126 | .out1_imag(in_imag_step1[2]), 127 | .out2_real(in_real_step1[3]), 128 | .out2_imag(in_imag_step1[3]), 129 | // 输出是否有效信号 130 | // 有效代表该数据可用,否则则不可用 131 | .valid(en_connect[1][1]) 132 | ); 133 | 134 | // 连接第一层蝶形运算模块valid和第二层蝶形运算模块en 135 | assign en_connect[2][0] = en_connect[0][1]; 136 | assign en_connect[3][0] = en_connect[1][1]; 137 | 138 | // 第二层蝶形运算 139 | Butterfly #( 140 | .DATA_WIDTH(DATA_WIDTH+1), .EXPAND(EXPAND) 141 | ) butterfly_unit_1_0 ( 142 | // 控制信号 143 | .clk(clk), 144 | .rst_n(rst_n), 145 | .en(en_connect[2][0]), 146 | // 输入 147 | .in1_real(in_real_step1[0]), 148 | .in1_imag(in_imag_step1[0]), 149 | .in2_real(in_real_step1[2]), 150 | .in2_imag(in_imag_step1[2]), 151 | // 旋转因子 152 | .ro_real(RO_ARRAY[0][0]), 153 | .ro_imag(RO_ARRAY[0][1]), 154 | // 输出 155 | .out1_real(in_real_step2[0]), 156 | .out1_imag(in_imag_step2[0]), 157 | .out2_real(in_real_step2[2]), 158 | .out2_imag(in_imag_step2[2]), 159 | // 输出是否有效信号 160 | // 有效代表该数据可用,否则则不可用 161 | .valid(en_connect[2][1]) 162 | ); 163 | 164 | Butterfly #( 165 | .DATA_WIDTH(DATA_WIDTH+1), .EXPAND(EXPAND) 166 | ) butterfly_unit_1_1 ( 167 | // 控制信号 168 | .clk(clk), 169 | .rst_n(rst_n), 170 | .en(en_connect[3][0]), 171 | // 输入 172 | .in1_real(in_real_step1[1]), 173 | .in1_imag(in_imag_step1[1]), 174 | .in2_real(in_real_step1[3]), 175 | .in2_imag(in_imag_step1[3]), 176 | // 旋转因子 177 | .ro_real(RO_ARRAY[1][0]), 178 | .ro_imag(RO_ARRAY[1][1]), 179 | // 输出 180 | .out1_real(in_real_step2[1]), 181 | .out1_imag(in_imag_step2[1]), 182 | .out2_real(in_real_step2[3]), 183 | .out2_imag(in_imag_step2[3]), 184 | // 输出是否有效信号 185 | // 有效代表该数据可用,否则则不可用 186 | .valid(en_connect[3][1]) 187 | ); 188 | 189 | assign valid = en_connect[3][1]; 190 | 191 | assign out0_real = in_real_step2[0]; 192 | assign out0_imag = in_imag_step2[0]; 193 | assign out1_real = in_real_step2[1]; 194 | assign out1_imag = in_imag_step2[1]; 195 | assign out2_real = in_real_step2[2]; 196 | assign out2_imag = in_imag_step2[2]; 197 | assign out3_real = in_real_step2[3]; 198 | assign out3_imag = in_imag_step2[3]; 199 | 200 | endmodule 201 | 202 | 203 | 204 | // genvar m, k; 205 | // // 对于每一个fft,每层蝶形运算单元的个数都是相等的 206 | // // 对于一个四点的fft, 每一层有两个有两个蝶形运算单元 207 | // generate 208 | // // 构造两层 209 | // for(m = 0; m < 2; m = m + 1) begin: stage 210 | // // 每层两个 211 | // for (k = 0; k < 2; k = k + 1) begin: unit 212 | // 213 | // end 214 | // end 215 | // endgenerate 216 | -------------------------------------------------------------------------------- /fft4_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1ps / 1ps 2 | 3 | module fft4_tb; 4 | 5 | // 数据宽度 6 | parameter DATA_WIDTH = 8; 7 | 8 | // 输入数据 9 | reg signed [DATA_WIDTH-1:0] in0_real; 10 | reg signed [DATA_WIDTH-1:0] in0_imag; 11 | reg signed [DATA_WIDTH-1:0] in1_real; 12 | reg signed [DATA_WIDTH-1:0] in1_imag; 13 | reg signed [DATA_WIDTH-1:0] in2_real; 14 | reg signed [DATA_WIDTH-1:0] in2_imag; 15 | reg signed [DATA_WIDTH-1:0] in3_real; 16 | reg signed [DATA_WIDTH-1:0] in3_imag; 17 | 18 | // 输出数据 19 | wire signed [DATA_WIDTH+1:0] out0_real; 20 | wire signed [DATA_WIDTH+1:0] out0_imag; 21 | wire signed [DATA_WIDTH+1:0] out1_real; 22 | wire signed [DATA_WIDTH+1:0] out1_imag; 23 | wire signed [DATA_WIDTH+1:0] out2_real; 24 | wire signed [DATA_WIDTH+1:0] out2_imag; 25 | wire signed [DATA_WIDTH+1:0] out3_real; 26 | wire signed [DATA_WIDTH+1:0] out3_imag; 27 | 28 | // 数据有效信号 29 | wire valid; 30 | 31 | // 仿真时钟 32 | reg clk = 0; 33 | always #5 clk = !clk; 34 | 35 | // fft4模块实例化 36 | fft4 #( 37 | .DATA_WIDTH(DATA_WIDTH) 38 | ) fft4_inst ( 39 | .clk(clk), 40 | .rst_n(1'b1), 41 | .en(1'b1), 42 | .in0_real(in0_real), 43 | .in0_imag(in0_imag), 44 | .in1_real(in1_real), 45 | .in1_imag(in1_imag), 46 | .in2_real(in2_real), 47 | .in2_imag(in2_imag), 48 | .in3_real(in3_real), 49 | .in3_imag(in3_imag), 50 | .out0_real(out0_real), 51 | .out0_imag(out0_imag), 52 | .out1_real(out1_real), 53 | .out1_imag(out1_imag), 54 | .out2_real(out2_real), 55 | .out2_imag(out2_imag), 56 | .out3_real(out3_real), 57 | .out3_imag(out3_imag), 58 | .valid(valid) 59 | ); 60 | 61 | // 模拟输入数据 62 | initial begin 63 | in0_real = 1; 64 | in0_imag = 0; 65 | in1_real = 2; 66 | in1_imag = 0; 67 | in2_real = -1; 68 | in2_imag = 0; 69 | in3_real = 3; 70 | in3_imag = 0; 71 | #100; 72 | $finish; 73 | end 74 | 75 | initial begin 76 | $dumpfile("build/top_fft4_tb.vcd"); // 指定用作dumpfile的文件 77 | $dumpvars; // dump all vars 78 | end 79 | 80 | endmodule 81 | -------------------------------------------------------------------------------- /ifft4.v: -------------------------------------------------------------------------------- 1 | module ifft4 #( 2 | parameter DATA_WIDTH = 8 3 | ) 4 | ( 5 | input wire clk, 6 | input wire rst_n, 7 | input wire en, 8 | 9 | // 4点的FFT,四个输入实部和虚部 10 | input wire signed [DATA_WIDTH-1:0] in0_real, 11 | input wire signed [DATA_WIDTH-1:0] in0_imag, 12 | input wire signed [DATA_WIDTH-1:0] in1_real, 13 | input wire signed [DATA_WIDTH-1:0] in1_imag, 14 | input wire signed [DATA_WIDTH-1:0] in2_real, 15 | input wire signed [DATA_WIDTH-1:0] in2_imag, 16 | input wire signed [DATA_WIDTH-1:0] in3_real, 17 | input wire signed [DATA_WIDTH-1:0] in3_imag, 18 | 19 | // 4点的FFT需要进行两层Butterfly, 因此为其多富余两个位宽 20 | output wire signed [DATA_WIDTH+1:0] out0_real, 21 | output wire signed [DATA_WIDTH+1:0] out0_imag, 22 | output wire signed [DATA_WIDTH+1:0] out1_real, 23 | output wire signed [DATA_WIDTH+1:0] out1_imag, 24 | output wire signed [DATA_WIDTH+1:0] out2_real, 25 | output wire signed [DATA_WIDTH+1:0] out2_imag, 26 | output wire signed [DATA_WIDTH+1:0] out3_real, 27 | output wire signed [DATA_WIDTH+1:0] out3_imag, 28 | 29 | output wire valid 30 | ); 31 | // 1 << 9 = 512 故下面的旋转因子参数也被扩大了512倍 32 | localparam EXPAND = 9; 33 | // 四点fft 34 | localparam POINTS = 4; 35 | 36 | // 旋转因子 37 | reg signed [EXPAND+1:0] RO_ARRAY[POINTS-1:0][1:0]; 38 | // 初始化旋转因子 39 | initial begin 40 | RO_ARRAY[0][0] <= 512; 41 | RO_ARRAY[0][1] <= 0; 42 | RO_ARRAY[1][0] <= 0; 43 | RO_ARRAY[1][1] <= 512; 44 | RO_ARRAY[2][0] <= -512; 45 | RO_ARRAY[2][1] <= 0; 46 | RO_ARRAY[3][0] <= 0; 47 | RO_ARRAY[3][1] <= -512; 48 | end 49 | 50 | 51 | 52 | // 四点的fft共需要三层连线:码位倒置一层,第一次蝶形运算一层,第二次蝶形运算一层 53 | // [DATA_WIDTH+EXPAND-1:0]表示每个数据的位宽 54 | // [2:0]表示 共需要三层 55 | // [3:0]表示每个层有四个数据,即四点的数据 56 | // wire signed [DATA_WIDTH+EXPAND-1:0] in_real[2:0][3:0]; 57 | // wire signed [DATA_WIDTH+EXPAND-1:0] in_imag[2:0][3:0]; 58 | 59 | // 用于原始数据层和第一计算层 60 | wire signed [DATA_WIDTH-1:0] in_real[3:0]; 61 | wire signed [DATA_WIDTH-1:0] in_imag[3:0]; 62 | // 用于第一计算层和第二计算层 63 | wire signed [DATA_WIDTH+0:0] in_real_step1[3:0]; 64 | wire signed [DATA_WIDTH+0:0] in_imag_step1[3:0]; 65 | // 用于第二计算层和输出层 66 | wire signed [DATA_WIDTH+1:0] in_real_step2[3:0]; 67 | wire signed [DATA_WIDTH+1:0] in_imag_step2[3:0]; 68 | // 用于连接各个模块的en引脚 69 | 70 | wire en_connect [3:0][1:0]; 71 | // 连接引脚第一层蝶形运算模块 72 | assign en_connect[0][0] = en; 73 | assign en_connect[1][0] = en; 74 | // 第一步: 码位倒置 75 | assign in_real[0] = in0_real; 76 | assign in_imag[0] = in0_imag; 77 | assign in_real[1] = in2_real; 78 | assign in_imag[1] = in2_imag; 79 | assign in_real[2] = in1_real; 80 | assign in_imag[2] = in1_imag; 81 | assign in_real[3] = in3_real; 82 | assign in_imag[3] = in3_imag; 83 | 84 | // 第二步: 连接倒置后的数据和第一层蝶形运算 85 | Butterfly #( 86 | .DATA_WIDTH(DATA_WIDTH), .EXPAND(EXPAND) 87 | ) butterfly_unit_0_0 ( 88 | // 控制信号 89 | .clk(clk), 90 | .rst_n(rst_n), 91 | .en(en_connect[0][0]), 92 | // 输入 93 | .in1_real(in_real[0]), 94 | .in1_imag(in_imag[0]), 95 | .in2_real(in_real[1]), 96 | .in2_imag(in_imag[1]), 97 | // 旋转因子 98 | .ro_real(RO_ARRAY[0][0]), 99 | .ro_imag(RO_ARRAY[0][1]), 100 | // 输出 101 | .out1_real(in_real_step1[0]), 102 | .out1_imag(in_imag_step1[0]), 103 | .out2_real(in_real_step1[1]), 104 | .out2_imag(in_imag_step1[1]), 105 | // 输出是否有效信号 106 | // 有效代表该数据可用,否则则不可用 107 | .valid(en_connect[0][1]) 108 | ); 109 | 110 | Butterfly #( 111 | .DATA_WIDTH(DATA_WIDTH), .EXPAND(EXPAND) 112 | ) butterfly_unit_0_1 ( 113 | // 控制信号 114 | .clk(clk), 115 | .rst_n(rst_n), 116 | .en(en_connect[1][0]), 117 | // 输入 118 | .in1_real(in_real[2]), 119 | .in1_imag(in_imag[2]), 120 | .in2_real(in_real[3]), 121 | .in2_imag(in_imag[3]), 122 | // 旋转因子 123 | .ro_real(RO_ARRAY[0][0]), 124 | .ro_imag(RO_ARRAY[0][1]), 125 | // 输出 126 | .out1_real(in_real_step1[2]), 127 | .out1_imag(in_imag_step1[2]), 128 | .out2_real(in_real_step1[3]), 129 | .out2_imag(in_imag_step1[3]), 130 | // 输出是否有效信号 131 | // 有效代表该数据可用,否则则不可用 132 | .valid(en_connect[1][1]) 133 | ); 134 | 135 | // 连接第一层蝶形运算模块valid和第二层蝶形运算模块en 136 | assign en_connect[2][0] = en_connect[0][1]; 137 | assign en_connect[3][0] = en_connect[1][1]; 138 | 139 | // 第二层蝶形运算 140 | Butterfly #( 141 | .DATA_WIDTH(DATA_WIDTH+1), .EXPAND(EXPAND) 142 | ) butterfly_unit_1_0 ( 143 | // 控制信号 144 | .clk(clk), 145 | .rst_n(rst_n), 146 | .en(en_connect[2][0]), 147 | // 输入 148 | .in1_real(in_real_step1[0]), 149 | .in1_imag(in_imag_step1[0]), 150 | .in2_real(in_real_step1[2]), 151 | .in2_imag(in_imag_step1[2]), 152 | // 旋转因子 153 | .ro_real(RO_ARRAY[0][0]), 154 | .ro_imag(RO_ARRAY[0][1]), 155 | // 输出 156 | .out1_real(in_real_step2[0]), 157 | .out1_imag(in_imag_step2[0]), 158 | .out2_real(in_real_step2[2]), 159 | .out2_imag(in_imag_step2[2]), 160 | // 输出是否有效信号 161 | // 有效代表该数据可用,否则则不可用 162 | .valid(en_connect[2][1]) 163 | ); 164 | 165 | Butterfly #( 166 | .DATA_WIDTH(DATA_WIDTH+1), .EXPAND(EXPAND) 167 | ) butterfly_unit_1_1 ( 168 | // 控制信号 169 | .clk(clk), 170 | .rst_n(rst_n), 171 | .en(en_connect[3][0]), 172 | // 输入 173 | .in1_real(in_real_step1[1]), 174 | .in1_imag(in_imag_step1[1]), 175 | .in2_real(in_real_step1[3]), 176 | .in2_imag(in_imag_step1[3]), 177 | // 旋转因子 178 | .ro_real(RO_ARRAY[1][0]), 179 | .ro_imag(RO_ARRAY[1][1]), 180 | // 输出 181 | .out1_real(in_real_step2[1]), 182 | .out1_imag(in_imag_step2[1]), 183 | .out2_real(in_real_step2[3]), 184 | .out2_imag(in_imag_step2[3]), 185 | // 输出是否有效信号 186 | // 有效代表该数据可用,否则则不可用 187 | .valid(en_connect[3][1]) 188 | ); 189 | 190 | assign valid = en_connect[3][1]; 191 | 192 | assign out0_real = in_real_step2[0] >>> 2; 193 | assign out0_imag = in_imag_step2[0] >>> 2; 194 | assign out1_real = in_real_step2[1] >>> 2; 195 | assign out1_imag = in_imag_step2[1] >>> 2; 196 | assign out2_real = in_real_step2[2] >>> 2; 197 | assign out2_imag = in_imag_step2[2] >>> 2; 198 | assign out3_real = in_real_step2[3] >>> 2; 199 | assign out3_imag = in_imag_step2[3] >>> 2; 200 | 201 | endmodule 202 | -------------------------------------------------------------------------------- /ifft4_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1ps / 1ps 2 | 3 | module ifft4_tb; 4 | 5 | // 数据宽度 6 | parameter DATA_WIDTH = 8; 7 | 8 | // 输入数据 9 | reg signed [DATA_WIDTH-1:0] in0_real; 10 | reg signed [DATA_WIDTH-1:0] in0_imag; 11 | reg signed [DATA_WIDTH-1:0] in1_real; 12 | reg signed [DATA_WIDTH-1:0] in1_imag; 13 | reg signed [DATA_WIDTH-1:0] in2_real; 14 | reg signed [DATA_WIDTH-1:0] in2_imag; 15 | reg signed [DATA_WIDTH-1:0] in3_real; 16 | reg signed [DATA_WIDTH-1:0] in3_imag; 17 | 18 | // 输出数据 19 | wire signed [DATA_WIDTH+1:0] out0_real; 20 | wire signed [DATA_WIDTH+1:0] out0_imag; 21 | wire signed [DATA_WIDTH+1:0] out1_real; 22 | wire signed [DATA_WIDTH+1:0] out1_imag; 23 | wire signed [DATA_WIDTH+1:0] out2_real; 24 | wire signed [DATA_WIDTH+1:0] out2_imag; 25 | wire signed [DATA_WIDTH+1:0] out3_real; 26 | wire signed [DATA_WIDTH+1:0] out3_imag; 27 | 28 | // 数据有效信号 29 | wire valid; 30 | 31 | // 仿真时钟 32 | reg clk = 0; 33 | always #5 clk = !clk; 34 | 35 | // fft4模块实例化 36 | ifft4 #( 37 | .DATA_WIDTH(DATA_WIDTH) 38 | ) fft4_inst ( 39 | .clk(clk), 40 | .rst_n(1'b1), 41 | .en(1'b1), 42 | .in0_real(in0_real), 43 | .in0_imag(in0_imag), 44 | .in1_real(in1_real), 45 | .in1_imag(in1_imag), 46 | .in2_real(in2_real), 47 | .in2_imag(in2_imag), 48 | .in3_real(in3_real), 49 | .in3_imag(in3_imag), 50 | .out0_real(out0_real), 51 | .out0_imag(out0_imag), 52 | .out1_real(out1_real), 53 | .out1_imag(out1_imag), 54 | .out2_real(out2_real), 55 | .out2_imag(out2_imag), 56 | .out3_real(out3_real), 57 | .out3_imag(out3_imag), 58 | .valid(valid) 59 | ); 60 | 61 | // 模拟输入数据 62 | initial begin 63 | in0_real = 'd1; 64 | in0_imag = 'd0; 65 | in1_real = -2; 66 | in1_imag = 'd3; 67 | in2_real = -1; 68 | in2_imag = 'd0; 69 | in3_real = -2; 70 | in3_imag = -3; 71 | // input 1+j0,-2+j3,-1+j0,-2-j3 72 | // output -1+j0,-1+j0, 1+j0, 2+j0 73 | #100 $finish; 74 | end 75 | 76 | initial begin 77 | $dumpfile("build/top_ifft4_tb.vcd"); // 指定用作dumpfile的文件 78 | $dumpvars; // dump all vars 79 | end 80 | 81 | endmodule 82 | -------------------------------------------------------------------------------- /image/Butterfly.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amo3oR/fft_verilog/2668b8a7b76cd664b0ec3ab782ceaca199405919/image/Butterfly.jpg -------------------------------------------------------------------------------- /image/top_butterfly_tb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amo3oR/fft_verilog/2668b8a7b76cd664b0ec3ab782ceaca199405919/image/top_butterfly_tb.png -------------------------------------------------------------------------------- /image/top_ifft4_tb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amo3oR/fft_verilog/2668b8a7b76cd664b0ec3ab782ceaca199405919/image/top_ifft4_tb.png -------------------------------------------------------------------------------- /script/project/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /script/project/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.23) 2 | project(project C) 3 | 4 | # set c standard 5 | set(CMAKE_C_STANDARD 11) 6 | 7 | add_executable(project main.c) 8 | -------------------------------------------------------------------------------- /script/project/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | #define POINTS 8 7 | #define PI 3.14159265358979323846 8 | 9 | typedef int32_t rotation_factor_t; 10 | 11 | 12 | /** 13 | * @brief generate a array of rotation_factor 14 | * @param array return array 15 | * @param len length of array 16 | * @param factor magnification of factor 17 | */ 18 | void CreateRoArray(rotation_factor_t *array, int len, int factor) 19 | { 20 | double angle = 0; 21 | for (int i = 0; i < len; i+=2) { 22 | angle = PI * i / len; 23 | array[i] = (rotation_factor_t)(cos(angle) * factor); 24 | array[i+1] = (rotation_factor_t)(sin(angle) * factor); 25 | } 26 | } 27 | 28 | int main() { 29 | 30 | static rotation_factor_t ro_array[POINTS] = {0}; 31 | 32 | CreateRoArray(ro_array, POINTS, 512); 33 | 34 | printf("rotation factor:"); 35 | for (int i = 0; i < POINTS; i++) { 36 | printf("%d,",ro_array[i]); 37 | } 38 | 39 | return 0; 40 | } 41 | --------------------------------------------------------------------------------