/*
 * 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 "sc_arbiter.h"

ScArbiter::ScArbiter(sc_module_name name, size_t nrq):
  sc_module(name),
  p_clk("clk"),
  p_resetn("resetn")
{
  m_nrq = nrq;
  sc_rq = new SECURITY_RQ_IN[m_nrq];

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

  SC_METHOD(genMealy_cmdack);
  dont_initialize();
  sensitive << p_clk.neg()
	    << sc_ctrl;

  SC_METHOD(genMealy_cmdval);
  dont_initialize();
  sensitive << p_clk.neg();
  for (size_t i=0;i<m_nrq;i++)
    sensitive << sc_rq[i];

  SC_METHOD(genMealy_rsp);
  dont_initialize();
  sensitive << p_clk.neg()
	    << sc_ctrl;

  DEBUG(NONE, STD, "[SECBUS] Building ScArbiter (%s) with %u entries\n", 
	basename(), nrq);
}

ScArbiter::~ScArbiter()
{
  delete sc_rq;
}

void ScArbiter::transition()
{
  if (p_resetn.read() == false)
  {
    selected_src_s = 0;
    fsm_s = FSM_IDLE;

    return;
  }

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

  switch(fsm_s.read())
  {
  case FSM_IDLE:
    for (size_t i=0;i<m_nrq;i++)
    {
      if (sc_rq[i].cmdval.read())
      {
	selected_src_s = i;
	fsm_s = FSM_CMD;
	break;
      }
    }
    break;

  case FSM_CMD:
    if (sc_rq[selected_src_s].cmdval.read() &&
	sc_ctrl.cmdack.read())
      fsm_s = FSM_RSP;
    break;

  case FSM_RSP:
    if (sc_ctrl.rspval.read())
    {
      for (size_t i=0;i<m_nrq;i++)
      {
	if (sc_rq[i].cmdval.read())
	{
	  selected_src_s = i;
	  fsm_s = FSM_CMD;
	  break;
	}
	fsm_s = FSM_IDLE;
      }
    }
    break;

  default:
    break;
  }
}

void ScArbiter::genMealy_cmdval()
{
  unsigned int rq = selected_src_s.read();
  if (fsm_s.read() == FSM_CMD)
  {
    sc_ctrl.address = sc_rq[rq].address.read();
    sc_ctrl.be      = sc_rq[rq].be.read();
    sc_ctrl.mb      = sc_rq[rq].mb.read();
    sc_ctrl.cmd     = sc_rq[rq].cmd.read();
    sc_ctrl.cmdval  = sc_rq[rq].cmdval.read();
  }
  else
  {
    sc_ctrl.cmdval = false;
  }
}

void ScArbiter::genMealy_cmdack()
{
  if (fsm_s.read() == FSM_CMD)
  {
    for (size_t i=0;i<m_nrq;i++)
    {
      if (i == selected_src_s.read())
      {
	sc_rq[i].cmdack = sc_ctrl.cmdack.read();
      }
      else
      {
	sc_rq[i].cmdack = false;
      }
    }
  }
  else
  {
    for (size_t i=0;i<m_nrq;i++)
    {
      sc_rq[i].cmdack = false;
    }
  }
}

void ScArbiter::genMealy_rsp()
{
  if (fsm_s.read() == FSM_RSP)
  {
    for (size_t i=0;i<m_nrq;i++)
    {
      if (i == selected_src_s.read())
      {
	sc_rq[i].rspval = sc_ctrl.rspval.read();
      }
      else
      {
	sc_rq[i].rspval = false;
      }
    }
  }
  else
  {
    for (size_t i=0;i<m_nrq;i++)
    {
      sc_rq[i].rspval = false;
    }
  }
}
