#ifdef SYSTEMC
/*
 * $Id: Address_management_transition.cpp 98 2008-12-31 10:18:08Z rosiere $
 *
 * [ 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_begin(Address_management,FUNCTION);
    log_function(Address_management,FUNCTION,_name.c_str());

    if (PORT_READ(in_NRESET) == 0)
      {
	// nothing is valid
	reg_PC_CURRENT_VAL   = 0;

        reg_PC_NEXT_VAL      = 1;
        reg_PC_NEXT          = 0x100>>2;

	reg_PC_NEXT_NEXT_VAL = 0;
      }
    else
      {
	// =========================================
	// ===== PREDICT ===========================
	// =========================================
	if (PORT_READ(in_PREDICT_ACK) and internal_PREDICT_VAL)
	  {
	    for (uint32_t i=0; i<_param->_nb_instruction; i++)
	    reg_PC_NEXT_INSTRUCTION_ENABLE [i] = PORT_READ(in_PREDICT_INSTRUCTION_ENABLE [i]);
	    if (_param->_have_port_inst_ifetch_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_depth)
	    reg_PC_NEXT_BRANCH_UPDATE_PREDICTION_ID = PORT_READ(in_PREDICT_BRANCH_UPDATE_PREDICTION_ID);
	    
	    reg_PC_NEXT_NEXT_VAL                    = 1; // address is valid
	    reg_PC_NEXT_NEXT                        = PORT_READ(in_PREDICT_PC_NEXT                    );
	    reg_PC_NEXT_NEXT_IS_DS_TAKE             = PORT_READ(in_PREDICT_PC_NEXT_IS_DS_TAKE         );

#ifdef STATISTICS
            if (usage_is_set(_usage,USE_STATISTICS))
              (*_stat_nb_transaction_predict) ++;
#endif
	  }

	// =========================================
	// ===== ADDRESS ===========================
	// =========================================
	// transaction with icache
	if ( (internal_ADDRESS_VAL and PORT_READ(in_ADDRESS_ACK)) or not reg_PC_CURRENT_VAL)
	  {
#ifdef STATISTICS
            if (usage_is_set(_usage,USE_STATISTICS))
              if (reg_PC_CURRENT_VAL)
                {
                  (*_stat_nb_transaction_address) ++;
                  
                  for (uint32_t i=0; i<_param->_nb_instruction; i++)
                    if (reg_PC_CURRENT_INSTRUCTION_ENABLE [i] == true)
                      (*_stat_sum_packet_size) ++;
                }
#endif


	    Tcontrol_t pc_next_val = reg_PC_NEXT_VAL and reg_PC_NEXT_NEXT_VAL;

	    // next pc became current pc
	    reg_PC_CURRENT_VAL                             = pc_next_val;

	    // 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 (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];
	    
		reg_PC_NEXT_VAL                            = reg_PC_NEXT_NEXT_VAL       ;
		// if pc_next_next is not valid : don't erase PC_NEXT and PC_NEXT_IS_DS_TAKE : this register is send a the predict (to compute pc_next)
		if (reg_PC_NEXT_NEXT_VAL)
		  {
		    reg_PC_NEXT                            = reg_PC_NEXT_NEXT           ;
		    reg_PC_NEXT_IS_DS_TAKE                 = reg_PC_NEXT_NEXT_IS_DS_TAKE;
		  }
		
		// invalid next next pc
		reg_PC_NEXT_NEXT_VAL                       = 0;
	      }

	  }
	

	// =========================================
	// ===== EVENT =============================
	// =========================================
	if (PORT_READ(in_EVENT_VAL) and internal_EVENT_ACK)
	  {
	    log_printf(TRACE,Address_management,FUNCTION,"  * EVENT : Transaction");
	    log_printf(TRACE,Address_management,FUNCTION,"    * IS_DS_TAKE       : %d"  ,PORT_READ(in_EVENT_IS_DS_TAKE      ));
	    log_printf(TRACE,Address_management,FUNCTION,"    * ADDRESS          : %.8x (%.8x)",PORT_READ(in_EVENT_ADDRESS         ),PORT_READ(in_EVENT_ADDRESS         )<<2);
	    log_printf(TRACE,Address_management,FUNCTION,"    * ADDRESS_NEXT     : %.8x (%.8x)",PORT_READ(in_EVENT_ADDRESS_NEXT    ),PORT_READ(in_EVENT_ADDRESS_NEXT    )<<2);
	    log_printf(TRACE,Address_management,FUNCTION,"    * ADDRESS_NEXT_VAL : %d"  ,PORT_READ(in_EVENT_ADDRESS_NEXT_VAL));
	    reg_PC_CURRENT_VAL                      = 0;
	    reg_PC_NEXT_VAL                         = 1;
	    reg_PC_NEXT                             = 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_NEXT_IS_DS_TAKE                  = PORT_READ(in_EVENT_IS_DS_TAKE);
// 	    reg_PC_NEXT_INST_IFETCH_PTR             = 0;
// 	    reg_PC_NEXT_BRANCH_STATE                = BRANCH_STATE_NONE;
// 	    reg_PC_NEXT_BRANCH_UPDATE_PREDICTION_ID = 0;
	    
	    reg_PC_NEXT_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_NEXT_INSTRUCTION_ENABLE [i] = 0;

            reg_PC_NEXT_NEXT_VAL                    = PORT_READ(in_EVENT_ADDRESS_NEXT_VAL);
            reg_PC_NEXT_NEXT                        = PORT_READ(in_EVENT_ADDRESS_NEXT);
            reg_PC_NEXT_NEXT_IS_DS_TAKE             = 0;//??

            // Note : is_ds_take = address_next_val
            // Because, is not ds take, can continue in sequence

// #ifdef DEBUG_TEST
//             if (PORT_READ(in_EVENT_ADDRESS_NEXT_VAL) and not PORT_READ(in_EVENT_IS_DS_TAKE))
//               throw ERRORMORPHEO(FUNCTION,_("Event : address_next_next_val but next is not a ds take"));
// #endif

#ifdef STATISTICS
            if (usage_is_set(_usage,USE_STATISTICS))
              (*_stat_nb_transaction_event) ++;
#endif
	  }
      }

#if defined(DEBUG) and (DEBUG >= DEBUG_TRACE)
    log_printf(TRACE,Address_management,FUNCTION,"  * Dump PC");
    log_printf(TRACE,Address_management,FUNCTION,"    * Current   : %d %d 0x%.8x (%.8x)",reg_PC_CURRENT_VAL  , reg_PC_CURRENT_IS_DS_TAKE  , reg_PC_CURRENT  , reg_PC_CURRENT  <<2);
    log_printf(TRACE,Address_management,FUNCTION,"    * Next      : %d %d 0x%.8x (%.8x)",reg_PC_NEXT_VAL     , reg_PC_NEXT_IS_DS_TAKE     , reg_PC_NEXT     , reg_PC_NEXT     <<2);    
    log_printf(TRACE,Address_management,FUNCTION,"    * Next_Next : %d %d 0x%.8x (%.8x)",reg_PC_NEXT_NEXT_VAL, reg_PC_NEXT_NEXT_IS_DS_TAKE, reg_PC_NEXT_NEXT, reg_PC_NEXT_NEXT<<2);    
#endif 

#if defined(STATISTICS) or defined(VHDL_TESTBENCH)
    end_cycle ();
#endif
    
    log_end(Address_management,FUNCTION);
  };

}; // 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
