/////////////////////////////////////////////////////////////////////////
// File: tsarv4_vgmn_generic_32_top.cpp
// Author: Alain Greiner
// Copyright: UPMC/LIP6
// Date : november 5 2010
// This program is released under the GNU public license
/////////////////////////////////////////////////////////////////////////
// This file define a generic TSAR architecture without virtual memory.
// - It uses the vci_vgmn as global interconnect 
// - It uses the vci_local_crossbar  as local interconnect 
// - It uses the vci_cc_xcache (No MMU) 
// The physical address space is 32 bits.
// The number of clusters cannot be larger than 256.
// The three parameters are 
// - xmax : number of clusters in a row
// - ymax : number of clusters in a column
// - nprocs : number of processor per cluster
//
// Each cluster contains nprocs processors, one Memory Cache,
// and one XICU component.
// The peripherals BDEV, CDMA, FBUF, MTTY and the boot BROM
// are in the cluster containing address 0xBFC00000.
// - The bdev_irq is connected to IRQ_IN[0]
// - The cdma_irq is connected to IRQ_IN[1]
// - The tty_irq[i] is connected to IRQ_IN[i+2]
// For all clusters, the XICU component contains nprocs timers.
// 
// As we target up to 256 clusters, each cluster can contain 
// at most 16 Mbytes (in a 4Gbytes address space).
// - Each memory cache contains 9 Mbytes.
// - The Frame buffer contains 2 Mbytes.
// - The Boot ROM contains 1 Mbytes.
//
// General policy for 32 bits address decoding:
// To simplifly, all segments base addresses are aligned
// on 1 Mbyte addresses. Therefore the 12 address MSB bits
// define the target in the direct address space.
// In these 12 bits, the (x_width + y_width) MSB bits define
// the cluster index, and the 4 LSB bits define the local index:
// 
//      | X_ID  | Y_ID  |---| L_ID |     OFFSET          |
//	|x_width|y_width|---|  4   |       20            |
/////////////////////////////////////////////////////////////////////////

#include <systemc>
#include <sys/time.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstdarg>
#include <stdint.h>

#include "mapping_table.h"
#include "mips32.h"
#include "vci_simple_ram.h"
#include "vci_multi_tty.h"
#include "vci_mem_cache_v4.h"
#include "vci_cc_xcache_wrapper_v4.h"
#include "vci_xicu.h"
#include "vci_vgmn.h"
#include "vci_local_crossbar.h"
#include "vci_framebuffer.h"
#include "vci_dma_tsar_v2.h"
#include "vci_block_device_tsar_v2.h"
#include "gdbserver.h"

#define	SECTOR_SIZE	2048

#define FBUF_XSIZE	960
#define FBUF_YSIZE	960

#define N_TTYS		4

//////////////////////////////////////////////
// segments definition in direct space.
// There is 16 Mbytes address space per cluster.
// The 8 MSB bits define the cluster index (x,y),
// even if the number of clusters is less than 256.
// Each memory cache contains up to 9 Mbytes.
// There is one MEMC segment and one XICU segment per cluster
// The peripherals BDEV, FBUF, MTTY, CDMA and the boot BROM
// are mapped in cluster containing address 0xBFC00000

#define	MEMC_BASE	0x00000000	
#define MEMC_SIZE	0x00900000

#define	XICU_BASE	0x00900000	
#define XICU_SIZE	0x00001000

#define	FBUF_BASE	0xBFA00000	
#define FBUF_SIZE	0x00200000

#define	BROM_BASE	0xBFC00000	
#define BROM_SIZE	0x00100000

#define	BDEV_BASE	0xBFD00000	
#define BDEV_SIZE	0x00000100

#define	MTTY_BASE	0xBFE00000	
#define MTTY_SIZE	0x00000100

#define	CDMA_BASE	0xBFF00000	
#define CDMA_SIZE	0x00000100

/* Pour ALMOS

#define BOOT_INFO_BLOCK 0xbfc08000
#define KERNEL_BIN_IMG  0xbfc10000

*/

////////////////////////////////////////////////////////////////////
//     TGTID & SRCID definition in direct space
// For all components:  global TGTID = global SRCID = cluster_index
// For processors, the local SRCID is between 0 & nprocs-1

#define MEMC_TGTID	0
#define XICU_TGTID	1
#define FBUF_TGTID	2
#define MTTY_TGTID	3
#define BROM_TGTID	4
#define BDEV_TGTID	5
#define CDMA_TGTID	6

#define PROC_SRCID	0
#define BDEV_SRCID	nprocs
#define CDMA_SRCID	(nprocs+1)

////////////////////////////////////////////////////////
//     TGTID & SRCID definition in coherence space
// For all components:  global TGTID = global SRCID = cluster_index
// For MEMC       : local SRCID = local TGTID = nprocs
// For processors : local SRCID = local TGTID = PROC_ID

//////////////////////
// Router ports index

#define NORTH		0
#define SOUTH		1
#define EAST		2
#define WEST		3
#define LOCAL		4

///////////////
// VCI format
#define cell_width	4
#define address_width	32
#define plen_width	8
#define error_width	1
#define clen_width	1
#define rflag_width	1
#define srcid_width	14
#define pktid_width	4
#define trdid_width	4
#define wrplen_width	1

//  cluster index (computed from x,y coordinates)
#define cluster(x,y)	(y + ymax*x)

/////////////////////////////////
int _main(int argc, char *argv[])
{
    using namespace sc_core;
    using namespace soclib::caba;
    using namespace soclib::common;

    char    soft_name[128]  = "undefined_binary_code";	// pathname to binary code
    char    disk_name[128]  = "undefined_disk_image";	// pathname to the disk image
    size_t  ncycles         = 1000000000;       	// simulated cycles
    size_t  xmax            = 2;			// number of clusters in a row
    size_t  ymax            = 2;                	// number of clusters in a column
    size_t  nprocs          = 1;			// number of processors per cluster
    bool    debug_ok        = false;            	// debug activated
    size_t  from_cycle      = 0;                	// debug start cycle
    size_t  to_cycle        = 1000000000;       	// debug end cycle

    ////////////// command line arguments //////////////////////
    if (argc > 1)
    {
        for( int n=1 ; n<argc ; n=n+2 )
        {
            if( (strcmp(argv[n],"-NCYCLES") == 0) && (n+1<argc) )
            {
                ncycles = atoi(argv[n+1]);
            }
            else if( (strcmp(argv[n],"-NPROCS") == 0) && (n+1<argc) )
            {
                nprocs = atoi(argv[n+1]);
                assert( (nprocs <= 8) && "The number of processors per cluster cannot be larger than 8");
            }
            else if( (strcmp(argv[n],"-XMAX") == 0) && (n+1<argc) )
            {
                xmax = atoi(argv[n+1]);
                assert( ((xmax >= 2) && (xmax <= 16)) 
                    && "The XMAX parameter (number of clusters in a row) must be in the range [2,16]" );
            }
            else if( (strcmp(argv[n],"-YMAX") == 0) && (n+1<argc) )
            {
                ymax = atoi(argv[n+1]);
                assert( ((ymax >= 2) && (ymax <= 16)) 
                    && "The YMAX parameter (number of clusters in a column) must be in the range [2,16]" );
            }
            else if( (strcmp(argv[n],"-SOFT") == 0) && (n+1<argc) )
            {
                strcpy(soft_name, argv[n+1]);
            }
            else if( (strcmp(argv[n],"-DISK") == 0) && (n+1<argc) )
            {
                strcpy(disk_name, argv[n+1]);
            }
            else if( (strcmp(argv[n],"-DEBUG") == 0) && (n+1<argc) )
            {
                debug_ok = true;
                from_cycle = atoi(argv[n+1]);
            }
            else if( (strcmp(argv[n],"-TOCYCLE") == 0) && (n+1<argc) )
            {
                to_cycle = atoi(argv[n+1]);
            }
            else
            {
                std::cout << "   Arguments on the command line are (key,value) couples." << std::endl;
                std::cout << "   The order is not important." << std::endl;
                std::cout << "   Accepted arguments are :" << std::endl << std::endl;
                std::cout << "     -SOFT elf_file_name" << std::endl;
                std::cout << "     -DISK disk_image_file_name" << std::endl;
                std::cout << "     -NCYCLES number_of_simulated_cycles" << std::endl;
                std::cout << "     -NPROCS number_of_processors_per_cluster" << std::endl;
                std::cout << "     -XMAX number_of_clusters_in_a_row" << std::endl;
                std::cout << "     -YMAX number_of_clusters_in_a_column" << std::endl;
                std::cout << "     -DEBUG debug_start_cycle" << std::endl;
                std::cout << "     -TOCYCLE debug_end_cycle" << std::endl;
                exit(0);
            }
        }
    }

    std::cout << std::endl << "***********  TSAR ARCHITECTURE  **************" << std::endl
              << " - Interconnect = VGMN & CROSSBAR" << std::endl
              << " - Number of clusters = " << xmax << " * " << ymax << std::endl
              << " - Number of processors per cluster = " << nprocs << std::endl
              << "**********************************************" << std::endl
              << std::endl;


    // Define VCI parameters
    typedef soclib::caba::VciParams<cell_width,
                                    plen_width,
                                    address_width,
                                    error_width,                                   
                                    clen_width,
                                    rflag_width,
                                    srcid_width,
                                    pktid_width,
                                    trdid_width,
                                    wrplen_width> vci_param;

    size_t	cluster_io_index;
    size_t	x_width;
    size_t	y_width;

    if      (xmax == 2) x_width = 1;
    else if (xmax <= 4) x_width = 2;
    else if (xmax <= 8) x_width = 3;
    else                x_width = 4;

    if      (ymax == 2) y_width = 1;
    else if (ymax <= 4) y_width = 2;
    else if (ymax <= 8) y_width = 3;
    else                y_width = 4;

    cluster_io_index = 0xBF >> (8 - x_width - y_width);
    
    /////////////////////
    //  Mapping Tables
    /////////////////////

    // direct network
    MappingTable maptabd(address_width, 
                         IntTab(x_width + y_width, 12 - x_width - y_width), 
                         IntTab(x_width + y_width, srcid_width - x_width - y_width), 
                         0x00F00000);
    for ( size_t x = 0 ; x < xmax ; x++)
    {
        for ( size_t y = 0 ; y < ymax ; y++)
        {
            sc_uint<address_width> offset  = cluster(x,y) << (address_width-x_width-y_width);
            std::ostringstream 	sm;
            sm << "d_seg_memc_" << x << "_" << y;
            maptabd.add(Segment(sm.str(), MEMC_BASE+offset, MEMC_SIZE, IntTab(cluster(x,y),MEMC_TGTID), true));
            std::ostringstream 	si;
            si << "d_seg_xicu_" << x << "_" << y;
            maptabd.add(Segment(si.str(), XICU_BASE+offset, XICU_SIZE, IntTab(cluster(x,y),XICU_TGTID), false));
            if ( cluster(x,y) == cluster_io_index )
            {
	      maptabd.add(Segment("d_seg_fbuf", FBUF_BASE, FBUF_SIZE, IntTab(cluster(x,y),FBUF_TGTID), false));
	      maptabd.add(Segment("d_seg_bdev", BDEV_BASE, BDEV_SIZE, IntTab(cluster(x,y),BDEV_TGTID), false));
	      maptabd.add(Segment("d_seg_mtty", MTTY_BASE, MTTY_SIZE, IntTab(cluster(x,y),MTTY_TGTID), false));
	      maptabd.add(Segment("d_seg_brom", BROM_BASE, BROM_SIZE, IntTab(cluster(x,y),BROM_TGTID), true));
	      maptabd.add(Segment("d_seg_cdma", CDMA_BASE, CDMA_SIZE, IntTab(cluster(x,y),CDMA_TGTID), false));
            }
        }
    }
    std::cout << maptabd << std::endl;

    // coherence network
    MappingTable maptabc(address_width, 
                         IntTab(x_width + y_width, 12 - x_width - y_width), 
                         IntTab(x_width + y_width, srcid_width - x_width - y_width), 
                         0xF0000000);

    for ( size_t x = 0 ; x < xmax ; x++)
    {
        for ( size_t y = 0 ; y < ymax ; y++)
        {
            sc_uint<address_width> offset  = cluster(x,y) << (address_width-x_width-y_width);
            
            std::ostringstream sm;
            sm << "c_seg_memc_" << x << "_" << y;
            maptabc.add(Segment(sm.str(), MEMC_BASE+offset, MEMC_SIZE, IntTab(cluster(x,y), nprocs), false));
            // the segment base and size will be modified 
            // when the segmentation of the coherence space will be simplified

            if ( cluster(x,y) == cluster_io_index )
            {
                std::ostringstream sr;
                sr << "c_seg_brom_" << x << "_" << y;
                maptabc.add(Segment(sr.str(), BROM_BASE, BROM_SIZE, IntTab(cluster(x,y), nprocs), false));
            }

	    sc_uint<address_width> avoid_collision  = 0;
	    for ( size_t p = 0 ; p < nprocs ; p++)
            {
	        sc_uint<address_width> base = MEMC_SIZE + (p*0x100000) + offset;
                // the following test is to avoid a collision between the c_seg_brom segment
                // and a c_seg_proc segment (all segments base addresses being multiple of 1Mbytes)
                if ( base == BROM_BASE ) avoid_collision = 0x100000;
	        std::ostringstream sp;
	        sp << "c_seg_proc_" << x << "_" << y << "_" << p;
	        maptabc.add(Segment(sp.str(), base + avoid_collision, 0x20, IntTab(cluster(x,y), p), false, 
				  true, IntTab(cluster(x,y), p))); 
                // the two last arguments will be removed 
                // when the segmentation of the coherence space will be simplified
            }
        }
    }
    std::cout << maptabc << std::endl;

    // external network
    MappingTable maptabx(address_width, IntTab(1), IntTab(10), 0xF0000000);

    for ( size_t x = 0 ; x < xmax ; x++)
    {
        for ( size_t y = 0 ; y < ymax ; y++)
        { 
	  sc_uint<address_width> offset  = cluster(x,y) << (address_width-x_width-y_width);
	  std::ostringstream sx;
	  
	  sx << "seg_xram_" << x << "_" << y;
	  maptabx.add(Segment(sx.str(), MEMC_BASE + offset, MEMC_SIZE, IntTab(0), false));
        }
    }
    std::cout << maptabx << std::endl;

    ////////////////////
    // Signals
    ///////////////////

    sc_clock		signal_clk("clk");
    sc_signal<bool> 	signal_resetn("resetn");
    sc_signal<bool>	signal_false;
   
    // IRQ signals (one signal per proc)
    sc_signal<bool>****	signal_proc_it =
      alloc_elems<sc_signal<bool> >("signal_proc_it", 1,xmax, ymax, nprocs);

    sc_signal<bool>*	signal_irq_mtty = 
        alloc_elems<sc_signal<bool> >("signal_irq_mtty", N_TTYS);

    sc_signal<bool>	signal_irq_bdev;
    sc_signal<bool>	signal_irq_cdma;

    // Direct VCI signals

    VciSignals<vci_param>*** signal_vci_ini_d_proc = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_ini_d_proc", xmax, ymax, nprocs);

    VciSignals<vci_param>** signal_vci_tgt_d_memc = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_tgt_d_memc", xmax, ymax);

    VciSignals<vci_param>** signal_vci_tgt_d_xicu = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_tgt_d_xicu", xmax, ymax);

    VciSignals<vci_param>** signal_vci_l2g_d = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_l2g_d", xmax, ymax);

    VciSignals<vci_param>** signal_vci_g2l_d = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_g2l_d", xmax, ymax);

    VciSignals<vci_param> signal_vci_tgt_d_mtty("signal_vci_tgt_d_mtty");
    VciSignals<vci_param> signal_vci_tgt_d_brom("signal_vci_tgt_d_brom");
    VciSignals<vci_param> signal_vci_tgt_d_bdev("signal_vci_tgt_d_bdev");
    VciSignals<vci_param> signal_vci_tgt_d_fbuf("signal_vci_tgt_d_fbuf");
    VciSignals<vci_param> signal_vci_tgt_d_cdma("signal_vci_tgt_d_cdma");

    VciSignals<vci_param> signal_vci_ini_d_bdev("signal_vci_ini_d_bdev");
    VciSignals<vci_param> signal_vci_ini_d_cdma("signal_vci_ini_d_cdma");

    // Coherence VCI signals

    VciSignals<vci_param>*** signal_vci_ini_c_proc = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_ini_c_proc", xmax, ymax, nprocs);

    VciSignals<vci_param>*** signal_vci_tgt_c_proc = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_tgt_c_proc", xmax, ymax, nprocs);

    VciSignals<vci_param>** signal_vci_ini_c_memc = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_ini_c_memc", xmax, ymax);

    VciSignals<vci_param>** signal_vci_tgt_c_memc = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_tgt_c_memc", xmax, ymax);

    VciSignals<vci_param>** signal_vci_l2g_c = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_l2g_c", xmax, ymax);

    VciSignals<vci_param>** signal_vci_g2l_c = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_g2l_c", xmax, ymax);

    // Xternal network VCI signals

    VciSignals<vci_param> signal_vci_tgt_x_xram("signal_vci_tgt_x_xram");
    VciSignals<vci_param>** signal_vci_ini_x_memc = 
        alloc_elems<VciSignals<vci_param> >("signal_vci_ini_x_memc", xmax, ymax);

    ////////////////////////////
    //      Components
    ////////////////////////////

    typedef soclib::common::GdbServer<soclib::common::Mips32ElIss> proc_iss;

    soclib::common::Loader loader(soft_name);
    proc_iss::set_loader(loader);

/* code specifique ALMOS pour chargement du binaire 

#define _TO_STR(_str) #_str
#define  TO_STR(_str) _TO_STR(_str)

    soclib::common::Loader loader("bootloader.bin",
				  "arch-info.bin@"TO_STR(BOOT_INFO_BLOCK)":D",
				  "kernel-soclib.bin@"TO_STR(KERNEL_BIN_IMG)":D");
*/

    // External RAM
    VciSimpleRam<vci_param> xram(
        "xram", 
        IntTab(0), 
        maptabx, 
        loader);

    // Direct network
    VciVgmn<vci_param> dnoc(
        "dnoc",
        maptabd, 
        xmax*ymax,
        xmax*ymax,
        2, 2);

    // Coherence network
    VciVgmn<vci_param> cnoc(
        "cnoc",
        maptabc, 
        xmax*ymax,
        xmax*ymax,
        2, 2);

    // External network
    VciVgmn<vci_param> xnoc(
        "xnoc",
        maptabx, 
        xmax*ymax,
        1, 
        2, 2);

    // Peripherals : TTY, Frame Buffer, Block Device, Boot ROM, & DMA 
    VciSimpleRam<vci_param> brom(
        "brom", 
        IntTab(cluster_io_index, BROM_TGTID), 
        maptabd, 
        loader);

    VciMultiTty<vci_param> mtty(
        "mtty",
        IntTab(cluster_io_index, MTTY_TGTID),
        maptabd,
        "tty0","tty1","tty2","tty3",NULL);

    VciFrameBuffer<vci_param> fbuf(
        "fbuf", 
        IntTab(cluster_io_index, FBUF_TGTID),
        maptabd, 
        FBUF_XSIZE,
        FBUF_YSIZE);

    VciBlockDeviceTsarV2<vci_param> bdev(
        "bdev", 
        maptabd, 
        IntTab(cluster_io_index, BDEV_SRCID),	// SRCID_D
        IntTab(cluster_io_index, BDEV_TGTID),	// TGTID_D
        disk_name, 
        SECTOR_SIZE); 

    VciDmaTsarV2<vci_param> cdma(
	"cdma",
	maptabd,
	IntTab(cluster_io_index,CDMA_SRCID),	// SRCID_D
	IntTab(cluster_io_index,CDMA_TGTID),	// TGTID_D
	64); 

    // processors (nprocs per cluster)

    VciCcXCacheWrapperV4<vci_param, proc_iss> *proc[xmax][ymax][nprocs];

    for( size_t x = 0 ; x < xmax ; x++ )
    {
      for( size_t y = 0 ; y < ymax ; y++ )
      {
	for ( size_t p = 0 ; p < nprocs ; p++ ) 
	{
	  
	  std::ostringstream sp;
	  sp << "proc_" << x << "_" << y << "_" << p;
			       
	  proc[x][y][p] = new VciCcXCacheWrapperV4<vci_param, proc_iss>(
                    sp.str().c_str(),
                    p+nprocs*cluster(x,y), 
                    maptabd, maptabc,
                    IntTab(cluster(x,y), PROC_SRCID+p),		// SRCID_D
                    IntTab(cluster(x,y), PROC_SRCID+p),		// SRCID_C
                    IntTab(cluster(x,y), PROC_SRCID+p),		// TGTID_C
                    1,1,
                    4,64,16,4,64,16 				// Icache and Dcache sizes
                    ,4,8,16
                    );
	}
      }
    }

    //  memory caches (one per cluster)
    VciMemCacheV4<vci_param>* memc[xmax][ymax];

    for( size_t x = 0 ; x < xmax ; x++ )
    {
        for( size_t y = 0 ; y < ymax ; y++ )
	{
	  std::ostringstream sm;
	  sm << "memc_" << x << "_" << y;

	  memc[x][y] = new VciMemCacheV4<vci_param>(
                sm.str().c_str(),
                maptabd, maptabc, maptabx,
                IntTab(cluster(x,y)),				// SRCID_X
                IntTab(cluster(x,y), nprocs),			// SRCID_C
                IntTab(cluster(x,y), MEMC_TGTID),		// TGTID_D
                IntTab(cluster(x,y), nprocs),         		// TGTID_C
                16,256,16,					// CACHE SIZE
                4096);						// HEAP SIZE
        }
    }

    // XICU (one per cluster)
    VciXicu<vci_param> *xicu[xmax][ymax];
    
    for( size_t x = 0 ; x < xmax ; x++ )
    {
        for( size_t y = 0 ; y < ymax ; y++ )
        {
            std::ostringstream si;
            si << "xicu_" << x << "_" << y;
            size_t	nhwi;
            if ( cluster(x,y) == cluster_io_index )	nhwi = N_TTYS + 2;
            else					nhwi = 0;
	    xicu[x][y] = new VciXicu<vci_param>(
                si.str().c_str(),
                maptabd,
                IntTab(cluster(x,y), XICU_TGTID),	// TGTID_D
               	nprocs,					// number of TIMERS
		nhwi,					// number of hard IRQs
		nprocs,					// number of soft IRQs
		nprocs);				// number of output IRQ lines
        }
    }
                                                
    // Local interconnects : one direct crossbar & one coherence crossbar per cluster
    VciLocalCrossbar<vci_param>* dxbar[xmax][ymax];
    
    VciLocalCrossbar<vci_param>* cxbar[xmax][ymax];

    for( size_t x = 0 ; x < xmax ; x++ )
    {
        for( size_t y = 0 ; y < ymax ; y++ )
        {
            std::ostringstream sd;
            sd << "dxbar_" << x << "_" << y;
            size_t nb_direct_initiators 	= nprocs;
            size_t nb_direct_targets	        = 2;
            if ( cluster(x,y) == cluster_io_index ) 
            {
                nb_direct_initiators		= nprocs + 2;
                nb_direct_targets		= 7;
            }
	    dxbar[x][y] = new VciLocalCrossbar<vci_param>( 
                sd.str().c_str(),
                maptabd,
                IntTab(cluster(x,y)),		// global initiator index
                IntTab(cluster(x,y)),		// global target index
                nb_direct_initiators,		// number of initiators
                nb_direct_targets);		// number of targets

            std::ostringstream sc;
            sc << "cxbar_" << x << "_" << y;
	    cxbar[x][y] = new VciLocalCrossbar<vci_param>( 
                sc.str().c_str(),
                maptabc,
                IntTab(cluster(x,y)),		// global initiator index
                IntTab(cluster(x,y)),		// global target index
                nprocs+1,			// number of initiators
		nprocs+1);			// number of targets
        }
    }

    std::cout << "all components created" << std::endl;

    ///////////////////////////////////////////////////////////////
    //     Net-list 
    ///////////////////////////////////////////////////////////////

    // External Ram (one instance)
    xram.p_clk						(signal_clk);
    xram.p_resetn					(signal_resetn);
    xram.p_vci						(signal_vci_tgt_x_xram);	

    // External Network (one instance)
    xnoc.p_clk						(signal_clk);
    xnoc.p_resetn					(signal_resetn);
    xnoc.p_to_target[0]					(signal_vci_tgt_x_xram);
    for ( size_t x = 0 ; x < xmax ; x++ )
    {
        for ( size_t y = 0 ; y < ymax ; y++ )
        {
            xnoc.p_to_initiator[cluster(x,y)]		(signal_vci_ini_x_memc[x][y]);
        }
    }

    // Direct Network (one instance)
    dnoc.p_clk						(signal_clk);
    dnoc.p_resetn					(signal_resetn);
    for ( size_t x = 0 ; x < xmax ; x++ )
    {
        for ( size_t y = 0 ; y < ymax ; y++ )
        {
            dnoc.p_to_initiator[cluster(x,y)]		(signal_vci_l2g_d[x][y]);
            dnoc.p_to_target[cluster(x,y)]		(signal_vci_g2l_d[x][y]);
        }
    }

    // Coherence Network (one instance)
    cnoc.p_clk						(signal_clk);
    cnoc.p_resetn					(signal_resetn);
    for ( size_t x = 0 ; x < xmax ; x++ )
    {
        for ( size_t y = 0 ; y < ymax ; y++ )
        {
            cnoc.p_to_initiator[cluster(x,y)]		(signal_vci_l2g_c[x][y]);
            cnoc.p_to_target[cluster(x,y)]		(signal_vci_g2l_c[x][y]);
        }
    }

    // Distributed components (in clusters)

    for ( size_t x = 0 ; x < xmax ; x++ )
    {
        for ( size_t y = 0 ; y < ymax ; y++ )
	{
            // direct local crossbar
            dxbar[x][y]->p_clk				(signal_clk);
            dxbar[x][y]->p_resetn			(signal_resetn);
            dxbar[x][y]->p_initiator_to_up		(signal_vci_l2g_d[x][y]);
	    dxbar[x][y]->p_target_to_up			(signal_vci_g2l_d[x][y]);
            dxbar[x][y]->p_to_target[MEMC_TGTID]	(signal_vci_tgt_d_memc[x][y]);
            dxbar[x][y]->p_to_target[XICU_TGTID]	(signal_vci_tgt_d_xicu[x][y]);
	    for ( size_t p = 0 ; p < nprocs ; p++ )
            {
	        dxbar[x][y]->p_to_initiator[p]		(signal_vci_ini_d_proc[x][y][p]);
	    }

            // coherence local crossbar
            cxbar[x][y]->p_clk				(signal_clk);
            cxbar[x][y]->p_resetn			(signal_resetn);
            cxbar[x][y]->p_initiator_to_up		(signal_vci_l2g_c[x][y]);
            cxbar[x][y]->p_target_to_up			(signal_vci_g2l_c[x][y]);
            cxbar[x][y]->p_to_initiator[nprocs]	(signal_vci_ini_c_memc[x][y]);
            cxbar[x][y]->p_to_target[nprocs]		(signal_vci_tgt_c_memc[x][y]);
            for ( size_t p = 0 ; p < nprocs ; p++ )
            {
                cxbar[x][y]->p_to_initiator[p]		(signal_vci_ini_c_proc[x][y][p]);
                cxbar[x][y]->p_to_target[p]		(signal_vci_tgt_c_proc[x][y][p]);
            }
	
	    // Processors
	    for ( size_t p = 0 ; p < nprocs ; p++ )
            {
                proc[x][y][p]->p_clk			(signal_clk);  
                proc[x][y][p]->p_resetn			(signal_resetn);  
                proc[x][y][p]->p_vci_ini_rw		(signal_vci_ini_d_proc[x][y][p]);
                proc[x][y][p]->p_vci_ini_c		(signal_vci_ini_c_proc[x][y][p]);
                proc[x][y][p]->p_vci_tgt		(signal_vci_tgt_c_proc[x][y][p]);
                proc[x][y][p]->p_irq[0][0]	        (signal_proc_it[0][x][y][p]);
                for ( size_t j = 1 ; j < 6 ; j++ )
                {
                    proc[x][y][p]->p_irq[0][j]		(signal_false); 
                }
	    }

            // XICU
	    xicu[x][y]->p_clk				(signal_clk);
            xicu[x][y]->p_resetn			(signal_resetn);
	    xicu[x][y]->p_vci				(signal_vci_tgt_d_xicu[x][y]);
	    for ( size_t p = 0 ; p < nprocs ; p++ )
            {
                xicu[x][y]->p_irq[p]			(signal_proc_it[0][x][y][p]);
            }

            // MEMC
            memc[x][y]->p_clk				(signal_clk);
            memc[x][y]->p_resetn			(signal_resetn);
            memc[x][y]->p_vci_tgt			(signal_vci_tgt_d_memc[x][y]);	
            memc[x][y]->p_vci_ini			(signal_vci_ini_c_memc[x][y]);
            memc[x][y]->p_vci_tgt_cleanup		(signal_vci_tgt_c_memc[x][y]);
            memc[x][y]->p_vci_ixr			(signal_vci_ini_x_memc[x][y]);

            // I/O peripherals
            if ( cluster(x,y) == cluster_io_index )
            {
	        bdev.p_clk				(signal_clk);
	        bdev.p_resetn				(signal_resetn);
	        bdev.p_irq				(signal_irq_bdev); 
	        bdev.p_vci_target			(signal_vci_tgt_d_bdev);
	        bdev.p_vci_initiator			(signal_vci_ini_d_bdev);

	        cdma.p_clk				(signal_clk);
	        cdma.p_resetn				(signal_resetn);
	        cdma.p_irq				(signal_irq_cdma); 
	        cdma.p_vci_target			(signal_vci_tgt_d_cdma);
	        cdma.p_vci_initiator			(signal_vci_ini_d_cdma);

	        fbuf.p_clk				(signal_clk); 
	        fbuf.p_resetn				(signal_resetn); 
	        fbuf.p_vci				(signal_vci_tgt_d_fbuf); 

	        brom.p_clk				(signal_clk);
	        brom.p_resetn				(signal_resetn);
	        brom.p_vci				(signal_vci_tgt_d_brom);

	        mtty.p_clk				(signal_clk);
	        mtty.p_resetn				(signal_resetn);
	        mtty.p_vci				(signal_vci_tgt_d_mtty);
		for(size_t i=0 ; i<N_TTYS ; i++)
                {
                    mtty.p_irq[i]			(signal_irq_mtty[i]);
                }

                dxbar[x][y]->p_to_target[BROM_TGTID]	(signal_vci_tgt_d_brom);
                dxbar[x][y]->p_to_target[MTTY_TGTID]	(signal_vci_tgt_d_mtty);
	        dxbar[x][y]->p_to_target[BDEV_TGTID]	(signal_vci_tgt_d_bdev);
	        dxbar[x][y]->p_to_target[FBUF_TGTID]	(signal_vci_tgt_d_fbuf);
	        dxbar[x][y]->p_to_target[CDMA_TGTID]	(signal_vci_tgt_d_cdma);

	        dxbar[x][y]->p_to_initiator[BDEV_SRCID]	(signal_vci_ini_d_bdev);
	        dxbar[x][y]->p_to_initiator[CDMA_SRCID]	(signal_vci_ini_d_cdma);

                xicu[x][y]->p_hwi[0]			(signal_irq_bdev);
                xicu[x][y]->p_hwi[1]			(signal_irq_cdma);
		
		for(size_t i=0 ; i<N_TTYS ; i++)
                {
                    xicu[x][y]->p_hwi[2+i]		(signal_irq_mtty[i]);
                }
            }
        } // end for y
    } // end for x

    std::cout << "all components connected" << std::endl;

    ////////////////////////////////////////////////////////
    //   Simulation
    ///////////////////////////////////////////////////////
    
    sc_start(sc_core::sc_time(0, SC_NS));
    signal_resetn = false;

    sc_start(sc_core::sc_time(1, SC_NS));
    signal_resetn = true;

    for(size_t i=1 ; i<ncycles ; i++)
    {
        sc_start(sc_core::sc_time(1, SC_NS));

        if( debug_ok && (i > from_cycle) && (i < to_cycle) )
        {
        std::cout << std::dec << "*************** cycle " << i << "    ***********************" << std::endl; 
        proc[0][0][0]->print_trace();
        proc[0][1][0]->print_trace();
        proc[1][0][0]->print_trace();
        proc[1][1][0]->print_trace();
        std::cout << std::endl;
        dxbar[0][0]->print_trace();
        dxbar[0][1]->print_trace();
        dxbar[1][0]->print_trace();
        dxbar[1][1]->print_trace();
        std::cout << std::endl;
        cxbar[0][0]->print_trace();
        cxbar[0][1]->print_trace();
        cxbar[1][0]->print_trace();
        cxbar[1][1]->print_trace();
        std::cout << std::endl;
        memc[0][0]->print_trace();
        memc[0][1]->print_trace();
        memc[1][0]->print_trace();
        memc[1][1]->print_trace();
        std::cout << std::endl;
        }
    }

    std::cout << "Hit ENTER to end simulation" << std::endl;
    char buf[1];
    std::cin.getline(buf,1);

    for( size_t x = 0 ; x < xmax ; x++ )
      for( size_t y = 0 ; y < ymax ; y++ )
	for ( size_t p = 0 ; p < nprocs ; p++ ) 
          proc[x][y][p]->print_stats();

    return EXIT_SUCCESS;
}

int sc_main (int argc, char *argv[])
{
	try {
		return _main(argc, argv);
	} catch (std::exception &e) {
		std::cout << e.what() << std::endl;
	} catch (...) {
		std::cout << "Unknown exception occured" << std::endl;
		throw;
	}
	return 1;
}
