#ifdef SYSTEMC
/*
 * $Id: Two_Level_Branch_Predictor_transition.cpp 119 2009-05-25 17:40:26Z rosiere $
 *
 * [ Description ]
 * 
 */

#include "Behavioural/Core/Multi_Front_end/Front_end/Prediction_unit/Direction/Meta_Predictor/Two_Level_Branch_Predictor/include/Two_Level_Branch_Predictor.h"
// #include <assert.h>

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_front_end {
namespace front_end {
namespace prediction_unit {
namespace direction {
namespace meta_predictor {
namespace two_level_branch_predictor {


#undef  FUNCTION
#define FUNCTION "Two_Level_Branch_Predictor::transition"
  void Two_Level_Branch_Predictor::transition (void)
  {
    log_begin(Two_Level_Branch_Predictor,FUNCTION);
    log_function(Two_Level_Branch_Predictor,FUNCTION,_name.c_str());

    if (PORT_READ(in_NRESET) == 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])
            {
              log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * PREDICT [%d]",i);

              // Predict if 
              //  * update_on_prediction and direction is valid
              if (_param->_update_on_prediction)
                if (PORT_READ(in_PREDICT_DIRECTION_VAL [i]))
                  {
                    Tcontrol_t direction = PORT_READ(in_PREDICT_DIRECTION [i]);
                    
                    if (_param->_have_bht)
                      {
                        Thistory_t bht_num_reg = internal_PREDICT_BHT_NUM_REG [i];
                        log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * bht_num_reg      : %d",bht_num_reg);

// #ifdef DEBUG_TEST
//                         assert(bht_num_reg < _param->_bht_nb_shifter);
// #endif

                        Thistory_t bht_history = reg_BHT[bht_num_reg];
                        log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * bht_history (old): %x",bht_history);

                        
                        bht_history = ((bht_history<<1) | direction)&_param->_bht_history_mask ;
                        log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * bht_history (new): %x",bht_history);
                        reg_BHT [bht_num_reg] = bht_history;
                      }

                    if (_param->_have_pht)
                      {
                        Thistory_t pht_num_reg = internal_PREDICT_PHT_NUM_REG  [i];
                        Thistory_t pht_num_bank= internal_PREDICT_PHT_NUM_BANK [i];
                        log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * pht_num_reg      : %d",pht_num_reg);
                        log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * pht_num_bank     : %d",pht_num_bank);

                        Thistory_t pht_history = reg_PHT [pht_num_bank][pht_num_reg];
                        log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * pht_history (old): %x",pht_history);
                        
                        // PHT : saturation counter
                        pht_history = (direction==1)?((pht_history<_param->_pht_counter_max)?(pht_history+1):(pht_history)):((pht_history>0)?(pht_history-1):(pht_history));
                        
                        log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * pht_history (new): %x",pht_history);
                        
                        reg_PHT [pht_num_bank][pht_num_reg] = pht_history;
                      }
                  }
            }        

        // ===================================================================
        // =====[ 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,Two_Level_Branch_Predictor,FUNCTION,"  * UPDATE [%d]",i);
              log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * miss             : %d",PORT_READ(in_UPDATE_MISS [i]));
              log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * update_on_predict: %d",_param->_update_on_prediction);
              
              // Update if 
              //  * update_on_prediction and miss
              //  * not update_on_prediction
              Tcontrol_t history_val = PORT_READ(in_UPDATE_HISTORY_VAL [i]);

              if (not _param->_update_on_prediction or 
                  (_param->_update_on_prediction and PORT_READ(in_UPDATE_MISS [i])) or
                  not history_val // static_prediction
                  )
                {
                  Taddress_t address     = PORT_READ(in_UPDATE_ADDRESS     [i]);
                  Thistory_t history     = PORT_READ(in_UPDATE_HISTORY     [i]);
                  Tcontrol_t direction   = PORT_READ(in_UPDATE_DIRECTION   [i])&1;

                  log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * address          : %.8x",address);
                  log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * direction        : %d",direction);
                  log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * history_val      : %d",direction);

                  Thistory_t pht_bht_history = 0;

                  if (_param->_have_bht)
                    {
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * bht");

                      Thistory_t bht_history = (history>>_param->_bht_history_shift )&_param->_bht_history_mask;
                      Thistory_t bht_num_reg = address & _param->_bht_address_mask;
                      
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * bht_history (old): %x",bht_history);
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * bht_num_reg      : %x",bht_num_reg);
                      
                      // BHT : shift register
                      if (not history_val)
			{
			  bht_history = (direction)?_param->_bht_init_take:_param->_bht_init_ntake;
			}
                      else
			{
			  bht_history = ((bht_history<<1) | direction)&_param->_bht_history_mask ;
			}
                        
                      pht_bht_history = bht_history;
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * bht_history (new): %x",bht_history);

// #ifdef DEBUG_TEST
//                         assert(bht_num_reg < _param->_bht_nb_shifter);
// #endif

                      reg_BHT [bht_num_reg]               = bht_history;
                    }

                  if (_param->_have_pht)
                    {
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * pht");

                      Thistory_t pht_history = (history>>_param->_pht_history_shift )&_param->_pht_history_mask;
                      Thistory_t pht_num_bank= (address>>_param->_pht_address_bank_shift )&_param->_pht_address_bank_mask;
                      Thistory_t pht_num_reg = pht_bht_history xor ((address&_param->_pht_address_share_mask)<<_param->_pht_address_share_shift);
                      
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * bht_history (old): %x",pht_bht_history);
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * pht_history (old): %x",pht_history);
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * pht_num_reg      : %x",pht_num_reg);
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * pht_num_bank     : %x",pht_num_bank);
                      
                      // PHT : saturation counter
                      if (not history_val)
			{
			  pht_history = (direction)?_param->_pht_init_take:_param->_pht_init_ntake;
			}
                      else
			{
			  pht_history = (direction==1)?((pht_history<_param->_pht_counter_max)?(pht_history+1):(pht_history)):((pht_history>0)?(pht_history-1):(pht_history));
			}
                      
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    * pht_history (new): %x",pht_history);
                      
                      reg_PHT [pht_num_bank][pht_num_reg] = pht_history;
                    }
                }
            }
      }

#if defined(DEBUG) and DEBUG_Two_Level_Branch_Predictor and (DEBUG >= DEBUG_TRACE)

# if 0
    {
      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * Dump Two_Level_Branch_Predictor");

      if (_param->_have_bht)
        {
          log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * Dump BHT");

          uint32_t limit = 4;

          for (uint32_t i=0; i<_param->_bht_nb_shifter; i+=limit)
            {
              std::string str = "";

              for (uint32_t j=0; j<limit; j++)
                {
                  uint32_t index = i+j;
                  if (index >= _param->_bht_nb_shifter)
                    break;
                  else
                    {
                      str+=toString("[%.4d] %.4x ",index,reg_BHT[index]);
                    }
                }
              
              log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    %s",str.c_str());
            }
        }

      if (_param->_have_pht)
        {
          log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  * Dump PHT");

          uint32_t limit = 4;

          for (uint32_t num_bank=0; num_bank <_param->_pht_nb_bank; ++num_bank)
            {
              if (_param->_pht_size_bank == 1)
                {
                  log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  [%.4d][0000] %4x",num_bank,reg_PHT[num_bank][0]);
                }
              else
                {
                  log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"  [%.4d]",num_bank);
                  
                  for (uint32_t i=0; i<_param->_pht_size_bank; i+=limit)
                    {
                      std::string str = "";
                      
                      for (uint32_t j=0; j<limit; j++)
                        {
                          uint32_t index = i+j;
                          if (index >= _param->_pht_size_bank)
                            break;
                          else
                            str+=toString("[%.4d] %.4x ",index,reg_PHT[num_bank][index]);
                        }
                      
                      log_printf(TRACE,Two_Level_Branch_Predictor,FUNCTION,"    %s",str.c_str());
                    }
                }
            }
        }
    }
# endif
#endif

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

    log_end(Two_Level_Branch_Predictor,FUNCTION);
  };

}; // end namespace two_level_branch_predictor
}; // end namespace meta_predictor
}; // end namespace direction
}; // end namespace prediction_unit
}; // end namespace front_end
}; // end namespace multi_front_end
}; // end namespace core

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