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

MacController::MacController(sc_module_name name, ofstream * file, bool mac_cache_enabled): sc_module(name)
{
  _secbus_debug(cout, TRACE, "BUILDING MAC CONTROLLER ..."<<"\n");

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

  output = file;
  cache_enabled = mac_cache_enabled;

  key_s				= new int	[SP_SIZE];
  mac_s				= new int	[SIZE_64_IN_WORDS];
  reference_mac_s		= new int	[SIZE_64_IN_WORDS];

  _secbus_debug(cout, TRACE, "MAC CONTROLLER BUILT"<<"\n");
}

//------------------------------------------------------------------------- ps() --------------------------------------------------------------
void MacController::ps()
{
  _secbus_debug(*output, TRACE, dec<<"MAC CONTROLLER STATE: "<<state<<"\n");

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

  switch (state) 
  {
    case IDLE : //0
      if(cmd_in.enable)
      {
        memcpy( key_s, sc_register.sp, SP_SIZE_IN_BYTES);
        index_s              = 0;
        mac_page_size_s      = getSizeBit(sc_register.mpspe);
        cmd_in_s             = cmd_in.cmd;
        mac_start_address_s  = getBaseAddress(sc_register.mpspe);
        set_address_s        = cmd_in.address;
        mac_address_s        = getMacAddress(cmd_in.address, getBaseAddress(sc_register.mpspe), getSizeBit(sc_register.mpspe));
        //next state
        if		(cmd_in.cmd == UPDATE)			state = U_START_UPDATE_PARENT;
        else if (cmd_in.cmd == CHECK)			state = C_START_IV_GENERATION;
        else if (cmd_in.cmd == INITIALIZE)		state = I_START_READ_SET;
        else									cout<<"ERROR IN MAC CONTROLLER CMD"<<"\n";
      }
      break;

//------------------------------------------------------------------ check -------------------------------------------------------------

    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_in_ack)
        //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 (cache_enabled)
      {
        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_in_ack)
          {
            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_in_ack.rack)
        {
          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_in_ack)
          {
            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_in_ack)
        //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;

//------------------------------------------------------------------- initialize --------------------------------------------------------

    case I_START_READ_SET:
      state	= I_WAIT_MAC_GENERATION;
      break;

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

        if (index_s == MAC_ARITY)
          state   = I_START_WRITE_PARENT;
        else if (fill_64(register_2_in.be, set_address_s + index_s * SIZE_64_IN_BYTES))
          state	= I_START_MAC_GENERATION;
      }
      break;

    case I_START_MAC_GENERATION :
      index_s	= index_s + 1;	
      state	= I_WAIT_MAC_GENERATION;
      break;

    case I_START_WRITE_PARENT :
      state	= I_WAIT_WRITE_PARENT;
      break;

    case I_WAIT_WRITE_PARENT :
      if (cache_in_ack.wack)
      {
        set_address_s	= set_address_s + MS_SIZE_IN_BYTES;
        mac_address_s	= getMacAddress(set_address_s + MS_SIZE_IN_BYTES, mac_start_address_s, mac_page_size_s);
        index_s         = 0;

        if (mac_page_size_s == SIZE_4K)
          if ((set_address_s + MS_SIZE_IN_BYTES)%0x1000 == 0)
            state	= IDLE;
          else
            state	= I_START_READ_SET;
        else 
          if ((set_address_s + MS_SIZE_IN_BYTES)%0x400000 == 0)
            state	= IDLE;
          else
            state	= I_START_READ_SET;
      }
      break;

    case MAC_INTEGRITY_ERROR :
      _secbus_debug(cout, NONE, "ERROR : MAC INTEGRITY OPERATION"<<"\n");
      _secbus_debug(cout, NONE, "mac address : "<<hex<<mac_address_s<<"\n");
      _secbus_debug(cout, NONE, "set address : "<<hex<<set_address_s<<"\n");
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        _secbus_debug(cout, NONE, "mac : "<<hex<<mac_s[i]<<"\n");
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        _secbus_debug(cout, NONE, "reference mac : "<<hex<<reference_mac_s[i]<<"\n");
      for(unsigned int i=0;i<LAST_HS_SIZE;i++)
        _secbus_debug(cout, NONE, "set : "<<hex<<register_1_in.data[i]<<"\n");	
      //stop simulation
      sc_stop();
      break;

    default :
      break;  
  }
}


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

void MacController::pc()
{

  ack_out.cack			= false;
  ack_out.uack			= false;
  ack_out.initialize_ack	= false;
  cache_out_cmd.enable		= false;
  mac_generator_out_cmd.enable	= false;
  mac_generator_out_cmd.imode	= MAC;
  mac_generator_out_cmd.index	= 0;
  mac_generator_out_cmd.size	= MS_SIZE;
  mac_generator_out_iv.data[0]	= 0;
  mac_generator_out_iv.data[1]	= 0;
  mac_generator_out_cmd.address	= set_address_s;
  mac_generator_out_cmd.stage_0	= true;
  mac_generator_out_cmd.key	= key_s;
  memory_out_cmd.enable		= false;

  switch (state)
  {

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

    case C_START_IV_GENERATION :
      mac_count ++;
      mac_generator_out_cmd.enable	= 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.size	= SIZE_64_IN_WORDS;
      mac_generator_out_cmd.stage_0	= false;
      mac_generator_out_iv.data[0]	= mac_s[0];
      mac_generator_out_iv.data[1]	= mac_s[1];
      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(cache_enabled)
      {
        cache_out_cmd.enable		= true;
        cache_out_cmd.address		= mac_address_s;
        cache_out_cmd.cmd		= READ; 
      }
      else
      {
        memory_out_cmd.id		= 5;
        memory_out_cmd.enable		= 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.size	= SIZE_64_IN_WORDS;
      mac_generator_out_cmd.stage_0	= false;
      mac_generator_out_iv.data[0]	= mac_s[0];
      mac_generator_out_iv.data[1]	= mac_s[1];
      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 U_START_UPDATE_PARENT :
      //start mac generation
      mac_count++;
      mac_generator_out_cmd.enable	= true;
      mac_generator_out_cmd.address	= set_address_s;
      for(unsigned int i=0;i<MS_SIZE;i++)
        mac_generator_out_data.data[i]	= register_1_in.data[i];
      break;

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

    case I_START_READ_SET:
      memory_out_cmd.id			= 5;
      memory_out_cmd.enable		= true;
      memory_out_cmd.cmd		= READ;
      memory_out_cmd.start_address	= set_address_s;
      memory_out_cmd.plen		= MS_SIZE_IN_BYTES;
      //start hash generation
      mac_count++;
      mac_generator_out_cmd.enable	= true;
      mac_generator_out_cmd.size	= 0;
      mac_generator_out_cmd.address	= set_address_s;
      break;

    case I_START_MAC_GENERATION:
      mac_generator_out_cmd.enable	= true;
      mac_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      mac_generator_out_cmd.stage_0	= false;
      mac_generator_out_iv.data[0]	= mac_s[0];
      mac_generator_out_iv.data[1]	= mac_s[1];
      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 I_START_WRITE_PARENT :
      cache_out_cmd.enable		= true;
      cache_out_cmd.address		= mac_address_s;
      cache_out_cmd.cmd			= WRITE; 
      cache_out_wdata.data[0]		= mac_s[0];
      cache_out_wdata.data[1]		= mac_s[1];
      break;

    default :
      break;
  }

}


void MacController::print_stats(ofstream & file)
{
  _secbus_debug(file, NONE, "- MAC OPERATION	    		= " <<mac_count<<"\n");
}
void MacController::reset_stats()
{
  mac_count = 0;
}

