Files
gps/hardware/external/gps/rtl/acq_src/fft_radix_2.sv
T
2026-07-12 11:01:51 +03:00

380 lines
12 KiB
Systemverilog
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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