#ifdef SYSTEMC
/*
 * $Id: Functionnal_unit_transition.cpp 100 2009-01-08 13:06:27Z rosiere $
 *
 * [ Description ]
 * 
 */

#include "Behavioural/Core/Multi_Execute_loop/Execute_loop/Multi_Execute_unit/Execute_unit/Functionnal_unit/include/Functionnal_unit.h"

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_execute_loop {
namespace execute_loop {
namespace multi_execute_unit {
namespace execute_unit {
namespace functionnal_unit {


#undef  FUNCTION
#define FUNCTION "Functionnal_unit::transition"
  void Functionnal_unit::transition (void)
  {
    log_begin(Functionnal_unit,FUNCTION);
    log_function(Functionnal_unit,FUNCTION,_name.c_str());

    if (PORT_READ(in_NRESET) == 0)
      {
	reg_BUSY_IN  = false;
	reg_BUSY_OUT = false;
	
	for (uint32_t i=0; i<_param->_nb_context; i++)
	  for (uint32_t j=0; j<_param->_nb_front_end; j++)
	    for (uint32_t k=0; k<_param->_nb_ooo_engine; k++)
	      {
// 		uint32_t num_thread = get_num_thread(i,_param->_size_context_id,
// 						     j,_param->_size_front_end_id,
// 						     k,_param->_size_ooo_engine_id);
		
		for (uint32_t x=GROUP_CUSTOM_1; x<GROUP_CUSTOM_8; x++)
		  {
		    function_execute_end_cycle_t * fct = (_param->_get_custom_information()._get_custom_execute_reset(x));
		    
		    if (fct != NULL)
		      (* fct) (_execute_register[i][j][k], _execute_param);
		  }
	      }
      }
    else
      {
	// Test if pop
	if (internal_EXECUTE_OUT_VAL and PORT_READ(in_EXECUTE_OUT_ACK))
	  {
            log_printf(TRACE,Functionnal_unit,FUNCTION,"  * EXECUTE_OUT : Transaction Accepted");
	    // transaction
	    reg_BUSY_OUT = false;
	  }
	
	// Test if push
	execute_register_t * execute_register = NULL;

	if (PORT_READ(in_EXECUTE_IN_VAL) and internal_EXECUTE_IN_ACK)
	  {
            log_printf(TRACE,Functionnal_unit,FUNCTION,"  * EXECUTE_IN : Transaction Accepted");

	    reg_BUSY_IN = true;

	    Tcontext_t   context_id    = (_param->_have_port_context_id   )?PORT_READ(in_EXECUTE_IN_CONTEXT_ID   ):0;
	    Tcontext_t   front_end_id  = (_param->_have_port_front_end_id )?PORT_READ(in_EXECUTE_IN_FRONT_END_ID ):0;
	    Tcontext_t   ooo_engine_id = (_param->_have_port_ooo_engine_id)?PORT_READ(in_EXECUTE_IN_OOO_ENGINE_ID):0;
	    Tcontext_t   packet_id     = (_param->_have_port_rob_ptr      )?PORT_READ(in_EXECUTE_IN_PACKET_ID    ):0;
	    Toperation_t operation     = PORT_READ(in_EXECUTE_IN_OPERATION);
	    Ttype_t      type          = PORT_READ(in_EXECUTE_IN_TYPE);
	    
	    _execute_operation_in->_context_id    = context_id   ;
	    _execute_operation_in->_front_end_id  = front_end_id ;
	    _execute_operation_in->_ooo_engine_id = ooo_engine_id;
	    _execute_operation_in->_packet_id     = packet_id    ;
	    _execute_operation_in->_operation     = operation    ;
	    _execute_operation_in->_type          = type         ;
	    _execute_operation_in->_has_immediat  = PORT_READ(in_EXECUTE_IN_HAS_IMMEDIAT);
	    _execute_operation_in->_immediat      = PORT_READ(in_EXECUTE_IN_IMMEDIAT    );
	    _execute_operation_in->_data_ra       = PORT_READ(in_EXECUTE_IN_DATA_RA     );
	    _execute_operation_in->_data_rb       = PORT_READ(in_EXECUTE_IN_DATA_RB     );
	    _execute_operation_in->_data_rc       = PORT_READ(in_EXECUTE_IN_DATA_RC     );
	    _execute_operation_in->_write_rd      = PORT_READ(in_EXECUTE_IN_WRITE_RD    );
	    _execute_operation_in->_num_reg_rd    = PORT_READ(in_EXECUTE_IN_NUM_REG_RD  );
	    _execute_operation_in->_data_rd       = 0; // no necessaray
	    _execute_operation_in->_write_re      = PORT_READ(in_EXECUTE_IN_WRITE_RE    );
	    _execute_operation_in->_num_reg_re    = PORT_READ(in_EXECUTE_IN_NUM_REG_RE  );
	    _execute_operation_in->_data_re       = 0; // no necessaray

            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * context_id    : %d",_execute_operation_in->_context_id   );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * front_end_id  : %d",_execute_operation_in->_front_end_id );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * ooo_engine_id : %d",_execute_operation_in->_ooo_engine_id);
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * packet_id     : %d",_execute_operation_in->_packet_id    );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * operation     : %d",_execute_operation_in->_operation    );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * type          : %s",toString(_execute_operation_in->_type).c_str());
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * has_immediat  : %d",_execute_operation_in->_has_immediat );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * immediat      : %.8x",_execute_operation_in->_immediat     );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * data_ra       : %.8x",_execute_operation_in->_data_ra      );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * data_rb       : %.8x",_execute_operation_in->_data_rb      );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * data_rc       : %.8x",_execute_operation_in->_data_rc      );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * write_rd      : %d"  ,_execute_operation_in->_write_rd     );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * num_reg_rd    : %d"  ,_execute_operation_in->_num_reg_rd   );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * write_re      : %d"  ,_execute_operation_in->_write_re     );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * num_reg_re    : %d"  ,_execute_operation_in->_num_reg_re   );
            
#ifdef DEBUG_TEST
	    if (type >= _param->_nb_type)
	      throw ERRORMORPHEO(FUNCTION,"Invalid type : '"+toString(type)+"' is higher that _nb_type");
	    if (operation >= _param->_nb_operation)
	      throw ERRORMORPHEO(FUNCTION,"Invalid operation : '"+toString(operation)+"' is higher that _nb_operation");
#endif

	    // execute the operation
 	    execute_register = _execute_register[context_id][front_end_id][ooo_engine_id];

	    // Test if operation is a custom
	    if (type == TYPE_CUSTOM)
	      {
// 		uint32_t num_thread = get_num_thread(context_id   ,_param->_size_context_id,
// 						     front_end_id ,_param->_size_front_end_id,
// 						     ooo_engine_id,_param->_size_ooo_engine_id);

		(*(_param->_get_custom_information()._get_custom_execute_genMoore(operation))) (_execute_operation_in, execute_register, _execute_param);
	      }
	    else
	      (*(_function_execute[type][operation])) (_execute_operation_in, execute_register, _execute_param);


            log_printf(TRACE,Functionnal_unit,FUNCTION,"    -----------------");
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * latence       : %.8x",_execute_operation_in->_timing._latence);
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * delay         : %.8x",_execute_operation_in->_timing._delay);
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * data_rd       : %.8x",_execute_operation_in->_data_rd    );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * data_re       : %.8x",_execute_operation_in->_data_re    );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * exception     : %d"  ,_execute_operation_in->_exception  );
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * no_sequence   : %d"  ,_execute_operation_in->_no_sequence);
            log_printf(TRACE,Functionnal_unit,FUNCTION,"    * address       : %.8x (%.8x)",_execute_operation_in->_address,_execute_operation_in->_address<<2);

#ifdef STATISTICS
            if (usage_is_set(_usage,USE_STATISTICS))
              {
                (*_stat_use_functionnal_unit) ++;
                (*_stat_sum_delay)   += _execute_operation_in->_timing._delay;
                (*_stat_sum_latence) += _execute_operation_in->_timing._latence;
              }
#endif
	  }
	
	// End cycle
	{
	  for (uint32_t i=0; i<_param->_nb_context; i++)
	    for (uint32_t j=0; j<_param->_nb_front_end; j++)
	      for (uint32_t k=0; k<_param->_nb_ooo_engine; k++)
		{
// 		  uint32_t num_thread = get_num_thread(i,_param->_size_context_id,
// 						       j,_param->_size_front_end_id,
// 						       k,_param->_size_ooo_engine_id);

		  for (uint32_t x=GROUP_CUSTOM_1; x<GROUP_CUSTOM_8; x++)
		    {
		      function_execute_end_cycle_t * fct = (_param->_get_custom_information()._get_custom_execute_transition(x));
		      
		      if (fct != NULL)
			(* fct) (_execute_register[i][j][k], _execute_param);
		    }
		}

	  // Update status
	  if (execute_register != NULL)
	    {
	      // They have an access
	      execute_register->_i_write_spr = false;
	      execute_register->_i_read_spr  = false;
	    }
	}

	if (reg_BUSY_IN and not reg_BUSY_OUT)
	  {
	    reg_BUSY_OUT = reg_BUSY_IN;
	    reg_BUSY_IN  = false;

	    _execute_operation_out->_timing        = _execute_operation_in->_timing       ;
	    _execute_operation_out->_context_id    = _execute_operation_in->_context_id   ;
	    _execute_operation_out->_front_end_id  = _execute_operation_in->_front_end_id ;
	    _execute_operation_out->_ooo_engine_id = _execute_operation_in->_ooo_engine_id;
	    _execute_operation_out->_packet_id     = _execute_operation_in->_packet_id    ;
	    _execute_operation_out->_operation     = _execute_operation_in->_operation    ;
	    _execute_operation_out->_type          = _execute_operation_in->_type         ;
	    _execute_operation_out->_has_immediat  = _execute_operation_in->_has_immediat ;
	    _execute_operation_out->_immediat      = _execute_operation_in->_immediat     ;
	    _execute_operation_out->_data_ra       = _execute_operation_in->_data_ra      ;
	    _execute_operation_out->_data_rb       = _execute_operation_in->_data_rb      ;
	    _execute_operation_out->_data_rc       = _execute_operation_in->_data_rc      ;
	    _execute_operation_out->_data_rd       = _execute_operation_in->_data_rd      ;
	    _execute_operation_out->_data_re       = _execute_operation_in->_data_re      ;
	    _execute_operation_out->_write_rd      = _execute_operation_in->_write_rd     ;
	    _execute_operation_out->_num_reg_rd    = _execute_operation_in->_num_reg_rd   ;
	    _execute_operation_out->_write_re      = _execute_operation_in->_write_re     ;
	    _execute_operation_out->_num_reg_re    = _execute_operation_in->_num_reg_re   ;
	    _execute_operation_out->_exception     = _execute_operation_in->_exception    ;
	    _execute_operation_out->_no_sequence   = _execute_operation_in->_no_sequence  ;
	    _execute_operation_out->_address       = _execute_operation_in->_address      ;
	  }
	
	// each cycle : decrease the latence
	if (reg_BUSY_OUT and (_execute_operation_out->_timing._latence > 0))
	  _execute_operation_out->_timing._latence --;
      }

#if defined(STATISTICS) or defined(VHDL_TESTBENCH)
    end_cycle ();
#endif

    log_end(Functionnal_unit,FUNCTION);
  };

}; // end namespace functionnal_unit
}; // end namespace execute_unit
}; // end namespace multi_execute_unit
}; // end namespace execute_loop
}; // end namespace multi_execute_loop
}; // end namespace core

}; // end namespace behavioural
}; // end namespace morpheo              
#endif
