#include "../include/tsarv4_cluster_mmu.h"

namespace soclib {
namespace caba  {

//////////////////////////////////////////////////////////////////////////
//                 Constructor
//////////////////////////////////////////////////////////////////////////
template<typename vci_param, typename iss_t, int cmd_width, int rsp_width>
TsarV4ClusterMmu<vci_param, iss_t, cmd_width, rsp_width>::TsarV4ClusterMmu(
                        sc_module_name  insname,
                        size_t          nprocs,
                        size_t          x_id,
                        size_t          y_id,
                        size_t          cluster_id,
                        const   	soclib::common::MappingTable &mtd,
                        const   	soclib::common::MappingTable &mtc, 
                        const   	soclib::common::MappingTable &mtx, 
                        size_t          x_width,
                        size_t          y_width,
                        size_t		tgtid_memc,
                        size_t		tgtid_xicu,
                        size_t		tgtid_fbuf,
                        size_t		tgtid_mtty,
                        size_t		tgtid_brom,
                        size_t		tgtid_bdev,
                        size_t		tgtid_mdma,
                        size_t		memc_ways,
                        size_t		memc_sets,
                        size_t		l1_i_ways,
                        size_t		l1_i_sets,
                        size_t		l1_d_ways,
                        size_t		l1_d_sets,
                        size_t		xram_latency,
                        bool            io,
                        size_t		xfb,
                        size_t		yfb,
                        char*		disk_name,
                        size_t		block_size,
                        Loader		loader,
                        uint32_t	frozen_cycles,
                        uint32_t	debug_start_cycle,
                        bool            debug_ok)
      : soclib::caba::BaseModule(insname),
        p_clk("clk"),
        p_resetn("resetn"),

        signal_dspin_cmd_l2g_d("signal_dspin_cmd_l2g_d"),
        signal_dspin_cmd_g2l_d("signal_dspin_cmd_g2l_d"),
        signal_dspin_cmd_l2g_c("signal_dspin_cmd_l2g_c"),
        signal_dspin_cmd_g2l_c("signal_dspin_cmd_g2l_c"),
        signal_dspin_rsp_l2g_d("signal_dspin_rsp_l2g_d"),
        signal_dspin_rsp_g2l_d("signal_dspin_rsp_g2l_d"),
        signal_dspin_rsp_l2g_c("signal_dspin_rsp_l2g_c"),
        signal_dspin_rsp_g2l_c("signal_dspin_rsp_g2l_c"),

	signal_vci_ini_d_bdev("signal_vci_ini_d_bdev"),
	signal_vci_ini_d_mdma("signal_vci_ini_d_mdma"),

        signal_vci_tgt_d_memc("signal_vci_tgt_d_memc"),
        signal_vci_tgt_d_mtty("signal_vci_tgt_d_mtty"),
        signal_vci_tgt_d_xicu("signal_vci_tgt_d_xicu"),
        signal_vci_tgt_d_bdev("signal_vci_tgt_d_bdev"),
        signal_vci_tgt_d_mdma("signal_vci_tgt_d_mdma"),
        signal_vci_tgt_d_brom("signal_vci_tgt_d_brom"),
        signal_vci_tgt_d_fbuf("signal_vci_tgt_d_fbuf"),

        signal_vci_ini_c_memc("signal_vci_ini_c_memc"), 
        signal_vci_tgt_c_memc("signal_vci_tgt_c_memc"),

        signal_vci_xram("signal_vci_xram")

{
        // Vectors of ports definition

        p_cmd_in        = alloc_elems<DspinInput<cmd_width> >("p_cmd_in", 2, 4);
        p_cmd_out       = alloc_elems<DspinOutput<cmd_width> >("p_cmd_out", 2, 4);
        p_rsp_in        = alloc_elems<DspinInput<rsp_width> >("p_rsp_in", 2, 4);
        p_rsp_out       = alloc_elems<DspinOutput<rsp_width> >("p_rsp_out", 2, 4);

        // Components definition 

        // on direct network : local srcid[proc] in [0...nprocs-1]
        // on direct network : local srcid[mdma] = nprocs
        // on direct network : local srcid[bdev] = nprocs + 1

        // on coherence network : local srcid[proc] in [0...nprocs-1]
	// on coherence network : local srcid[memc] = nprocs

std::cout << "  - building proc_" << x_id << "_" << y_id << "-*" << std::endl;

        for ( size_t p=0 ; p<nprocs ; p++ )
        { 
            std::ostringstream sproc;
            sproc << "proc_" << x_id << "_" << y_id << "_" << p;
            proc[p] = new VciCcVCacheWrapperV4<vci_param, iss_t>(
                sproc.str().c_str(),
                cluster_id*nprocs + p,
                mtd,				// Mapping Table Direct
                mtc,				// Mapping Table Coherence
                IntTab(cluster_id,p),    	// SRCID_D
                IntTab(cluster_id,p),    	// SRCID_C
                IntTab(cluster_id,p),    	// TGTID_C
                8,				// ITLB ways
                8,				// ITLB sets
                8,				// DTLB ways
                8,				// DTLB sets
                l1_i_ways,l1_i_sets,16,  	// ICACHE size
                l1_d_ways,l1_d_sets,16,      	// DCACHE size
                4,				// WBUF width
                4, 				// WBUF depth
                frozen_cycles,			// max frozen cycles
                debug_start_cycle,
                debug_ok);
        }

std::cout << "  - building memc_" << x_id << "_" << y_id << std::endl;

        std::ostringstream smemc;
        smemc << "memc_" << x_id << "_" << y_id;
        memc = new VciMemCacheV4<vci_param>(
                   smemc.str().c_str(),
                   mtd, mtc, mtx,
                   IntTab(cluster_id),           	// SRCID_X
                   IntTab(cluster_id, nprocs),   	// SRCID_C
                   IntTab(cluster_id, tgtid_memc),	// TGTID_D
                   IntTab(cluster_id, nprocs),   	// TGTID_C
                   memc_ways, memc_sets, 16,	 	// CACHE SIZE
                   4096,     			 	// HEAP SIZE
                   8,					// TRANSACTION TABLE DEPTH
                   8,					// UPDATE TABLE DEPTH
                   debug_start_cycle,
                   debug_ok);

        
std::cout << "  - building xram_" << x_id << "_" << y_id << std::endl;

        std::ostringstream sxram;
        sxram << "xram_" << x_id << "_" << y_id;
        xram = new VciSimpleRam<vci_param>(
                   sxram.str().c_str(),
                   IntTab(cluster_id),
                   mtx,
                   loader,
                   xram_latency);

std::cout << "  - building xicu_" << x_id << "_" << y_id << std::endl;

        size_t  nhwi = 4;				// always 4 (or 9) ports, even if 
        if( io == true ) nhwi = 9;			// there if less than 4 processors
        std::ostringstream sicu;
        sicu << "xicu_" << x_id << "_" << y_id;
        xicu = new VciXicu<vci_param>(
                  sicu.str().c_str(),
                  mtd,				  	// mapping table
                  IntTab(cluster_id, tgtid_xicu),  	// TGTID_D
                  0,					// number of timer IRQs
                  nhwi,                          	// number of hard IRQs
                  0,					// number of soft IRQs
                  nprocs);				// number of output IRQs

std::cout << "  - building dma_" << x_id << "_" << y_id << std::endl;

        // dma multi-canaux
        std::ostringstream sdma;
        sdma << "dma_" << x_id << "_" << y_id;
        mdma = new VciMultiDma<vci_param>(
                   sdma.str().c_str(),
                   mtd,
                   IntTab(cluster_id, nprocs),		// SRCID
                   IntTab(cluster_id, tgtid_mdma),	// TGTID
                   64,					// burst size
                   nprocs);				// number of IRQs

std::cout << "  - building xbard_" << x_id << "_" << y_id << std::endl;

        // direct local crossbar
        size_t nb_direct_initiators      = nprocs + 1;
        size_t nb_direct_targets         = 3;
        if( io == true )
        {
            nb_direct_initiators         = nprocs + 2;
            nb_direct_targets            = 7;
	}
        std::ostringstream sd;
        sd << "xbard_" << x_id << "_" << y_id;
        xbard = new VciLocalCrossbar<vci_param>(
                    sd.str().c_str(),
                    mtd,
                    IntTab(cluster_id),              	// cluster initiator index
                    IntTab(cluster_id),              	// cluster target index
                    nb_direct_initiators,           	// number of initiators
                    nb_direct_targets);             	// number of targets      
        
std::cout << "  - building xbarc_" << x_id << "_" << y_id << std::endl;

        // coherence local crossbar
        std::ostringstream sc;
        sc << "xbarc_" << x_id << "_" << y_id;
        xbarc = new VciLocalCrossbar<vci_param>(
                    sc.str().c_str(),
                    mtc,
                    IntTab(cluster_id),                	// cluster initiator index
                    IntTab(cluster_id),                	// cluster target index
                    nprocs + 1,                		// number of initiators
                    nprocs + 1);               		// number of targets
        
std::cout << "  - building wrappers in cluster_" << x_id << "_" << y_id << std::endl;

        // direct initiator wrapper
        std::ostringstream wid;
        wid << "iniwrapperd_" << x_id << "_" << y_id;
        iniwrapperd = new VciVdspinInitiatorWrapper<vci_param,cmd_width,rsp_width>(
                          wid.str().c_str(),
                          4,				// cmd fifo depth
                          4);				// rsp fifo depth

        // direct target wrapper
        std::ostringstream wtd;
        wtd << "tgtwrapperd_" << x_id << "_" << y_id;
        tgtwrapperd = new VciVdspinTargetWrapper<vci_param,cmd_width,rsp_width>(
                          wtd.str().c_str(),
                          4,				// cmd fifo depth
                          4);				// rsp fifo depth

        // coherence initiator wrapper
        std::ostringstream wic;
        wic << "iniwrapperc_" << x_id << "_" << y_id;
        iniwrapperc = new VciVdspinInitiatorWrapper<vci_param,cmd_width,rsp_width>(
                          wic.str().c_str(),
                          4,				// cmd fifo depth
                          4);				// rsp fifo depth

        // coherence target wrapper
        std::ostringstream wtc;
        wtc << "tgtwrapperc_" << x_id << "_" << y_id;
        tgtwrapperc = new VciVdspinTargetWrapper<vci_param,cmd_width,rsp_width>(
                          wtc.str().c_str(),
                          4,				// cmd fifo depth
                          4);				// rsp fifo depth

std::cout << "  - building cmdrouter_" << x_id << "_" << y_id << std::endl;

        // CMD router
        std::ostringstream scmd;
        scmd << "cmdrouter_" << x_id << "_" << y_id;
        cmdrouter = new VirtualDspinRouter<cmd_width>(
                        scmd.str().c_str(),
                        x_id,y_id,                    // coordinate in the mesh
                        x_width, y_width,             // x & y fields width
                        4,4);                         // input & output fifo depths
        
std::cout << "  - building rsprouter_" << x_id << "_" << y_id << std::endl;

        // RSP router
        std::ostringstream srsp;
        srsp << "rsprouter_" << x_id << "_" << y_id;
        rsprouter = new VirtualDspinRouter<rsp_width>(
                        srsp.str().c_str(),
                        x_id,y_id,                    // coordinates in mesh
                        x_width, y_width,             // x & y fields width
                        4,4);                         // input & output fifo depths
        
        // IO cluster components
        if ( io == true )
	{
            brom = new VciSimpleRam<vci_param>(
                       "brom",
                       IntTab(cluster_id, tgtid_brom),
                       mtd,
                       loader);

            fbuf = new VciFrameBuffer<vci_param>(
                       "fbuf",
                       IntTab(cluster_id, tgtid_fbuf),
                       mtd,
		       xfb, yfb); 

            bdev = new VciBlockDeviceTsarV4<vci_param>(
                       "bdev",
                       mtd,
                       IntTab(cluster_id, nprocs+1),
                       IntTab(cluster_id, tgtid_bdev),
                       disk_name,
                       block_size);

            mtty = new VciMultiTty<vci_param>(
                       "mtty",
                       IntTab(cluster_id, tgtid_mtty),
                       mtd, 
                       "tty0", "tty1", "tty2", "tty3", NULL);
	}

std::cout << "  - all components constructed" << std::endl;

        ////////////////////////////////////
        // Connections are defined here
        ////////////////////////////////////

        // CMDROUTER and RSPROUTER
        cmdrouter->p_clk                	(this->p_clk);
        cmdrouter->p_resetn             	(this->p_resetn);
        rsprouter->p_clk                	(this->p_clk);
        rsprouter->p_resetn             	(this->p_resetn);
        for(int x = 0; x < 2; x++)
        {
          for(int y = 0; y < 4; y++)
          {
            cmdrouter->p_out[x][y]              (this->p_cmd_out[x][y]);
            cmdrouter->p_in[x][y]               (this->p_cmd_in[x][y]);
            rsprouter->p_out[x][y]              (this->p_rsp_out[x][y]);
            rsprouter->p_in[x][y]               (this->p_rsp_in[x][y]);
          }
        }
        
        cmdrouter->p_out[0][4]  		(signal_dspin_cmd_g2l_d);
        cmdrouter->p_out[1][4]  		(signal_dspin_cmd_g2l_c);
        cmdrouter->p_in[0][4]   		(signal_dspin_cmd_l2g_d);
        cmdrouter->p_in[1][4]   		(signal_dspin_cmd_l2g_c);

        rsprouter->p_out[0][4]  		(signal_dspin_rsp_g2l_d);
        rsprouter->p_out[1][4]  		(signal_dspin_rsp_g2l_c);
        rsprouter->p_in[0][4]   		(signal_dspin_rsp_l2g_d);
        rsprouter->p_in[1][4]   		(signal_dspin_rsp_l2g_c);

        // VCI/DSPIN WRAPPERS
        iniwrapperd->p_clk			(this->p_clk);
        iniwrapperd->p_resetn			(this->p_resetn);
	iniwrapperd->p_vci			(signal_vci_l2g_d);
	iniwrapperd->p_dspin_out		(signal_dspin_cmd_l2g_d);
	iniwrapperd->p_dspin_in			(signal_dspin_rsp_g2l_d);

        tgtwrapperd->p_clk			(this->p_clk);
        tgtwrapperd->p_resetn			(this->p_resetn);
	tgtwrapperd->p_vci			(signal_vci_g2l_d);
	tgtwrapperd->p_dspin_out		(signal_dspin_rsp_l2g_d);
	tgtwrapperd->p_dspin_in			(signal_dspin_cmd_g2l_d);

        iniwrapperc->p_clk			(this->p_clk);
        iniwrapperc->p_resetn			(this->p_resetn);
	iniwrapperc->p_vci			(signal_vci_l2g_c);
	iniwrapperc->p_dspin_out		(signal_dspin_cmd_l2g_c);
	iniwrapperc->p_dspin_in			(signal_dspin_rsp_g2l_c);

        tgtwrapperc->p_clk			(this->p_clk);
        tgtwrapperc->p_resetn			(this->p_resetn);
	tgtwrapperc->p_vci			(signal_vci_g2l_c);
	tgtwrapperc->p_dspin_out		(signal_dspin_rsp_l2g_c);
	tgtwrapperc->p_dspin_in			(signal_dspin_cmd_g2l_c);

        // CROSSBAR direct
        xbard->p_clk                  		(this->p_clk);
        xbard->p_resetn                 	(this->p_resetn);
        xbard->p_initiator_to_up        	(signal_vci_l2g_d);
        xbard->p_target_to_up           	(signal_vci_g2l_d);
          
        xbard->p_to_target[tgtid_memc]  	(signal_vci_tgt_d_memc);
        xbard->p_to_target[tgtid_xicu]  	(signal_vci_tgt_d_xicu);
        xbard->p_to_target[tgtid_mdma]  	(signal_vci_tgt_d_mdma);
          
        xbard->p_to_initiator[nprocs]  		(signal_vci_ini_d_mdma);

        for ( size_t p=0 ; p<nprocs ; p++)
        {
            xbard->p_to_initiator[p]		(signal_vci_ini_d_proc[p]);
        }

	if ( io == true )
	{
            xbard->p_to_target[tgtid_mtty]  	(signal_vci_tgt_d_mtty);
            xbard->p_to_target[tgtid_brom]  	(signal_vci_tgt_d_brom);
            xbard->p_to_target[tgtid_bdev]  	(signal_vci_tgt_d_bdev);
            xbard->p_to_target[tgtid_fbuf]  	(signal_vci_tgt_d_fbuf);
            
            xbard->p_to_initiator[nprocs+1]  	(signal_vci_ini_d_bdev);
	}
        
        // CROSSBAR coherence
        xbarc->p_clk                    	(this->p_clk);
        xbarc->p_resetn                 	(this->p_resetn);
        xbarc->p_initiator_to_up        	(signal_vci_l2g_c);
        xbarc->p_target_to_up           	(signal_vci_g2l_c);
        xbarc->p_to_initiator[nprocs]  		(signal_vci_ini_c_memc);
        xbarc->p_to_target[nprocs]     		(signal_vci_tgt_c_memc);
        for ( size_t p=0 ; p<nprocs ; p++)
        {
            xbarc->p_to_target[p]       	(signal_vci_tgt_c_proc[p]);
            xbarc->p_to_initiator[p]    	(signal_vci_ini_c_proc[p]);
        }

        // Processors
        for ( size_t p=0 ; p<nprocs ; p++)
        {
            proc[p]->p_clk              	(this->p_clk);
            proc[p]->p_resetn           	(this->p_resetn);
            proc[p]->p_vci_ini_d        	(signal_vci_ini_d_proc[p]);
            proc[p]->p_vci_ini_c        	(signal_vci_ini_c_proc[p]);
            proc[p]->p_vci_tgt_c          	(signal_vci_tgt_c_proc[p]);
            proc[p]->p_irq[0]           	(signal_proc_it[p]);
            for ( size_t j = 1 ; j < 6 ; j++ )
            {
                proc[p]->p_irq[j]       	(signal_false);
            }
        }
        
        // XICU
        xicu->p_clk                     	(this->p_clk);
        xicu->p_resetn                  	(this->p_resetn);
        xicu->p_vci                     	(signal_vci_tgt_d_xicu);
        for ( size_t p=0 ; p<nprocs ; p++)
        {
            xicu->p_irq[p]              	(signal_proc_it[p]);
        }
        for ( size_t p=0 ; p<nprocs ; p++)
        {
            xicu->p_hwi[p]			(signal_irq_mdma[p]);
        }
        for ( size_t x=nprocs ; x<4 ; x++)
        {
            xicu->p_hwi[x]			(signal_false);
        }
        if ( io == true )
	{
            xicu->p_hwi[4]			(signal_irq_tty0);
            xicu->p_hwi[5]			(signal_irq_tty1);
            xicu->p_hwi[6]			(signal_irq_tty2);
            xicu->p_hwi[7]			(signal_irq_tty3);
            xicu->p_hwi[8]			(signal_irq_bdev);
	}

        // MEMC
        memc->p_clk                     	(this->p_clk);
        memc->p_resetn                  	(this->p_resetn);
        memc->p_vci_ixr                 	(signal_vci_xram);
        memc->p_vci_tgt                 	(signal_vci_tgt_d_memc);
        memc->p_vci_ini                 	(signal_vci_ini_c_memc);
        memc->p_vci_tgt_cleanup         	(signal_vci_tgt_c_memc);

        // XRAM
        xram->p_clk                     	(this->p_clk);
        xram->p_resetn                  	(this->p_resetn);
        xram->p_vci                 		(signal_vci_xram);

        // CDMA
        mdma->p_clk                       	(this->p_clk);
        mdma->p_resetn                    	(this->p_resetn);
        mdma->p_vci_target                	(signal_vci_tgt_d_mdma);
        mdma->p_vci_initiator             	(signal_vci_ini_d_mdma);
        for (size_t p=0 ; p<nprocs ; p++)
        {
            mdma->p_irq[p]                       (signal_irq_mdma[p]);
        }

	// Components in IO cluster

	if ( io == true )
	{
       	    // BDEV            
	    bdev->p_clk                      	(this->p_clk);
       	    bdev->p_resetn                   	(this->p_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);

       	    // FBUF
       	    fbuf->p_clk                       	(this->p_clk);
       	    fbuf->p_resetn                    	(this->p_resetn);
       	    fbuf->p_vci                       	(signal_vci_tgt_d_fbuf);

       	    // BROM
       	    brom->p_clk                       	(this->p_clk);
       	    brom->p_resetn                    	(this->p_resetn);
       	    brom->p_vci                       	(signal_vci_tgt_d_brom);

            // MTTY
            mtty->p_clk                       	(this->p_clk);
            mtty->p_resetn                    	(this->p_resetn);
            mtty->p_vci                       	(signal_vci_tgt_d_mtty);
            mtty->p_irq[0]           		(signal_irq_tty0);
            mtty->p_irq[1]           		(signal_irq_tty1);
            mtty->p_irq[2]           		(signal_irq_tty2);
            mtty->p_irq[3]           		(signal_irq_tty3);
        }
} // end constructor

///////////////////////////////////////////////////////////////////////////
//    destructor
///////////////////////////////////////////////////////////////////////////
template<typename vci_param, typename iss_t, int cmd_width, int rsp_width>
TsarV4ClusterMmu<vci_param, iss_t, cmd_width, rsp_width>::~TsarV4ClusterMmu() {}

}}
