
// Project:	MIPS V2.0
// Entity	Integer Execution Unit
// File		int_exe.hase
// Date:	3 April 2002


// All Execution Unit receives instruction/operand packets from the Register
// Fetch Unit and send instruction/operand packets to the Memory Access Unit. 
// Each contains a pipeline with length equal to its latency value.
// Except in the case of the Integer Unit, this value is a parameter of
// the parent Execution Unit, so the code is designed to
// deal with any value (>=1) and can be common to all units
// In the model the result is computed in the first stage and simply
// copied through the remaining pipeline stages

// The Integer Unit executes instructions ADD, ADDI, SUB, SUBI,
// AND, ANDI, OR, ORI, XOR XORI, SLL, SRL, SRA, SLLI, SRLI, SRAI
// and is involved in the execution of LB, LH, LW, LF, SW, SF, BNEZ, J, TRAP
// In the output, the data2 field is only used for store instructions
// The compare instructions (SEQ, SNE, SLT, SGT, SLE, SGE,
// SEQI, SNEI, SLTI, SGTI, SLEI, SGEI) set the output data field to 1
// if the condition is satisfied, and to 0 if not.

// 12 Mar 2001 update - offset calculated once for all Immediates
// April 200r updated to use new clock mechanism

$class_decls

//entity references

  sim_entity_id pipe;

  mem_access * myMA;

//structures and variables
  t_instrn_reg Instruction;
  unit_data_struct InputPacket, InputBuffer, Pipeline[8];
  t_exe_rslt result;

  bool Hold;

  int i, j;		// index into Pipeline array
  int latency;
  char Destination[8], Source[8];
  t_reg_args dest_args, source_args;
  int sign_ext, offset, shift, mask;
  int ClockPhase;
  int Any;		// Unit is active if Any pipeline stage is valid
  int old_state;
  int tempint;

//classes
  t_exe_rslt Result();

$class_defs
	
t_exe_rslt Int_exe::Result()


// computes result of instruction/operands in first pipeline stage
{
	  if (InputBuffer.instrn.function == t_mips_instrn_set::MTC1)
		{
		result.status = 0;
		result.data = floatFromInt((int)InputBuffer.data1);
		}
	  
	  if (InputBuffer.instrn.function == t_mips_instrn_set::ADD)
		{
		result.status = 0;
		result.data = InputBuffer.data1 + InputBuffer.data2;
		}

	  if (InputBuffer.instrn.decode_MT())
		{
		result.status = 0;
		result.data = InputBuffer.data1;
		}
	  
	  if (InputBuffer.instrn.decode_MF())
		{
		result.status = 0;
		result.data = InputBuffer.data1;
		}
	  
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::ADDI)
		{
		result.status = 0;
		result.data = InputBuffer.data1 + offset;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::ADDU)
		{
		result.status = 0;
		result.data = InputBuffer.data1 + InputBuffer.data2;
//		result.data = (int)(IntToUint((int)InputBuffer.data1) + IntToUint((int)InputBuffer.data2));
		}
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::ADDIU)
		{
		result.status = 0;
		result.data = InputBuffer.data1 + offset;
//		result.data = (int)(IntToUint((int)InputBuffer.data1) + IntToUint((int)offset));
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SUB)
		{
		result.status = 0;
		result.data = InputBuffer.data1 - InputBuffer.data2;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SUBU)
		{
		result.status = 0;
		result.data = InputBuffer.data1 - InputBuffer.data2;
//		result.data = (int)(IntToUint((int)InputBuffer.data1) - IntToUint((int)InputBuffer.data2));
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::AND)
		{
		result.status = 0;
		result.data = (int)InputBuffer.data1 & (int)InputBuffer.data2;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::ANDI)
		{
		result.status = 0;
		result.data = (int) InputBuffer.data1 & (int)offset;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::OR)
		{
		result.status = 0;
		result.data = (int)InputBuffer.data1 | (int)InputBuffer.data2;
		}
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::ORI)
		{
		result.status = 0;
		result.data = (int)InputBuffer.data1 | (int)offset;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::XOR)
		{
		result.status = 0;
		result.data = (int)InputBuffer.data1 ^ (int)InputBuffer.data2;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::XORI)
		{
		result.status = 0;
		result.data = (int)InputBuffer.data1 ^ (int)offset;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::NOR)
		{
		result.status = 0;
		result.data = (int)(~((int)InputBuffer.data1 | (int)InputBuffer.data2));
		}
	  
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::LUI)
		{
		result.status = 0;
		offset = InputBuffer.instrn.loadi_field.immediate;
		result.data = ((offset & 65535) << 16);
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLLV)
		{
		result.status = 0;
		result.data = (int)InputBuffer.data1 << (int)InputBuffer.data2;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLL)
		{
		result.status = 0;
		result.data = (int) InputBuffer.data1 << offset;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SRLV)
		{
		//>>>A result.data = (int)InputBuffer.data1 >>> (int)InputBuffer.data2;
		//>>>S5
		result.status = 0;
		shift = (int)InputBuffer.data2;
		mask = -1 ^ ( ( (pow (2, shift)) - 1) << (32 - shift) );
		result.data = (int)InputBuffer.data1 >> shift;
		result.data = (int)result.data & mask;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SRL)
		{
		//>>>A result.data = (int)InputBuffer.data1 >>> offset;
		//>>>S5
		result.status = 0;
		shift = offset;
		mask = -1 ^ ( ( (pow (2, shift)) - 1) << (32 - shift) );
		result.data = (int)InputBuffer.data1 >> shift;
		result.data = (int)result.data & mask;
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SRAV)
		{
		//>>>A result.data = (int)InputBuffer.data1 >> (int)InputBuffer.data2;
		//>>>S7
		result.status = 0;
		shift = (int)InputBuffer.data2;
		sign_ext = (int)InputBuffer.data1 & (1 << 31);
		mask = ( (pow (2, shift)) - 1) << (32 - shift);
		result.data = (int)InputBuffer.data1 >> shift;
			if (sign_ext != 0)
			{result.data = (int) result.data | mask;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SRA)
		{
		//>>>A result.data = (int)InputBuffer.data1 >> offset;
		//>>>S7
		result.status = 0;
		shift = offset;
		sign_ext = (int)InputBuffer.data1 & (1 << 31);
		mask = ( (pow (2, shift)) - 1) << (32 - shift);
		result.data = (int)InputBuffer.data1 >> shift;
			if (sign_ext != 0)
			{result.data = (int) result.data | mask;}
		}
/*
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SEQ)
		{
		result.status = 0;
			if (InputBuffer.data1 == InputBuffer.data2)
			{result.data = 1;}
			else
			{result.data = 0;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SNE)
		{
		result.status = 0;
			if (InputBuffer.data1 != InputBuffer.data2)
			{result.data = 1;}
			else
			{result.data = 0;}
		}
*/
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLT)
		{
		result.status = 0;
			if (InputBuffer.data1 < InputBuffer.data2)
			{result.data = 1;}
			else
			{result.data = 0;}
		}
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLT)
		{
		result.status = 0;
//			if (IntToUint((int)InputBuffer.data1) < IntToUint((int)InputBuffer.data2))
			if (InputBuffer.data1 < InputBuffer.data2)
			{result.data = 1;}
			else
			{result.data = 0;}
		}
/*
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SGT)
		{
		result.status = 0;
			if (InputBuffer.data1 > InputBuffer.data2)
			{result.data = 1;}
			else
			{result.data = 0;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLE)
		{
		result.status = 0;
			if (InputBuffer.data1 <= InputBuffer.data2)
			{result.data = 1;}
			else
			{result.data = 0;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SGE)
		{
		result.status = 0;
			if (InputBuffer.data1 >= InputBuffer.data2)
			{result.data = 1;}
			else
			{result.data = 0;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SEQI)
		{
		result.status = 0;
		  if (InputBuffer.data1 == offset)
			{result.data = 1;}
		  else
			{result.data = 0;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SNEI)
		{
		result.status = 0;
		if (InputBuffer.data1 != offset)
			{result.data = 1;}
			else
			{result.data = 0;}
		}
*/
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLTI)
		{
		result.status = 0;
		  if (InputBuffer.data1 < offset)
			{result.data = 1;}
		  else
			{result.data = 0;}
		}
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLTIU)
		{
		result.status = 0;
//		  if (IntToUint((int)InputBuffer.data1) < IntToUint((int)offset))
		  if (InputBuffer.data1 < offset)
			{result.data = 1;}
		  else
			{result.data = 0;}
		}
/*
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SGTI)
		{
		result.status = 0;
		  if (InputBuffer.data1 > offset)
			{result.data = 1;}
		  else
			{result.data = 0;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SLEI)
		{
		result.status = 0;
		  if (InputBuffer.data1 <= offset)
			{result.data = 1;}
		  else
			{result.data = 0;}
		}

	  else if (InputBuffer.instrn.function == t_mips_instrn_set::SGEI)
		{
		result.status = 0;
		  if (InputBuffer.data1 >= offset)
			{result.data = 1;}
		  else
			{result.data = 0;}
		}
*/
	  else if (InputBuffer.instrn.decode_LOAD())
		{
		 result.status = 0;
		 strcpy(Source, InputBuffer.instrn.load_field.src);
		 source_args = Decode_Register(Source);
		 result.data = InputBuffer.data1 + source_args.index;
		}

	  else if (InputBuffer.instrn.decode_STORE())
		{
		 result.status = 0;
		 strcpy(Destination, InputBuffer.instrn.store_field.dest);
		 dest_args = Decode_Register(Destination);
		 result.data = InputBuffer.data1 + dest_args.index;
		}

	  else if (InputBuffer.instrn.decode_MOVE())
		{
			if(InputBuffer.instrn.function == t_mips_instrn_set::MFC1)
			{
				tempint = (int)(floatToInt((float)InputBuffer.data1));
				result.data = (float)tempint;
			}
			else
			{
		 		result.status = 0;
		 		result.data = InputBuffer.data1;
			}
		}
//	  else if(InputBuffer.instrn.function == t_mips_instrn_set::CVTDW){
//			tempint = (int) InputBuffer.data1;
//			result.data = tempint;
//		}
	  else if(InputBuffer.instrn.function == t_mips_instrn_set::CVTSW){
			tempint = (int) InputBuffer.data1;
			result.data = tempint;
		}
	  else if (InputBuffer.instrn.function == t_mips_instrn_set::JR)
		{
		 result.status = 0;
		 result.data = InputBuffer.data1;
		}

	  else if((InputBuffer.instrn.decode_JIMM()) 
	  && ((InputBuffer.instrn.function != t_mips_instrn_set::BC1T) && (InputBuffer.instrn.function != t_mips_instrn_set::BC1F)))
		{
		 result.status = 0;
		result.data = InputBuffer.data1;
		}

	  else if ((InputBuffer.instrn.function) == t_mips_instrn_set::NOP)
		{
		 result.status = 0;
		result.data = InputBuffer.data1;
		}

		
      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BC1T)
	   	 result.status = (((int)InputBuffer.data1 & 4194304) == 4194304 ? 256 : 0);	   
      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BC1F)
	   	 result.status = (((int)InputBuffer.data1 & 4194304) == 4194304 ? 0 : 256);	   

		
				
      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BEQ)
	   	 result.status = (InputBuffer.data1 == InputBuffer.data2 ? 256 : 0);	   
      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BNE) 
	   	 result.status = (InputBuffer.data1 != InputBuffer.data2 ? 256 : 0);	   
	  
	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BNE) 
	   	 result.status = (InputBuffer.data1 != InputBuffer.data2 ? 256 : 0);	   
	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BEQ) 
	   	 result.status = (InputBuffer.data1 == InputBuffer.data2 ? 256 : 0);	   

      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BGTZ)
	   	 result.status = (InputBuffer.data1 > 0 ? 256 : 0);	   
      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BLTZ) 
	   	 result.status = (InputBuffer.data1 < 0 ? 256 : 0);	   
      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BGEZ) 
	   	 result.status = (InputBuffer.data1 < 0 ? 0 : 256);	    
      	   else if (InputBuffer.instrn.function == t_mips_instrn_set::BLEZ) 
		 result.status = (InputBuffer.data1 > 0 ? 0 : 256);   
/*		

	  else if (InputBuffer.instrn.decode_BRNC())
		{
		 if (InputBuffer.data1 == InputBuffer.data2)
		  {result.status = 0;}
		 else
		  {result.status = 256;}
		result.data = InputBuffer.data1;
		}
	  else if (InputBuffer.instrn.decode_BRNCI())
		{
		 if (InputBuffer.data1 < 0)
		{result.status = 1;}
		 else if (InputBuffer.data1 > 0)
		  {result.status = 2;}
		 else if (InputBuffer.data1 == 0)
		  {result.status = 0;}
		result.data = InputBuffer.data1;
		}*/
	return result;
}

$pre

  latency = 1;

  pipe = sim.get_entity_id("PIPE_DISP");

  myMA = (mem_access*)sim.get_entity(sim.get_entity_id("MEM_ACCESS"));
  Hold = false;
  Any = 0;		// Unit is active if Any pipeline stage is valid
  my_state = EI_VOID;
  old_state = EI_VOID;
  dump_state();
// initialise pipeline 

	for ( j=0; j < 8; j++)
	{
	Pipeline[j].status = 0;
	Pipeline[j].instrn.reset();
	}

$phase0
 
// predicates for use in sim_waiting test

  sim_from_port input_packet(int_instr_in);	    

     if (Hold)
    {
	  if (my_state == EI_BUSY)
	  {my_state = EI_HELD;} // i.e. pipeline busy but stalled
    }

   else// Hold = false

   {
    // PROGRESS PIPELINE

    // move instruction/operand packets through pipeline registers
    // NB in hardware the Pipeline registers would be copied
    // simultaneously. In the simulation they are dealt with serially, 
    // so need to start at the output end

	Any = 0;
	for ( i= (latency-1); i>=1; i--)
	 {
	 Pipeline[i].status = Pipeline[i-1].status;
		if ((Pipeline[i].status & 1) == 1)
		{Any = 1;}
	 Pipeline[i].instrn = Pipeline[i-1].instrn;
	 Pipeline[i].data1 = Pipeline[i-1].data1;
	 Pipeline[i].data2 = Pipeline[i-1].data2;
	 }

    // DEAL WITH INPUT

    InputBuffer.status = 0;		// initialise to nonvalid
    if  (sim_waiting(ev, input_packet) > 0)
     {
	SIM_CAST( unit_data_struct, InputPacket, ev);
	InputBuffer.instrn = InputPacket.instrn;
	InputBuffer.data1  = InputPacket.data1;
	InputBuffer.data2  = InputPacket.data2;
	InputBuffer.status = InputPacket.status;
      }

// If Stage 0 contains a valid instruction, compute Result and
// copy into first Pipeline stage output register (Pipeline[0])

	if ((InputBuffer.status & 1) == 1)
	  {Any = 1;

// For functions using an Immediate form 32-bit offset from Immediate value
	  if (InputBuffer.instrn.decode_ALUI())
	    {
	     if ((InputBuffer.instrn.function == t_mips_instrn_set::ANDI)
	      || (InputBuffer.instrn.function == t_mips_instrn_set::ORI)
	      || (InputBuffer.instrn.function == t_mips_instrn_set::XORI)
	      || (InputBuffer.instrn.function == t_mips_instrn_set::ADDIU))
		// instructions which don't sign extend offset
	     {
		offset = InputBuffer.instrn.alui_field.immediate;
		offset = offset & 65535;	// mask off ms 16 bits
	     }
	      else	// instructions which do sign extend offset
	       {
		sign_ext = 0;
		offset = InputBuffer.instrn.alui_field.immediate;
		if ((offset > 32767) || (offset < -32768))
 	 	  {
		   stopSimulation(-46);	// invalid immediate value
		  }
		offset = offset & 65535;	// mask off ms 16 bits
		if ((offset & 32768) != 0)
			{sign_ext = -1 ^ 65535;}// generate sign extension
		offset = offset | sign_ext;
	       }
	     }

	   Pipeline[0].instrn = InputBuffer.instrn;
	   Pipeline[0].data1  = Result().data;
	   Pipeline[0].data2  = InputBuffer.data2;
//	   Pipeline[0].data2  = 0;			// In flt, mul, div
	   Pipeline[0].status = InputBuffer.status | Result().status;
	  }
         else
	  {Pipeline[0].data1 = 0;
	   Pipeline[0].status = 0;}

// set void/active state in display
	if (Any == 1)
	  {my_state = EI_BUSY;} // i.e. valid instruction received
	else
	  {my_state = EI_VOID;} // i.e. non-valid instruction received

   }		// closes !Hold

// send packet to pipeline display

		Instruction.status = InputBuffer.status;
		Instruction.instrn = InputBuffer.instrn;
		sim_schedule(pipe, 0.0, INSTRUCTION,
			SIM_PUT(t_instrn_reg, Instruction));
	 if (my_state != old_state)
	  {dump_state();
	   old_state = my_state;}

	// end of ClockPhase = 0

$phase1
	Hold = myMA->MAHeld;

	// IF NOT HELD UP, SEND OUTPUT TO MA

	if (!Hold)
	{

// Send result in last to MEM_ACCESS unit - if valid, packet is animated
// MEM_ACCESS unit determines which packet is valid

	 if ((Pipeline[latency-1].status & 1) == 1)
	  {send_UNIT_DATA(int_instr_out, Pipeline[latency-1], 0.0, 17, 2 );}
	 else
	  {sim_schedule(int_instr_out, 0.0, UNIT_DATA,
			SIM_PUT(unit_data_struct, Pipeline[latency-1]));}
	}

	// end of  ClockPhase 1



