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

#include "Behavioural/Generic/Shifter/SelfTest/include/test.h"
#include "Common/include/BitManipulation.h"
#include "Common/include/Test.h"

#define NB_ITERATION 64

void test (string name,
	   morpheo::behavioural::generic::shifter::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);
    }
  Shifter * _Shifter = new Shifter (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 <Tdirection_t>                 NRESET("nreset");
  sc_signal <Tdata_t     >                 SHIFTER_DATA_IN   [param._nb_port];
  sc_signal <Tshift_t    >                 SHIFTER_SHIFT     [param._nb_port];
  sc_signal <Tdirection_t>                 SHIFTER_DIRECTION [param._nb_port];
  sc_signal <Ttype_t     >                 SHIFTER_TYPE      [param._nb_port];
  sc_signal <Tcarry_t    >                 SHIFTER_CARRY     [param._nb_port];
  sc_signal <Tcontrol_t  >                 SHIFTER_CARRY_IN  [param._nb_port];
  sc_signal <Tdata_t     >                 SHIFTER_COMPLETION[param._nb_port];
  sc_signal <Tdata_t     >                 SHIFTER_DATA_OUT  [param._nb_port];
  
  /********************************************************
   * Instanciation
   ********************************************************/
  
  cout << "<" << name << "> Instanciation of _Shifter" << endl;
  
#if (defined(STATISTICS) || defined (VHDL_TESTBENCH))
  (*(_Shifter->in_CLOCK))        (CLOCK);
  (*(_Shifter->in_NRESET))       (NRESET);
#endif
  for (uint32_t i=0; i<param._nb_port; i++)
    {
      (*(_Shifter-> in_SHIFTER_DATA      [i])) (SHIFTER_DATA_IN   [i]);
      if (param._shift_value == 0)
      (*(_Shifter-> in_SHIFTER_SHIFT     [i])) (SHIFTER_SHIFT     [i]);
      if (param._direction   == external_direction)
      (*(_Shifter-> in_SHIFTER_DIRECTION [i])) (SHIFTER_DIRECTION [i]);
      if (param._rotate      == external_rotate)
      (*(_Shifter-> in_SHIFTER_TYPE      [i])) (SHIFTER_TYPE      [i]);
      if (param._carry       == external_carry)
      (*(_Shifter-> in_SHIFTER_CARRY     [i])) (SHIFTER_CARRY     [i]);
      if (param._carry       == external_completion)
      if (param._type_completion_bool == true)
      (*(_Shifter-> in_SHIFTER_CARRY_IN  [i])) (SHIFTER_CARRY_IN  [i]);
      else
      (*(_Shifter-> in_SHIFTER_COMPLETION[i])) (SHIFTER_COMPLETION[i]);
      (*(_Shifter->out_SHIFTER_DATA      [i])) (SHIFTER_DATA_OUT  [i]);
    }

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

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

  srand (0);
  //srand (time(NULL));
  
  const uint32_t iteration        = NB_ITERATION;

  Tdirection_t   direction       ;
  Ttype_t        type            ;
  Tcarry_t       carry           ; 
  const Tdata_t  data_max        = (1<<param._size_data);
  Tdata_t        data_in         [param._nb_port];
  Tdata_t        data_completion [param._nb_port];
  Tshift_t       shift_min       ;
  Tshift_t       shift_max       ;
  Tdata_t        data_out        [param._nb_port];
  string         op              ;

  if (param._shift_value != 0)
    {
      shift_min = param._shift_value;
      shift_max = param._shift_value;
    }
  else
    {
      shift_min = 0;
      shift_max = param._size_data-1;
    }

  sc_start(0);

  NRESET.write(1);
  
  for (uint32_t x=0; x<6; x++)
    {
      switch (x)
	{
	case 0 :
	  if (param._have_shift_logic_left)
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} Test    Shift   Left  Logic" << endl;
	      direction = _left ;
	      type      = _shift;
	      carry     = _logic;
	      op        = " sll " ;	
	      break;
	    }
	  else
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} No Test Shift   Left  Logic" << endl;
	      continue;
	    }
	case 1 :
	  if (param._have_shift_logic_right)
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} Test    Shift   Right Logic" << endl;
	      direction = _right;
	      type      = _shift;
	      carry     = _logic;
	      op        = " srl " ;
	      break;
	    }
	  else
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} No Test Shift   Right Logic" << endl;
	      continue;
	    }
	case 2 :
	  if (param._have_shift_arithmetic_left)
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} Test    Shift   Left  Arithmetic" << endl;
	      direction = _left      ;
	      type      = _shift     ;
	      carry     = _arithmetic;
	      op        = " sla "    ;
	      break;
	    }
	  else
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} No Test Shift   Left  Arithmetic" << endl;
	      continue;
	    }
	case 3 :
	  if (param._have_shift_arithmetic_right)
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} Test    Shift   Right Arithmetic" << endl;
	      direction = _right;
	      type      = _shift;
	      carry     = _arithmetic;
	      op        = " sra "    ;
	      break;
	    }
	  else
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} No Test Shift   Right Arithmetic" << endl;
	      continue;
	    }
	case 4 :
	  if ( param._have_rotate_left)
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} Test    Rotate  Left" << endl;
	      direction = _left  ;
	      type      = _rotate;
	      carry     = _logic ; 
	      op        = " rol "; 
	      break;
	    }
	  else
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} No Test Rotate  Left" << endl;
	      continue;
	    }
	case 5 :
	  if (param._have_rotate_right)
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} Test    Rotate  Right" << endl;
	      direction = _right;
	      type      = _rotate;
	      carry     = _logic ; 
	      op        = " ror "; 
	      break;
	    }
	  else
	    {
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "} No Test Rotate  Right" << endl;
	      continue;
	    }
	default : 
	  continue;
	}

      for (uint32_t i=0; i<param._nb_port; i++)
	{
	  SHIFTER_DIRECTION [i].write(direction );
	  SHIFTER_TYPE      [i].write(type      ); 
	  SHIFTER_CARRY     [i].write(carry     );
	}

      for (uint32_t it=0; it < iteration; it++)
	{
	  for (Tshift_t shift=shift_min; shift<=shift_max; shift++)
	    {
	      Tdata_t mask_left  = 0;
	      Tdata_t mask_right = 0;
	      
	      if (param._size_data_completion > 0)
		{
		  mask_left  = gen_mask<Tdata_t> (shift);
		  mask_right = mask_left << (param._size_data - shift);
		}
	      
	      for (uint32_t i=0; i<param._nb_port; i++)
		{
		  data_in         [i] = rand() % data_max;
		  data_completion [i] = (rand() % data_max) & gen_mask<Tdata_t>(param._size_data_completion); 

		  switch (x)
		    {
		    case 0  : data_out [i] =((shift_logic_left       <Tdata_t>(param._size_data, data_in[i], shift) & ~mask_left ) | (data_completion [i] & mask_left )); break;
		    case 1  : data_out [i] =((shift_logic_right      <Tdata_t>(param._size_data, data_in[i], shift) & ~mask_right) | (data_completion [i] & mask_right)); break;
		    case 2  : data_out [i] = shift_arithmetic_left  <Tdata_t>(param._size_data, data_in[i], shift); break;
		    case 3  : data_out [i] = shift_arithmetic_right <Tdata_t>(param._size_data, data_in[i], shift); break;
		    case 4  : data_out [i] = rotate_left            <Tdata_t>(param._size_data, data_in[i], shift); break;
		    case 5  : data_out [i] = rotate_right           <Tdata_t>(param._size_data, data_in[i], shift); break;
		    default : break;
		  }
		  SHIFTER_DATA_IN   [i].write(data_in         [i]);
		  SHIFTER_SHIFT     [i].write(shift              );  
		  if (param._type_completion_bool == true)
		    SHIFTER_CARRY_IN  [i].write(data_completion [i]!=0);
		  else
		    SHIFTER_COMPLETION[i].write(data_completion [i]);
		}
	      
	      sc_start(0);
	      
	      cout << "{" << static_cast<uint32_t>(sc_simulation_time()) << "}" << endl;
	      
	      for (uint32_t i=0; i<param._nb_port; i++)
		{
		  cout << "\t[" << i << "] " 
		       << hex
		       << data_in[i] << op
		       << dec 
		       << shift      << " = " 
		       << hex
		       << SHIFTER_DATA_OUT [i].read()
		       << " - completion : "
		       << data_completion [i]
		       << dec ;
		  
		  TEST(Tdata_t,SHIFTER_DATA_OUT [i].read(),data_out[i]);
		}
	      sc_start(1);
	    }
	}
    }
  
  sc_start(0);
  

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

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

#endif

  delete _Shifter;
}
