/*
 * $Id$
 *
 * [ Description ]
 * 
 * Test
 */

#define NB_ITERATION 1024

#include "Behavioural/Stage_1_Ifetch/Predictor/Meta_Predictor/Two_Level_Branch_Predictor/Branch_History_Table/SelfTest/include/test.h"
#include "Include/BitManipulation.h"
#include "Include/Test.h"

void test (string name,
	   morpheo::behavioural::stage_1_ifetch::predictor::meta_predictor::two_level_branch_predictor::branch_history_table::Parameters param)
{
  cout << "<" << name << "> : Simulation SystemC" << endl;

  try 
    {
      cout << param.print(1);
      param.test();
    }
  catch (morpheo::ErrorMorpheo & error)
    {
      cout << "<" << name << "> : " <<  error.what ();
      return;
    }
  catch (...)
    {
      cerr << "<" << name << "> : This test must generate a error" << endl;
      exit (EXIT_FAILURE);
    }

  Branch_History_Table * _Branch_History_Table = new Branch_History_Table (name.c_str(),
#ifdef STATISTICS
					     morpheo::behavioural::Parameters_Statistics(5,50),
#endif
					     param);
  
#ifdef SYSTEMC

  /*********************************************************************
   * Déclarations des signaux
   *********************************************************************/
  sc_clock                                 CLOCK ("clock", 1.0, 0.5);
  sc_signal<Tcontrol_t>                    PREDICT_VAL               [param._nb_prediction];
  sc_signal<Tcontrol_t>                    PREDICT_ACK               [param._nb_prediction];
  sc_signal<Taddress_t>                    PREDICT_ADDRESS           [param._nb_prediction];
  sc_signal<Thistory_t>                    PREDICT_HISTORY           [param._nb_prediction];
				           			     
  sc_signal<Tcontrol_t>                    BRANCH_COMPLETE_VAL       [param._nb_branch_complete];
  sc_signal<Tcontrol_t>                    BRANCH_COMPLETE_ACK       [param._nb_branch_complete];
  sc_signal<Taddress_t>                    BRANCH_COMPLETE_ADDRESS   [param._nb_branch_complete];
  sc_signal<Thistory_t>                    BRANCH_COMPLETE_HISTORY   [param._nb_branch_complete];
  sc_signal<Tcontrol_t>                    BRANCH_COMPLETE_DIRECTION [param._nb_branch_complete];
  
  /********************************************************
   * Instanciation
   ********************************************************/
  
  cout << "<" << name << "> Instanciation of _Branch_History_Table" << endl;
  
  (*(_Branch_History_Table->in_CLOCK))        (CLOCK);

  for (uint32_t i=0; i<param._nb_prediction; i++)
    {
      (*(_Branch_History_Table-> in_PREDICT_VAL     [i]))        (PREDICT_VAL     [i]);
      (*(_Branch_History_Table->out_PREDICT_ACK     [i]))        (PREDICT_ACK     [i]);
      (*(_Branch_History_Table-> in_PREDICT_ADDRESS [i]))        (PREDICT_ADDRESS [i]);
      (*(_Branch_History_Table->out_PREDICT_HISTORY [i]))        (PREDICT_HISTORY [i]);
    }

  for (uint32_t i=0; i<param._nb_branch_complete; i++)
    {
      (*(_Branch_History_Table-> in_BRANCH_COMPLETE_VAL       [i]))        (BRANCH_COMPLETE_VAL       [i]);
      (*(_Branch_History_Table->out_BRANCH_COMPLETE_ACK       [i]))        (BRANCH_COMPLETE_ACK       [i]);
      (*(_Branch_History_Table-> in_BRANCH_COMPLETE_ADDRESS   [i]))        (BRANCH_COMPLETE_ADDRESS   [i]);
      (*(_Branch_History_Table-> in_BRANCH_COMPLETE_HISTORY   [i]))        (BRANCH_COMPLETE_HISTORY   [i]);
      (*(_Branch_History_Table-> in_BRANCH_COMPLETE_DIRECTION [i]))        (BRANCH_COMPLETE_DIRECTION [i]);
    }

  /********************************************************
   * Simulation - Begin
   ********************************************************/

  cout << "<" << name << "> Start Simulation ............" << endl;
  // Initialisation
  sc_start(0);

  srand(0);
  //srand(time(NULL));

  
  _Branch_History_Table->vhdl_testbench_label("Initialisation");
  cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} Initialisation" << endl;

  for (uint32_t i=0; i<param._nb_prediction; i++)
    PREDICT_VAL         [i].write(0);
  for (uint32_t i=0; i<param._nb_branch_complete; i++)
    BRANCH_COMPLETE_VAL [i].write(0);

  // Initialisation of Register
  uint32_t   num_port_branch_complete = 0;
  uint32_t   num_port_predict         = 0;
  Taddress_t address                  = 0;
  Thistory_t history                  = 0;
  Thistory_t mask                     = gen_mask <Thistory_t> (param._size_shifter);
  Tcontrol_t direction                = 0;

  while (address<param._nb_shifter)
    {
      BRANCH_COMPLETE_VAL       [0].write(1);
      BRANCH_COMPLETE_ADDRESS   [0].write(address);
      BRANCH_COMPLETE_HISTORY   [0].write(0);
      BRANCH_COMPLETE_DIRECTION [0].write(0);

      sc_start(1);
      
      if (BRANCH_COMPLETE_ACK[0].read()==1)
	address++;
    }
 
  BRANCH_COMPLETE_VAL     [0].write(0);
  sc_start(0);

  _Branch_History_Table->vhdl_testbench_label("Loop of Test");
  cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} Loop of Test" << endl;

  for (uint32_t iteration=0; iteration<NB_ITERATION; iteration ++)
    {
      _Branch_History_Table->vhdl_testbench_label("Iteration "+toString(iteration));
      num_port_branch_complete = rand() % param._nb_branch_complete;
      num_port_predict         = rand() % param._nb_prediction     ;
      address                  = rand() % param._nb_shifter        ;
      history                  = rand() % (1<<param._size_shifter) ;
      direction                = rand() % 2;

      cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} ["+toString(num_port_branch_complete)+"]" << endl
	   << hex
	   << " - address     : " << address  << endl
	   << " - history old : " << history  << endl
	   << " - direction   : " << direction<< endl;

      BRANCH_COMPLETE_VAL       [num_port_branch_complete].write(1);
      BRANCH_COMPLETE_ADDRESS   [num_port_branch_complete].write(address);
      BRANCH_COMPLETE_HISTORY   [num_port_branch_complete].write(history);
      BRANCH_COMPLETE_DIRECTION [num_port_branch_complete].write(direction);

      // Wait Ack
      do
	{
	  sc_start(1);
	}
      while (BRANCH_COMPLETE_ACK[num_port_branch_complete].read()!=1);

      BRANCH_COMPLETE_VAL     [num_port_branch_complete].write(0);
      PREDICT_VAL             [num_port_predict        ].write(1);
      PREDICT_ADDRESS         [num_port_predict        ].write(address);
      
      history                  = ((history<<1)&mask)|direction;
      
      // Wait Ack
      do
	{
	  sc_start(1);
	}
      while (PREDICT_ACK        [num_port_predict        ].read()!=1);

      PREDICT_VAL             [num_port_predict        ].write(0);

      cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} ["+toString(num_port_predict)+"]" << endl
	   << " - history new : " << PREDICT_HISTORY [num_port_predict].read() << endl;

      TEST(Thistory_t,PREDICT_HISTORY [num_port_predict].read(),history);

      cout << dec;
    }

  /********************************************************
   * Simulation - End
   ********************************************************/

  cout << "<" << name << "> ............ Stop Simulation" << endl;

#endif

  delete _Branch_History_Table;
}
