380 lines
12 KiB
Systemverilog
380 lines
12 KiB
Systemverilog
/*
|
||
Ahmet Alaca:
|
||
> FFT_POWER = log2( transform length)
|
||
> DATA_WIDTH = input Real_DI, Imag_DI wordlength
|
||
> TWIDDLE_WIDTH parameter is used for setting the twiddle factor wordlength. In the previous versions, twiddle factor wordlength is equal to the input real/imag data width. Now, it is selectable.
|
||
> TWIDDLE_TRUNC parameter indicates that how many bits of the twiddle factor bits at the output of butterfly operation will be stored. In the previous version, the design behaves like TWIDDLE_TRUNC = TWIDDLE_WIDTH = DATA_WIDTH. Currently, each of them can be defined independently.
|
||
> One concern is that,
|
||
TWIDDLE_TRUNC <= TWIDDLE_WIDTH
|
||
> When TWIDDLE_TRUNC = 0. FFT_RTL result is nearly equal to the matlab builtin FFT. As you decrease TWIDDLE_TRUNC, hardware will be bigger and critical timing path will be increased.
|
||
*/
|
||
module fft_radix_2 #(
|
||
parameter FFT_POWER = 14,
|
||
parameter DATA_WIDTH = 13,
|
||
parameter TWIDDLE_WIDTH = 15,
|
||
parameter FRAC_BITS = 4
|
||
)
|
||
(
|
||
input logic rstn,
|
||
input logic clk,
|
||
input logic fwd_i,
|
||
input logic vld_i,
|
||
input logic signed [DATA_WIDTH-1 : 0] real_i,
|
||
input logic signed [DATA_WIDTH-1 : 0] imag_i,
|
||
|
||
output logic vld_o,
|
||
output logic signed [DATA_WIDTH + FFT_POWER-1 : 0] real_o,
|
||
output logic signed [DATA_WIDTH + FFT_POWER-1 : 0] imag_o
|
||
);
|
||
|
||
logic [FFT_POWER-2 : 0] k; //twiddle factor index
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] x0_real;
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] x0_imag;
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] x1_real;
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] x1_imag;
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] y0_real;
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] y0_imag;
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] y1_real;
|
||
logic signed [FRAC_BITS + DATA_WIDTH + FFT_POWER - 1 : 0] y1_imag;
|
||
|
||
logic [FFT_POWER - 1 : 0] port1_addr;
|
||
logic [FFT_POWER - 1 : 0] port2_addr;
|
||
logic signed [(FRAC_BITS + DATA_WIDTH + FFT_POWER) * 2 - 1 : 0] port1_wr_data;
|
||
logic signed [(FRAC_BITS + DATA_WIDTH + FFT_POWER) * 2 - 1 : 0] port2_wr_data;
|
||
logic port1_wr_en;
|
||
logic port2_wr_en;
|
||
logic signed [(FRAC_BITS + DATA_WIDTH + FFT_POWER) * 2 - 1 : 0] port1_rd_data;
|
||
logic signed [(FRAC_BITS + DATA_WIDTH + FFT_POWER) * 2 - 1 : 0] port2_rd_data;
|
||
|
||
logic [FFT_POWER-1 : 0] addr_cntr_n, addr_cntr_p;
|
||
logic addr_cntr_clr;
|
||
logic addr_cntr_en;
|
||
logic ram_full;
|
||
logic fft_engine_calc_ended;
|
||
|
||
logic [$clog2(FFT_POWER)-1 : 0] fft_s;
|
||
logic [FFT_POWER-2 : 0] fft_k;
|
||
logic [FFT_POWER-2 : 0] fft_e;
|
||
|
||
logic write_n, write_p;
|
||
logic rw_toggle_clr, rw_toggle_en;
|
||
logic release_en;
|
||
|
||
logic signed [DATA_WIDTH + FFT_POWER - 1 : 0] real_data;
|
||
logic signed [DATA_WIDTH + FFT_POWER - 1 : 0] imag_data;
|
||
|
||
logic data_vld; //vld_i
|
||
logic data_vld_p;
|
||
|
||
// FSM State Type Definition
|
||
localparam N_FSM_STATES = 4;
|
||
typedef logic [$clog2(N_FSM_STATES)-1:0] fsm_state_bt;
|
||
typedef enum fsm_state_bt {FSM_IDLE, FSM_SAVE_DATA, FSM_CALC_FFT, FSM_RELEASE_DATA} fsm_state_et;
|
||
// FSM State Register
|
||
(* fsm_encoding = "auto" *)
|
||
fsm_state_et FsmState_SN, FsmState_SP;
|
||
|
||
butterfly #(
|
||
.FFT_POWER(FFT_POWER),
|
||
.DATA_WIDTH(DATA_WIDTH),
|
||
.TWIDDLE_WIDTH(TWIDDLE_WIDTH),
|
||
.FRAC_BITS(FRAC_BITS)
|
||
)
|
||
butterfly_i (
|
||
.clk(clk),
|
||
.k_i(k),
|
||
.x0_real_i(x0_real),
|
||
.x0_imag_i(x0_imag),
|
||
.x1_real_i(x1_real),
|
||
.x1_imag_i(x1_imag),
|
||
|
||
.y0_real_o(y0_real),
|
||
.y0_imag_o(y0_imag),
|
||
.y1_real_o(y1_real),
|
||
.y1_imag_o(y1_imag)
|
||
);
|
||
|
||
dual_port_bram #(
|
||
.DATA_WIDTH((DATA_WIDTH + FFT_POWER + FRAC_BITS)*2),
|
||
.ADDR_WIDTH(FFT_POWER)
|
||
)
|
||
dual_port_bram_i (
|
||
.clk(clk),
|
||
.port1_addr_i(port1_addr),
|
||
.port2_addr_i(port2_addr),
|
||
|
||
.port1_wr_data_i(port1_wr_data),
|
||
.port2_wr_data_i(port2_wr_data),
|
||
|
||
.port1_wr_en_i(port1_wr_en),
|
||
.port2_wr_en_i(port2_wr_en),
|
||
|
||
.port1_rd_data_o(port1_rd_data),
|
||
.port2_rd_data_o(port2_rd_data)
|
||
);
|
||
|
||
assign x0_real = port1_rd_data[(DATA_WIDTH + FFT_POWER + FRAC_BITS) * 2 - 1 : (DATA_WIDTH + FFT_POWER + FRAC_BITS)];
|
||
assign x0_imag = port1_rd_data[(DATA_WIDTH + FFT_POWER + FRAC_BITS) - 1 : 0];
|
||
assign x1_real = port2_rd_data[(DATA_WIDTH + FFT_POWER + FRAC_BITS) * 2 - 1 : (DATA_WIDTH + FFT_POWER + FRAC_BITS)];
|
||
assign x1_imag = port2_rd_data[(DATA_WIDTH + FFT_POWER + FRAC_BITS) - 1 : 0];
|
||
|
||
// FSM State Transitions
|
||
always_comb begin
|
||
// Default State Assignment
|
||
FsmState_SN = FSM_IDLE;
|
||
|
||
case (FsmState_SP)
|
||
FSM_IDLE : begin
|
||
if (vld_i == 0)
|
||
FsmState_SN = FSM_IDLE;
|
||
else if (vld_i == 1)
|
||
FsmState_SN = FSM_SAVE_DATA;
|
||
end
|
||
|
||
FSM_SAVE_DATA : begin
|
||
if (ram_full == 0)
|
||
FsmState_SN = FSM_SAVE_DATA;
|
||
else if (ram_full == 1)
|
||
FsmState_SN = FSM_CALC_FFT;
|
||
end
|
||
|
||
FSM_CALC_FFT : begin
|
||
if (fft_engine_calc_ended == 0)
|
||
FsmState_SN = FSM_CALC_FFT;
|
||
else if (fft_engine_calc_ended == 1)
|
||
FsmState_SN = FSM_RELEASE_DATA;
|
||
end
|
||
|
||
FSM_RELEASE_DATA : begin
|
||
if (ram_full == 0)
|
||
FsmState_SN = FSM_RELEASE_DATA;
|
||
else if (ram_full == 1)
|
||
FsmState_SN = FSM_IDLE;
|
||
end
|
||
|
||
default :
|
||
FsmState_SN = FSM_IDLE;
|
||
endcase
|
||
end
|
||
logic wrt_vld;
|
||
// FSM Signal Outputs
|
||
always_comb begin
|
||
// Default Output Assignments
|
||
addr_cntr_en = 1;
|
||
addr_cntr_clr = 1;
|
||
rw_toggle_clr = 1;
|
||
rw_toggle_en = 0;
|
||
release_en = 0;
|
||
data_vld = 0;
|
||
wrt_vld = 0;
|
||
case (FsmState_SP)
|
||
FSM_IDLE : begin
|
||
addr_cntr_en = vld_i;
|
||
addr_cntr_clr = 0;
|
||
rw_toggle_clr = 1;
|
||
rw_toggle_en = 1;
|
||
data_vld = vld_i;
|
||
wrt_vld = vld_i;
|
||
end
|
||
FSM_SAVE_DATA : begin
|
||
addr_cntr_en = 1;
|
||
addr_cntr_clr = 0;
|
||
data_vld = 1;
|
||
wrt_vld = vld_i;
|
||
end
|
||
FSM_CALC_FFT : begin
|
||
addr_cntr_en = 1;
|
||
addr_cntr_clr = 1;
|
||
rw_toggle_clr = 0;
|
||
rw_toggle_en = 1;
|
||
end
|
||
|
||
FSM_RELEASE_DATA : begin
|
||
addr_cntr_en = 1;
|
||
addr_cntr_clr = 0;
|
||
release_en = 1;
|
||
end
|
||
|
||
default : begin
|
||
// Default Output Assignments
|
||
addr_cntr_en = 0;
|
||
addr_cntr_clr = 0;
|
||
rw_toggle_clr = 0;
|
||
rw_toggle_en = 0;
|
||
release_en = 0;
|
||
end
|
||
endcase
|
||
end
|
||
|
||
// FSM State Register
|
||
always_ff @(posedge clk or negedge rstn) begin
|
||
if (!rstn)
|
||
FsmState_SP <= FSM_IDLE;
|
||
else
|
||
FsmState_SP <= FsmState_SN;
|
||
end
|
||
|
||
assign addr_cntr_n = ~addr_cntr_clr ? addr_cntr_p + 1 : '0;
|
||
|
||
// Register
|
||
always_ff @(posedge clk or negedge rstn) begin
|
||
if (!rstn)
|
||
addr_cntr_p <= '0;
|
||
else if (addr_cntr_en)
|
||
addr_cntr_p <= addr_cntr_n;
|
||
end
|
||
|
||
assign ram_full = addr_cntr_p == (2**FFT_POWER - 1);
|
||
|
||
always_comb begin
|
||
if (wrt_vld == 1 && fwd_i == 1) begin
|
||
port1_wr_data = { {FFT_POWER{real_i[DATA_WIDTH-1]}} , real_i , {(FRAC_BITS){1'b0}} , {FFT_POWER{imag_i[DATA_WIDTH-1]}} , imag_i , {(FRAC_BITS){1'b0}}};
|
||
port2_wr_data = 0;
|
||
end
|
||
else if (wrt_vld == 1 && fwd_i == 0) begin
|
||
port1_wr_data = { {FFT_POWER{imag_i[DATA_WIDTH-1]}} , imag_i , {(FRAC_BITS){1'b0}} , {FFT_POWER{real_i[DATA_WIDTH-1]}} , real_i , {(FRAC_BITS){1'b0}}};
|
||
port2_wr_data = 0;
|
||
end
|
||
else if (write_p) begin
|
||
port1_wr_data = {y0_real , y0_imag};
|
||
port2_wr_data = {y1_real , y1_imag};
|
||
end
|
||
else begin
|
||
port1_wr_data = 0;
|
||
port2_wr_data = 0;
|
||
end
|
||
end
|
||
|
||
always_comb begin
|
||
if (data_vld == 1 ) begin
|
||
port1_wr_en = 1;
|
||
port2_wr_en = 0;
|
||
end
|
||
else if (rw_toggle_en == 1 ) begin
|
||
port1_wr_en = write_p;
|
||
port2_wr_en = write_p;
|
||
end
|
||
else begin
|
||
port1_wr_en = 0;
|
||
port2_wr_en = 0;
|
||
end
|
||
end
|
||
|
||
always_comb begin
|
||
if (data_vld == 1 ) begin
|
||
port1_addr = {<<{addr_cntr_p}}; // bit reversal
|
||
port2_addr = 0;
|
||
end
|
||
else if (rw_toggle_en == 1 ) begin
|
||
port1_addr = fft_k * (2**(fft_s + 1)) + fft_e; //%AA: 0 2 4 6 , 0 1 ,
|
||
port2_addr = fft_k * (2**(fft_s + 1)) + fft_e + (2**fft_s); //%AA: 1 3 5 7 , 2 3 ,
|
||
end
|
||
else if(release_en == 1) begin
|
||
port1_addr = 0;
|
||
port2_addr = addr_cntr_p;
|
||
end
|
||
else begin
|
||
port1_addr = 0;
|
||
port2_addr = 0;
|
||
end
|
||
end
|
||
|
||
always_comb begin
|
||
if (rw_toggle_en == 1 )
|
||
k = fft_e * (2**(FFT_POWER - fft_s - 1)); //%AA: 0 0 0 0 ;
|
||
else
|
||
k = 0;
|
||
end
|
||
|
||
assign write_n = ~rw_toggle_clr ? ~write_p : '0;
|
||
|
||
always_ff @(posedge clk or negedge rstn) begin
|
||
if (!rstn)
|
||
write_p <= 0;
|
||
else if (rw_toggle_en)
|
||
write_p <= write_n;
|
||
end
|
||
|
||
always_ff @(posedge clk or negedge rstn) begin
|
||
if (!rstn) begin
|
||
fft_s <= 0;
|
||
fft_k <= 0;
|
||
fft_e <= 0;
|
||
fft_engine_calc_ended <= 0;
|
||
end
|
||
else if (write_p) begin
|
||
// fft_s: Satır indexi
|
||
// fft_k: Küme indexi
|
||
// fft_e: Eleman indexi
|
||
// Eleman sonuna ulaşıldı ise
|
||
if (fft_e == 2**fft_s - 1) begin
|
||
// Küme sonuna ulaşıldı ise
|
||
if (fft_k == 2**(FFT_POWER - fft_s - 1) - 1) begin
|
||
// Satır sonuna ulaşıldı ise
|
||
if (fft_s == FFT_POWER - 1) begin
|
||
//FFT işlemi bitmiştir
|
||
fft_s <= 0;
|
||
fft_k <= 0;
|
||
fft_e <= 0;
|
||
fft_engine_calc_ended <= 1;
|
||
end
|
||
else begin // Satır sonuna ulaşılmadı ise
|
||
// Bir sonraki satıra geç
|
||
// Küme ve dizi değişkenlerini sıfırla
|
||
fft_s <= fft_s + 1;
|
||
fft_k <= 0;
|
||
fft_e <= 0;
|
||
end
|
||
end
|
||
else begin // Küme sonuna ulaşılmadı ise
|
||
// Bir sonraki kümeye geç
|
||
fft_k <= fft_k + 1;
|
||
fft_e <= 0;
|
||
end
|
||
end
|
||
else //Eleman sonuna ulaşılmadı ise
|
||
//Bir sonraki elemana geç
|
||
fft_e <= fft_e + 1;
|
||
end
|
||
else if (rw_toggle_clr == 1) begin
|
||
fft_s <= 0;
|
||
fft_k <= 0;
|
||
fft_e <= 0;
|
||
fft_engine_calc_ended <= 0;
|
||
end
|
||
end
|
||
|
||
always_ff @(posedge clk or negedge rstn) begin
|
||
if (!rstn)
|
||
data_vld_p <= 0;
|
||
else if (release_en)
|
||
data_vld_p <= 1;
|
||
else
|
||
data_vld_p <= 0;
|
||
end
|
||
|
||
assign vld_o = data_vld_p;
|
||
|
||
always_comb begin
|
||
if (data_vld_p == 1 && fwd_i == 1) begin
|
||
// real_data = port2_rd_data[(DATA_WIDTH + FFT_POWER) * 2 - 1 : DATA_WIDTH + FFT_POWER];
|
||
// imag_data = port2_rd_data[(DATA_WIDTH + FFT_POWER) - 1 : 0];
|
||
real_data = port2_rd_data[(FRAC_BITS + DATA_WIDTH + FFT_POWER) * 2 - 1 : FRAC_BITS + FRAC_BITS + DATA_WIDTH + FFT_POWER];
|
||
imag_data = port2_rd_data[(FRAC_BITS + DATA_WIDTH + FFT_POWER) - 1 : FRAC_BITS];
|
||
end
|
||
else if (data_vld_p == 1 && fwd_i == 0) begin
|
||
// real_data = port2_rd_data[(DATA_WIDTH + FFT_POWER) - 1 : 0];
|
||
// imag_data = port2_rd_data[(DATA_WIDTH + FFT_POWER) * 2 - 1 : DATA_WIDTH + FFT_POWER];
|
||
real_data = port2_rd_data[(FRAC_BITS + DATA_WIDTH + FFT_POWER) - 1 : FRAC_BITS];
|
||
imag_data = port2_rd_data[(FRAC_BITS + DATA_WIDTH + FFT_POWER) * 2 - 1 : FRAC_BITS + FRAC_BITS + DATA_WIDTH + FFT_POWER];
|
||
end
|
||
else begin
|
||
real_data = '0;
|
||
imag_data = '0;
|
||
end
|
||
end
|
||
|
||
assign real_o = real_data;
|
||
assign imag_o = imag_data;
|
||
|
||
endmodule |