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

#include "Behavioural/Core/Multi_Front_end/Front_end/Prediction_unit/Branch_Target_Buffer/Branch_Target_Buffer_Register/include/Branch_Target_Buffer_Register.h"

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_front_end {
namespace front_end {
namespace prediction_unit {
namespace branch_target_buffer {
namespace branch_target_buffer_register {

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

    if (PORT_READ(in_NRESET) == 0)
      {
	for (uint32_t i=0; i<_param->_size_bank; i++)
	  for (uint32_t j=0; j<_param->_associativity; j++)
	    reg_BTB [i][j]._val = false;
      }
    else
      {
	if (not _param->_have_port_victim)
	  {
	    genMealy_decod  ();
	    genMealy_update ();
	  }

	// =======================================================
	// =====[ PREDICT ]=======================================
	// =======================================================

	// =======================================================
	// =====[ DECOD ]=========================================
	// =======================================================
	for (uint32_t i=0; i<_param->_nb_inst_decod; i++)
	  if (PORT_READ(in_DECOD_VAL [i]) and internal_DECOD_ACK [i])
	    {
	      bool     hit       = internal_DECOD_HIT      [i];
// 	      uint32_t num_bank  = internal_DECOD_NUM_BANK [i];
// 	      uint32_t num_entry = (hit)?internal_DECOD_NUM_ENTRY [i]:((_param->_have_port_victim)?PORT_READ(in_DECOD_VICTIM [i]):0);

	      // detect new branch !!! insert in branch target buffer
	      if (not hit)
		{
		  // =====[ Miss Case ]
		  uint32_t num_bank  = internal_DECOD_NUM_BANK [i];
		  uint32_t num_entry = (_param->_have_port_victim)?PORT_READ(in_DECOD_VICTIM [i]):0;

		  // dest_val if not jr or jalr
		  Tbranch_condition_t cond     = PORT_READ(in_DECOD_CONDITION [i]);
		  bool                dest_val = not ((cond == BRANCH_CONDITION_READ_REGISTER_WITHOUT_WRITE_STACK) or
						      (cond == BRANCH_CONDITION_READ_REGISTER_WITH_WRITE_STACK   ) or
						      (cond == BRANCH_CONDITION_READ_STACK                       ));
		  
		  reg_BTB[num_bank][num_entry]._val              = 1;
		  reg_BTB[num_bank][num_entry]._context          = (_param->_have_port_context_id)?PORT_READ(in_DECOD_CONTEXT_ID [i]):0;
		  reg_BTB[num_bank][num_entry]._address_src      = PORT_READ(in_DECOD_ADDRESS_SRC  [i]);
		  reg_BTB[num_bank][num_entry]._address_dest     = PORT_READ(in_DECOD_ADDRESS_DEST [i]);
		  reg_BTB[num_bank][num_entry]._address_dest_val = dest_val;
		  reg_BTB[num_bank][num_entry]._condition        = cond;
		  reg_BTB[num_bank][num_entry]._last_take        = PORT_READ(in_DECOD_LAST_TAKE    [i]);
		  reg_BTB[num_bank][num_entry]._accurate         = (PORT_READ(in_DECOD_IS_ACCURATE [i]))?_param->_first_accurate_if_hit:_param->_first_accurate_if_miss;
		}
	      // else (hit) : no update -> it's not the last result of the branch
	    }

	// =======================================================
	// =====[ UPDATE ]=========================================
	// =======================================================
	for (uint32_t i=0; i<_param->_nb_inst_update; i++)
	  if (PORT_READ(in_UPDATE_VAL [i]) and internal_UPDATE_ACK [i])
	    {
	      bool     hit       = internal_UPDATE_HIT      [i];
	      uint32_t num_bank  = internal_UPDATE_NUM_BANK [i];
	      uint32_t num_entry = (hit)?internal_UPDATE_NUM_ENTRY [i]:((_param->_have_port_victim)?PORT_READ(in_UPDATE_VICTIM [i]):0);
	      bool     miss_pred = PORT_READ(in_UPDATE_MISS_PREDICTION [i]);

	      // detect new branch !!! insert in branch target buffer
	      Tcounter_t accurate_new = 0;

	      if (hit)
		{
		  // =====[ Hit  Case ]
		  //  * Have destination
		  //    * if cond == read_register or read_stack : destination valid in commit stage
		  //    * if cond != read_register or read_stack : destination valid in decod  stage
		  //    * in all case : is valid in this step
		  Tcounter_t accurate_old = reg_BTB[num_bank][num_entry]._accurate;
		  // hit  : increase accurate
		  // miss : decrease accurate
		  Tcounter_t accurate_new = (miss_pred)?((accurate_old>0)?(accurate_old-1):accurate_old):((accurate_old<_param->_accurate_max)?(accurate_old+1):accurate_old);

		  // test if accurate go to the threshold
		  if ((accurate_old >= _param->_accurate_limit) and
		      (accurate_new <  _param->_accurate_limit))
		    accurate_new = 0;
		    
		//reg_BTB[num_bank][num_entry]._val              : no update because hit
		//reg_BTB[num_bank][num_entry]._context          : no update because hit
		//reg_BTB[num_bank][num_entry]._address_src      : no update because hit
		//reg_BTB[num_bank][num_entry]._condition        : no update because hit
		}
	      else
		{
		  // =====[ Miss Case ]
		  //reg_BTB[num_bank][num_entry]._val              = 1;
		  //reg_BTB[num_bank][num_entry]._context          = (_param->_have_port_context_id)?PORT_READ(in_UPDATE_CONTEXT_ID [i]):0;
		  //reg_BTB[num_bank][num_entry]._address_src      = PORT_READ(in_UPDATE_ADDRESS_SRC  [i]);
		  //reg_BTB[num_bank][num_entry]._condition        = PORT_READ(in_UPDATE_CONDITION    [i]);

		  accurate_new =  (miss_pred)?_param->_first_accurate_if_miss:_param->_first_accurate_if_hit;

		  reg_BTB[num_bank][num_entry]._val              = 1;
		  reg_BTB[num_bank][num_entry]._context          = (_param->_have_port_context_id)?PORT_READ(in_UPDATE_CONTEXT_ID [i]):0;
		  reg_BTB[num_bank][num_entry]._address_src      = PORT_READ(in_UPDATE_ADDRESS_SRC  [i]);
		  reg_BTB[num_bank][num_entry]._condition        = PORT_READ(in_UPDATE_CONDITION    [i]);
		  reg_BTB[num_bank][num_entry]._last_take        = PORT_READ(in_UPDATE_LAST_TAKE    [i]);
		}

	      // =====[ All Case ]
	      if (reg_BTB[num_bank][num_entry]._address_dest_val == 0)
		{
		  reg_BTB[num_bank][num_entry]._address_dest_val = 1;
		  reg_BTB[num_bank][num_entry]._address_dest     = PORT_READ(in_UPDATE_ADDRESS_DEST [i]);
		}
	      reg_BTB[num_bank][num_entry]._accurate         = accurate_new;
	    }

	for (uint32_t i=0; i<_param->_size_bank; i++)
	  for (uint32_t j=0; j<_param->_associativity; j++)
	    log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"[%.4d][%.4d] %d - %.2d %.8x %.1d %.8x %.3d %.1d %.4d", 
		       i,j,
		       reg_BTB [i][j]._val             ,
		       reg_BTB [i][j]._context         ,
		       reg_BTB [i][j]._address_src     ,
		       reg_BTB [i][j]._address_dest_val,
		       reg_BTB [i][j]._address_dest    ,
		       reg_BTB [i][j]._condition       ,
		       reg_BTB [i][j]._last_take       ,
		       reg_BTB [i][j]._accurate        );
      }
    
#if defined(STATISTICS) or defined(VHDL_TESTBENCH)
    end_cycle ();
#endif

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

}; // end namespace branch_target_buffer_register
}; // end namespace branch_target_buffer
}; // end namespace prediction_unit
}; // end namespace front_end
}; // end namespace multi_front_end
}; // end namespace core

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