///////////////////////////////////////////////////////////////////////////
// File : soclib_segment_table.h
// Date : 08/07/2004
// authors : François Pécheux & Alain Greiner
//
// Copyright : UPMC - LIP6
// This program is released under the GNU public license
//
// This object is used to describe the memory mapping and address 
// decoding scheme. It supports a two level hierarchical interconnect :
// All VCI initiators and VCI targets share the same address space. 
// - Several sub-systems can be interconnected thanks to a VCI ADVANCED 
// global interconnect that can be either a micro-network, or a global system bus.
// - Each sub-system can contain as many targets as the LSB size allows.
// They are connected by a VCI ADVANCED local interconnect, that can be
// a local bus, or a VCI to VCI bridge such as the TARGET_DEMUX 
// and INITIATOR_MUX components of the SoCLib library.
// 
// The VCI ADDRESS is structured as follows (three fields): | MSB | LSB | OFFSET |
// The MSB bits of the VCI ADDRESS are decoded by the global interconnect
// to route a VCI CMD/RSP packet to a given sub-system.
// In each sub-system, the LSB bits of the VCI ADDRESS are decoded by the
// local interconnect to route the a VCI CMD/RSP packet to the selected target/initiator.
//
// If the number of bits of the LSB field is zero, a global "flat" interconnect 
// is used, and both the VCI target index and the VCI initiator index are coded
// on 8 bits.
//
// The segment table is an associative table that is organized as a 
// list of segments. This table must be built in the system top cell.
// 
// Each segment descriptor contains the following fields:
//
// - const char		*name	  : segment name
// - unsigned int 	base	  : base address
// - unsigned int 	size	  : segment size (in bytes)
// - unsigned int	globalindex	: VCI subsystem index 
// - unsigned int	localindex	: VCI target index in the subsystem
// - bool 		uncached  : uncached when true
//
// The constraints on the memory mapping are the following :
// 
// 1/ The system designer must define the number of bits for the address
// MSB and LSB fields. Both numbers can be zero. (For example the number 
// of LSB bits will be zero, in case of a "flat" VCI interconnect).
// 
// 2/ The (MSB | LSB) bits are indexing a "page" in the address space.
// Several pages can be mapped to the same VCI target.
// 
// 3/ A segment is allocated to a VCI target, and must be contained in 
// a single page or in several contiguous pages.
//
// 4/ Several segments can be mapped to the same VCI target
// (thanks to the SOCLIB_VCI_MULTIRAM component)
// 
// 5/ A given page cannot contain two segments that are not
// mapped to the same VCI target.
// 
// The segment table is used by most SoCLib  components :
//
// - The constructors of the VCI interconnect components can use it to build 
// the ROMs implementing the routing tables 
// - The constructors of all VCI targets use it to implement the segmentation
// violation detection mechanism, and allocate the buffers representing 
// the memory in case of an embedded memory component.
// - The constructors of several processors (MIPS, SPARC) use it to build 
// the ROM implementing the uncached table.
///////////////////////////////////////////////////////////////////////////

#ifndef SEGMENT_TABLE_H
#define SEGMENT_TABLE_H

#include <list>
#include <iostream>
// using namespace std;

//////////////////////////////////////////////
//	SEGMENT_TABLE_ENTRY definition
//////////////////////////////////////////////

struct SEGMENT_TABLE_ENTRY
{

private:

  const char 		*name;
  unsigned int 		base;
  unsigned int 		size;
  unsigned int 		globalTarget;
  unsigned int		localTarget;
  bool                  uncached ;
  //-----[ SIM2OS - begin ]-----------------------------------------
  void *                addr_alloc;
  //-----[ SIM2OS - end   ]-----------------------------------------

public:

// constructor

SEGMENT_TABLE_ENTRY(const char *nm ,
		    unsigned int ba , 
		    unsigned int sz ,
		    unsigned int glt,
		    unsigned int lct,
		    bool unc
		    )
	{
	name=nm;
	base=ba;
	size=sz;
	globalTarget=glt;
	localTarget=lct;
	uncached=unc;
  //-----[ SIM2OS - begin ]-----------------------------------------
	addr_alloc = (void *) 0;
  //-----[ SIM2OS - end   ]-----------------------------------------
	}; // end constructor

// basic methods

void print()
	{ 
	printf("segment : %s , base = %.8x , size = %.8x , Global target = %d , Local Target = %d , unc = %d, addr_alloc : %.8x\n",
	       name, base, size, globalTarget, localTarget, uncached, (unsigned int)((long long)addr_alloc));
	};

const char *getName()
	{
	return name;
	};

unsigned int getBase()
	{
	return base;
	};

unsigned int getSize()
	{
	return size;
	};

unsigned int getGlobalTarget()
	{
	return globalTarget;
	};
	
unsigned int getLocalTarget()
	{
	return localTarget;
	};

bool getUncached()
	{
	return uncached;
	};

  //-----[ SIM2OS - begin ]-----------------------------------------
  void * getAddrAlloc()
  {
    return addr_alloc;
  }

  void setAddrAlloc (void * addr)
  {
    addr_alloc = addr;
  }
  //-----[ SIM2OS - end   ]-----------------------------------------

  };// end struct SEGMENT_TABLE_ENTRY


//////////////////////////////////////////////////////////
//	SegmentTable definition
//////////////////////////////////////////////////////////

// using namespace std;

struct SOCLIB_SEGMENT_TABLE {
	
private :

std::list<SEGMENT_TABLE_ENTRY> 	segList;
int 				MSBNumber;
int 				LSBNumber;
unsigned int			defaultTargetGlobalIndex;
unsigned int			defaultTargetLocalIndex;
bool 				MSBNumberCalled;
bool 				defaultTargetCalled;
bool				LSBNumberCalled;

public :	

//	CONSTRUCTOR

SOCLIB_SEGMENT_TABLE()
	{
	MSBNumberCalled=false;
	LSBNumberCalled=false;
	defaultTargetCalled=false;
	};  // end constructor 

//	BASIC METHODS

void setMSBNumber (int number)  
	{
	if ((number<0)||(number>16))
		{
		std::cerr << "ERROR in the Segment Table :" << std::endl ;
		std::cerr << "MSB number must be in the [0..16] range !" << std::endl ;
		//sc_stop();
		}
		MSBNumberCalled=true;
		MSBNumber=number;
	};

void setLSBNumber (int number)  
	{
	if ((number<0)||(number>16))
		{
		std::cerr << "ERROR in the Segment Table :" << std::endl ;
		std::cerr << "LSB number must be in the [0..16] range !" << std::endl ;
		//sc_stop();
		}
		LSBNumberCalled=true;		
		LSBNumber=number;
	};

int getMSBNumber()
 	{
	return MSBNumber;
	};

int getLSBNumber()
	{
	return LSBNumber;
	};

void setDefaultTarget (int _defTargetGlobalIndex , int _defTargetLocalIndex)
        {
		defaultTargetCalled=true;
        defaultTargetGlobalIndex=_defTargetGlobalIndex;
        defaultTargetLocalIndex=_defTargetLocalIndex;
        };

int getdefaultTargetGlobalIndex()
        {
        return defaultTargetGlobalIndex;
        };
        
int getdefaultTargetLocalIndex()
		{
		return defaultTargetLocalIndex;
		};	

void addSegment(const char *nm,
		unsigned int ba,
		unsigned int sz,
		unsigned int glt,
		unsigned int lct,
		bool unc)
	{
	SEGMENT_TABLE_ENTRY *seg=new SEGMENT_TABLE_ENTRY(nm,ba,sz,glt,lct,unc);
        segList.push_back(*seg);
	delete seg;
	};

  //-----[ SIM2OS - begin ]-----------------------------------------
  void setAddrAlloc(unsigned int ba  ,
		    void *       addr)
  {
    std::list<SEGMENT_TABLE_ENTRY>::iterator segIterator;
    // Scan of all list
    for (segIterator = segList.begin() ; segIterator != segList.end() ; ++segIterator) 
      {
	// Find a entry match with number of base 
	// NOTE : The memory is shared, also we can't have 2 segment with the same base
	if (ba == (*segIterator).getBase ())
	  {
	    (*segIterator).setAddrAlloc(addr);
	  }
      }
  };

  void * getAddrAlloc(unsigned int addr)
  {
    std::list<SEGMENT_TABLE_ENTRY>::iterator segIterator;

    // Scan of all list
    for (segIterator = segList.begin() ; segIterator != segList.end() ; ++segIterator) 
      {
	unsigned int addr_min = (*segIterator).getBase ();
	unsigned int addr_max = addr_min + (*segIterator).getSize ();
	
	if ((addr >= addr_min) && (addr <= addr_max))
	  {
	    unsigned int offset = (addr-addr_min);
	    return (void*)((unsigned int)(long long)((*segIterator).getAddrAlloc()) + offset);
	  }
      }
    return NULL;
  }
  //-----[ SIM2OS - end   ]-----------------------------------------

void print()
	{
	  std::cout << "\n                 SEGMENT_TABLE\n\n" ;
	std::list<SEGMENT_TABLE_ENTRY>::iterator iter;
	for (iter = segList.begin() ; iter != segList.end() ; ++iter)
		{
		(*iter).print();
		}
	std::cout << "\n" ;
	}

void printMSBRoutingTable()
	{
	  std::cout << "\n                 MSB ROUTING_TABLE\n\n" ;
	  int size = 1 << MSBNumber+LSBNumber;
	  unsigned int	*tab = new unsigned int[size];
	  initRoutingTable(tab);
	  for(int index = 0 ; index < size ; index++) {
	    if (tab[index]!=0xFFFFFFFF){
	      printf("TAB[%x] = %d\n", index, tab[index]);
	    }
	  }
	  std::cout << "\n" ;
	};
  
  void printLSBRoutingTable(int glt)
  {
    printf( "\n    LSB_ROUTING_TABLE OF GLOBAL TARGET :%d \n\n", glt );
    int size = 1 << MSBNumber+LSBNumber;
    unsigned int	*tab = new unsigned int[size];
    initLocalRoutingTable(tab,glt);
    for(int index = 0 ; index < size ; index++) {
      if (tab[index]!=0xFFFFFFFF){
	printf("TAB[%d] = %d\n", index, tab[index]);
      }
    }
    std::cout << "\n" ;
  };
	


void initRoutingTable(unsigned int* tab) {
	if (MSBNumberCalled==false) {
		std::cerr << "ERROR in initMSBRoutingTable:" << std::endl ;
		std::cerr << "MSB number has not been defined !" << std::endl ;
		//sc_stop();
	}
	if (LSBNumberCalled==false) {
	  std::cout << "WARNING in initMSBRoutingTable:" << std::endl ;
	  std::cout << "LSB number has not been defined !" << std::endl ;
	  std::cout << "it is set to 0" << std::endl ;
	  LSBNumberCalled=true;
	  LSBNumber=0;
	}
	
	
	if (defaultTargetCalled==false) {
		std::cerr << "ERROR in initMSBRoutingTable:" << std::endl ;
		std::cerr << "Default Target has not been defined!" << std::endl ;
		//sc_stop();
	}

	int RoutingTableSize = 1 << MSBNumber+LSBNumber;
	int PageSize         = 1 << (32 - MSBNumber - LSBNumber);
	
	// The 0xFFFFFFFF value means "not allocated"
	for (int i=0 ; i < RoutingTableSize ; i++) {
		tab[i] = 0xFFFFFFFF;
	}
//		printf ( " tableau rempli de 0xFFFFFFFF \n");

	// loop on the segment list
	std::list<SEGMENT_TABLE_ENTRY>::iterator seg;
	for (seg = segList.begin() ; seg != segList.end() ; ++seg) {
		const char    *name   = (*seg).getName();
		unsigned int  base    = (*seg).getBase();
		unsigned int  size    = (*seg).getSize();
		unsigned int globalTarget  = (*seg).getGlobalTarget();

		unsigned int page = base >> (32-MSBNumber - LSBNumber) ;
		
		for (int x = (int) size ; x > 0 ; x = x - PageSize) {		     
			if(tab[page] != 0xFFFFFFFF) {
		//		printf("\n page n° %x can not be added to cluster %x because it is already allocated to %x\n",page,globalTarget,tab[page]);
				std::cerr << "Error in initMSBRoutingTable:" << std::endl ;
				std::cerr << "Segment " << name << " allocated to VCI target " << globalTarget << std::endl;
				std::cerr << "overlap another segment... or is in the same page" << std::endl;
				std::cerr << "as another segment allocated to another VCI target" << std::endl;
				//sc_stop();
		  	} else {
				tab[page]=globalTarget;
				
		//		printf("\n page n° %x allocated to cluster °%x i.e %s \n",page,tab[page],name);
				}
			page = page + 1;
			} // end for x
	} // end for seg
	
	// Default target
	for (int i=0 ; i < RoutingTableSize ; i++) {
		if(tab[i] == 0xFFFFFFFF) tab[i] = defaultTargetGlobalIndex;
	}
	
};  // end initRoutingTable()

////////////////////////////////////////////////////////////////
//		initGlobalRoutingTable()
//	This method can be used by any global VCI interconnect.
//	It initializes the routing table that implements 
//	the VCI address MSB bits decoding ROM.
//	- The argument is a pointer to the int table that
//	has been allocated by the constructor of the 
//	VCI interconnect component.
//	- Each entry contains a VCI target global index.
//	The VCI local Target index is not used here
////////////////////////////////////////////////////////////////


void initGlobalRoutingTable(unsigned int* tab) {
	if (MSBNumberCalled==false) {
		std::cerr << "ERROR in initMSBRoutingTable:" << std::endl ;
		std::cerr << "MSB number has not been defined !" << std::endl ;
		//sc_stop();
	}
	
	if (defaultTargetCalled==false) {
		std::cerr << "ERROR in initMSBRoutingTable:" << std::endl ;
		std::cerr << "Default Target has not been defined!" << std::endl ;
		//sc_stop();
	}

	int RoutingTableSize = 1 << MSBNumber;
	int PageSize         = 1 << (32 - MSBNumber);
	
	// The 0xFFFFFFFF value means "not allocated"
	for (int i=0 ; i < RoutingTableSize ; i++) {
		tab[i] = 0xFFFFFFFF;
	}
//		printf ( " tableau rempli de 0xFFFFFFFF \n");

	// loop on the segment list
	std::list<SEGMENT_TABLE_ENTRY>::iterator seg;
	for (seg = segList.begin() ; seg != segList.end() ; ++seg) {
		const char    *name   = (*seg).getName();
		unsigned int  base    = (*seg).getBase();
		unsigned int  size    = (*seg).getSize();
		unsigned int globalTarget  = (*seg).getGlobalTarget();

		unsigned int page = base >> (32-MSBNumber) ;
		
		for (int x = (int) size ; x > 0 ; x = x - PageSize) {		     
			if(tab[page] != 0xFFFFFFFF) {
		//		printf("\n page n° %x can not be added to cluster %x because it is already allocated to %x\n",page,globalTarget,tab[page]);
				std::cerr << "Error in initGlobalRoutingTable:" << std::endl ;
				std::cerr << "Segment " << name << " allocated to VCI target " << globalTarget << std::endl;
				std::cerr << "overlap another segment... or is in the same page" << std::endl;
				std::cerr << "as another segment allocated to another VCI target" << std::endl;
				//sc_stop();
		  	} else {
				tab[page]=globalTarget;
				
		//		printf("\n page n° %x allocated to cluster °%x i.e %s \n",page,tab[page],name);
				}
			page = page + 1;
			} // end for x
	} // end for seg
	
	// Default target
	for (int i=0 ; i < RoutingTableSize ; i++) {
		if(tab[i] == 0xFFFFFFFF) tab[i] = defaultTargetGlobalIndex;
	}
	
};  // end initGlobalRoutingTable()


////////////////////////////////////////////////////////////////
//		initLocalRoutingTable()
//	This method can be used by the local VCI interconnect.
//	It initializes the routing table that implements 
//	the VCI address LSB bits decoding ROM.
//	- The argument is a pointer to the char table that
//	has been allocated by the constructor of the local 
//	VCI interconnect component.
//	- Each entry contains a VCI target local index as well as a global index .
//	- This method needs a parameter : the global index of the subsystem ,
//	because it only fills the pages corresponding to a segment in this subsystem
//	other pages are adressed to the Default Target
////////////////////////////////////////////////////////////////

void initLocalRoutingTable(unsigned int* tab, unsigned int cluster) {
	if (MSBNumberCalled==false) {
		std::cerr << "ERROR in initLocalRoutingTable:" << std::endl ;
		std::cerr << "MSB number has not been defined !" << std::endl ;
		//sc_stop();
	}
		
	if (defaultTargetCalled==false) {
		std::cerr << "ERROR in initLocalRoutingTable:" << std::endl ;
		std::cerr << "Default Target has not been defined!" << std::endl ;
		//sc_stop();
	}

	int RoutingTableSize = 1 << MSBNumber+LSBNumber;
	int PageSize         = 1 << (32 - MSBNumber - LSBNumber );
	
	// The 0xFF value means "not allocated"
	for (int i=0 ; i < RoutingTableSize ; i++) {
		tab[i] = 0xFF;
	}
	// printf ( " Local routing table cluster %x: tableau rempli de 0xFF \n",cluster);
	// loop on the segment list
	std::list<SEGMENT_TABLE_ENTRY>::iterator seg;
	for (seg = segList.begin() ; seg != segList.end() ; ++seg) {
		const char    *name   = (*seg).getName();
		unsigned int  base    = (*seg).getBase();
		unsigned int  size    = (*seg).getSize();
		unsigned int localTarget  = (*seg).getLocalTarget();
		unsigned int globalTarget = (*seg).getGlobalTarget();

		unsigned int page = base >> (32 - MSBNumber - LSBNumber) ;
		
		for (int x = (int) size ; x > 0 ; x = x - PageSize) {		     
			  if(tab[page] != 0xFF) {
			  // printf("\n page n° %x ne peut etre adressée à target n°%x car elle est allouée à %x \n",page,localTarget,tab[page]);	
			  std::cerr << "Error in initLocalRoutingTable:" << std::endl ;
			  std::cerr << "Segment " << name << " allocated to VCI target " << localTarget << std::endl;
			  std::cerr << "overlap another segment... or is in the same page" << std::endl;
			  std::cerr << "as another segment allocated to another VCI target" << std::endl;
			  //sc_stop();
		  	  } else {
		  	  	if (globalTarget==cluster){
			  		tab[page] = localTarget;
			  		// printf(" page n° %x adressée à target n°%x dans cluster %x i.e %s \n",page,tab[page],cluster,name);
			  	  }
			  page = page + 1;
			  
			  }
		} 
	} // end loop segment list

	for (int i=0 ; i < RoutingTableSize ; i++) {
		if ((tab[i]==0xFF) && (defaultTargetGlobalIndex==cluster))
			tab[i]=defaultTargetLocalIndex;
	}
		
};  // end initLocalRoutingTable()

//////////////////////////////////////////////////////////////
//		getSegmentList()
//	returns the list of all segments 
//	allocated to a given VCI target.
//	This method tests both the local and global VCI target index
//////////////////////////////////////////////////////////////

std::list<SEGMENT_TABLE_ENTRY> getSegmentList(unsigned int globalIndex,unsigned int localIndex){
	std::list<SEGMENT_TABLE_ENTRY> targetlist;
	std::list<SEGMENT_TABLE_ENTRY>::iterator segIterator;
	unsigned int localTarget;
	unsigned int globalTarget;
	
	for (segIterator = segList.begin() ; segIterator != segList.end() ; ++segIterator) {
		localTarget=(*segIterator).getLocalTarget();
		globalTarget=(*segIterator).getGlobalTarget();
		if ((localTarget==localIndex)&&(globalTarget==globalIndex)){
			targetlist.push_back(*segIterator);
		}
	}
	return(targetlist);
	
}; // end getSegmentList()

////////////////////////////////////////////////////
// function getTotalsegmentList
// used to get the whole segmentlist
///////////////////////////////////////////////////

std::list<SEGMENT_TABLE_ENTRY> getTotalSegmentList()
{

return(segList);

}; // end getSegmentList()

////////////////////////////////////////////////////////////////
//		initUncachedTable()
//	This method can be used by any SoCLib processor.
//	It initializes an "Uncached Table" that implements 
//	the address MSB bits decoding ROM.
//	- The argument is a pointer to the bool table that
//	has been allocated by the constructor of the processor.
//	- Each ROM entry contains a boolean that is true when
//	the adress Ris to an uncached segment.
///////////////////////////////////////////////////////////////:

void initUncachedTable(bool* tab) {
	if ((MSBNumberCalled==false)||(LSBNumberCalled==false))
		{
		std::cerr << "ERROR in initUncachedTable:" << std::endl ;
		std::cerr << "MSB or LSB number has not been defined !" << std::endl ;
		//sc_stop();
		}

	int UncachedTableSize  = 1 << (MSBNumber+LSBNumber);
	int PageSize           = 1 << (32 - (MSBNumber + LSBNumber));
	
	for (int i=0 ; i < UncachedTableSize ; i++) 
		{
		tab[i] = true;
		}

	std::list<SEGMENT_TABLE_ENTRY>::iterator iter;

	for (   iter = segList.begin() ; 
		iter != segList.end() ; 
		++iter)
		{
		unsigned int  base      = (*iter).getBase();
		unsigned int  size      = (*iter).getSize();
		bool          uncached  = (*iter).getUncached();

		unsigned int page_index = base >> (32-((MSBNumber + LSBNumber))) ;

		for (int x = (int) size ; x > 0 ; x = x - PageSize) 
			{		     
			tab[page_index] = uncached;
			page_index = page_index + 1;
			} 
		} // end for segment list
};  // end initUncachedTable()

};  // end struct SOCLIB_SEGMENT_TABLE


#endif
