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

MS_Ctrl::MS_Ctrl(sc_module_name name)
  : sc_module(name)
{

  SC_METHOD(ps);
  sensitive << p_clk.pos();
  SC_METHOD(pc);
  sensitive << p_clk.neg();

  key_s				= new uint32_t[DES_KEY_SIZE_IN_WORDS];
  mac_s				= new uint32_t[SIZE_64_IN_WORDS];
  reference_mac_s		= new uint32_t[SIZE_64_IN_WORDS];

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

void MS_Ctrl::ps()
{
  DEBUG(TRACE, FSM, "[%s]\t\tFSM\t%u\n", basename(), state.read());

  if (!p_resetn) 
  {
    mac_count = 0;
    state = IDLE;
    m_cycle = 0;
    return;
  }

  switch (state) 
  {
    case IDLE : //0
      if(cmd_in.enable)
      {
	sp_t tmp_sp = SP_ALLOC();
	for (size_t i=0;i<SP_SIZE_IN_WORDS;i++)
	  tmp_sp[i] = sc_register.sp[i].read();

	sp_getKeyI(&tmp_sp, &key_s);
	
	src_s = cmd_in.src;
	
	pspe_t mpspe;
	mpspe[0] = sc_register.m_pspe[0].read();
	mpspe[1] = sc_register.m_pspe[1].read();	

        index_s              = 0;
        mac_page_size_s      = pspe_getSize(&mpspe);
        cmd_in_s             = cmd_in.cmd;
        mac_start_address_s  = pspe_getMSAdd(&mpspe)+(getPageSize(pspe_getSize(&mpspe))/4)*pspe_getMSIdx(&mpspe);//getBaseAddress(sc_register.mpspe);
        set_address_s        = cmd_in.address;
        mac_address_s        = ms_getMacAddress(cmd_in.address, 
						pspe_getMSAdd(&mpspe)+(getPageSize(pspe_getSize(&mpspe))/4)*pspe_getMSIdx(&mpspe),
						pspe_getSize(&mpspe));
        //next state
        if (cmd_in.cmd == CHECK)
	{
	  state = C_START_IV_GENERATION;
	}
        else if (cmd_in.cmd == UPDATE)		
	{
	  state = U_START_UPDATE_PARENT;
	}
        else
	  DEBUG(ERROR, STD, "Error in mac controller cmd\n");
      }
      break;

    case C_START_IV_GENERATION :
      state = C_WAIT_MAC_GENERATION;
      break;

    case C_START_MAC_GENERATION :
      index_s	= index_s + 1;	
      state	= C_WAIT_MAC_GENERATION;
      break;

    case C_WAIT_MAC_GENERATION :
      if (mac_generator_out_cmd.rspval)
        //New Mac value is available
      {
        mac_s[0] = mac_generator_in_data.data[0];
        mac_s[1] = mac_generator_in_data.data[1];

        if (fill_64(register_2_in.be, set_address_s + index_s * SIZE_64_IN_BYTES))
        {
          if (index_s == MAC_ARITY - 1)
            state	= C_START_READ_PARENT;
          else
            state	= C_START_MAC_GENERATION;
        }
      }
      break;

    case C_START_READ_PARENT :
      index_s	= index_s + 1;	
      state	= C_WAIT_READ_PARENT;
      break;

    case C_WAIT_READ_PARENT :
      if (p_cache.read())
      {
        if (cache_in_ack.rack)
        {
          reference_mac_s[0] = cache_in_rdata.data[0];
          reference_mac_s[1] = cache_in_rdata.data[1];

          if (mac_generator_out_cmd.rspval)
          {
            mac_s[0] = mac_generator_in_data.data[0];
            mac_s[1] = mac_generator_in_data.data[1];
            //Compare two Mac values
            if (compare(reference_mac_s, mac_s, SIZE_64_IN_BYTES))
              state = IDLE;
            else
              state = MAC_INTEGRITY_ERROR;
          }
        }
      }
      else
      {
        if (memory_out_cmd.rspval)
        {
          reference_mac_s[0] = register_2_in.data[index(mac_address_s, MS_SIZE_IN_BYTES)];
          reference_mac_s[1] = register_2_in.data[1 + index(mac_address_s, MS_SIZE_IN_BYTES)];

          if (mac_generator_out_cmd.rspval)
          {
            mac_s[0] = mac_generator_in_data.data[0];
            mac_s[1] = mac_generator_in_data.data[1];
            //Compare two Mac values
            if (compare(reference_mac_s, mac_s, SIZE_64_IN_BYTES))
              state = IDLE;
            else
              state = MAC_INTEGRITY_ERROR;
          }
        }
      }


      break;

//--------------------------- update ---------------------------------

    case U_START_UPDATE_PARENT :
      state = U_WAIT_UPDATE_PARENT;
      break;

    case U_WAIT_UPDATE_PARENT :
      if (mac_generator_out_cmd.rspval)
        //New Mac value is available
      {
        mac_s[0] = mac_generator_in_data.data[0];
        mac_s[1] = mac_generator_in_data.data[1];

        state = U_START_WRITE_PARENT;
      }
      break;

    case U_START_WRITE_PARENT :
      state = U_WAIT_WRITE_PARENT;
      break;

    case U_WAIT_WRITE_PARENT :
      if (cache_in_ack.wack)
      {
        state = IDLE;
      }
      break;

    case MAC_INTEGRITY_ERROR :
      DEBUG(ERROR, STD, "ERROR : MAC INTEGRITY OPERATION\n");
      DEBUG(ERROR, STD, "mac address : %x\n", mac_address_s.read());
      DEBUG(ERROR, STD, "set address : %x\n", set_address_s.read());
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        DEBUG(ERROR, STD, "mac : %x\n", mac_s[i]);
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        DEBUG(ERROR, STD, "reference mac : %x\n", reference_mac_s[i]);
      for(unsigned int i=0;i<MS_SIZE_IN_WORDS;i++)
        DEBUG(ERROR, STD, "set : %x\n", register_2_in.data[i].read());	
      //stop simulation
      sc_stop();
      break;

    default :
      break;  
  }
}


//---------------------------------------------------------------- pc() -----------------------------------------------------------------

void MS_Ctrl::pc()
{
  ack_out.cack			  = false;
  ack_out.uack			  = false;
  cache_out_cmd.enable		  = false;
  mac_generator_out_cmd.cmdval    = false;
  mac_generator_out_cmd.enable	  = false;
  mac_generator_out_cmd.mode	  = crypto_mac;
  mac_generator_out_cmd.reg_ready = true;
  mac_generator_out_cmd.size	  = MS_SIZE_IN_WORDS;
  mac_generator_out_cmd.address	  = set_address_s;
  mac_generator_out_cmd.no_init   = false;
  mac_generator_out_data.enable   = false;
  mac_generator_out_cmd.direct_data = true;
  memory_out_cmd.cmdval		  = false;
  memory_out_cmd.id               = 4;
  memory_out_cmd.direct_write     = false;
  memory_out_wdata.enable         = false;
  m_cycle++;

  switch (state)
  {

    case IDLE :
      ack_out.cack		= true;
      ack_out.uack		= true;
      break;

    case C_START_IV_GENERATION :
      mac_count ++;
      mac_generator_out_cmd.enable	= true;
      mac_generator_out_cmd.cmdval      = true;
      mac_generator_out_cmd.size	= 0;
      mac_generator_out_cmd.address	= set_address_s;
      break;

    case C_START_MAC_GENERATION :
      mac_generator_out_cmd.enable	= true;
      mac_generator_out_cmd.cmdval      = true;
      mac_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      mac_generator_out_cmd.no_init	= true;
      mac_generator_out_cmd.iv[0]	= mac_s[0];
      mac_generator_out_cmd.iv[1]	= mac_s[1];
      mac_generator_out_data.enable = true;
      mac_generator_out_data.data[0]	= register_2_in.data[index_s * SIZE_64_IN_WORDS];
      mac_generator_out_data.data[1]	= register_2_in.data[index_s * SIZE_64_IN_WORDS + 1];
      break;

    case C_START_READ_PARENT :
      if(p_cache.read())
      {
        cache_out_cmd.enable		= true;
        cache_out_cmd.address		= mac_address_s;
        cache_out_cmd.cmd		= MTC_READ; 
      }
      else
      {
        memory_out_cmd.cmdval		= true;
        memory_out_cmd.cmd		= READ;
        memory_out_cmd.start_address	= mac_address_s;
        memory_out_cmd.plen		= SIZE_64_IN_BYTES;
      }
      //start mac generation
      mac_generator_out_cmd.enable	= true;
      mac_generator_out_cmd.cmdval      = true;
      mac_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      mac_generator_out_cmd.no_init	= true;
      mac_generator_out_cmd.iv[0]	= mac_s[0];
      mac_generator_out_cmd.iv[1]	= mac_s[1];
      mac_generator_out_data.enable = true;
      switch(src_s.read())
      {
      case 0:
	mac_generator_out_data.data[0]	= register_0_in.data[index_s * SIZE_64_IN_WORDS];
	mac_generator_out_data.data[1]	= register_0_in.data[index_s * SIZE_64_IN_WORDS + 1];
	break;

      case 1:
	mac_generator_out_data.data[0]	= register_1_in.data[index_s * SIZE_64_IN_WORDS];
	mac_generator_out_data.data[1]	= register_1_in.data[index_s * SIZE_64_IN_WORDS + 1];
	break;

      case 2:
	mac_generator_out_data.data[0]	= register_2_in.data[index_s * SIZE_64_IN_WORDS];
	mac_generator_out_data.data[1]	= register_2_in.data[index_s * SIZE_64_IN_WORDS + 1];
	break;

      default:
	break;
      }
      break;

    case U_START_UPDATE_PARENT :
      //start mac generation
      mac_count++;
      mac_generator_out_cmd.enable	= true;
      mac_generator_out_cmd.cmdval      = true;
      mac_generator_out_cmd.address	= set_address_s;
      mac_generator_out_data.enable = true;
      switch(src_s.read())
      {
      case 0:
	for(unsigned int i=0;i<MS_SIZE_IN_WORDS;i++)
	{
	  mac_generator_out_data.data[i] = register_0_in.data[i];
	}
      break;

      case 1:
	for(unsigned int i=0;i<MS_SIZE_IN_WORDS;i++)
	{
	  mac_generator_out_data.data[i] = register_1_in.data[i];
	}
	break;
	
      case 2:
	for(unsigned int i=0;i<MS_SIZE_IN_WORDS;i++)
	{
	  mac_generator_out_data.data[i] = register_2_in.data[i];
	}
	break;

      default:
	break;
      }
      break;

    case U_START_WRITE_PARENT :
      cache_out_cmd.enable		= true;
      cache_out_cmd.address		= mac_address_s;
      cache_out_cmd.cmd			= MTC_WRITE; 
      cache_out_wdata.data[0]		= mac_s[0];
      cache_out_wdata.data[1]		= mac_s[1];
      break;

    default :
      break;
  }

}


void MS_Ctrl::print_stats()
{
  DEBUG(STAT, STD, "- MAC OPERATION = %u\n", mac_count);
}

void MS_Ctrl::reset_stats()
{
  mac_count = 0;
}

