#ifdef SYSTEMC
/*
 * $Id: Branch_Target_Buffer_Register_transition.cpp 107 2009-02-10 23:03:25Z 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_begin(Branch_Target_Buffer_Register,FUNCTION);
    log_function(Branch_Target_Buffer_Register,FUNCTION,_name.c_str());

    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])
	    {
              log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"  * UPDATE [%d]",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
              log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"    * hit          : %d",hit);
              log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"    * miss_pred    : %d",miss_pred);
              log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"    * num_bank     : %d",num_bank );
              log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"    * num_entry    : %d",num_entry);

	      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;

                  log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"    * accurate_old : %d",accurate_old);

		  // hit  : increase accurate
		  // miss : decrease accurate
                  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))
                    {
                      log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"    * decrease downto the accurate_limid (%d)",_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]);
//                reg_BTB[num_bank][num_entry]._address_dest_val = 0;
                }

	      // =====[ 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]);
// 		}

              log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"    * accurate_new : %d",accurate_new);

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

#if (DEBUG >= DEBUG_TRACE) and DEBUG_Branch_Target_Buffer_Register
        log_printf(TRACE,Branch_Target_Buffer_Register,FUNCTION,"  * Dump BTB");
	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 - %.4d %.8x (%.8x) %.1d %.8x (%.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_src <<2,
		       reg_BTB [i][j]._address_dest_val,
		       reg_BTB [i][j]._address_dest    ,
		       reg_BTB [i][j]._address_dest<<2 ,
		       reg_BTB [i][j]._condition       ,
		       reg_BTB [i][j]._last_take       ,
		       reg_BTB [i][j]._accurate        );
#endif
      }

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

    log_end(Branch_Target_Buffer_Register,FUNCTION);
  };

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