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

#define NB_ITERATION 512

#include "Behavioural/Core/Multi_Front_end/Front_end/Prediction_unit/Direction/Meta_Predictor/Two_Level_Branch_Predictor/SelfTest/include/test.h"
#include "Common/include/Test.h"
#include "Common/include/BitManipulation.h"
void test (string name,
	   morpheo::behavioural::core::multi_front_end::front_end::prediction_unit::direction::meta_predictor::two_level_branch_predictor::Parameters param)
{
  cout << "<" << name << "> : Simulation SystemC" << endl;

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

  /*********************************************************************
   * Déclarations des signaux
   *********************************************************************/
  sc_clock                              *  CLOCK;
  sc_signal<Tcontrol_t>                 *  NRESET;

  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<Tbht_history_t>             *  PREDICT_BHT_HISTORY         [param._nb_prediction];
  sc_signal<Tpht_history_t>             *  PREDICT_PHT_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<Tbht_history_t>             *  BRANCH_COMPLETE_BHT_HISTORY [param._nb_branch_complete];
  sc_signal<Tpht_history_t>             *  BRANCH_COMPLETE_PHT_HISTORY [param._nb_branch_complete];
  sc_signal<Tcontrol_t>                 *  BRANCH_COMPLETE_DIRECTION   [param._nb_branch_complete];

  // Rename signal

  string rename;

  CLOCK  = new sc_clock              ("clock", 1.0, 0.5);
  NRESET = new sc_signal<Tcontrol_t> ("NRESET");

  for (uint32_t i=0; i<param._nb_prediction; i++)
    {
      rename = "PREDICT_VAL_"        +toString(i);
      PREDICT_VAL                 [i] = new sc_signal<Tcontrol_t>     (rename.c_str());
      rename = "PREDICT_ACK_"        +toString(i);
      PREDICT_ACK                 [i] = new sc_signal<Tcontrol_t>     (rename.c_str());
      rename = "PREDICT_ADDRESS_"    +toString(i);
      PREDICT_ADDRESS             [i] = new sc_signal<Taddress_t>     (rename.c_str());
      rename = "PREDICT_BHT_HISTORY_"+toString(i);
      PREDICT_BHT_HISTORY         [i] = new sc_signal<Tbht_history_t> (rename.c_str());
      rename = "PREDICT_PHT_HISTORY_"+toString(i);
      PREDICT_PHT_HISTORY         [i] = new sc_signal<Tpht_history_t> (rename.c_str());
    }
  
  for (uint32_t i=0; i<param._nb_branch_complete; i++)
    {
      rename = "BRANCH_COMPLETE_VAL_"        +toString(i);
      BRANCH_COMPLETE_VAL         [i] = new sc_signal<Tcontrol_t>     (rename.c_str());
      rename = "BRANCH_COMPLETE_ACK_"        +toString(i);
      BRANCH_COMPLETE_ACK         [i] = new sc_signal<Tcontrol_t>     (rename.c_str());
      rename = "BRANCH_COMPLETE_ADDRESS_"    +toString(i);
      BRANCH_COMPLETE_ADDRESS     [i] = new sc_signal<Taddress_t>     (rename.c_str());
      rename = "BRANCH_COMPLETE_BHT_HISTORY_"+toString(i);
      BRANCH_COMPLETE_BHT_HISTORY [i] = new sc_signal<Tbht_history_t> (rename.c_str());
      rename = "BRANCH_COMPLETE_PHT_HISTORY_"+toString(i);
      BRANCH_COMPLETE_PHT_HISTORY [i] = new sc_signal<Tpht_history_t> (rename.c_str());
      rename = "BRANCH_COMPLETE_DIRECTION_"  +toString(i);
      BRANCH_COMPLETE_DIRECTION   [i] = new sc_signal<Tcontrol_t>     (rename.c_str());
    }

  /********************************************************
   * Instanciation
   ********************************************************/
  
  cout << "<" << name << "> Instanciation of _Two_Level_Branch_Predictor" << endl;
  
  (*(_Two_Level_Branch_Predictor->in_CLOCK ))        (*(CLOCK ));
  (*(_Two_Level_Branch_Predictor->in_NRESET))        (*(NRESET));
  
  for (uint32_t i=0; i<param._nb_prediction; i++)
    {
      (*(_Two_Level_Branch_Predictor-> in_PREDICT_VAL                 [i])) (*(PREDICT_VAL                  [i]));
      (*(_Two_Level_Branch_Predictor->out_PREDICT_ACK                 [i])) (*(PREDICT_ACK                  [i]));
      (*(_Two_Level_Branch_Predictor-> in_PREDICT_ADDRESS             [i])) (*(PREDICT_ADDRESS              [i]));
      if (param._have_bht)						       				       
      (*(_Two_Level_Branch_Predictor->out_PREDICT_BHT_HISTORY         [i])) (*(PREDICT_BHT_HISTORY          [i]));
      if (param._have_pht)
      (*(_Two_Level_Branch_Predictor->out_PREDICT_PHT_HISTORY         [i])) (*(PREDICT_PHT_HISTORY          [i]));
    }

    for (uint32_t i=0; i<param._nb_branch_complete; i++)
      {
	(*(_Two_Level_Branch_Predictor-> in_BRANCH_COMPLETE_VAL         [i])) (*(BRANCH_COMPLETE_VAL          [i]));
	(*(_Two_Level_Branch_Predictor->out_BRANCH_COMPLETE_ACK         [i])) (*(BRANCH_COMPLETE_ACK          [i]));
	(*(_Two_Level_Branch_Predictor-> in_BRANCH_COMPLETE_ADDRESS     [i])) (*(BRANCH_COMPLETE_ADDRESS      [i]));
	if (param._have_bht)						       					
	(*(_Two_Level_Branch_Predictor-> in_BRANCH_COMPLETE_BHT_HISTORY [i])) (*(BRANCH_COMPLETE_BHT_HISTORY  [i]));
	if (param._have_pht)						       					
	(*(_Two_Level_Branch_Predictor-> in_BRANCH_COMPLETE_PHT_HISTORY [i])) (*(BRANCH_COMPLETE_PHT_HISTORY  [i]));
	(*(_Two_Level_Branch_Predictor-> in_BRANCH_COMPLETE_DIRECTION   [i])) (*(BRANCH_COMPLETE_DIRECTION    [i]));
      }

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

  cout << "<" << name << "> Start Simulation ............" << endl;
  // Initialisation

  const uint32_t seed = 0;
//const uint32_t seed = static_cast<uint32_t>(time(NULL));

  srand(seed);

  const uint32_t   num_port_predict         = rand()%param._nb_prediction;
  const uint32_t   num_port_branch_complete = rand()%param._nb_branch_complete;
  const Taddress_t address                  = rand()%param._size_address;
        Tcontrol_t direction;

  sc_start(0);

  NRESET->write(0);
  sc_start(5);
  NRESET->write(1);

  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);

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

  // Step 1
  BRANCH_COMPLETE_VAL          [num_port_branch_complete]->write(1);
  BRANCH_COMPLETE_DIRECTION    [num_port_branch_complete]->write(0);

  if (param._have_pht)						       					
    {
      // Step 1.1 -> pattern_history_table

      // Step 1.1.1 compute number of group.
      uint32_t size_address_shift=0;

      if (param._have_bht)
	size_address_shift=param._param_two_level_branch_predictor_glue->_pht_size_address_shift;

      uint32_t nb_group        = (1<<size_address_shift);
      uint32_t nb_reg_by_group = (1<<(param._pht_size_address-size_address_shift));

//       cout << "pht_size_address           : " << param._pht_size_address << endl;
//       cout << "size_address_shift         : " << size_address_shift << endl;
//       cout << "nb_group                   : " << nb_group           << endl;
//       cout << "nb_reg_by_group            : " << nb_reg_by_group    << endl;

      cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} Init pht" << endl;
      
      BRANCH_COMPLETE_PHT_HISTORY  [num_port_branch_complete]->write(0);

      // Step 1.1.2 for all group ...
      for (uint32_t i=0; i<nb_group; i++)
	{
	  if (param._have_bht)
	    BRANCH_COMPLETE_BHT_HISTORY  [num_port_branch_complete]->write(i);

	  for (uint32_t j=0; j<nb_reg_by_group; j++)
	    {
	      cout << "j : " << j << " on " << nb_reg_by_group << endl;
	      BRANCH_COMPLETE_ADDRESS [num_port_branch_complete]->write(j);

	      sc_start(1);

	      // wait ackwolegde
	      while (BRANCH_COMPLETE_ACK [num_port_branch_complete] -> read() == 0)
		{
		  sc_start(1);
		}
	    }
	}
    }

  if (param._have_bht)						       					
    {
      // Step 1.2 -> branch_history_table
      BRANCH_COMPLETE_BHT_HISTORY  [num_port_branch_complete]->write(0);
      
      cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} Init bht" << endl;
      
      for (uint32_t i=0; i<param._bht_nb_shifter; i++)
	{
	  BRANCH_COMPLETE_ADDRESS [num_port_branch_complete]->write(i);

	  sc_start(1);
	}

      // wait ackwolegde
      while (BRANCH_COMPLETE_ACK [num_port_branch_complete] -> read() == 0)
	{
	  sc_start(1);
	}
    }

  // Step 2
  BRANCH_COMPLETE_VAL          [num_port_branch_complete]->write(0);
  PREDICT_ADDRESS              [num_port_predict        ]->write(address);
  BRANCH_COMPLETE_ADDRESS      [num_port_branch_complete]->write(address);

  cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} Loop of Test" << endl
       << " * predict_address : " << hex << address << dec << endl;
  
  // A lot of prediction
  Tbht_history_t bht_history = 0;
  Tpht_history_t pht_history [1<<param._bht_size_shifter];
  
  for (uint32_t i=0; i<static_cast<uint32_t>(1<<param._bht_size_shifter); i++)
    pht_history [i] = 0;

  for (uint32_t iteration=0; iteration<NB_ITERATION; iteration ++)
    {
      cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} Predict          : bht_history " << bht_history << " - pht_history " << pht_history[bht_history] << endl; 


      // Ask a new prediction
      PREDICT_VAL                  [num_port_predict        ]->write(1);

      sc_start(1);

      // wait ackwolegde
      while (PREDICT_ACK [num_port_predict] -> read() == 0)
	{
	  sc_start(1);
	}
      PREDICT_ACK                  [num_port_predict        ]->write(0);

      sc_start(0);

      // Test
      cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} Predict (Test)   : " << endl; 
      if (param._have_bht)
	{
	  cout << "\tbht_history " << PREDICT_BHT_HISTORY[num_port_predict]->read() << endl;
	  TEST(Tbht_history_t,bht_history             ,PREDICT_BHT_HISTORY[num_port_predict]->read());
	}
      if (param._have_pht)
	{
	  cout << "\tpht_history " << PREDICT_PHT_HISTORY[num_port_predict]->read() << endl;
	  TEST(Tpht_history_t,pht_history[bht_history],PREDICT_PHT_HISTORY[num_port_predict]->read());
	}

      // update
      direction = ((rand()%2)==1);

      BRANCH_COMPLETE_VAL          [num_port_branch_complete]->write(1);
      if (param._have_bht)
      BRANCH_COMPLETE_BHT_HISTORY  [num_port_branch_complete]->write(bht_history             );


      if (param._have_pht)
      BRANCH_COMPLETE_PHT_HISTORY  [num_port_branch_complete]->write(pht_history[bht_history]);
      BRANCH_COMPLETE_DIRECTION    [num_port_branch_complete]->write(direction);

      if (param._have_pht)
      if (direction == 0)
	{
	  if (pht_history[bht_history] > 0)
	    pht_history[bht_history] --;
	}
      else
	{
	  if (pht_history[bht_history] < (static_cast<Tpht_history_t>(1<<param._pht_size_counter)-1))
	    pht_history[bht_history] ++;
	}

      if (param._have_bht)
      bht_history              = (((bht_history) << 1) | direction) & gen_mask<Tbht_history_t>(param._bht_size_shifter);

      sc_start(1);

      // wait ackwolegde
      while (BRANCH_COMPLETE_ACK [num_port_branch_complete] -> read() == 0)
	{
	  sc_start(1);
	}
      BRANCH_COMPLETE_VAL          [num_port_branch_complete]->write(0);
    }

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

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

#endif

  delete _Two_Level_Branch_Predictor;
}
