/* 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