/*
 * $Id: test.cpp 88 2008-12-10 18:31:39Z rosiere $
 *
 * [ Description ]
 * 
 * Test
 */

#define NB_ITERATION  1
#define CYCLE_MAX     (10000*NB_ITERATION)

#include "Behavioural/Core/Multi_Front_end/Front_end/Prediction_unit/Branch_Target_Buffer/SelfTest/include/test.h"
#include "Common/include/Test.h"
#include "Behavioural/include/Allocation.h"
#include "Behavioural/Core/Multi_Front_end/Front_end/Prediction_unit/Branch_Target_Buffer/Branch_Target_Buffer_Register/SelfTest/include/test_BTB.h"

void test (string name,
	   morpheo::behavioural::core::multi_front_end::front_end::prediction_unit::branch_target_buffer::Parameters * _param)
{
  msg(_("<%s> : Simulation SystemC.\n"),name.c_str());

#ifdef STATISTICS
  morpheo::behavioural::Parameters_Statistics * _parameters_statistics = new morpheo::behavioural::Parameters_Statistics (5,CYCLE_MAX);
#endif

  Tusage_t _usage = USE_ALL;

//   _usage = usage_unset(_usage,USE_SYSTEMC              );
//   _usage = usage_unset(_usage,USE_VHDL                 );
//   _usage = usage_unset(_usage,USE_VHDL_TESTBENCH       );
//   _usage = usage_unset(_usage,USE_VHDL_TESTBENCH_ASSERT);
//   _usage = usage_unset(_usage,USE_POSITION             );
//   _usage = usage_unset(_usage,USE_STATISTICS           );
//   _usage = usage_unset(_usage,USE_INFORMATION          );

  Branch_Target_Buffer * _Branch_Target_Buffer = new Branch_Target_Buffer 
    (name.c_str(),
#ifdef STATISTICS
     _parameters_statistics,
#endif
     _param,
     _usage);

#ifdef SYSTEMC
  /*********************************************************************
   * Déclarations des signaux
   *********************************************************************/
  string rename;

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

  ALLOC1_SC_SIGNAL( in_PREDICT_VAL           ," in_PREDICT_VAL           ",Tcontrol_t         ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL(out_PREDICT_ACK           ,"out_PREDICT_ACK           ",Tcontrol_t         ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL( in_PREDICT_CONTEXT_ID    ," in_PREDICT_CONTEXT_ID    ",Tcontext_t         ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL( in_PREDICT_ADDRESS       ," in_PREDICT_ADDRESS       ",Tgeneral_data_t    ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL(out_PREDICT_HIT           ,"out_PREDICT_HIT           ",Tcontrol_t         ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL(out_PREDICT_ADDRESS_SRC   ,"out_PREDICT_ADDRESS_SRC   ",Tgeneral_data_t    ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL(out_PREDICT_ADDRESS_DEST  ,"out_PREDICT_ADDRESS_DEST  ",Tgeneral_data_t    ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL(out_PREDICT_CONDITION     ,"out_PREDICT_CONDITION     ",Tbranch_condition_t,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL(out_PREDICT_LAST_TAKE     ,"out_PREDICT_LAST_TAKE     ",Tcontrol_t         ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL(out_PREDICT_IS_ACCURATE   ,"out_PREDICT_IS_ACCURATE   ",Tcontrol_t         ,_param->_nb_inst_predict);
  ALLOC1_SC_SIGNAL( in_DECOD_VAL             ," in_DECOD_VAL             ",Tcontrol_t         ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL(out_DECOD_ACK             ,"out_DECOD_ACK             ",Tcontrol_t         ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_DECOD_CONTEXT_ID      ," in_DECOD_CONTEXT_ID      ",Tcontext_t         ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_DECOD_ADDRESS_SRC     ," in_DECOD_ADDRESS_SRC     ",Tgeneral_data_t    ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_DECOD_ADDRESS_DEST    ," in_DECOD_ADDRESS_DEST    ",Tgeneral_data_t    ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_DECOD_CONDITION       ," in_DECOD_CONDITION       ",Tbranch_condition_t,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_DECOD_LAST_TAKE       ," in_DECOD_LAST_TAKE       ",Tcontrol_t         ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_DECOD_MISS_PREDICTION ," in_DECOD_MISS_PREDICTION ",Tcontrol_t         ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_DECOD_IS_ACCURATE     ," in_DECOD_IS_ACCURATE     ",Tcontrol_t         ,_param->_nb_inst_decod);
  ALLOC1_SC_SIGNAL( in_UPDATE_VAL            ," in_UPDATE_VAL            ",Tcontrol_t         ,_param->_nb_inst_update);
  ALLOC1_SC_SIGNAL(out_UPDATE_ACK            ,"out_UPDATE_ACK            ",Tcontrol_t         ,_param->_nb_inst_update);
  ALLOC1_SC_SIGNAL( in_UPDATE_CONTEXT_ID     ," in_UPDATE_CONTEXT_ID     ",Tcontext_t         ,_param->_nb_inst_update);
  ALLOC1_SC_SIGNAL( in_UPDATE_ADDRESS_SRC    ," in_UPDATE_ADDRESS_SRC    ",Tgeneral_data_t    ,_param->_nb_inst_update);
  ALLOC1_SC_SIGNAL( in_UPDATE_ADDRESS_DEST   ," in_UPDATE_ADDRESS_DEST   ",Tgeneral_data_t    ,_param->_nb_inst_update);
  ALLOC1_SC_SIGNAL( in_UPDATE_CONDITION      ," in_UPDATE_CONDITION      ",Tbranch_condition_t,_param->_nb_inst_update);
  ALLOC1_SC_SIGNAL( in_UPDATE_LAST_TAKE      ," in_UPDATE_LAST_TAKE      ",Tcontrol_t         ,_param->_nb_inst_update);
  ALLOC1_SC_SIGNAL( in_UPDATE_MISS_PREDICTION," in_UPDATE_MISS_PREDICTION",Tcontrol_t         ,_param->_nb_inst_update);
  
  /********************************************************
   * Instanciation
   ********************************************************/
  
  msg(_("<%s> : Instanciation of _Branch_Target_Buffer.\n"),name.c_str());

  (*(_Branch_Target_Buffer->in_CLOCK))        (*(in_CLOCK));
  (*(_Branch_Target_Buffer->in_NRESET))       (*(in_NRESET));

  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_PREDICT_VAL           ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_PREDICT_ACK           ,_param->_nb_inst_predict);
  if (_param->_have_port_context_id)
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_PREDICT_CONTEXT_ID    ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_PREDICT_ADDRESS       ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_PREDICT_HIT           ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_PREDICT_ADDRESS_SRC   ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_PREDICT_ADDRESS_DEST  ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_PREDICT_CONDITION     ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_PREDICT_LAST_TAKE     ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_PREDICT_IS_ACCURATE   ,_param->_nb_inst_predict);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_VAL             ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_DECOD_ACK             ,_param->_nb_inst_decod);
  if (_param->_have_port_context_id)
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_CONTEXT_ID      ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_ADDRESS_SRC     ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_ADDRESS_DEST    ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_CONDITION       ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_LAST_TAKE       ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_MISS_PREDICTION ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_DECOD_IS_ACCURATE     ,_param->_nb_inst_decod);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_UPDATE_VAL            ,_param->_nb_inst_update);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer,out_UPDATE_ACK            ,_param->_nb_inst_update);
  if (_param->_have_port_context_id)
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_UPDATE_CONTEXT_ID     ,_param->_nb_inst_update);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_UPDATE_ADDRESS_SRC    ,_param->_nb_inst_update);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_UPDATE_ADDRESS_DEST   ,_param->_nb_inst_update);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_UPDATE_CONDITION      ,_param->_nb_inst_update);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_UPDATE_LAST_TAKE      ,_param->_nb_inst_update);
  INSTANCE1_SC_SIGNAL(_Branch_Target_Buffer, in_UPDATE_MISS_PREDICTION,_param->_nb_inst_update);

  msg(_("<%s> : Start Simulation ............\n"),name.c_str());
    
  // Initialisation
  const uint32_t seed = 0;
//const uint32_t seed = static_cast<uint32_t>(time(NULL));

  srand(seed);

  const  int32_t percent_transaction_decod   = 75;
  const  int32_t percent_transaction_predict = 75;

  SC_START(0);

  Time * _time = new Time();

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

  {
    LABEL("Reset");
    in_NRESET->write(0);
    SC_START(5);
    in_NRESET->write(1);  
    
    LABEL("Loop of Test");
    
    Taddress_t btb_address [_param->_associativity];
    Tcontext_t btb_context [_param->_associativity];

    for (uint32_t iteration=0; iteration<NB_ITERATION; iteration ++)
      {
	LABEL("Iteration %d",iteration);

	// FIRST STEP - Test (without sort, with concurent access, same context)

	// fill a btb line
#define NB_REQUEST (8*_param->_associativity)
	uint32_t   nb_request;
	uint32_t   index      = rand()&_param->_param_branch_target_buffer_register->_mask_bank;
	Tcontext_t context    = rand()&_param->_nb_context;

	for (uint32_t j=0; j<_param->_associativity; j++)
// 	  btb_val     [j] = false;
	  btb_address [j] = static_cast<Taddress_t>(-1);

	{
	  uint32_t associativity = 0;
	  
	  while (associativity < _param->_associativity)
	    {
	      for (uint32_t j=0; j<_param->_nb_inst_decod; j++)
		{
		  Taddress_t address = ((associativity << _param->_param_branch_target_buffer_register->_shift_tag) |
					(index         << _param->_param_branch_target_buffer_register->_shift_bank)|
					(0));
		  
		  in_DECOD_VAL             [j]->write(((rand()%100)<percent_transaction_decod) and ((associativity+j) < _param->_associativity));
		  in_DECOD_CONTEXT_ID      [j]->write(context);
		  in_DECOD_ADDRESS_SRC     [j]->write( address );
		  in_DECOD_ADDRESS_DEST    [j]->write(~address );
		  in_DECOD_CONDITION       [j]->write(BRANCH_CONDITION_FLAG_SET);
		  in_DECOD_LAST_TAKE       [j]->write(address&1);
		  in_DECOD_MISS_PREDICTION [j]->write(1);
		  in_DECOD_IS_ACCURATE     [j]->write(1);
		}
	      
	      SC_START(0);
	      
	      for (uint32_t j=0; j<_param->_nb_inst_decod; j++)
		if (in_DECOD_VAL [j]->read() and out_DECOD_ACK [j]->read())
		  {
		    LABEL("DECOD     [%d] Transaction Accepted",j);
		    
		    Taddress_t address = in_DECOD_ADDRESS_SRC [j]->read();
		    
		    btb_address[associativity] = address;
		    btb_context[associativity] = context;
		    associativity ++;
		  }
	      
	      SC_START(1);
	    }
	}
	
	nb_request = 0;

	while (nb_request < NB_REQUEST)
	  {
	    for (uint32_t i=0; i<_param->_nb_inst_predict; i++)
	      {
		in_PREDICT_VAL        [i]->write(((rand()%100)<percent_transaction_predict));
		in_PREDICT_CONTEXT_ID [i]->write(rand()&_param->_nb_context);
		in_PREDICT_ADDRESS    [i]->write(gen_addr(_param->_param_branch_target_buffer_register,index));
	      }

	    SC_START(0);

	    for (uint32_t i=0; i<_param->_nb_inst_predict; i++)
	      if (in_PREDICT_VAL [i]->read() and out_PREDICT_ACK [i]->read())
		{
		  LABEL("PREDICT   [%d] Transaction Accepted",i);
		  
		  Tcontrol_t hit          = out_PREDICT_HIT          [i]->read();
		  Taddress_t address      =  in_PREDICT_ADDRESS      [i]->read();
		  Taddress_t address_src  = out_PREDICT_ADDRESS_SRC  [i]->read();
		  Taddress_t address_dest = out_PREDICT_ADDRESS_DEST [i]->read();

		  Taddress_t tag          = address >> _param->_param_branch_target_buffer_register->_shift_tag;
		  
		  Tcontrol_t my_hit = false;
		  Tcontrol_t my_addr;

		  for (uint32_t i=0; i<_param->_associativity; i++)
		    {
		      Taddress_t btb_address [_param->_associativity];
		      Tcontext_t btb_context [_param->_associativity];
		    }
		  
		  if (hit)
		    {
		      TEST(Tbranch_condition_t,out_PREDICT_CONDITION   [i]->read(),BRANCH_CONDITION_FLAG_SET);
		      TEST(Tcontrol_t         ,out_PREDICT_LAST_TAKE   [i]->read(),address_src&1);
//                    TEST(Tcontrol_t         ,out_PREDICT_IS_ACCURATE [i]->read(), );
		    }

		  nb_request ++;
		}
	    
	    SC_START(1);
	  }
      }
  }

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

  TEST_OK ("End of Simulation");
  delete _time;

  msg(_("<%s> : ............ Stop Simulation\n"),name.c_str());

  delete in_CLOCK;
  delete in_NRESET;

  delete []  in_PREDICT_VAL           ;
  delete [] out_PREDICT_ACK           ;
  delete []  in_PREDICT_CONTEXT_ID    ;
  delete []  in_PREDICT_ADDRESS       ;
  delete [] out_PREDICT_HIT           ;
  delete [] out_PREDICT_ADDRESS_SRC   ;
  delete [] out_PREDICT_ADDRESS_DEST  ;
  delete [] out_PREDICT_CONDITION     ;
  delete [] out_PREDICT_LAST_TAKE     ;
  delete [] out_PREDICT_IS_ACCURATE   ;
  
  delete []  in_DECOD_VAL             ;
  delete [] out_DECOD_ACK             ;
  delete []  in_DECOD_CONTEXT_ID      ;
  delete []  in_DECOD_ADDRESS_SRC     ;
  delete []  in_DECOD_ADDRESS_DEST    ;
  delete []  in_DECOD_CONDITION       ;
  delete []  in_DECOD_LAST_TAKE       ;
  delete []  in_DECOD_MISS_PREDICTION ;
  delete []  in_DECOD_IS_ACCURATE     ;
  
  delete []  in_UPDATE_VAL            ;
  delete [] out_UPDATE_ACK            ;
  delete []  in_UPDATE_CONTEXT_ID     ;
  delete []  in_UPDATE_ADDRESS_SRC    ;
  delete []  in_UPDATE_ADDRESS_DEST   ;
  delete []  in_UPDATE_CONDITION      ;
  delete []  in_UPDATE_LAST_TAKE      ;
  delete []  in_UPDATE_MISS_PREDICTION;
#endif

  delete _Branch_Target_Buffer;
#ifdef STATISTICS
  delete _parameters_statistics;
#endif
}
