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

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

#include "Behavioural/Generic/RegisterFile/RegisterFile_Multi_Banked/SelfTest/include/test.h"
#include "Common/include/Test.h"


#define LABEL(str)                                                                       \
{                                                                                        \
  cout << "{"+toString(static_cast<uint32_t>(sc_simulation_time()))+"} " << str << endl; \
} while(0)

#define SC_START(cycle)                                        \
do                                                             \
{                                                              \
  if (static_cast<uint32_t>(sc_simulation_time()) > CYCLE_MAX) \
    {                                                          \
      TEST_KO("Maximal cycles Reached");                       \
    }                                                          \
  sc_start(cycle);                                             \
} while(0)

void test (string name,
	   morpheo::behavioural::generic::registerfile::registerfile_multi_banked::Parameters * _param)
{
  cout << "<" << name << "> : Simulation SystemC" << endl;


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

  RegisterFile_Multi_Banked * _RegisterFile_Multi_Banked = new RegisterFile_Multi_Banked (name.c_str(),
#ifdef STATISTICS
											  _param_stat,
#endif
											  _param);
  
#ifdef SYSTEMC
  /*********************************************************************
   * Déclarations des signaux
   *********************************************************************/
  sc_clock                               * CLOCK;
  sc_signal<Tcontrol_t>                  * NRESET;

  sc_signal<Tcontrol_t>                    READ_VAL      [_param->_nb_port_read];
  sc_signal<Tcontrol_t>                    READ_ACK      [_param->_nb_port_read];
  sc_signal<Taddress_t>                    READ_ADDRESS  [_param->_nb_port_read];
  sc_signal<Tdata_t>                       READ_DATA     [_param->_nb_port_read];

  sc_signal<Tcontrol_t>                    WRITE_VAL     [_param->_nb_port_write];
  sc_signal<Tcontrol_t>                    WRITE_ACK     [_param->_nb_port_write];
  sc_signal<Taddress_t>                    WRITE_ADDRESS [_param->_nb_port_write];
  sc_signal<Tdata_t>                       WRITE_DATA    [_param->_nb_port_write];

  string rename;

  CLOCK                                  = new sc_clock ("clock", 1.0, 0.5);
  NRESET                                 = new sc_signal<Tcontrol_t> ("NRESET");
  
  /********************************************************
   * Instanciation
   ********************************************************/
  
  cout << "<" << name << "> Instanciation of _RegisterFile_Multi_Banked" << endl;
  
  (*(_RegisterFile_Multi_Banked->in_CLOCK))        (*(CLOCK));
  (*(_RegisterFile_Multi_Banked->in_NRESET))       (*(NRESET));

  for (uint32_t i=0; i<_param->_nb_port_read; i++)
    {
      (*(_RegisterFile_Multi_Banked-> in_READ_VAL      [i]))        (READ_VAL      [i]);
      (*(_RegisterFile_Multi_Banked->out_READ_ACK      [i]))        (READ_ACK      [i]);
      if (_param->_have_port_address==true)
      (*(_RegisterFile_Multi_Banked-> in_READ_ADDRESS  [i]))        (READ_ADDRESS  [i]);
      (*(_RegisterFile_Multi_Banked->out_READ_DATA     [i]))        (READ_DATA     [i]);
    }

  for (uint32_t i=0; i<_param->_nb_port_write; i++)
    {
      (*(_RegisterFile_Multi_Banked-> in_WRITE_VAL     [i]))        (WRITE_VAL     [i]);
      (*(_RegisterFile_Multi_Banked->out_WRITE_ACK     [i]))        (WRITE_ACK     [i]);
      if (_param->_have_port_address==true)
      (*(_RegisterFile_Multi_Banked-> in_WRITE_ADDRESS [i]))        (WRITE_ADDRESS [i]);
      (*(_RegisterFile_Multi_Banked-> in_WRITE_DATA    [i]))        (WRITE_DATA    [i]);
    }

  cout << "<" << name << "> Start Simulation ............" << endl;
  Time * _time = new Time();

  /********************************************************
   * Simulation - Begin
   ********************************************************/
  const uint32_t nb_request    = _param->_nb_word;
  // random init
  const uint32_t grain         = 0;
  //const uint32_t grain = static_cast<uint32_t>(time(NULL));
  
  srand(grain);

  // Initialisation

  SC_START(0);
  
  for (uint32_t i=0; i<_param->_nb_port_write; i++)
    WRITE_VAL [i] .write (0);

  for (uint32_t i=0; i<_param->_nb_port_read; i++)
    READ_VAL  [i] .write (0);

  NRESET->write(0);

  SC_START(5);

  NRESET->write(1);

  for (uint32_t nb_iteration=0; nb_iteration < NB_ITERATION; nb_iteration ++)
    {
      cout << "<" << name << "> 1) Write the RegisterFile (no read)" << endl;

      Taddress_t nb_val            = 0;
      Taddress_t nb_ack            = 0;
      
      Tdata_t    tab_data    [_param->_nb_word];
      Taddress_t tab_address [nb_request      ];
  
      for (uint32_t i=0; i<_param->_nb_word; i++)
	tab_data    [i]= rand()%(1<<(_param->_size_word-1));
      for (uint32_t i=0; i<nb_request; i++)
	tab_address [i]= rand()%(1<<(_param->_size_address));

      while (nb_ack < nb_request)
	{
	  cout << "cycle : " << static_cast<uint32_t> (sc_simulation_time()) << endl;

	  for (uint32_t num_port=0; num_port < _param->_nb_port_write; num_port ++)
	    {
	      if ((nb_val            < nb_request) and
		  (WRITE_VAL [num_port].read() == 0))
		{
		  cout << "(" << num_port << ") [" << tab_address[nb_val] << "] <= " << tab_data[tab_address[nb_val]] << endl;
		  
		  WRITE_VAL     [num_port] .write(1);
		  WRITE_DATA    [num_port] .write(tab_data[tab_address[nb_val]]);
		  WRITE_ADDRESS [num_port] .write(tab_address[nb_val]);

		  nb_val ++;

		  // Address can be not a multiple of nb_port_write
		  if (nb_val >= nb_request)
		    break;
		}
	    }
	  
	  SC_START(1);

	  // reset write_val port
	  for (uint32_t num_port=0; num_port < _param->_nb_port_write; num_port ++)
	    {
	      if ((WRITE_ACK [num_port].read() == 1) and
		  (WRITE_VAL [num_port].read() == 1))
		{
		  WRITE_VAL  [num_port] .write(0);
		  nb_ack ++;
		}
	    }

	  SC_START(0);
	}
      

      {
	cout << "<" << name << "> 2) Read the RegisterFile (no write)" << endl;
	
	nb_val = 0;
	nb_ack = 0;
	Tdata_t read_address [_param->_nb_port_read];
	
	while (nb_ack < nb_request)
	  {
	    cout << "cycle : " << static_cast<uint32_t> (sc_simulation_time()) << endl;
	    
	    for (uint32_t num_port=0; num_port < _param->_nb_port_read; num_port ++)
	      {
		if ((nb_val < nb_request) and
		    (READ_VAL [num_port].read() == 0))
		  {
		    read_address [num_port] = tab_address[nb_val];
		    READ_VAL     [num_port].write(1);
		    READ_ADDRESS [num_port].write(read_address [num_port]);
		    
		    nb_val ++;
		    
		    if (nb_val >= nb_request)
		      break;
		  }
	      }
	      
	    SC_START(1);
	    
	    // reset write_val port
	    for (uint32_t num_port=0; num_port < _param->_nb_port_read; num_port ++)
	      {
		if ((READ_ACK [num_port].read() == 1) and
		    (READ_VAL [num_port].read() == 1))
		  {
		    READ_VAL  [num_port] .write(0);
		    
		    cout << "(" << num_port << ") [" << read_address [num_port] << "] => " << READ_DATA [num_port].read() << endl;
		    
		    TEST(Tdata_t,READ_DATA [num_port].read(), tab_data[read_address [num_port]]);
		    nb_ack ++;
		  }
	      }
	    
	    SC_START(0);
	  }
      }
    }
  
  /********************************************************
   * Simulation - End
   ********************************************************/

  TEST_OK("End of Simulation");
  delete _time;
  cout << "<" << name << "> ............ Stop Simulation" << endl;

  delete CLOCK;
  delete NRESET;
#endif

  delete _RegisterFile_Multi_Banked;
}
