#ifdef SYSTEMC
/*
 * $Id: Update_Prediction_Table_transition.cpp 82 2008-05-01 16:48:45Z rosiere $
 *
 * [ Description ]
 * 
 */

#include "Behavioural/Core/Multi_Front_end/Front_end/Prediction_unit/Update_Prediction_Table/include/Update_Prediction_Table.h"

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_front_end {
namespace front_end {
namespace prediction_unit {
namespace update_prediction_table {


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

    if (PORT_READ(in_NRESET) == 0)
      {
	for (uint32_t i=0; i<_param->_nb_context; i++)
	  {
	    reg_TOP                [i] = 0;
	    reg_BOTTOM             [i] = 0;
	    reg_NB_ELT             [i] = 0;
	    reg_NB_ELT_UPDATE      [i] = 0;
	    reg_NB_ELT_NEED_UPDATE [i] = 0;
	    for (uint32_t j=0; j<_param->_size_queue[i]; j++)
	      reg_UPDATE_PREDICTION_TABLE [i][j]._state = UPDATE_PREDICTION_STATE_EMPTY;
	  }
	reg_UPDATE_PRIORITY = 0;
      }
    else
      {
	// ===================================================================
	// =====[ PREDICT ]===================================================
	// ===================================================================
	for (uint32_t i=0; i<_param->_nb_inst_predict; i++)
	  if (PORT_READ(in_PREDICT_VAL[i]) and internal_PREDICT_ACK [i])
	    {
	      Tcontext_t context = (_param->_have_port_context_id)?PORT_READ(in_PREDICT_CONTEXT_ID [i]):0;
	      uint32_t   top     = reg_TOP [context];

	      log_printf(TRACE,Update_Prediction_Table,FUNCTION,"PREDICT[%d] - Accepted",i);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * context : %d",context);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * top     : %d",top);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * nb_elt  : %d",reg_NB_ELT[context]);

	      reg_UPDATE_PREDICTION_TABLE [context][top]._state        = UPDATE_PREDICTION_STATE_WAIT_DECOD;
	      reg_UPDATE_PREDICTION_TABLE [context][top]._ifetch_prediction = true;
	      reg_UPDATE_PREDICTION_TABLE [context][top]._condition    = PORT_READ(in_PREDICT_BTB_CONDITION    [i]);
	      reg_UPDATE_PREDICTION_TABLE [context][top]._address_src  = PORT_READ(in_PREDICT_BTB_ADDRESS_SRC  [i]);
	      reg_UPDATE_PREDICTION_TABLE [context][top]._address_dest = PORT_READ(in_PREDICT_BTB_ADDRESS_DEST [i]);
	      reg_UPDATE_PREDICTION_TABLE [context][top]._last_take    = PORT_READ(in_PREDICT_BTB_LAST_TAKE    [i]);
	    //reg_UPDATE_PREDICTION_TABLE [context][top]._good_take    
	      reg_UPDATE_PREDICTION_TABLE [context][top]._history      = (_param->_have_port_history)?PORT_READ(in_PREDICT_DIR_HISTORY [i]):0;
	      reg_UPDATE_PREDICTION_TABLE [context][top]._address_ras  = PORT_READ(in_PREDICT_RAS_ADDRESS      [i]);
	      reg_UPDATE_PREDICTION_TABLE [context][top]._index_ras    = PORT_READ(in_PREDICT_RAS_INDEX        [i]);

	      reg_TOP    [context] = (top+1)%_param->_size_queue [context];
	      reg_NB_ELT [context] ++;
	    }

	// ===================================================================
	// =====[ DECOD ]=====================================================
	// ===================================================================
	for (uint32_t i=0; i<_param->_nb_inst_decod; i++)
	  if (PORT_READ(in_DECOD_VAL[i]) and internal_DECOD_ACK [i])
	    {
	      Tcontext_t context     = (_param->_have_port_context_id)?PORT_READ(in_DECOD_CONTEXT_ID [i]):0;
	      Tcontrol_t miss_ifetch = PORT_READ(in_DECOD_MISS_IFETCH [i]);
	      Tcontrol_t miss_decod  = PORT_READ(in_DECOD_MISS_DECOD  [i]);

	      log_printf(TRACE,Update_Prediction_Table,FUNCTION,"DECOD[%d] - Accepted",i);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * context     : %d",context);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * miss_ifetch : %d",miss_ifetch);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * miss_decod  : %d",miss_decod);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * nb_elt      : %d",reg_NB_ELT[context]);
	      
	      // Test if miss ifetch
	      //   miss ifetch = decod a branch and the predict unit have not predict this branch ... gloup :P
	      if (miss_ifetch or miss_decod)
		{
		  Tdepth_t depth = (_param->_have_port_max_depth)?PORT_READ(in_DECOD_UPDATE_PREDICTION_ID [i]):0;
		  Tdepth_t top   = reg_TOP [context];

		  reg_UPDATE_PREDICTION_TABLE [context][top]._state        = UPDATE_PREDICTION_STATE_WAITEND;
		  reg_UPDATE_PREDICTION_TABLE [context][top]._ifetch_prediction = false; // static prediction
		  reg_UPDATE_PREDICTION_TABLE [context][top]._condition    = PORT_READ(in_DECOD_BTB_CONDITION    [i]);
		  reg_UPDATE_PREDICTION_TABLE [context][top]._address_src  = PORT_READ(in_DECOD_BTB_ADDRESS_SRC  [i]);
		  reg_UPDATE_PREDICTION_TABLE [context][top]._address_dest = PORT_READ(in_DECOD_BTB_ADDRESS_DEST [i]);
		  reg_UPDATE_PREDICTION_TABLE [context][top]._last_take    = PORT_READ(in_DECOD_BTB_LAST_TAKE    [i]);
		//reg_UPDATE_PREDICTION_TABLE [context][top]._good_take
		//reg_UPDATE_PREDICTION_TABLE [context][top]._history
		  reg_UPDATE_PREDICTION_TABLE [context][top]._address_ras  = PORT_READ(in_DECOD_RAS_ADDRESS      [i]);
		  reg_UPDATE_PREDICTION_TABLE [context][top]._index_ras    = PORT_READ(in_DECOD_RAS_INDEX        [i]);

		  // Invalid all previous "ifetch" prediction 
		  // (ifetch prediction = prediction make in fetch stage and not again comming in decod stage)
		  uint32_t nb_elt_miss        = ((top> depth)?top:(top+_param->_size_queue[context]))-depth;
		  uint32_t nb_elt_need_update = 0;
		  for (uint32_t j=0; j<nb_elt_miss; j++)
		    {
		      uint32_t k=(depth+j)%_param->_size_queue[context];
		      
		      // Ifetch have make a prediction and it's a miss
		      // When ifetch predict :
		      //   * btb is not change       -> needn't update
		      //   * direction is not change -> needn't update
		      //   * ras have change         -> need    update

		      Tbranch_condition_t cond = reg_UPDATE_PREDICTION_TABLE [context][k]._condition;
		      if ((cond == BRANCH_CONDITION_NONE_WITH_WRITE_STACK) or
			  (cond == BRANCH_CONDITION_READ_REGISTER_WITH_WRITE_STACK) or
			  (cond == BRANCH_CONDITION_READ_STACK))
			{
			  nb_elt_need_update ++;
			  reg_UPDATE_PREDICTION_TABLE [context][k]._state = UPDATE_PREDICTION_STATE_KO;
			}
		      else
			{
			  reg_UPDATE_PREDICTION_TABLE [context][k]._state = UPDATE_PREDICTION_STATE_END;
			}
		    }
		  reg_UPDATE_PREDICTION_TABLE [context][depth]._state = UPDATE_PREDICTION_STATE_EVENT; // rewrite

		  reg_TOP                [context] = (depth+1)%_param->_size_queue [context];
		  reg_NB_ELT_NEED_UPDATE [context] += nb_elt_need_update;
		  reg_NB_ELT             [context] ++;
		}
	      else
		{
		  // Normal case : branch is previous predicated, change state of branch
		  Tdepth_t depth = (_param->_have_port_max_depth)?PORT_READ(in_DECOD_UPDATE_PREDICTION_ID [i]):0;

		  reg_UPDATE_PREDICTION_TABLE [context][depth]._state = UPDATE_PREDICTION_STATE_WAITEND;
		}
	    }

	// ===================================================================
	// =====[ BRANCH_COMPLETE ]===========================================
	// ===================================================================
	for (uint32_t i=0; i<_param->_nb_inst_branch_complete; i++)
	  if (PORT_READ(in_BRANCH_COMPLETE_VAL[i]) and internal_BRANCH_COMPLETE_ACK [i])
	    {
	      Tcontext_t context = (_param->_have_port_context_id)?PORT_READ(in_BRANCH_COMPLETE_CONTEXT_ID [i]):0;
	      Tdepth_t   depth   = (_param->_have_port_max_depth     )?PORT_READ(in_BRANCH_COMPLETE_DEPTH      [i]):0;

	      log_printf(TRACE,Update_Prediction_Table,FUNCTION,"BRANCH_COMPLETE[%d] - Accepted",i);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * context : %d",context);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * depth   : %d",depth);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * nb_elt  : %d",reg_NB_ELT[context]);
	      
	      if (internal_BRANCH_COMPLETE_MISS_PREDICTION [i])
		{
		  // Miss case
		  reg_UPDATE_PREDICTION_TABLE [context][depth]._state = UPDATE_PREDICTION_STATE_EVENT;

		  // Another prediction (prediction with depth higer)
		  Tdepth_t top                = reg_TOP [context];
		  uint32_t nb_elt_miss        = ((top> depth)?top:(top+_param->_size_queue[context]))-depth;
		  uint32_t nb_elt_need_update = 1;
		  for (uint32_t j=1; j<nb_elt_miss; j++)
		    {
		      uint32_t k=(depth+j)%_param->_size_queue[context];
		      
		      // Ifetch have make a prediction and it's a miss
		      // When ifetch predict :
		      //   * btb is not change       -> needn't update
		      //   * direction is not change -> needn't update
		      //   * ras have change         -> need    update

		      Tbranch_condition_t cond = reg_UPDATE_PREDICTION_TABLE [context][k]._condition;
		      if ((cond == BRANCH_CONDITION_NONE_WITH_WRITE_STACK) or
			  (cond == BRANCH_CONDITION_READ_REGISTER_WITH_WRITE_STACK) or
			  (cond == BRANCH_CONDITION_READ_STACK))
			{
			  nb_elt_need_update ++;
			  reg_UPDATE_PREDICTION_TABLE [context][k]._state = UPDATE_PREDICTION_STATE_KO;
			}
		      else
			{
			  reg_UPDATE_PREDICTION_TABLE [context][k]._state = UPDATE_PREDICTION_STATE_END;
			}
		    }
		  reg_NB_ELT_NEED_UPDATE [context] += nb_elt_need_update;

		  // Update pointer :
		  reg_TOP                [context]  = depth;
		  reg_NB_ELT             [context] -= nb_elt_miss;
		}
	      else
		{
		  // Hit case
#ifdef DEBUG_TEST
		  if (reg_UPDATE_PREDICTION_TABLE [context][depth]._state != UPDATE_PREDICTION_STATE_WAITEND)
		    ERRORMORPHEO(FUNCTION,"Branche complete : invalid state");
#endif
		  reg_UPDATE_PREDICTION_TABLE [context][depth]._state = UPDATE_PREDICTION_STATE_OK;
		}

	      // In all case : update good_take and address_destination
	      reg_UPDATE_PREDICTION_TABLE [context][depth]._good_take    = internal_BRANCH_COMPLETE_TAKE         [i];
	      reg_UPDATE_PREDICTION_TABLE [context][depth]._address_dest = internal_BRANCH_COMPLETE_ADDRESS_DEST [i];
	    }

	// ===================================================================
	// =====[ UPDATE ]====================================================
	// ===================================================================
	for (uint32_t i=0; i<_param->_nb_inst_update; i++)
	  if (internal_UPDATE_VAL[i] and PORT_READ(in_UPDATE_ACK [i]))
	    {
	      Tcontext_t context = internal_UPDATE_CONTEXT_ID [i];
	      Tdepth_t   depth   = internal_UPDATE_DEPTH      [i];

	      log_printf(TRACE,Update_Prediction_Table,FUNCTION,"UPDATE[%d] - Accepted",i);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * context : %d",context);
	      log_printf(TRACE,Update_Prediction_Table,FUNCTION," * depth   : %d",depth);

	      if (reg_UPDATE_PREDICTION_TABLE [context][depth]._state == UPDATE_PREDICTION_STATE_KO)
		reg_NB_ELT_NEED_UPDATE [context] --;

	      reg_UPDATE_PREDICTION_TABLE [context][depth]._state = UPDATE_PREDICTION_STATE_END;
	    }
	
	// Round robin
	reg_UPDATE_PRIORITY = (reg_UPDATE_PRIORITY+1)%_param->_nb_context;

	// ===================================================================
	// =====[ BRANCH_EVENT ]==============================================
	// ===================================================================
	for (uint32_t i=0; i<_param->_nb_context; i++)
	  if (internal_BRANCH_EVENT_VAL [i] and PORT_READ(in_BRANCH_EVENT_ACK [i]))
	  {
	    Tdepth_t depth = internal_BRANCH_EVENT_DEPTH [i];
	    
	    reg_UPDATE_PREDICTION_TABLE [i][depth]._state = UPDATE_PREDICTION_STATE_KO;
	  }

	// ===================================================================
	// =====[ GARBAGE COLLECTOR ]=========================================
	// ===================================================================

	for (uint32_t i=0; i<_param->_nb_context; i++)
	  {
	    uint32_t bottom = reg_BOTTOM [i];
	    
	    // Test if the tail of queue is finish (ok or ko and update)
	    if (reg_UPDATE_PREDICTION_TABLE [i][bottom]._state == UPDATE_PREDICTION_STATE_END)
	      {
		log_printf(TRACE,Update_Prediction_Table,FUNCTION,"GARBAGE_COLLECTOR");
		log_printf(TRACE,Update_Prediction_Table,FUNCTION," * context : %d",i);
		log_printf(TRACE,Update_Prediction_Table,FUNCTION," * bottom  : %d",bottom);

		reg_UPDATE_PREDICTION_TABLE [i][bottom]._state = UPDATE_PREDICTION_STATE_EMPTY;
		reg_BOTTOM [i] = (bottom+1)%_param->_size_queue [i];
		if (reg_NB_ELT [i]>0)
		  reg_NB_ELT [i] --;
	      }
	  }
      }

#if (DEBUG >= DEBUG_TRACE)
    log_printf(TRACE,Update_Prediction_Table,FUNCTION,"Dump Update_Prediction_Table");
    log_printf(TRACE,Update_Prediction_Table,FUNCTION,"  * reg_UPDATE_PRIORITY      : %d",reg_UPDATE_PRIORITY);
    for (uint32_t i=0; i<_param->_nb_context; i++)
      {
	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"  * Update_Prediction_Table   [%d]",i);
	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * reg_TOP                : %d",reg_TOP                [i]);
	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * reg_BOTTOM             : %d",reg_BOTTOM             [i]);
	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * reg_NB_ELT             : %d",reg_NB_ELT             [i]);
	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * reg_NB_ELT_NEED_UPDATE : %d",reg_NB_ELT_NEED_UPDATE [i]);
	for (uint32_t j=0; j<_param->_size_queue[i]; j++)
	  log_printf(TRACE,Update_Prediction_Table,FUNCTION,"      [%d] %s %x",j,toString(reg_UPDATE_PREDICTION_TABLE [i][j]._state).c_str(),reg_UPDATE_PREDICTION_TABLE [i][j]._address_src);
      }
#endif


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

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

}; // end namespace update_prediction_table
}; // end namespace prediction_unit
}; // end namespace front_end
}; // end namespace multi_front_end
}; // end namespace core

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