#ifdef SYSTEMC
/*
 * $Id$
 *
 * [ Description ]
 * 
 */

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

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


#undef  FUNCTION
#define FUNCTION "Address_management::transition"
  void Address_management::transition (void)
  {
    log_printf(FUNC,Address_management,FUNCTION,"Begin");

    if (PORT_READ(in_NRESET) == 0)
      {
	reg_PC_PREVIOUS_VAL = 0;
	reg_PC_CURRENT_VAL  = 0;
	reg_PC_NEXT_VAL     = 0;	
      }
    else
      {
	// =========================================
	// ===== ADDRESS ===========================
	// =========================================
	// transaction with icache
	if (internal_ADDRESS_VAL and PORT_READ(in_ADDRESS_ACK))
	  {
	    // current pc became previous pc 
	    reg_PC_PREVIOUS_VAL                        = 1;
	    reg_PC_PREVIOUS                            = reg_PC_CURRENT    ;
	    
	    // next    pc became next     pc
	    reg_PC_CURRENT_VAL                         = reg_PC_NEXT_VAL; // can be not valid

	    // if pc_next is not valid : don't erase PC and PC_IS_DS_TAKE : this register is send a the predict (to compute pc_next)
	    if (reg_PC_NEXT_VAL)
	      {
	    reg_PC_CURRENT                             = reg_PC_NEXT    ;
	    reg_PC_CURRENT_IS_DS_TAKE                  = reg_PC_NEXT_IS_DS_TAKE                 ;
	      }

	    reg_PC_CURRENT_INST_IFETCH_PTR             = reg_PC_NEXT_INST_IFETCH_PTR            ;
	    reg_PC_CURRENT_BRANCH_STATE                = reg_PC_NEXT_BRANCH_STATE               ;
	    reg_PC_CURRENT_BRANCH_UPDATE_PREDICTION_ID = reg_PC_NEXT_BRANCH_UPDATE_PREDICTION_ID;

	    for (uint32_t i=0; i<_param->_nb_instruction; i++)
	      reg_PC_CURRENT_INSTRUCTION_ENABLE [i] = reg_PC_NEXT_INSTRUCTION_ENABLE [i];

	    // have not next pc
	    reg_PC_NEXT_VAL                            = 0;
	  }
	
	// =========================================
	// ===== PREDICT ===========================
	// =========================================
	bool new_pc_current = not reg_PC_CURRENT_VAL;
	if (PORT_READ(in_PREDICT_ACK) and internal_PREDICT_VAL)
	  if (new_pc_current)
	    {
	      reg_PC_CURRENT_VAL                         = 1;
	      reg_PC_CURRENT                             = PORT_READ(in_PREDICT_PC_NEXT                    );
	      reg_PC_CURRENT_IS_DS_TAKE                  = PORT_READ(in_PREDICT_PC_NEXT_IS_DS_TAKE         );
	      if (_param->_have_port_instruction_ptr)
	      reg_PC_CURRENT_INST_IFETCH_PTR             = PORT_READ(in_PREDICT_INST_IFETCH_PTR            );
	      reg_PC_CURRENT_BRANCH_STATE                = PORT_READ(in_PREDICT_BRANCH_STATE               );
	      if (_param->_have_port_branch_update_prediction_id)
	      reg_PC_CURRENT_BRANCH_UPDATE_PREDICTION_ID = PORT_READ(in_PREDICT_BRANCH_UPDATE_PREDICTION_ID);

	      for (uint32_t i=0; i<_param->_nb_instruction; i++)
		reg_PC_CURRENT_INSTRUCTION_ENABLE [i] = PORT_READ(in_PREDICT_INSTRUCTION_ENABLE [i]);
	    }
	  else
	    {
	      reg_PC_NEXT_VAL                         = 1;
	      reg_PC_NEXT                             = PORT_READ(in_PREDICT_PC_NEXT                    );
	      reg_PC_NEXT_IS_DS_TAKE                  = PORT_READ(in_PREDICT_PC_NEXT_IS_DS_TAKE         );
	      if (_param->_have_port_instruction_ptr)
	      reg_PC_NEXT_INST_IFETCH_PTR             = PORT_READ(in_PREDICT_INST_IFETCH_PTR            );
	      reg_PC_NEXT_BRANCH_STATE                = PORT_READ(in_PREDICT_BRANCH_STATE               );
	      if (_param->_have_port_branch_update_prediction_id)
	      reg_PC_NEXT_BRANCH_UPDATE_PREDICTION_ID = PORT_READ(in_PREDICT_BRANCH_UPDATE_PREDICTION_ID);

	      for (uint32_t i=0; i<_param->_nb_instruction; i++)
		reg_PC_NEXT_INSTRUCTION_ENABLE [i] = PORT_READ(in_PREDICT_INSTRUCTION_ENABLE [i]);
	    }

	// =========================================
	// ===== EVENT =============================
	// =========================================
	if (PORT_READ(in_EVENT_VAL) and internal_EVENT_ACK)
	  {
	    reg_PC_CURRENT_VAL                         = 1;
	    reg_PC_CURRENT                             = PORT_READ(in_EVENT_ADDRESS);
	    // Event is never is ds_take :
	    //  * branch miss speculation : can't be place a branch in delay slot
	    //  * load   miss speculation : the load is execute, the event_address is the next address (also the destination of branch)
	    //  * exception               : goto the first instruction of exception handler (also is not in delay slot).
	    reg_PC_CURRENT_IS_DS_TAKE                  = 0;
	    reg_PC_CURRENT_INST_IFETCH_PTR             = 0;
	    reg_PC_CURRENT_BRANCH_STATE                = BRANCH_STATE_NONE;
	    reg_PC_CURRENT_BRANCH_UPDATE_PREDICTION_ID = 0;
	    
	    reg_PC_CURRENT_INSTRUCTION_ENABLE [0]      = 1; // only the instruction at the event address is valid, because we have no information on the branch presence in the instruction bundle.
	    for (uint32_t i=1; i<_param->_nb_instruction; i++)
	      reg_PC_CURRENT_INSTRUCTION_ENABLE [i] = 0;

	    reg_PC_NEXT_VAL                            = 0; // cancel all prediction (event is send at the predict unit)
	  }
      }

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

    log_printf(FUNC,Address_management,FUNCTION,"End");
  };

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

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