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

MemArbiter::MemArbiter(sc_module_name name, size_t nrq):
  sc_module(name),
  p_clk("clk"),
  p_resetn("resetn")
{
  m_nrq = nrq;
  mem_rq = new MEMORY_CTRL_CMD_IN[m_nrq];

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

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

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

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

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

MemArbiter::~MemArbiter()
{
  delete mem_rq;
}

void MemArbiter::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 (mem_rq[i].cmdval.read())
      {
	selected_src_s = i;
	fsm_s = FSM_CMD;
	break;
      }
    }
    break;

  case FSM_CMD:
    if (mem_rq[selected_src_s].cmdval.read() &&
	mem_ctrl.cmdack.read())
    {
      fsm_s = FSM_RSP;
    }
    break;

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

  default:
    break;
  }
}

void MemArbiter::genMealy_cmdval()
{
  unsigned int rq = selected_src_s.read();
  if (fsm_s.read() == FSM_CMD)
  {
    mem_ctrl.start_address = mem_rq[rq].start_address.read();
    mem_ctrl.plen          = mem_rq[rq].plen.read();
    mem_ctrl.id            = mem_rq[rq].id.read();
    mem_ctrl.cmd           = mem_rq[rq].cmd.read();
    mem_ctrl.cmdval        = mem_rq[rq].cmdval.read();
    mem_ctrl.direct_write  = mem_rq[rq].direct_write.read();
  }
  else
  {
    mem_ctrl.cmdval = false;
  }
}

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

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