feat(ip): integrate 1024-point AXI FFT IP
This commit is contained in:
32
rtl/ip/fft/Butterfly.v
Normal file
32
rtl/ip/fft/Butterfly.v
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Butterfly: Add/Sub and Scaling
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
module Butterfly #(
|
||||||
|
parameter WIDTH = 16,
|
||||||
|
parameter RH = 0 // Round Half Up
|
||||||
|
)(
|
||||||
|
input signed [WIDTH-1:0] x0_re, // Input Data #0 (Real)
|
||||||
|
input signed [WIDTH-1:0] x0_im, // Input Data #0 (Imag)
|
||||||
|
input signed [WIDTH-1:0] x1_re, // Input Data #1 (Real)
|
||||||
|
input signed [WIDTH-1:0] x1_im, // Input Data #1 (Imag)
|
||||||
|
output signed [WIDTH-1:0] y0_re, // Output Data #0 (Real)
|
||||||
|
output signed [WIDTH-1:0] y0_im, // Output Data #0 (Imag)
|
||||||
|
output signed [WIDTH-1:0] y1_re, // Output Data #1 (Real)
|
||||||
|
output signed [WIDTH-1:0] y1_im // Output Data #1 (Imag)
|
||||||
|
);
|
||||||
|
|
||||||
|
wire signed [WIDTH:0] add_re, add_im, sub_re, sub_im;
|
||||||
|
|
||||||
|
// Add/Sub
|
||||||
|
assign add_re = x0_re + x1_re;
|
||||||
|
assign add_im = x0_im + x1_im;
|
||||||
|
assign sub_re = x0_re - x1_re;
|
||||||
|
assign sub_im = x0_im - x1_im;
|
||||||
|
|
||||||
|
// Scaling
|
||||||
|
assign y0_re = (add_re + RH) >>> 1;
|
||||||
|
assign y0_im = (add_im + RH) >>> 1;
|
||||||
|
assign y1_re = (sub_re + RH) >>> 1;
|
||||||
|
assign y1_im = (sub_im + RH) >>> 1;
|
||||||
|
|
||||||
|
endmodule
|
||||||
32
rtl/ip/fft/DelayBuffer.v
Normal file
32
rtl/ip/fft/DelayBuffer.v
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//----------------------------------------------------------------------
|
||||||
|
// DelayBuffer: Generate Constant Delay
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
module DelayBuffer #(
|
||||||
|
parameter DEPTH = 32,
|
||||||
|
parameter WIDTH = 16
|
||||||
|
)(
|
||||||
|
input clock, // Master Clock
|
||||||
|
input [WIDTH-1:0] di_re, // Data Input (Real)
|
||||||
|
input [WIDTH-1:0] di_im, // Data Input (Imag)
|
||||||
|
output [WIDTH-1:0] do_re, // Data Output (Real)
|
||||||
|
output [WIDTH-1:0] do_im // Data Output (Imag)
|
||||||
|
);
|
||||||
|
|
||||||
|
reg [WIDTH-1:0] buf_re[0:DEPTH-1];
|
||||||
|
reg [WIDTH-1:0] buf_im[0:DEPTH-1];
|
||||||
|
integer n;
|
||||||
|
|
||||||
|
// Shift Buffer
|
||||||
|
always @(posedge clock) begin
|
||||||
|
for (n = DEPTH-1; n > 0; n = n - 1) begin
|
||||||
|
buf_re[n] <= buf_re[n-1];
|
||||||
|
buf_im[n] <= buf_im[n-1];
|
||||||
|
end
|
||||||
|
buf_re[0] <= di_re;
|
||||||
|
buf_im[0] <= di_im;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign do_re = buf_re[DEPTH-1];
|
||||||
|
assign do_im = buf_im[DEPTH-1];
|
||||||
|
|
||||||
|
endmodule
|
||||||
89
rtl/ip/fft/FFT1024_32B.v
Normal file
89
rtl/ip/fft/FFT1024_32B.v
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FFT: 1024-Point FFT Using Radix-2^2 Single-Path Delay Feedback
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
module FFT #(
|
||||||
|
parameter WIDTH = 32
|
||||||
|
)(
|
||||||
|
input clock, // Master Clock
|
||||||
|
input reset, // Active High Asynchronous Reset
|
||||||
|
input di_en, // Input Data Enable
|
||||||
|
input [WIDTH-1:0] di_re, // Input Data (Real)
|
||||||
|
input [WIDTH-1:0] di_im, // Input Data (Imag)
|
||||||
|
output do_en, // Output Data Enable
|
||||||
|
output [WIDTH-1:0] do_re, // Output Data (Real)
|
||||||
|
output [WIDTH-1:0] do_im // Output Data (Imag)
|
||||||
|
);
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Data must be input consecutively in natural order.
|
||||||
|
// The result is scaled to 1/N and output in bit-reversed order.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
wire su1_do_en;
|
||||||
|
wire[WIDTH-1:0] su1_do_re;
|
||||||
|
wire[WIDTH-1:0] su1_do_im;
|
||||||
|
wire su2_do_en;
|
||||||
|
wire[WIDTH-1:0] su2_do_re;
|
||||||
|
wire[WIDTH-1:0] su2_do_im;
|
||||||
|
wire su3_do_en;
|
||||||
|
wire[WIDTH-1:0] su3_do_re;
|
||||||
|
wire[WIDTH-1:0] su3_do_im;
|
||||||
|
wire su4_do_en;
|
||||||
|
wire[WIDTH-1:0] su4_do_re;
|
||||||
|
wire[WIDTH-1:0] su4_do_im;
|
||||||
|
|
||||||
|
SdfUnit #(.N(1024),.M(1024),.WIDTH(WIDTH)) SU1 (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.reset (reset ), // i
|
||||||
|
.di_en (di_en ), // i
|
||||||
|
.di_re (di_re ), // i
|
||||||
|
.di_im (di_im ), // i
|
||||||
|
.do_en (su1_do_en ), // o
|
||||||
|
.do_re (su1_do_re ), // o
|
||||||
|
.do_im (su1_do_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
SdfUnit #(.N(1024),.M(256),.WIDTH(WIDTH)) SU2 (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.reset (reset ), // i
|
||||||
|
.di_en (su1_do_en ), // i
|
||||||
|
.di_re (su1_do_re ), // i
|
||||||
|
.di_im (su1_do_im ), // i
|
||||||
|
.do_en (su2_do_en ), // o
|
||||||
|
.do_re (su2_do_re ), // o
|
||||||
|
.do_im (su2_do_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
SdfUnit #(.N(1024),.M(64),.WIDTH(WIDTH)) SU3 (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.reset (reset ), // i
|
||||||
|
.di_en (su2_do_en ), // i
|
||||||
|
.di_re (su2_do_re ), // i
|
||||||
|
.di_im (su2_do_im ), // i
|
||||||
|
.do_en (su3_do_en ), // o
|
||||||
|
.do_re (su3_do_re ), // o
|
||||||
|
.do_im (su3_do_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
SdfUnit #(.N(1024),.M(16),.WIDTH(WIDTH)) SU4 (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.reset (reset ), // i
|
||||||
|
.di_en (su3_do_en ), // i
|
||||||
|
.di_re (su3_do_re ), // i
|
||||||
|
.di_im (su3_do_im ), // i
|
||||||
|
.do_en (su4_do_en ), // o
|
||||||
|
.do_re (su4_do_re ), // o
|
||||||
|
.do_im (su4_do_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
SdfUnit #(.N(1024),.M(4),.WIDTH(WIDTH)) SU5 (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.reset (reset ), // i
|
||||||
|
.di_en (su4_do_en ), // i
|
||||||
|
.di_re (su4_do_re ), // i
|
||||||
|
.di_im (su4_do_im ), // i
|
||||||
|
.do_en (do_en ), // o
|
||||||
|
.do_re (do_re ), // o
|
||||||
|
.do_im (do_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
35
rtl/ip/fft/Multiply.v
Normal file
35
rtl/ip/fft/Multiply.v
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Multiply: Complex Multiplier
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
module Multiply #(
|
||||||
|
parameter WIDTH = 16
|
||||||
|
)(
|
||||||
|
input signed [WIDTH-1:0] a_re,
|
||||||
|
input signed [WIDTH-1:0] a_im,
|
||||||
|
input signed [WIDTH-1:0] b_re,
|
||||||
|
input signed [WIDTH-1:0] b_im,
|
||||||
|
output signed [WIDTH-1:0] m_re,
|
||||||
|
output signed [WIDTH-1:0] m_im
|
||||||
|
);
|
||||||
|
|
||||||
|
wire signed [WIDTH*2-1:0] arbr, arbi, aibr, aibi;
|
||||||
|
wire signed [WIDTH-1:0] sc_arbr, sc_arbi, sc_aibr, sc_aibi;
|
||||||
|
|
||||||
|
// Signed Multiplication
|
||||||
|
assign arbr = a_re * b_re;
|
||||||
|
assign arbi = a_re * b_im;
|
||||||
|
assign aibr = a_im * b_re;
|
||||||
|
assign aibi = a_im * b_im;
|
||||||
|
|
||||||
|
// Scaling
|
||||||
|
assign sc_arbr = arbr >>> (WIDTH-1);
|
||||||
|
assign sc_arbi = arbi >>> (WIDTH-1);
|
||||||
|
assign sc_aibr = aibr >>> (WIDTH-1);
|
||||||
|
assign sc_aibi = aibi >>> (WIDTH-1);
|
||||||
|
|
||||||
|
// Sub/Add
|
||||||
|
// These sub/add may overflow if unnormalized data is input.
|
||||||
|
assign m_re = sc_arbr - sc_aibi;
|
||||||
|
assign m_im = sc_arbi + sc_aibr;
|
||||||
|
|
||||||
|
endmodule
|
||||||
277
rtl/ip/fft/SdfUnit.v
Normal file
277
rtl/ip/fft/SdfUnit.v
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SdfUnit: Radix-2^2 Single-Path Delay Feedback Unit for N-Point FFT
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
module SdfUnit #(
|
||||||
|
parameter N = 64, // Number of FFT Point
|
||||||
|
parameter M = 64, // Twiddle Resolution
|
||||||
|
parameter WIDTH = 16 // Data Bit Length
|
||||||
|
)(
|
||||||
|
input clock, // Master Clock
|
||||||
|
input reset, // Active High Asynchronous Reset
|
||||||
|
input di_en, // Input Data Enable
|
||||||
|
input [WIDTH-1:0] di_re, // Input Data (Real)
|
||||||
|
input [WIDTH-1:0] di_im, // Input Data (Imag)
|
||||||
|
output do_en, // Output Data Enable
|
||||||
|
output [WIDTH-1:0] do_re, // Output Data (Real)
|
||||||
|
output [WIDTH-1:0] do_im // Output Data (Imag)
|
||||||
|
);
|
||||||
|
|
||||||
|
// log2 constant function
|
||||||
|
function integer log2;
|
||||||
|
input integer x;
|
||||||
|
integer value;
|
||||||
|
begin
|
||||||
|
value = x-1;
|
||||||
|
for (log2=0; value>0; log2=log2+1)
|
||||||
|
value = value>>1;
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
localparam LOG_N = log2(N); // Bit Length of N
|
||||||
|
localparam LOG_M = log2(M); // Bit Length of M
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Internal Regs and Nets
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 1st Butterfly
|
||||||
|
reg [LOG_N-1:0] di_count; // Input Data Count
|
||||||
|
wire bf1_bf; // Butterfly Add/Sub Enable
|
||||||
|
wire[WIDTH-1:0] bf1_x0_re; // Data #0 to Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf1_x0_im; // Data #0 to Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] bf1_x1_re; // Data #1 to Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf1_x1_im; // Data #1 to Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] bf1_y0_re; // Data #0 from Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf1_y0_im; // Data #0 from Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] bf1_y1_re; // Data #1 from Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf1_y1_im; // Data #1 from Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] db1_di_re; // Data to DelayBuffer (Real)
|
||||||
|
wire[WIDTH-1:0] db1_di_im; // Data to DelayBuffer (Imag)
|
||||||
|
wire[WIDTH-1:0] db1_do_re; // Data from DelayBuffer (Real)
|
||||||
|
wire[WIDTH-1:0] db1_do_im; // Data from DelayBuffer (Imag)
|
||||||
|
wire[WIDTH-1:0] bf1_sp_re; // Single-Path Data Output (Real)
|
||||||
|
wire[WIDTH-1:0] bf1_sp_im; // Single-Path Data Output (Imag)
|
||||||
|
reg bf1_sp_en; // Single-Path Data Enable
|
||||||
|
reg [LOG_N-1:0] bf1_count; // Single-Path Data Count
|
||||||
|
wire bf1_start; // Single-Path Output Trigger
|
||||||
|
wire bf1_end; // End of Single-Path Data
|
||||||
|
wire bf1_mj; // Twiddle (-j) Enable
|
||||||
|
reg [WIDTH-1:0] bf1_do_re; // 1st Butterfly Output Data (Real)
|
||||||
|
reg [WIDTH-1:0] bf1_do_im; // 1st Butterfly Output Data (Imag)
|
||||||
|
|
||||||
|
// 2nd Butterfly
|
||||||
|
reg bf2_bf; // Butterfly Add/Sub Enable
|
||||||
|
wire[WIDTH-1:0] bf2_x0_re; // Data #0 to Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf2_x0_im; // Data #0 to Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] bf2_x1_re; // Data #1 to Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf2_x1_im; // Data #1 to Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] bf2_y0_re; // Data #0 from Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf2_y0_im; // Data #0 from Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] bf2_y1_re; // Data #1 from Butterfly (Real)
|
||||||
|
wire[WIDTH-1:0] bf2_y1_im; // Data #1 from Butterfly (Imag)
|
||||||
|
wire[WIDTH-1:0] db2_di_re; // Data to DelayBuffer (Real)
|
||||||
|
wire[WIDTH-1:0] db2_di_im; // Data to DelayBuffer (Imag)
|
||||||
|
wire[WIDTH-1:0] db2_do_re; // Data from DelayBuffer (Real)
|
||||||
|
wire[WIDTH-1:0] db2_do_im; // Data from DelayBuffer (Imag)
|
||||||
|
wire[WIDTH-1:0] bf2_sp_re; // Single-Path Data Output (Real)
|
||||||
|
wire[WIDTH-1:0] bf2_sp_im; // Single-Path Data Output (Imag)
|
||||||
|
reg bf2_sp_en; // Single-Path Data Enable
|
||||||
|
reg [LOG_N-1:0] bf2_count; // Single-Path Data Count
|
||||||
|
reg bf2_start; // Single-Path Output Trigger
|
||||||
|
wire bf2_end; // End of Single-Path Data
|
||||||
|
reg [WIDTH-1:0] bf2_do_re; // 2nd Butterfly Output Data (Real)
|
||||||
|
reg [WIDTH-1:0] bf2_do_im; // 2nd Butterfly Output Data (Imag)
|
||||||
|
reg bf2_do_en; // 2nd Butterfly Output Data Enable
|
||||||
|
|
||||||
|
// Multiplication
|
||||||
|
wire[1:0] tw_sel; // Twiddle Select (2n/n/3n)
|
||||||
|
wire[LOG_N-3:0] tw_num; // Twiddle Number (n)
|
||||||
|
wire[LOG_N-1:0] tw_addr; // Twiddle Table Address
|
||||||
|
wire[WIDTH-1:0] tw_re; // Twiddle Factor (Real)
|
||||||
|
wire[WIDTH-1:0] tw_im; // Twiddle Factor (Imag)
|
||||||
|
reg mu_en; // Multiplication Enable
|
||||||
|
wire[WIDTH-1:0] mu_a_re; // Multiplier Input (Real)
|
||||||
|
wire[WIDTH-1:0] mu_a_im; // Multiplier Input (Imag)
|
||||||
|
wire[WIDTH-1:0] mu_m_re; // Multiplier Output (Real)
|
||||||
|
wire[WIDTH-1:0] mu_m_im; // Multiplier Output (Imag)
|
||||||
|
reg [WIDTH-1:0] mu_do_re; // Multiplication Output Data (Real)
|
||||||
|
reg [WIDTH-1:0] mu_do_im; // Multiplication Output Data (Imag)
|
||||||
|
reg mu_do_en; // Multiplication Output Data Enable
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 1st Butterfly
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
always @(posedge clock or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
di_count <= {LOG_N{1'b0}};
|
||||||
|
end else begin
|
||||||
|
di_count <= di_en ? (di_count + 1'b1) : {LOG_N{1'b0}};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign bf1_bf = di_count[LOG_M-1];
|
||||||
|
|
||||||
|
// Set unknown value x for verification
|
||||||
|
assign bf1_x0_re = bf1_bf ? db1_do_re : {WIDTH{1'bx}};
|
||||||
|
assign bf1_x0_im = bf1_bf ? db1_do_im : {WIDTH{1'bx}};
|
||||||
|
assign bf1_x1_re = bf1_bf ? di_re : {WIDTH{1'bx}};
|
||||||
|
assign bf1_x1_im = bf1_bf ? di_im : {WIDTH{1'bx}};
|
||||||
|
|
||||||
|
Butterfly #(.WIDTH(WIDTH),.RH(0)) BF1 (
|
||||||
|
.x0_re (bf1_x0_re ), // i
|
||||||
|
.x0_im (bf1_x0_im ), // i
|
||||||
|
.x1_re (bf1_x1_re ), // i
|
||||||
|
.x1_im (bf1_x1_im ), // i
|
||||||
|
.y0_re (bf1_y0_re ), // o
|
||||||
|
.y0_im (bf1_y0_im ), // o
|
||||||
|
.y1_re (bf1_y1_re ), // o
|
||||||
|
.y1_im (bf1_y1_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
DelayBuffer #(.DEPTH(2**(LOG_M-1)),.WIDTH(WIDTH)) DB1 (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.di_re (db1_di_re ), // i
|
||||||
|
.di_im (db1_di_im ), // i
|
||||||
|
.do_re (db1_do_re ), // o
|
||||||
|
.do_im (db1_do_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
assign db1_di_re = bf1_bf ? bf1_y1_re : di_re;
|
||||||
|
assign db1_di_im = bf1_bf ? bf1_y1_im : di_im;
|
||||||
|
assign bf1_sp_re = bf1_bf ? bf1_y0_re : bf1_mj ? db1_do_im : db1_do_re;
|
||||||
|
assign bf1_sp_im = bf1_bf ? bf1_y0_im : bf1_mj ? -db1_do_re : db1_do_im;
|
||||||
|
|
||||||
|
always @(posedge clock or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
bf1_sp_en <= 1'b0;
|
||||||
|
bf1_count <= {LOG_N{1'b0}};
|
||||||
|
end else begin
|
||||||
|
bf1_sp_en <= bf1_start ? 1'b1 : bf1_end ? 1'b0 : bf1_sp_en;
|
||||||
|
bf1_count <= bf1_sp_en ? (bf1_count + 1'b1) : {LOG_N{1'b0}};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign bf1_start = (di_count == (2**(LOG_M-1)-1));
|
||||||
|
assign bf1_end = (bf1_count == (2**LOG_N-1));
|
||||||
|
assign bf1_mj = (bf1_count[LOG_M-1:LOG_M-2] == 2'd3);
|
||||||
|
|
||||||
|
always @(posedge clock) begin
|
||||||
|
bf1_do_re <= bf1_sp_re;
|
||||||
|
bf1_do_im <= bf1_sp_im;
|
||||||
|
end
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 2nd Butterfly
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
always @(posedge clock) begin
|
||||||
|
bf2_bf <= bf1_count[LOG_M-2];
|
||||||
|
end
|
||||||
|
|
||||||
|
// Set unknown value x for verification
|
||||||
|
assign bf2_x0_re = bf2_bf ? db2_do_re : {WIDTH{1'bx}};
|
||||||
|
assign bf2_x0_im = bf2_bf ? db2_do_im : {WIDTH{1'bx}};
|
||||||
|
assign bf2_x1_re = bf2_bf ? bf1_do_re : {WIDTH{1'bx}};
|
||||||
|
assign bf2_x1_im = bf2_bf ? bf1_do_im : {WIDTH{1'bx}};
|
||||||
|
|
||||||
|
// Negative bias occurs when RH=0 and positive bias occurs when RH=1.
|
||||||
|
// Using both alternately reduces the overall rounding error.
|
||||||
|
Butterfly #(.WIDTH(WIDTH),.RH(1)) BF2 (
|
||||||
|
.x0_re (bf2_x0_re ), // i
|
||||||
|
.x0_im (bf2_x0_im ), // i
|
||||||
|
.x1_re (bf2_x1_re ), // i
|
||||||
|
.x1_im (bf2_x1_im ), // i
|
||||||
|
.y0_re (bf2_y0_re ), // o
|
||||||
|
.y0_im (bf2_y0_im ), // o
|
||||||
|
.y1_re (bf2_y1_re ), // o
|
||||||
|
.y1_im (bf2_y1_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
DelayBuffer #(.DEPTH(2**(LOG_M-2)),.WIDTH(WIDTH)) DB2 (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.di_re (db2_di_re ), // i
|
||||||
|
.di_im (db2_di_im ), // i
|
||||||
|
.do_re (db2_do_re ), // o
|
||||||
|
.do_im (db2_do_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
assign db2_di_re = bf2_bf ? bf2_y1_re : bf1_do_re;
|
||||||
|
assign db2_di_im = bf2_bf ? bf2_y1_im : bf1_do_im;
|
||||||
|
assign bf2_sp_re = bf2_bf ? bf2_y0_re : db2_do_re;
|
||||||
|
assign bf2_sp_im = bf2_bf ? bf2_y0_im : db2_do_im;
|
||||||
|
|
||||||
|
always @(posedge clock or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
bf2_sp_en <= 1'b0;
|
||||||
|
bf2_count <= {LOG_N{1'b0}};
|
||||||
|
end else begin
|
||||||
|
bf2_sp_en <= bf2_start ? 1'b1 : bf2_end ? 1'b0 : bf2_sp_en;
|
||||||
|
bf2_count <= bf2_sp_en ? (bf2_count + 1'b1) : {LOG_N{1'b0}};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clock) begin
|
||||||
|
bf2_start <= (bf1_count == (2**(LOG_M-2)-1)) & bf1_sp_en;
|
||||||
|
end
|
||||||
|
assign bf2_end = (bf2_count == (2**LOG_N-1));
|
||||||
|
|
||||||
|
always @(posedge clock) begin
|
||||||
|
bf2_do_re <= bf2_sp_re;
|
||||||
|
bf2_do_im <= bf2_sp_im;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clock or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
bf2_do_en <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
bf2_do_en <= bf2_sp_en;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Multiplication
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
assign tw_sel[1] = bf2_count[LOG_M-2];
|
||||||
|
assign tw_sel[0] = bf2_count[LOG_M-1];
|
||||||
|
assign tw_num = bf2_count << (LOG_N-LOG_M);
|
||||||
|
assign tw_addr = tw_num * tw_sel;
|
||||||
|
|
||||||
|
Twiddle TW (
|
||||||
|
.clock (clock ), // i
|
||||||
|
.addr (tw_addr), // i
|
||||||
|
.tw_re (tw_re ), // o
|
||||||
|
.tw_im (tw_im ) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
// Multiplication is bypassed when twiddle address is 0.
|
||||||
|
always @(posedge clock) begin
|
||||||
|
mu_en <= (tw_addr != {LOG_N{1'b0}});
|
||||||
|
end
|
||||||
|
// Set unknown value x for verification
|
||||||
|
assign mu_a_re = mu_en ? bf2_do_re : {WIDTH{1'bx}};
|
||||||
|
assign mu_a_im = mu_en ? bf2_do_im : {WIDTH{1'bx}};
|
||||||
|
|
||||||
|
Multiply #(.WIDTH(WIDTH)) MU (
|
||||||
|
.a_re (mu_a_re), // i
|
||||||
|
.a_im (mu_a_im), // i
|
||||||
|
.b_re (tw_re ), // i
|
||||||
|
.b_im (tw_im ), // i
|
||||||
|
.m_re (mu_m_re), // o
|
||||||
|
.m_im (mu_m_im) // o
|
||||||
|
);
|
||||||
|
|
||||||
|
always @(posedge clock) begin
|
||||||
|
mu_do_re <= mu_en ? mu_m_re : bf2_do_re;
|
||||||
|
mu_do_im <= mu_en ? mu_m_im : bf2_do_im;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clock or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
mu_do_en <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
mu_do_en <= bf2_do_en;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// No multiplication required at final stage
|
||||||
|
assign do_en = (LOG_M == 2) ? bf2_do_en : mu_do_en;
|
||||||
|
assign do_re = (LOG_M == 2) ? bf2_do_re : mu_do_re;
|
||||||
|
assign do_im = (LOG_M == 2) ? bf2_do_im : mu_do_im;
|
||||||
|
|
||||||
|
endmodule
|
||||||
1064
rtl/ip/fft/Twiddle1024_32B.v
Normal file
1064
rtl/ip/fft/Twiddle1024_32B.v
Normal file
File diff suppressed because it is too large
Load Diff
383
rtl/ip/fft/axi_fft_wrapper.v
Normal file
383
rtl/ip/fft/axi_fft_wrapper.v
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
`define FFT_CSR_ADDR 16'hf000
|
||||||
|
`define FFT_IN_RE_BASE 16'h1000
|
||||||
|
`define FFT_IN_IM_BASE 16'h2000
|
||||||
|
`define FFT_OUT_RE_BASE 16'h3000
|
||||||
|
`define FFT_OUT_IM_BASE 16'h4000
|
||||||
|
|
||||||
|
module fft_controller #(
|
||||||
|
parameter FFT_LEN = 1024, // FFT处理长度
|
||||||
|
parameter BRAM_RD_LATENCY = 2 // 适配 BRAM 的读延迟 (通常 Block RAM 开启输出寄存器为 2)
|
||||||
|
)(
|
||||||
|
input aclk,
|
||||||
|
input aresetn,
|
||||||
|
|
||||||
|
// AXI Lite / AXI4 Wrapper ports
|
||||||
|
input [4 :0] s_awid,
|
||||||
|
input [31:0] s_awaddr,
|
||||||
|
input [7 :0] s_awlen,
|
||||||
|
input [2 :0] s_awsize,
|
||||||
|
input [1 :0] s_awburst,
|
||||||
|
input s_awlock,
|
||||||
|
input [3 :0] s_awcache,
|
||||||
|
input [2 :0] s_awprot,
|
||||||
|
input s_awvalid,
|
||||||
|
output reg s_awready,
|
||||||
|
input [4 :0] s_wid,
|
||||||
|
input [31:0] s_wdata,
|
||||||
|
input [3 :0] s_wstrb,
|
||||||
|
input s_wlast,
|
||||||
|
input s_wvalid,
|
||||||
|
output reg s_wready,
|
||||||
|
output [4 :0] s_bid,
|
||||||
|
output [1 :0] s_bresp,
|
||||||
|
output reg s_bvalid,
|
||||||
|
input s_bready,
|
||||||
|
|
||||||
|
input [4 :0] s_arid,
|
||||||
|
input [31:0] s_araddr,
|
||||||
|
input [7 :0] s_arlen,
|
||||||
|
input [2 :0] s_arsize,
|
||||||
|
input [1 :0] s_arburst,
|
||||||
|
input s_arlock,
|
||||||
|
input [3 :0] s_arcache,
|
||||||
|
input [2 :0] s_arprot,
|
||||||
|
input s_arvalid,
|
||||||
|
output reg s_arready,
|
||||||
|
output [4 :0] s_rid,
|
||||||
|
output reg [31:0] s_rdata,
|
||||||
|
output [1 :0] s_rresp,
|
||||||
|
output reg s_rlast,
|
||||||
|
output reg s_rvalid,
|
||||||
|
input s_rready,
|
||||||
|
|
||||||
|
output fft_finish
|
||||||
|
);
|
||||||
|
|
||||||
|
// AXI Burst 内部寄存器
|
||||||
|
reg [31:0] aw_addr_reg;
|
||||||
|
reg [1 :0] aw_burst_reg;
|
||||||
|
reg [4 :0] aw_id_reg;
|
||||||
|
|
||||||
|
reg [31:0] ar_addr_reg;
|
||||||
|
reg [7 :0] ar_len_reg;
|
||||||
|
reg [1 :0] ar_burst_reg;
|
||||||
|
reg [4 :0] ar_id_reg;
|
||||||
|
reg [7 :0] ar_cnt;
|
||||||
|
|
||||||
|
// 写通道状态机 (AW, W, B)
|
||||||
|
localparam W_IDLE = 2'd0;
|
||||||
|
localparam W_DATA = 2'd1;
|
||||||
|
localparam W_RESP = 2'd2;
|
||||||
|
reg [1:0] w_state;
|
||||||
|
|
||||||
|
always @(posedge aclk) begin
|
||||||
|
if (~aresetn) begin
|
||||||
|
w_state <= W_IDLE;
|
||||||
|
s_awready <= 1'b1;
|
||||||
|
s_wready <= 1'b0;
|
||||||
|
s_bvalid <= 1'b0;
|
||||||
|
aw_addr_reg <= 32'h0;
|
||||||
|
aw_burst_reg <= 2'h0;
|
||||||
|
aw_id_reg <= 5'h0;
|
||||||
|
end else begin
|
||||||
|
case (w_state)
|
||||||
|
W_IDLE: begin
|
||||||
|
s_awready <= 1'b1;
|
||||||
|
if (s_awvalid && s_awready) begin
|
||||||
|
s_awready <= 1'b0;
|
||||||
|
s_wready <= 1'b1;
|
||||||
|
aw_addr_reg <= s_awaddr;
|
||||||
|
aw_burst_reg <= s_awburst;
|
||||||
|
aw_id_reg <= s_awid;
|
||||||
|
w_state <= W_DATA;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
W_DATA: begin
|
||||||
|
// 接收到有效写数据
|
||||||
|
if (s_wvalid && s_wready) begin
|
||||||
|
// INCR 模式下地址递增
|
||||||
|
if (aw_burst_reg == 2'b01) begin
|
||||||
|
aw_addr_reg <= aw_addr_reg + 4;
|
||||||
|
end
|
||||||
|
// 若是最后一拍,跳转到发送响应
|
||||||
|
if (s_wlast) begin
|
||||||
|
s_wready <= 1'b0;
|
||||||
|
s_bvalid <= 1'b1;
|
||||||
|
w_state <= W_RESP;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
W_RESP: begin
|
||||||
|
if (s_bvalid && s_bready) begin
|
||||||
|
s_bvalid <= 1'b0;
|
||||||
|
s_awready <= 1'b1; // 准备接收下一次写请求
|
||||||
|
w_state <= W_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
default: w_state <= W_IDLE;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 产生有效的写动作脉冲,用于触发 BRAM 写使能
|
||||||
|
wire w_active = (w_state == W_DATA) && s_wvalid && s_wready;
|
||||||
|
assign s_bid = aw_id_reg;
|
||||||
|
assign s_bresp = 2'b00;
|
||||||
|
|
||||||
|
// 读通道状态机(AR, R)
|
||||||
|
localparam R_IDLE = 2'd0;
|
||||||
|
localparam R_WAIT = 2'd1;
|
||||||
|
localparam R_DATA = 2'd2;
|
||||||
|
reg [1:0] r_state;
|
||||||
|
reg [3:0] r_wait_cnt;
|
||||||
|
|
||||||
|
always @(posedge aclk) begin
|
||||||
|
if (~aresetn) begin
|
||||||
|
r_state <= R_IDLE;
|
||||||
|
s_arready <= 1'b1;
|
||||||
|
s_rvalid <= 1'b0;
|
||||||
|
s_rlast <= 1'b0;
|
||||||
|
ar_addr_reg <= 32'h0;
|
||||||
|
ar_len_reg <= 8'h0;
|
||||||
|
ar_burst_reg <= 2'h0;
|
||||||
|
ar_id_reg <= 5'h0;
|
||||||
|
ar_cnt <= 8'h0;
|
||||||
|
r_wait_cnt <= 4'h0;
|
||||||
|
end else begin
|
||||||
|
case (r_state)
|
||||||
|
R_IDLE: begin
|
||||||
|
s_arready <= 1'b1;
|
||||||
|
if (s_arvalid && s_arready) begin
|
||||||
|
s_arready <= 1'b0;
|
||||||
|
ar_addr_reg <= s_araddr;
|
||||||
|
ar_len_reg <= s_arlen;
|
||||||
|
ar_burst_reg <= s_arburst;
|
||||||
|
ar_id_reg <= s_arid;
|
||||||
|
ar_cnt <= 8'h0;
|
||||||
|
r_wait_cnt <= BRAM_RD_LATENCY;
|
||||||
|
r_state <= R_WAIT; // 进入等待以拉取第一笔 BRAM 数据
|
||||||
|
end
|
||||||
|
end
|
||||||
|
R_WAIT: begin
|
||||||
|
// 等待 BRAM 固定潜伏期结束
|
||||||
|
if (r_wait_cnt <= 1) begin
|
||||||
|
s_rvalid <= 1'b1;
|
||||||
|
s_rlast <= (ar_cnt == ar_len_reg);
|
||||||
|
r_state <= R_DATA;
|
||||||
|
r_wait_cnt <= 4'h0;
|
||||||
|
end else begin
|
||||||
|
r_wait_cnt <= r_wait_cnt - 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
R_DATA: begin
|
||||||
|
// 数据被总线成功读取
|
||||||
|
if (s_rvalid && s_rready) begin
|
||||||
|
if (ar_cnt == ar_len_reg) begin
|
||||||
|
// Burst 结束
|
||||||
|
s_rvalid <= 1'b0;
|
||||||
|
s_rlast <= 1'b0;
|
||||||
|
s_arready <= 1'b1;
|
||||||
|
r_state <= R_IDLE;
|
||||||
|
end else begin
|
||||||
|
// 准备读取下一拍数据
|
||||||
|
ar_cnt <= ar_cnt + 1;
|
||||||
|
// INCR 模式下地址递增
|
||||||
|
if (ar_burst_reg == 2'b01) begin
|
||||||
|
ar_addr_reg <= ar_addr_reg + 4;
|
||||||
|
end
|
||||||
|
s_rvalid <= 1'b0;
|
||||||
|
s_rlast <= 1'b0;
|
||||||
|
r_wait_cnt <= BRAM_RD_LATENCY;
|
||||||
|
r_state <= R_WAIT; // 再次进入等待状态
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
default: r_state <= R_IDLE;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign s_rid = ar_id_reg;
|
||||||
|
assign s_rresp = 2'b00;
|
||||||
|
|
||||||
|
// 地址译码
|
||||||
|
wire hit_in_re = (aw_addr_reg[15:12] == 4'h1);
|
||||||
|
wire hit_in_im = (aw_addr_reg[15:12] == 4'h2);
|
||||||
|
wire hit_out_re = (ar_addr_reg[15:12] == 4'h3);
|
||||||
|
wire hit_out_im = (ar_addr_reg[15:12] == 4'h4);
|
||||||
|
|
||||||
|
// 寄存器控制
|
||||||
|
reg [3:0] fft_ctrl;
|
||||||
|
reg [3:0] fft_status;
|
||||||
|
|
||||||
|
wire [31:0] fft_csr = {24'h0, fft_ctrl, fft_status};
|
||||||
|
// 只有发生有效的写握手且地址对应 CSR 时,才写入控制寄存器
|
||||||
|
wire write_fft_csr = w_active && (aw_addr_reg[15:0] == `FFT_CSR_ADDR);
|
||||||
|
|
||||||
|
// 状态机
|
||||||
|
localparam IDLE = 2'd0;
|
||||||
|
localparam LOAD = 2'd1;
|
||||||
|
localparam WAIT_OUT = 2'd2;
|
||||||
|
localparam DONE = 2'd3;
|
||||||
|
|
||||||
|
reg [1:0] state, next_state;
|
||||||
|
reg [10:0] in_cnt;
|
||||||
|
reg [10:0] out_cnt;
|
||||||
|
|
||||||
|
wire do_en;
|
||||||
|
wire [31:0] do_re;
|
||||||
|
wire [31:0] do_im;
|
||||||
|
|
||||||
|
always @(posedge aclk) begin
|
||||||
|
if (~aresetn) state <= IDLE;
|
||||||
|
else state <= next_state;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
case (state)
|
||||||
|
IDLE: if (fft_ctrl[0]) next_state = LOAD; else next_state = IDLE;
|
||||||
|
LOAD: if (in_cnt == FFT_LEN - 1) next_state = WAIT_OUT; else next_state = LOAD;
|
||||||
|
WAIT_OUT: if (do_en && out_cnt == FFT_LEN - 1) next_state = DONE; else next_state = WAIT_OUT;
|
||||||
|
DONE: next_state = IDLE;
|
||||||
|
default: next_state = IDLE;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge aclk) begin
|
||||||
|
if (~aresetn) begin
|
||||||
|
in_cnt <= 0;
|
||||||
|
out_cnt <= 0;
|
||||||
|
end else if (state == IDLE) begin
|
||||||
|
in_cnt <= 0;
|
||||||
|
out_cnt <= 0;
|
||||||
|
end else begin
|
||||||
|
if (state == LOAD) in_cnt <= in_cnt + 1;
|
||||||
|
if (do_en) out_cnt <= out_cnt + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 更新寄存器与状态
|
||||||
|
always @(posedge aclk) begin
|
||||||
|
if (~aresetn) begin
|
||||||
|
fft_ctrl <= 4'h0;
|
||||||
|
fft_status <= 4'h0;
|
||||||
|
end else begin
|
||||||
|
if (write_fft_csr) begin
|
||||||
|
fft_ctrl <= s_wdata[7:4];
|
||||||
|
end else if (state != IDLE) begin
|
||||||
|
fft_ctrl[0] <= 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
fft_status[0] <= (state != IDLE && state != DONE);
|
||||||
|
|
||||||
|
if (write_fft_csr && s_wdata[4]) begin
|
||||||
|
fft_status[1] <= 1'b0;
|
||||||
|
end else if (state == DONE) begin
|
||||||
|
fft_status[1] <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign fft_finish = fft_status[1];
|
||||||
|
|
||||||
|
// BRAM 接口连线
|
||||||
|
wire [31:0] in_re_dout;
|
||||||
|
wire [31:0] in_im_dout;
|
||||||
|
wire [31:0] out_re_dout;
|
||||||
|
wire [31:0] out_im_dout;
|
||||||
|
|
||||||
|
bram0 u_in0(
|
||||||
|
.clka (aclk),
|
||||||
|
.ena (w_active && hit_in_re), // 由 burst写逻辑产生
|
||||||
|
.wea (1'b1),
|
||||||
|
.addra (aw_addr_reg[11:2]), // 取当前递增的突发写地址
|
||||||
|
.dina (s_wdata),
|
||||||
|
|
||||||
|
.clkb (aclk),
|
||||||
|
.enb (state == LOAD),
|
||||||
|
.addrb (in_cnt[9:0]),
|
||||||
|
.doutb (in_re_dout)
|
||||||
|
);
|
||||||
|
|
||||||
|
bram0 u_in1(
|
||||||
|
.clka (aclk),
|
||||||
|
.ena (w_active && hit_in_im),
|
||||||
|
.wea (1'b1),
|
||||||
|
.addra (aw_addr_reg[11:2]),
|
||||||
|
.dina (s_wdata),
|
||||||
|
|
||||||
|
.clkb (aclk),
|
||||||
|
.enb (state == LOAD),
|
||||||
|
.addrb (in_cnt[9:0]),
|
||||||
|
.doutb (in_im_dout)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 对于输出 BRAM,采用原先的位反转(Bit-reverse)读取逻辑
|
||||||
|
wire [9:0] b_rd_addr = ar_addr_reg[11:2];
|
||||||
|
wire [9:0] bit_reversed_index = {
|
||||||
|
b_rd_addr[0], b_rd_addr[1], b_rd_addr[2], b_rd_addr[3], b_rd_addr[4],
|
||||||
|
b_rd_addr[5], b_rd_addr[6], b_rd_addr[7], b_rd_addr[8], b_rd_addr[9]
|
||||||
|
};
|
||||||
|
|
||||||
|
bram0 u_out0(
|
||||||
|
.clka (aclk),
|
||||||
|
.ena (do_en),
|
||||||
|
.wea (1'b1),
|
||||||
|
.addra (out_cnt[9:0]),
|
||||||
|
.dina (do_re),
|
||||||
|
|
||||||
|
.clkb (aclk),
|
||||||
|
.enb (r_state != R_IDLE), // 当处于读突发事务时始终使能 BRAM
|
||||||
|
.addrb (bit_reversed_index),
|
||||||
|
.doutb (out_re_dout)
|
||||||
|
);
|
||||||
|
|
||||||
|
bram0 u_out1(
|
||||||
|
.clka (aclk),
|
||||||
|
.ena (do_en),
|
||||||
|
.wea (1'b1),
|
||||||
|
.addra (out_cnt[9:0]),
|
||||||
|
.dina (do_im),
|
||||||
|
|
||||||
|
.clkb (aclk),
|
||||||
|
.enb (r_state != R_IDLE),
|
||||||
|
.addrb (bit_reversed_index),
|
||||||
|
.doutb (out_im_dout)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 支持 1 到 N 拍的读延迟,让 di_en 严格对齐 BRAM 的吐出数据
|
||||||
|
reg [3:0] di_en_shift;
|
||||||
|
always @(posedge aclk) begin
|
||||||
|
if (~aresetn) di_en_shift <= 0;
|
||||||
|
else di_en_shift <= {di_en_shift[2:0], (state == LOAD)};
|
||||||
|
end
|
||||||
|
|
||||||
|
wire di_en_r = (BRAM_RD_LATENCY == 1) ? di_en_shift[0] :
|
||||||
|
(BRAM_RD_LATENCY == 2) ? di_en_shift[1] :
|
||||||
|
(BRAM_RD_LATENCY == 3) ? di_en_shift[2] : di_en_shift[3];
|
||||||
|
|
||||||
|
FFT #(
|
||||||
|
.WIDTH(32)
|
||||||
|
) u_FFT (
|
||||||
|
.clock (aclk),
|
||||||
|
.reset (~aresetn),
|
||||||
|
.di_en (di_en_r),
|
||||||
|
.di_re (in_re_dout),
|
||||||
|
.di_im (in_im_dout),
|
||||||
|
.do_en (do_en),
|
||||||
|
.do_re (do_re),
|
||||||
|
.do_im (do_im)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 读取返回数据选择
|
||||||
|
wire [31:0] rdata_d =
|
||||||
|
(ar_addr_reg[15:0] == `FFT_CSR_ADDR) ? fft_csr :
|
||||||
|
hit_out_re ? out_re_dout :
|
||||||
|
hit_out_im ? out_im_dout :
|
||||||
|
32'h0;
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
s_rdata = rdata_d;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
Reference in New Issue
Block a user