`timescale 1 ns/1 ps
import test_pkg::*;

module FFT_top_BFM
      (
       input  t_DATA   DATA_out,      // output from FFT_top - serial data
       output t_DATA   DATA_in,       // input to FFT_top - serial data
       output wire     clk,           // main clock domain, used for counting FFT
       output wire     clk_data_en,   // clock signal, drives DATA_in sending
       output wire     rst            // system reset
      );

  logic    clk_en = 0;                // enable clk generator
  logic    clk_var = 0;               // clk signal
  logic    clk_data_en_en = 0;        // enable clk_data_en generator
  logic    clk_data_en_var = 0;       // clk_data_en signal
  logic    load_en_var = 0;           // load_en signal
  logic    reset = 0;                 // rst signal
  t_DATA   DATA_in_var = 0;           // data_in signal

  time     clk_per = c_CLK_PER;       // period of clk signal
  int      data_en_per = c_DATA_EN_AFTER_CLK_PER; 
                                      // number of periods of clk_data_en signal


 // enable clk generator
 task run_clk (integer clk_run); // 1 - enable
                                 // 0 - disable
  if (clk_run) clk_en = 1;
  else  clk_en = 0;
 endtask


 // set clk and clk_data_en period
 task set_clk_per (time clk_period,
                   int  clk_data_en_period);
  clk_per = clk_period;
  data_en_per = clk_data_en_period;
 endtask


 // perform reset, reset is active for 1 clk period
 task perform_reset (int synch_to_negedge); // 1 - synch to clk negedge
                                            // 0 - asynch release after 2 clk_per
   reset = 0;
  #(clk_per)
   reset = 1;
  #(clk_per)
  if (synch_to_negedge == 1)
   @ (negedge clk_var);
   reset = 0;
 endtask

 // send and receive data to/from FFT module
 task send_and_receive_data(
       input  t_DATA_ARRAY send_data_re_array,
       input  t_DATA_ARRAY send_data_im_array,
       output t_DATA_ARRAY received_data_array[1:0]);

  // 1. enable clk_data_en generator on falling edge of clk

   @ (posedge(clk_var))
    clk_data_en_en = 1;
  // 2. sending data
  for (int i = 0; i < c_FFT_SIZE; i++)
   begin
  // set Data_in defined in send_data_array on rising edge of clk_data_en
     @ (posedge(clk_data_en_var));
     DATA_in_var = send_data_re_array [i];
   end

  // 3. wait one count cycle
  for (int i = 0; i < c_FFT_SIZE -1; i++)
   begin
      @ (posedge(clk_data_en_var));
      DATA_in_var = 'd0; //send_data_re_array [i];
   end

  // 4. receiving data from FFT module
  for (int i = 0; i < c_FFT_SIZE; i++)  //i = 1
   begin
  //  sample output data when rising edge of clk_data_en
  //  and save them into received_data_array
  @ (posedge(clk_data_en_var));
      DATA_in_var = 'd0;
  @ (posedge clk_var);
  @ (negedge clk_var);
      received_data_array [0][i] = DATA_out;
   end

  // 5. disable clk_data_en
     @(negedge(clk_data_en_var))
     clk_data_en_en = 0;

  endtask


 // clk generator
 always
  begin
   if (clk_en) begin
      clk_var = 1'b1;
     # (clk_per/2);
      clk_var = 1'b0;
     # (clk_per/2);
   end else begin
      clk_var = 1'b0;
    @ (clk_en);
   end
  end


 // clk_data_en generator, synchronize with rising edge of clk 
 always
  begin
  @ (posedge(clk_var));
   while (clk_data_en_en)
    begin
     @ (negedge(clk_var));
      clk_data_en_var = 1'b1;
     # (clk_per);
      clk_data_en_var = 1'b0;
     repeat (data_en_per - 1)
       @ (posedge clk_var);
    end
     clk_data_en_var = 1'b0;
  end


 // output assignment, generated signals are assgined with output signals
 assign rst = reset;
 assign clk = clk_var;
 assign clk_data_en = clk_data_en_var;
 assign DATA_in = DATA_in_var;
endmodule