/*
 * 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 "ht_controller.h"
HashTreeController::HashTreeController(sc_module_name name, ofstream * dfile, ofstream * sfile): sc_module(name)
{
  _secbus_debug(cout, TRACE, "BUILDING HASH TREE CONTROLLER ..."<<"\n");

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

  doutput = dfile;
  soutput = sfile;

  //allocation
  check_register_s = new int[LAST_HS_SIZE];
  rhv_s = new int[SIZE_64_IN_WORDS];
  hash_s = new int[SIZE_64_IN_WORDS];
  new_hash_s = new int[SIZE_64_IN_WORDS];
  reference_hash_s = new int[SIZE_64_IN_WORDS];
  new_spspe_s = new int[PSPE_SIZE];
  restore_fifo = new  GenericFifo<addr_t>(LAST_HS_SIZE);
  synchronize_fifo = new GenericFifo<addr_t>(LAST_HS_SIZE);
  M = new int [(MAX_TREE_LEVEL + 1) * SIZE_64_IN_WORDS];

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

//------------------------------------------------------ ps ---------------------------------------------------------------------------
//
void HashTreeController::ps()
{

  _secbus_debug(*doutput, TRACE, "HASH TREE CONTROLLER INITIALIZE STATE: "<<initialize_state<<"\n");
  _secbus_debug(*doutput, TRACE, "HASH TREE CONTROLLER CHECK STATE: "<<check_state<<"\n");
  _secbus_debug(*doutput, TRACE, "HASH TREE CONTROLLER UPDATE STATE: "<<update_state<<"\n");
  _secbus_debug(*doutput, TRACE, "HASH TREE CONTROLLER INIT STATE: "<<init_mht<<"\n");

  if (!reset) 
  {
    memset(rhv_s, 0, SIZE_64_IN_BYTES);
    memset(check_register_s, 0, LAST_HS_SIZE_IN_BYTES);
    memset(reference_hash_s, 0, SIZE_64_IN_BYTES);
    memset(hash_s, 0, SIZE_64_IN_BYTES);
    memset(new_hash_s, 0, SIZE_64_IN_BYTES);
    memset(reference_hash_s, 0, SIZE_64_IN_BYTES);
    memset(new_spspe_s, 0, PSPE_SIZE_IN_BYTES);
    memset(M, 0, (MAX_TREE_LEVEL + 1) * SIZE_64_IN_BYTES);
    update_state = HTU_IDLE;
    initialize_state = HTI_IDLE;
    hash_count = 0;
    mb_size_s = (SP_BLOCK_SIZE + PSPE_BLOCK_SIZE)/HS_SIZE_IN_BYTES;
    synchronize_address_s = PSPE_BASE_ADDRESS;
    brother_address_s = PSPE_BASE_ADDRESS;
    check_state = HTC_IDLE;
    init_mht = MHT_IDLE;
    mb_address = 0; //PSPE_BASE_ADDRESS;
	state_s = 0;
    return;
  }

//-------------------------------------------------------- initialize_state ----------------------------------------------
//
  switch (initialize_state) 
  {
	  case HTI_IDLE : 
		state_s = 1;
	  	  if(cmd_in.enable && cmd_in.cmd == INITIALIZE)
		{
			level_s				= 0;
			brother_cpt_s 		= 0;
			set_address_s		= cmd_in.address;
			brother_address_s	= getPageBaseAddress(getBaseAddress(sc_register_in.mpspe), getSizeBit(sc_register_in.mpspe));

			if (isMasterTree(cmd_in.address))
			{
				ht_start_address_s	= MHT_BASE_ADDRESS;
				ht_page_size_s		= SIZE_OTHER;
				master_tree_s		= true;
			}
			else
			{
				ht_start_address_s	= getBaseAddress(sc_register_in.mpspe);
				ht_page_size_s		= getSizeBit(sc_register_in.mpspe);
				master_tree_s		= false;
				memcpy(new_spspe_s, sc_register_in.alap_spspe, PSPE_SIZE_IN_BYTES);
			}

			initialize_state = I_START_RESTORE_BROTHER;
		}
		else if(cmd_in.enable && cmd_in.cmd == INITIALIZE_PAGE)
		{
			memcpy(new_spspe_s, sc_register_in.alap_spspe, PSPE_SIZE_IN_BYTES);
			memset(check_register_s, 0, LAST_HS_SIZE_IN_BYTES);
			brother_address_s	= getPageBaseAddress(getBaseAddress(sc_register_in.mpspe), getSizeBit(sc_register_in.mpspe));
			set_address_s 		= cmd_in.address;
			ht_start_address_s	= getBaseAddress(sc_register_in.mpspe);
			ht_page_size_s		= getSizeBit(sc_register_in.mpspe);
			master_tree_s		= false;
			level_s				= 0;
			brother_cpt_s 		= 0;
			initialize_state	= I_START_WRITE_NULL_BROTHER;

		}
	break;

      //Initialise Last Set 

    case I_START_WRITE_NULL_BROTHER:
		initialize_state = I_WAIT_WRITE_NULL_BROTHER;
	  break;	

    case I_WAIT_WRITE_NULL_BROTHER:
		if (memory_in_ack.wack)
		{
			if(brother_cpt_s == LAST_HASH_ARITY -1)
				initialize_state = I_START_READ_SET;
			else
			{
				brother_cpt_s 		= brother_cpt_s + 1;
				brother_address_s 	= brother_address_s + SIZE_64_IN_BYTES;
				initialize_state	= I_START_WRITE_NULL_BROTHER;
			}
		}
      break;

      //Check Last Set

    case I_START_RESTORE_BROTHER :
		initialize_state	= I_WAIT_RESTORE_BROTHER;
      break;

    case I_WAIT_RESTORE_BROTHER :
		if (hash_in_ack.rstack && hash_generator_in_ack)
		{
			check_register_s[brother_cpt_s * SIZE_64_IN_WORDS]		= hash_in_rdata.data[0];
			check_register_s[brother_cpt_s * SIZE_64_IN_WORDS + 1]	= hash_in_rdata.data[1];

			hash_s[0] = hash_generator_in_data.data[0];
			hash_s[1] = hash_generator_in_data.data[1];

			if (brother_cpt_s == LAST_HASH_ARITY -1)
			//Last brother
				initialize_state	= I_START_CHECK_LAST_SET;
			else
			{
				brother_address_s	= brother_address_s + SIZE_64_IN_BYTES;
				brother_cpt_s 		= brother_cpt_s + 1;
				initialize_state	= I_START_RESTORE_BROTHER;
			}
		}
      break;

    case I_START_CHECK_LAST_SET :
      getRHV(new_spspe_s, reference_hash_s);
      initialize_state	= I_WAIT_CHECK_LAST_SET;
      break;

    case I_WAIT_CHECK_LAST_SET :
      if (hash_generator_in_ack)
      {
        hash_s[0] = hash_generator_in_data.data[0] & 0xFFFFFFF8;
        hash_s[1] = hash_generator_in_data.data[1];
        //Compare two Hashes
        if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
          initialize_state	= I_START_READ_SET;
        else 
          initialize_state	= I_INTEGRITY_ERROR;
      }
      break;

      //Build HASH-TREE

    case I_START_READ_SET:
		w_parent_address_s	= getParentLeavesAddress(set_address_s, ht_start_address_s, ht_page_size_s);
		initialize_state	= I_WAIT_IV_GENERATION;
      break;

    case I_START_IV_GENERATION :
		if (isLastLevel(w_parent_address_s, ht_page_size_s))
		{
			level_s				= level_s + 1; 
			initialize_state	= I_WAIT_LAST_IV_GENERATION;
		}
		else
		{
			level_s				= level_s + 1; 
			initialize_state	= I_WAIT_IV_GENERATION;
			w_parent_address_s	= getParentAddress(w_parent_address_s, ht_page_size_s);
		}
      break;

    case I_WAIT_IV_GENERATION:
		if (hash_generator_in_ack)
		{
			M[level_s * SIZE_64_IN_WORDS]		= hash_generator_in_data.data[0];
			M[level_s * SIZE_64_IN_WORDS + 1]	= hash_generator_in_data.data[1];

			if (isLastLevel(w_parent_address_s, ht_page_size_s))
			{
				if	(master_tree_s && ((w_parent_address_s & 0x3F) == 0))
					initialize_state	= I_START_IV_GENERATION;
				else if (!master_tree_s && ((w_parent_address_s & 0xF) == 0))
					initialize_state	= I_START_IV_GENERATION;
				else if (memory_in_ack.rack)
				{
					w_parent_address_s	= getParentLeavesAddress(set_address_s, ht_start_address_s, ht_page_size_s);
					level_s			= 0;
					initialize_state	= I_START_UPDATE_LEAVE_PARENT;
				}
			}
			else if ((w_parent_address_s & 0x1F) == 0)
				initialize_state	= I_START_IV_GENERATION;
			else if (memory_in_ack.rack)
			{
				w_parent_address_s	= getParentLeavesAddress(set_address_s, ht_start_address_s, ht_page_size_s);
				level_s			= 0;
				initialize_state	= I_START_UPDATE_LEAVE_PARENT;
			}
		}	
      break;

    case I_WAIT_LAST_IV_GENERATION:
		if (hash_generator_in_ack)
		{
			M[level_s * SIZE_64_IN_WORDS]		= hash_generator_in_data.data[0];
			M[level_s * SIZE_64_IN_WORDS + 1]	= hash_generator_in_data.data[1];

			if (master_tree_s || ((w_parent_address_s & 0x3F) == 0))
			{
				w_parent_address_s	= getParentLeavesAddress(set_address_s, ht_start_address_s, ht_page_size_s);
				level_s			= 0;
				initialize_state	= I_START_UPDATE_LEAVE_PARENT;
			}		
			else 
			{
				index_s				= 0;
				initialize_state	= I_START_UPDATE_ROOT_M;
			}
		}	
		break;

    case I_START_UPDATE_LEAVE_PARENT :
		initialize_state	= I_WAIT_UPDATE_LEAVE_PARENT;
      break;

    case I_WAIT_UPDATE_LEAVE_PARENT :
		if (hash_generator_in_ack)
		{
			M[0]				= hash_generator_in_data.data[0];
			M[1]				= hash_generator_in_data.data[1];
			initialize_state	= I_START_UPDATE_PARENT_M;
		}
      break;

    case I_START_UPDATE_PARENT_M :
		initialize_state		= I_WAIT_UPDATE_PARENT_M;
		index_s					= getBlockIndex(w_parent_address_s, ht_page_size_s);
		level_s					= level_s + 1;
      break;

    case I_WAIT_UPDATE_PARENT_M :
		if (hash_generator_in_ack && memory_in_ack.wack)
		{
			M[level_s * SIZE_64_IN_WORDS]		= hash_generator_in_data.data[0];
			M[level_s * SIZE_64_IN_WORDS + 1]	= hash_generator_in_data.data[1];

			if (master_tree_s && (index_s == LAST_HASH_ARITY - 1))
			{
				initialize_state = HTI_IDLE;
				memcpy(MASTER_HV, M + level_s * SIZE_64_IN_WORDS, SIZE_64_IN_BYTES);	
			}
			else if (isLastLevel(w_parent_address_s, ht_page_size_s) && (index_s % SIZE_64_IN_WORDS == 1))
			{
				if (index_s ==  LAST_HASH_ARITY - 1)
				{
					setRHV(new_spspe_s, &M[level_s * SIZE_64_IN_WORDS]);
				//update PSPE
					initialize_state = I_START_WRITE_PSPE;
				}
				else
				{
					initialize_state	= I_START_UPDATE_ROOT_M;
					index_s				= index_s + 1;
				}
			}
			else if (index_s == (HASH_ARITY - 1))
			{	
				w_parent_address_s		= getParentAddress(w_parent_address_s, ht_page_size_s);
				initialize_state		= I_START_UPDATE_PARENT_M;
			}
			else
			{
				level_s					= 0;
				set_address_s			= set_address_s + HS_SIZE_IN_BYTES;
				initialize_state		= I_START_READ_SET;
			}
		}
      break;

    case I_START_UPDATE_ROOT_M :
      initialize_state		= I_WAIT_UPDATE_ROOT_M;
      break;

    case I_WAIT_UPDATE_ROOT_M :
      if (hash_generator_in_ack)
      {
        M[level_s * SIZE_64_IN_WORDS]		= hash_generator_in_data.data[0];
        M[level_s * SIZE_64_IN_WORDS + 1]	= hash_generator_in_data.data[1];

        if (index_s ==  LAST_HASH_ARITY - 1)
        {
          setRHV(new_spspe_s, &M[level_s * SIZE_64_IN_WORDS]);
          //update PSPE
          initialize_state = I_START_WRITE_PSPE;
        }
        else if (index_s == getBlockIndex(w_parent_address_s, ht_page_size_s) - 1)
        {
          w_parent_address_s	= getParentLeavesAddress(set_address_s, ht_start_address_s, ht_page_size_s);
          level_s				= 0;
          initialize_state	= I_START_UPDATE_LEAVE_PARENT;
        }
        else
        {
          index_s				= index_s + 1;
          initialize_state	= I_START_UPDATE_ROOT_M;
        }
      }
      break; 

    case I_START_WRITE_PSPE :
      initialize_state = I_WAIT_WRITE_PSPE;
      break;

    case I_WAIT_WRITE_PSPE:
      //PSPE written 
      if(sc_in_ack.wack)
        initialize_state = HTI_IDLE;
      break;

    case I_INTEGRITY_ERROR :
      _secbus_debug(cout, NONE, "ERROR : HASH INTEGRITY INITIALIZE OPERATION"<<"\n");
      _secbus_debug(cout, NONE, "parent address : "<<hex<<r_parent_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, "hash : "<<hex<<hash_s[i]<<"\n");
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        _secbus_debug(cout, NONE, "reference hash : "<<hex<<reference_hash_s[i]<<"\n");
      for(unsigned int i=0;i<LAST_HS_SIZE;i++)
        _secbus_debug(cout, NONE, "set : "<<hex<<check_register_s[i]<<"\n");
      //stop simulation
      sc_stop();
      break;

    default:
      break;
  }

  // masterblock hash tree initialization

  switch (init_mht)
  {
  	  case MHT_IDLE:
		state_s = 2;
      if (cmd_in.enable && cmd_in.cmd == INIT_MHT)
      {
        init_mht = MHT_INIT;
        master_tree_s = true;
      }
      break;

    case MHT_INIT:
      if (isLastLevel(brother_address_s, SIZE_OTHER))
      {
        set_address_s = getSetAddress(brother_address_s, SIZE_OTHER);
        init_mht = MHT_READ_LAST_LEVEL;
      }
      else
      {
        if (master_tree_s)
        {
          set_address_s = synchronize_address_s + mb_address*HS_SIZE_IN_BYTES;
          r_parent_address_s = getParentLeavesAddress(synchronize_address_s + mb_address*HS_SIZE_IN_BYTES, MHT_BASE_ADDRESS, SIZE_OTHER);
          init_mht = MHT_READ_SET;
          for (unsigned int i = 0;i<HS_SIZE;i++)
            check_register_s[i] = 0;

        }
        else
        {
          set_address_s = getSetAddress(brother_address_s, SIZE_OTHER);
          r_parent_address_s = getParentAddress(getSetAddress(brother_address_s, SIZE_OTHER), SIZE_OTHER);
          brother_address_s = brother_address_s + HS_SIZE_IN_BYTES;
          init_mht = MHT_READ_SET;
        }
      }
      break;

    case MHT_READ_SET:
      mb_address = mb_address + 1;
      init_mht = MHT_WAIT_READ_SET;
      break;

    case MHT_WAIT_READ_SET:
      if (memory_in_ack.rack)
      {
        for (unsigned int i = 0;i<HS_SIZE;i++)
          check_register_s[i] = register_2_in.data[i];

        init_mht = MHT_HASH_GEN;
      }
      break;

    case MHT_HASH_GEN:
      init_mht = MHT_WAIT_HASH_GEN;
      break;

    case MHT_WAIT_HASH_GEN:
      if (hash_generator_in_ack)
      {
        hash_s[0] = hash_generator_in_data.data[0];
        hash_s[1] = hash_generator_in_data.data[1];
        init_mht = MHT_WRITE_REF;
      }
      break;

    case MHT_WRITE_REF:
      init_mht = MHT_WAIT_WRITE_REF;
      break;

    case MHT_WAIT_WRITE_REF:
      if (memory_in_ack.wack)
      {
        if (mb_address < mb_size_s)
        {
          if (master_tree_s)
          {
            set_address_s = synchronize_address_s + mb_address*HS_SIZE_IN_BYTES;
            r_parent_address_s = getParentLeavesAddress(synchronize_address_s + mb_address*HS_SIZE_IN_BYTES, MHT_BASE_ADDRESS, SIZE_OTHER);
          }
          else
          {
            set_address_s = getSetAddress(brother_address_s, SIZE_OTHER);
            r_parent_address_s = getParentAddress(getSetAddress(brother_address_s,SIZE_OTHER), SIZE_OTHER);
            brother_address_s = brother_address_s + HS_SIZE_IN_BYTES;
          }
          init_mht = MHT_READ_SET;
        }
        else
        {
          mb_size_s = mb_size_s / HASH_ARITY;
          mb_address = 0;
          if (master_tree_s)
          {
            synchronize_address_s = getParentLeavesAddress(getSetAddress(synchronize_address_s,SIZE_OTHER),MHT_BASE_ADDRESS, SIZE_OTHER);
            brother_address_s = getParentLeavesAddress(synchronize_address_s, MHT_BASE_ADDRESS, SIZE_OTHER);
          }
          else
          {
            synchronize_address_s = getParentAddress(getSetAddress(synchronize_address_s,SIZE_OTHER), SIZE_OTHER);
            brother_address_s = getParentAddress(getSetAddress(synchronize_address_s,SIZE_OTHER), SIZE_OTHER);
          }
          master_tree_s = false;
          init_mht = MHT_INIT;
        }
      }
      break;

    case MHT_READ_LAST_LEVEL:
      init_mht = MHT_WAIT_READ_LAST_LEVEL;
      break;

    case MHT_WAIT_READ_LAST_LEVEL:
      if (memory_in_ack.rack)
      {
        for (unsigned int i = 0;i<SIZE_256_IN_WORDS;i++)
          check_register_s[i] = register_2_in.data[i];

        init_mht = MHT_READ_LAST_LEVEL2;
      }
      break;

    case MHT_READ_LAST_LEVEL2:
      init_mht = MHT_WAIT_READ_LAST_LEVEL2;
      break;

    case MHT_WAIT_READ_LAST_LEVEL2:
      if (memory_in_ack.rack)
      {
        for (unsigned int i = 0;i<LAST_HS_SIZE-SIZE_256_IN_WORDS;i++)
          check_register_s[SIZE_256_IN_WORDS+i] = register_2_in.data[i];

        init_mht = MHT_HASH_LAST_LEVEL;
      }
      break;

    case MHT_HASH_LAST_LEVEL:
      init_mht = MHT_WAIT_HASH_LAST_LEVEL;
      break;

    case MHT_WAIT_HASH_LAST_LEVEL:
      if (hash_generator_in_ack)
      {
        int a[2];
        a[0] = hash_generator_in_data.data[0];
        a[1] = hash_generator_in_data.data[1];
        memcpy(MASTER_HV, a, SIZE_64_IN_BYTES);
        init_mht = MHT_IDLE;
      }
      break;
  }

//---------------------------------------------------- update_state ---------------------------------------------
//
  switch (update_state) 
  {
		case HTU_IDLE : 
			state_s = 3;
			if(cmd_in.enable && cmd_in.cmd == UPDATE)
			{
				m_cpt_u = m;
				leaves_s = true;
				victim_address_s = cmd_in.address;
				set_address_s	= cmd_in.address;
				
				if (isMasterTree(cmd_in.address))
				{
					ht_start_address_s	= MHT_BASE_ADDRESS;
					ht_page_size_s		= SIZE_OTHER;
					master_tree_s		= true;
					getRHV(MASTER_HV, rhv_s);
				}
				else //--
				{									
					ht_start_address_s	= getBaseAddress(sc_register_in.mpspe);
					ht_page_size_s		= getSizeBit(sc_register_in.mpspe);
					master_tree_s		= false;
					getRHV(sc_register_in.alap_spspe, rhv_s);
				}
				
				//Parent address to update
				update_state = U_START_PARENT_UPDATE;	
			}
		break;

		case U_START_PARENT_UPDATE:	//--
			update_state = U_WAIT_PARENT_UPDATE;
		break;

		case U_WAIT_PARENT_UPDATE:
			if (hash_generator_in_ack)
			{
				new_hash_s[0] = hash_generator_in_data.data[0];
				new_hash_s[1] = hash_generator_in_data.data[1];

				if (leaves_s)//--
				{
					leaves_s			= false;
					w_parent_address_s	= getParentLeavesAddress(victim_address_s, ht_start_address_s, ht_page_size_s);
				}
				else
				{	
					w_parent_address_s = getParentAddress(victim_address_s, ht_page_size_s);
				}

				update_state = U_START_WRITE_NEW_PARENT;
			}
      break;

    case U_START_WRITE_NEW_PARENT : //--
      restore_fifo->init();
      synchronize_fifo->init();
      update_state = U_WAIT_WRITE_NEW_PARENT;
      break;

    case U_WAIT_WRITE_NEW_PARENT :
      if (hash_in_ack.wack) //--
      {
        if (hash_in_ack.full)
          //Cache dirty threshold reached
        {
          victim_address_s = hash_in_ack.victim_address;
          
		  //reset Hit and brother counter
          hit_cpt_s		= 0;
          brother_cpt_s	= 0;

          if (isMasterTree(hash_in_ack.victim_address))
            //address belongs to master tree
          {
            memcpy(rhv_s, MASTER_HV, SIZE_64_IN_BYTES);
            ht_start_address_s	= MHT_BASE_ADDRESS;
            ht_page_size_s		= SIZE_OTHER;
            master_tree_s		= true;
            brother_address_s	= getSetAddress(hash_in_ack.victim_address, SIZE_OTHER);
            update_state		= U_START_READ_VICTIM_SET;
          }
          else
          {
            if (getSizeBit(sc_register_in.alap_spspe)== SIZE_4K)
            {
              if (get4kPageAddress(hash_in_ack.victim_address) == sc_register_in.alap_page_address)
                //sPSPE is already in ALAP register
              {
                getRHV(sc_register_in.alap_spspe, rhv_s);
                ht_start_address_s	= hash_in_ack.victim_address;
                ht_page_size_s		= SIZE_4K;
                master_tree_s		= false;
                brother_address_s	= getSetAddress(hash_in_ack.victim_address, SIZE_4K);
                update_state		= U_START_READ_VICTIM_SET;
              }
              else
                update_state		= U_START_READ_SLAVE_PSPE;
            }
            else if (getSizeBit(sc_register_in.alap_spspe) == SIZE_4M)
            {
              if (get4mPageAddress(hash_in_ack.victim_address) == sc_register_in.alap_page_address)
                //sPSPE is already in ALAP register
              {
                getRHV(sc_register_in.alap_spspe, rhv_s);
                ht_start_address_s	= hash_in_ack.victim_address;
                ht_page_size_s		= SIZE_4M;
                master_tree_s		= false;
                brother_address_s	= getSetAddress(hash_in_ack.victim_address, SIZE_4M);
                update_state		= U_START_READ_VICTIM_SET;
              }
              else
                update_state		= U_START_READ_SLAVE_PSPE;
            }
          }
        }
        else //--
        {
          update_state = HTU_IDLE;
          _secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_u<<" HT_U "<<m - m_cpt_u<<" cycles\n");
        }
      }
      break;

    case U_START_READ_SLAVE_PSPE :
      update_state = U_WAIT_READ_SLAVE_PSPE;
      break;

    case U_WAIT_READ_SLAVE_PSPE :
      if (sc_in_ack.rack && check_state == HTC_IDLE)
      {
        getRHV(sc_register_in.alap_spspe, rhv_s);
        ht_start_address_s	= victim_address_s;
        ht_page_size_s		= getSizeBit(sc_register_in.alap_spspe);
        brother_address_s	= getSetAddress(victim_address_s, getSizeBit(sc_register_in.alap_spspe));		
        master_tree_s		= false;
        hit_cpt_s			= 0;
        brother_cpt_s		= 0;
        update_state		= U_START_READ_VICTIM_SET;
      }
      break;

    case U_START_READ_VICTIM_SET :
      set_address_s	= getSetAddress(victim_address_s, ht_page_size_s);
      update_state	= U_WAIT_READ_VICTIM_HASH_SET;
      break;

    case U_WAIT_READ_VICTIM_HASH_SET :
      if (hash_in_ack.rack && hash_generator_in_ack)
      {
        if (hash_in_ack.hit)
          //Increment Hit counter
          hit_cpt_s = hit_cpt_s + 1;
        if (hash_in_ack.dirty)
        {
          //store brother address in dirty fifo
          restore_fifo->simple_put(brother_address_s);
          synchronize_fifo->simple_put(brother_address_s);
        }

        check_register_s[brother_cpt_s * SIZE_64_IN_WORDS]		= hash_in_rdata.data[0];
        check_register_s[brother_cpt_s * SIZE_64_IN_WORDS + 1]	= hash_in_rdata.data[1];

        hash_s[0] = hash_generator_in_data.data[0];
        hash_s[1] = hash_generator_in_data.data[1];

        if (isLastLevel(victim_address_s, ht_page_size_s))
        {
          if (brother_cpt_s == LAST_HASH_ARITY - 1)
            //Last brother
            update_state		= U_START_NEW_HASH_GENERATION;
          else
          {
            brother_address_s	= brother_address_s + SIZE_64_IN_BYTES;
            brother_cpt_s 		= brother_cpt_s + 1;
            update_state		= U_START_READ_VICTIM_SET;
          }
        }
        else
        {
          if (brother_cpt_s == HASH_ARITY - 1)
            update_state		= U_START_NEW_HASH_GENERATION;
          else
            //We continue to read brother
          {
            brother_address_s	= brother_address_s + SIZE_64_IN_BYTES;
            brother_cpt_s 		= brother_cpt_s + 1;
            update_state		= U_START_READ_VICTIM_SET;
          }
        }
      }
      break;


    case U_START_NEW_HASH_GENERATION:
      update_state		= U_WAIT_NEW_HASH_GENERATION;
      break;

    case U_WAIT_NEW_HASH_GENERATION:
      if (hash_generator_in_ack)
      {
        new_hash_s[0] = hash_generator_in_data.data[0];
        new_hash_s[1] = hash_generator_in_data.data[1];


        if (isLastLevel(victim_address_s, ht_page_size_s))
        {
          if (hit_cpt_s == LAST_HASH_ARITY)
            //Hit on all brothers
          {
            synchronize_address_s	= synchronize_fifo->read();
            update_state			= U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS;
          }
          else
            //we must restore dirty adresses stored in fifo
          {
            restore_address_s	= restore_fifo->read();
            update_state		= U_START_RESTORE_DIRTY_VICTIM_BROTHERS;
          }
        }
        else
        {
          //We have read all brothers
          if (hit_cpt_s == HASH_ARITY)
            //Hit on all brothers
          {
            synchronize_address_s	= synchronize_fifo->read();
            update_state			= U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS;
          }
          else
            //we must restore dirty adresses stored in fifo
          {
            restore_address_s	= restore_fifo->read();
            update_state		= U_START_RESTORE_DIRTY_VICTIM_BROTHERS;
          }
        }
      }
      break;

    case U_START_RESTORE_DIRTY_VICTIM_BROTHERS : 
      //Decrease fifo read pointer
      restore_fifo->simple_get();
      index_s		= getBlockIndex(restore_address_s, ht_page_size_s);
      update_state	= U_WAIT_RESTORE_DIRTY_VICTIM_BROTHERS;
      break;

    case U_WAIT_RESTORE_DIRTY_VICTIM_BROTHERS :
      if (hash_in_ack.rstack)
      {
        check_register_s[index_s * SIZE_64_IN_WORDS]		= hash_in_rdata.data[0];
        check_register_s[index_s * SIZE_64_IN_WORDS + 1]	= hash_in_rdata.data[1];

        if (isLastLevel(victim_address_s, ht_page_size_s))
        {
          if (restore_fifo->empty() )
            //whole dirty value restored
          {
            r_parent_address_s	= getParentAddress(victim_address_s, ht_page_size_s);
            //Copy root hash value in reference value
            memcpy(reference_hash_s, rhv_s, SIZE_64_IN_BYTES);
            update_state = U_START_LAST_IC;
          }
          else
            //We continue to restore
          {
            restore_address_s	= restore_fifo->read();
            update_state		= U_START_RESTORE_DIRTY_VICTIM_BROTHERS;
          }
        }
        else
        {
          if (restore_fifo->empty())
            //whole dirty value restored
          {
            r_parent_address_s	= getParentAddress(victim_address_s, ht_page_size_s);
            update_state		= U_START_READ_REFERENCE_PARENT;
          }
          else
            //We continue to restore
          {
            restore_address_s	= restore_fifo->read();
            update_state		= U_START_RESTORE_DIRTY_VICTIM_BROTHERS;
          }
        }
      }
      break;

    case U_START_READ_REFERENCE_PARENT:
      update_state 	= U_WAIT_READ_REFERENCE_PARENT;
      index_s 		= getBlockIndex(r_parent_address_s, ht_page_size_s);
      break;

    case U_WAIT_READ_REFERENCE_PARENT :
      if (hash_in_ack.rack)
      {
        reference_hash_s[0] = hash_in_rdata.data[0];
        reference_hash_s[1] = hash_in_rdata.data[1];

        if (hash_in_ack.hit)
          //no need to restore brother
        {		
          if (hash_generator_in_ack)
          {
            hash_s[0] = hash_generator_in_data.data[0];
            hash_s[1] = hash_generator_in_data.data[1];
            //Compare two Hashes
            if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
              //synchronize diry values stored in fifo
            {
              synchronize_address_s	= synchronize_fifo->read();
              update_state			= U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS;
            }
            else update_state = U_INTEGRITY_ERROR;
          }
        }
        else
          //Cache Miss
        {
          //Copy reference_hash_s value in check_register_s
          check_register_s[index_s * SIZE_64_IN_WORDS]		= hash_in_rdata.data[0];
          check_register_s[index_s * SIZE_64_IN_WORDS + 1] 	= hash_in_rdata.data[1];

          brother_cpt_s		= 0;
          brother_address_s	= getBrotherAddress(r_parent_address_s, ht_page_size_s);
          update_state		= U_START_RESTORE_REFERENCE_PARENT_BROTHERS;
          set_address_s		= getSetAddress(r_parent_address_s, ht_page_size_s);
        }
      }
      break;

    case U_START_LAST_IC:
      update_state = U_WAIT_LAST_IC;
      break;

    case  U_WAIT_LAST_IC :
      if (hash_generator_in_ack)
      {
        hash_s[0] = hash_generator_in_data.data[0];
        hash_s[1] = hash_generator_in_data.data[1];
        if (!master_tree_s)
          //Last three validity, type and size bits must be masked before comparaison
          hash_s[0] = (hash_s[0] & 0xFFFFFFF8);
        //Compare two Hashes
        if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
        {
          synchronize_address_s	= synchronize_fifo->read();
          update_state			= U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS;
        }
        else update_state = U_INTEGRITY_ERROR;
      }
      break;


    case U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS :
      //Decrease fifo read pointer
      synchronize_fifo->simple_get();
	  index_s		= getBlockIndex(synchronize_address_s, ht_page_size_s)*SIZE_64_IN_WORDS;
      update_state	= U_WAIT_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS;
      break;

    case U_WAIT_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS :
      if (hash_in_ack.sack)
      {
        if (isLastLevel(victim_address_s, ht_page_size_s))
        {
          if (synchronize_fifo->empty())
          {
            if (master_tree_s)
            {
              update_state = HTU_IDLE;	
              memcpy(MASTER_HV, new_hash_s, SIZE_64_IN_BYTES);
              _secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_u<<" HT_U "<<m - m_cpt_u<<" cycles\n");
            }
            else
            {
              //create new_spspe_s
              setSizeBit	(new_spspe_s, getSizeBit(sc_register_in.alap_spspe));
              setTypeBit	(new_spspe_s, getTypeBit(sc_register_in.alap_spspe));
              setValidBit	(new_spspe_s, getValidBit(sc_register_in.alap_spspe));
              setRHV		(new_spspe_s, new_hash_s);

              //update PSPE
              update_state = U_START_WRITE_PSPE;
            }
          }
          else
            //Some dirty values are still not synchronized	
          {
            synchronize_address_s	= synchronize_fifo->read();
            update_state			= U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS;
          }
        }
        else
        {
          if (synchronize_fifo->empty())
          {
            w_parent_address_s		= getParentAddress(victim_address_s, ht_page_size_s);
            update_state			= U_START_WRITE_NEW_PARENT;
          }
          else
            //Some dirty values are still not synchronized	
          {
            synchronize_address_s	= synchronize_fifo->read();
            update_state			= U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS;
          }
        }
      }
      break;

    case U_START_WRITE_PSPE :
      update_state = U_WAIT_WRITE_PSPE;
      break;

    case U_WAIT_WRITE_PSPE :
      if(cmd_in.enable && cmd_in.cmd == UPDATE)
      {
        //store input in registers
        ht_start_address_s	= MHT_BASE_ADDRESS;
        victim_address_s	= cmd_in.address;
        master_tree_s		= true;
        leaves_s			= true;
        ht_page_size_s		= SIZE_OTHER;
        set_address_s		= cmd_in.address;
        memcpy(rhv_s, MASTER_HV, SIZE_64_IN_BYTES);	
        //Parent address to update
        update_state = U_START_PARENT_UPDATE;
      }
      break;


    case U_START_RESTORE_REFERENCE_PARENT_BROTHERS :
      index_s			= getBlockIndex(brother_address_s, ht_page_size_s);
      update_state	= U_WAIT_RESTORE_REFERENCE_PARENT_BROTHERS;
      break;

    case U_WAIT_RESTORE_REFERENCE_PARENT_BROTHERS :
      if (hash_in_ack.rstack)
      {
        check_register_s[index_s * SIZE_64_IN_WORDS]		= hash_in_rdata.data[0];
        check_register_s[index_s * SIZE_64_IN_WORDS + 1]	= hash_in_rdata.data[1];

        if (isLastLevel(r_parent_address_s, ht_page_size_s))
        {
          if (brother_cpt_s == (LAST_HASH_ARITY - 2))
            //whole set restored
          {	
            if (hash_generator_in_ack)
            {
              hash_s[0] = hash_generator_in_data.data[0];
              hash_s[1] = hash_generator_in_data.data[1];
              //Compare two Hashes
              if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
              {
                //Copy root hash value in reference value
                memcpy(reference_hash_s, rhv_s, SIZE_64_IN_BYTES);
                update_state		= U_START_LAST_IC;
              }
              else update_state = U_INTEGRITY_ERROR;
            }

          }
          else
            //We continue to restore
          {
            brother_cpt_s		= brother_cpt_s + 1;
            brother_address_s	= getBrotherAddress(brother_address_s, ht_page_size_s);
            update_state		= U_START_RESTORE_REFERENCE_PARENT_BROTHERS;
          }
        }
        else
        {
          if (brother_cpt_s == (HASH_ARITY - 2))
            //we have restored the whole set
          {
            if (hash_generator_in_ack)
            {
              hash_s[0] = hash_generator_in_data.data[0];
              hash_s[1] = hash_generator_in_data.data[1];
              //Compare two Hashes
              if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
              {
                r_parent_address_s	= getParentAddress(r_parent_address_s, ht_page_size_s);
                update_state		= U_START_READ_REFERENCE_PARENT;
              }
              else update_state		= U_INTEGRITY_ERROR;

            }
          }
          else
            //We continue to restore brothers
          {
            brother_cpt_s		= brother_cpt_s + 1;
            brother_address_s	= getBrotherAddress(brother_address_s, ht_page_size_s);
            update_state		= U_START_RESTORE_REFERENCE_PARENT_BROTHERS;
          }
        }
      }
      break;

    case U_INTEGRITY_ERROR :
      _secbus_debug(cout, NONE, "ERROR : HASH INTEGRITY WRITE OPERATION"<<"\n");
      _secbus_debug(cout, NONE, "parent address : "<<hex<<r_parent_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, "hash : "<<hex<<hash_s[i]<<"\n");
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        _secbus_debug(cout, NONE, "reference hash : "<<hex<<reference_hash_s[i]<<"\n");
      for(unsigned int i=0;i<LAST_HS_SIZE;i++)
        _secbus_debug(cout, NONE, "set : "<<hex<<check_register_s[i]<<"\n");
      //stop simulation
      sc_stop();
      break;
  }

//------------------------------------------------------------- check_state -------------------------------------------
  switch (check_state) 
  {
  	  case HTC_IDLE : 
		state_s = 4;
  
  	  if(cmd_in.enable && cmd_in.cmd == CHECK)
      {
        m_cpt_c = m;
        index_s                 = 0;
        set_address_s			= cmd_in.address;
        ht_start_address_s		= getBaseAddress(sc_register_in.mpspe);
        ht_page_size_s			= getSizeBit(sc_register_in.mpspe);
        master_tree_s			= isMasterTree(cmd_in.address);

        if (isMasterTree(cmd_in.address))
          memcpy(rhv_s, MASTER_HV, SIZE_64_IN_BYTES); // MASTER_HV : 0x80e3a8
        else
          getRHV(sc_register_in.alap_spspe, rhv_s); //ALAP_SPSPE : 0x25c3b78

        //Parent address
        check_state = C_START_IV_GENERATION;
      }
      break;


    case C_START_IV_GENERATION :
      check_state = C_WAIT_HASH_GENERATION;
      break;

    case C_START_HASH_GENERATION :
      index_s	        = index_s + 1;	
      check_state	    = C_WAIT_HASH_GENERATION;
      break;

    case C_WAIT_HASH_GENERATION :
      if (hash_generator_in_ack)
        //New value is available
      {
        hash_s[0] = hash_generator_in_data.data[0];
        hash_s[1] = hash_generator_in_data.data[1];

        if (fill_64(register_2_in.be, set_address_s + index_s * SIZE_64_IN_BYTES))
        {
          if (index_s == HASH_ARITY - 1)
          { 
            check_state	        = C_START_READ_REFERENCE_PARENT;
            r_parent_address_s	= getParentLeavesAddress(set_address_s, ht_start_address_s, ht_page_size_s);
		  }
		  else
            check_state	= C_START_HASH_GENERATION;
        }
      }
      break;


    case  C_START_READ_REFERENCE_PARENT :
      check_state 	= C_WAIT_READ_REFERENCE_PARENT;
      index_s 	    = getBlockIndex(r_parent_address_s, ht_page_size_s);
      break;

    case  C_START_CHECK_REFERENCE_PARENT :
      check_state 	= C_WAIT_READ_REFERENCE_PARENT;
      index_s 	    = getBlockIndex(r_parent_address_s, ht_page_size_s);
      break;

    case C_WAIT_READ_REFERENCE_PARENT :
      if (hash_in_ack.rack)
      {
        reference_hash_s[0] = hash_in_rdata.data[0];
        reference_hash_s[1] = hash_in_rdata.data[1];

        if (hash_in_ack.hit)
          //Cache Hit: no need to restore brother
        {
          if (hash_generator_in_ack)
          {
            hash_s[0] = hash_generator_in_data.data[0];
            hash_s[1] = hash_generator_in_data.data[1];
            //Compare two Hashes
            if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
            {
              check_state = HTC_IDLE;
              _secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_c<<" HT_C "<<m - m_cpt_c<<" cycles\n");
            }
            else check_state = C_INTEGRITY_ERROR;
          }
        }
        else
          //Cache Miss
        {
          if (hash_generator_in_ack)
          {
            hash_s[0] = hash_generator_in_data.data[0];
            hash_s[1] = hash_generator_in_data.data[1];
            //Compare two Hashes
            if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
            {
              brother_cpt_s = 0;
              //Copy reference hash in check_register_s
              check_register_s[index_s * SIZE_64_IN_WORDS]	    = hash_in_rdata.data[0];
              check_register_s[index_s * SIZE_64_IN_WORDS + 1] 	= hash_in_rdata.data[1];
              //Compute brother address
              brother_address_s = getBrotherAddress(r_parent_address_s, ht_page_size_s);
              check_state = C_START_RESTORE_REFERENCE_PARENT_BROTHERS;
            }
            else check_state = C_INTEGRITY_ERROR;
          }
        }
      }
      break;

    case C_START_RESTORE_REFERENCE_PARENT_BROTHERS :
      index_s = getBlockIndex(brother_address_s, ht_page_size_s)*SIZE_64_IN_WORDS;
      check_state = C_WAIT_RESTORE_REFERENCE_PARENT_BROTHERS;
      break;

    case C_WAIT_RESTORE_REFERENCE_PARENT_BROTHERS :
      if (hash_in_ack.rstack)
      {
        check_register_s[index_s]		    = hash_in_rdata.data[0];
        check_register_s[index_s + 1]       = hash_in_rdata.data[1];

        if (isLastLevel(r_parent_address_s, ht_page_size_s))
        {
          if (brother_cpt_s == (LAST_HASH_ARITY - 2))
            //whole set restored
          {
            r_parent_address_s	= getParentAddress(r_parent_address_s, ht_page_size_s);
            set_address_s		= getSetAddress(r_parent_address_s, ht_page_size_s);
            check_state			= C_START_LAST_IC;
          }
          else
            //Some brothers are still missing	
          {
            brother_cpt_s		= brother_cpt_s + 1;
            brother_address_s	= getBrotherAddress(brother_address_s, ht_page_size_s);
            check_state			= C_START_RESTORE_REFERENCE_PARENT_BROTHERS;
          }
        }
        else
        {
          if (brother_cpt_s == (HASH_ARITY - 2))
            //whole set restored
          {
            r_parent_address_s	= getParentAddress(r_parent_address_s, ht_page_size_s);
            set_address_s		= getSetAddress(r_parent_address_s, ht_page_size_s);
            check_state			= C_START_CHECK_REFERENCE_PARENT;
          }
          else
            //Some brothers are still missing	
          {
            brother_cpt_s		= brother_cpt_s + 1;
            brother_address_s	= getBrotherAddress(brother_address_s, ht_page_size_s);
            check_state			= C_START_RESTORE_REFERENCE_PARENT_BROTHERS;
          }
        }
      }
      break;

    case  C_START_LAST_IC :
      memcpy(reference_hash_s, rhv_s, SIZE_64_IN_BYTES);;
      check_state =  C_WAIT_LAST_IC;
      break;

    case  C_WAIT_LAST_IC :
      if (hash_generator_in_ack)
      {
        hash_s[0] = hash_generator_in_data.data[0];
        hash_s[1] = hash_generator_in_data.data[1];
        if (!master_tree_s)
          //Last three validity, type and size bits must be masked before comparaison
          hash_s[0] = hash_s[0] & 0xFFFFFFF8;
        //Compare two Hashes
        if (compare(hash_s, reference_hash_s, SIZE_64_IN_BYTES))
        {
          check_state = HTC_IDLE;
          _secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_c<<" HT_C "<<m - m_cpt_c<<" cycles\n");
        }
        else
          check_state = C_INTEGRITY_ERROR;
      }
      break;

    case C_INTEGRITY_ERROR :
      _secbus_debug(cout, NONE, "ERROR : HASH INTEGRITY READ OPERATION"<<"\n");
      _secbus_debug(cout, NONE, "parent address : "<<hex<<r_parent_address_s<<"\n");
      _secbus_debug(cout, NONE, "set address : "<<hex<<set_address_s<<"\n");
      _secbus_debug(cout, NONE, "pspe address : "<<hex<<sc_register_in.alap_spspe_address<<"\n");
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        _secbus_debug(cout, NONE, "hash : "<<hex<<hash_s[i]<<"\n");
      for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
        _secbus_debug(cout, NONE, "reference hash : "<<hex<<reference_hash_s[i]<<"\n");
      for(unsigned int i=0;i<LAST_HS_SIZE;i++)
        _secbus_debug(cout, NONE, "set : "<<hex<<check_register_s[i]<<"\n");
      //stop simulation
      sc_stop();
      break;


    default :
      break;
  }
}

//------------------------------------------------------------ pc ------------------------------------------------------------------
void HashTreeController::pc()
{
  hash_out_cmd.enable			= false;
  sc_out_cmd.enable			= false;
  memory_out_cmd.enable			= false;
  ack_out.cack				= false;
  ack_out.uack				= false;
  ack_out.alap_ack			= false;
  ack_out.initialize_ack		= false;
  hash_generator_out_cmd.enable		= false;
  hash_generator_out_cmd.imode		= HASH_TREE;
  hash_generator_out_cmd.address	= set_address_s;
  hash_generator_out_cmd.stage_0	= true;
  hash_generator_out_iv.data[0]		= 0;
  hash_generator_out_iv.data[1]		= 0;
  memory_out_cmd.id			= 4;
  ack_out.init_mbht_ack   		= false;

//----------------------------------------------------- init_mht
  switch (init_mht)
  {
    case MHT_IDLE:
      ack_out.init_mbht_ack = true;
      break;

    case MHT_INIT:
      break;

    case MHT_READ_SET:
      memory_out_cmd.enable = true;
      memory_out_cmd.cmd = READ;
      memory_out_cmd.start_address = set_address_s.read();
      memory_out_cmd.plen = HS_SIZE_IN_BYTES;
      break;

    case MHT_HASH_GEN:
      hash_generator_out_cmd.enable = true;
      hash_generator_out_cmd.address = set_address_s.read();
      hash_generator_out_cmd.size = HS_SIZE;
      for (unsigned int i = 0;i<HS_SIZE;i++)
        hash_generator_out_data.data[i] = check_register_s[i];

      hash_generator_out_cmd.stage_0 = true;
      break;

    case MHT_WRITE_REF:
      memory_out_cmd.enable = true;
      memory_out_cmd.start_address = r_parent_address_s;
      memory_out_cmd.cmd = WRITE;
      memory_out_cmd.plen = SIZE_64_IN_BYTES;
      memory_out_wdata.data[0] = hash_s[0];
      memory_out_wdata.data[1] = hash_s[1];
      break;

    case MHT_READ_LAST_LEVEL:
      memory_out_cmd.enable = true;
      memory_out_cmd.cmd = READ;
      memory_out_cmd.start_address = set_address_s;
      memory_out_cmd.plen = SIZE_256_IN_BYTES;
      break;

    case MHT_READ_LAST_LEVEL2:
      memory_out_cmd.enable = true;
      memory_out_cmd.cmd = READ;
      memory_out_cmd.start_address = set_address_s + SIZE_256_IN_BYTES;
      memory_out_cmd.plen = LAST_HS_SIZE_IN_BYTES - SIZE_256_IN_BYTES;
      break;

    case MHT_HASH_LAST_LEVEL:
      hash_generator_out_cmd.enable = true;
      hash_generator_out_cmd.size = LAST_HS_SIZE;
      hash_generator_out_cmd.address = set_address_s;
      for (unsigned int i=0;i<LAST_HS_SIZE;i++)
        hash_generator_out_data.data[i] = check_register_s[i];
      hash_generator_out_cmd.stage_0 = true;
      break;

    default:
      break;
  }

//-------------------------------------------------------- initialize_state
  switch (initialize_state) 
  {
    case HTI_IDLE : 
      ack_out.initialize_ack		= true;
      break;


    case I_START_WRITE_NULL_BROTHER  :
      memory_out_cmd.enable		= true;
      memory_out_cmd.cmd		= WRITE;
      memory_out_cmd.start_address	= brother_address_s;
      memory_out_cmd.plen		= SIZE_64_IN_BYTES;
      memory_out_wdata.data[0]		= 0;
      memory_out_wdata.data[1]		= 0;
      break;

    case I_START_RESTORE_BROTHER  :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= brother_address_s;
      hash_out_cmd.cmd			= RESTORE;
      if (brother_cpt_s == 0)
      {
        hash_count++;
        hash_generator_out_cmd.enable	= true;
        hash_generator_out_cmd.size	= 0;
        hash_generator_out_cmd.address	= brother_address_s;
      }
      else
      {
        hash_generator_out_cmd.enable	= true;
        hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
        hash_generator_out_cmd.stage_0	= false;
        hash_generator_out_iv.data[0]	= hash_s[0];
        hash_generator_out_iv.data[1]	= hash_s[1];
        hash_generator_out_data.data[0]	= check_register_s[(brother_cpt_s - 1) * SIZE_64_IN_WORDS];
        hash_generator_out_data.data[1]	= check_register_s[(brother_cpt_s - 1) * SIZE_64_IN_WORDS + 1];
      }
      break;


    case I_START_CHECK_LAST_SET  :
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      hash_generator_out_cmd.stage_0	= false;
      hash_generator_out_iv.data[0]	= hash_s[0];
      hash_generator_out_iv.data[1]	= hash_s[1];
      hash_generator_out_data.data[0]	= check_register_s[brother_cpt_s * SIZE_64_IN_WORDS];
      hash_generator_out_data.data[1]	= check_register_s[brother_cpt_s * SIZE_64_IN_WORDS + 1];
      break;

    case  I_START_READ_SET :
      memory_out_cmd.enable		= true;
      memory_out_cmd.cmd		= READ;
      memory_out_cmd.start_address	= set_address_s;
      memory_out_cmd.plen		= HS_SIZE_IN_BYTES;
      //start hash generation
      hash_count++;
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= 0;
      hash_generator_out_cmd.address	= set_address_s;
      break;

    case  I_START_IV_GENERATION :
      //start hash generation
      hash_count++;
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= 0;
      if (isLastLevel(w_parent_address_s, ht_page_size_s))
        hash_generator_out_cmd.address	= w_parent_address_s & 0xFFFFFF00;
      else
        hash_generator_out_cmd.address	= w_parent_address_s;
      break;

    case  I_START_UPDATE_LEAVE_PARENT :
      //start hash generation
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= HS_SIZE;
      hash_generator_out_cmd.address	= w_parent_address_s;
      hash_generator_out_cmd.stage_0	= false;
      hash_generator_out_iv.data[0]	= M[0];
      hash_generator_out_iv.data[1]	= M[1];
      for(unsigned int i=0;i<HS_SIZE;i++)
        hash_generator_out_data.data[i]	= register_2_in.data[i];
      break;

    case I_START_UPDATE_PARENT_M  :
      memory_out_cmd.enable		= true;
      memory_out_cmd.cmd		= WRITE;
      memory_out_cmd.start_address	= w_parent_address_s;
      memory_out_cmd.plen		= SIZE_64_IN_BYTES;
      memory_out_wdata.data[0]		= M[level_s * SIZE_64_IN_WORDS];
      memory_out_wdata.data[1]		= M[level_s * SIZE_64_IN_WORDS + 1];
      //update M[i+1]
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      hash_generator_out_cmd.address	= w_parent_address_s;
      hash_generator_out_cmd.stage_0	= false;
      hash_generator_out_iv.data[0]	= M[(level_s + 1) * SIZE_64_IN_WORDS];
      hash_generator_out_iv.data[1]	= M[(level_s + 1) * SIZE_64_IN_WORDS + 1];
      hash_generator_out_data.data[0]	= M[level_s * SIZE_64_IN_WORDS];
      hash_generator_out_data.data[1]	= M[level_s * SIZE_64_IN_WORDS + 1];
      //
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= w_parent_address_s;
      hash_out_cmd.cmd			= INVALIDATE;
      break;

    case I_START_UPDATE_ROOT_M :
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      hash_generator_out_cmd.address	= w_parent_address_s;
      hash_generator_out_cmd.stage_0	= false;
      hash_generator_out_iv.data[0]	= M[level_s * SIZE_64_IN_WORDS];
      hash_generator_out_iv.data[1]	= M[level_s * SIZE_64_IN_WORDS + 1];
      hash_generator_out_data.data[0]	= check_register_s[index_s * SIZE_64_IN_WORDS];
      hash_generator_out_data.data[1]	= check_register_s[index_s * SIZE_64_IN_WORDS + 1];
      break;

    case I_START_WRITE_PSPE :
      //Write in alap regsiter
      sc_out_cmd.enable	  		= true;
      sc_out_cmd.cmd	    		= WRITE_PSPE;
      sc_out_cmd.address		= sc_register_in.alap_spspe_address;
      for (unsigned int i = 0; i < PSPE_SIZE; i++)
        sc_out_wdata.data[i] 		= new_spspe_s[i];
      break;

    default :
      break;
  }

//----------------------------------------------------- update_state
  switch (update_state) 
  {
    case HTU_IDLE : 
      ack_out.uack			= true;
      ack_out.alap_ack			= true;
      break;

    case U_START_PARENT_UPDATE:
      hash_count++;
      //start hash generation
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= HS_SIZE;
      hash_generator_out_cmd.index	= 0;
      for(unsigned int i=0;i<HS_SIZE;i++)
        hash_generator_out_data.data[i]	= register_1_in.data[i];
      break;

    case U_START_WRITE_NEW_PARENT :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= w_parent_address_s;
      hash_out_cmd.cmd			= WRITE;
      hash_out_wdata.data[0]		= new_hash_s[0];
      hash_out_wdata.data[1]		= new_hash_s[1];
      break;

    case U_START_READ_SLAVE_PSPE :
      sc_out_cmd.enable		  	= true;
      sc_out_cmd.address		= victim_address_s;
      sc_out_cmd.cmd			= READ_SC;
      break;

    case U_WAIT_READ_SLAVE_PSPE :
      ack_out.alap_ack	= true;
      break;

    case U_START_READ_VICTIM_SET :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= brother_address_s;
      hash_out_cmd.cmd			= READ;
      if (brother_cpt_s == 0)
      {
        hash_count++;
        hash_generator_out_cmd.enable	= true;
        hash_generator_out_cmd.size	= 0;
        hash_generator_out_cmd.address	= brother_address_s;
      }
      else
      {
        hash_generator_out_cmd.enable	= true;
        hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
        hash_generator_out_cmd.stage_0	= false;
        hash_generator_out_iv.data[0]	= hash_s[0];
        hash_generator_out_iv.data[1]	= hash_s[1];
        hash_generator_out_data.data[0]	= check_register_s[(brother_cpt_s - 1) * SIZE_64_IN_WORDS];
        hash_generator_out_data.data[1]	= check_register_s[(brother_cpt_s - 1) * SIZE_64_IN_WORDS + 1];
      }
      break;

    case U_START_NEW_HASH_GENERATION:
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      hash_generator_out_cmd.stage_0	= false;
      hash_generator_out_iv.data[0]	= hash_s[0];
      hash_generator_out_iv.data[1]	= hash_s[1];
      hash_generator_out_data.data[0]	= check_register_s[brother_cpt_s * SIZE_64_IN_WORDS];
      hash_generator_out_data.data[1]	= check_register_s[brother_cpt_s * SIZE_64_IN_WORDS + 1];
      break;


    case U_START_RESTORE_DIRTY_VICTIM_BROTHERS :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= restore_address_s;
      hash_out_cmd.cmd			= RESTORE;
      break;

    case U_START_READ_REFERENCE_PARENT:
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= r_parent_address_s;
      hash_out_cmd.cmd			= READ;
      //start hash generation
      hash_count++;
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= HS_SIZE;
      hash_generator_out_cmd.index	= 0;
      for(unsigned int i=0;i<HS_SIZE;i++)
        hash_generator_out_data.data[i]	= check_register_s[i];
      break;

    case U_START_LAST_IC:
      //start hash generation
      hash_count++;
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= LAST_HS_SIZE;
      hash_generator_out_cmd.index	= 0;
      for(unsigned int i=0;i<LAST_HS_SIZE;i++)
        hash_generator_out_data.data[i]	= check_register_s[i];
      break;

    case U_START_WRITE_PSPE :
      sc_out_cmd.enable	  		= true;
      sc_out_cmd.cmd	    		= WRITE_PSPE;
      sc_out_cmd.address		= sc_register_in.alap_spspe_address;
      for (unsigned int i = 0; i < PSPE_SIZE; i++)
        sc_out_wdata.data[i] 		= new_spspe_s[i];
      break;

    case U_WAIT_WRITE_PSPE :
      ack_out.alap_ack			= true;
      break;

    case U_START_SYNCHRONIZE_DIRTY_VICTIM_BROTHERS :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= synchronize_address_s;
      hash_out_cmd.cmd			= SYNCHRONIZE;
      break;

    case U_START_RESTORE_REFERENCE_PARENT_BROTHERS :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= brother_address_s;
      hash_out_cmd.cmd			= RESTORE;
      break;

    default :
      break;
  }

//------------------------------------------------------- check_state
  switch (check_state) 
  {
    case HTC_IDLE : 
      ack_out.cack 			= true;
      break;

    case C_START_IV_GENERATION :
      hash_count++;
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= 0;
      hash_generator_out_cmd.address	= set_address_s;
      break;

    case C_START_HASH_GENERATION :
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      hash_generator_out_cmd.stage_0	= false;
      hash_generator_out_iv.data[0]	= hash_s[0];
      hash_generator_out_iv.data[1]	= hash_s[1];
      hash_generator_out_data.data[0]	= register_2_in.data[index_s * SIZE_64_IN_WORDS];
      hash_generator_out_data.data[1]	= register_2_in.data[index_s * SIZE_64_IN_WORDS + 1];
      break;

    case C_START_READ_REFERENCE_PARENT:
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= r_parent_address_s;
      hash_out_cmd.cmd			= READ; 
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= SIZE_64_IN_WORDS;
      hash_generator_out_cmd.stage_0	= false;
      hash_generator_out_iv.data[0]	= hash_s[0];
      hash_generator_out_iv.data[1]	= hash_s[1];
      hash_generator_out_data.data[0]	= register_2_in.data[index_s * SIZE_64_IN_WORDS];
      hash_generator_out_data.data[1]	= register_2_in.data[index_s * SIZE_64_IN_WORDS + 1];
      break;

    case  C_START_CHECK_REFERENCE_PARENT :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= r_parent_address_s;
      hash_out_cmd.cmd			= READ; 
      //start hash generation
      hash_count++;
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.index	= 0;
      hash_generator_out_cmd.size	= HS_SIZE;
      for(unsigned int i=0;i<HS_SIZE;i++)
        hash_generator_out_data.data[i]	= check_register_s[i];
      break;

    case  C_START_LAST_IC :
      //start hash generation
      hash_count++;
      hash_generator_out_cmd.enable	= true;
      hash_generator_out_cmd.size	= LAST_HS_SIZE;
      hash_generator_out_cmd.index	= 0;
      for(unsigned int i=0;i<LAST_HS_SIZE;i++)
        hash_generator_out_data.data[i]	= check_register_s[i];
      break;

    case C_START_RESTORE_REFERENCE_PARENT_BROTHERS :
      hash_out_cmd.enable		= true;
      hash_out_cmd.address		= brother_address_s;
      hash_out_cmd.cmd			= RESTORE;
      break;

    default :
      break;
  }
}

void HashTreeController::print_stats(ofstream & file)
{
  _secbus_debug(file, NONE, "- HASH OPERATION	    	= " <<hash_count<<"\n");
}
void HashTreeController::reset_stats()
{
  hash_count = 0;
}


