// Copyright (c) 2017-2019 Bluespec, Inc.  All Rights Reserved

package Semi_FIFOF;

// ================================================================
// Separate interfaces for input-side and output-side of FIFOF.
// Conversion functions to these, from FIFOF interfaces.

// ================================================================
// BSV library imports

import FIFOF       :: *;
import Connectable :: *;
import GetPut      :: *;
import FIFOLevel   :: *;

// ================================================================
// Semi-FIFOF interfaces

interface FIFOF_I #(type t);
   method Action  enq (t x);
   method Bool    notFull ();
endinterface

interface FIFOF_O #(type t);
   method t       first ();
   method Action  deq ();
   method Bool    notEmpty ();
endinterface

// ================================================================
// Converters to and from Semi-FIFOF interfaces

typeclass To_FIFOF_IO#(type tf, type t)
   dependencies (tf determines t);

   function FIFOF_I #(t) to_FIFOF_I (tf f);
   function FIFOF_O #(t) to_FIFOF_O (tf f);
endtypeclass

instance To_FIFOF_IO#(FIFOF#(t), t);
   function FIFOF_I #(t) to_FIFOF_I (FIFOF #(t) f);
      return interface FIFOF_I;
		method enq (x) = f.enq (x);
		method notFull = f.notFull;
	     endinterface;
   endfunction

   function FIFOF_O #(t) to_FIFOF_O (FIFOF #(t) f);
      return interface FIFOF_O;
		method first    = f.first;
		method deq      = f.deq;
		method notEmpty = f.notEmpty;
	     endinterface;
   endfunction
endinstance

instance To_FIFOF_IO#(FIFOLevelIfc#(t,n), t);
   function FIFOF_I #(t) to_FIFOF_I (FIFOLevelIfc #(t,n) f);
      return interface FIFOF_I;
		method enq (x) = f.enq (x);
		method notFull = f.notFull;
	     endinterface;
   endfunction

   function FIFOF_O #(t) to_FIFOF_O (FIFOLevelIfc #(t,n) f);
      return interface FIFOF_O;
		method first    = f.first;
		method deq      = f.deq;
		method notEmpty = f.notEmpty;
	     endinterface;
   endfunction
endinstance

// -----------------------------------------------------------
// Converters to Get/Put interfaces

instance ToGet#(FIFOF_O#(t), t);
   function toGet(ff) = (
      interface Get;
	 method get();
	    actionvalue
	       ff.deq;
	       return ff.first;
	    endactionvalue
	 endmethod
      endinterface
			 );
endinstance

instance ToPut#(FIFOF_I#(t), t);
   function toPut(ff) = (
      interface Put;
	 method Action put(x);
	    ff.enq(x);
	 endmethod
      endinterface
			 );
endinstance

// ================================================================
// Connections

// ----------------
// FIFOF_O to FIFOF_I

instance Connectable #(FIFOF_O #(t), FIFOF_I #(t));
   module mkConnection #(FIFOF_O #(t) fo, FIFOF_I #(t) fi) (Empty);
      rule rl_connect;
	 fi.enq (fo.first);
	 fo.deq;
      endrule
   endmodule
endinstance

// ----------------
// FIFOF_I to FIFOF_O

instance Connectable #(FIFOF_I #(t), FIFOF_O #(t));
   module mkConnection #(FIFOF_I #(t) fi, FIFOF_O #(t) fo) (Empty);
      mkConnection (fo, fi);
   endmodule
endinstance

// ----------------
// FIFOF_O to FIFOF

instance Connectable #(FIFOF_O #(t), FIFOF #(t));
   module mkConnection #(FIFOF_O #(t) fo, FIFOF #(t) fi) (Empty);
      rule rl_connect;
	 fi.enq (fo.first);
	 fo.deq;
      endrule
   endmodule
endinstance

// ----------------
// FIFOF to FIFOF_I

instance Connectable #(FIFOF #(t), FIFOF_I #(t));
   module mkConnection #(FIFOF #(t) fo, FIFOF_I #(t) fi) (Empty);
      rule rl_connect;
	 fi.enq (fo.first);
	 fo.deq;
      endrule
   endmodule
endinstance

// ================================================================
// Convenience function combining first/enq

function ActionValue #(t) pop_o (FIFOF_O #(t) f);
   actionvalue
      f.deq;
      return f.first;
   endactionvalue
endfunction

// ================================================================
// Dummy tie-off interfaces

// dummy_FIFO_I that never accepts anything (always "full")

FIFOF_I #(t) dummy_FIFOF_I = interface FIFOF_I;
				method Action enq (x) if (False);
				   noAction;
				endmethod
				method notFull;
				   return False;
				endmethod
			     endinterface;

// Dummy FIFO_O that never yields anything (always "empty")

FIFOF_O #(t) dummy_FIFOF_O = interface FIFOF_O;
				method first () if (False);
				   return ?;
				endmethod

				method Action deq () if (False);
				   noAction;
				endmethod

				method notEmpty;
				   return False;
				endmethod
			     endinterface;


// ================================================================

endpackage
