/*
 * 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_split.h"
#include "secbus_lib.h"

#define vci_split(x) template<typename vci_param> x VciSplit<vci_param>

vci_split(/**/)::VciSplit(sc_module_name name):sc_module(name)
{
  SC_METHOD(transition);
  dont_initialize();
  sensitive << p_clk.pos();

  SC_METHOD(genMealy_i2t);
  dont_initialize();
  sensitive << p_vci_initiator << fsm_s << sec_s;

  SC_METHOD(genMealy_t2i);
  dont_initialize();
  sensitive << p_vci_target_sec << p_vci_target_unsec << fsm_s << sec_s;

  DEBUG(NONE, STD, "[SECBUS] Building VciSplit (%s) \n", basename());
}

vci_split(void)::transition()
{
  if (!p_resetn)
  {
    fsm_s = FSM_IDLE;
    m_addr = 0;
    m_size = 0;
    m_enable = false;

    return;
  }

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

  m_addr = p_addr;
  m_size = p_size;
  m_enable = p_enable;

  switch(fsm_s)
  {
  case FSM_IDLE:
    if (p_vci_initiator.cmdval) // If initiator sends request
    {
      // If address falls in protected region and HSM enabled, security is required
      sec_s = inclusion(p_vci_initiator.address.read(), m_addr, m_addr + m_size) && m_enable;
      fsm_s = FSM_CMD;
    }
    break;

  case FSM_CMD:
    // If initiator sends last request of packet and target acknowledges
    if (p_vci_initiator.cmdval && p_vci_initiator.eop && ((sec_s && p_vci_target_sec.cmdack) || ((!sec_s) && p_vci_target_unsec.cmdack)))
    {
      fsm_s = FSM_RSP;
    }
    break;

  case FSM_RSP:
    // If a target sends last response of packet and initiator acknowledges
    if (((p_vci_target_sec.rspval && p_vci_target_sec.reop) || (p_vci_target_unsec.rspval && p_vci_target_unsec.reop)) && p_vci_initiator.rspack)
    {
      fsm_s = FSM_IDLE;
    }
    break;

  default:
    break;
  }
}

vci_split(void)::genMealy_i2t()
{
  // Initiator to targets forwarding
  i2t(p_vci_target_sec);
  i2t(p_vci_target_unsec);
  // Requests and response acknowledges to targets
  p_vci_target_sec.cmdval   = (fsm_s == FSM_CMD)  && sec_s    && p_vci_initiator.cmdval;
  p_vci_target_unsec.cmdval = (fsm_s == FSM_CMD)  && (!sec_s) && p_vci_initiator.cmdval;
  p_vci_target_sec.rspack   = (fsm_s != FSM_IDLE) && sec_s    && p_vci_initiator.rspack;
  p_vci_target_unsec.rspack = (fsm_s != FSM_IDLE) && (!sec_s) && p_vci_initiator.rspack;
}

vci_split(void)::genMealy_t2i()
{
  if (sec_s) // If current target is the secure one
  {
    t2i(p_vci_target_sec); // Forward responses from secure target to initiator
  }
  else // If current target is the unsecure one
  {
    t2i(p_vci_target_unsec); // Forward responses from unsecure target to initiator
  }
  // Responses and request acknowledges to initiator
  p_vci_initiator.cmdack =(fsm_s == FSM_CMD)  && ((sec_s &&  p_vci_target_sec.cmdack) || ((!sec_s) &&  p_vci_target_unsec.cmdack));
  p_vci_initiator.rspval =(fsm_s != FSM_IDLE) && ((sec_s &&  p_vci_target_sec.rspval) || ((!sec_s) &&  p_vci_target_unsec.rspval));
}
