/*
 * SecBus - This file is part of SecBus
 * Copyright (C) 2012 - Telecom ParisTech
 * 
 * This file must be used under the terms of the CeCILL.
 * This source file is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at    
 * http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.txt
 *
 */

#include "vci_merge.h"
#include "secbus_lib.h"
#include "alloc_elems.h"

#define vci_merge(x) template<typename vci_param> x VciMerge<vci_param>

vci_merge(/**/)::VciMerge(sc_module_name name, size_t n_init):
		sc_module(name),
		p_vci_initiator(soclib::common::alloc_elems<soclib::caba::VciTarget<vci_param> >("p_vci_initiator", n_init)),
		p_vci_target("p_vci_target")
{
  m_n_init = n_init;

  // State machine and register of requester index
  SC_METHOD(transition);
  dont_initialize();
  sensitive << p_clk.pos();

  // Initiator to target flow
  SC_METHOD(genMealy_i2t);
  dont_initialize();
  sensitive << p_clk.neg();
  for (size_t i=0;i<m_n_init;i++)
    sensitive << p_vci_initiator[i];

  // Target to initiator flow
  SC_METHOD(genMealy_t2i);
  dont_initialize();
  sensitive << p_clk.neg()
	    << p_vci_target;

  DEBUG(NONE, STD, "[SECBUS] Building VciMerge (%s) with %u inputs\n", 
	basename(), n_init);
}

vci_merge(/**/)::~VciMerge()
{
  soclib::common::dealloc_elems(p_vci_initiator, m_n_init);
}

vci_merge(void)::transition()
{
  if (!p_resetn.read())
  {
    fsm_s = FSM_IDLE;
    sel_s = 0;

    return;
  }

  DEBUG(TRACE, FSM, "[%s]\t\tFSM\t%u\n", 
	basename(), (uint32_t)fsm_s.read());

  switch(fsm_s.read())
  {
  case FSM_IDLE: // Waiting for VCI request
    for (size_t i=0;i<m_n_init;i++)
    {
      if (p_vci_initiator[i].cmdval.read()) // If VCI request
      {
	sel_s = i;       // Index of highest priority requester
	fsm_s = FSM_CMD; // Start forwarding requests, request acknowledges, responses and response acknowledges
	break;
      }
    }
    break;

  case FSM_CMD: // Waiting for last request of packet
    if (p_vci_initiator[sel_s.read()].eop.read() &&
	p_vci_initiator[sel_s.read()].cmdval.read() &&
	p_vci_target.cmdack.read())
      fsm_s = FSM_RSP; // Stop forwarding requests and request acknowledges. Continue forwarding responses and response acknowledges
    break;

  case FSM_RSP: // Waiting for last response of packet
    if (p_vci_target.reop.read() &&
	p_vci_target.rspval.read() &&
	p_vci_initiator[sel_s.read()].rspack.read())
      fsm_s = FSM_IDLE;
    break;

  default:
    break;
  }
}

vci_merge(void)::genMealy_i2t()
{
  if (fsm_s.read() == FSM_CMD) // Forward requests
  {
    p_vci_target.cmdval  = p_vci_initiator[sel_s.read()].cmdval.read();
    p_vci_target.wdata   = p_vci_initiator[sel_s.read()].wdata.read();
    p_vci_target.address = p_vci_initiator[sel_s.read()].address.read();
    p_vci_target.be      = p_vci_initiator[sel_s.read()].be.read();
    p_vci_target.plen    = p_vci_initiator[sel_s.read()].plen.read();
    p_vci_target.srcid   = p_vci_initiator[sel_s.read()].srcid.read();
    p_vci_target.trdid   = p_vci_initiator[sel_s.read()].trdid.read();
    p_vci_target.pktid   = p_vci_initiator[sel_s.read()].pktid.read();
    p_vci_target.cmd     = p_vci_initiator[sel_s.read()].cmd.read();
    p_vci_target.contig  = p_vci_initiator[sel_s.read()].contig.read();
    p_vci_target.eop     = p_vci_initiator[sel_s.read()].eop.read();
    p_vci_target.cons    = p_vci_initiator[sel_s.read()].cons.read();
    p_vci_target.wrap    = p_vci_initiator[sel_s.read()].wrap.read();
    p_vci_target.cfixed  = p_vci_initiator[sel_s.read()].cfixed.read();
    p_vci_target.clen    = p_vci_initiator[sel_s.read()].clen.read();
  }
  else
  {
    p_vci_target.cmdval = false;
  }
  if (fsm_s.read() != FSM_IDLE) // Forward response acknowledges
  {
    p_vci_target.rspack = p_vci_initiator[sel_s.read()].rspack.read();
  }
  else
  {
    p_vci_target.rspack = false;
  }
}

vci_merge(void)::genMealy_t2i()
{
  for (size_t i=0;i<m_n_init;i++) // By default no request acknowledge and no response valid
  {
    p_vci_initiator[i].cmdack = false;
    p_vci_initiator[i].rspval = false;
  }
  if (fsm_s.read() == FSM_CMD) // Forward request acknowledges
  {
    p_vci_initiator[sel_s.read()].cmdack = p_vci_target.cmdack.read();
  }
  if (fsm_s.read() != FSM_IDLE) // Forward responses
  {
    p_vci_initiator[sel_s.read()].rdata  = p_vci_target.rdata.read();
    p_vci_initiator[sel_s.read()].rerror = p_vci_target.rerror.read();
    p_vci_initiator[sel_s.read()].rsrcid = p_vci_target.rsrcid.read();
    p_vci_initiator[sel_s.read()].rtrdid = p_vci_target.rtrdid.read();
    p_vci_initiator[sel_s.read()].rpktid = p_vci_target.rpktid.read();
    p_vci_initiator[sel_s.read()].reop   = p_vci_target.reop.read();
    p_vci_initiator[sel_s.read()].rspval = p_vci_target.rspval.read();
  }
}
