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

#include "Behavioural/Generic/Victim/Victim_Pseudo_LRU/include/Victim_Pseudo_LRU.h"

namespace morpheo {
namespace behavioural {
namespace generic {
namespace victim {
namespace victim_pseudo_lru {

  void Victim_Pseudo_LRU::vhdl_body (Vhdl * & vhdl)
  {
    vhdl->set_body ("");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("-- Access");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("");
    vhdl->set_body ("-- Tree of Pseudo-LRU - example to 8 entity");
    vhdl->set_body ("--");
    vhdl->set_body ("--                4-5-6-7?              ");
    vhdl->set_body ("--           0_______|_______1	    ");
    vhdl->set_body ("--           |               |	    ");
    vhdl->set_body ("--          2-3?            6-7?        ");
    vhdl->set_body ("--       0___|___1       0___|___1      ");
    vhdl->set_body ("--       |       |       |       |      ");
    vhdl->set_body ("--       1?      3?      5?      7?     ");
    vhdl->set_body ("--     0_|_1   0_|_1   0_|_1   0_|_1    ");
    vhdl->set_body ("--     |   |   |   |   |   |   |   |    ");
    vhdl->set_body ("--    Way Way Way Way Way Way Way Way   ");
    vhdl->set_body ("--     0   1   2   3   4   5   6   7    ");
    for (uint32_t i=0; i<_param->_nb_access; i++)
      {
	vhdl->set_body ("");
	// Read the table
	
	std::string access_address;

	if (_param->_size_table>1)
	  access_address = "conv_integer(in_ACCESS_"+toString(i)+"_ADDRESS)";
	else
	  access_address = "0";

	vhdl->set_body ("access_entry_"+toString(i)+" <= reg_TABLE ("+access_address+");");
	vhdl->set_body ("");

	for (int32_t j=static_cast<uint32_t>(log2(_param->_nb_entity)-1); j>=0; j--)
	  {
 	    vhdl->set_body ("access_entity_"+toString(i)+"("+toString(j)+") <= ");

	    uint32_t cpt=0;

	    for (int32_t k=(1<<j)-1; k<static_cast<int32_t>(_param->_nb_entity-1); k+=(1<<(j+1)))
	      {
		std::string cond = "";
		
		// Create the condition
		for (uint32_t l=j+1; l<static_cast<uint32_t>(log2(_param->_nb_entity));l++)
		  {
		    if (l==static_cast<uint32_t>(j+1))
		      cond += "when";
		    else
		      cond += " and";
		    cond += " access_entity_"+toString(i)+"("+toString(l)+") = '"+toString((cpt>>(l-(j+1)))&1)+"'";
		  }
		
		std::string print_else = (k==(1<<j)-1)?"     ":"else ";

		vhdl->set_body ("\t"+print_else+"access_entry_"+toString(i)+"("+toString(k)+") "+cond);
		cpt ++;
	      }
	    vhdl->set_body ("\t;");
	  }
      }

    vhdl->set_body ("");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("-- Update");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("");
    vhdl->set_body ("-- port access");
    for (uint32_t i=0; i<_param->_nb_access; i++)
      for (int32_t j=static_cast<uint32_t>(log2(_param->_nb_entity)-1); j>=0; j--)
	{
	  uint32_t cpt=0;
	  
	  for (int32_t k=(1<<j)-1; k<static_cast<int32_t>(_param->_nb_entity-1); k+=(1<<(j+1)))
	    {
	      bool   have_cond = false;
	      std::string cond      = "";
	      
	      // condition to change the bit
	      for (uint32_t l=j+1; l<static_cast<uint32_t>(log2(_param->_nb_entity));l++)
		{
		  have_cond = true;

		  if (l==static_cast<uint32_t>(j+1))
		    cond += "when";
		  else
		    cond += " and";
		  cond += " access_entity_"+toString(i)+"("+toString(l)+")='"+toString((cpt>>(l-(j+1)))&1)+"'";
		}
	      
	      vhdl->set_body ("access_next_entry_"+toString(i)+"("+toString(k)+") <=");
	      vhdl->set_body ("\tnot access_entity_"+toString(i)+"("+toString(j)+") "+cond);
	      if (have_cond == true)
		vhdl->set_body ("\telse access_entry_"+toString(i)+"("+toString(k)+")");
	      vhdl->set_body ("\t;");	      
	      cpt ++;
	    }
	}

    vhdl->set_body ("");
    vhdl->set_body ("-- port update");
    for (uint32_t i=0; i<_param->_nb_update; i++)
      for (int32_t j=static_cast<uint32_t>(log2(_param->_nb_entity)-1); j>=0; j--)
	{
	  uint32_t cpt=0;
	  
	  for (int32_t k=(1<<j)-1; k<static_cast<int32_t>(_param->_nb_entity-1); k+=(1<<(j+1)))
	    {
	      bool   have_cond = false;
	      std::string cond      = "";
	      
	      // condition to change the bit
	      for (uint32_t l=j+1; l<static_cast<uint32_t>(log2(_param->_nb_entity));l++)
		{
		  have_cond = true;

		  if (l==static_cast<uint32_t>(j+1))
		    cond += "when";
		  else
		    cond += " and";
		  cond += " in_UPDATE_"+toString(i)+"_ENTITY("+toString(l)+")='"+toString((cpt>>(l-(j+1)))&1)+"'";
		}
	      
	      vhdl->set_body ("update_next_entry_"+toString(i)+"("+toString(k)+") <=");
	      vhdl->set_body ("\tnot in_UPDATE_"+toString(i)+"_ENTITY("+toString(j)+") "+cond);
	      if (have_cond == true)
		{
		  std::string update_address;

		  if (_param->_size_table>1)
		    update_address = "conv_integer(in_UPDATE_"+toString(i)+"_ADDRESS)";
		  else
		    update_address = "0";
		  
		  vhdl->set_body ("\telse reg_TABLE ("+update_address+")("+toString(k)+")");
		}
	      vhdl->set_body ("\t;");	      
	      cpt ++;
	    }
	}

    vhdl->set_body ("");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("-- Transition");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("");

    vhdl->set_body ("reg_TABLE_write: process (in_CLOCK)");
    vhdl->set_body ("begin");
    vhdl->set_body ("\tif in_CLOCK'event and in_CLOCK = '1' then");
    vhdl->set_body ("\t\t-- Access port");
    for (uint32_t i=0; i<_param->_nb_access; i++)
      {
	std::string access_address;

	if (_param->_size_table>1)
	  access_address = "conv_integer(in_ACCESS_"+toString(i)+"_ADDRESS)";
	else
	  access_address = "0";

	vhdl->set_body ("\t\tif (in_ACCESS_"+toString(i)+"_VAL = '1') then");
	vhdl->set_body ("\t\t\treg_TABLE ("+access_address+") <= access_next_entry_"+toString(i)+";");
	vhdl->set_body ("\t\tend if;");
      }

    vhdl->set_body ("\t\t-- Update port");
    for (uint32_t i=0; i<_param->_nb_update; i++)
      {
	std::string update_address;

	if (_param->_size_table>1)
	  update_address = "conv_integer(in_UPDATE_"+toString(i)+"_ADDRESS)";
	else
	  update_address = "0";
		  
	vhdl->set_body ("\t\tif (in_UPDATE_"+toString(i)+"_VAL = '1') then");
	vhdl->set_body ("\t\t\treg_TABLE ("+update_address+") <= update_next_entry_"+toString(i)+";");
	vhdl->set_body ("\t\tend if;");
      }

    vhdl->set_body ("\tend if;");
    vhdl->set_body ("end process reg_TABLE_write;");

    vhdl->set_body ("");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("-- Output");
    vhdl->set_body ("-----------------------------------------------------------------------------");
    vhdl->set_body ("");
    vhdl->set_body ("-- Ack is always ");
    vhdl->set_body ("");
    for (uint32_t i=0; i<_param->_nb_access; i++)
      {
	vhdl->set_body ("out_ACCESS_"+toString(i)+"_ACK    <= '1';");
	vhdl->set_body ("out_ACCESS_"+toString(i)+"_ENTITY <= access_entity_"+toString(i)+" when in_ACCESS_"+toString(i)+"_VAL = '1' else (others => '0');");
      }
    vhdl->set_body ("");
    for (uint32_t i=0; i<_param->_nb_update; i++)
      {
	vhdl->set_body ("out_UPDATE_"+toString(i)+"_ACK    <= '1';");
      }
  };

}; // end namespace victim_pseudo_lru
}; // end namespace victim
}; // end namespace generic

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