#ifndef HIERARCHY_MEMORY_H
#define HIERARCHY_MEMORY_H
#include <iostream>
#include <stdint.h>
#include <stdarg.h>

#include <systemc.h>
#include "shared/soclib_segment_table.h"
#include "shared/soclib_caches_interfaces.h"

#include "hierarchy_memory/data/data.h"
#include "hierarchy_memory/file/sort_file.h"
#include "hierarchy_memory/file/entry.h"
#include "hierarchy_memory/cache/cache.h"
#include "hierarchy_memory/ramlock/ramlock.h"
#include "hierarchy_memory/tty/tty.h"
#include "hierarchy_memory/sim2os/sim2os.h"

#include "hierarchy_memory/cache/type_req_cache.h"

using namespace std;
using namespace hierarchy_memory::data;
using namespace hierarchy_memory::sort_file;
using namespace hierarchy_memory::cache;
using namespace hierarchy_memory::ramlock;
using namespace hierarchy_memory::tty;
using namespace hierarchy_memory::sim2os;

namespace hierarchy_memory
{
  template <class T>
  class param_entity_t
  {
  public : uint32_t address ;
  public : uint32_t size    ;
  public : T        param   ;
    
  public : param_entity_t () {};

  public : param_entity_t (uint32_t address ,
			   uint32_t size    ,
			   T        param   )
    {
      this->address = address ;
      this->size    = size    ;
      this->param   = param   ;
    }
  };//end param_entity_t

  class param_t
  {
  public : uint32_t                            nb_entity_tty;
  public : param_entity_t <tty::param_t>     * param_tty;
  public : uint32_t                            nb_entity_ramlock;
  public : param_entity_t <ramlock::param_t> * param_ramlock;
  public : param_entity_t <sim2os::param_t>    param_sim2os;
  public : cache::param_t                      param_cache;

  public : param_t () {};

  public : param_t (uint32_t                            nb_entity_tty     ,
		    param_entity_t <tty::param_t>     * param_tty         ,
		    uint32_t                            nb_entity_ramlock ,
		    param_entity_t <ramlock::param_t> * param_ramlock     ,
		    param_entity_t <sim2os::param_t>    param_sim2os      ,
		    cache::param_t                      param_cache)
    {
      this->nb_entity_tty     = nb_entity_tty;
      this->param_tty         = param_tty;
      this->nb_entity_ramlock = nb_entity_ramlock;
      this->param_ramlock     = param_ramlock;
      this->param_sim2os      = param_sim2os;
      this->param_cache       = param_cache;
    }
  };//end param_t

  template<uint32_t SIZE_TRDID    ,
	   uint32_t SIZE_IPKTID   ,
	   uint32_t SIZE_IADDR    ,
	   uint32_t SIZE_IDATA    ,
	   uint32_t NB_IWORD      ,
	   uint32_t SIZE_DPKTID   ,
	   uint32_t SIZE_DADDR    ,
	   uint32_t SIZE_DDATA    
	   >

  class HIERARCHY_MEMORY : sc_module 
  {
    //---------------------------------------------------------------------------------------------
    //-----[ PORT ]--------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------

  public : sc_in_clk                                                                        CLK;
  public : sc_in<bool>                                                                      NRESET;
  public : ICACHE_CACHE_PORTS<SIZE_TRDID, SIZE_IPKTID, SIZE_IADDR, SIZE_IDATA, NB_IWORD> ** ICACHE;
  public : DCACHE_CACHE_PORTS<SIZE_TRDID, SIZE_DPKTID, SIZE_DADDR, SIZE_DDATA>           ** DCACHE;

    //---------------------------------------------------------------------------------------------
    //-----[ INTERNAL SIGNAL ]---------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  private: bool                                                                          ** irsp_val;
  private: bool                                                                          ** drsp_val;

  private: bool                                                                          ** ireq_ack;
  private: bool                                                                          ** dreq_ack;
    
    //---------------------------------------------------------------------------------------------
    //-----[ COMPONENT ]---------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  private: Data                                                                           * component_data;
  private: Sort_File<Entry>                                                              ** component_buffer_irsp;
  private: Sort_File<Entry>                                                              ** component_buffer_drsp;

  private: Sim2os                                                                         * component_sim2os;
  private: Tty                                                                           ** component_tty;
  private: Ramlock                                                                       ** component_ramlock;

  private: Cache                                                                          * component_cache;
    //---------------------------------------------------------------------------------------------
    //-----[ VARIABLE ] ---------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------

  private: char                                                                           * NAME;                    // instance name
  private: uint32_t                                                                       * nb_iport;
  private: uint32_t                                                                       * nb_dport;
  private: const uint32_t                                                                   nb_context;
  private: const uint32_t                                                                   nb_entity;
  private: const uint32_t                                                                   nb_entity_tty;
  private: const uint32_t                                                                   nb_entity_ramlock;

  private: char **                                                                          read_iram;
  private: char *                                                                           read_dram;
  private: char *                                                                           write_dram;

  private: bool                                                                           * context_stop;    // to determine which context have send the signal stop (a same thread can send many signal)
  private: uint32_t                                                                         nb_context_stop; // stop the simulation when all context have send the stop signal 
    //---------------------------------------------------------------------------------------------
    //-----[ CONSTRUCTOR ]-------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
    SC_HAS_PROCESS(HIERARCHY_MEMORY);
    
  public : HIERARCHY_MEMORY (// Fixe parameter
			     sc_module_name              insname               ,
			     uint32_t                    globalIndex           , // VCI target index
			     uint32_t                    localIndex            ,
			     SOCLIB_SEGMENT_TABLE      * segtab                , // segment table pointer
			     // variable parameter			           
			     uint32_t                    nb_entity             ,
			     uint32_t                    nb_context            ,
			     //*****[ buffer respons ]*****	           
			     uint32_t                    size_buffer_irsp      ,
			     uint32_t                    size_buffer_drsp      ,
			     //*****[ entity_param   ]*****
			     param_t                     param        				  
			     ):
    nb_context        (nb_context             ),
    nb_entity         (nb_entity              ),
    nb_entity_tty     (param.nb_entity_tty    ),
    nb_entity_ramlock (param.nb_entity_ramlock)
    {
      uint32_t size_name = strlen(insname)+1;
      NAME               = new char [size_name];
      strncpy(NAME,insname,size_name);

      // *****[ Signal creation ]*****
      ICACHE = new ICACHE_CACHE_PORTS<SIZE_TRDID, SIZE_IPKTID, SIZE_IADDR, SIZE_IDATA, NB_IWORD> * [nb_entity];
      DCACHE = new DCACHE_CACHE_PORTS<SIZE_TRDID, SIZE_DPKTID, SIZE_DADDR, SIZE_DDATA>           * [nb_entity];

      nb_iport     = new uint32_t     [nb_entity];
      nb_dport     = new uint32_t     [nb_entity];
	           		      
      irsp_val     = new bool *       [nb_entity];
      drsp_val     = new bool *       [nb_entity];
	           		      
      ireq_ack     = new bool *       [nb_entity];
      dreq_ack     = new bool *       [nb_entity];

      for (uint32_t it = 0; it < nb_entity; it++)
	{
	  nb_iport     [it] = param.param_cache.param_icache_dedicated [it].nb_port;
	  nb_dport     [it] = param.param_cache.param_dcache_dedicated [it].nb_port;
		      
	  irsp_val     [it] = new bool       [nb_iport[it]];
	  drsp_val     [it] = new bool       [nb_dport[it]];
		     
	  ireq_ack     [it] = new bool       [nb_iport[it]];
	  dreq_ack     [it] = new bool       [nb_dport[it]];

	  ICACHE [it] = new ICACHE_CACHE_PORTS<SIZE_TRDID, SIZE_IPKTID, SIZE_IADDR, SIZE_IDATA, NB_IWORD> [nb_iport [it]];
	  DCACHE [it] = new DCACHE_CACHE_PORTS<SIZE_TRDID, SIZE_DPKTID, SIZE_DADDR, SIZE_DDATA>           [nb_dport [it]];
	}

      read_iram = new char * [NB_IWORD];
      for (unsigned int num_word = 0; num_word < NB_IWORD; num_word ++)
	read_iram [num_word] = new char [SIZE_IDATA/8]; 

      read_dram  = new char [SIZE_DDATA/8];
      write_dram = new char [SIZE_DDATA/8];

      // *****[ Create internal structure ]*****

      component_data        = new Data     (data::param_t("component_data", 16, globalIndex, localIndex, segtab));
      component_buffer_irsp = new Sort_File<Entry> * [nb_entity];
      component_buffer_drsp = new Sort_File<Entry> * [nb_entity];

      for (uint32_t it = 0; it < nb_entity; it++)
	{
	  char name [100];
	  sprintf(name,"component_buffer_irsp[%d]",it);
	  component_buffer_irsp [it] = new Sort_File<Entry> (sort_file::param_t(name,size_buffer_irsp));
	  sprintf(name,"component_buffer_drsp[%d]",it);
	  component_buffer_drsp [it] = new Sort_File<Entry> (sort_file::param_t(name,size_buffer_drsp));
	  
	}

      component_cache       = new Cache   (param.param_cache);
      component_tty         = new Tty     * [nb_entity_tty    ];
      component_ramlock     = new Ramlock * [nb_entity_ramlock];

      for (uint32_t it = 0; it < nb_entity_tty    ; it++)
	{
	  component_tty     [it] = new Tty     (param.param_tty[it]    .param);
	  entity_t entity = component_data->entity(param.param_tty[it].address,     param.param_tty[it].size);
	  
	  if (entity.present == false)
	    {
	      cerr << "<HIERARCHY_MEMORY> the tty [" << it << "] have not a segment in the segment table" << endl;
	      exit (1);
	    }

	  entity.segment->define_target(TYPE_TTY,it);
	} 

      for (uint32_t it = 0; it < nb_entity_ramlock; it++)
	{
	  component_ramlock [it] = new Ramlock (param.param_ramlock[it].param);
	  entity_t entity = component_data->entity(param.param_ramlock[it].address, param.param_ramlock[it].size);

	  if (entity.present == false)
	    {
	      cerr << "<HIERARCHY_MEMORY> the ramlock [" << it << "] have not a segment in the segment table" << endl;
	      exit (1);
	    }

	  entity.segment->define_target(TYPE_RAMLOCK,it);
	}

      component_sim2os = new Sim2os   (param.param_sim2os.param);
      entity_t entity  = component_data->entity(param.param_sim2os.address, param.param_sim2os.size);

      if (entity.present == false)
	{
	  cerr << "<HIERARCHY_MEMORY> the sim2os have not a segment in the segment table" << endl;
	  exit (1);
	}
      
      entity.segment->define_target(TYPE_SIM2OS,0);

      nb_context_stop = 0;
      context_stop    = new bool [1<<SIZE_TRDID]; // the number of context is include in the REQ_TRDID
      for (uint32_t num_thread = 0; num_thread < (1<<SIZE_TRDID); num_thread ++)
	context_stop [num_thread] = false;
      
      // *****[ Definition of method ]*****
      SC_METHOD (transition);
      sensitive_pos << CLK;
      
      SC_METHOD (genMoore);
      sensitive_neg << CLK;

      cout << "<" << NAME << "> Successful Instanciation" << endl;
    };
    
    //---------------------------------------------------------------------------------------------
    //-----[ DESTRUCTOR ]--------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  public : ~HIERARCHY_MEMORY ()
    {
      for (uint32_t it = 0; it < nb_entity_ramlock; it++)
	delete component_ramlock [it];      
      for (uint32_t it = 0; it < nb_entity_tty    ; it++)
	delete component_tty     [it];

      for (uint32_t it = 0; it < nb_entity        ; it++)
	{
	  delete component_buffer_drsp [it];
	  delete component_buffer_irsp [it];
	}

      delete component_cache       ;
      delete component_data        ;
      delete component_sim2os      ;
    }

    //---------------------------------------------------------------------------------------------
    //-----[ req_type2cache_type ]-----------------------------------------------------------------
    //---------------------------------------------------------------------------------------------

    class req_type2cache_type_t
    {
    public : type_req_cache_t      type      ;
    public : direction_req_cache_t direction ;

    public : req_type2cache_type_t () {};

    public : req_type2cache_type_t (type_req_cache_t      type      ,
				    direction_req_cache_t direction )
      {
	this->type      = type     ;
	this->direction = direction;
      };
    };

    req_type2cache_type_t ireq_type2cache_type (uint32_t ireq_type, bool uncached)
    {
      type_req_cache_t      type     ;
      direction_req_cache_t direction;
      
      switch (ireq_type)
	{
	case ITYPE_READ       : direction=READ ; type=CACHED    ; break;
	case ITYPE_INVALIDATE : direction=NONE ; type=INVALIDATE; break;
	case ITYPE_PREFETCH   : direction=NONE ; type=PREFETCH  ; break;
	default : cerr << "<HIERARCHY_MEMORY.ireq_type2cache_type> Unkown type (" << type << ")" << endl; exit(1);
	}

      return req_type2cache_type_t (type,direction);
    }

    req_type2cache_type_t dreq_type2cache_type (uint32_t dreq_type, bool uncached)
    {
      type_req_cache_t      type     ;
      direction_req_cache_t direction;
      
      switch (dreq_type)
	{
	case DTYPE_READ       : direction=READ ; type=((uncached==true)?UNCACHED:CACHED); break;
	case DTYPE_INVALIDATE : direction=NONE ; type=INVALIDATE                        ; break;
	case DTYPE_WRITE      : direction=WRITE; type=((uncached==true)?UNCACHED:CACHED); break;
	case DTYPE_WRITE_ACK  : direction=WRITE; type=((uncached==true)?UNCACHED:CACHED); break;
	case DTYPE_FLUSH      : direction=NONE ; type=FLUSH                             ; break;
	case DTYPE_PREFETCH   : direction=NONE ; type=INVALIDATE                        ; break;
	  
	default : cerr << "<HIERARCHY_MEMORY.ireq_type2cache_type> Unkown type (" << type << ")" << endl; exit(1);
	}

      return req_type2cache_type_t (type,direction);
    }
    //---------------------------------------------------------------------------------------------
    //-----[ init ]--------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  public : bool init (char * section, const char * filename, const char ** list_section)
    {
      return component_data->init(section,filename,list_section);
    }
    //---------------------------------------------------------------------------------------------
    //-----[ reset ]-------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  public : void reset ()
    {
      component_cache -> reset();
      // Reset buffer of respons
      for (uint32_t it = 0; it < nb_entity; it++)
	{
	  component_buffer_irsp [it] ->reset();
	  component_buffer_drsp [it] ->reset();
	}
      for (uint32_t it = 0; it < nb_entity_ramlock; it++)
	component_ramlock [it]   ->reset();
      for (uint32_t it = 0; it < nb_entity_tty    ; it++)
	component_tty     [it]   ->reset();
    }//end reset

    //---------------------------------------------------------------------------------------------
    //-----[ itoa ]--------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  private :char * itoa (uint32_t int_src, char * string_dest, uint32_t size)
    {
      for (unsigned int it_size = 0; it_size < size; it_size ++)
	{
	  string_dest [it_size] = (int_src & 0xFF);
	  //string_dest [size-it_size-1] = (int_src & 0xFF);

	  int_src >>= 8;
	}
      return string_dest;
    }

    //---------------------------------------------------------------------------------------------
    //-----[ stop ]--------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  public : bool stop ()
    {
      return (nb_context_stop >= nb_context);
    }

    //---------------------------------------------------------------------------------------------
    //-----[ transition ]--------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  public : void transition()
    {
      if (NRESET.read() == false)
	{
	  reset ();
	  return;
	}
      
      //~~~~~[ respons ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      // POP All buffer of respons
      for (uint32_t num_entity = 0; num_entity < nb_entity; num_entity++)
	{
	  for (uint32_t num_port = nb_iport [num_entity]; num_port > 0 ; num_port --)
	    if ((irsp_val[num_entity][num_port-1] && ICACHE [num_entity][num_port-1].RSP_ACK.read())==true)
	      component_buffer_irsp [num_entity]->pop(num_port-1);
	  
	  for (uint32_t num_port = nb_dport [num_entity]; num_port > 0 ; num_port --)
	    if ((drsp_val[num_entity][num_port-1] && DCACHE [num_entity][num_port-1].RSP_ACK.read())==true)
	      component_buffer_drsp [num_entity]->pop(num_port-1);
	}

      //~~~~~[ request ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      //       cout << "<HIERARCHY_MEMORY.transition>" << endl;
      for (uint32_t num_entity = 0; num_entity < nb_entity; num_entity++)
	{
	  // ******************
	  // ***** ICACHE *****
	  // ******************
	  for (uint32_t num_port = 0; num_port < nb_iport [num_entity]; num_port ++)
	    {
	      // Test if transaction
	      if ( (ICACHE [num_entity][num_port].REQ_VAL.read() && ireq_ack [num_entity][num_port]) == false)
		continue;

	      entity_t entity    = component_data->entity((uint32_t)ICACHE [num_entity][num_port].REQ_ADDR.read(), SIZE_IDATA/8);
	      bool     uncached  = false;
	      bool     bus_error = false;
	      
	      sc_uint<2>           type           = ICACHE[num_entity][num_port].REQ_TYPE  .read();
	      // A lot of flag
	      bool                 must_read      = ((type == DTYPE_READ      ) );
	      bool                 must_ack       = ((type == DTYPE_READ      ) );
	      
	      // Test the type of the address : if != MEMORY -> error
	      if ((entity.present == true) && (entity.segment->getType() == TYPE_MEMORY))
		{
		if (must_read == true) // Test if must read the ram
		  for (unsigned int num_word = 0; num_word < NB_IWORD; num_word ++)
		    {
		      uint32_t addr = (uint32_t) ICACHE [num_entity][num_port].REQ_ADDR.read()+num_word*(SIZE_IDATA/8);
		      bus_error |= !component_data->read(addr               ,
							SIZE_IDATA/8       ,
							read_iram[num_word]);
		      
		      if (isSameEndianness((uint32_t)ICACHE[num_entity][num_port].REQ_TRDID.read()) == false) 
			read_iram[num_word] = swapBytes(read_iram[num_word],SIZE_IDATA/8,4);
		    }
		uncached = entity.segment->getUncached();
		}
		else
		  {
		    // Have a bus error
		    bus_error = true;
		    uncached  = true;
		  }

	      req_type2cache_type_t cache_type = ireq_type2cache_type ((uint32_t)type,uncached);
	      // Simplification : the size of a line is a multiple of size_iword (no test)
	      uint32_t latence = component_cache->latence(INSTRUCTION_CACHE                                        ,
							  num_entity                                               ,
							  num_port                                                 ,
							  (uint32_t)ICACHE [num_entity][num_port].REQ_ADDR .read() ,
							  (uint32_t)ICACHE [num_entity][num_port].REQ_TRDID.read() ,
							  cache_type.type                                          ,
							  cache_type.direction                                     );
	      
	      // If is a respons -> compute the latence and push in the write_buffer
	      if (must_ack == true)
		{
		  if (bus_error == true)
		    cout << "Icache : have a bus error" << endl
			 << "  * num_entity : " << num_entity << endl
			 << "  * num_port   : " << num_port   << endl
			 << hex
			 << "  * req_addr   : " << (uint32_t)ICACHE [num_entity][num_port].REQ_ADDR .read() << endl
			 << dec
			 << "  * req_trdid  : " << (uint32_t)ICACHE [num_entity][num_port].REQ_TRDID.read() << endl
			 << "  * req_pktid  : " << (uint32_t)ICACHE [num_entity][num_port].REQ_PKTID.read() << endl;
		  
		  component_buffer_irsp [num_entity]->push(latence,
							   Entry((uint32_t)ICACHE [num_entity][num_port].REQ_TRDID.read() ,
								 (uint32_t)ICACHE [num_entity][num_port].REQ_PKTID.read() ,
								 NB_IWORD                                                 ,
								 SIZE_IDATA/8                                             ,
								 read_iram                                                ,
								 (bus_error==true)?ERR_BUS:ERR_NO                         )
							   );			
		}
	    }//num_port

	  // ******************
	  // ***** DCACHE *****
	  // ******************

	  for (uint32_t num_port = 0; num_port < nb_dport [num_entity]; num_port ++)
	    {
	      // Test if transaction
// 	      cout << "[" << num_entity << "]"
// 		   << "[" << num_port   << "] "
// 		   << "dreq_val : " << DCACHE [num_entity][num_port].REQ_VAL.read() << " "
// 		   << "dreq_ack : " << dreq_ack [num_entity][num_port] << endl;

	      if ( (DCACHE [num_entity][num_port].REQ_VAL.read() && dreq_ack [num_entity][num_port]) == false)
		continue;
	      
	      entity_t             entity         = component_data->entity((uint32_t)DCACHE [num_entity][num_port].REQ_ADDR.read(), SIZE_DDATA/8);

	      bool                 uncached       = DCACHE [num_entity][num_port].REQ_UNC.read();
	      bool                 bus_error      = false;
	      
	      uint32_t             addr           = (uint32_t) DCACHE [num_entity][num_port].REQ_ADDR.read();
	      sc_uint<SIZE_DDATA>  wdata          = DCACHE[num_entity][num_port].REQ_WDATA .read();
	      sc_uint<3>           type           = DCACHE[num_entity][num_port].REQ_TYPE  .read();
	      uint32_t             nb_bytes       = access_nb_bytes(DCACHE[num_entity][num_port].REQ_ACCESS.read());
	      // A lot of flag
	      bool                 must_read      = ((type == DTYPE_READ      ));
	      bool                 must_write     = ((type == DTYPE_WRITE     ) ||
						     (type == DTYPE_WRITE_ACK ) );
	      bool                 must_ack       = ((type == DTYPE_READ      ) ||
						     (type == DTYPE_WRITE_ACK ) );

	      // Test the type of the address
	      if (entity.present == true)
		{
		switch (entity.segment->getType())
		  {
		    // ACCESS AT A RAM
		  case TYPE_MEMORY  : 
		    {
		      if (must_read == true)
			{
			  // Read
			  bus_error |= !component_data->read(addr         ,
							    SIZE_DDATA/8 , // always read a complete word
							    read_dram    );
			  
			  for (unsigned int it_size_data = nb_bytes; it_size_data < SIZE_DDATA/8; it_size_data+=nb_bytes)
			    memcpy(&(read_dram[it_size_data]),&(read_dram[0]),nb_bytes);

			  // Permutation if problem of endianness
			  if (isSameEndianness((uint32_t)DCACHE[num_entity][num_port].REQ_TRDID.read()) == false) 
			    read_dram  = swapBytes(read_dram , SIZE_DDATA/8, nb_bytes);
			}
		      
		      if (must_write == true)
			{
			  // Write
			  for (unsigned int it_nb_bytes = 0; it_nb_bytes < SIZE_DDATA / 8; it_nb_bytes ++)
			    write_dram [it_nb_bytes] = wdata.range(8*(it_nb_bytes+1)-1,8*it_nb_bytes);
			}
		      break;
		    }
		    //ACCESS AT THE TTY
		  case TYPE_TTY     :
		    {
		      if (must_write == false)
			{
			  bus_error = true;
			  break;
			}
		      uint32_t num_tty           = (addr - entity.segment->getBase())>>4;
		      uint32_t num_print         = ((addr>>2) & 0x3);
		      
		      switch (num_print)
			{
			case 0 : // Write TTY
			  {
			    uint32_t num_component_tty = entity.segment->getIndex();
			    char     char_write        = (char)wdata.range( 7, 0);
			    bus_error |= !component_tty [num_component_tty]->write(num_tty,char_write);
			    break;
			  }
			case 1 : // STOP
			  {
			    printf("\n\t***** [ stop    ] Time : %.10d - Address : %.8x - Wdata[31:0] : %.2x%.2x%.2x%.2x         *****\n"
				   ,(unsigned int)sc_simulation_time()
				   ,(unsigned int)addr
				   ,(unsigned int)wdata.range(31,24)
				   ,(unsigned int)wdata.range(23,16)
				   ,(unsigned int)wdata.range(15, 8)
				   ,(unsigned int)wdata.range( 7, 0)
				   );

			    uint32_t trdid = (uint32_t) DCACHE[num_entity][num_port].REQ_TRDID.read();
			    
			    if (context_stop [trdid] == false)
			      {
				context_stop [trdid] = true;
				nb_context_stop ++;

				if (nb_context_stop >= nb_context)
				  sc_stop();
			      }

			    break;
			  }
			case 2 : // PRINT
			  {
			    printf("\n\t----- [ print   ] Time : %.10d - Address : %.8x - Wdata[31:0] : %.2x%.2x%.2x%.2x         -----\n"
				   ,(unsigned int)sc_simulation_time()
				   ,(unsigned int)addr
				   ,(unsigned int)wdata.range(31,24)
				   ,(unsigned int)wdata.range(23,16)
				   ,(unsigned int)wdata.range(15, 8)
				   ,(unsigned int)wdata.range( 7, 0)
				   );
			    
			    break;
			  }
			default :
			  {
			    printf("<%s> : [address : %.8x] tty %d, reg %d don't exist\n",NAME,(unsigned int)addr,num_tty,num_print);
			    exit(1);
			  }
			}
		      
		      break;
		    }
		  case TYPE_RAMLOCK :
		    {
		      // Access is on a byte, else error
		      if (nb_bytes != 1)
			{
			  bus_error = true;
			  break;
			}
		      uint32_t num_ramlock           = (addr - entity.segment->getBase()); // Char access
		      uint32_t num_component_ramlock = entity.segment->getIndex();
		      bus_error |= !component_ramlock [num_component_ramlock]->test(num_ramlock);
		      
		      if (bus_error == true)
			break;
		      
		      memset (read_dram,0,SIZE_DDATA/8);
			
		      if (must_read == true)
			read_dram [0] = (char)component_ramlock [num_component_ramlock]->read (num_ramlock);
		      if (must_write == true)
			read_dram [0] = (char)component_ramlock [num_component_ramlock]->write(num_ramlock);

		      /*
		      printf("Access ramlock   ( %d )\n" ,(uint32_t)sc_simulation_time());
		      printf(" * addr          : %.8x\n" ,(uint32_t)addr);
		      printf(" * trdid         : %d\n"   ,(uint32_t)DCACHE[num_entity][num_port].REQ_TRDID.read());
		      printf(" * r/w           : %d/%d\n",must_read,must_write);
		      printf(" * val           : %d\n"   ,(uint32_t)read_dram[0]);
		      */
		      break;
		    }
		  case TYPE_SIM2OS  :
		    {
		      // Mapping :
		      // [0]  number of service - Wonly - A write in this register lunch the execution of service
		      // [1]  result            - Ronly - Content the result of the service
		      // [2]  error             - Ronly - Content the code of errno
		      // [3+] argument          - Wonly - it's all argument to execute the service

		      uint32_t num_reg = (addr - entity.segment->getBase())>>2;
		      
		      switch (num_reg)
			{
			case 0  : // ---> number of service
			  {
			    if (must_write == false)
			      {
				cerr << "<" << NAME << "> {ERROR} : SIM2OS[0] is not accessible in Read" << endl;
				bus_error = true;
			      }
			    else
			      {
				printf("<sim2os> service     : %.8x\n",(uint32_t)wdata);
				component_sim2os->execute(int2service((uint32_t)wdata));
			      }
			  break;
			  }
			case 1  : // ---> result
			  { 
			    if (must_read == false)
			      {
				cerr << "<" << NAME << "> {ERROR} : SIM2OS[1] is not accessible in Write" << endl;
				bus_error = true;
			      }
			    else
			      {
				// Decomposition en groupe octect
				uint32_t result = (uint32_t) component_sim2os->result;
				printf("<sim2os> result      : %.8x (%d)\n",result,result);
				
				read_dram = itoa(result,read_dram,SIZE_DDATA/8);
			      }
			    break;
			  }
			case 2  : // ---> error
			  {
			    if (must_read == false)
			      {
				cerr << "<" << NAME << "> {ERROR} : SIM2OS[2] is not accessible in Write" << endl;
				bus_error = true;
			      }
			    else
			      {
				// Decomposition en groupe octect
				uint32_t error = (uint32_t) component_sim2os->error;
				printf("<sim2os> error       : %.8x\n",error);
				read_dram = itoa(error ,read_dram,SIZE_DDATA/8);
			      }
			    break;
			  }
			default : // ---> argument
			  { 
			    if (must_write == false)
			      {
				cerr << "<" << NAME << "> {ERROR} : SIM2OS[" << num_reg << "] is not accessible in Write" << endl;
				bus_error = true;
			      }
			    else
			      {
				uint32_t data = (uint32_t)wdata;
				printf("<sim2os> argument[%d] : %.8x\n",num_reg-1,data);
				component_sim2os->parameter(num_reg-2,(void *)data);
			      }
			    break;
			  }
			}//end switch num_reg
		      
		      break;
		    }
		  default           :
		    {
		      // Have a bus error
		      bus_error = true;
		      break;
		    }
		  }// switch
		uncached |= entity.segment->getUncached();
		}
	      else
		uncached = true; // If segment don't exist : it's the system bus that determine if the segment exist


	      if ((must_write == true) && (bus_error == false))
		{
		  // Permutation if problem of endianness
		  if (isSameEndianness((uint32_t)DCACHE[num_entity][num_port].REQ_TRDID.read()) == false) 
		    write_dram = swapBytes(write_dram, SIZE_DDATA/8, nb_bytes);
		  
		  bus_error |= !component_data->write(addr                   ,
						     nb_bytes, // take the good access
						     write_dram             );
		}
	      

	      // Acces at the cache !!!
	      req_type2cache_type_t cache_type = dreq_type2cache_type (type, uncached);
	      
	      uint32_t latence = component_cache->latence(DATA_CACHE                                               ,
							  num_entity                                               ,
							  num_port                                                 ,
							  (uint32_t)DCACHE [num_entity][num_port].REQ_ADDR .read() ,
							  (uint32_t)DCACHE [num_entity][num_port].REQ_TRDID.read() ,
							  cache_type.type                                          ,
							  cache_type.direction                                     );
	      
	      // If is a respons -> compute the latence and push in the write_buffer
	      if ( must_ack == true)
		{
		  if (bus_error == true)
		    cout << "Dcache : have a bus error" << endl;
		  component_buffer_drsp [num_entity]->push(latence,
							   Entry((uint32_t)DCACHE [num_entity][num_port].REQ_TRDID.read() ,
								 (uint32_t)DCACHE [num_entity][num_port].REQ_PKTID.read() ,
								 1                                                        ,
								 SIZE_DDATA/8                                             ,
								 &read_dram                                               ,
								 (bus_error==true)?ERR_BUS:ERR_NO                         )
							   );			
		}
	    }// dnb_port
	}//num_entity
      
      // Transition for each component
      component_cache -> transition();
      // Transition buffer of respons
      for (uint32_t it = 0; it < nb_entity; it++)
	{
	  component_buffer_irsp [it] ->transition();
	  component_buffer_drsp [it] ->transition();
	}
      component_sim2os->transition();

    }//end transition

    //---------------------------------------------------------------------------------------------
    //-----[ genMoore ]----------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------
  public : void genMoore()
    {
      //Scan all entity and for each entity scan all port
      for (uint32_t num_entity = 0; num_entity < nb_entity; num_entity++)
	{
	  //##### REQUEST #####
	  uint32_t nb_slot_free = component_buffer_irsp [num_entity]->nb_slot_free ();
	  for (uint32_t num_port = 0; num_port < nb_iport [num_entity]; num_port ++)
	    {
	      ireq_ack [num_entity][num_port] = (num_port < nb_slot_free);
	    
	      ICACHE [num_entity][num_port].REQ_ACK.write (ireq_ack [num_entity][num_port]);
	    }

	  nb_slot_free = component_buffer_drsp [num_entity]->nb_slot_free ();
	  for (uint32_t num_port = 0; num_port < nb_dport [num_entity]; num_port ++)
	    {
	      dreq_ack [num_entity][num_port] = (num_port < nb_slot_free );
	    
	      DCACHE [num_entity][num_port].REQ_ACK.write (dreq_ack [num_entity][num_port]);
	    }

	  //##### RESPONS #####
      	  // ~~~~~> ICACHE <~~~~~
      	  for (uint32_t num_port = 0; num_port < nb_iport [num_entity]; num_port ++)
      	    {
	      // Test the number of element in the respons's buffer
      	      if (num_port >= component_buffer_irsp [num_entity]->nb_slot_use())
      		{
      		  irsp_val[num_entity][num_port] = 0; // No respons
      		}
      	      else
      		{
      		  slot_t<Entry> slot = component_buffer_irsp [num_entity]->read(num_port);
		  
      		  irsp_val[num_entity][num_port] = (slot.delay == 0); // respons if have a result
      		  ICACHE  [num_entity][num_port].RSP_TRDID.write((sc_uint<SIZE_TRDID> )slot.data.trdid);
      		  ICACHE  [num_entity][num_port].RSP_PKTID.write((sc_uint<SIZE_IPKTID>)slot.data.pktid);
      		  ICACHE  [num_entity][num_port].RSP_ERR  .write((sc_uint<2>          )slot.data.error);
		  
      		  for (uint32_t num_word = 0; num_word < NB_IWORD; num_word ++)
		    {
		      sc_uint<SIZE_IDATA> data;

		      for (unsigned int it_nb_bytes = 0; it_nb_bytes < SIZE_IDATA / 8; it_nb_bytes ++)
			{
#if defined(systemcass)
			  RANGE_SET_VAL(SIZE_IDATA         ,
					data               ,
					8*(it_nb_bytes+1)-1,
					8*it_nb_bytes      ,
					slot.data.data [num_word][it_nb_bytes]);
#else //#elif defined(systemc)
			  data.range(8*(it_nb_bytes+1)-1,8*it_nb_bytes) = slot.data.data [num_word][it_nb_bytes];
#endif
			}
		      ICACHE  [num_entity][num_port].RSP_INS [num_word].write(data);
		    }
      		}

	      ICACHE  [num_entity][num_port].RSP_VAL.write(irsp_val[num_entity][num_port]);
      	    }//end nb_iport

	  // ~~~~~> DCACHE <~~~~~
	  for (uint32_t num_port = 0; num_port < nb_dport [num_entity]; num_port ++)
	    {
	      if (num_port >= component_buffer_drsp [num_entity]->nb_slot_use())
		{
		  drsp_val[num_entity][num_port] = 0; // No respons
		}
	      else
		{
		  slot_t<Entry> slot = component_buffer_drsp [num_entity]->read(num_port);
		  
		  drsp_val[num_entity][num_port] = (slot.delay == 0);
		  DCACHE  [num_entity][num_port].RSP_TRDID   = (sc_uint<SIZE_TRDID> )slot.data.trdid;
		  DCACHE  [num_entity][num_port].RSP_PKTID   = (sc_uint<SIZE_DPKTID>)slot.data.pktid;
		  DCACHE  [num_entity][num_port].RSP_ERR     = (sc_uint<2>          )slot.data.error;

		  sc_uint<SIZE_DDATA> data;

		  for (unsigned int it_nb_bytes = 0; it_nb_bytes < SIZE_DDATA / 8; it_nb_bytes ++)
		    {
#if defined(systemcass)
		      RANGE_SET_VAL(SIZE_DDATA         ,
				    data               ,
				    8*(it_nb_bytes+1)-1,
				    8*it_nb_bytes      ,
				    slot.data.data [0][it_nb_bytes]);
#else //#elif defined(systemc)
		      data.range(8*(it_nb_bytes+1)-1,8*it_nb_bytes) = slot.data.data [0][it_nb_bytes];
#endif
		    }
		  DCACHE  [num_entity][num_port].RSP_RDATA.write(data);
		}
	      DCACHE  [num_entity][num_port].RSP_VAL     = drsp_val[num_entity][num_port];
	    }//nb_dport
      	}//nb_entity
    }//end genMoore

    //---------------------------------------------------------------------------------------------
    //-----[ operator<< ]--------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------

    friend ostream& operator<< (ostream& output_stream, const HIERARCHY_MEMORY &x)
    {
      output_stream << "<" << x.NAME << ">" << endl
		    << "  * nb_entity         : " << x.nb_entity         << endl;

      output_stream << "  -----[ component_data ]-----------------------------------" << endl;
      output_stream << *x.component_data << endl;
	    
      output_stream << "  -----[ component_cache ]----------------------------------" << endl;
      output_stream << *x.component_cache;

      output_stream << "  -----[ component_buffer_irsp ]----------------------------" << endl;
      for (uint32_t num_entity = 0; num_entity < x.nb_entity; num_entity++)
	{
	  output_stream << "  * nb_iport [" << num_entity << "]      : " << x.nb_iport [num_entity] << endl;
	  output_stream << *x.component_buffer_irsp [num_entity];
	}
      
      output_stream << "  -----[ component_buffer_drsp ]----------------------------" << endl;
      for (uint32_t num_entity = 0; num_entity < x.nb_entity; num_entity++)
	{
	  output_stream << "  * nb_dport [" << num_entity << "]      : " << x.nb_dport [num_entity] << endl;
	  output_stream << *x.component_buffer_drsp [num_entity];
	}

      //       output_stream << "  -----[ component_ramlock ]--------------------------------" << endl;
      //       output_stream << "  * nb_entity_ramlock : " << x.nb_entity_ramlock << endl;
      //       for (uint32_t it = 0; it < x.nb_entity_ramlock; it++)
      // 	output_stream << *x.component_ramlock [it];      
      
      //       output_stream << "  -----[ component_tty ]------------------------------------" << endl;
      //       output_stream << "  * nb_entity_tty     : " << x.nb_entity_tty     << endl;
      //       for (uint32_t it = 0; it < x.nb_entity_tty    ; it++)
      // 	output_stream << *x.component_tty     [it];

      //       output_stream << *x.component_sim2os;
      
      return output_stream;
    }

  };//class
};

#endif //!HIERARCHY_MEMORY_H
