// A Verilog example taken from comp.lang.verilog


//-----------------------Cache Coherence Protocol-----------------------------
// This finite-state machine (Melay type) reads bus 
// per cycle and changes the state of each block in cache.

module cache_coherence ( 
	new_state, 
	Cache_Sector_Fill, 
	Invalidate,  
	AdrRetry,
	RMS, 
	RME, 
	WM, 
	WH,  
	SHR, 
	SHW, 
	state, 
	READ_DONE, 
	clk, 
	reset, 
	send_abort, 
	write_back_done,
	AllInvDone);

input RMS, RME, WM, WH, SHR, SHW, READ_DONE, clk, 
      reset, send_abort, write_back_done, AllInvDone;

input [2:0] state;

output [2:0] new_state;
output Cache_Sector_Fill, Invalidate, AdrRetry;

reg Cache_Sector_Fill, Invalidate, AdrRetry ;
reg [2:0] new_state; 

// The four possible states (symbolic names) for a sector 
// in the cache plus 2 mandatory states

parameter /*[2:0]*/ // synopsys enum state_info
	INVALID = 3'b000,
	SHARED_1 = 3'b001,
	EXCLUSIVE = 3'b010,
	MODIFIED = 3'b011,
	Cache_Fill = 3'b100,
	start_write_back = 3'b101,
	/* start_write_back = 5, */
	WaitUntilAllInv = 3'b110;

// Declare current_state and next_state variable.
reg [2:0] 	/* synopsys enum state_info */	present_state;
reg [2:0]	/* synopsys enum state_info */	next_state;
// synopsys state_vector present_state

/* Combinational */
always @(present_state or RMS or RME or WM or WH or SHW or READ_DONE 
         or SHR or write_back_done or AllInvDone or send_abort or state or reset) 
begin
	Cache_Sector_Fill = 0; 	// Default values
	Invalidate = 0; 
	AdrRetry = 0; 
	next_state = present_state; 
	if (reset) next_state = state; else 
	begin
	case(present_state) 	// synopsys full_case
		INVALID: 
			// ReadMiss (shared/exclusive), Write Miss
			if (RMS || RME || WM)  			 
			begin
				Cache_Sector_Fill = 1;
				next_state = Cache_Fill ;
			end
			else 
			begin 
				next_state = INVALID;
			end

		Cache_Fill:	
			/* During This State Cache is filled with the sector,
			   But if any other processor have that copy in modified 
			   state, it sends an abort signal. If no other cache has
			   that copy in modified state. Requesting processor waits
			   for the read from memory to be done.
			*/
			if (send_abort) 
			begin 
				next_state = INVALID;
			end
			else if (READ_DONE) 
			begin
				if (RMS) 
				begin
					next_state = SHARED_1;
				end
				else if (RME) 
				begin 
					next_state = EXCLUSIVE;	
				end
				else if (WM)
				begin
					Invalidate = 1;
					next_state = WaitUntilAllInv;
				end 
				else 
				begin 
					next_state = Cache_Fill ;
				end
			end	
			else 
			begin 
				next_state = Cache_Fill ;
			end
			
		SHARED_1:	  
			if (SHW) // Snoop Hit on a Write.
			begin 
				next_state = INVALID;		
			end
			else if (WH) // Write Hit				
			begin
				Invalidate = 1;
				next_state = WaitUntilAllInv;
			end 
			else 
			begin // Snoop Hit on a Read or Read Hit or any other
				next_state = SHARED_1; 		
			end

		WaitUntilAllInv:
			/* In this state Requesting Processor waits for the 
			   all other processor's to invalidate its cache copy.
			*/

			if (AllInvDone) 
			begin 
				next_state = MODIFIED;
			end
			else 
			begin 
				next_state = WaitUntilAllInv;
			end

		EXCLUSIVE: 
								
			if (SHR) // Snoop Hit on a Read:
			begin
				AdrRetry = 0;
				next_state = SHARED_1;  
			end 					
			else if (SHW) // Snoop Hit on a Write 
			begin
				next_state = INVALID;	
			end
			else if (WH) // Write Hit
			begin
				next_state = MODIFIED;	
			end
			else 
			begin // Read Hit 
				next_state = EXCLUSIVE;		
			end

		MODIFIED:  	
			if (SHW) // Snoop Hit on a Write 
			begin
				next_state = INVALID;		
			end
			else if (SHR) // Snoop Hit on a Read 			
			begin
				AdrRetry = 1;
				next_state = start_write_back;
			end
			else // Read Hit or Write Hit or anything else.
			begin
				next_state = MODIFIED; 		
			end

		start_write_back: 
			/* In this state, Processor waits until other processor 
			   has written back the modified copy.
			*/
			if (write_back_done) 
			begin
				next_state = SHARED_1; 
			end
			else
			begin 
				next_state = start_write_back;
			end
			
	endcase
	end
	
end

/* Sequential */
always @(posedge clk) 
begin
	present_state = next_state;
	new_state = next_state;
end
endmodule


//--------------Test File: Maintaining Cache coherence in 2-processor Systems------
/* Test File for Maintaining Cache Coherence in 2-processor system */

module cache;

reg RMS, RME, WM, WH, RH, SHR, SHW, clk, READ_DONE , reset, send_abort, write_back_done, AllInvDone, PA, PB, countE;
reg [2:0] state, SectorInCacheA, SectorInCacheB;
reg [3:0] count;
integer file1;

parameter CYCLE = 10;

wire [2:0] new_state;
wire Cache_Sector_Fill, Invalidate;
/*
cache_coherence u1 (
	new_state, 
	Cache_Sector_Fill, 
	Invalidate, 
	AdrRetry, 
	RMS, RME, 
	WM, WH, 
	SHR, SHW, 
	state, READ_DONE, clk, reset, send_abort, write_back_done, AllInvDone); 
*/
initial
begin
	$display("Example: maintaining cache coherence in 2-processor system");

	file1 = $fopen("cache.list");
	$fdisplay(file1, "Example: maintaining cache coherence in 2-processor system");

	/* The states of sector in both caches are initialized to Invalid (0). */
	SectorInCacheA = 0; SectorInCacheB = 0;
 
	/* Signals set/rest by cache controller */
	// Write Hit
	WH = 0;

	// ReadMiss (Shared)
	RMS = 0;

	// WriteMiss
	WM  = 0;

	// Snoop hit on read operation
	SHR = 0;

	// Snoop hit on write operation
	SHW = 0;

	// This signal is set/reset,
	// when a cache controller starts or finish reading a sector from memory
	READ_DONE = 0;

	// This signal is set when all other cache controllers 
	// invalidate their copy.
	AllInvDone = 0;

	// This signal is set when cache controller finishes updating memory.
	write_back_done = 0;

	// This signal is set when snoopy cache controller sends 
	// AdrRetry Signal to retry the operation. 
	send_abort = 0;

	// Present State of the Sector is INVALID in PA's local cache.
	// So, it puts the address of this sector on the bus and starts
	// reading this sector from memory.

	state = SectorInCacheA ; PA = 1; PB = 0; RME = 1; 
	reset = 1; #6 reset = 0;

	// After 15 time units Processor B will declare  
	// Snoop Hit on the Read operation done by Processor A.
	#40 PA = 0; PB = 1; RME = 0; RMS = 1; state = SectorInCacheB; 
	reset = 1;#10 reset = 0;

	#50 PB = 0; PA = 1; SHR = 1; state = SectorInCacheA; 
	reset = 1; #10 reset = 0;
	
	#20 SHR = 0; WH = 1; state = SectorInCacheA; 
	reset = 1; #10 reset = 0;

	#50 PA = 0; PB = 1; RMS = 1; WH = 0; state = SectorInCacheB; 
	reset = 1; #10 reset = 0;

	#30 SHR = 1; RMS = 0; PA = 1; PB = 0; state = SectorInCacheA; 
	reset = 1; #10 reset = 0;
	
	#20 PB = 1; PA = 0; SHR = 0; state = SectorInCacheB; 
	reset = 1; #10 reset = 0;

	// PA states writing back.
	#20 PB = 0; PA = 1; state = SectorInCacheA; 
	reset = 1; #10 reset = 0;

	// This signal is broadcast on the bus to tell every body that 
	// Processor B has written back its Modified data back to the Memory 
	// to make everything consistent. After 5 time units it is set back to 
	// Zero. 

	#30 write_back_done = 1;
	#10 write_back_done = 0;

	// PB retry.
	#10 PA = 0; PB = 1; RMS = 1; state = SectorInCacheB; 
	reset = 1; #10 reset = 0;

//	#100 $stop;
//	#100 $finish;
end

always @(new_state)
begin
	if (PA == 1) SectorInCacheA = new_state ;
	else if (PB == 1) SectorInCacheB = new_state ;
end

always @(posedge clk)
begin
	if (Invalidate)
	begin	
		#10 SectorInCacheB = 0;
		#10 AllInvDone = 1;
		#15 AllInvDone = 0;
	end
	else if (Cache_Sector_Fill)
	begin
		count = 0;
		countE = 1;

		// Intialize the Read_Done variable to 0; it 
		// it will be set to 1 by this test file after
		// some delay.
		READ_DONE = 0;
	end
	else if (AdrRetry)
	begin
		countE = 0;
		send_abort = 1;
		#40 send_abort = 0;
	end
end

always @(posedge clk)
begin
	if (READ_DONE == 1) READ_DONE = 0;
	if (countE == 1)
	begin
		if ( count == 2 )
		begin
			READ_DONE = 1;
			countE = 0;
		end
		else count = count + 1;
	end
$display("SHit %b Abort %b RD_DONE %b WB_DONE %b Cache_Sector_Fill %b Inv %b AllInvDone %b stateA %b stateB %b", SHR, send_abort, READ_DONE, write_back_done, Cache_Sector_Fill, Invalidate, AllInvDone, SectorInCacheA, SectorInCacheB);

// Added for v2v testing

$fdisplay(file1, $time,,"SHit %b Abort %b RD_DONE %b WB_DONE %b Cache_Sector_Fill %b Inv %b AllInvDone %b stateA %b stateB %b", SHR, send_abort, READ_DONE, write_back_done, Cache_Sector_Fill, Invalidate, AllInvDone, SectorInCacheA, SectorInCacheB);

end

initial clk = 0;

always #(CYCLE/2) clk = ~clk ;

endmodule 
