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

#include "Behavioural/Stage_1_Ifetch/Predictor/Meta_Predictor/Two_Level_Branch_Predictor/Two_Level_Branch_Predictor_Glue/include/Two_Level_Branch_Predictor_Glue.h"

namespace morpheo                    {
namespace behavioural {
namespace stage_1_ifetch {
namespace predictor {
namespace meta_predictor {
namespace two_level_branch_predictor {
namespace two_level_branch_predictor_glue {


  void Two_Level_Branch_Predictor_Glue::vhdl_body (Vhdl * & vhdl)
  {
    log_printf(FUNC,Two_Level_Branch_Predictor_Glue,"vhdl_body","Begin");

    vhdl->set_body ("-- ack output");
    for (uint32_t i=0; i<_param._nb_prediction; i++)
      {
	string bht_ack, pht_ack;

	if (_param._have_bht)
	  bht_ack = "in_PREDICT_"+toString(i)+"_BHT_ACK";
	else
	  bht_ack = "'1'";

	if (_param._have_pht)
	  pht_ack = "in_PREDICT_"+toString(i)+"_PHT_ACK";
	else
	  pht_ack = "'1'";

	vhdl->set_body ("out_PREDICT_"+toString(i)+"_ACK         <= "+bht_ack+" and "+pht_ack+";");
      }

    for (uint32_t i=0; i<_param._nb_branch_complete; i++)
      {
	string bht_ack, pht_ack;

	if (_param._have_bht)
	  bht_ack = "in_BRANCH_COMPLETE_"+toString(i)+"_BHT_ACK";
	else
	  bht_ack = "'1'";

	if (_param._have_pht)
	  pht_ack = "in_BRANCH_COMPLETE_"+toString(i)+"_PHT_ACK";
	else
	  pht_ack = "'1'";

	vhdl->set_body ("out_BRANCH_COMPLETE_"+toString(i)+"_ACK <= "+bht_ack+" and "+pht_ack+";");
      }
    
    vhdl->set_body ("");
    vhdl->set_body ("-- address output");

    for (uint32_t i=0; i<_param._nb_prediction; i++)
      {
	// The address is higher than the address size of bht
	if (_param._have_bht)
	  vhdl->set_body ("out_PREDICT_"+toString(i)+"_BHT_ADDRESS <= in_PREDICT_"+toString(i)+"_ADDRESS"+std_logic_range(_param._bht_size_address)+";");

	if (_param._have_pht)
	  {
	    // For pht : 2 cases
	    if (_param._have_bht)
	      {
		uint32_t min = 0;
		uint32_t max = _param._pht_size_address;

		// A lot of case :
		//
		//      pht_size_addr
		// addr             bht_history
		//
		// ---------] |
		//            |[---------
		//
		// -----------|----]               share
		//            |  [-------
		//
		// -----------|----]            no share
		//            |     [-----
		//
		
		// Test if address is share 
		if (_param._pht_size_address_shift > 0)
		  {
		    min = _param._pht_size_address_shift;
		    vhdl->set_body ("out_PREDICT_"+toString(i)+"_PHT_ADDRESS"+std_logic_range(min) + " <=  in_PREDICT_"+toString(i)+"_BHT_HISTORY"+std_logic_range(min)+";");
		  }

		if (_param._bht_size_history < _param._pht_size_address)
		  {
		    max = _param._bht_size_history;
		    vhdl->set_body ("out_PREDICT_"+toString(i)+"_PHT_ADDRESS"+std_logic_range(_param._pht_size_address-1,max) + " <=  in_PREDICT_"+toString(i)+"_ADDRESS"+std_logic_range(_param._pht_size_address-1-_param._pht_size_address_shift, _param._pht_size_address_share)+";");
		  }
		
		// intersection
		if (_param._pht_size_address_share > 0)
		  vhdl->set_body ("out_PREDICT_"+toString(i)+"_PHT_ADDRESS"+std_logic_range(max-1,min) + " <=  in_PREDICT_"+toString(i)+"_ADDRESS"+std_logic_range(_param._pht_size_address_share-1,0)+" xor in_PREDICT_"+toString(i)+"_BHT_HISTORY"+std_logic_range(max-1,min)+";");
	      }
	    else
	      vhdl->set_body ("out_PREDICT_"+toString(i)+"_PHT_ADDRESS <=  in_PREDICT_"+toString(i)+"_ADDRESS"+std_logic_range(_param._pht_size_address)+";");
	  }
      }

    for (uint32_t i=0; i<_param._nb_branch_complete; i++)
      {
	// The address is higher than the address size of bht
	if (_param._have_bht)
	  vhdl->set_body ("out_BRANCH_COMPLETE_"+toString(i)+"_BHT_ADDRESS <= in_BRANCH_COMPLETE_"+toString(i)+"_ADDRESS"+std_logic_range(_param._bht_size_address)+";");

	if (_param._have_pht)
	  {
	    // For pht : 2 cases
	    if (_param._have_bht)
	      {
		uint32_t min = 0;
		uint32_t max = _param._pht_size_address;

		// Test if address is share 
		if (_param._pht_size_address_shift > 0)
		  {
		    min = _param._pht_size_address_shift;
		    vhdl->set_body ("out_BRANCH_COMPLETE_"+toString(i)+"_PHT_ADDRESS"+std_logic_range(min) + " <=  in_BRANCH_COMPLETE_"+toString(i)+"_BHT_HISTORY"+std_logic_range(min)+";");
		  }

		if (_param._bht_size_history < _param._pht_size_address)
		  {
		    max = _param._bht_size_history;
		    vhdl->set_body ("out_BRANCH_COMPLETE_"+toString(i)+"_PHT_ADDRESS"+std_logic_range(_param._pht_size_address-1,max) + " <=  in_BRANCH_COMPLETE_"+toString(i)+"_ADDRESS"+std_logic_range(_param._pht_size_address-1-_param._pht_size_address_shift, _param._pht_size_address_share)+";");
		  }
		
		// intersection
		if (_param._pht_size_address_share > 0)
		  vhdl->set_body ("out_BRANCH_COMPLETE_"+toString(i)+"_PHT_ADDRESS"+std_logic_range(max-1,min) + " <=  in_BRANCH_COMPLETE_"+toString(i)+"_ADDRESS"+std_logic_range(_param._pht_size_address_share-1,0)+" xor in_BRANCH_COMPLETE_"+toString(i)+"_BHT_HISTORY"+std_logic_range(max-1,min)+";");
	      }
	    else
	      vhdl->set_body ("out_BRANCH_COMPLETE_"+toString(i)+"_PHT_ADDRESS <=  in_BRANCH_COMPLETE_"+toString(i)+"_ADDRESS"+std_logic_range(_param._pht_size_address)+";");
	  }
      }

    log_printf(FUNC,Two_Level_Branch_Predictor_Glue,"vhdl_body","End");
  };

}; // end namespace two_level_branch_predictor_glue
}; // end namespace two_level_branch_predictor
}; // end namespace meta_predictor
}; // end namespace predictor
}; // end namespace stage_1_ifetch

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