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

#define NB_ITERATION 16

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

void test (string name,
	   morpheo::behavioural::generic::registerfile::registerfile_monolithic::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);
    }

#ifdef STATISTICS
  morpheo::behavioural::Parameters_Statistics * _param_stat = new morpheo::behavioural::Parameters_Statistics (5,100);
#endif
  RegisterFile_Monolithic * registerfile = new RegisterFile_Monolithic (name.c_str()
#ifdef STATISTICS
									,_param_stat
#endif
									,_param);
  
#ifdef SYSTEMC
  /*********************************************************************
   * Déclarations des signaux
   *********************************************************************/
  sc_clock                                 CLOCK ("clock", 1.0, 0.5);
  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];

  sc_signal<Tcontrol_t>                    READ_WRITE_VAL     [_param->_nb_port_read_write];
  sc_signal<Tcontrol_t>                    READ_WRITE_ACK     [_param->_nb_port_read_write];
  sc_signal<Tcontrol_t>                    READ_WRITE_RW      [_param->_nb_port_read_write];
  sc_signal<Taddress_t>                    READ_WRITE_ADDRESS [_param->_nb_port_read_write];
  sc_signal<Tdata_t>                       READ_WRITE_RDATA   [_param->_nb_port_read_write];
  sc_signal<Tdata_t>                       READ_WRITE_WDATA   [_param->_nb_port_read_write];

  /********************************************************
   * Instanciation
   ********************************************************/
  
  cout << "<" << name << "> Instanciation of registerfile" << endl;
  
  (*(registerfile->in_CLOCK))        (CLOCK);
  (*(registerfile->in_NRESET))       (NRESET);

  for (uint32_t i=0; i<_param->_nb_port_read; i++)
    {
      (*(registerfile-> in_READ_VAL      [i]))        (READ_VAL      [i]);
      (*(registerfile->out_READ_ACK      [i]))        (READ_ACK      [i]);
      if (_param->_have_port_address)
      (*(registerfile-> in_READ_ADDRESS  [i]))        (READ_ADDRESS  [i]);
      (*(registerfile->out_READ_DATA     [i]))        (READ_DATA     [i]);
    }
  for (uint32_t i=0; i<_param->_nb_port_write; i++)
    {
      (*(registerfile-> in_WRITE_VAL     [i]))        (WRITE_VAL     [i]);
      (*(registerfile->out_WRITE_ACK     [i]))        (WRITE_ACK     [i]);
      if (_param->_have_port_address)
      (*(registerfile-> in_WRITE_ADDRESS [i]))        (WRITE_ADDRESS [i]);
      (*(registerfile-> in_WRITE_DATA    [i]))        (WRITE_DATA    [i]);
    }
  for (uint32_t i=0; i<_param->_nb_port_read_write; i++)
    {
      (*(registerfile-> in_READ_WRITE_VAL     [i])) (READ_WRITE_VAL      [i]);
      (*(registerfile->out_READ_WRITE_ACK     [i])) (READ_WRITE_ACK      [i]);
      (*(registerfile-> in_READ_WRITE_RW      [i])) (READ_WRITE_RW       [i]);
      if (_param->_have_port_address)
      (*(registerfile-> in_READ_WRITE_ADDRESS [i])) (READ_WRITE_ADDRESS  [i]);
      (*(registerfile-> in_READ_WRITE_WDATA   [i])) (READ_WRITE_WDATA    [i]);
      (*(registerfile->out_READ_WRITE_RDATA   [i])) (READ_WRITE_RDATA    [i]);
    }
  
  cout << "<" << name << "> Start Simulation ............" << endl;
  Time * _time = new Time();

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

  // 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);
  for (uint32_t i=0; i<_param->_nb_port_read_write; i++)
    READ_WRITE_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;

      // random init
      uint32_t grain = 0;
      //uint32_t grain = static_cast<uint32_t>(time(NULL));
      
      srand(grain);

      Tdata_t tab [_param->_nb_word];
      
      for (uint32_t i=0; i<_param->_nb_word; i++)
	tab[i]= rand()%(1<<(_param->_size_word-1));
      
      Taddress_t address_next = 0;
      Taddress_t nb_ack = 0;
      
      while (nb_ack < _param->_nb_word)
	{
	  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 ((address_next < _param->_nb_word) and
		  (WRITE_VAL [num_port].read() == 0))
		{
		  cout << "(" << num_port << ") [" << address_next << "] <= " << tab[address_next] << endl;
		  
		  WRITE_VAL     [num_port] .write(1);
		  WRITE_DATA    [num_port] .write(tab[address_next]);
		  WRITE_ADDRESS [num_port] .write(address_next++);
		  
		  // Address can be not a multiple of nb_port_write
		  if (address_next >= _param->_nb_word)
		    break;
		}
	    }

	  for (uint32_t num_port=0; num_port < _param->_nb_port_read_write; num_port ++)
	    {
	      if ((address_next < _param->_nb_word) and
		  (READ_WRITE_VAL [num_port].read() == 0))
		{
		  cout << "(" << num_port << ") [" << address_next << "] <= " << tab[address_next] << endl;
		  
		  READ_WRITE_VAL     [num_port] .write(1);
		  READ_WRITE_RW      [num_port] .write(RW_WRITE);
		  READ_WRITE_WDATA   [num_port] .write(tab[address_next]);
		  READ_WRITE_ADDRESS [num_port] .write(address_next++);
		  
		  // Address can be not a multiple of nb_port_write
		  if (address_next >= _param->_nb_word)
		    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 ++;
		}
	    }
	  // reset write_val port
	  for (uint32_t num_port=0; num_port < _param->_nb_port_read_write; num_port ++)
	    {
	      if ((READ_WRITE_ACK [num_port].read() == 1) and
		  (READ_WRITE_VAL [num_port].read() == 1))
		{
		  READ_WRITE_VAL  [num_port] .write(0);
		  nb_ack ++;
		}
	    }

	  sc_start(0);
	}
      
      address_next = 0;
      nb_ack       = 0;

      cout << "<" << name << "> 2) Read the RegisterFile (no write)" << endl;
      
      Tdata_t read_address       [_param->_nb_port_read];
      Tdata_t read_write_address [_param->_nb_port_read_write];

      while (nb_ack < _param->_nb_word)
	{
	  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 ((address_next < _param->_nb_word) and
		  (READ_VAL [num_port].read() == 0))
		{
		  read_address [num_port] = address_next++;

		  READ_VAL     [num_port].write(1);
		  READ_ADDRESS [num_port].write(read_address [num_port]);

		  if (address_next >= _param->_nb_word)
		    break;
		}
	    }

	  for (uint32_t num_port=0; num_port < _param->_nb_port_read_write; num_port ++)
	    {
	      if ((address_next < _param->_nb_word) and
		  (READ_WRITE_VAL [num_port].read() == 0))
		{
		  read_write_address [num_port] = address_next++;

		  READ_WRITE_VAL     [num_port].write(1);
		  READ_WRITE_RW      [num_port].write(RW_READ);
		  READ_WRITE_ADDRESS [num_port].write(read_write_address [num_port]);

		  if (address_next >= _param->_nb_word)
		    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[read_address [num_port]]);
		  nb_ack ++;
		}
	    }

	  for (uint32_t num_port=0; num_port < _param->_nb_port_read_write; num_port ++)
	    {
	      if ((READ_WRITE_ACK [num_port].read() == 1) and
		  (READ_WRITE_VAL [num_port].read() == 1))
		{
		  READ_WRITE_VAL  [num_port] .write(0);

		  cout << "(" << num_port << ") [" << read_write_address [num_port] << "] => " << READ_WRITE_RDATA [num_port].read() << endl;

		  TEST(Tdata_t,READ_WRITE_RDATA [num_port].read(), tab[read_write_address [num_port]]);
		  nb_ack ++;
		}
	    }

	  sc_start(0);
	}
    }

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

  TEST_STR(bool,true,true, "End of Simulation");
  delete _time;
  cout << "<" << name << "> ............ Stop Simulation" << endl;

#endif

  delete registerfile;
}
