#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());

    if (PORT_READ(in_NRESET))
      {
    // 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++)
      {
        log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"  * DECOD [%d][%d]",decod_unit,i);
        log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * val  : %d",PORT_READ(in_DECOD_VAL [decod_unit][i]));
        log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * port : %d",port);

      // 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);
          Tcontrol_t        can_continue          = false;

	  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          take;
	      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");

                    take        = true ;
		    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");

                    take        = true ;
		    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");

                    take        = true ;
		    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
                    take        = true ;
		    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");

                    take        = PORT_READ(in_DECOD_BRANCH_DIRECTION [decod_unit][i]);
		    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");

                    take        = true;
		    is_accurate = true;

		    break;
		  }
		}

              log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * take         : %d",take);

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

              can_continue = false; // need update upt
	    }
 	  else
 	    {
              log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * hit");

              if (branch_state == BRANCH_STATE_SPEC_NTAKE)
                can_continue = PORT_READ(in_DECOD_UPT_CAN_CONTINUE [port]);
              else
                // BRANCH_STATE_NONE       -> miss
                // BRANCH_STATE_NSPEC_TAKE -> take
                // BRANCH_STATE_SPEC_TAKE  -> take
                can_continue = false;

// 	      miss_decod = false;
// 	      // Hit speculation
 	    }
	  
	  // in all case
	  log_printf(TRACE,Prediction_unit_Glue,FUNCTION,"    * can_continue : %d",can_continue);
	  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(out_DECOD_CAN_CONTINUE [decod_unit][i], can_continue);

	  port ++; // have find 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]);
      }
    else
      {
        // Reset
        for (uint32_t i=0; i<_param->_nb_inst_branch_decod; i++)
          {
            PORT_WRITE(out_DECOD_BTB_VAL [i], 0);
            PORT_WRITE(out_DECOD_RAS_VAL [i], 0);
            PORT_WRITE(out_DECOD_UPT_VAL [i], 0);
          }
        
        for (uint32_t i=0; i<_param->_nb_decod_unit; i++)
          for (uint32_t j=0; j<_param->_nb_inst_decod[i]; j++)
            PORT_WRITE(out_DECOD_ACK [i][j], 0);
      }
    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
