#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,
                        const 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
                x_width,                        // X Width
                y_width,                        // Y Width
                nprocs,                         // Memory Cache Local Id (coherence)
                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() {}

}}
