#ifdef SYSTEMC
/*
 * $Id: Ifetch_queue_transition.cpp 101 2009-01-15 17:19:08Z rosiere $
 *
 * [ Description ]
 * 
 */

#include "Behavioural/Core/Multi_Front_end/Front_end/Ifetch_unit/Ifetch_queue/include/Ifetch_queue.h"

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_front_end {
namespace front_end {
namespace ifetch_unit {
namespace ifetch_queue {


#undef  FUNCTION
#define FUNCTION "Ifetch_queue::transition"
  void Ifetch_queue::transition (void)
  {
    log_begin(Ifetch_queue,FUNCTION);

    if (PORT_READ(in_NRESET) == 0)
      {
	reg_PTR_READ  = 0;
	reg_PTR_WRITE = 0;

	for (uint32_t i=0; i<_param->_size_queue; i++)
	  _queue [i]->_state = IFETCH_QUEUE_STATE_EMPTY;
      }
    else
      {
	// ==========================================================
	// =====[ ADDRESS ]==========================================
	// ==========================================================
	if (PORT_READ(in_ADDRESS_VAL) and internal_ADDRESS_ACK)
	  {
	    // New slot in ifetch_queue is allocated
	    
	    _queue[reg_PTR_WRITE]->_state = IFETCH_QUEUE_STATE_WAIT_RSP;
	      
#ifdef STATISTICS
	    if (usage_is_set(_usage,USE_STATISTICS))
              (*_sum_transaction_address) ++;
#endif

	    for (uint32_t i=0; i<_param->_nb_instruction; i++)
	      {
		Tcontrol_t enable = PORT_READ(in_ADDRESS_INSTRUCTION_ENABLE [i]);
#ifdef STATISTICS
                if (usage_is_set(_usage,USE_STATISTICS))
                  (*_sum_inst_enable) += enable;
#endif
		_queue[reg_PTR_WRITE]->_instruction_enable [i]      = enable;
	      }

	    _queue[reg_PTR_WRITE]->_address                     = PORT_READ(in_ADDRESS_INSTRUCTION_ADDRESS);
	    _queue[reg_PTR_WRITE]->_inst_ifetch_ptr             = (_param->_have_port_inst_ifetch_ptr)?PORT_READ(in_ADDRESS_INST_IFETCH_PTR            ):0;
	    _queue[reg_PTR_WRITE]->_branch_state                = PORT_READ(in_ADDRESS_BRANCH_STATE);
	    _queue[reg_PTR_WRITE]->_branch_update_prediction_id = (_param->_have_port_depth)?PORT_READ(in_ADDRESS_BRANCH_UPDATE_PREDICTION_ID):0;
	    
	    reg_PTR_WRITE = (reg_PTR_WRITE+1)%_param->_size_queue;
	  }

	// ==========================================================
	// =====[ DECOD ]============================================
	// ==========================================================
	bool have_instruction_decod  = false;
	bool have_instruction_enable = false;
	for (uint32_t i=0; i<_param->_nb_instruction; i++)
	  {
	    if (internal_DECOD_VAL [i] and PORT_READ(in_DECOD_ACK[i]))
	      {
		have_instruction_decod = true;
		_queue[reg_PTR_READ]->_instruction_enable [i] = false;
	      }
	    have_instruction_enable |= _queue[reg_PTR_READ]->_instruction_enable [i];
	  }

	// Test if all is decoded
	if (have_instruction_decod and not have_instruction_enable)
	  {
	    // all is decod
	    _queue[reg_PTR_READ]->_state = IFETCH_QUEUE_STATE_EMPTY;
	    reg_PTR_READ = (reg_PTR_READ+1)%_param->_size_queue;
	  }
	  
	// ==========================================================
	// =====[ ICACHE_RSP ]=======================================
	// ==========================================================
	if (PORT_READ(in_ICACHE_RSP_VAL) and internal_ICACHE_RSP_ACK)
	  {
	    Tpacket_t ptr = (_param->_have_port_ifetch_queue_ptr)?PORT_READ(in_ICACHE_RSP_PACKET_ID):0;
	    
	    for (uint32_t i=0; i<_param->_nb_instruction; i++)
	      _queue[ptr]->_instruction [i]      = PORT_READ(in_ICACHE_RSP_INSTRUCTION [i]);
	    
	    switch (PORT_READ(in_ICACHE_RSP_ERROR))
	      {
	      case ICACHE_ERROR_NONE      : _queue[ptr]->_exception = EXCEPTION_IFETCH_NONE     ; break;
	      case ICACHE_ERROR_BUS_ERROR : _queue[ptr]->_exception = EXCEPTION_IFETCH_BUS_ERROR; break;
	      default : ERRORMORPHEO(FUNCTION,"icache_rsp_error : unknow value.");
	      }

	    switch (_queue[ptr]->_state)
	      {
	      case IFETCH_QUEUE_STATE_WAIT_RSP       : _queue[ptr]->_state = IFETCH_QUEUE_STATE_HAVE_RSP; break;
	      case IFETCH_QUEUE_STATE_ERROR_WAIT_RSP : _queue[ptr]->_state = IFETCH_QUEUE_STATE_EMPTY   ; break;
	      default : ERRORMORPHEO(FUNCTION,"icache_rsp : invalid ifetch_queue state.");
	      }
	  }

	// ==========================================================
	// =====[ EVENT_RESET ]======================================
	// ==========================================================
	if (PORT_READ(in_EVENT_RESET_VAL) and internal_EVENT_RESET_ACK)
	  {
	    // Scan all entry of queue and test the status
	    for (uint32_t i=0; i<_param->_size_queue; i++)
	      switch (_queue[i]->_state)
	      {
	      case IFETCH_QUEUE_STATE_ERROR_WAIT_RSP :                                                        break;
	      case IFETCH_QUEUE_STATE_WAIT_RSP       : _queue[i]->_state = IFETCH_QUEUE_STATE_ERROR_WAIT_RSP; break;
	      default                                : _queue[i]->_state = IFETCH_QUEUE_STATE_EMPTY         ; break;
	      }

	    // all entry is empty (or wait respons to flush)
	    // reset ptr
	    //   1) reg_PTR_READ = reg_PTR_WRITE =  = 0
	    //   2) reg_PTR_READ = reg_PTR_WRITE
	    // In method 1), the probalitie than the entry pointed by reg_PTR_WRITE is a slot with state "error_wait_rsp" is more importate that the method 2)
	    reg_PTR_READ = reg_PTR_WRITE;
	  }

#if defined(DEBUG) and (DEBUG >= DEBUG_TRACE)
	log_printf(TRACE,Ifetch_queue,FUNCTION,"  * Dump ifetch_queue");
	log_printf(TRACE,Ifetch_queue,FUNCTION,"    * reg_PTR_WRITE : %d",reg_PTR_WRITE);
	log_printf(TRACE,Ifetch_queue,FUNCTION,"    * reg_PTR_READ  : %d",reg_PTR_READ );
	for (uint32_t i=0; i<_param->_size_queue; i++)
	  {
            log_printf(TRACE,Ifetch_queue,FUNCTION,"    * [%d] 0x%.8x (0x%.8x) %d - %d %d %d - %s",
                       i, 
                       _queue [i]->_address,
                       _queue [i]->_address<<2,
                       _queue [i]->_inst_ifetch_ptr,
                       _queue [i]->_branch_state,
                       _queue [i]->_branch_update_prediction_id,
                       _queue [i]->_exception,
                       toString(_queue [i]->_state).c_str()
                       );
	    
	    for (uint32_t j=0; j<_param->_nb_instruction; j++)
	      log_printf(TRACE,Ifetch_queue,FUNCTION,"      * %d 0x%.8x", _queue [i]->_instruction_enable[j], _queue [i]->_instruction[j]);
	  }
#endif

#ifdef STATISTICS
        if (usage_is_set(_usage,USE_STATISTICS))
          for (uint32_t i=0; i<_param->_size_queue; i++)
            switch (_queue[i]->_state)
              {
              case IFETCH_QUEUE_STATE_EMPTY          : break;
              case IFETCH_QUEUE_STATE_WAIT_RSP       : (*_sum_use_queue_wait_rsp      ) ++; break;
              case IFETCH_QUEUE_STATE_HAVE_RSP       : (*_sum_use_queue_have_rsp      ) ++; break;
              case IFETCH_QUEUE_STATE_ERROR_WAIT_RSP : (*_sum_use_queue_error_wait_rsp) ++; break;
              default : break;
              }
#endif
      }
    
#if defined(STATISTICS) or defined(VHDL_TESTBENCH)
    end_cycle ();
#endif

    log_end(Ifetch_queue,FUNCTION);
  };

}; // end namespace ifetch_queue
}; // end namespace ifetch_unit
}; // end namespace front_end
}; // end namespace multi_front_end
}; // end namespace core

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