`include "Def_StructureParameter.v"
`include "Def_RegisterFile.v"

module IF(//Instruction fetch
		in_Instruction,		//input from instruction prefetched buffer
		in_InstructionWait,	//wait for the prefetch buffer 
		out_InstructionAddress,	//output to instruction prefetched buffer
		out_NextInstructionAddress,
		//use to read pc
		out_FourthReadRegisterEnable,
		out_FourthReadRegisterNumber,
		in_FourthReadBus,
		//use to write pc
		out_SecondWriteRegisterEnable,
		out_SecondWriteRegisterNumber,
		out_SecondWriteBus,
		//can decoder go
		in_IDCanGo,
		//fetched instruction
		out_Instruction,
		out_ValidInstruction,
		out_AddressGoWithInstruction,
		//signal relate to pc change in branch instruction
		in_ChangePC,
		in_NewPC,
		//signal send out by mem to update pc
		in_MEMChangePC,
		in_MEMNewPC,
		clock,
		reset
		);

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//		input and output declaration			//
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
input [`InstructionWidth-1:0] in_Instruction;
input in_InstructionWait;

output [`AddressBusWidth-1:0] out_InstructionAddress;
output	[`AddressBusWidth-1:0]	out_NextInstructionAddress;

output out_FourthReadRegisterEnable,out_SecondWriteRegisterEnable;
output [`Def_RegisterSelectWidth-1:0] out_FourthReadRegisterNumber,out_SecondWriteRegisterNumber;
input [`WordWidth-1:0] in_FourthReadBus;
output [`WordWidth-1:0] out_SecondWriteBus;

output	[`InstructionWidth-1:0]	out_Instruction;
output					out_ValidInstruction;
output	[`AddressBusWidth-1:0]		out_AddressGoWithInstruction;
reg	[`AddressBusWidth-1:0]		out_AddressGoWithInstruction;


input in_IDCanGo;

input					in_ChangePC;
input	[`AddressBusWidth-1:0]		in_NewPC;

input					in_MEMChangePC;
input	[`AddressBusWidth-1:0]		in_MEMNewPC;

input clock,reset;


//////////////////////////////////////////////////////////
//   can if go							//
////////////////////////////////////////////////////////
wire	IFOwnCanGo,IFCanGo;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//		pipeline register for IF			//
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
reg [`InstructionWidth-1:0] PipelineRegister_IFID;
reg ValidInstruction_IFID;

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//		program counter and its next state		//
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
wire [`AddressBusWidth-1:0] PC;
reg  [`AddressBusWidth-1:0] NextPC;
wire	[`AddressBusWidth-1:0] PCAdd4;
reg	[`AddressBusWidth-1:0]		InstructionAddressStore;
reg	[`AddressBusWidth-1:0]		Next_InstructionAddressStore;

assign out_Instruction=PipelineRegister_IFID;
assign out_ValidInstruction=ValidInstruction_IFID;


//////////////////////////////////////////////////
//////////////////////////////////////////////////
//	assignment for NextPC			//
//////////////////////////////////////////////////
//////////////////////////////////////////////////
assign PCAdd4=PC+32'h0000_0004;//pc is the address of next instruction

always @(PC or 
	IFOwnCanGo or 
	in_IDCanGo or 
	in_NewPC or 
	in_ChangePC or 
	PCAdd4 or 
	in_MEMChangePC	or
	in_MEMNewPC
)
begin
//origin code that have following problem
//if the mem or alu want to change pc
//but the if is busy waiting for prefetch buffer
//then the change pc signal will be lost
//so i create a seperate register(InstructionAddressStore) that store the output address to prefetch buffer
//and change of pc will be put to pc immediatly regardless the condition,
//and when the prefetch buffer is not busy, the if will compare the pc with InstructionAddressStore
//if not same, it means that the requested instruction is not the branch destination,just ignore it
//if same, it means that the requested instruction is the wanted instruction
//	if(IFOwnCanGo==1'b1)
//	begin
//		if(in_IDCanGo==1'b1)
//		begin
			//IF and ID both can go
			//fetch a new instruction
//			if(in_MEMChangePC==1'b1)
//				NextPC=in_MEMNewPC;
//			else if(in_ChangePC==1'b1)
//				NextPC=in_NewPC;
//			else
//				NextPC=PCAdd4;
//		end
//		else
//		begin
//			NextPC=PC;
//		end
//	end
//	else
//	begin
//		NextPC=PC;
//	end

	if(in_MEMChangePC==1'b1)
	begin
	end
	else if(in_ChangePC==1'b1)
end


//////////////////////////////////////////////////
//////////////////////////////////////////////////
//	can varias pipeline stage can go	//
//////////////////////////////////////////////////
//////////////////////////////////////////////////
assign IFOwnCanGo=in_InstructionWait?1'b0:1'b1;

//you must take account for the instruction type and the function unit it will go to
assign IFCanGo=IFOwnCanGo & in_IDCanGo;

//always read pc
assign out_FourthReadRegisterEnable=1'b1;
assign out_FourthReadRegisterNumber=`Def_PCNumber;
assign PC=in_FourthReadBus;

//preserve the second write bus for pc
assign out_SecondWriteRegisterEnable=1'b1;
assign out_SecondWriteRegisterNumber=`Def_PCNumber;
assign out_SecondWriteBus=NextPC;

//send out fetch address
assign out_InstructionAddress=InstructionAddressStore;
//next instruction address for branch with link
assign out_NextInstructionAddress=PC;

always @(posedge clock or negedge reset)
begin
	if(reset==1'b0)
	begin
		
		//////////////////////////////////
		//initial pipeline register	//
		//////////////////////////////////
		PipelineRegister_IFID=`InstructionZero;
		ValidInstruction_IFID=1'b0;
		out_AddressGoWithInstruction=`AddressBusZero;
		InstructionAddressStore=`AddressBusZero;

	end
	else
	begin
		//////////////////////////////////////////////////
		//get a instruction and send out next address	//
		//////////////////////////////////////////////////
		InstructionAddressStore=Next_InstructionAddressStore;
		if(IFOwnCanGo==1'b1)
		begin
		   if(in_IDCanGo==1'b1)
		   begin
		   	//IF and ID both can go,
		   	//fetch a valid new instruction
		   	if(in_ChangePC==1'b1 || in_MEMChangePC==1'b1)
		   	begin
		   		//a branch or a load to pc
		   		//so your previous fetch instruction can not be use
				PipelineRegister_IFID=in_Instruction;
				ValidInstruction_IFID=1'b0;
				out_AddressGoWithInstruction=PC;
		   	end
		   	else
		   	begin
		   		//no branch
		   		//just fetch a instruction
				PipelineRegister_IFID=in_Instruction;
				ValidInstruction_IFID=1'b1;
				out_AddressGoWithInstruction=PC;
			end
		   end
		end
		else	
		begin
		   if(in_IDCanGo==1'b1)
		   begin
		   	//IF can not go,but the ID can go
		   	//make the PipelineRegister_IFID invalid
		   	ValidInstruction_IFID=1'b0;
		   end			
		end
	end
end

endmodule