#ifdef SYSTEMC
/*
 * $Id: Commit_unit_genMoore.cpp 124 2009-06-17 12:11:25Z rosiere $
 *
 * [ Description ]
 * 
 */

#include "Behavioural/Core/Multi_OOO_Engine/OOO_Engine/Commit_unit/include/Commit_unit.h"

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_ooo_engine {
namespace ooo_engine {
namespace commit_unit {


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

    if (PORT_READ(in_NRESET))
      {
    // ===================================================================
    // =====[ REEXECUTE ]=================================================
    // ===================================================================
    {
      uint32_t nb_scan_bank = 0; // last scan bank
      bool     can_continue = true;

      uint32_t event_nb_inst  [_param->_nb_front_end][_param->_max_nb_context];
      for (uint32_t i=0; i<_param->_nb_front_end; i++)
        for (uint32_t j=0; j<_param->_nb_context [i]; j++)
          event_nb_inst [i][j] = 0;

      // for each reexecute_port
      for (uint32_t i=0; i<_param->_nb_inst_reexecute; ++i)
	{
	  log_printf(TRACE,Commit_unit,FUNCTION,"  * REEXECUTE [%d]",i);
	  
	  // Store instruction comming Out Of Order in Load Store Unit. 
	  // Must be executed in no speculative mode. Also, send a signal when an Store is in head of ROB
	  
	  Tcontrol_t val = false;

	  for (uint32_t j=nb_scan_bank; j<_param->_nb_bank; j++)
	    {
	      nb_scan_bank ++;
	      
              // translate bank number
	      uint32_t num_bank = (reg_NUM_BANK_HEAD+j)%_param->_nb_bank;
	      
	      log_printf(TRACE,Commit_unit,FUNCTION,"    * num_bank      : %d",num_bank);

	      // Test if the head of rob is not empty
	      if (not _rob[num_bank].empty())
		{
		  log_printf(TRACE,Commit_unit,FUNCTION,"    * ROB is not empty");
		  
		  // Read state
		  entry_t    * entry = _rob [num_bank].front();
                  uint32_t     num_packet   = ((entry->ptr << _param->_shift_num_slot) | num_bank);
                  Tcontext_t   front_end_id = entry->front_end_id;
                  Tcontext_t   context_id   = entry->context_id  ;

		  rob_state_t  state = entry->state;
		  
		  // Test state
		  //  * store is ko, send signal at store_queue
		  //  * store_is ok, test if in head
// 		  val   = (((state == ROB_STORE_OK) and (num_bank == (reg_NUM_BANK_HEAD))) or
// 			    (state == ROB_STORE_KO)  or
// 			    (state == ROB_STORE_EVENT)
// 			    );
		  val   = (((state == ROB_STORE_OK) and can_continue) or
			    (state == ROB_STORE_KO)  or
			    (state == ROB_STORE_EVENT)
			    );
		  
                  can_continue &= (((state == ROB_STORE_OK          ) or
                                    (state == ROB_STORE_OK_WAIT_END ) or
                                    (state == ROB_END_OK_SPECULATIVE) or
                                    (state == ROB_END_OK            ) or
                                    (state == ROB_END               )) and
                                   not ((reg_EVENT_STATE [front_end_id][context_id] == COMMIT_EVENT_STATE_NOT_YET_EVENT) and
                                        (reg_EVENT_PACKET[front_end_id][context_id] == num_packet)) and
                                   not ((reg_EVENT_NB_INST [front_end_id][context_id] > 0) and
                                        ((++event_nb_inst [front_end_id][context_id]) >= reg_EVENT_NB_INST [front_end_id][context_id]))
                                   );

		  log_printf(TRACE,Commit_unit,FUNCTION,"    * val           : %d",val);

		  if (val)
		    {
		      internal_REEXECUTE_NUM_BANK [i] = num_bank;

		      Tpacket_t packet_id = ((entry->ptr << _param->_shift_num_slot) | num_bank);
		      
		      log_printf(TRACE,Commit_unit,FUNCTION,"    * packet_id     : %d",packet_id);
		      
		      // Reexecute store
		      if (_param->_have_port_context_id)
		      PORT_WRITE(out_REEXECUTE_CONTEXT_ID            [i], entry->context_id           );
		      if (_param->_have_port_front_end_id)
		      PORT_WRITE(out_REEXECUTE_FRONT_END_ID          [i], entry->front_end_id         );
		      if (_param->_have_port_rob_ptr  )
		      PORT_WRITE(out_REEXECUTE_PACKET_ID             [i], packet_id                   );
		      PORT_WRITE(out_REEXECUTE_TYPE                  [i], entry->type                 );
		      PORT_WRITE(out_REEXECUTE_STORE_QUEUE_PTR_WRITE [i], entry->store_queue_ptr_write);
		      PORT_WRITE(out_REEXECUTE_OPERATION             [i], (state == ROB_STORE_OK)?OPERATION_MEMORY_STORE_HEAD_OK:OPERATION_MEMORY_STORE_HEAD_KO);

		      break; // Stop scan
		    }
		}
	    }  

	  internal_REEXECUTE_VAL      [i] = val;
	}
    }

    // ===================================================================
    // =====[ BRANCH_COMPLETE ]===========================================
    // ===================================================================
    {
      // Branchement must be send at the prediction unit

      uint32_t nb_scan_bank = 0; // last scan bank

      // for each port, find a valid branchement.
      for (uint32_t i=0; i<_param->_nb_inst_branch_complete; i++)
	{
          log_printf(TRACE,Commit_unit,FUNCTION,"  * BRANCH_COMPLETE [%d]",i);

	  Tcontrol_t  val  = false;
	  
	  for (uint32_t j=nb_scan_bank; j<_param->_nb_bank; j++)
	    {
	      nb_scan_bank ++;

              // translate bank number
	      uint32_t num_bank = (reg_NUM_BANK_HEAD+j)%_param->_nb_bank;

              log_printf(TRACE,Commit_unit,FUNCTION,"    * num_bank           : %d",num_bank);
	      
              // Test if in this bank, they have an instruction
	      if (not _rob [num_bank].empty())
		{
                  log_printf(TRACE,Commit_unit,FUNCTION,"    * not empty");

                  // Read information
		  entry_t    * entry    = _rob [num_bank].front();
		  rob_state_t  state    = entry->state;
		  Tcontext_t   front_end_id = entry->front_end_id;
		  Tcontext_t   context_id   = entry->context_id  ;

                  log_printf(TRACE,Commit_unit,FUNCTION,"    * front_end_id       : %d",front_end_id);
                  log_printf(TRACE,Commit_unit,FUNCTION,"    * context_id         : %d",context_id  );

                  // don't complete a branch when rob manage an present event
		  if (((reg_EVENT_STATE [front_end_id][context_id] == COMMIT_EVENT_STATE_NO_EVENT) or
                       (reg_EVENT_STATE [front_end_id][context_id] == COMMIT_EVENT_STATE_NOT_YET_EVENT)) and
                      (state == ROB_BRANCH_COMPLETE))
		    {
                      log_printf(TRACE,Commit_unit,FUNCTION,"    * find !!!");

                      // test if have a future event (stop is set)
//                       log_printf(TRACE,Commit_unit,FUNCTION,"      * reg_EVENT_STOP   : %d",reg_EVENT_STOP [front_end_id][context_id]);

                      if (reg_EVENT_STATE [front_end_id][context_id] == COMMIT_EVENT_STATE_NOT_YET_EVENT)
                        {
                          // Have future event, can complete the branch if the event is most speculative than this branchement
                          // Also, need compare packet_id (is order)

                          uint32_t _top = ((_rob[reg_NUM_BANK_HEAD].front()->ptr << _param->_shift_num_slot) | reg_NUM_BANK_HEAD);
                          uint32_t _old = reg_EVENT_PACKET [front_end_id][context_id];
                          uint32_t _new = ((entry->ptr << _param->_shift_num_slot) | num_bank);

//                           log_printf(TRACE,Commit_unit,FUNCTION,"      * _top             : %d",_top);
//                           log_printf(TRACE,Commit_unit,FUNCTION,"      * _old (before)    : %d",_old);
//                           log_printf(TRACE,Commit_unit,FUNCTION,"      * _new (before)    : %d",_new);

                          if (_old < _top) _old = _old+_param->_size_queue;
                          if (_new < _top) _new = _new+_param->_size_queue;
                          if (_new < _old) val = true;

//                           log_printf(TRACE,Commit_unit,FUNCTION,"      * _old (after )    : %d",_old);
//                           log_printf(TRACE,Commit_unit,FUNCTION,"      * _new (after )    : %d",_new);

                        }
                      else
                        val = true;
		      
                      log_printf(TRACE,Commit_unit,FUNCTION,"      * val              : %d",val);

                      if (val)
                        {
                          // Have an valid branchement to complete
		      internal_BRANCH_COMPLETE_NUM_BANK           [i] = num_bank;
		      
		      if (_param->_have_port_front_end_id)
		      PORT_WRITE(out_BRANCH_COMPLETE_FRONT_END_ID [i],        front_end_id    );
		      if (_param->_have_port_context_id)
		      PORT_WRITE(out_BRANCH_COMPLETE_CONTEXT_ID   [i],        context_id      );
		      if (_param->_have_port_depth)
		      PORT_WRITE(out_BRANCH_COMPLETE_DEPTH        [i], entry->depth           );
		      PORT_WRITE(out_BRANCH_COMPLETE_ADDRESS      [i], entry->address_next    );
// 		      PORT_WRITE(out_BRANCH_COMPLETE_FLAG         [i],(entry->flags&FLAG_F)!=0);
		      PORT_WRITE(out_BRANCH_COMPLETE_NO_SEQUENCE  [i], entry->no_sequence     );
		      
		      break; // Stop scan
                        }
		    }
		}
	    }

	  internal_BRANCH_COMPLETE_VAL [i] = val;
	}
    }

    // ===================================================================
    // =====[ UPDATE ]====================================================
    // ===================================================================
    {
      internal_UPDATE_VAL = 0;
      internal_UPDATE_NUM_BANK = reg_NUM_BANK_HEAD;

      // Test if have an instruction
      if (not _rob[internal_UPDATE_NUM_BANK].empty())
	{
          log_printf(TRACE,Commit_unit,FUNCTION,"  * UPDATE");
          log_printf(TRACE,Commit_unit,FUNCTION,"    * num_bank : %d",internal_UPDATE_NUM_BANK);

	  entry_t * entry = _rob [internal_UPDATE_NUM_BANK].front();

          // Test state
          // Update if exception or load miss
          switch (entry->state)
            {
            case ROB_END_EXCEPTION_UPDATE :
              {
                internal_UPDATE_VAL = 1;
                throw ERRORMORPHEO(FUNCTION,_("Moore : exception is not yet supported (Coming Soon).\n"));
                break;
              }
            case ROB_END_LOAD_MISS_UPDATE :
              {
                log_printf(TRACE,Commit_unit,FUNCTION,"    * ROB_END_LOAD_MISS_UPDATE");

                internal_UPDATE_VAL = 1;

                Tcontext_t front_end_id = entry->front_end_id;
                Tcontext_t context_id   = entry->context_id  ;

                log_printf(TRACE,Commit_unit,FUNCTION,"      * front_end_id : %d",front_end_id);
                log_printf(TRACE,Commit_unit,FUNCTION,"      * context_id   : %d",context_id  );

                if (_param->_have_port_front_end_id)
                PORT_WRITE(out_UPDATE_FRONT_END_ID    ,front_end_id);
                if (_param->_have_port_context_id)
                PORT_WRITE(out_UPDATE_CONTEXT_ID      ,context_id  );
                if (_param->_have_port_depth)
                PORT_WRITE(out_UPDATE_DEPTH           ,entry->depth);
                PORT_WRITE(out_UPDATE_TYPE            ,EVENT_TYPE_LOAD_MISS_SPECULATION);
//                 PORT_WRITE(out_UPDATE_IS_DELAY_SLOT   ,reg_PC_CURRENT_IS_DS      [front_end_id][context_id]);
//                 PORT_WRITE(out_UPDATE_ADDRESS         ,reg_PC_CURRENT            [front_end_id][context_id]);
//                 PORT_WRITE(out_UPDATE_ADDRESS_EPCR_VAL,reg_PC_CURRENT_IS_DS_TAKE [front_end_id][context_id]);
//                 PORT_WRITE(out_UPDATE_ADDRESS_EPCR    ,reg_PC_NEXT               [front_end_id][context_id]);
//                 PORT_WRITE(out_UPDATE_ADDRESS_EEAR_VAL,0);
// //              PORT_WRITE(out_UPDATE_ADDRESS_EEAR    ,);

                PORT_WRITE(out_UPDATE_IS_DELAY_SLOT   ,0);
                PORT_WRITE(out_UPDATE_ADDRESS         ,reg_PC_NEXT [front_end_id][context_id]);
                PORT_WRITE(out_UPDATE_ADDRESS_EPCR_VAL,0);
//              PORT_WRITE(out_UPDATE_ADDRESS_EPCR    ,);
                PORT_WRITE(out_UPDATE_ADDRESS_EEAR_VAL,0);
//              PORT_WRITE(out_UPDATE_ADDRESS_EEAR    ,);

                break;
              }
            default :
              {
//                 internal_UPDATE_VAL = 0; 
              }
            }
	}
      
      log_printf(TRACE,Commit_unit,FUNCTION,"  * UPDATE (end)");
    }
 
    // ===================================================================
    // =====[ NB_INST ]===================================================
    // ===================================================================
    {
#ifdef DEBUG_TEST
      bool empty = true;
#endif
      for (uint32_t i=0; i<_param->_nb_front_end; i++)
        for (uint32_t j=0; j<_param->_nb_context [i]; j++)
          {
#ifdef DEBUG_TEST
            empty &= (reg_NB_INST_COMMIT_ALL [i][j] == 0);
#endif
            PORT_WRITE(out_NB_INST_COMMIT_ALL [i][j], reg_NB_INST_COMMIT_ALL [i][j]);
            PORT_WRITE(out_NB_INST_COMMIT_MEM [i][j], reg_NB_INST_COMMIT_MEM [i][j]);
          }
#ifdef DEBUG_TEST
      PORT_WRITE(out_INFO_ROB_EMPTY,empty);
#endif
    }


    // ===================================================================
    // =====[ RETIRE_EVENT ]==============================================
    // ===================================================================
    for (uint32_t i=0; i<_param->_nb_front_end; i++)
      for (uint32_t j=0; j<_param->_nb_context [i]; j++)
        {
//           bool flush = (((reg_EVENT_STATE [i][j] == COMMIT_EVENT_STATE_EVENT) or
//                          (reg_EVENT_STATE [i][j] == COMMIT_EVENT_STATE_WAIT_DECOD)) and
//                         not reg_EVENT_CAN_RESTART[i][j]);

          PORT_WRITE(out_RETIRE_EVENT_STATE [i][j], commit_event_state_to_event_state(reg_EVENT_STATE[i][j]));
//        PORT_WRITE(out_RETIRE_EVENT_FLUSH [i][j], flush);
//        PORT_WRITE(out_RETIRE_EVENT_STOP  [i][j], reg_EVENT_STOP [i][j]);
          PORT_WRITE(out_RETIRE_EVENT_STOP  [i][j], ((reg_EVENT_STATE [i][j] == COMMIT_EVENT_STATE_NOT_YET_EVENT) or
                                                     reg_EVENT_NEXT_STOP [i][j]));
        }
      }
    else
      {
        for (uint32_t i=0; i<_param->_nb_inst_reexecute; ++i)
          {
            internal_REEXECUTE_VAL      [i] = 0;
//             internal_REEXECUTE_NUM_BANK [i] = num_bank;
          }

        for (uint32_t i=0; i<_param->_nb_inst_branch_complete; i++)
          {
            internal_BRANCH_COMPLETE_VAL [i] = 0;
//             internal_BRANCH_COMPLETE_NUM_BANK           [i] = num_bank;
          }

        internal_UPDATE_VAL = 0;
//       internal_UPDATE_NUM_BANK = reg_NUM_BANK_HEAD;


        for (uint32_t i=0; i<_param->_nb_front_end; i++)
          for (uint32_t j=0; j<_param->_nb_context [i]; j++)
            {
              PORT_WRITE(out_RETIRE_EVENT_STATE [i][j], commit_event_state_to_event_state(COMMIT_EVENT_STATE_NO_EVENT));
//            PORT_WRITE(out_RETIRE_EVENT_FLUSH [i][j], flush);
//            PORT_WRITE(out_RETIRE_EVENT_STOP  [i][j], reg_EVENT_STOP [i][j]);
              PORT_WRITE(out_RETIRE_EVENT_STOP  [i][j], true);
            }
      }

      for (uint32_t i=0; i<_param->_nb_inst_reexecute; ++i)
        PORT_WRITE(out_REEXECUTE_VAL[i], internal_REEXECUTE_VAL [i]);
      for (uint32_t i=0; i<_param->_nb_inst_branch_complete; i++)
        PORT_WRITE(out_BRANCH_COMPLETE_VAL [i], internal_BRANCH_COMPLETE_VAL [i]);
      PORT_WRITE(out_UPDATE_VAL, internal_UPDATE_VAL);
        
    log_end(Commit_unit,FUNCTION);
  };

}; // end namespace commit_unit
}; // end namespace ooo_engine
}; // end namespace multi_ooo_engine
}; // end namespace core
}; // end namespace behavioural
}; // end namespace morpheo              
#endif
