/* -*- c++ -*-
 * File 	: vci_traffic_generator.cpp
 * Date 	: 26/08/2010
 * Copyright 	: UPMC / LIP6
 * Authors 	: Christophe Choichillon
 *
 * SOCLIB_LGPL_HEADER_BEGIN
 *
 * This file is part of SoCLib, GNU LGPLv2.1.
 *
 * SoCLib is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 of the License.
 *
 * SoCLib is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with SoCLib; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 * SOCLIB_LGPL_HEADER_END
 *
 * Maintainers: christophe.choichillon@lip6.fr
 */

#include "../include/vci_synthetic_initiator.h"



namespace soclib { namespace caba {


#define tmpl(x) template<typename vci_param> x VciSyntheticInitiator<vci_param>

  //using soclib::common::uint32_log2;	
  
  ////////////////////////////////
  // 	Constructor 
  ////////////////////////////////

  tmpl(/**/)::VciSyntheticInitiator( 
      sc_module_name name,
      const soclib::common::MappingTable &mt,
      const soclib::common::IntTab       &vci_index,
      const uint32_t length,    // Packet length (flit numbers)
      const float  rho,       // Packets ratio on the network
      const uint32_t depth,     // Fifo depth
      const uint32_t xmesh,	
      const uint32_t ymesh,
      const uint32_t bc_period, // Broadcast period, if no broadcast => 0
      const uint32_t xmin, 
      const uint32_t xmax,
      const uint32_t ymin,
      const uint32_t ymax
      )

    : soclib::caba::BaseModule(name),

    p_clk("clk"),
    p_resetn("resetn"),
    p_vci("vci_ini"),

    m_srcid( mt.indexForId(vci_index) ),
    m_coord(vci_index[1]),
    //  FIFOs 
    m_length(length),
    m_rho(rho),
    m_depth(depth),
    m_xmesh(xmesh),
    m_ymesh(ymesh),
    m_bc_period(bc_period),
    m_xmin(xmin),
    m_xmax(xmax),
    m_ymin(ymin),
    m_ymax(ymax),
    m_date_fifo("m_date_fifo", depth),
    r_vci_fsm("r_vci_fsm")
    {


      SC_METHOD(transition);
      dont_initialize();
      sensitive << p_clk.pos();

      SC_METHOD(genMoore);
      dont_initialize();
      sensitive << p_clk.neg();

    } // end constructor


  /////////////////////////////////
  tmpl(/**/)::~VciSyntheticInitiator()
    /////////////////////////////////
  {
  }

  ///////////////////////////////////
  tmpl(size_t)::destAdress()
  ///////////////////////////////////
  {
    size_t dest;
    do{
      dest = (size_t) (rand() % (m_xmesh * m_ymesh));
    } while(dest == m_srcid);
    return dest ;
  }


  ///////////////////////////////////
//  tmpl(void)::destAdress(/*size_t X_local, size_t Y_local,*/ size_t &X_dest, size_t &Y_dest)
  ///////////////////////////////////
//  {
//    size_t x_dest_calc, y_dest_calc; 
//    do{
//      x_dest_calc = (rand()%m_xmesh);
//      y_dest_calc = (rand()%m_ymesh);
//    } while((x_dest_calc = m_x) && (y_dest_calc == m_y));
//  }

  //////////////////////////////////
  tmpl(void)::transition()
    //////////////////////////////////
  {
    //  RESET          
    if ( ! p_resetn.read() ) {
      // Initializing seed for random numbers generation
      srand(time(NULL));

      // Initializing FSMs
      r_vci_fsm = VCI_IDLE;

      // Initializing FIFOs 
      m_date_fifo.init();

      // Activity counters
      m_cpt_cycles		= 0;
      m_npackets		= 0;

      return;
    }

    bool    date_fifo_put = false;
    bool    date_fifo_get = false;

    switch ( r_vci_fsm.read() ) {

      //////////////////
      case VCI_IDLE:
        {
	  if (m_date_fifo.rok()){
	    if (r_broadcast_req.read()){
	      r_vci_fsm = VCI_BC_SEND ;
	    } else {
	      r_vci_fsm = VCI_SINGLE_SEND ;
	      r_address_to_send = destAdress();
	      m_count = 0;
	    }
	  }
          break;
        }
        //////////////////
      case VCI_SINGLE_SEND:
        {
	  if (p_vci.cmdack.read()){
	    m_count++;
	    if (m_count == m_length-1) {
	      m_start_latency1 = m_date_fifo.read();
	      m_start_latency2 = m_cpt_cycles;
	      r_vci_fsm = VCI_SINGLE_RECEIVE ;
	    } else {
	      r_vci_fsm = VCI_SINGLE_SEND ;
	    }
	  }
          break;
        }
        //////////////////////
      case VCI_SINGLE_RECEIVE:
        {
	  if (p_vci.rspval.read()) {
	    m_start_latency1 = m_date_fifo.read();
	    m_start_latency2 = m_cpt_cycles;
	    m_npackets++;
	    date_fifo_get = true;
	    r_vci_fsm = VCI_IDLE ;
	  }
          break;
        }
        ///////////////////
      case VCI_BC_SEND:
        {
	  r_address_to_send = (((((((((m_xmin << 5) & m_xmax ) << 5 ) & m_ymin ) << 5 ) & m_ymax ) << 5 ) << 17 ) & 0x3) | 0 ;
	  //m_address_to_send.broadcast_address.xmin = m_xmin;
	  //m_address_to_send.broadcast_address.xmax = m_xmax;
	  //m_address_to_send.broadcast_address.ymin = m_ymin;
	  //m_address_to_send.broadcast_address.ymax = m_ymax;
	  //m_address_to_send.broadcast_address.bc   = 0x3;
	  m_bc_nrsp = (m_xmax - m_xmin) * (m_ymax - m_ymin);
	  r_vci_fsm = VCI_BC_SEND;
          break;
        }
        ////////////////////
      case VCI_BC_RECEIVE:
        {
	  if (p_vci.rspval.read()){
	    if (m_bc_nrsp == 0) {
	      r_broadcast_req = false;
	      r_vci_fsm = VCI_IDLE ;
	    } else {
	      m_bc_nrsp--;
	      r_vci_fsm = VCI_BC_RECEIVE ;
	    }
	  }
          break;
        }
    } // end switch vci_fsm


/////////////////// Filling fifo
    if( (m_rhos < m_rho) && (rand()/RAND_MAX) ){
      if (m_date_fifo.wok()){
        date_fifo_put = true ;
      } 
      if (!r_broadcast_req.read() && (m_cpt_cycles % m_bc_period)){
        r_broadcast_req = true;
      }
    }

    if (date_fifo_put){
      if (date_fifo_get){
        m_date_fifo.put_and_get(m_cpt_cycles);
      } else {
        m_date_fifo.simple_put(m_cpt_cycles);
      }
    } else {
      if (date_fifo_get){
        m_date_fifo.simple_get();
      }
    }
   
    m_rhos = (float) ((m_npackets * m_length) / m_cpt_cycles) ;

    m_cpt_cycles++;

    return;

  } // end transition()

  /////////////////////////////
  tmpl(void)::genMoore()
    /////////////////////////////
  {
    ////////////////////////////////////////////////////////////
    // Command signals on the p_vci port
    ////////////////////////////////////////////////////////////
     p_vci.cmd        = vci_param::CMD_WRITE;   
     p_vci.be         = 0xF;                             
     p_vci.pktid      = 0;      
     p_vci.srcid      = m_srcid;   
     p_vci.cons       = false;        
     p_vci.wrap       = false;       
     p_vci.contig     = true;       
     p_vci.clen       = 0;         
     p_vci.cfixed     = false;           


    switch ( r_vci_fsm.read() ) {

      //////////////////
      case VCI_IDLE:
        {
	  p_vci.cmdval  = false;                 
	  p_vci.address = 0; 
	  p_vci.plen    = 0;                                         
	  p_vci.wdata   = 0;                                       
	  p_vci.trdid   = 0;                  
	  p_vci.eop     = false;                                   
	  p_vci.rspack  = false;
          break;
        }
        //////////////////
      case VCI_SINGLE_SEND:
        {
	  p_vci.cmdval  = true;                 
	  p_vci.address = (vci_addr_t)(r_address_to_send.read() + (m_count*4)); 
	  p_vci.plen    = m_length*4;                                         
	  p_vci.wdata   = 0;                                       
	  p_vci.trdid   = 0;                  
	  if (m_count == m_length - 1 ) {
	    p_vci.eop     = true;                                   
	  } else {
	    p_vci.eop     = false;                                   
	  }
	  p_vci.rspack  = false;
          break;
        }
        //////////////////////
      case VCI_SINGLE_RECEIVE:
        {
       	  p_vci.cmdval  = false;                 
	  p_vci.address = 0; 
	  p_vci.plen    = 0;                                         
	  p_vci.wdata   = 0;                                       
	  p_vci.trdid   = 0;                  
	  p_vci.eop     = false;                                   
	  p_vci.rspack  = true;
   	  break;
        }
        ///////////////////
      case VCI_BC_SEND:
        {
       	  p_vci.cmdval  = true;                 
	  p_vci.address = (vci_addr_t) r_address_to_send.read(); 
	  p_vci.plen    = 4;                                         
	  p_vci.wdata   = 0;                                       
	  p_vci.trdid   = 0;                  
	  p_vci.eop     = true;                                   
	  p_vci.rspack  = false;
          break;
        }
        ////////////////////
      case VCI_BC_RECEIVE:
        {
       	  p_vci.cmdval  = false;                 
	  p_vci.address = 0; 
	  p_vci.plen    = 0;                                         
	  p_vci.wdata   = 0;                                       
	  p_vci.trdid   = 0;                  
	  p_vci.eop     = false;                                   
	  p_vci.rspack  = true;
          break;
        }
    } // end switch vci_fsm

  } // end genMoore()

}} // end name space
