#ifdef SYSTEMC
/*
 * $Id: Return_Address_Stack_transition.cpp 95 2008-12-16 16:24:26Z rosiere $
 *
 * [ Description ]
 * 
 */

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

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


#undef  FUNCTION
#define FUNCTION "Return_Address_Stack::transition"
  void Return_Address_Stack::transition (void)
  {
    log_printf(FUNC,Return_Address_Stack,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_PREDICT_TOP    [i] = 0;
	    reg_PREDICT_BOTTOM [i] = 0;
	    reg_PREDICT_NB_ELT [i] = 0;

	    for (uint32_t j=0; j<_param->_size_queue [i]; j++)
	      {
		reg_stack[i][j]._val     = false;
		reg_stack[i][j]._predict = false;
		reg_stack[i][j]._miss    = false;
	      }
	  }
      }
    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])
	    {
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"PREDICT[%d] : Transaction",i);
	      Tcontext_t context    = (_param->_have_port_context_id)?PORT_READ(in_PREDICT_CONTEXT_ID [i]):0;
	      Tcontrol_t push       = PORT_READ(in_PREDICT_PUSH [i]);
	      Tptr_t     top_old    = reg_PREDICT_TOP    [context];
	      Tptr_t     top_new    = top_old;
	      Tptr_t     bottom_old = reg_PREDICT_BOTTOM [context];

	      log_printf(TRACE,Return_Address_Stack,FUNCTION," * context : %d",context);

	      // Hit  : push or (val and not miss and not empty)
	      // Miss : ifetch is stall, no update
	      if (internal_PREDICT_HIT [i])
		{
		  log_printf(TRACE,Return_Address_Stack,FUNCTION," * before");
		  log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_predict_top     : %d",reg_PREDICT_TOP    [context]);
		  log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_predict_bottom  : %d",reg_PREDICT_BOTTOM [context]);
		  log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_predict_nb_elt  : %d",reg_PREDICT_NB_ELT [context]);

		  if (push)
		    {
		      // push : increase the top (circular)
		      top_new = (top_old+1)%_param->_size_queue[context];
		      
		      reg_stack [context][top_new]._val     = true; // New addr
		      reg_stack [context][top_new]._predict = true; // Is speculative (erase a old addr (or not))
		    //reg_stack [context][top_new]._miss    = ;
		      reg_stack [context][top_new]._address = PORT_READ(in_PREDICT_ADDRESS_PUSH [i]);

		      // the stack is full, erase the most old stack

		      // Test if full
		      if (reg_PREDICT_NB_ELT[context]==_param->_size_queue[context])
			reg_PREDICT_BOTTOM [context] = (bottom_old+1)%_param->_size_queue[context];
		      // A new data is write : the stack is not empty
		      if (reg_PREDICT_NB_ELT[context]< _param->_size_queue[context])
			reg_PREDICT_NB_ELT[context]++;
		    }
		  else
		    {
		      // pop
// 		      top_new = (top_old==0)?(_param->_size_queue[context]-1):(top_old-1);
		      
		    //reg_stack [context][top_new]._val     = ;
		    //reg_stack [context][top_new]._predict = ;
		    //reg_stack [context][top_new]._miss    = ;
		    //reg_stack [context][top_new]._address = ; 

		      // the stack is empty
		      if (reg_PREDICT_NB_ELT[context]>0)
			{
			  top_new = (top_old==0)?(_param->_size_queue[context]-1):(top_old-1);
			  reg_PREDICT_NB_ELT[context] --;
			}
		    }
		  
		  reg_PREDICT_TOP [context] = top_new;

		  log_printf(TRACE,Return_Address_Stack,FUNCTION," * after");
		  log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_predict_top     : %d",reg_PREDICT_TOP    [context]);
		  log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_predict_bottom  : %d",reg_PREDICT_BOTTOM [context]);
		  log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_predict_nb_elt  : %d",reg_PREDICT_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])
	    {
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"DECOD[%d] : Transaction",i);
	      Tcontext_t context    = (_param->_have_port_context_id)?PORT_READ(in_DECOD_CONTEXT_ID [i]):0;
	      Tcontrol_t push       = PORT_READ(in_DECOD_PUSH [i]);
	      Tptr_t     top_old    = reg_TOP    [context];
	      Tptr_t     top_new    = top_old;
	      Tptr_t     bottom_old = reg_BOTTOM [context];
	      //Tcontrol_t hit        = internal_DECOD_HIT [i];
	      Tcontrol_t miss       = PORT_READ(in_DECOD_MISS_PREDICTION [i]);

	      log_printf(TRACE,Return_Address_Stack,FUNCTION," * context : %d",context);

	      log_printf(TRACE,Return_Address_Stack,FUNCTION," * before");
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_top     : %d",reg_TOP    [context]);
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_bottom  : %d",reg_BOTTOM [context]);
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_nb_elt  : %d",reg_NB_ELT [context]);

	      if (push)
		{
		  // push : increase the top (circular)
		  top_new = (top_old+1)%_param->_size_queue[context];
		  
		  reg_stack [context][top_new]._val     = true;  // New address
		  reg_stack [context][top_new]._predict = false; // No speculative
		  reg_stack [context][top_new]._miss    = false;
		  reg_stack [context][top_new]._address = PORT_READ(in_DECOD_ADDRESS_PUSH [i]);
		  
		  // Test if full : if true, then icrease the bottom (erase the most old stack)
		  if (reg_NB_ELT[context]==_param->_size_queue[context])
		    reg_BOTTOM [context] = (bottom_old+1)%_param->_size_queue[context];
		  // A new data is write : the stack is not empty
		  if (reg_NB_ELT[context]< _param->_size_queue[context])
		    reg_NB_ELT[context]++;
		}
	      else
		{
		  // pop
// 		  top_new = (top_old==0)?(_param->_size_queue[context]-1):(top_old-1);

		  //reg_stack [context][top_new]._val     = ;
		  //reg_stack [context][top_new]._predict = ;
		  //reg_stack [context][top_new]._miss    = ;
		  //reg_stack [context][top_new]._address = ; 
		  
		  // the stack is empty
		  if (reg_NB_ELT[context]>0)
		    {
		      top_new = (top_old==0)?(_param->_size_queue[context]-1):(top_old-1);
		      reg_NB_ELT[context] --;
		    }
		}
	      
	      reg_TOP [context] = top_new;
	      
	      log_printf(TRACE,Return_Address_Stack,FUNCTION," * after");
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_top     : %d",reg_TOP    [context]);
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_bottom  : %d",reg_BOTTOM [context]);
	      log_printf(TRACE,Return_Address_Stack,FUNCTION,"   * reg_nb_elt  : %d",reg_NB_ELT [context]);

	      // have previous miss of ifetch ?
	      // 2 miss : 
	      //   1) miss predict : is very limited (local at context), can be update very quickly
	      //   2) miss decod   : result is in commit stage ... 
	      if (miss)
		{
		  reg_PREDICT_BOTTOM [context] = reg_BOTTOM [context];
		  reg_PREDICT_TOP    [context] = reg_TOP    [context];
		  reg_PREDICT_NB_ELT [context] = reg_NB_ELT [context];
		  
		  // Scan full assoc !!!
		  for (uint32_t j=0; j<_param->_size_queue [context]; j++)
		    // Test if this slot is tagged with "predict" : if true, tagged as miss
		    if (reg_stack [context][j]._predict)
		      {
			reg_stack [context][j]._predict = false;
			reg_stack [context][j]._miss    = true;
		      }
		}
	    }

	// ===================================================================
	// =====[ UPDATE ]===================================================
	// ===================================================================
	for (uint32_t i=0; i<_param->_nb_inst_update; i++)
	  if (PORT_READ(in_UPDATE_VAL [i]) and internal_UPDATE_ACK [i])
	    {
	      throw ERRORMORPHEO(FUNCTION,"Fonction ŕ implémenter !!!!!!!!!!!!");
	      

// 	      Tcontrol_t miss   = PORT_READ(in_UPDATE_MISS_PREDICTION [i]);
// 	      // 
// 	      if (miss)
// 		{
// 		  Tcontrol_t context = (_param->_have_port_context_id)?PORT_READ(in_UPDATE_CONTEXT_ID [i]):0;
// 		  Tcontrol_t ifetch  = PORT_READ(in_UPDATE_PREDICTION_IFETCH [i]);
// 		  Tcontrol_t push    = PORT_READ(in_UPDATE_PUSH             [i]);
// 		  Tcontrol_t flush   = PORT_READ(in_UPDATE_FLUSH            [i]);
// 		  Tptr_t     index   = PORT_READ(in_UPDATE_INDEX            [i]);
// 		  Taddress_t address = PORT_READ(in_UPDATE_ADDRESS          [i]);
		  
// 		  if (push)
// 		    {
// // 		      // push
// // 		      top_new = (top_old+1)%_param->_size_queue[context];
		      
// // 		      reg_stack [context][index]._val     = true;
// // 		      reg_stack [context][index]._predict = false;
// // 		      reg_stack [context][index]._miss    = false;
// // 		      reg_stack [context][index]._address = PORT_READ(in_UPDATE_ADDRESS [i]);

// 		    }
// 		  else
// 		    {
// // 		    //reg_stack [context][top_new]._val     = ;
// // 		    //reg_stack [context][top_new]._predict = ;
// // 		    //reg_stack [context][top_new]._miss    = ;
// // 		    //reg_stack [context][top_new]._address = ; 
// 		    }
		  
// // // 		  // Mouais bof .......
// // // 		  reg_PREDICT_TOP [context] = index;
// 		}
	    }
      }

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

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

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

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