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

#include "Behavioural/Generic/RegisterFile/RegisterFile_Multi_Banked/include/RegisterFile_Multi_Banked.h"

namespace morpheo                    {
namespace behavioural {
namespace generic {
namespace registerfile {
namespace registerfile_multi_banked {


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

    uint32_t read_select_limit ;
    uint32_t read_nb_select1   ;
    uint32_t read_nb_select2   ;
    uint32_t write_select_limit;
    uint32_t write_nb_select1  ;
    uint32_t write_nb_select2  ;
    
    read_select_limit = _param->_nb_port_read%_param->_nb_port_read_by_bank;
    read_nb_select2   = _param->_nb_port_read/_param->_nb_port_read_by_bank;
    read_nb_select1   = (read_select_limit==0)?0:(read_nb_select2+1);		     
 
    write_select_limit= _param->_nb_port_write%_param->_nb_port_write_by_bank;
    write_nb_select2  = _param->_nb_port_write/_param->_nb_port_write_by_bank;
    write_nb_select1  = (write_select_limit==0)?0:(write_nb_select2+1);		     

    vhdl->set_body("");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("-- Instance bank                   ");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("");

    for (uint32_t i=0; i<_param->_nb_bank; i++)
      {
	vhdl->set_body(_name+"_bank_"+toString(i)+" : "+_name+"_bank");
	vhdl->set_body("port map (");
	vhdl->set_body("\t  in_CLOCK \t=>\tin_CLOCK ");
	vhdl->set_body("\t, in_NRESET\t=>\tin_NRESET");
	for (uint32_t j=0; j<_param->_nb_port_read_by_bank; j++)
	  {
	    vhdl->set_body("\t, in_READ_"+toString(j)+"_VAL     \t=>\tinternal_BANK_READ_"+toString(i)+"_"+toString(j)+"_VAL");
	    vhdl->set_body("\t,out_READ_"+toString(j)+"_ACK     \t=>\tinternal_BANK_READ_"+toString(i)+"_"+toString(j)+"_ACK");
	    if (_param->_have_bank_port_address == true)
	    vhdl->set_body("\t, in_READ_"+toString(j)+"_ADDRESS \t=>\tinternal_BANK_READ_"+toString(i)+"_"+toString(j)+"_ADDRESS");
	    vhdl->set_body("\t,out_READ_"+toString(j)+"_DATA    \t=>\tinternal_BANK_READ_"+toString(i)+"_"+toString(j)+"_DATA");
	  }
	for (uint32_t j=0; j<_param->_nb_port_write_by_bank; j++)
	  {
	    vhdl->set_body("\t, in_WRITE_"+toString(j)+"_VAL     \t=>\tinternal_BANK_WRITE_"+toString(i)+"_"+toString(j)+"_VAL");
	    vhdl->set_body("\t,out_WRITE_"+toString(j)+"_ACK     \t=>\tinternal_BANK_WRITE_"+toString(i)+"_"+toString(j)+"_ACK");
	    if (_param->_have_bank_port_address == true)
	    vhdl->set_body("\t, in_WRITE_"+toString(j)+"_ADDRESS \t=>\tinternal_BANK_WRITE_"+toString(i)+"_"+toString(j)+"_ADDRESS");
	    vhdl->set_body("\t, in_WRITE_"+toString(j)+"_DATA    \t=>\tinternal_BANK_WRITE_"+toString(i)+"_"+toString(j)+"_DATA");
	  }

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

    vhdl->set_body("");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("-- Instance select");
    vhdl->set_body("-- (1 select by port)");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("");
    for (uint32_t i=0; i<_param->_nb_bank; i++)
      {
	for (uint32_t j=0; j<_param->_nb_port_read_by_bank; j++)
	  {
	    uint32_t nb_port = (_param->_crossbar == FULL_CROSSBAR)?_param->_nb_port_read:((j<read_select_limit)?read_nb_select1:read_nb_select2);

	    vhdl->set_body(_name+"_read_select_"+toString(i)+"_"+toString(j)+" : "+_name+"_select_"+toString(nb_port)+"_ports");
	    vhdl->set_body("port map (");
	    for (uint32_t k=0; k<nb_port; k++)
	      {
		uint32_t num_port  = (_param->_crossbar == FULL_CROSSBAR)?k:(_param->_nb_port_read_by_bank*k+j);
		std::string   separator = ((k==0)?" ":",");
		std::string   index     = (_param->_crossbar == FULL_CROSSBAR)?("_"+toString(j)):"";

		vhdl->set_body("\t"+separator+" in_VAL_"+toString(k)+"     \t=>\tinternal_READ_"+toString(i)+"_"+toString(num_port)+index+"_VAL");
		vhdl->set_body("\t,out_ACK_"+toString(k)+"     \t=>\tinternal_SELECT_READ_"+toString(i)+"_"+toString(num_port)+index+"_VAL");
	      }
	    vhdl->set_body(");");
	    vhdl->set_body("");
	  }

	for (uint32_t j=0; j<_param->_nb_port_write_by_bank; j++)
	  {
	    uint32_t nb_port = (_param->_crossbar == FULL_CROSSBAR)?_param->_nb_port_write:((j<write_select_limit)?write_nb_select1:write_nb_select2);

	    vhdl->set_body(_name+"_write_select_"+toString(i)+"_"+toString(j)+" : "+_name+"_select_"+toString(nb_port)+"_ports");
	    vhdl->set_body("port map (");
	    for (uint32_t k=0; k<nb_port; k++)
	      {
		uint32_t num_port  = (_param->_crossbar == FULL_CROSSBAR)?k:(_param->_nb_port_write_by_bank*k+j);
		std::string   separator = ((k==0)?" ":",");
		std::string   index     = (_param->_crossbar == FULL_CROSSBAR)?("_"+toString(j)):"";
		
		vhdl->set_body("\t"+separator+" in_VAL_"+toString(k)+"     \t=>\tinternal_WRITE_"+toString(i)+"_"+toString(num_port)+index+"_VAL");
		vhdl->set_body("\t,out_ACK_"+toString(k)+"     \t=>\tinternal_SELECT_WRITE_"+toString(i)+"_"+toString(num_port)+index+"_VAL");
	      }
	    vhdl->set_body(");");
	    vhdl->set_body("");
	  }
      }

    vhdl->set_body("");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("-- Bank Val");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("");
    for (uint32_t i=0; i<_param->_nb_bank; i++)
      {
	for (uint32_t j=0; j<_param->_nb_port_read_by_bank; j++)
	  {
	    uint32_t nb_port = (_param->_crossbar == FULL_CROSSBAR)?_param->_nb_port_read:((j<read_select_limit)?read_nb_select1:read_nb_select2);

	    vhdl->set_body("internal_BANK_READ_"+toString(i)+"_"+toString(j)+ "_VAL  <= '0'");
	    for (uint32_t k=0; k<nb_port; k++)
	      {
		uint32_t num_port  = (_param->_crossbar == FULL_CROSSBAR)?k:(_param->_nb_port_read_by_bank*k+j);
		std::string   index     = (_param->_crossbar == FULL_CROSSBAR)?("_"+toString(j)):"";
		vhdl->set_body("\tor internal_SELECT_READ_"+toString(i)+"_"+toString(num_port)+index+"_VAL");
	      }
	    vhdl->set_body(";");
	  }
	for (uint32_t j=0; j<_param->_nb_port_write_by_bank; j++)
	  {
	    uint32_t nb_port = (_param->_crossbar == FULL_CROSSBAR)?_param->_nb_port_write:((j<write_select_limit)?write_nb_select1:write_nb_select2);

	    vhdl->set_body("internal_BANK_WRITE_"+toString(i)+"_"+toString(j)+ "_VAL <= '0'");
	    for (uint32_t k=0; k<nb_port; k++)
	      {
		uint32_t num_port  = (_param->_crossbar == FULL_CROSSBAR)?k:(_param->_nb_port_write_by_bank*k+j);
		std::string   index     = (_param->_crossbar == FULL_CROSSBAR)?("_"+toString(j)):"";

		vhdl->set_body("\tor internal_SELECT_WRITE_"+toString(i)+"_"+toString(num_port)+index+"_VAL");
	      }
	    vhdl->set_body(";");
	  }
      }

    vhdl->set_body("");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("-- Bank Address");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("");

    if (_param->_have_bank_port_address == true)
    for (uint32_t i=0; i<_param->_nb_bank; i++)
      {
	for (uint32_t j=0; j<_param->_nb_port_read_by_bank; j++)
	  {
	    uint32_t nb_port = (_param->_crossbar == FULL_CROSSBAR)?_param->_nb_port_read:((j<read_select_limit)?read_nb_select1:read_nb_select2);

	    vhdl->set_body("internal_BANK_READ_"+toString(i)+"_"+toString(j)+ "_ADDRESS  <=");
	    for (uint32_t k=1; k<nb_port; k++)
	      {
		uint32_t num_port  = (_param->_crossbar == FULL_CROSSBAR)?k:(_param->_nb_port_read_by_bank*k+j);
		std::string   index     = (_param->_crossbar == FULL_CROSSBAR)?("_"+toString(j)):"";
				
		vhdl->set_body("\tin_READ_"+toString(num_port)+"_ADDRESS"+std_logic_range(_param->_size_address_by_bank)+"  when internal_SELECT_READ_"+toString(i)+"_"+toString(num_port)+index+"_VAL ='1' else");
	      }
	    vhdl->set_body("\tin_READ_"+toString(j)+"_ADDRESS"+std_logic_range(_param->_size_address_by_bank)+";");
// 	    vhdl->set_body("\t"+std_logic_others(_param->_size_word,0)+";");
	  }
	for (uint32_t j=0; j<_param->_nb_port_write_by_bank; j++)
	  {
	    uint32_t nb_port = (_param->_crossbar == FULL_CROSSBAR)?_param->_nb_port_write:((j<write_select_limit)?write_nb_select1:write_nb_select2);

	    vhdl->set_body("internal_BANK_WRITE_"+toString(i)+"_"+toString(j)+ "_ADDRESS <=");
	    for (uint32_t k=1; k<nb_port; k++)
	      {
		uint32_t num_port  = (_param->_crossbar == FULL_CROSSBAR)?k:(_param->_nb_port_write_by_bank*k+j);
		std::string   index     = (_param->_crossbar == FULL_CROSSBAR)?("_"+toString(j)):"";

		vhdl->set_body("\tin_WRITE_"+toString(num_port)+"_ADDRESS"+std_logic_range(_param->_size_address_by_bank)+" when internal_SELECT_WRITE_"+toString(i)+"_"+toString(num_port)+index+"_VAL='1' else");
	      }
	    vhdl->set_body("\tin_WRITE_"+toString(j)+"_ADDRESS"+std_logic_range(_param->_size_address_by_bank)+";");
// 	    vhdl->set_body("\t"+std_logic_others(_param->_size_word,0)+";");
	  }
      }

    vhdl->set_body("");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("-- Bank Data");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("");
    for (uint32_t i=0; i<_param->_nb_bank; i++)
      {
	for (uint32_t j=0; j<_param->_nb_port_write_by_bank; j++)
	  {
	    uint32_t nb_port = (_param->_crossbar == FULL_CROSSBAR)?_param->_nb_port_write:((j<write_select_limit)?write_nb_select1:write_nb_select2);

	    vhdl->set_body("internal_BANK_WRITE_"+toString(i)+"_"+toString(j)+ "_DATA <=");
	    for (uint32_t k=1; k<nb_port; k++)
	      {
		uint32_t num_port  = (_param->_crossbar == FULL_CROSSBAR)?k:(_param->_nb_port_write_by_bank*k+j);
		std::string   index     = (_param->_crossbar == FULL_CROSSBAR)?("_"+toString(j)):"";
		vhdl->set_body("\tin_WRITE_"+toString(num_port)+"_DATA when internal_SELECT_WRITE_"+toString(i)+"_"+toString(num_port)+index+"_VAL='1' else");
	      }
	    vhdl->set_body("\tin_WRITE_"+toString(j)+"_DATA;");
// 	    vhdl->set_body("\t"+std_logic_others(_param->_size_word,0)+";");
	  }
      }

    vhdl->set_body("");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("-- VAL (to Select)");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("");

    for (uint32_t i=0; i<_param->_nb_bank; i++)
      {
	for (uint32_t j=0; j<_param->_nb_port_read; j ++)
	  {
	    std::string str_address;

	    if (_param->_have_bank_port_address == true)
	      str_address = (_param->_nb_bank==1)?"":("and (in_READ_"+toString(j)+"_ADDRESS"+std_logic_range(_param->_size_address-1,_param->_size_address_by_bank)+"="+std_logic_conv( _param->_size_address-_param->_size_address_by_bank,i)+") ");
	    else
	      str_address = "";

	    vhdl->set_body("internal_READ_"+toString(i)+"_"+toString(j)+"_VAL  <= '1' when (in_READ_"+toString(j)+"_VAL ='1') "+str_address+"else '0';");
	  }
	for (uint32_t j=0; j<_param->_nb_port_write; j ++)
	  {
	    std::string str_address;

	    if (_param->_have_port_address == true)
	      str_address = (_param->_nb_bank==1)?"":("and (in_WRITE_"+toString(j)+"_ADDRESS"+std_logic_range(_param->_size_address-1,_param->_size_address_by_bank)+"="+std_logic_conv( _param->_size_address-_param->_size_address_by_bank,i)+") ");
	    else
	      str_address = "";

	    vhdl->set_body("internal_WRITE_"+toString(i)+"_"+toString(j)+"_VAL <= '1' when (in_WRITE_"+toString(j)+"_VAL='1') "+str_address+"else '0';");
	  }
      }

    if (_param->_crossbar == FULL_CROSSBAR)
      {
	for (uint32_t i=0; i<_param->_nb_bank; i++)
	  {
	    for (uint32_t j=0; j<_param->_nb_port_read; j++)
	      {
		for (uint32_t k=0; k<_param->_nb_port_read_by_bank; k++)
		  {
		    vhdl->set_body("internal_READ_"+toString(i)+"_"+toString(j)+"_"+toString(k)+"_VAL  <= internal_READ_"+toString(i)+"_"+toString(j)+"_VAL  and not");
		    vhdl->set_body("\t('0'");
		    for (uint32_t l=0; l<k; l++)
		      {
			vhdl->set_body("\tor internal_SELECT_READ_"+toString(i)+"_"+toString(j)+"_"+toString(l)+"_VAL"); 
		      }
		    
		    vhdl->set_body("\t);");
		  }
	      }
	    for (uint32_t j=0; j<_param->_nb_port_write; j++)
	      {
		for (uint32_t k=0; k<_param->_nb_port_write_by_bank; k++)
		  {
		    vhdl->set_body("internal_WRITE_"+toString(i)+"_"+toString(j)+"_"+toString(k)+"_VAL <= internal_WRITE_"+toString(i)+"_"+toString(j)+"_VAL and not");
		    vhdl->set_body("\t('0'");

		      for (uint32_t l=0; l<k; l++)
		      {
			vhdl->set_body("\tor internal_SELECT_WRITE_"+toString(i)+"_"+toString(j)+"_"+toString(l)+"_VAL"); 
		      }
		    
		    vhdl->set_body("\t);");
		  }
	      }
	  }
      }
    vhdl->set_body("");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("-- OUTPUT");
    vhdl->set_body("-----------------------------------");
    vhdl->set_body("");

    if (_param->_crossbar == FULL_CROSSBAR)
      {
	for (uint32_t i=0; i<_param->_nb_port_read; i ++)
	  {
	    vhdl->set_body("out_READ_"+toString(i)+"_ACK  <= ");
	    for (uint32_t j=0; j<_param->_nb_bank; j ++)
	      {
		for (uint32_t k=0; k<_param->_nb_port_read_by_bank; k ++)
		  {
		    vhdl->set_body("\tinternal_BANK_READ_"+toString(j)+"_"+toString(k)+"_ACK  when internal_SELECT_READ_"+toString(j)+"_"+toString(i)+"_"+toString(k)+"_VAL  = '1' else");
		  }
	      }
	    vhdl->set_body("\t'0';");
	    vhdl->set_body("out_READ_"+toString(i)+"_DATA <= ");
	    for (uint32_t j=0; j<_param->_nb_bank; j ++)
	      {
		for (uint32_t k=0; k<_param->_nb_port_read_by_bank; k ++)
		  {
		    vhdl->set_body("\tinternal_BANK_READ_"+toString(j)+"_"+toString(k)+"_DATA when internal_SELECT_READ_"+toString(j)+"_"+toString(i)+"_"+toString(k)+"_VAL  = '1' else");
		  }
	      }
 	    vhdl->set_body("\t"+std_logic_others(_param->_size_word,0)+";");
	  }
	for (uint32_t i=0; i<_param->_nb_port_write; i ++)
	  {
	    vhdl->set_body("out_WRITE_"+toString(i)+"_ACK <= ");
	    for (uint32_t j=0; j<_param->_nb_bank; j ++)
	      {
		for (uint32_t k=0; k<_param->_nb_port_write_by_bank; k ++)
		  {
		    vhdl->set_body("\tinternal_BANK_WRITE_"+toString(j)+"_"+toString(k)+"_ACK when internal_SELECT_WRITE_"+toString(j)+"_"+toString(i)+"_"+toString(k)+"_VAL = '1' else");
		  }
	      }
	    vhdl->set_body("\t'0';");
	  }
      }
    else
      {
	for (uint32_t i=0; i<_param->_nb_port_read; i ++)
	  {
	    uint32_t link = _param->_link_port_read_to_bank_read[i];

	    vhdl->set_body("out_READ_"+toString(i)+"_ACK  <= ");
	    for (uint32_t j=0; j<_param->_nb_bank; j ++)
	      {
		vhdl->set_body("\tinternal_BANK_READ_"+toString(j)+"_"+toString(link)+"_ACK  when internal_SELECT_READ_"+toString(j)+"_"+toString(i)+"_VAL  = '1' else");
	      }
// 	    vhdl->set_body("\tinternal_BANK_READ_"+toString(0)+"_"+toString(link)+"_ACK;");
	    vhdl->set_body("\t'0';");

	    vhdl->set_body("out_READ_"+toString(i)+"_DATA <= ");
	    for (uint32_t j=1; j<_param->_nb_bank; j ++)
	      {
		vhdl->set_body("\tinternal_BANK_READ_"+toString(j)+"_"+toString(link)+"_DATA when internal_SELECT_READ_"+toString(j)+"_"+toString(i)+"_VAL = '1' else");
	      }
	    vhdl->set_body("\tinternal_BANK_READ_"+toString(0)+"_"+toString(link)+"_DATA;");
// 	    vhdl->set_body("\t"+std_logic_others(_param->_size_word,0)+";");
	  }
	for (uint32_t i=0; i<_param->_nb_port_write; i ++)
	  {
	    uint32_t link = _param->_link_port_write_to_bank_write[i];

	    vhdl->set_body("out_WRITE_"+toString(i)+"_ACK <= ");
	    for (uint32_t j=0; j<_param->_nb_bank; j ++)
	      {
		vhdl->set_body("\tinternal_BANK_WRITE_"+toString(j)+"_"+toString(link)+"_ACK when internal_SELECT_WRITE_"+toString(j)+"_"+toString(i)+"_VAL = '1' else");
	      }
// 	    vhdl->set_body("\tinternal_BANK_WRITE_"+toString(0)+"_"+toString(link)+"_ACK;");
	    vhdl->set_body("\t'0';");
	  }
      }

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

}; // end namespace registerfile_multi_banked
}; // end namespace registerfile
}; // end namespace generic
}; // end namespace behavioural
}; // end namespace morpheo              
#endif
