#ifdef SYSTEMC
/*
 * $Id$
 *
 * [ 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::genMoore"
  void Update_Prediction_Table::genMoore (void)
  {
    log_begin(Update_Prediction_Table,FUNCTION);
    log_function(Update_Prediction_Table,FUNCTION,_name.c_str());

    if (PORT_READ(in_NRESET) == 1)
      {

    // ===================================================================
    // =====[ DEPTH ]=====================================================
    // ===================================================================

    for (uint32_t i=0; i<_param->_nb_context; i++)
      {
	if (_param->_have_port_depth)
          {
        PORT_WRITE(out_DEPTH_CURRENT [i], reg_UPT_TOP    [i]);
        PORT_WRITE(out_DEPTH_MIN     [i], reg_UPT_BOTTOM [i]);
          }
	PORT_WRITE(out_DEPTH_MAX     [i], reg_UPT_TOP    [i]);
      }

    // ===================================================================
    // =====[ UPDATE ]====================================================
    // ===================================================================

    bool     retire_ras_from_ufpt [_param->_nb_context]; // event ufpt -> restore RAS, else update upt
    bool     retire_ras_from_upt  [_param->_nb_context]; // event upt  -> restore RAS, else restore others structure
    bool     ufpt_update          [_param->_nb_context];
    bool     upt_update           [_param->_nb_context];
    Tdepth_t tab_ufpt_depth       [_param->_nb_context];
    Tdepth_t tab_upt_depth        [_param->_nb_context];

    for (uint32_t i=0; i<_param->_nb_context; i++)
      {
        event_state_t event_state = reg_EVENT_STATE [i];

        retire_ras_from_ufpt [i] = ((event_state == EVENT_STATE_FLUSH_UFPT            ) or
                                    (event_state == EVENT_STATE_FLUSH_UFPT_AND_UPT));
        retire_ras_from_upt  [i] = (event_state == EVENT_STATE_FLUSH_UPT);

        ufpt_update          [i] = true;
        upt_update           [i] = true;
        tab_ufpt_depth       [i] = reg_UFPT_UPDATE [i];
        tab_upt_depth        [i] = reg_UPT_UPDATE  [i];
      }

    for (uint32_t i=0; i<_param->_nb_inst_update; i++)
      {
	Tcontext_t          context     = (reg_UPDATE_PRIORITY+i)%_param->_nb_context;

	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"  * UPDATE [%d] (genMoore)",i);
	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * context         : %d",context);
	log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * event_state     : %s",toString(reg_EVENT_STATE [context]).c_str());

        Tcontrol_t          val                  = false;
        Tcontrol_t          val_without_ack      = false;
        Tcontrol_t          miss_prediction      ;
        Tcontrol_t          direction_good       ;
        Tcontrol_t          btb_val              ;
        Taddress_t          btb_address_src      ;
        Taddress_t          btb_address_dest     ;
        Tbranch_condition_t btb_condition        ;
        Tcontrol_t          dir_val              ;
        Thistory_t          dir_history          ;
        Tcontrol_t          ras_val              ;
        Tcontrol_t          ras_flush            ;
        Tcontrol_t          ras_push             ;
        Taddress_t          ras_address          ;
        Tptr_t              ras_index            ;
        Tcontrol_t          ras_prediction_ifetch;

        // Test if update fetch prediction table need update port
        if (retire_ras_from_ufpt [context])
          {
            if (ufpt_update [context])
              {
                // Update Fetch Prediction Table
                // An update of ufpt is to previous miss. Just restore Return Address Stack
                
                Tdepth_t            depth     = tab_ufpt_depth[context];
                ufpt_state_t        state     = reg_UPDATE_FETCH_PREDICTION_TABLE [context][depth]._state; 
                Tbranch_condition_t condition = reg_UPDATE_FETCH_PREDICTION_TABLE [context][depth]._condition; 
                
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * Update Fetch Prediction Table");
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * depth           : %d",depth    );
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * state           : %s",toString(state    ).c_str());
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * condition       : %s",toString(condition).c_str());
                
                val                   = (state == UPDATE_FETCH_PREDICTION_STATE_EVENT);
//              val_without_ack       = not update_ras(condition);

                miss_prediction       = 1;
//              direction_good        = ;
                btb_val               = 0; // don't update btb (is update by the event branch)
//              btb_address_src       = ;
//              btb_address_dest      = ;
//              btb_condition         = ;
                dir_val               = 0; // don't update btb (is update by the event branch (if conditionnal branch))
//              dir_history           = ;
                ras_val               = update_ras(condition); // repop/ repush data -> don't corrupt ras
                ras_flush             = 0;
                ras_push              = push_ras(condition);
                ras_address           = reg_UPDATE_FETCH_PREDICTION_TABLE [context][depth]._address_ras;
                ras_index             = reg_UPDATE_FETCH_PREDICTION_TABLE [context][depth]._index_ras;
                ras_prediction_ifetch = 1;
                
                internal_UPDATE_FROM_UFPT [i] = true;
                internal_UPDATE_DEPTH     [i] = depth;
                internal_UPDATE_RAS       [i] = false;

                // Warning : don't update same entry
                if (depth == reg_UFPT_BOTTOM[context])
                  ufpt_update [context] = false;
                
                tab_ufpt_depth[context] = ((depth==0)?_param->_size_ufpt_queue[context]:depth)-1;
              }
          }
        else
          {
            if (upt_update [context])
              {
                // Update Prediction Table
                
                Tdepth_t            depth     = tab_upt_depth[context];
                upt_state_t         state     = reg_UPDATE_PREDICTION_TABLE [context][depth]._state; 
                Tbranch_condition_t condition = reg_UPDATE_PREDICTION_TABLE [context][depth]._condition; 
                Tcontrol_t          ifetch    = reg_UPDATE_PREDICTION_TABLE [context][depth]._ifetch_prediction;
                
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * Update Prediction Table");
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * depth           : %d",depth    );
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * state           : %s",toString(state    ).c_str());
                log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * condition       : %s",toString(condition).c_str());
              
                Tcontrol_t          state_is_ok_ko           = ((state == UPDATE_PREDICTION_STATE_OK   ) or
                                                                (state == UPDATE_PREDICTION_STATE_KO   ));
                Tcontrol_t          state_is_event           = ((state == UPDATE_PREDICTION_STATE_KO   ) or
                                                                (state == UPDATE_PREDICTION_STATE_EVENT));
                Tcontrol_t          state_is_event_update    = state_is_event and     need_update(condition);
                Tcontrol_t          state_is_event_no_update = state_is_event and not need_update(condition);

                if (retire_ras_from_upt [context])
                  {
                val                   = state_is_event_update;
                val_without_ack       = state_is_event_no_update;
                  }
                else
                  {
                val                   = (state == UPDATE_PREDICTION_STATE_OK);
                val_without_ack       = false;
                  }                  

                miss_prediction       = (state != UPDATE_PREDICTION_STATE_OK);
                direction_good        = reg_UPDATE_PREDICTION_TABLE [context][depth]._good_take   ;
                btb_val               = state_is_ok_ko and update_btb(condition);
                btb_address_src       = reg_UPDATE_PREDICTION_TABLE [context][depth]._address_src ;
                btb_address_dest      = reg_UPDATE_PREDICTION_TABLE [context][depth]._address_dest;
                btb_condition         = condition;
                dir_val               = state_is_ok_ko and update_dir(condition) and ifetch; // if not ifetch, then static prediction
                dir_history           = reg_UPDATE_PREDICTION_TABLE [context][depth]._history     ;
                ras_val               = update_ras(condition); // repop/ repush data -> don't corrupt ras
                ras_flush             = (state == UPDATE_PREDICTION_STATE_KO); // miss prediction, RAS is corrupted
                ras_push              = push_ras(condition);
                ras_address           = reg_UPDATE_PREDICTION_TABLE [context][depth]._address_ras;
                ras_index             = reg_UPDATE_PREDICTION_TABLE [context][depth]._index_ras;
                ras_prediction_ifetch = ifetch;

                internal_UPDATE_FROM_UFPT [i] = false;
                internal_UPDATE_DEPTH     [i] = depth;
                internal_UPDATE_RAS       [i] = retire_ras_from_upt [context];

                // Warning : don't update same entry
                if (retire_ras_from_upt [context])
                  {
                    // Restore RAS. 
                    if ((depth == reg_UPT_BOTTOM[context]) or not (val or val_without_ack))
                      upt_update [context] = false;
                    
                    tab_upt_depth[context] = (depth==0)?(_param->_size_upt_queue[context]-1):(depth-1);
                  }
                else
                  {
                    if ((depth == reg_UPT_TOP [context]) or not (val or val_without_ack))
                      upt_update [context] = false;
                    
                    tab_upt_depth[context] = (depth+1)%_param->_size_upt_queue[context];
                  }
              }
          }
        
        log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * val             : %d",val    );
        log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * val_without_ack : %d",val_without_ack);
        log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * miss_prediction : %d",miss_prediction);
        log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * direction_good  : %d",direction_good );
        log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * btb_val         : %d",btb_val);
        log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * dir_val         : %d",dir_val);
        log_printf(TRACE,Update_Prediction_Table,FUNCTION,"    * ras_val         : %d",ras_val);

	internal_UPDATE_VAL             [i] = val;
	internal_UPDATE_VAL_WITHOUT_ACK [i] = val_without_ack;
        internal_UPDATE_CONTEXT_ID      [i] = context;

	PORT_WRITE(out_UPDATE_VAL                   [i],internal_UPDATE_VAL [i]);
        if (val)
          {
        if (_param->_have_port_context_id)
        PORT_WRITE(out_UPDATE_CONTEXT_ID            [i],context              );
        PORT_WRITE(out_UPDATE_MISS_PREDICTION       [i],miss_prediction      );
        PORT_WRITE(out_UPDATE_DIRECTION_GOOD        [i],direction_good       );
        PORT_WRITE(out_UPDATE_BTB_VAL               [i],btb_val              );
        PORT_WRITE(out_UPDATE_BTB_ADDRESS_SRC       [i],btb_address_src      );
        PORT_WRITE(out_UPDATE_BTB_ADDRESS_DEST      [i],btb_address_dest     );
        PORT_WRITE(out_UPDATE_BTB_CONDITION         [i],btb_condition        );
        PORT_WRITE(out_UPDATE_DIR_VAL               [i],dir_val              );
        if (_param->_have_port_history)
        PORT_WRITE(out_UPDATE_DIR_HISTORY           [i],dir_history          );
        PORT_WRITE(out_UPDATE_RAS_VAL               [i],ras_val              );
        PORT_WRITE(out_UPDATE_RAS_FLUSH             [i],ras_flush            );
        PORT_WRITE(out_UPDATE_RAS_PUSH              [i],ras_push             );
        PORT_WRITE(out_UPDATE_RAS_ADDRESS           [i],ras_address          );
        PORT_WRITE(out_UPDATE_RAS_INDEX             [i],ras_index            );
        PORT_WRITE(out_UPDATE_RAS_PREDICTION_IFETCH [i],ras_prediction_ifetch);
          }
      }
    
    // ===================================================================
    // =====[ BRANCH_EVENT ]==============================================
    // ===================================================================
    for (uint32_t i=0; i<_param->_nb_context; i++)
      {
	Tcontrol_t val = (reg_EVENT_STATE [i] == EVENT_STATE_UPDATE_CONTEXT);

        PORT_WRITE(out_BRANCH_EVENT_VAL              [i],val);
        PORT_WRITE(out_BRANCH_EVENT_ADDRESS_SRC      [i],reg_EVENT_ADDRESS_SRC      [i]);
        PORT_WRITE(out_BRANCH_EVENT_ADDRESS_DEST_VAL [i],reg_EVENT_ADDRESS_DEST_VAL [i]);
        PORT_WRITE(out_BRANCH_EVENT_ADDRESS_DEST     [i],reg_EVENT_ADDRESS_DEST     [i]);

	internal_BRANCH_EVENT_VAL [i] = val;
      }
      }

    log_end(Update_Prediction_Table,FUNCTION);
  };

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