#ifdef SYSTEMC
/*
 * $Id$
 *
 * [ Description ]
 * 
 */

#include "Behavioural/Core/Multi_Front_end/Front_end/Prediction_unit/Prediction_unit_Glue/include/Prediction_unit_Glue.h"

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_front_end {
namespace front_end {
namespace prediction_unit {
namespace prediction_unit_glue {


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

    // Init
    uint32_t   decod_unit = reg_DECOD_PRIORITY;
    
    log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"  * decod_unit : %d",decod_unit);

    Tcontrol_t ack [_param->_nb_inst_decod[decod_unit]];
    
    for (uint32_t i=0;i<_param->_nb_inst_decod[decod_unit]; i++)
      ack [i] = true;

    Tcontrol_t btb_val [_param->_nb_inst_branch_decod];
    Tcontrol_t ras_val [_param->_nb_inst_branch_decod];
    Tcontrol_t upt_val [_param->_nb_inst_branch_decod];

    for (uint32_t i=0; i<_param->_nb_inst_branch_decod; i++)
      {
	btb_val [i] = false;
	ras_val [i] = false;
	upt_val [i] = false;
      }

    uint32_t port = 0;
    for (uint32_t i=0; i<_param->_nb_inst_decod[decod_unit]; i++)
      // Test if decod_unit have detected a branch
      if ((port>=_param->_nb_inst_branch_decod) or
	  (PORT_READ(in_DECOD_VAL [decod_unit][i]) == 0))
 	{
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"  * DECOD[%d][%d] : not valid",decod_unit,i);
	  ack [i] = false;
	}
      else
	{
	  Tcontext_t        context               = (_param->_have_port_context_id)?PORT_READ(in_DECOD_CONTEXT_ID [decod_unit][i]):0;
	  Tbranch_state_t   branch_state          = PORT_READ(in_DECOD_BRANCH_STATE          [decod_unit][i]);
	  Tcontrol_t        match_inst_ifetch_ptr = PORT_READ(in_DECOD_MATCH_INST_IFETCH_PTR [decod_unit][i]);
	  Tprediction_ptr_t depth                 = (_param->_have_port_depth)?PORT_READ(in_DECOD_BRANCH_UPDATE_PREDICTION_ID [decod_unit][i]):0;
	  Tcontrol_t        use_btb               = false;
	  Tcontrol_t        use_ras               = false;
	  Tcontrol_t        use_upt               = true ; // always update upt.

          // Ifetch stage made an miss if :
          //   * branch is previously predict, but it's not the good
	  Tcontrol_t        miss_ifetch           = ((branch_state != BRANCH_STATE_NONE) and (match_inst_ifetch_ptr == 0));
          // Decod stage made an miss if :
          //   * branch was not detected
          Tcontrol_t        miss_decod            = (branch_state == BRANCH_STATE_NONE);

	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"  * DECOD[%d][%d] :     valid",decod_unit,i);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * branch_state          : %d",branch_state);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * match_inst_ifetch_ptr : %d",match_inst_ifetch_ptr);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * depth                 : [%d] %d",i,depth);
          log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * miss_ifetch           : %d",miss_ifetch);
          log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * miss_decod            : %d",miss_decod);

	  // Test if ifetch have detecte a branch is the packet
	  if (miss_ifetch or miss_decod)
	    {
	      Tbranch_condition_t condition    = PORT_READ(in_DECOD_BRANCH_CONDITION            [decod_unit][i]);
	      Tcontrol_t          direction    = PORT_READ(in_DECOD_BRANCH_DIRECTION            [decod_unit][i]);
	      Taddress_t          address_src  = PORT_READ(in_DECOD_ADDRESS_SRC                 [decod_unit][i]);
	      Taddress_t          address_dest = PORT_READ(in_DECOD_ADDRESS_DEST                [decod_unit][i]);
	      Tcontrol_t          is_accurate;

	      // Miss speculation
	      use_btb = true;
	      
	      switch (condition)
		{
		case BRANCH_CONDITION_NONE_WITH_WRITE_STACK             :
		  {
		    // l.jal
                    log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"      * BRANCH_CONDITION_NONE_WITH_WRITE_STACK");

		    is_accurate = true ;//PORT_READ(in_DECOD_RAS_HIT [port]);
		    use_ras     = true ;

		    PORT_WRITE(out_DECOD_RAS_PUSH         [port],1);
		    PORT_WRITE(out_DECOD_RAS_ADDRESS_PUSH [port],address_src+2);
		    
		    break;
		  }

		case BRANCH_CONDITION_READ_REGISTER_WITH_WRITE_STACK    :
		  {
		    // l.jalr
                    log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"      * BRANCH_CONDITION_READ_REGISTER_WITH_WRITE_STACK");

		    is_accurate = false; // address unknow : in a register
		    use_ras     = true;

		    PORT_WRITE(out_DECOD_RAS_PUSH         [port],1);
		    PORT_WRITE(out_DECOD_RAS_ADDRESS_PUSH [port],address_src+2);
		    
		    break;
		  }
		case BRANCH_CONDITION_READ_STACK                        :
		  {
		    // l.jr with rb=r9
                    log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"      * BRANCH_CONDITION_READ_STACK");

		    is_accurate = PORT_READ(in_DECOD_RAS_HIT [port]);
		    use_ras     = true;

		    PORT_WRITE(out_DECOD_RAS_PUSH         [port],0); // POP
		    address_dest = PORT_READ(in_DECOD_RAS_ADDRESS_POP [port]);
		    
		    break;
		  }
		case BRANCH_CONDITION_READ_REGISTER_WITHOUT_WRITE_STACK :
		  {
                    // l.jr with rb!=r9
                    log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"      * BRANCH_CONDITION_READ_REGISTER_WITHOUT_WRITE_STACK");

		    //   * READ_REGISTER_WITHOUT_WRITE_STACK : Take but destination is unknow - don't continue
		    is_accurate = false; // address unknow : in a register

		    break;
		  }

		case BRANCH_CONDITION_FLAG_UNSET                        :
		case BRANCH_CONDITION_FLAG_SET                          :
		  {
		    // l.bf, l.bnf
		    //   * FLAG                              : static direction and destination is know
                    log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"      * BRANCH_CONDITION_FLAG");

		    is_accurate = true; // address dest is know

		    break;
		  }

		case BRANCH_CONDITION_NONE_WITHOUT_WRITE_STACK          :
		  {
		    // l.j
                    log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"      * BRANCH_CONDITION_NONE_WITHOUT_WRITE_STACK");

		    is_accurate = true;

		    break;
		  }
		}

	      if (use_btb)
		{
		  if (_param->_have_port_context_id)
	          PORT_WRITE(out_DECOD_BTB_CONTEXT_ID      [port],context);
		  PORT_WRITE(out_DECOD_BTB_ADDRESS_SRC     [port],address_src);
		  PORT_WRITE(out_DECOD_BTB_ADDRESS_DEST    [port],address_dest);
		  PORT_WRITE(out_DECOD_BTB_CONDITION       [port],condition);
		  PORT_WRITE(out_DECOD_BTB_LAST_TAKE       [port],direction);
		  PORT_WRITE(out_DECOD_BTB_MISS_PREDICTION [port],1);
		  PORT_WRITE(out_DECOD_BTB_IS_ACCURATE     [port],is_accurate);
		}
	      if (use_ras)
		{
		  miss_decod |= not PORT_READ(in_DECOD_RAS_HIT [port]);

		  if (_param->_have_port_context_id)
		  PORT_WRITE(out_DECOD_RAS_CONTEXT_ID      [port],context);
		  PORT_WRITE(out_DECOD_RAS_MISS_PREDICTION [port],1);
		}
	      if (use_upt)
		{
		  PORT_WRITE(out_DECOD_UPT_BTB_ADDRESS_SRC [port],address_src);
		  PORT_WRITE(out_DECOD_UPT_BTB_ADDRESS_DEST[port],address_dest);
		  PORT_WRITE(out_DECOD_UPT_BTB_CONDITION   [port],condition);
		  PORT_WRITE(out_DECOD_UPT_BTB_LAST_TAKE   [port],direction);
		  PORT_WRITE(out_DECOD_UPT_RAS_ADDRESS     [port],PORT_READ(in_DECOD_RAS_ADDRESS_POP [port]));
		  PORT_WRITE(out_DECOD_UPT_IS_ACCURATE     [port],is_accurate);
		}
	    }
// 	  else
// 	    {
//               log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * hit");

// 	      miss_decod = false;
// 	      // Hit speculation
// 	    }
	  

	  // in all case
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * use_btb : %d",use_btb);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * use_ras : %d",use_ras);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * use_upt : %d",use_upt);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * btb_ack [%d] : %d",port,PORT_READ(in_DECOD_BTB_ACK [port]));
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * ras_ack [%d] : %d",port,PORT_READ(in_DECOD_RAS_ACK [port]));
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * upt_ack [%d] : %d",port,PORT_READ(in_DECOD_UPT_ACK [port]));
  
	  btb_val [port] = (use_btb and
			    (not use_ras or (use_ras and PORT_READ(in_DECOD_RAS_ACK [port]))) and 
			    (not use_upt or (use_upt and PORT_READ(in_DECOD_UPT_ACK [port]))));
	  
	  ras_val [port] = (use_ras and
			    (not use_btb or (use_btb and PORT_READ(in_DECOD_BTB_ACK [port]))) and 
			    (not use_upt or (use_upt and PORT_READ(in_DECOD_UPT_ACK [port]))));
	  
	  upt_val [port] = (use_upt and
			    (not use_btb or (use_btb and PORT_READ(in_DECOD_BTB_ACK [port]))) and 
			    (not use_ras or (use_ras and PORT_READ(in_DECOD_RAS_ACK [port]))));

	  if (use_btb)
	    ack [i] &= btb_val [port] and PORT_READ(in_DECOD_BTB_ACK [port]);
	  if (use_ras)
	    ack [i] &= ras_val [port] and PORT_READ(in_DECOD_RAS_ACK [port]);
	  if (use_upt)
	    ack [i] &= upt_val [port] and PORT_READ(in_DECOD_UPT_ACK [port]);
	  
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * btb_val [%d] : %d",port,btb_val [port]);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * ras_val [%d] : %d",port,ras_val [port]);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * upt_val [%d] : %d",port,upt_val [port]);
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * ack     [%d] : %d",i   ,ack     [i   ]);

	  if (_param->_have_port_context_id)
	  PORT_WRITE(out_DECOD_UPT_CONTEXT_ID           [port],context);
	  PORT_WRITE(out_DECOD_UPT_MISS_IFETCH          [port],miss_ifetch); 
	  PORT_WRITE(out_DECOD_UPT_MISS_DECOD           [port],miss_decod ); 
	  if (_param->_have_port_depth)
	  PORT_WRITE(out_DECOD_UPT_UPDATE_PREDICTION_ID [port],depth);
	  
	  port ++;
	}
    
    // Write output

    for (uint32_t i=0; i<_param->_nb_inst_branch_decod; i++)
      {
	PORT_WRITE(out_DECOD_BTB_VAL [i], btb_val [i]);
	PORT_WRITE(out_DECOD_RAS_VAL [i], ras_val [i]);
	PORT_WRITE(out_DECOD_UPT_VAL [i], upt_val [i]);
      }

    for (uint32_t i=0; i<_param->_nb_decod_unit; i++)
      for (uint32_t j=0; j<_param->_nb_inst_decod[i]; j++)
	if (i!= decod_unit)
	  PORT_WRITE(out_DECOD_ACK [i][j], 0);
	else
	  PORT_WRITE(out_DECOD_ACK [i][j], ack[j]);

    log_end(Prediction_unit_Glue,FUNCTION);
  };

}; // end namespace prediction_unit_glue
}; // end namespace prediction_unit
}; // end namespace front_end
}; // end namespace multi_front_end
}; // end namespace core

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