/////////////////////////////////////////////////////////////////////////
// File:      tsarv1_mono_ring_top.cpp
// Author:    Cesar Fuguet / Abdelmalek Simerabe
// Copyright: UPMC/LIP6
// Date :     2012
//
// This program is released under the GNU public license
/////////////////////////////////////////////////////////////////////////

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

#include "tsar_cluster_v1.h"
#include "mapping_table.h"
#include "vci_simple_ring_fast.h"
#include "vci_simple_ram.h"
#include "segmentation.h"

///////////////////////////////////
// flit widths for the ring network

#define cmd_width	    40
#define rsp_width	    33

//////////////////
// 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	  5
#define pktid_width	  4
#define trdid_width	  4
#define wrplen_width  1

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

    char    soft_name[128]  = "undefined_binary_file";  // pathname to binary code
    size_t  ncycles         = 200000;           	      // simulated cycles
    size_t  xmax            = 1;         		            // number of clusters in a row
    size_t  ymax            = 1;                	      // 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        = 200000;           	      // 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],"-SOFT") == 0) && (n+1<argc) )
            {
                strcpy(soft_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) ";
                std::cout << "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 << "     -NCYCLES number_of_simulated_cycles" << std::endl;
                std::cout << "     -NPROCS number_of_processors_per_cluster" << 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 = DSPIN & RING" << 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 x_width = 0;
    size_t y_width = 0;

    /////////////////////
    //  Mapping Tables
    /////////////////////

    // direct network
    MappingTable maptabd(address_width, 
                         IntTab(16 - x_width - y_width), 
                         IntTab(srcid_width - x_width - y_width), 
                         0x00FF0000);

    maptabd.add(
        Segment("d_seg_xicu", 
          XICU_BASE,
          XICU_SIZE, 
          IntTab(XICU_TGTID), 
          false
          )
        );

    maptabd.add(
        Segment("d_seg_stak",
          STAK_BASE,
          STAK_SIZE,
          IntTab(MEMC_TGTID),
          true
          )
        );

    maptabd.add(
        Segment("d_seg_mtty",
          TTY_BASE,
          TTY_SIZE,
          IntTab(MTTY_TGTID),
          false
          )
        );

    maptabd.add(
        Segment("d_seg_reset",
          RESET_BASE,
          RESET_SIZE,
          IntTab(MEMC_TGTID),
          true
          )
        );

    maptabd.add(
        Segment("d_seg_kcode",
          KCODE_BASE,
          KCODE_SIZE,
          IntTab(MEMC_TGTID),
          true
          )
        );

    maptabd.add(
        Segment("d_seg_kdata",
          KDATA_BASE,
          KDATA_SIZE,
          IntTab(MEMC_TGTID),
          true
          )
        );

    maptabd.add(
        Segment("d_seg_kunc",
          KUNC_BASE,
          KUNC_SIZE,
          IntTab(MEMC_TGTID),
          false
          )
        );

    maptabd.add(
        Segment("d_seg_code",
          CODE_BASE,
          CODE_SIZE,
          IntTab(MEMC_TGTID),
          true
          )
        );

    maptabd.add(
        Segment("d_seg_data",
          DATA_BASE,
          DATA_SIZE,
          IntTab(MEMC_TGTID),
          true
          )
        );
    std::cout << maptabd << std::endl;

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

    std::ostringstream sm;
    sm << "c_seg_memc_" << 0 << "_" << 0;
    maptabc.add(
        Segment(sm.str(),
          (nprocs << (address_width - srcid_width)),
          0xC,
          IntTab(nprocs),
          false
          )
        );

    for ( size_t p = 0 ; p < nprocs ; p++)
    {
      std::ostringstream sp;
      sp << "c_seg_proc_" << 0 << "_" << 0 << "_" << p;
      maptabc.add(
          Segment(sp.str(),
            (p << (address_width - srcid_width)),
            0xC,
            IntTab(p),
            false
            )
          );
    }
    std::cout << maptabc << std::endl;

    // external network
    MappingTable maptabx(
      address_width,
      IntTab(x_width + y_width),
      IntTab(x_width + y_width),
      0x00FF0000
    );

    maptabx.add(
      Segment("x_seg_kcode",
        KCODE_BASE,
        KCODE_SIZE,
        IntTab(0),
        false
      )
    );

    maptabx.add(
      Segment("x_seg_kdata",
        KDATA_BASE,
        KDATA_SIZE,
        IntTab(0),
        false
      )
    );

    maptabx.add(
      Segment("x_seg_kunc",
        KUNC_BASE,
        KUNC_SIZE,
        IntTab(0),
        false
      )
    );

    maptabx.add(
      Segment("x_seg_code",
        CODE_BASE,
        CODE_SIZE,
        IntTab(0),
        false
      )
    );

    maptabx.add(
      Segment("x_seg_reset",
        RESET_BASE,
        RESET_SIZE,
        IntTab(0),
        false
      )
    );

    maptabx.add(
      Segment("x_seg_data",
        DATA_BASE,
        DATA_SIZE,
        IntTab(0),
        false
      )
    );
    
    maptabx.add(
        Segment("x_seg_stak",
          STAK_BASE,
          STAK_SIZE,
          IntTab(0),
          false
          )
        );
    std::cout << maptabx << std::endl;

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

    sc_clock		    signal_clk("clk");
    sc_signal<bool> signal_resetn("resetn");

    // Xternal network VCI signals
    VciSignals<vci_param> signal_vci_tgt_x_xram("signal_vci_tgt_x_xram");

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

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

    cout << loader << endl;

    // TSAR Clusters
    TsarClusterV1<vci_param, cmd_width, rsp_width> * cluster;
    std::ostringstream scluster;
    scluster << "cluster_" << 0 << "_" << 0;

    cluster = new TsarClusterV1<vci_param, cmd_width, rsp_width> (
        scluster.str().c_str(),
        0,                          // Cluster index
        4,4,                        // Router fifo's depth
        0,0,                        // X, Y coordinates
        x_width, y_width,           // Global routing bits
        maptabd, maptabc, maptabx,  // Mapping Tables
        nprocs,                     // Number of processors in the cluster
        nprocs,                     // Maximum number of processors per cluster
        4,32,16,/**/4,32,16,  		  // Icache and Dcache sizes
        4,32,16,				            // MemCache size
        1,                          // Is IO cluster
        loader                      // Program Loader
        ); 

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

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

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

    // Connection with the external network
    cluster->p_vci_ixr(signal_vci_tgt_x_xram);

    // Clock and Reset
    cluster->p_clk(signal_clk);
    cluster->p_resetn(signal_resetn);

    ////////////////////////////////////////////////////////
    //   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) {
          cluster->print_trace();
        }
    }

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

    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;
}

// vim: tabstop=2 shiftwidth=2 expandtab softtabstop=2
