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

#ifdef _OPENMP
#include <omp.h>
#endif

#include "mapping_table.h"
#include "mips32.h"
#include "vci_simple_ram.h"
#include "vci_multi_tty.h"
#include "vci_vgmn.h"
#include "vci_mem_cache_v4.h"
#include "vci_cc_vcache_wrapper_v4.h"
#include "vci_xicu.h"
#include "vci_simhelper.h"
#include "vci_multi_dma.h"

// VCI format

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

//   segments definition in direct space

#define    XICU_BASE	0xd8200000
#define    XICU_SIZE	0x00001000

#define	   MDMA_BASE	0xe8000000
#define	   MDMA_SIZE	0x00000014

#define    MTTY_BASE	0xd0200000
#define    MTTY_SIZE	0x00000010

#define    EXIT_BASE	0xe0000000
#define    EXIT_SIZE	0x00000010

#define    BOOT_BASE	0xbfc00000
#define    BOOT_SIZE	0x00040000

#define    MEMC_BASE    0x00000000
#define    MEMC_SIZE    0x02000000

/////////////////////////////////
int _main(int argc, char *argv[])
{

using namespace sc_core;
using namespace soclib::common;
using namespace soclib::caba;

#ifdef _OPENMP
        omp_set_dynamic(false);
	omp_set_num_threads(1);
	std::cerr << "Built with openmp version " << _OPENMP << std::endl;
#endif

    char	soft_name[256] 	= "test.elf";	// pathname to binary code
    size_t	ncycles		= 1000000000;	// max number of simulation cycles
    bool	trace_ok	= false;
    size_t	from_cycle	= 0;		// debug start cycle 
    size_t	max_frozen	= 1000;		// max number of frozen cycles
    size_t      nprocs          = 1;    

    /////////////// 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],"-SOFT") == 0) && (n+1<argc) )
            {
                strcpy(soft_name, argv[n+1]);
            }
            else if( (strcmp(argv[n],"-TRACE") == 0) && (n+1<argc) )
            {
                trace_ok = true;
                from_cycle = atoi(argv[n+1]);
            }
            else if( (strcmp(argv[n],"-MAXFROZEN") == 0) && (n+1<argc) )
            {
                max_frozen = 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 pathname_for_embedded_soft" << std::endl;
                std::cout << "     -NCYCLES number_of_simulated_cycles" << std::endl;
                std::cout << "     -TRACE debug_start_cycle" << std::endl;
                exit(0);
            }
        }
    }

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

    // Define processor type
    typedef soclib::common::Mips32ElIss proc_iss;

    // Mapping table for direct network
    soclib::common::MappingTable maptabd(32, IntTab(6), IntTab(6), 0xF0000000);
    maptabd.add(Segment("memc_d" , MEMC_BASE , MEMC_SIZE , IntTab(0), true));
    maptabd.add(Segment("boot_d" , BOOT_BASE , BOOT_SIZE , IntTab(1), true));
    maptabd.add(Segment("exit_d" , EXIT_BASE , EXIT_SIZE , IntTab(2), false));
    maptabd.add(Segment("mtty_d" , MTTY_BASE , MTTY_SIZE , IntTab(3), false));
    maptabd.add(Segment("xicu_d" , XICU_BASE , XICU_SIZE , IntTab(4), false));
    maptabd.add(Segment("mdma_d" , MDMA_BASE , MDMA_SIZE , IntTab(5), false));
    std::cout << maptabd << std::endl;

    // Mapping table for coherence network
    soclib::common::MappingTable maptabc(32, IntTab(srcid_width), IntTab(srcid_width), 0xF0000000);
    maptabc.add(
        Segment( "proc_c"
                , 0x0000000
                , 0x10
                , IntTab(0)
                , false
        )
    );

    maptabc.add(
        Segment( "memc_c"
                , nprocs << (address_width - srcid_width)
                , 0x10
                , IntTab(nprocs)
                , false
        )
    );
    std::cout << maptabc << std::endl;
	
    // Signals

    sc_clock        signal_clk			("signal_clk");
    sc_signal<bool> signal_resetn		("isgnal_resetn");
   
    sc_signal<bool> signal_mdma_irq		("signal_mdma_irq");
    sc_signal<bool> signal_mtty_irq		("signal_mtty_irq");
    sc_signal<bool> signal_proc_irq		("signal_proc_irq"); 
    sc_signal<bool> signal_false		("signal_false");

    VciSignals<vci_param> signal_vci_ini_d_proc	("vci_ini_d_proc");
    VciSignals<vci_param> signal_vci_ini_c_proc	("vci_ini_c_proc");
    VciSignals<vci_param> signal_vci_tgt_c_proc	("vci_tgt_c_proc");

    VciSignals<vci_param> signal_vci_tgt_d_mtty	("signal_vci_tgt_d_mtty");

    VciSignals<vci_param> signal_vci_tgt_d_exit	("signal_vci_tgt_d_exit");

    VciSignals<vci_param> signal_vci_tgt_d_xicu	("signal_vci_tgt_d_xicu");

    VciSignals<vci_param> signal_vci_ini_d_mdma	("signal_vci_ini_d_mdma");
    VciSignals<vci_param> signal_vci_tgt_d_mdma	("signal_vci_tgt_d_mdma");

    VciSignals<vci_param> signal_vci_tgt_d_brom ("signal_vci_tgt_d_brom");


    VciSignals<vci_param> signal_vci_ini_c_memc ("signal_vci_ini_c_memc");
    VciSignals<vci_param> signal_vci_tgt_d_memc ("signal_vci_tgt_d_memc");
    VciSignals<vci_param> signal_vci_tgt_c_memc ("signal_vci_tgt_c_memc");

    VciSignals<vci_param> signal_vci_xram	("signal_vci_xram");

    // Components

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

    VciCcVCacheWrapperV4<vci_param, proc_iss > 
    proc("proc", 
         0,			// proc_id 
         maptabd, 		// direct space
         maptabc, 		// coherence space
         IntTab(0),		// srcid_d
         IntTab(0),		// srcid_c
         IntTab(0),		// tgtid_c
         8,8,			// itlb size
         8,8,			// dtlb size
         4,64,16,		// icache size
         4,64,16,		// dcache size
         4, 4, 			// wbuf size
         0, 0,                  // x, y Width
         nprocs,                // memory cache local id
         max_frozen,		// max frozen cycles
         from_cycle, trace_ok);

    VciSimpleRam<vci_param> 
    rom("rom", 
         IntTab(1), 		// tgtid_d
         maptabd, 
         loader);

    VciSimpleRam<vci_param> 
    xram("xram", 
         IntTab(0),		// tgtid_d(RAM) = tgtid_d(MEMC) 
         maptabd, 
         loader);

    VciMemCacheV4<vci_param> 
    memc("memc",
         maptabd,
         maptabc,
         maptabd,
         IntTab(0),		// srcid_x
         IntTab(1),		// srcid_c
         IntTab(0), 		// tgtid_d
         IntTab(1),		// tgtid_c
         16,256,16, 		// cache size
         1024,			// HEAP size
         4,			// TRT size
         4,			// UPT size
         from_cycle, trace_ok);
       
    VciSimhelper<vci_param> 
    vciexit("vciexit",
         IntTab(2), 		// tgtid_d
         maptabd);

    VciXicu<vci_param> 
    xicu("xicu", 
         maptabd,
         IntTab(4),		// tgtid_d
         1,			// number of timers
         2,			// number of hard interrupts 
         0,			// number of soft interrupts
         1);			// number of output IRQs

    VciMultiTty<vci_param> 
    mtty("mtty", 
         IntTab(3),		// tgtid_d 
         maptabd, 
         "mtty0", NULL);

    VciMultiDma<vci_param>
    mdma("mdma", 
         maptabd, 
         IntTab(1),		// srcid_d 
         IntTab(5), 		// tgtid_d
         64,			// burst size
         1);			// number of channels 
	
    VciVgmn<vci_param> 
    ringd("ringd",
         maptabd, 
         2, 			// number of initiators
         6, 			// number of targets
         2, 
         8);

    VciVgmn<vci_param> 
    ringc("ringc",
         maptabc, 
         2, 			// number of initiators
         2, 			// number of targets
         2, 
         8);

    // net-list
    proc.p_clk(signal_clk);  
    proc.p_resetn		(signal_resetn);  
    proc.p_irq[0]		(signal_proc_irq); 
    proc.p_irq[1]		(signal_false); 
    proc.p_irq[2]		(signal_false); 
    proc.p_irq[3]		(signal_false); 
    proc.p_irq[4]		(signal_false); 
    proc.p_irq[5]		(signal_false); 
    proc.p_vci_ini_d		(signal_vci_ini_d_proc);
    proc.p_vci_ini_c		(signal_vci_ini_c_proc);
    proc.p_vci_tgt_c		(signal_vci_tgt_c_proc);

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

    xicu.p_resetn		(signal_resetn);
    xicu.p_clk			(signal_clk);
    xicu.p_vci			(signal_vci_tgt_d_xicu);
    xicu.p_hwi[0]		(signal_mtty_irq);
    xicu.p_hwi[1]		(signal_mdma_irq);
    xicu.p_irq[0]		(signal_proc_irq);

    mdma.p_clk			(signal_clk);
    mdma.p_resetn		(signal_resetn);
    mdma.p_vci_target		(signal_vci_tgt_d_mdma);
    mdma.p_vci_initiator	(signal_vci_ini_d_mdma);
    mdma.p_irq[0]		(signal_mdma_irq); 

    mtty.p_clk			(signal_clk);
    mtty.p_resetn		(signal_resetn);
    mtty.p_vci			(signal_vci_tgt_d_mtty);
    mtty.p_irq[0]		(signal_mtty_irq); 

    vciexit.p_clk		(signal_clk);
    vciexit.p_resetn		(signal_resetn);
    vciexit.p_vci		(signal_vci_tgt_d_exit);

    memc.p_clk			(signal_clk);
    memc.p_resetn		(signal_resetn);
    memc.p_vci_tgt		(signal_vci_tgt_d_memc);
    memc.p_vci_tgt_cleanup	(signal_vci_tgt_c_memc);
    memc.p_vci_ini		(signal_vci_ini_c_memc);
    memc.p_vci_ixr		(signal_vci_xram);

    xram.p_clk			(signal_clk);
    xram.p_resetn		(signal_resetn);
    xram.p_vci			(signal_vci_xram);
	
    ringc.p_clk			(signal_clk);
    ringc.p_resetn		(signal_resetn);
    ringc.p_to_initiator[0]	(signal_vci_ini_c_proc);
    ringc.p_to_initiator[1]	(signal_vci_ini_c_memc);
    ringc.p_to_target[0]	(signal_vci_tgt_c_proc);
    ringc.p_to_target[1]	(signal_vci_tgt_c_memc);

    ringd.p_clk			(signal_clk);
    ringd.p_resetn		(signal_resetn);
    ringd.p_to_initiator[0]	(signal_vci_ini_d_proc);
    ringd.p_to_initiator[1]	(signal_vci_ini_d_mdma);
    ringd.p_to_target[0]	(signal_vci_tgt_d_memc);
    ringd.p_to_target[1]	(signal_vci_tgt_d_brom);
    ringd.p_to_target[2]	(signal_vci_tgt_d_exit);
    ringd.p_to_target[3]	(signal_vci_tgt_d_mtty);
    ringd.p_to_target[4]	(signal_vci_tgt_d_xicu);
    ringd.p_to_target[5]	(signal_vci_tgt_d_mdma);
	
    // simulation loop

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

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

    for ( size_t n=1 ; n<ncycles ; n++ )
    {
        if ( trace_ok and (n > from_cycle) )
        {
            std::cout << "****************** cycle " << std::dec << n
                      << " ************************************************" << std::endl;
            proc.print_trace();
            memc.print_trace();
            signal_vci_ini_d_proc.print_trace("proc_ini_d");
            signal_vci_tgt_c_proc.print_trace("proc_tgt_c");
            signal_vci_ini_c_proc.print_trace("proc_ini_c");
            signal_vci_tgt_d_memc.print_trace("memc_tgt_d");
            signal_vci_tgt_c_memc.print_trace("memc_tgt_c");
            signal_vci_ini_c_memc.print_trace("memc_ini_c");
            if ( signal_proc_irq.read() ) std::cout << "---- IRQ ----" << std::endl;
        }
        sc_start(sc_core::sc_time(1, SC_NS));
    }
    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;
}
