#ifdef SYSTEMC
/*
 * $Id: Address_management_transition.cpp 123 2009-06-08 20:43:30Z 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_ACCESS_VAL    = 0;

	reg_PC_CURRENT_VAL   = 0;

        reg_PC_NEXT_VAL      = 1;
        reg_PC_NEXT          = 0x100>>2;
        reg_PC_NEXT_IS_DS_TAKE                  = 0;
        uint32_t index = reg_PC_NEXT % _param->_nb_instruction;
        for (uint32_t i=0; i<_param->_nb_instruction; i++)
        reg_PC_NEXT_INSTRUCTION_ENABLE [i]      = 0;
        reg_PC_NEXT_INSTRUCTION_ENABLE [index]  = 1;
        reg_PC_NEXT_INST_IFETCH_PTR             = 0;
        reg_PC_NEXT_BRANCH_STATE                = 0;
        reg_PC_NEXT_BRANCH_UPDATE_PREDICTION_ID = 0;


	reg_PC_NEXT_NEXT_VAL = 0;
      }
    else
      {
	// =========================================
	// ===== PREDICT ===========================
	// =========================================
	if (PORT_READ(in_PREDICT_ACK) and internal_PREDICT_VAL)
	  {
	    log_printf(TRACE,Address_management,FUNCTION,"  * PREDICT");

            bool branch_is_current = reg_PC_NEXT_IS_DS_TAKE;
            if (branch_is_current)
              {
                if (_param->_have_port_inst_ifetch_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_depth)
                reg_PC_CURRENT_BRANCH_UPDATE_PREDICTION_ID = PORT_READ(in_PREDICT_BRANCH_UPDATE_PREDICTION_ID);
              }
            else
              {
                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);
              }

	    for (uint32_t i=0; i<_param->_nb_instruction; i++)
	    reg_PC_NEXT_INSTRUCTION_ENABLE [i] = PORT_READ(in_PREDICT_INSTRUCTION_ENABLE [i]);
	    
	    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))
          {

            reg_PC_ACCESS_VAL = 0;
#ifdef STATISTICS
            if (usage_is_set(_usage,USE_STATISTICS))
              {
                  (*_stat_nb_transaction_address) ++;
                  
                  for (uint32_t i=0; i<_param->_nb_instruction; i++)
                    if (reg_PC_ACCESS_INSTRUCTION_ENABLE [i] == true)
                      (*_stat_sum_packet_size) ++;
                }
#endif
          }

	// =========================================
	// ===== Shift Register ====================
	// =========================================
        
        // Shift register
        if (reg_PC_NEXT_NEXT_VAL and reg_PC_NEXT_VAL and reg_PC_CURRENT_VAL and not reg_PC_ACCESS_VAL)
          {
	    log_printf(TRACE,Address_management,FUNCTION,"  * New PC_ACCESS");

            reg_PC_ACCESS_VAL  = 1; // new request
            reg_PC_CURRENT_VAL = 0; // invalid current
            
            reg_PC_ACCESS                             = reg_PC_CURRENT                            ;
            reg_PC_ACCESS_IS_DS_TAKE                  = reg_PC_CURRENT_IS_DS_TAKE                 ;
            reg_PC_ACCESS_INST_IFETCH_PTR             = reg_PC_CURRENT_INST_IFETCH_PTR            ;
            reg_PC_ACCESS_BRANCH_STATE                = reg_PC_CURRENT_BRANCH_STATE               ;
            reg_PC_ACCESS_BRANCH_UPDATE_PREDICTION_ID = reg_PC_CURRENT_BRANCH_UPDATE_PREDICTION_ID;
            
            for (uint32_t i=0; i<_param->_nb_instruction; i++)
              reg_PC_ACCESS_INSTRUCTION_ENABLE [i] = reg_PC_CURRENT_INSTRUCTION_ENABLE [i];
          }
        
//      if (not reg_PC_CURRENT_VAL and reg_PC_NEXT_VAL)
        if (reg_PC_NEXT_NEXT_VAL and reg_PC_NEXT_VAL and not reg_PC_CURRENT_VAL)
          {
	    log_printf(TRACE,Address_management,FUNCTION,"  * New PC_CURRENT");

            reg_PC_CURRENT_VAL = 1; // new PC_CURRENT if PC_NEXT is valid
            reg_PC_NEXT_VAL    = 0; // invalid next

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

//      if (not reg_PC_NEXT_VAL and reg_PC_NEXT_NEXT_VAL)
        if (reg_PC_NEXT_NEXT_VAL and not reg_PC_NEXT_VAL)
          {
	    log_printf(TRACE,Address_management,FUNCTION,"  * New PC_NEXT");

            reg_PC_NEXT_VAL      = 1; // new PC_NEXT if PC_NEXT_NEXT is valid
            reg_PC_NEXT_NEXT_VAL = 0; // invalid next_next
            
            reg_PC_NEXT                             = reg_PC_NEXT_NEXT                            ;
            reg_PC_NEXT_IS_DS_TAKE                  = reg_PC_NEXT_NEXT_IS_DS_TAKE                 ;
//          reg_PC_NEXT_INST_IFETCH_PTR             = reg_PC_NEXT_NEXT_INST_IFETCH_PTR            ;
//          reg_PC_NEXT_BRANCH_STATE                = reg_PC_NEXT_NEXT_BRANCH_STATE               ;
//          reg_PC_NEXT_BRANCH_UPDATE_PREDICTION_ID = reg_PC_NEXT_NEXT_BRANCH_UPDATE_PREDICTION_ID;
            
//          for (uint32_t i=0; i<_param->_nb_instruction; i++)
//            reg_PC_NEXT_INSTRUCTION_ENABLE [i] = reg_PC_NEXT_NEXT_INSTRUCTION_ENABLE [i];
          }

	// =========================================
	// ===== EVENT =============================
	// =========================================

        // Event is after shift register : because, it's to write in pc_next and in not pc_current

	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_ACCESS_VAL                       = 0;
	    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;
	    
            // only the instruction at the event address is valid, because we have no information on the branch presence in the instruction bundle.
            uint32_t index = reg_PC_NEXT % _param->_nb_instruction;
            for (uint32_t i=0; i<_param->_nb_instruction; i++)
            reg_PC_NEXT_INSTRUCTION_ENABLE [i]      = 0;
            reg_PC_NEXT_INSTRUCTION_ENABLE [index]  = 1;

            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_Address_management and (DEBUG >= DEBUG_TRACE)
    log_printf(TRACE,Address_management,FUNCTION,"  * Dump PC");
    {
      std::string instruction_enable;
      for (uint32_t i=0; i<_param->_nb_instruction; ++i)
        instruction_enable += ((reg_PC_ACCESS_VAL)?(toString(reg_PC_ACCESS_INSTRUCTION_ENABLE [i])):"X") + " ";

      log_printf(TRACE,Address_management,FUNCTION,"    * Access    : %d %d 0x%.8x (%.8x) - %.2d %.2d %.2d - %s",
                 reg_PC_ACCESS_VAL,
                 reg_PC_ACCESS_IS_DS_TAKE,
                 reg_PC_ACCESS,
                 reg_PC_ACCESS<<2,
                 reg_PC_ACCESS_BRANCH_STATE,
                 reg_PC_ACCESS_INST_IFETCH_PTR,
                 reg_PC_ACCESS_BRANCH_UPDATE_PREDICTION_ID,
                 instruction_enable.c_str()
                 );
    }
    {
      std::string instruction_enable;
      for (uint32_t i=0; i<_param->_nb_instruction; ++i)
        instruction_enable += ((reg_PC_CURRENT_VAL)?(toString(reg_PC_CURRENT_INSTRUCTION_ENABLE [i])):"X") + " ";

      log_printf(TRACE,Address_management,FUNCTION,"    * Current   : %d %d 0x%.8x (%.8x) - %.2d %.2d %.2d - %s",
                 reg_PC_CURRENT_VAL,
                 reg_PC_CURRENT_IS_DS_TAKE,
                 reg_PC_CURRENT,
                 reg_PC_CURRENT<<2,
                 reg_PC_CURRENT_BRANCH_STATE,
                 reg_PC_CURRENT_INST_IFETCH_PTR,
                 reg_PC_CURRENT_BRANCH_UPDATE_PREDICTION_ID,
                 instruction_enable.c_str()
                 );
    }
    {
      std::string instruction_enable;
      for (uint32_t i=0; i<_param->_nb_instruction; ++i)
        instruction_enable += ((reg_PC_NEXT_VAL)?(toString(reg_PC_NEXT_INSTRUCTION_ENABLE [i])):"X") + " ";

      log_printf(TRACE,Address_management,FUNCTION,"    * Next      : %d %d 0x%.8x (%.8x) - %.2d %.2d %.2d - %s",
                 reg_PC_NEXT_VAL,
                 reg_PC_NEXT_IS_DS_TAKE,
                 reg_PC_NEXT,
                 reg_PC_NEXT<<2,
                 reg_PC_NEXT_BRANCH_STATE,
                 reg_PC_NEXT_INST_IFETCH_PTR,
                 reg_PC_NEXT_BRANCH_UPDATE_PREDICTION_ID,
                 instruction_enable.c_str());
    }
    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
