`timescale 1 ns/1 ps
import test_pkg::*;
`include "ref_fft.sv"
program testcase;
// module testcase;


task init;
 FFT_top_testbench.BFM.set_clk_per(100ns, 10);
 FFT_top_testbench.BFM.run_clk(1);
 FFT_top_testbench.BFM.perform_reset(1);
endtask : init

task randomize_data(
     output t_DATA_ARRAY data_bit_re,
     output t_DATA_ARRAY data_bit_im,
     output t_COMPLEX data_compl [c_FFT_SIZE - 1 : 0]
                    );
     int rand_re;
     int rand_im;

 for (int a = 0; a < c_FFT_SIZE; a++)
  begin
  // generate real part of input data
   rand_re = $random%(2**(c_DATA_WIDTH - 1));
   data_compl[a].r = real'(rand_re)/real'(2**(c_DATA_WIDTH - 1));
   data_bit_re [a] =  t_DATA'(rand_re);

  // generate imaginary part of input data
  if(c_COMPLEX_EN == 1)
   rand_im = $random%(2**(c_DATA_WIDTH - 1));
  else
   rand_im = 0;

   data_compl[a].i = real'(rand_im)/real'(2**(c_DATA_WIDTH - 1));
   data_bit_im [a] =  t_DATA'(rand_im);
  end

  if (c_VERBOSE > 1)
   begin
    for (int i =0; i< c_FFT_SIZE; i++)
     begin
       $write("DEBUG randomize: Data randomization: pos:%2d.: model stimuli: re =%5f,im= %5f, design stimuli: re =%5d, im=%5d.\n",
         i, data_compl[i].r, data_compl[i].i, int'(data_bit_re[i]), int'(data_bit_im[i]));
     end
  end
 endtask

// task send_receive_data
// 1.  Build refernece model object (call constructor new in ref_fft class)
// 2.  Generate reference output in real type
// 3.  Send generated data into DUT (call task send_and_receive_data in BFM module)
task send_receive_data( input  t_COMPLEX data_real_in  [c_FFT_SIZE - 1 : 0],
                        output t_COMPLEX data_real_out [c_FFT_SIZE - 1 : 0],
                        input  t_DATA_ARRAY data_bit_re_in,
                        input  t_DATA_ARRAY data_bit_im_in,
                        output t_DATA_ARRAY data_bit_out[1:0]);

  automatic ref_fft ref_model = new();
  ref_model.run_FFT (data_real_in, data_real_out);
  FFT_top_testbench.BFM.send_and_receive_data (data_bit_re_in,
                                               data_bit_im_in,
                                               data_bit_out[1:0]);

endtask

// Check received data - compare with reference model result
task check_data (input int run,
                 input t_COMPLEX data_real_out [c_FFT_SIZE - 1 : 0],
                 input t_DATA_ARRAY data_bit_out[1:0]);

 t_COMPLEX data_bit2real [c_FFT_SIZE - 1 : 0];
 automatic int ind = 0;

 // transform received data
 if (c_DEBUG == 0) // do not use, used only for ver env debug 
  begin
    data_bit2real[0].r = conv_bit2real(data_bit_out[0][0]);
    data_bit2real[0].i = 0.0;
   for (int i = 1; i < c_FFT_SIZE/2; i++)
    begin
     data_bit2real[i].r = conv_bit2real(data_bit_out[0][i + ind    ]);
     data_bit2real[i].i = conv_bit2real(data_bit_out[0][i + ind + 1]);

     data_bit2real[c_FFT_SIZE - i].r = data_bit2real[i].r;
     data_bit2real[c_FFT_SIZE - i].i = -data_bit2real[i].i;
     ind++;
    end
   data_bit2real[c_FFT_SIZE/2].r = conv_bit2real(data_bit_out[0][c_FFT_SIZE-1]);
   data_bit2real[c_FFT_SIZE/2].i = 0.0;
  end
 else
  begin  
   for (int i = 0; i < c_FFT_SIZE-1; i++)
    begin
     data_bit2real[i].r =conv_bit2real(data_bit_out[i][0]); //FIXME
     data_bit2real[i].i =conv_bit2real(data_bit_out[i][1]); //FIXME
    end
 end

 // compare data
  if (c_VERBOSE == 0) 
   begin
   for (int position = 0; position < c_FFT_SIZE; position ++)
    begin
     if ((data_real_out[position].r > data_bit2real[position].r + c_TOLERANCE) ||
         (data_real_out[position].r < data_bit2real[position].r - c_TOLERANCE))
       begin
         $write("ERROR check_data: %2d. run, %3d position, wrong real data received, reference model result: %f, received data: %f.\n",
                run, position, data_real_out[position].r, data_bit2real[position].r);
       end
     if ((data_real_out[position].i > data_bit2real[position].i + c_TOLERANCE) ||
         (data_real_out[position].i < data_bit2real[position].i - c_TOLERANCE))
       begin
         $write("ERROR check_data: %2d. run, %3d position, wrong complex data received, reference model result: %f, received data: %f.\n",
                run, position, data_real_out[position].i, data_bit2real[position].i);
       end
    end
   end

  // Print out received data and expected data.
  if (c_VERBOSE > 0) 
   begin
   for (int position = 0; position < c_FFT_SIZE; position ++)
    begin

         $write("DEBUG check_data: %2d. run, position: %3d, real data received, ref model result: %f, received data: %f.\n",
                run, position, data_real_out[position].r, data_bit2real[position].r);

         $write("DEBUG check_data: %2d. run, position: %3d, cmpl data received, ref model result: %f, received data: %f.\n",
                run, position, data_real_out[position].i, data_bit2real[position].i);
    end
   end

endtask


task run_random_data_test (int run);
 t_DATA_ARRAY data_bit_re_in;
 t_DATA_ARRAY data_bit_im_in;
 t_DATA_ARRAY data_bit_out[1:0];
 t_COMPLEX data_real_in  [c_FFT_SIZE - 1 : 0];
 t_COMPLEX data_real_out [c_FFT_SIZE - 1 : 0];
 
   randomize_data(data_bit_re_in, data_bit_im_in, data_real_in);
   send_receive_data (data_real_in,    data_real_out,
                      data_bit_re_in,  data_bit_im_in,
                      data_bit_out[1:0]);

   check_data (run, data_real_out, data_bit_out[1:0]);
endtask

initial
 begin
 init;
 for (int run = 0; run < 50; run ++)
  begin
    #10us;
    run_random_data_test (run+1);
    #10us;
  end
  FFT_top_testbench.BFM.run_clk(0);
 end

endprogram
// endmodule