Allow mkReducePipe vector length to be non power of 2

Change-Id: I73ba56d07dae8215bba69d02b93081ef520edf1c

// Copyright (c) 2014 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFOF::*;
import GetPut::*;
import Connectable::*;
import Vector::*;
import MIMO::*;
import DefaultValue::*;
import Gearbox::*;

interface PipeIn#(type a);
   method Action enq(a v);
   method Bool notFull();
endinterface

interface PipeOut#(type a);
   method a first();
   method Action deq();
   method Bool notEmpty();
endinterface

typeclass ToPipeIn#(type a, type b);
   function PipeIn#(a) toPipeIn(b in);
endtypeclass

typeclass ToPipeOut#(type a, type b);
   function PipeOut#(a) toPipeOut(b in);
endtypeclass

typeclass MkPipeOut#(type a, type b);
   module mkPipeOut#(b in)(PipeOut#(a));
endtypeclass

typeclass MkPipeIn#(type a, type b);
   module mkPipeIn#(b in)(PipeIn#(a));
endtypeclass

instance ToPipeIn#(a, FIFOF#(a));
   function PipeIn#(a) toPipeIn(FIFOF#(a) in);
      return (interface PipeIn#(a);
		 method enq = in.enq;
		 method notFull = in.notFull;
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(a, function a pipefn());
   function PipeOut#(a) toPipeOut(function a pipefn());
      return (interface PipeOut#(a);
		 method first(); return pipefn(); endmethod
		 method Action deq(); endmethod
		 method Bool notEmpty(); return False; endmethod
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(a, Reg#(a));
   function PipeOut#(a) toPipeOut(Reg#(a) in);
      return (interface PipeOut#(a);
		 method first(); return in; endmethod
		 method Action deq(); endmethod
		 method Bool notEmpty(); return False; endmethod
	      endinterface);
   endfunction
endinstance

instance ToPipeIn#(Vector#(m, a), Gearbox#(m, n, a));
   function PipeIn#(Vector#(m, a)) toPipeIn(Gearbox#(m, n, a) in);
      return (interface PipeIn#(Vector#(m, a));
		 method enq = in.enq;
		 method notFull = in.notFull;
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(a, FIFOF#(a));
   function PipeOut#(a) toPipeOut(FIFOF#(a) in);
      return (interface PipeOut#(a);
		 method first = in.first;
		 method deq = in.deq;
		 method notEmpty = in.notEmpty;
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(Vector#(n,a), MIMO#(k,n,sz,a));
   function PipeOut#(Vector#(n,a)) toPipeOut(MIMO#(k,n,sz,a) in);
      return (interface PipeOut#(a);
		 method first = in.first;
		 method Action deq() if (in.deqReadyN(fromInteger(valueOf(n))));
		    in.deq(fromInteger(valueOf(n)));
		 endmethod
		 method Bool notEmpty();
		    return in.deqReadyN(fromInteger(valueOf(n)));
		 endmethod
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(Vector#(n, a), Gearbox#(m, n, a));
   function PipeOut#(Vector#(n, a)) toPipeOut(Gearbox#(m, n, a) in);
      return (interface PipeOut#(Vector#(n,a));
		 method first = in.first;
		 method deq = in.deq;
		 method notEmpty = in.notEmpty;
	      endinterface);
   endfunction
endinstance

instance MkPipeOut#(a, Get#(a))
   provisos (Bits#(a, asz));
   module mkPipeOut#(Get#(a) in)(PipeOut#(a));
      FIFOF#(a) fifo <- mkFIFOF();
      rule connect;
	 let v <- in.get();
	 fifo.enq(v);
      endrule
      return toPipeOut(fifo);
   endmodule
endinstance

instance MkPipeIn#(a, Put#(a))
   provisos (Bits#(a, asz));
   module mkPipeIn#(Put#(a) out)(PipeIn#(a));
      FIFOF#(a) fifo <- mkFIFOF();
      rule connect;
	 let v <- toGet(fifo).get;
	 out.put(v);
      endrule
      return toPipeIn(fifo);
   endmodule
endinstance

function PipeOut#(a) toCountedPipeOut(Reg#(Bit#(n)) r, PipeOut#(a) pipe);
   return (interface PipeOut#(Vector#(n,a));
	      method first = pipe.first;
	      method Action deq(); pipe.deq; r <= r + 1; endmethod
	      method notEmpty = pipe.notEmpty;
	   endinterface);
endfunction

instance ToGet #(PipeOut #(a), a);
   function Get #(a) toGet (PipeOut #(a) po);
      return (interface Get;
                 method ActionValue #(a) get ();
                    po.deq ();
                    return po.first ();
                 endmethod
              endinterface);
   endfunction
endinstance

instance ToPut #(PipeIn #(a), a);
   function Put #(a) toPut (PipeIn #(a) pi);
      return (interface Put;
		 method Action put(a v);
                    pi.enq (v);
                 endmethod
              endinterface);
   endfunction
endinstance

instance Connectable#(PipeOut#(a),Put#(a));
   module mkConnection#(PipeOut#(a) in, Put#(a) out)(Empty);
      rule connect;
	 let v = in.first;
	 in.deq();
	 out.put(v);
      endrule
   endmodule
endinstance

instance Connectable#(PipeOut#(a),PipeIn#(a));
   module mkConnection#(PipeOut#(a) in, PipeIn#(a) out)(Empty);
      rule connect;
	 let v = in.first;
	 in.deq();
	 out.enq(v);
      endrule
   endmodule
endinstance

function PipeOut#(a) unvectorPipeOut(PipeOut#(Vector#(1,a)) in);
   return (interface PipeOut#(a);
	      method first = in.first[0];
	      method deq = in.deq;
	      method notEmpty = in.notEmpty;
	   endinterface);
endfunction

function PipeOut#(Tuple2#(a,b)) zipPipeOut(PipeOut#(a) ina, PipeOut#(b) inb);
      return (interface PipeOut#(Tuple2#(a,b));
		 method Tuple2#(a,b) first(); return tuple2(ina.first, inb.first); endmethod
		 method Action deq(); ina.deq(); inb.deq(); endmethod
		 method Bool notEmpty(); return ina.notEmpty() && inb.notEmpty(); endmethod
	      endinterface);
   endfunction

module mkFunnel#(PipeOut#(Vector#(mk,a)) in)(PipeOut#(Vector#(m, a)))
   provisos (Mul#(m, k, mk),
	     Bits#(a, asz),
	     Add#(a__, TMul#(asz, m), TMul#(asz, mk)),
	     Add#(1, b__, asz),
	     Add#(2, c__, mk),
	     Add#(d__, m, mk),
	     Add#(asz, m, e__),
	     Add#(asz, mk, f__));
   let m = fromInteger(valueOf(m));
   let mk = fromInteger(valueOf(mk));

   MIMOConfiguration cfg = defaultValue();
   MIMO#(mk, m, mk, a) mimo <- mkMIMO(cfg);
   rule consumer if (mimo.enqReadyN(mk));
      Vector#(mk, a) v = in.first();
      in.deq();
      mimo.enq(mk, v);
   endrule

   method Vector#(m, a) first() if (mimo.deqReadyN(m));
      return mimo.first();
   endmethod
   method Action deq() if (mimo.deqReadyN(m));
      mimo.deq(m);
   endmethod
   method notEmpty();
      return mimo.deqReadyN(m);
   endmethod
endmodule

module mkFunnel1#(PipeOut#(Vector#(k,a)) in)(PipeOut#(a))
   provisos (Bits#(a, asz), Log#(k,ksz));

   Reg#(Bit#(ksz)) selector <- mkReg(0);

   method a first();
      return in.first[selector];
   endmethod
   method Action deq();
      if (selector == fromInteger(valueOf(k)-1)) begin
	 in.deq();
	 selector <= 0;
      end
      else
	 selector <= selector + 1;
   endmethod
   method notEmpty();
      return in.notEmpty();
   endmethod
endmodule

module mkFunnelGB1#(Clock slowClock, Reset slowReset, Clock fastClock, Reset fastReset, PipeOut#(Vector#(k,a)) in)(PipeOut#(a))
   provisos (Bits#(a, asz), Log#(k,ksz), Add#(1,a__,k));

   Gearbox#(k,1,a) gb <- mkNto1Gearbox(slowClock, slowReset, fastClock, fastReset);
   PipeIn#(Vector#(k,a)) toGb = toPipeIn(gb);
   mkConnection(in, toGb);
   PipeOut#(Vector#(1,a)) fromGb = toPipeOut(gb);
   return mapPipe(head, fromGb);
endmodule

// 'j' is the width of the narrow end, and 'k' is the width of the wide end
typedef Vector#(j,PipeOut#(a))   FunnelPipe#(numeric type j, numeric type k, type a, numeric type bitsPerCycle);
typedef Vector#(k,PipeOut#(a)) UnFunnelPipe#(numeric type j, numeric type k, type a, numeric type bitsPerCycle);

typeclass FunnelPipesPipelined#(numeric type j, numeric type k, type a, numeric type bpc);
   module mkFunnelPipesPipelined#(Vector#(k,PipeOut#(a)) in) (FunnelPipe#(j,k,a,bpc));
   module mkFunnelPipesPipelinedRR#(Vector#(k,PipeOut#(a)) in, Integer c) (FunnelPipe#(j,k,a,bpc));
   module mkUnFunnelPipesPipelined#(Vector#(j,PipeOut#(Tuple2#(Bit#(TLog#(k)),a))) in) (UnFunnelPipe#(j,k,a,bpc));
   module mkUnFunnelPipesPipelinedRR#(Vector#(j,PipeOut#(a)) in, Integer c) (UnFunnelPipe#(j,k,a,bpc));
endtypeclass

function PipeOut#(b) pipeSecond(PipeOut#(Tuple2#(a,b)) x) =
   (interface PipeOut;
       method b first;
	  return tpl_2(x.first);
       endmethod
       method Action deq = x.deq;
       method Bool notEmpty = x.notEmpty;
    endinterface);

instance FunnelPipesPipelined#(1,1,a,bpc)
   provisos (Bits#(a,a__));
   module mkFunnelPipesPipelined#(Vector#(1,PipeOut#(a)) in) (FunnelPipe#(1,1,a,bpc));
      return in;
   endmodule
   module mkFunnelPipesPipelinedRR#(Vector#(1,PipeOut#(a)) in, Integer c) (FunnelPipe#(1,1,a,bpc));
      return in;
   endmodule
   module mkUnFunnelPipesPipelined#(Vector#(1,PipeOut#(Tuple2#(Bit#(0),a))) in) (UnFunnelPipe#(1,1,a,bpc));
      return map(pipeSecond, in);
   endmodule
   module mkUnFunnelPipesPipelinedRR#(Vector#(1,PipeOut#(a)) in, Integer c) (UnFunnelPipe#(1,1,a,bpc));
      return in;
   endmodule
endinstance

module mkUnFunnelPipesPipelinedInternal#(Vector#(1, PipeOut#(Tuple2#(Bit#(TLog#(k)),a))) in) (UnFunnelPipe#(1,k,a,bpc))
   provisos (Log#(k, logk),
	     Bits#(a,a__),
	     Add#(1,b__,k),
	     Div#(logk,bpc,stages));
   Vector#(k, PipeOut#(Tuple2#(Bit#(logk),a))) ins  = append(in,replicate(?));
   Vector#(k, PipeOut#(Tuple2#(Bit#(logk),a))) outs = newVector;
   for(Integer j = 0; j < valueOf(stages); j=j+1) begin
      for(Integer i = 0; i < 2**(j*valueOf(bpc)); i=i+1) begin
	 Integer bits = (j == valueOf(stages)-1) ? valueOf(logk)-(j*valueOf(bpc)) : valueOf(bpc);
	 function Bit#(bpc) sh(Bit#(bpc) x) = x<<(valueOf(bpc)-bits);
	 for(Integer l = 0; l < 2**bits; l=l+1)  begin
	    let buff <- mkFIFOF;
	    // extra conditional in case 'k' is not a power of 2
	    let idx = (2**bits)*i+l;
	    if (idx < valueOf(k)) begin
	       outs[idx] = toPipeOut(buff);
	       rule xfer if(tpl_1(ins[i].first)[(valueOf(logk)-1):(valueOf(logk)-valueOf(bpc))] == sh(fromInteger(l)));
		  match{.idx, .v} <- toGet(ins[i]).get;
		  buff.enq(tuple2(idx<<valueOf(bpc), v));
	       endrule
	    end
	 end
      end
      ins = outs;
   end
   return map(pipeSecond,outs);
endmodule

instance FunnelPipesPipelined#(1,k,a,bpc)
   provisos (Log#(k, logk),
	     Bits#(a,a__),
	     Add#(1,b__,k),
	     Div#(logk,bpc,stages));
   module mkFunnelPipesPipelined#(Vector#(k,PipeOut#(a)) in) (FunnelPipe#(1,k,a,bpc));
      Vector#(stages, Vector#(k, FIFOF#(a))) buffs  <- replicateM(replicateM(mkFIFOF));
      Vector#(TAdd#(stages,1), Vector#(k, PipeOut#(a))) infss = append(map(map(toPipeOut),buffs), cons(in,nil));
      for(Integer j = valueOf(stages); j > 0; j=j-1)
	 for(Integer i = 0; i < 2**(j*valueOf(bpc)) && i < valueOf(k); i=i+1)
	    mkConnection(infss[j][i], toPut(buffs[j-1][i/(2**valueOf(bpc))]));
      return cons(infss[0][0],nil);
   endmodule
   module mkFunnelPipesPipelinedRR#(Vector#(k,PipeOut#(a)) in, Integer c) (FunnelPipe#(1,k,a,bpc));
      Vector#(stages, Vector#(k, FIFOF#(a))) buffs  <- replicateM(replicateM(mkFIFOF));
      Vector#(TAdd#(stages,1), Vector#(k, PipeOut#(a))) infss = append(map(map(toPipeOut),buffs), cons(in,nil));
      for(Integer j = valueOf(stages); j > 0; j=j-1) begin
	 Vector#(k, FIFOF#(void)) ctrl  <- replicateM(mkFIFOF1());
   	 for(Integer i = 0; i < 2**(j*valueOf(bpc)) && i < valueOf(k); i=i+1) begin
   	    let first = i==0;
   	    Reg#(Bit#(32)) cnt <- mkReg(0);
   	    let maxp = (2**((valueOf(stages)-j)*valueOf(bpc)))*c;
   	    let last = (maxp*(i+1) >= valueOf(k)*c);
   	    if (maxp*(i+1) > valueOf(k)*c) begin
   	       maxp = valueOf(k)*c-maxp*i;
	    end
   	    let xfer_guard = True;
   	    if (!first)
   	       xfer_guard = xfer_guard && ctrl[(i-1)].notEmpty;
   	    if (!last)
   	       xfer_guard = xfer_guard && (!ctrl[i].notEmpty);
   	    rule xfer if (xfer_guard);
   	       let new_cnt = cnt+1;
   	       if (new_cnt==fromInteger(maxp)) begin
   		  cnt <= 0;
   		  if (!last)
   		     ctrl[i].enq(?);
   		  if (last)
   		     for(Integer ff = 0; ff < i; ff=ff+1)
   		     	ctrl[ff].deq;
   	       end
   	       else begin
   		  cnt <= new_cnt;
   	       end
   	       let v <- toGet(infss[j][i]).get;
   	       toPut(buffs[j-1][i/(2**valueOf(bpc))]).put(v);
   	    endrule
   	 end
      end
      return cons(infss[0][0],nil);
   endmodule
   module mkUnFunnelPipesPipelined#(Vector#(1, PipeOut#(Tuple2#(Bit#(logk),a))) in) (UnFunnelPipe#(1,k,a,bpc));
      let rv <- mkUnFunnelPipesPipelinedInternal(in);
      return rv;
   endmodule
   module mkUnFunnelPipesPipelinedRR#(Vector#(1, PipeOut#(a)) in, Integer c) (UnFunnelPipe#(1,k,a,bpc));
      Vector#(1, FIFOF#(Tuple2#(Bit#(logk),a))) tagged_in_buffers <- replicateM(mkFIFOF);
      Vector#(1, PipeOut#(Tuple2#(Bit#(logk),a))) tagged_in = map(toPipeOut, tagged_in_buffers);
      let rv <- mkUnFunnelPipesPipelinedInternal(tagged_in);
      Reg#(Bit#(TAdd#(logk,1))) dest <- mkReg(0);
      Reg#(Bit#(32)) cnt <- mkReg(0);
      rule fill;
	 let new_cnt = cnt+1;
	 let new_dest = dest;
	 if (new_cnt == fromInteger(c)) begin
	    new_cnt = 0;
	    new_dest = dest+1;
	    if(new_dest==fromInteger(valueOf(k)))
	       new_dest=0;
	 end
	 cnt <= new_cnt;
	 dest <= new_dest;
	 let v <- toGet(in[0]).get;
	 tagged_in_buffers[0].enq(tuple2(truncate(dest),v));
      endrule
      return rv;
   endmodule
endinstance

module mkUnfunnel#(PipeOut#(Vector#(m,a)) in)(PipeOut#(Vector#(mk, a)))
   provisos (Mul#(m, k, mk),
	     Bits#(a, asz),
	     Add#(1, b__, asz),
	     Add#(2, c__, mk),
	     Add#(d__, m, mk),
	     Add#(asz, m, e__),
	     Add#(asz, mk, f__));
   let m = fromInteger(valueOf(m));
   let mk = fromInteger(valueOf(mk));

   MIMOConfiguration cfg = defaultValue();
   MIMO#(m, mk, mk, a) mimo <- mkMIMO(cfg);
   rule consumer if (mimo.enqReadyN(m));
      Vector#(m, a) v = in.first();
      in.deq();
      mimo.enq(m, v);
   endrule

   method Vector#(mk, a) first() if (mimo.deqReadyN(mk));
      return mimo.first();
   endmethod
   method Action deq() if (mimo.deqReadyN(mk));
      mimo.deq(mk);
   endmethod
   method notEmpty();
      return mimo.deqReadyN(mk);
   endmethod
endmodule

module mkUnfunnelGB#(Clock slowClock, Reset slowReset, Clock fastClock, Reset fastReset, PipeOut#(Vector#(1,a)) in)(PipeOut#(Vector#(k, a)))
   provisos (Bits#(a, asz),
	     Add#(1, a__, k),
	     Add#(1, b__, asz),
	     Add#(1, c__, TMul#(2,k)),
	     Add#(k, d__, TMul#(2,k))
      );
   let k = fromInteger(valueOf(k));

   Gearbox#(1,k,a) gb <- mk1toNGearbox(fastClock, fastReset, slowClock, slowReset);
   PipeIn#(Vector#(1,a)) toGb = toPipeIn(gb);
   PipeOut#(Vector#(k,a)) fromGb = toPipeOut(gb);
   mkConnection(in,toGb);
   return fromGb;
endmodule: mkUnfunnelGB

module mkFunnelPipes#(Vector#(mk, PipeOut#(a)) ins)(Vector#(m, PipeOut#(a)))
   provisos (Mul#(m, k, mk),
	     Bits#(a, asz),
	     Log#(k,ksz)
      );
   let k = fromInteger(valueOf(k));
   let m = fromInteger(valueOf(m));
   let mk = fromInteger(valueOf(mk));

   Vector#(m, FIFOF#(a)) fifos <- replicateM(mkFIFOF);
   for (Integer i = 0; i < m; i = i+1) begin
      Reg#(Bit#(asz)) which <- mkReg(0);
      rule consumer;
	 let index = (which << valueOf(ksz)) + fromInteger(i);
	 let v <- toGet(ins[index]).get();
	 fifos[i].enq(v);
	 which <= (which + 1) % k;
      endrule
   end

   return map(toPipeOut, fifos);
endmodule

module mkFunnelPipes1#(Vector#(k, PipeOut#(a)) ins)(PipeOut#(a))
   provisos (Bits#(a, asz),
	     Log#(k,ksz)
      );
   let k = fromInteger(valueOf(k));
   Reg#(Bit#(ksz)) selector <- mkReg(0);

   method a first();
      return ins[selector].first();
   endmethod
   method Action deq();
      ins[selector].deq();
      if (selector == fromInteger(valueOf(k)-1))
	 selector <= 0;
   else
      selector <= selector + 1;
   endmethod
   method Bool notEmpty();
      return ins[selector].notEmpty();
   endmethod
endmodule

module mkUnfunnelPipes#(Vector#(m, PipeOut#(a)) ins)(Vector#(mk, PipeOut#(a)))
   provisos (Mul#(m, k, mk),
	     Log#(k,ksz),
	     Bits#(a,asz));

   let m = fromInteger(valueOf(m));
   let k = fromInteger(valueOf(k));
   let mk = fromInteger(valueOf(mk));

   Vector#(mk, FIFOF#(a)) fifos <- replicateM(mkFIFOF);
   for (Integer i = 0; i < m; i = i + 1) begin
      Reg#(Bit#(TAdd#(1,ksz))) which <- mkReg(0);
      rule consumer;
	 let index = which + fromInteger(i)*k;
	 let v <- toGet(ins[i]).get();
	 fifos[index].enq(v);
	 which <= (which + 1) % k;
      endrule
   end
   return map(toPipeOut, fifos);
endmodule

module mkRepeat#(UInt#(n) repetitions, PipeOut#(a) inpipe)(PipeOut#(a));
   Reg#(UInt#(n)) count <- mkReg(0);
   method first = inpipe.first;
   method Action deq();
      let c = count + 1;
      if (count == (repetitions - 1)) begin
	 c = 0;
	 inpipe.deq();
      end
      count <= c;
   endmethod
   method notEmpty = inpipe.notEmpty;
endmodule

module mkPipelinedForkVector#(PipeOut#(a) inpipe, Integer id)(UnFunnelPipe#(1,k,a,bpc))
   provisos ( Bits#(a,a__)
	     ,Add#(1,b__,k)
	     ,Log#(k,logk)
	     ,Div#(logk,bpc,stages));
   Vector#(k, FIFOF#(a))  buffs = newVector;
   Vector#(k, PipeOut#(a)) infs = cons(inpipe,replicate(?));
   for(Integer j = 0; j < valueOf(stages); j=j+1)begin
      for(Integer i = 0; i < 2**((j+1)*valueOf(bpc)) && i < valueOf(k); i=i+1)
	 buffs[i] <- mkFIFOF;
      rule xfer;
      	 for(Integer i = 0; i < 2**(j*valueOf(bpc)) && i < valueOf(k); i=i+1) begin
      	    for(Integer l = 0; l < 2**valueOf(bpc) && l < valueOf(k); l=l+1) begin
	       Integer idx = (i*(2**valueOf(bpc)))+l;
      	       if (idx < valueOf(k))
		  buffs[idx].enq(infs[i].first);
      	    end
      	    infs[i].deq;
      	 end
      endrule
      infs = map(toPipeOut, buffs);
   end
   return infs;
endmodule

module mkForkVector#(PipeOut#(a) inpipe)(Vector#(n, PipeOut#(a)))
   provisos (Bits#(a, asz));
   Vector#(n, FIFOF#(a)) fifos <- replicateM(mkFIFOF());
   rule forkelts;
      let v = inpipe.first();
      inpipe.deq;
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 fifos[i].enq(v);
      end
   endrule
   return map(toPipeOut, fifos);
endmodule

module mkSizedForkVector#(Integer size, PipeOut#(a) inpipe)(Vector#(n, PipeOut#(a)))
   provisos (Bits#(a, asz));
   Vector#(n, FIFOF#(a)) fifos <- replicateM(mkSizedFIFOF(size));
   rule forkelts;
      let v = inpipe.first();
      inpipe.deq;
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 fifos[i].enq(v);
      end
   endrule
   return map(toPipeOut, fifos);
endmodule

module mkJoin#(function c f(a av, b bv), PipeOut#(a) apipe, PipeOut#(b) bpipe)(PipeOut#(c));
   method c first();
      let av = apipe.first();
      let bv = bpipe.first();
      return f(av, bv);
   endmethod
   method Action deq();
      apipe.deq();
      bpipe.deq();
   endmethod
   method Bool notEmpty();
      return apipe.notEmpty() && bpipe.notEmpty();
   endmethod
endmodule

module mkJoinBuffered#(function c f(a av, b bv), PipeOut#(a) apipe, PipeOut#(b) bpipe)(PipeOut#(c))
   provisos (Bits#(c, csz));
   FIFOF#(c) joinFifo <- mkFIFOF();
   rule joinrule;
      let av <- toGet(apipe).get();
      let bv <- toGet(bpipe).get();
      joinFifo.enq(f(av, bv));
   endrule
   return toPipeOut(joinFifo);
endmodule

module mkJoinVector#(function b f(Vector#(n, a) av), Vector#(n, PipeOut#(a)) apipes)(PipeOut#(b))
   provisos (Bits#(Vector#(n,a),vasz));
   method b first();
      function a getfirst(PipeOut#(a) pipein); return pipein.first(); endfunction
      Vector#(n,a) vec = map(getfirst, apipes);
      return f(vec);
   endmethod
   method Action deq();
      function a getfirst(PipeOut#(a) pipein); return pipein.first(); endfunction
      for (Integer i = 0; i < valueOf(n); i = i + 1)
	 apipes[i].deq();
   endmethod
   method Bool notEmpty();
      function Bool getNotEmpty(PipeOut#(a) pipein); return pipein.notEmpty(); endfunction
      function Bool myand(Bool a, Bool b); return a && b; endfunction
      return foldl(myand, True, map(getNotEmpty, apipes));
   endmethod
endmodule

function PipeOut#(b) mapPipe(function b f(a av), PipeOut#(a) apipe);
   return (interface PipeOut#(b);
      method b first();
	 let av = apipe.first();
	 return f(av);
      endmethod
      method Action deq();
	 apipe.deq();
      endmethod
      method Bool notEmpty();
	 return apipe.notEmpty();
      endmethod
      endinterface);
endfunction

function PipeIn#(a) mapPipeIn(function b f(a av), PipeIn#(b) apipe);
   return (interface PipeIn#(b);
	      method Action enq(a v);
		 apipe.enq(f(v));
	      endmethod
	      method Bool notFull();
		 return apipe.notFull();
	      endmethod
	   endinterface);
endfunction

// buffered version of mapPipe
module mkMapPipe#(function b f(a av), PipeOut#(a) apipe)(PipeOut#(b))
   provisos (Bits#(b,bsz));
   FIFOF#(b) fifo <- mkFIFOF();
   rule compute;
      let v <- toGet(apipe).get();
      fifo.enq(f(v));
   endrule
   return toPipeOut(fifo);
endmodule

typedef (function tb f(ta x)) CombinePipe#(type ta, type tb);

typeclass ReducePipe#( numeric type n, type a);
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			 PipeOut#(Vector#(n,a)) inpipe,
			 PipeOut#(a) ifc);
   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			  Vector#(n,PipeOut#(a)) inpipe,
			  PipeOut#(a) ifc);
endtypeclass
instance ReducePipe#(1, a);
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
				 PipeOut#(Vector#(1,a)) inpipe,
				 PipeOut#(a) ifc);
      let pipe = mapPipe(head, inpipe);
      return pipe;
   endmodule
   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
				  Vector#(1,PipeOut#(a)) inpipes,
				  PipeOut#(a) ifc);
      return inpipes[0];
   endmodule
endinstance
instance ReducePipe#(2, a)
   provisos(Bits#(a,a__));
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			 PipeOut#(Vector#(2,a)) inpipe,
			 PipeOut#(a) ifc);
      function a foo(Vector#(2,a) invec);
	 return combinepipe(tuple2(invec[0], invec[1]));
      endfunction
      let pipe <- mkMapPipe(foo, inpipe);
      return pipe;
   endmodule
   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			  Vector#(2,PipeOut#(a)) inpipes,
			  PipeOut#(a) ifc);
      function a foo(Tuple2#(a,a) invec);
	 return combinepipe(invec);
      endfunction
      let pipe <- mkMapPipe(foo, zipPipeOut(inpipes[0], inpipes[1]));
      return pipe;
   endmodule
endinstance

instance ReducePipe#(n, a)
   provisos (Add#(TDiv#(n,2), a__, n),
	     Bits#(Vector#(TDiv#(n,2), a), b__),
	     ReducePipe#(TDiv#(n,2),a),
	     ReducePipe#(TSub#(n, TDiv#(n, 2)), a)
      );
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			 PipeOut#(Vector#(n,a)) inpipe,
			 PipeOut#(a) ifc);
      FIFOF#(Vector#(TDiv#(n,2),a)) infifo0 <- mkFIFOF;
      FIFOF#(Vector#(TSub#(n,TDiv#(n,2)),a)) infifo1 <- mkFIFOF;
      rule splitinput;
	 let v = inpipe.first();
	 inpipe.deq();
	 infifo0.enq(takeAt(0, v));
	 infifo1.enq(takeAt(valueOf(TDiv#(n,2)), v));
      endrule
      PipeOut#(Vector#(TDiv#(n,2),a)) inpipe0 = toPipeOut(infifo0);
      PipeOut#(Vector#(TSub#(n,TDiv#(n,2)),a)) inpipe1 = toPipeOut(infifo1);

      PipeOut#(a) p0 <- mkReducePipe(combinepipe, inpipe0);
      PipeOut#(a) p1 <- mkReducePipe(combinepipe, inpipe1);

      function a foo(Tuple2#(a,a) invec);
	 return combinepipe(invec);
      endfunction
      let pipe <- mkMapPipe(foo,zipPipeOut(p0, p1));
      return pipe;
   endmodule

   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			  Vector#(n, PipeOut#(a)) inpipes,
			  PipeOut#(a) ifc);
      Vector#(TDiv#(n,2),PipeOut#(a)) pipes0 = takeAt(0, inpipes);
      Vector#(TSub#(n,TDiv#(n,2)),PipeOut#(a)) pipes1 = takeAt(valueOf(TDiv#(n,2)), inpipes);

      PipeOut#(a) p0 <- mkReducePipes(combinepipe, pipes0);
      PipeOut#(a) p1 <- mkReducePipes(combinepipe, pipes1);

      function a foo(Tuple2#(a,a) invec);
	 return combinepipe(invec);
      endfunction
      let pipe <- mkMapPipe(foo,zipPipeOut(p0, p1));
      return pipe;
   endmodule
endinstance

interface FirstLastPipe#(type a);
   interface PipeOut#(Tuple2#(Bool,Bool)) pipe;
   method Action start(a count);
endinterface

module mkFirstLastPipe(FirstLastPipe#(a))
   provisos (Bits#(a,asz), Ord#(a), Arith#(a), Eq#(a));
   Reg#(a) countReg <- mkReg(0);
   Reg#(Bool) firstReg <- mkReg(False);
   Reg#(Bool) lastReg <- mkReg(False);
   interface PipeOut pipe;
      method Tuple2#(Bool, Bool) first();
	 return tuple2(firstReg, lastReg);
      endmethod
      method Action deq() if (countReg > 0);
	 firstReg <= False;
	 let c = countReg - 1;
	 if (c == 1)
	    lastReg <= True;
	 countReg <= c;
      endmethod
      method Bool notEmpty();
	 return countReg > 0;
      endmethod
   endinterface
   method Action start(a count) if (countReg == 0);
      firstReg <= True;
      lastReg <= False;
      countReg <= count;
   endmethod
endmodule

typedef struct {
   a xbase;
   a xlimit;
   a xstep;
} RangeConfig#(type a) deriving (Bits, FShow);

interface RangePipeIfc#(type a);
   interface PipeOut#(a) pipe;
   method Bool isFirst();
   method Bool isLast();
   method Action start(RangeConfig#(a) cfg);
endinterface

module mkRangePipeOut(RangePipeIfc#(a)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a));
   Reg#(a) x <- mkReg(1);
   Reg#(a) xbase <- mkReg(0);
   Reg#(a) xstep <- mkReg(1);
   // inclusive limit
   Reg#(a) xlimit <- mkReg(0);
   Reg#(Bool) first <- mkReg(False);
   Reg#(Bool) last <- mkReg(False);
   Bool verbose = False;
   interface PipeOut pipe;
      method a first();
	 return x;
      endmethod
      method Action deq if (x <= xlimit);
	 x <= x + xstep;
	 first <= False;
	 last <= (x+xstep >= xlimit);
      endmethod
      method Bool notEmpty();
	 return (x <= xlimit);
      endmethod
   endinterface
   method Action start(RangeConfig#(a) cfg) if (x > xlimit);
      x <= cfg.xbase;
      xbase <= cfg.xbase;
      xstep <= cfg.xstep;

      // inclusive limit, should update interface accordingly
      xlimit <= cfg.xlimit-cfg.xstep;

      first <= True;
      last <= (cfg.xbase+cfg.xstep >= cfg.xlimit);
      if (verbose) $display("mkRangePipeOut xbase=%d xstep=%d cfg.xlimit=%d xlimit=%d last=%d", cfg.xbase, cfg.xstep, cfg.xlimit, (cfg.xlimit-cfg.xstep), (cfg.xbase+cfg.xstep >= cfg.xlimit));
   endmethod
   method Bool isFirst() = first;
   method Bool isLast() = last;
endmodule: mkRangePipeOut

// Copyright (c) 2014 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFOF::*;
import GetPut::*;
import Connectable::*;
import Vector::*;
import MIMO::*;
import DefaultValue::*;
import Gearbox::*;

interface PipeIn#(type a);
   method Action enq(a v);
   method Bool notFull();
endinterface

interface PipeOut#(type a);
   method a first();
   method Action deq();
   method Bool notEmpty();
endinterface

typeclass ToPipeIn#(type a, type b);
   function PipeIn#(a) toPipeIn(b in);
endtypeclass

typeclass ToPipeOut#(type a, type b);
   function PipeOut#(a) toPipeOut(b in);
endtypeclass

typeclass MkPipeOut#(type a, type b);
   module mkPipeOut#(b in)(PipeOut#(a));
endtypeclass

typeclass MkPipeIn#(type a, type b);
   module mkPipeIn#(b in)(PipeIn#(a));
endtypeclass

instance ToPipeIn#(a, FIFOF#(a));
   function PipeIn#(a) toPipeIn(FIFOF#(a) in);
      return (interface PipeIn#(a);
		 method enq = in.enq;
		 method notFull = in.notFull;
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(a, function a pipefn());
   function PipeOut#(a) toPipeOut(function a pipefn());
      return (interface PipeOut#(a);
		 method first(); return pipefn(); endmethod
		 method Action deq(); endmethod
		 method Bool notEmpty(); return False; endmethod
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(a, Reg#(a));
   function PipeOut#(a) toPipeOut(Reg#(a) in);
      return (interface PipeOut#(a);
		 method first(); return in; endmethod
		 method Action deq(); endmethod
		 method Bool notEmpty(); return False; endmethod
	      endinterface);
   endfunction
endinstance

instance ToPipeIn#(Vector#(m, a), Gearbox#(m, n, a));
   function PipeIn#(Vector#(m, a)) toPipeIn(Gearbox#(m, n, a) in);
      return (interface PipeIn#(Vector#(m, a));
		 method enq = in.enq;
		 method notFull = in.notFull;
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(a, FIFOF#(a));
   function PipeOut#(a) toPipeOut(FIFOF#(a) in);
      return (interface PipeOut#(a);
		 method first = in.first;
		 method deq = in.deq;
		 method notEmpty = in.notEmpty;
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(Vector#(n,a), MIMO#(k,n,sz,a));
   function PipeOut#(Vector#(n,a)) toPipeOut(MIMO#(k,n,sz,a) in);
      return (interface PipeOut#(a);
		 method first = in.first;
		 method Action deq() if (in.deqReadyN(fromInteger(valueOf(n))));
		    in.deq(fromInteger(valueOf(n)));
		 endmethod
		 method Bool notEmpty();
		    return in.deqReadyN(fromInteger(valueOf(n)));
		 endmethod
	      endinterface);
   endfunction
endinstance

instance ToPipeOut#(Vector#(n, a), Gearbox#(m, n, a));
   function PipeOut#(Vector#(n, a)) toPipeOut(Gearbox#(m, n, a) in);
      return (interface PipeOut#(Vector#(n,a));
		 method first = in.first;
		 method deq = in.deq;
		 method notEmpty = in.notEmpty;
	      endinterface);
   endfunction
endinstance

instance MkPipeOut#(a, Get#(a))
   provisos (Bits#(a, asz));
   module mkPipeOut#(Get#(a) in)(PipeOut#(a));
      FIFOF#(a) fifo <- mkFIFOF();
      rule connect;
	 let v <- in.get();
	 fifo.enq(v);
      endrule
      return toPipeOut(fifo);
   endmodule
endinstance

instance MkPipeIn#(a, Put#(a))
   provisos (Bits#(a, asz));
   module mkPipeIn#(Put#(a) out)(PipeIn#(a));
      FIFOF#(a) fifo <- mkFIFOF();
      rule connect;
	 let v <- toGet(fifo).get;
	 out.put(v);
      endrule
      return toPipeIn(fifo);
   endmodule
endinstance

function PipeOut#(a) toCountedPipeOut(Reg#(Bit#(n)) r, PipeOut#(a) pipe);
   return (interface PipeOut#(Vector#(n,a));
	      method first = pipe.first;
	      method Action deq(); pipe.deq; r <= r + 1; endmethod
	      method notEmpty = pipe.notEmpty;
	   endinterface);
endfunction

instance ToGet #(PipeOut #(a), a);
   function Get #(a) toGet (PipeOut #(a) po);
      return (interface Get;
                 method ActionValue #(a) get ();
                    po.deq ();
                    return po.first ();
                 endmethod
              endinterface);
   endfunction
endinstance

instance ToPut #(PipeIn #(a), a);
   function Put #(a) toPut (PipeIn #(a) pi);
      return (interface Put;
		 method Action put(a v);
                    pi.enq (v);
                 endmethod
              endinterface);
   endfunction
endinstance

instance Connectable#(PipeOut#(a),Put#(a));
   module mkConnection#(PipeOut#(a) in, Put#(a) out)(Empty);
      rule connect;
	 let v = in.first;
	 in.deq();
	 out.put(v);
      endrule
   endmodule
endinstance

instance Connectable#(PipeOut#(a),PipeIn#(a));
   module mkConnection#(PipeOut#(a) in, PipeIn#(a) out)(Empty);
      rule connect;
	 let v = in.first;
	 in.deq();
	 out.enq(v);
      endrule
   endmodule
endinstance

function PipeOut#(a) unvectorPipeOut(PipeOut#(Vector#(1,a)) in);
   return (interface PipeOut#(a);
	      method first = in.first[0];
	      method deq = in.deq;
	      method notEmpty = in.notEmpty;
	   endinterface);
endfunction

function PipeOut#(Tuple2#(a,b)) zipPipeOut(PipeOut#(a) ina, PipeOut#(b) inb);
      return (interface PipeOut#(Tuple2#(a,b));
		 method Tuple2#(a,b) first(); return tuple2(ina.first, inb.first); endmethod
		 method Action deq(); ina.deq(); inb.deq(); endmethod
		 method Bool notEmpty(); return ina.notEmpty() && inb.notEmpty(); endmethod
	      endinterface);
   endfunction

module mkFunnel#(PipeOut#(Vector#(mk,a)) in)(PipeOut#(Vector#(m, a)))
   provisos (Mul#(m, k, mk),
	     Bits#(a, asz),
	     Add#(a__, TMul#(asz, m), TMul#(asz, mk)),
	     Add#(1, b__, asz),
	     Add#(2, c__, mk),
	     Add#(d__, m, mk),
	     Add#(asz, m, e__),
	     Add#(asz, mk, f__));
   let m = fromInteger(valueOf(m));
   let mk = fromInteger(valueOf(mk));

   MIMOConfiguration cfg = defaultValue();
   MIMO#(mk, m, mk, a) mimo <- mkMIMO(cfg);
   rule consumer if (mimo.enqReadyN(mk));
      Vector#(mk, a) v = in.first();
      in.deq();
      mimo.enq(mk, v);
   endrule

   method Vector#(m, a) first() if (mimo.deqReadyN(m));
      return mimo.first();
   endmethod
   method Action deq() if (mimo.deqReadyN(m));
      mimo.deq(m);
   endmethod
   method notEmpty();
      return mimo.deqReadyN(m);
   endmethod
endmodule

module mkFunnel1#(PipeOut#(Vector#(k,a)) in)(PipeOut#(a))
   provisos (Bits#(a, asz), Log#(k,ksz));

   Reg#(Bit#(ksz)) selector <- mkReg(0);

   method a first();
      return in.first[selector];
   endmethod
   method Action deq();
      if (selector == fromInteger(valueOf(k)-1)) begin
	 in.deq();
	 selector <= 0;
      end
      else
	 selector <= selector + 1;
   endmethod
   method notEmpty();
      return in.notEmpty();
   endmethod
endmodule

module mkFunnelGB1#(Clock slowClock, Reset slowReset, Clock fastClock, Reset fastReset, PipeOut#(Vector#(k,a)) in)(PipeOut#(a))
   provisos (Bits#(a, asz), Log#(k,ksz), Add#(1,a__,k));

   Gearbox#(k,1,a) gb <- mkNto1Gearbox(slowClock, slowReset, fastClock, fastReset);
   PipeIn#(Vector#(k,a)) toGb = toPipeIn(gb);
   mkConnection(in, toGb);
   PipeOut#(Vector#(1,a)) fromGb = toPipeOut(gb);
   return mapPipe(head, fromGb);
endmodule

// 'j' is the width of the narrow end, and 'k' is the width of the wide end
typedef Vector#(j,PipeOut#(a))   FunnelPipe#(numeric type j, numeric type k, type a, numeric type bitsPerCycle);
typedef Vector#(k,PipeOut#(a)) UnFunnelPipe#(numeric type j, numeric type k, type a, numeric type bitsPerCycle);

typeclass FunnelPipesPipelined#(numeric type j, numeric type k, type a, numeric type bpc);
   module mkFunnelPipesPipelined#(Vector#(k,PipeOut#(a)) in) (FunnelPipe#(j,k,a,bpc));
   module mkFunnelPipesPipelinedRR#(Vector#(k,PipeOut#(a)) in, Integer c) (FunnelPipe#(j,k,a,bpc));
   module mkUnFunnelPipesPipelined#(Vector#(j,PipeOut#(Tuple2#(Bit#(TLog#(k)),a))) in) (UnFunnelPipe#(j,k,a,bpc));
   module mkUnFunnelPipesPipelinedRR#(Vector#(j,PipeOut#(a)) in, Integer c) (UnFunnelPipe#(j,k,a,bpc));
endtypeclass

function PipeOut#(b) pipeSecond(PipeOut#(Tuple2#(a,b)) x) =
   (interface PipeOut;
       method b first;
	  return tpl_2(x.first);
       endmethod
       method Action deq = x.deq;
       method Bool notEmpty = x.notEmpty;
    endinterface);

instance FunnelPipesPipelined#(1,1,a,bpc)
   provisos (Bits#(a,a__));
   module mkFunnelPipesPipelined#(Vector#(1,PipeOut#(a)) in) (FunnelPipe#(1,1,a,bpc));
      return in;
   endmodule
   module mkFunnelPipesPipelinedRR#(Vector#(1,PipeOut#(a)) in, Integer c) (FunnelPipe#(1,1,a,bpc));
      return in;
   endmodule
   module mkUnFunnelPipesPipelined#(Vector#(1,PipeOut#(Tuple2#(Bit#(0),a))) in) (UnFunnelPipe#(1,1,a,bpc));
      return map(pipeSecond, in);
   endmodule
   module mkUnFunnelPipesPipelinedRR#(Vector#(1,PipeOut#(a)) in, Integer c) (UnFunnelPipe#(1,1,a,bpc));
      return in;
   endmodule
endinstance

module mkUnFunnelPipesPipelinedInternal#(Vector#(1, PipeOut#(Tuple2#(Bit#(TLog#(k)),a))) in) (UnFunnelPipe#(1,k,a,bpc))
   provisos (Log#(k, logk),
	     Bits#(a,a__),
	     Add#(1,b__,k),
	     Div#(logk,bpc,stages));
   Vector#(k, PipeOut#(Tuple2#(Bit#(logk),a))) ins  = append(in,replicate(?));
   Vector#(k, PipeOut#(Tuple2#(Bit#(logk),a))) outs = newVector;
   for(Integer j = 0; j < valueOf(stages); j=j+1) begin
      for(Integer i = 0; i < 2**(j*valueOf(bpc)); i=i+1) begin
	 Integer bits = (j == valueOf(stages)-1) ? valueOf(logk)-(j*valueOf(bpc)) : valueOf(bpc);
	 function Bit#(bpc) sh(Bit#(bpc) x) = x<<(valueOf(bpc)-bits);
	 for(Integer l = 0; l < 2**bits; l=l+1)  begin
	    let buff <- mkFIFOF;
	    // extra conditional in case 'k' is not a power of 2
	    let idx = (2**bits)*i+l;
	    if (idx < valueOf(k)) begin
	       outs[idx] = toPipeOut(buff);
	       rule xfer if(tpl_1(ins[i].first)[(valueOf(logk)-1):(valueOf(logk)-valueOf(bpc))] == sh(fromInteger(l)));
		  match{.idx, .v} <- toGet(ins[i]).get;
		  buff.enq(tuple2(idx<<valueOf(bpc), v));
	       endrule
	    end
	 end
      end
      ins = outs;
   end
   return map(pipeSecond,outs);
endmodule

instance FunnelPipesPipelined#(1,k,a,bpc)
   provisos (Log#(k, logk),
	     Bits#(a,a__),
	     Add#(1,b__,k),
	     Div#(logk,bpc,stages));
   module mkFunnelPipesPipelined#(Vector#(k,PipeOut#(a)) in) (FunnelPipe#(1,k,a,bpc));
      Vector#(stages, Vector#(k, FIFOF#(a))) buffs  <- replicateM(replicateM(mkFIFOF));
      Vector#(TAdd#(stages,1), Vector#(k, PipeOut#(a))) infss = append(map(map(toPipeOut),buffs), cons(in,nil));
      for(Integer j = valueOf(stages); j > 0; j=j-1)
	 for(Integer i = 0; i < 2**(j*valueOf(bpc)) && i < valueOf(k); i=i+1)
	    mkConnection(infss[j][i], toPut(buffs[j-1][i/(2**valueOf(bpc))]));
      return cons(infss[0][0],nil);
   endmodule
   module mkFunnelPipesPipelinedRR#(Vector#(k,PipeOut#(a)) in, Integer c) (FunnelPipe#(1,k,a,bpc));
      Vector#(stages, Vector#(k, FIFOF#(a))) buffs  <- replicateM(replicateM(mkFIFOF));
      Vector#(TAdd#(stages,1), Vector#(k, PipeOut#(a))) infss = append(map(map(toPipeOut),buffs), cons(in,nil));
      for(Integer j = valueOf(stages); j > 0; j=j-1) begin
	 Vector#(k, FIFOF#(void)) ctrl  <- replicateM(mkFIFOF1());
   	 for(Integer i = 0; i < 2**(j*valueOf(bpc)) && i < valueOf(k); i=i+1) begin
   	    let first = i==0;
   	    Reg#(Bit#(32)) cnt <- mkReg(0);
   	    let maxp = (2**((valueOf(stages)-j)*valueOf(bpc)))*c;
   	    let last = (maxp*(i+1) >= valueOf(k)*c);
   	    if (maxp*(i+1) > valueOf(k)*c) begin
   	       maxp = valueOf(k)*c-maxp*i;
	    end
   	    let xfer_guard = True;
   	    if (!first)
   	       xfer_guard = xfer_guard && ctrl[(i-1)].notEmpty;
   	    if (!last)
   	       xfer_guard = xfer_guard && (!ctrl[i].notEmpty);
   	    rule xfer if (xfer_guard);
   	       let new_cnt = cnt+1;
   	       if (new_cnt==fromInteger(maxp)) begin
   		  cnt <= 0;
   		  if (!last)
   		     ctrl[i].enq(?);
   		  if (last)
   		     for(Integer ff = 0; ff < i; ff=ff+1)
   		     	ctrl[ff].deq;
   	       end
   	       else begin
   		  cnt <= new_cnt;
   	       end
   	       let v <- toGet(infss[j][i]).get;
   	       toPut(buffs[j-1][i/(2**valueOf(bpc))]).put(v);
   	    endrule
   	 end
      end
      return cons(infss[0][0],nil);
   endmodule
   module mkUnFunnelPipesPipelined#(Vector#(1, PipeOut#(Tuple2#(Bit#(logk),a))) in) (UnFunnelPipe#(1,k,a,bpc));
      let rv <- mkUnFunnelPipesPipelinedInternal(in);
      return rv;
   endmodule
   module mkUnFunnelPipesPipelinedRR#(Vector#(1, PipeOut#(a)) in, Integer c) (UnFunnelPipe#(1,k,a,bpc));
      Vector#(1, FIFOF#(Tuple2#(Bit#(logk),a))) tagged_in_buffers <- replicateM(mkFIFOF);
      Vector#(1, PipeOut#(Tuple2#(Bit#(logk),a))) tagged_in = map(toPipeOut, tagged_in_buffers);
      let rv <- mkUnFunnelPipesPipelinedInternal(tagged_in);
      Reg#(Bit#(TAdd#(logk,1))) dest <- mkReg(0);
      Reg#(Bit#(32)) cnt <- mkReg(0);
      rule fill;
	 let new_cnt = cnt+1;
	 let new_dest = dest;
	 if (new_cnt == fromInteger(c)) begin
	    new_cnt = 0;
	    new_dest = dest+1;
	    if(new_dest==fromInteger(valueOf(k)))
	       new_dest=0;
	 end
	 cnt <= new_cnt;
	 dest <= new_dest;
	 let v <- toGet(in[0]).get;
	 tagged_in_buffers[0].enq(tuple2(truncate(dest),v));
      endrule
      return rv;
   endmodule
endinstance

module mkUnfunnel#(PipeOut#(Vector#(m,a)) in)(PipeOut#(Vector#(mk, a)))
   provisos (Mul#(m, k, mk),
	     Bits#(a, asz),
	     Add#(1, b__, asz),
	     Add#(2, c__, mk),
	     Add#(d__, m, mk),
	     Add#(asz, m, e__),
	     Add#(asz, mk, f__));
   let m = fromInteger(valueOf(m));
   let mk = fromInteger(valueOf(mk));

   MIMOConfiguration cfg = defaultValue();
   MIMO#(m, mk, mk, a) mimo <- mkMIMO(cfg);
   rule consumer if (mimo.enqReadyN(m));
      Vector#(m, a) v = in.first();
      in.deq();
      mimo.enq(m, v);
   endrule

   method Vector#(mk, a) first() if (mimo.deqReadyN(mk));
      return mimo.first();
   endmethod
   method Action deq() if (mimo.deqReadyN(mk));
      mimo.deq(mk);
   endmethod
   method notEmpty();
      return mimo.deqReadyN(mk);
   endmethod
endmodule

module mkUnfunnelGB#(Clock slowClock, Reset slowReset, Clock fastClock, Reset fastReset, PipeOut#(Vector#(1,a)) in)(PipeOut#(Vector#(k, a)))
   provisos (Bits#(a, asz),
	     Add#(1, a__, k),
	     Add#(1, b__, asz),
	     Add#(1, c__, TMul#(2,k)),
	     Add#(k, d__, TMul#(2,k))
      );
   let k = fromInteger(valueOf(k));

   Gearbox#(1,k,a) gb <- mk1toNGearbox(fastClock, fastReset, slowClock, slowReset);
   PipeIn#(Vector#(1,a)) toGb = toPipeIn(gb);
   PipeOut#(Vector#(k,a)) fromGb = toPipeOut(gb);
   mkConnection(in,toGb);
   return fromGb;
endmodule: mkUnfunnelGB

module mkFunnelPipes#(Vector#(mk, PipeOut#(a)) ins)(Vector#(m, PipeOut#(a)))
   provisos (Mul#(m, k, mk),
	     Bits#(a, asz),
	     Log#(k,ksz)
      );
   let k = fromInteger(valueOf(k));
   let m = fromInteger(valueOf(m));
   let mk = fromInteger(valueOf(mk));

   Vector#(m, FIFOF#(a)) fifos <- replicateM(mkFIFOF);
   for (Integer i = 0; i < m; i = i+1) begin
      Reg#(Bit#(asz)) which <- mkReg(0);
      rule consumer;
	 let index = (which << valueOf(ksz)) + fromInteger(i);
	 let v <- toGet(ins[index]).get();
	 fifos[i].enq(v);
	 which <= (which + 1) % k;
      endrule
   end

   return map(toPipeOut, fifos);
endmodule

module mkFunnelPipes1#(Vector#(k, PipeOut#(a)) ins)(PipeOut#(a))
   provisos (Bits#(a, asz),
	     Log#(k,ksz)
      );
   let k = fromInteger(valueOf(k));
   Reg#(Bit#(ksz)) selector <- mkReg(0);

   method a first();
      return ins[selector].first();
   endmethod
   method Action deq();
      ins[selector].deq();
      if (selector == fromInteger(valueOf(k)-1))
	 selector <= 0;
   else
      selector <= selector + 1;
   endmethod
   method Bool notEmpty();
      return ins[selector].notEmpty();
   endmethod
endmodule

module mkUnfunnelPipes#(Vector#(m, PipeOut#(a)) ins)(Vector#(mk, PipeOut#(a)))
   provisos (Mul#(m, k, mk),
	     Log#(k,ksz),
	     Bits#(a,asz));

   let m = fromInteger(valueOf(m));
   let k = fromInteger(valueOf(k));
   let mk = fromInteger(valueOf(mk));

   Vector#(mk, FIFOF#(a)) fifos <- replicateM(mkFIFOF);
   for (Integer i = 0; i < m; i = i + 1) begin
      Reg#(Bit#(TAdd#(1,ksz))) which <- mkReg(0);
      rule consumer;
	 let index = which + fromInteger(i)*k;
	 let v <- toGet(ins[i]).get();
	 fifos[index].enq(v);
	 which <= (which + 1) % k;
      endrule
   end
   return map(toPipeOut, fifos);
endmodule

module mkRepeat#(UInt#(n) repetitions, PipeOut#(a) inpipe)(PipeOut#(a));
   Reg#(UInt#(n)) count <- mkReg(0);
   method first = inpipe.first;
   method Action deq();
      let c = count + 1;
      if (count == (repetitions - 1)) begin
	 c = 0;
	 inpipe.deq();
      end
      count <= c;
   endmethod
   method notEmpty = inpipe.notEmpty;
endmodule

module mkPipelinedForkVector#(PipeOut#(a) inpipe, Integer id)(UnFunnelPipe#(1,k,a,bpc))
   provisos ( Bits#(a,a__)
	     ,Add#(1,b__,k)
	     ,Log#(k,logk)
	     ,Div#(logk,bpc,stages));
   Vector#(k, FIFOF#(a))  buffs = newVector;
   Vector#(k, PipeOut#(a)) infs = cons(inpipe,replicate(?));
   for(Integer j = 0; j < valueOf(stages); j=j+1)begin
      for(Integer i = 0; i < 2**((j+1)*valueOf(bpc)) && i < valueOf(k); i=i+1)
	 buffs[i] <- mkFIFOF;
      rule xfer;
      	 for(Integer i = 0; i < 2**(j*valueOf(bpc)) && i < valueOf(k); i=i+1) begin
      	    for(Integer l = 0; l < 2**valueOf(bpc) && l < valueOf(k); l=l+1) begin
	       Integer idx = (i*(2**valueOf(bpc)))+l;
      	       if (idx < valueOf(k))
		  buffs[idx].enq(infs[i].first);
      	    end
      	    infs[i].deq;
      	 end
      endrule
      infs = map(toPipeOut, buffs);
   end
   return infs;
endmodule

module mkForkVector#(PipeOut#(a) inpipe)(Vector#(n, PipeOut#(a)))
   provisos (Bits#(a, asz));
   Vector#(n, FIFOF#(a)) fifos <- replicateM(mkFIFOF());
   rule forkelts;
      let v = inpipe.first();
      inpipe.deq;
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 fifos[i].enq(v);
      end
   endrule
   return map(toPipeOut, fifos);
endmodule

module mkSizedForkVector#(Integer size, PipeOut#(a) inpipe)(Vector#(n, PipeOut#(a)))
   provisos (Bits#(a, asz));
   Vector#(n, FIFOF#(a)) fifos <- replicateM(mkSizedFIFOF(size));
   rule forkelts;
      let v = inpipe.first();
      inpipe.deq;
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 fifos[i].enq(v);
      end
   endrule
   return map(toPipeOut, fifos);
endmodule

module mkJoin#(function c f(a av, b bv), PipeOut#(a) apipe, PipeOut#(b) bpipe)(PipeOut#(c));
   method c first();
      let av = apipe.first();
      let bv = bpipe.first();
      return f(av, bv);
   endmethod
   method Action deq();
      apipe.deq();
      bpipe.deq();
   endmethod
   method Bool notEmpty();
      return apipe.notEmpty() && bpipe.notEmpty();
   endmethod
endmodule

module mkJoinBuffered#(function c f(a av, b bv), PipeOut#(a) apipe, PipeOut#(b) bpipe)(PipeOut#(c))
   provisos (Bits#(c, csz));
   FIFOF#(c) joinFifo <- mkFIFOF();
   rule joinrule;
      let av <- toGet(apipe).get();
      let bv <- toGet(bpipe).get();
      joinFifo.enq(f(av, bv));
   endrule
   return toPipeOut(joinFifo);
endmodule

module mkJoinVector#(function b f(Vector#(n, a) av), Vector#(n, PipeOut#(a)) apipes)(PipeOut#(b))
   provisos (Bits#(Vector#(n,a),vasz));
   method b first();
      function a getfirst(PipeOut#(a) pipein); return pipein.first(); endfunction
      Vector#(n,a) vec = map(getfirst, apipes);
      return f(vec);
   endmethod
   method Action deq();
      function a getfirst(PipeOut#(a) pipein); return pipein.first(); endfunction
      for (Integer i = 0; i < valueOf(n); i = i + 1)
	 apipes[i].deq();
   endmethod
   method Bool notEmpty();
      function Bool getNotEmpty(PipeOut#(a) pipein); return pipein.notEmpty(); endfunction
      function Bool myand(Bool a, Bool b); return a && b; endfunction
      return foldl(myand, True, map(getNotEmpty, apipes));
   endmethod
endmodule

function PipeOut#(b) mapPipe(function b f(a av), PipeOut#(a) apipe);
   return (interface PipeOut#(b);
      method b first();
	 let av = apipe.first();
	 return f(av);
      endmethod
      method Action deq();
	 apipe.deq();
      endmethod
      method Bool notEmpty();
	 return apipe.notEmpty();
      endmethod
      endinterface);
endfunction

function PipeIn#(a) mapPipeIn(function b f(a av), PipeIn#(b) apipe);
   return (interface PipeIn#(b);
	      method Action enq(a v);
		 apipe.enq(f(v));
	      endmethod
	      method Bool notFull();
		 return apipe.notFull();
	      endmethod
	   endinterface);
endfunction

// buffered version of mapPipe
module mkMapPipe#(function b f(a av), PipeOut#(a) apipe)(PipeOut#(b))
   provisos (Bits#(b,bsz));
   FIFOF#(b) fifo <- mkFIFOF();
   rule compute;
      let v <- toGet(apipe).get();
      fifo.enq(f(v));
   endrule
   return toPipeOut(fifo);
endmodule

typedef (function tb f(ta x)) CombinePipe#(type ta, type tb);

typeclass ReducePipe#( numeric type n, type a);
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			 PipeOut#(Vector#(n,a)) inpipe,
			 PipeOut#(a) ifc);
   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			  Vector#(n,PipeOut#(a)) inpipe,
			  PipeOut#(a) ifc);
endtypeclass
instance ReducePipe#(1, a);
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
				 PipeOut#(Vector#(1,a)) inpipe,
				 PipeOut#(a) ifc);
      let pipe = mapPipe(head, inpipe);
      return pipe;
   endmodule
   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
				  Vector#(1,PipeOut#(a)) inpipes,
				  PipeOut#(a) ifc);
      return inpipes[0];
   endmodule
endinstance
instance ReducePipe#(2, a)
   provisos(Bits#(a,a__));
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			 PipeOut#(Vector#(2,a)) inpipe,
			 PipeOut#(a) ifc);
      function a foo(Vector#(2,a) invec);
	 return combinepipe(tuple2(invec[0], invec[1]));
      endfunction
      let pipe <- mkMapPipe(foo, inpipe);
      return pipe;
   endmodule
   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			  Vector#(2,PipeOut#(a)) inpipes,
			  PipeOut#(a) ifc);
      function a foo(Tuple2#(a,a) invec);
	 return combinepipe(invec);
      endfunction
      let pipe <- mkMapPipe(foo, zipPipeOut(inpipes[0], inpipes[1]));
      return pipe;
   endmodule
endinstance

instance ReducePipe#(n, a)
   provisos (Add#(TDiv#(n,2), a__, n),
	     Bits#(Vector#(TDiv#(n,2), a), b__),
	     ReducePipe#(TDiv#(n,2),a));
   module  mkReducePipe (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			 PipeOut#(Vector#(n,a)) inpipe,
			 PipeOut#(a) ifc);
      FIFOF#(Vector#(TDiv#(n,2),a)) infifo0 <- mkFIFOF;
      FIFOF#(Vector#(TDiv#(n,2),a)) infifo1 <- mkFIFOF;
      rule splitinput;
	 let v = inpipe.first();
	 inpipe.deq();
	 infifo0.enq(takeAt(0, v));
	 infifo1.enq(takeAt(valueOf(TDiv#(n,2)), v));
      endrule
      PipeOut#(Vector#(TDiv#(n,2),a)) inpipe0 = toPipeOut(infifo0);
      PipeOut#(Vector#(TDiv#(n,2),a)) inpipe1 = toPipeOut(infifo1);

      PipeOut#(a) p0 <- mkReducePipe(combinepipe, inpipe0);
      PipeOut#(a) p1 <- mkReducePipe(combinepipe, inpipe1);

      function a foo(Tuple2#(a,a) invec);
	 return combinepipe(invec);
      endfunction
      let pipe <- mkMapPipe(foo,zipPipeOut(p0, p1));
      return pipe;
   endmodule

   module  mkReducePipes (CombinePipe#(Tuple2#(a,a), a) combinepipe,
			  Vector#(n, PipeOut#(a)) inpipes,
			  PipeOut#(a) ifc);
      Vector#(TDiv#(n,2),PipeOut#(a)) pipes0 = takeAt(0, inpipes);
      Vector#(TDiv#(n,2),PipeOut#(a)) pipes1 = takeAt(valueOf(TDiv#(n,2)), inpipes);

      PipeOut#(a) p0 <- mkReducePipes(combinepipe, pipes0);
      PipeOut#(a) p1 <- mkReducePipes(combinepipe, pipes1);

      function a foo(Tuple2#(a,a) invec);
	 return combinepipe(invec);
      endfunction
      let pipe <- mkMapPipe(foo,zipPipeOut(p0, p1));
      return pipe;
   endmodule
endinstance

interface FirstLastPipe#(type a);
   interface PipeOut#(Tuple2#(Bool,Bool)) pipe;
   method Action start(a count);
endinterface

module mkFirstLastPipe(FirstLastPipe#(a))
   provisos (Bits#(a,asz), Ord#(a), Arith#(a), Eq#(a));
   Reg#(a) countReg <- mkReg(0);
   Reg#(Bool) firstReg <- mkReg(False);
   Reg#(Bool) lastReg <- mkReg(False);
   interface PipeOut pipe;
      method Tuple2#(Bool, Bool) first();
	 return tuple2(firstReg, lastReg);
      endmethod
      method Action deq() if (countReg > 0);
	 firstReg <= False;
	 let c = countReg - 1;
	 if (c == 1)
	    lastReg <= True;
	 countReg <= c;
      endmethod
      method Bool notEmpty();
	 return countReg > 0;
      endmethod
   endinterface
   method Action start(a count) if (countReg == 0);
      firstReg <= True;
      lastReg <= False;
      countReg <= count;
   endmethod
endmodule

typedef struct {
   a xbase;
   a xlimit;
   a xstep;
} RangeConfig#(type a) deriving (Bits, FShow);

interface RangePipeIfc#(type a);
   interface PipeOut#(a) pipe;
   method Bool isFirst();
   method Bool isLast();
   method Action start(RangeConfig#(a) cfg);
endinterface

module mkRangePipeOut(RangePipeIfc#(a)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a));
   Reg#(a) x <- mkReg(1);
   Reg#(a) xbase <- mkReg(0);
   Reg#(a) xstep <- mkReg(1);
   // inclusive limit
   Reg#(a) xlimit <- mkReg(0);
   Reg#(Bool) first <- mkReg(False);
   Reg#(Bool) last <- mkReg(False);
   Bool verbose = False;
   interface PipeOut pipe;
      method a first();
	 return x;
      endmethod
      method Action deq if (x <= xlimit);
	 x <= x + xstep;
	 first <= False;
	 last <= (x+xstep >= xlimit);
      endmethod
      method Bool notEmpty();
	 return (x <= xlimit);
      endmethod
   endinterface
   method Action start(RangeConfig#(a) cfg) if (x > xlimit);
      x <= cfg.xbase;
      xbase <= cfg.xbase;
      xstep <= cfg.xstep;

      // inclusive limit, should update interface accordingly
      xlimit <= cfg.xlimit-cfg.xstep;

      first <= True;
      last <= (cfg.xbase+cfg.xstep >= cfg.xlimit);
      if (verbose) $display("mkRangePipeOut xbase=%d xstep=%d cfg.xlimit=%d xlimit=%d last=%d", cfg.xbase, cfg.xstep, cfg.xlimit, (cfg.xlimit-cfg.xstep), (cfg.xbase+cfg.xstep >= cfg.xlimit));
   endmethod
   method Bool isFirst() = first;
   method Bool isLast() = last;
endmodule: mkRangePipeOut


commented out big-banging I2C

commented out big-banging I2C

// Copyright (c) 2015 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import BuildVector::*;
import Clocks::*;
import GetPut::*;
import ClientServer::*;
import SCCB::*;
import FIFOF::*;
import BRAMFIFO::*;
import Gearbox::*;

import MemTypes::*;
import HostInterface::*;
import MemwriteEngine::*;
import Pipe::*;
import ConnectalClocks::*;
import ConnectalXilinxCells::*;
import Ov7670Interface::*;

interface Ov7670Controller;
   interface Ov7670ControllerRequest request;
   interface Ov7670Pins pins;
   interface Vector#(1,MemWriteClient#(DataBusWidth)) dmaClient;
endinterface

module mkOv7670Controller#(Ov7670ControllerIndication ind)(Ov7670Controller);

   Integer divisor = 12;
   Clock defaultClock <- exposeCurrentClock;
   Reset defaultReset <- exposeCurrentReset;
   ClockDividerIfc clockDivider <- mkClockDivider(divisor);
   B2C1 b2c <- mkB2C1;
   let pclk = b2c.c;
   Reset preset <- mkAsyncReset(2, defaultReset, pclk);
   SyncFIFOIfc#(Tuple2#(Bit#(32),Bit#(1))) vsyncFifo <- mkSyncFIFO(32, pclk, preset, defaultClock);
   SyncFIFOIfc#(Tuple3#(Bool, Bool, Bit#(8))) dataFifo <- mkSyncBRAMFIFO(16384, pclk, preset, defaultClock, defaultReset);
   Gearbox#(1, 8, Bit#(8))                  dataGearbox <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   MemwriteEngine#(DataBusWidth,8,1) writeEngine <- mkMemwriteEngineBuff(2048);
   FIFOF#(Bool)        sofFifo    <- mkFIFOF();
   Reg#(Bit#(32))      pointerReg <- mkReg(0);
   Reg#(Bool)     transferDoneReg <- mkReg(False);

   Reg#(Bit#(32)) cycleReg     <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(32)) lastVsyncReg <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(32)) lastDataReg  <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(1)) vsyncReg      <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(1)) hrefReg       <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(8)) dataReg       <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bool)    firstReg      <- mkReg(False, clocked_by pclk, reset_by preset);
   Reg#(Bool)    lastReg       <- mkReg(False, clocked_by pclk, reset_by preset);
   Reg#(Bit#(16)) dataGapCycles <- mkReg(0, clocked_by pclk, reset_by preset);
   Wire#(Bool)    dataRuleFired <- mkDWire(False, clocked_by pclk, reset_by preset);

   Vector#(3, SCCB) i2c <- replicateM(mkSCCB(1000));
   Reg#(bit) resetReg <- mkReg(0);
   Reg#(bit) pwdnReg <- mkReg(0);

   Reg#(Bit#(1))                   rSCL                <- mkReg(1);
   Reg#(Bit#(1))                   rSDA                <- mkReg(1);
   Reg#(Bool)                      rOutEn              <- mkReg(True);
   //IOBUF                           ioSCL               <- mkIOBUF(0, rSCL);
   //IOBUF                           ioSDA               <- mkIOBUF(rOutEn ? 0 : 1, rSDA);

   for (Integer i = 0; i < 3; i = i + 1)
      rule i2c_response_rule;
	 let response <- i2c[i].user.response.get();
	 ind.i2cResponse(fromInteger(i), response.data);
      endrule

   rule cycleRule;
      cycleReg <= cycleReg + 1;
   endrule

   rule vsyncRule;
      if (vsyncReg == 1) begin
	 vsyncFifo.enq(tuple2(cycleReg - lastVsyncReg, hrefReg));
	 lastVsyncReg <= cycleReg;
      end
   endrule

   rule vsyncSyncRule;
      match { .cycles, .href } <- toGet(vsyncFifo).get();
      ind.vsync(cycles, href);
      if (sofFifo.notFull())
	 sofFifo.enq(True);
   endrule

   rule dataRule;
      if (hrefReg == 1) begin
	 dataRuleFired <= True;
	 let gap = dataGapCycles != 0;
	 dataFifo.enq(tuple3(firstReg, (firstReg ? False : gap), dataReg));
	 lastDataReg <= cycleReg;
	 firstReg <= False;
      end
      else begin
	 firstReg <= True;
      end
   endrule

   rule dataRuleGap if (hrefReg == 1);
      if (!dataRuleFired)
	 dataGapCycles <= dataGapCycles + 1;
      else
	 dataGapCycles <= 0;
   endrule

   rule dataSyncRule;
      match { .first, .gap, .pxl } <- toGet(dataFifo).get();

      if (sofFifo.notEmpty() && transferDoneReg) begin
	 sofFifo.deq();
	 transferDoneReg <= False;
	 writeEngine.write_servers[0].cmdServer.request.put(MemengineCmd { sglId: pointerReg, base: 0, burstLen: 8*8, len: 640*480, tag: 0});
	 ind.frameStarted(pack(first));
      end

      dataGearbox.enq(unpack(pxl));
      if (gap)
	 ind.data(pack(first), pack(gap), pxl);
   endrule

   rule dataIndRule;
      let d = dataGearbox.first();
      dataGearbox.deq();
      //ind.data8(pack(d));
      if (pointerReg != 0)
	 writeEngine.write_servers[0].dataPipe.enq(pack(d));
   endrule

   rule transferDoneRule;
      let d <- writeEngine.write_servers[0].cmdServer.response.get();
      transferDoneReg <= True;
      ind.frameTransferred();
   endrule

   interface Ov7670ControllerRequest request;
      method Action setFramePointer(Bit#(32) frameId);
	 pointerReg <= frameId;
	 transferDoneReg <= True; // prime the pump
      endmethod
      method Action i2cRequest(Bit#(8) bus, Bool write, Bit#(7) slaveaddr, Bit#(8) address, Bit#(8) data);
	 i2c[bus].user.request.put(SCCBRequest {write: write, slaveaddr: slaveaddr, address: address, data: data});
      endmethod
      method Action pinRequest(Bit#(1) outen, Bit#(1) scl, Bit#(1) sdaOut);
// 	 let sdaIn = ioSDA.o();
// 	 rSCL <= scl;
// 	 rSDA <= sdaOut;
// 	 rOutEn <= unpack(outen);
//	 ind.pinResponse(sdaIn);
	 ind.pinResponse(sdaOut);
      endmethod
      method Action setReset(Bit#(1) rval);
	 resetReg <= rval;
      endmethod
      method Action setPowerDown(Bit#(1) pwdn);
	 pwdnReg <= pwdn;
      endmethod
   endinterface
   interface Ov7670Pins pins;
      interface SCCB_Pins i2c0 = i2c[0].i2c;
//       interface SCCB_Pins i2c0; //i2c[0].i2c;
// 	 interface Inout sda = ioSDA.io;
// 	 interface Inout scl = ioSCL.io;
//       endinterface
      interface SCCB_Pins i2c1 = i2c[1].i2c;
      interface SCCB_Pins i2c2 = i2c[2].i2c;
      interface Clock xclk = clockDivider.slowClock;
      interface Clock pclk_deleteme_unused_clock = pclk;
      method bit reset() = resetReg;
      method bit pwdn() = pwdnReg;
      method Action pclk(Bit#(1) v);
	 b2c.inputclock(v);
      endmethod
      method Action pxl(Bit#(1) vsync, Bit#(1) href, Bit#(8) data);
	 vsyncReg <= vsync;
	 hrefReg  <= href;
	 dataReg <= data;
      endmethod
   endinterface
   interface Vector dmaClient = vec(writeEngine.dmaClient);
endmodule

// Copyright (c) 2015 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import BuildVector::*;
import Clocks::*;
import GetPut::*;
import ClientServer::*;
import SCCB::*;
import FIFOF::*;
import BRAMFIFO::*;
import Gearbox::*;

import MemTypes::*;
import HostInterface::*;
import MemwriteEngine::*;
import Pipe::*;
import ConnectalClocks::*;
import ConnectalXilinxCells::*;
import Ov7670Interface::*;

interface Ov7670Controller;
   interface Ov7670ControllerRequest request;
   interface Ov7670Pins pins;
   interface Vector#(1,MemWriteClient#(DataBusWidth)) dmaClient;
endinterface

module mkOv7670Controller#(Ov7670ControllerIndication ind)(Ov7670Controller);

   Integer divisor = 4;
   Clock defaultClock <- exposeCurrentClock;
   Reset defaultReset <- exposeCurrentReset;
   ClockDividerIfc clockDivider <- mkClockDivider(divisor);
   B2C1 b2c <- mkB2C1;
   let pclk = b2c.c;
   Reset preset <- mkAsyncReset(2, defaultReset, pclk);
   SyncFIFOIfc#(Tuple2#(Bit#(32),Bit#(1))) vsyncFifo <- mkSyncFIFO(32, pclk, preset, defaultClock);
   SyncFIFOIfc#(Tuple3#(Bool, Bool, Bit#(8))) dataFifo <- mkSyncBRAMFIFO(16384, pclk, preset, defaultClock, defaultReset);
   Gearbox#(1, 8, Bit#(8))                  dataGearbox <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   MemwriteEngine#(DataBusWidth,8,1) writeEngine <- mkMemwriteEngineBuff(2048);
   FIFOF#(Bool)        sofFifo    <- mkFIFOF();
   Reg#(Bit#(32))      pointerReg <- mkReg(0);
   Reg#(Bool)     transferDoneReg <- mkReg(False);

   Reg#(Bit#(32)) cycleReg     <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(32)) lastVsyncReg <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(32)) lastDataReg  <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(1)) vsyncReg      <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(1)) hrefReg       <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bit#(8)) dataReg       <- mkReg(0, clocked_by pclk, reset_by preset);
   Reg#(Bool)    firstReg      <- mkReg(False, clocked_by pclk, reset_by preset);
   Reg#(Bool)    lastReg       <- mkReg(False, clocked_by pclk, reset_by preset);
   Reg#(Bit#(16)) dataGapCycles <- mkReg(0, clocked_by pclk, reset_by preset);
   Wire#(Bool)    dataRuleFired <- mkDWire(False, clocked_by pclk, reset_by preset);

   Vector#(3, SCCB) i2c <- replicateM(mkSCCB(1000));
   Reg#(bit) resetReg <- mkReg(0);
   Reg#(bit) pwdnReg <- mkReg(0);

   Reg#(Bit#(1))                   rSCL                <- mkReg(1);
   Reg#(Bit#(1))                   rSDA                <- mkReg(1);
   Reg#(Bool)                      rOutEn              <- mkReg(True);
   IOBUF                           ioSCL               <- mkIOBUF(0, rSCL);
   IOBUF                           ioSDA               <- mkIOBUF(rOutEn ? 0 : 1, rSDA);

   for (Integer i = 0; i < 3; i = i + 1)
      rule i2c_response_rule;
	 let response <- i2c[i].user.response.get();
	 ind.i2cResponse(fromInteger(i), response.data);
      endrule

   rule cycleRule;
      cycleReg <= cycleReg + 1;
   endrule

   rule vsyncRule;
      if (vsyncReg == 1) begin
	 vsyncFifo.enq(tuple2(cycleReg - lastVsyncReg, hrefReg));
	 lastVsyncReg <= cycleReg;
      end
   endrule

   rule vsyncSyncRule;
      match { .cycles, .href } <- toGet(vsyncFifo).get();
      ind.vsync(cycles, href);
      if (sofFifo.notFull())
	 sofFifo.enq(True);
   endrule

   rule dataRule;
      if (hrefReg == 1) begin
	 dataRuleFired <= True;
	 let gap = dataGapCycles != 0;
	 dataFifo.enq(tuple3(firstReg, (firstReg ? False : gap), dataReg));
	 lastDataReg <= cycleReg;
	 firstReg <= False;
      end
      else begin
	 firstReg <= True;
      end
   endrule

   rule dataRuleGap if (hrefReg == 1);
      if (!dataRuleFired)
	 dataGapCycles <= dataGapCycles + 1;
      else
	 dataGapCycles <= 0;
   endrule

   rule dataSyncRule;
      match { .first, .gap, .pxl } <- toGet(dataFifo).get();

      if (sofFifo.notEmpty() && transferDoneReg) begin
	 sofFifo.deq();
	 transferDoneReg <= False;
	 writeEngine.write_servers[0].cmdServer.request.put(MemengineCmd { sglId: pointerReg, base: 0, burstLen: 8*8, len: 640*480, tag: 0});
	 ind.frameStarted(pack(first));
      end

      dataGearbox.enq(unpack(pxl));
      if (gap)
	 ind.data(pack(first), pack(gap), pxl);
   endrule

   rule dataIndRule;
      let d = dataGearbox.first();
      dataGearbox.deq();
      //ind.data8(pack(d));
      if (pointerReg != 0)
	 writeEngine.write_servers[0].dataPipe.enq(pack(d));
   endrule

   rule transferDoneRule;
      let d <- writeEngine.write_servers[0].cmdServer.response.get();
      transferDoneReg <= True;
      ind.frameTransferred();
   endrule

   interface Ov7670ControllerRequest request;
      method Action setFramePointer(Bit#(32) frameId);
	 pointerReg <= frameId;
	 transferDoneReg <= True; // prime the pump
      endmethod
      method Action i2cRequest(Bit#(8) bus, Bool write, Bit#(7) slaveaddr, Bit#(8) address, Bit#(8) data);
	 i2c[bus].user.request.put(SCCBRequest {write: write, slaveaddr: slaveaddr, address: address, data: data});
      endmethod
      method Action pinRequest(Bit#(1) outen, Bit#(1) scl, Bit#(1) sdaOut);
	 let sdaIn = ioSDA.o();
	 rSCL <= scl;
	 rSDA <= sdaOut;
	 rOutEn <= unpack(outen);
	 ind.pinResponse(sdaIn);
      endmethod
      method Action setReset(Bit#(1) rval);
	 resetReg <= rval;
      endmethod
      method Action setPowerDown(Bit#(1) pwdn);
	 pwdnReg <= pwdn;
      endmethod
   endinterface
   interface Ov7670Pins pins;
      interface SCCB_Pins i2c0; //i2c[0].i2c;
	 interface Inout sda = ioSDA.io;
	 interface Inout scl = ioSCL.io;
      endinterface
      interface SCCB_Pins i2c1 = i2c[1].i2c;
      interface SCCB_Pins i2c2 = i2c[2].i2c;
      interface Clock xclk = clockDivider.slowClock;
      interface Clock pclk_deleteme_unused_clock = pclk;
      method bit reset() = resetReg;
      method bit pwdn() = pwdnReg;
      method Action pclk(Bit#(1) v);
	 b2c.inputclock(v);
      endmethod
      method Action pxl(Bit#(1) vsync, Bit#(1) href, Bit#(8) data);
	 vsyncReg <= vsync;
	 hrefReg  <= href;
	 dataReg <= data;
      endmethod
   endinterface
   interface Vector dmaClient = vec(writeEngine.dmaClient);
endmodule


match table sim model uses compile time IO to initialize table

// Copyright (c) 2016 Cornell University.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFO::*;
import FIFOF::*;
import GetPut::*;
import ClientServer::*;
import StmtFSM::*;
import Vector::*;
import DefaultValue::*;
import BRAM::*;
import FShow::*;
import Pipe::*;
import StringUtils::*;
import List::*;
import Bcam::*;
import BcamTypes::*;

interface MatchTable#(numeric type id, numeric type depth, numeric type keySz, numeric type actionSz);
   interface Server#(Bit#(keySz), Maybe#(Bit#(actionSz))) lookupPort;
   interface Put#(Tuple2#(Bit#(keySz), Bit#(actionSz))) add_entry;
   interface Put#(Bit#(TLog#(depth))) delete_entry;
   interface Put#(Tuple2#(Bit#(TLog#(depth)), Bit#(actionSz))) modify_entry;
endinterface

typeclass MatchTableSim#(numeric type id, numeric type ksz, numeric type vsz);
   function ActionValue#(Bit#(vsz)) matchtable_read(Bit#(id) v, Bit#(ksz) key);
   function Action matchtable_write(Bit#(id) v, Bit#(ksz) key, Bit#(vsz) data);
endtypeclass

module mkMatchTable#(String name)(MatchTable#(id, depth, keySz, actionSz))
   provisos(Mul#(c__, 256, d__),
            Add#(c__, 7, TLog#(depth)),
            Log#(d__, TLog#(depth)),
            Mul#(e__, 9, keySz),
            Add#(TAdd#(TLog#(c__), 4), 2, TLog#(TDiv#(depth, 4))),
            Log#(TDiv#(depth, 16), TAdd#(TLog#(c__), 4)),
            Add#(9, f__, keySz),
            PriorityEncoder::PEncoder#(d__),
            Add#(2, g__, TLog#(depth)),
            Add#(4, h__, TLog#(depth)),
`ifdef SIMULATION
            MatchTableSim#(id, keySz, actionSz),
`endif
            Add#(TAdd#(TLog#(c__), 4), i__, TLog#(depth)));

   MatchTable#(id, depth, keySz, actionSz) ret_ifc;
`ifdef SIMULATION
   ret_ifc <- mkMatchTableBluesim(name);
`else
   ret_ifc <- mkMatchTableSynth();
`endif
   return ret_ifc;
endmodule

module mkMatchTableSynth(MatchTable#(id, depth, keySz, actionSz))
   provisos (NumAlias#(depthSz, TLog#(depth)),
             Mul#(a__, 256, b__),
             Add#(a__, 7, depthSz),
             Log#(b__, depthSz),
             Mul#(c__, 9, keySz),
             Add#(TAdd#(TLog#(a__), 4), 2, TLog#(TDiv#(depth, 4))),
             Log#(TDiv#(depth, 16), TAdd#(TLog#(a__), 4)),
             Add#(9, d__, keySz),
             PriorityEncoder::PEncoder#(b__),
             Add#(2, e__, TLog#(depth)),
             Add#(4, f__, TLog#(depth)),
             Add#(TAdd#(TLog#(a__), 4), g__, TLog#(depth))
            );
   let verbose = True;
   Reg#(Bit#(32)) cycle <- mkReg(0);

   rule every1 (verbose);
      cycle <= cycle + 1;
   endrule

   BinaryCam#(depth, keySz) bcam <- mkBinaryCam();
   FIFO#(Bool) bcamMatchFifo <- mkFIFO();

   BRAM_Configure cfg = defaultValue;
   cfg.latency = 2;
   BRAM2Port#(Bit#(depthSz), Bit#(actionSz)) ram <- mkBRAM2Server(cfg);

   Reg#(Bit#(depthSz)) addrIdx <- mkReg(0);

   rule handle_bcam_response;
      let v <- bcam.readServer.response.get;
      if (verbose) $display("(%0d) matchTable recv bcam response ", $time, fshow(v));
      if (isValid(v)) begin // if match
         let address = fromMaybe(?, v);
         ram.portA.request.put(BRAMRequest{write:False, responseOnWrite: False, address: address, datain:?});
         bcamMatchFifo.enq(True);
      end
      else begin // if miss
         ram.portA.request.put(BRAMRequest{write:False, responseOnWrite: False, address: 0, datain: ?});
         bcamMatchFifo.enq(False);
      end
   endrule

   // Interface for lookup from data-plane modules
   interface Server lookupPort;
      interface Put request;
         method Action put (Bit#(keySz) v);
            BcamReadReq#(Bit#(keySz)) req_bcam = BcamReadReq{data: v};
            bcam.readServer.request.put(pack(req_bcam));
            //if (verbose) $display("matchTable %d: lookup ", cycle, fshow(req_bcam));
         endmethod
      endinterface
      interface Get response;
         method ActionValue#(Maybe#(Bit#(actionSz))) get();
            let m <- toGet(bcamMatchFifo).get;
            let act <- ram.portA.response.get;
            if (verbose) $display("(%0d) matchTable: recv ram response ", $time, fshow(act));
            case (m) matches
               True: return tagged Valid act;
               False: return tagged Invalid;
            endcase
         endmethod
      endinterface
   endinterface

   // Interface for write from control-plane
   interface Put add_entry;
      method Action put (Tuple2#(Bit#(keySz), Bit#(actionSz)) v);
         BcamWriteReq#(Bit#(depthSz), Bit#(keySz)) req_bcam = BcamWriteReq{addr: addrIdx, data: pack(tpl_1(v))};
         BRAMRequest#(Bit#(depthSz), Bit#(actionSz)) req_ram = BRAMRequest{write: True, responseOnWrite: False, address: addrIdx, datain: pack(tpl_2(v))};
         bcam.writeServer.put(req_bcam);
         ram.portA.request.put(req_ram);
         $display("(%0d) match_table: add flow %x %x %x", $time, addrIdx, tpl_1(v), tpl_2(v));
         addrIdx <= addrIdx + 1; //FIXME: currently no reuse of address.
      endmethod
   endinterface
   interface Put delete_entry;
      method Action put (Bit#(depthSz) id);
         BcamWriteReq#(Bit#(depthSz), Bit#(keySz)) req_bcam = BcamWriteReq{addr: id, data: 0};
         BRAMRequest#(Bit#(depthSz), Bit#(actionSz)) req_ram = BRAMRequest{write: True, responseOnWrite: False, address: id, datain: 0};
         bcam.writeServer.put(req_bcam);
         ram.portA.request.put(req_ram);
         $display("(%0d) match_table: delete flow %x", $time, id);
      endmethod
   endinterface
   interface Put modify_entry;
      method Action put (Tuple2#(Bit#(depthSz), Bit#(actionSz)) v);
         match { .flowid, .act} = v;
         BRAMRequest#(Bit#(depthSz), Bit#(actionSz)) req_ram = BRAMRequest{write: True, responseOnWrite: False, address: flowid, datain: pack(act)};
         ram.portA.request.put(req_ram);
      endmethod
   endinterface
endmodule

module mkMatchTableBluesim#(String name)(MatchTable#(id, depth, keySz, actionSz))
   provisos (MatchTable::MatchTableSim#(id, keySz, actionSz),
             Log#(depth, depthSz));
   let verbose = True;

   Integer idv = valueOf(id);
   Integer dpv = valueOf(depth);
   FIFO#(Tuple2#(Bit#(keySz), Bit#(actionSz))) writeReqFifo <- mkFIFO;
   FIFO#(Bit#(keySz)) readReqFifo <- mkFIFO;
   FIFO#(Maybe#(Bit#(actionSz))) readDataFifo <- mkFIFO;

   Reg#(Bool)      isInitialized   <- mkReg(False);

   Handle fp <- openFile(name, ReadMode);
   List#(Tuple2#(Bit#(keySz), Bit#(actionSz))) entryList = tagged Nil;
   let readable <- hIsReadable(fp);
   if (readable) begin
      Bool isEOF <- hIsEOF(fp);
      while (!isEOF) begin
         let line <- hGetLine(fp);
         List#(String) parsedEntries = parseCSV(line);
         while (parsedEntries != tagged Nil) begin
            Bit#(keySz) key = fromInteger(hexStringToInteger(parsedEntries[0]));
            Bit#(actionSz) v = fromInteger(hexStringToInteger(parsedEntries[1]));
            Bit#(id) tid = 0;
            parsedEntries = List::drop(2, parsedEntries);
            entryList = List::cons(tuple2(key, v), entryList);
         end
         isEOF <- hIsEOF(fp);
      end
   end
   hClose(fp);

   rule do_read (isInitialized);
      let v <- toGet(readReqFifo).get;
      $display("(%0d) MatchTable: do_read %h", $time, v);
      Bit#(id) tid = 0;
      let ret <- matchtable_read(tid, pack(v));
      $display("(%0d) MatchTable: read %h", $time, ret);
      if (ret != 0)
         readDataFifo.enq(tagged Valid ret);
      else
         readDataFifo.enq(tagged Invalid);
   endrule

   Reg#(Bit#(8)) fsmIndex <- mkReg(0);

   rule do_init (fsmIndex < fromInteger(List::length(entryList)));
      $display("insert entry ", fromInteger(List::length(entryList)));
      Bit#(keySz) key = tpl_1(entryList[fsmIndex]);
      Bit#(actionSz) v = tpl_2(entryList[fsmIndex]);
      Bit#(id) tid = 0;
      matchtable_write(tid, key, v);
      fsmIndex <= fsmIndex + 1;
      $display("finished insert entry ", fromInteger(List::length(entryList)));
   endrule

   rule finish_init(fsmIndex == fromInteger(List::length(entryList)));
      isInitialized <= True;
   endrule

   interface Server lookupPort;
      interface Put request = toPut(readReqFifo);
      interface Get response = toGet(readDataFifo);
   endinterface
   interface Put add_entry;
      method Action put (Tuple2#(Bit#(keySz), Bit#(actionSz)) v);
         $display("(%0d) MatchTable: insert %h %h", $time, tpl_1(v), tpl_2(v));
         Bit#(id) tid = 0;
         matchtable_write(tid, tpl_1(v), tpl_2(v));
      endmethod
   endinterface
   interface Put delete_entry;
      method Action put (Bit#(depthSz) id);

      endmethod
   endinterface
   interface Put modify_entry;
      method Action put (Tuple2#(Bit#(depthSz), Bit#(actionSz)) v);

      endmethod
   endinterface
endmodule

// Copyright (c) 2016 Cornell University.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFO::*;
import FIFOF::*;
import GetPut::*;
import ClientServer::*;
import StmtFSM::*;
import Vector::*;
import DefaultValue::*;
import BRAM::*;
import FShow::*;
import Pipe::*;
import Bcam::*;
import BcamTypes::*;

interface MatchTable#(numeric type id, numeric type depth, numeric type keySz, numeric type actionSz);
   interface Server#(Bit#(keySz), Maybe#(Bit#(actionSz))) lookupPort;
   interface Put#(Tuple2#(Bit#(keySz), Bit#(actionSz))) add_entry;
   interface Put#(Bit#(TLog#(depth))) delete_entry;
   interface Put#(Tuple2#(Bit#(TLog#(depth)), Bit#(actionSz))) modify_entry;
endinterface

typeclass MatchTableSim#(numeric type id, numeric type ksz, numeric type vsz);
   function ActionValue#(Bit#(vsz)) matchtable_read(Bit#(id) v, Bit#(ksz) key);
   function Action matchtable_write(Bit#(id) v, Bit#(ksz) key, Bit#(vsz) data);
endtypeclass

module mkMatchTable(MatchTable#(id, depth, keySz, actionSz))
   provisos(Mul#(c__, 256, d__),
            Add#(c__, 7, TLog#(depth)),
            Log#(d__, TLog#(depth)),
            Mul#(e__, 9, keySz),
            Add#(TAdd#(TLog#(c__), 4), 2, TLog#(TDiv#(depth, 4))),
            Log#(TDiv#(depth, 16), TAdd#(TLog#(c__), 4)),
            Add#(9, f__, keySz),
            PriorityEncoder::PEncoder#(d__),
            Add#(2, g__, TLog#(depth)),
            Add#(4, h__, TLog#(depth)),
`ifdef SIMULATION
            MatchTableSim#(id, keySz, actionSz),
`endif
            Add#(TAdd#(TLog#(c__), 4), i__, TLog#(depth)));

   MatchTable#(id, depth, keySz, actionSz) ret_ifc;
`ifdef SIMULATION
   ret_ifc <- mkMatchTableBluesim();
`else
   ret_ifc <- mkMatchTableSynth();
`endif
   return ret_ifc;
endmodule

module mkMatchTableSynth(MatchTable#(id, depth, keySz, actionSz))
   provisos (NumAlias#(depthSz, TLog#(depth)),
             Mul#(a__, 256, b__),
             Add#(a__, 7, depthSz),
             Log#(b__, depthSz),
             Mul#(c__, 9, keySz),
             Add#(TAdd#(TLog#(a__), 4), 2, TLog#(TDiv#(depth, 4))),
             Log#(TDiv#(depth, 16), TAdd#(TLog#(a__), 4)),
             Add#(9, d__, keySz),
             PriorityEncoder::PEncoder#(b__),
             Add#(2, e__, TLog#(depth)),
             Add#(4, f__, TLog#(depth)),
             Add#(TAdd#(TLog#(a__), 4), g__, TLog#(depth))
            );
   let verbose = True;
   Reg#(Bit#(32)) cycle <- mkReg(0);

   rule every1 (verbose);
      cycle <= cycle + 1;
   endrule

   BinaryCam#(depth, keySz) bcam <- mkBinaryCam();
   FIFO#(Bool) bcamMatchFifo <- mkFIFO();

   BRAM_Configure cfg = defaultValue;
   cfg.latency = 2;
   BRAM2Port#(Bit#(depthSz), Bit#(actionSz)) ram <- mkBRAM2Server(cfg);

   Reg#(Bit#(depthSz)) addrIdx <- mkReg(0);

   rule handle_bcam_response;
      let v <- bcam.readServer.response.get;
      if (verbose) $display("(%0d) matchTable recv bcam response ", $time, fshow(v));
      if (isValid(v)) begin // if match
         let address = fromMaybe(?, v);
         ram.portA.request.put(BRAMRequest{write:False, responseOnWrite: False, address: address, datain:?});
         bcamMatchFifo.enq(True);
      end
      else begin // if miss
         ram.portA.request.put(BRAMRequest{write:False, responseOnWrite: False, address: 0, datain: ?});
         bcamMatchFifo.enq(False);
      end
   endrule

   // Interface for lookup from data-plane modules
   interface Server lookupPort;
      interface Put request;
         method Action put (Bit#(keySz) v);
            BcamReadReq#(Bit#(keySz)) req_bcam = BcamReadReq{data: v};
            bcam.readServer.request.put(pack(req_bcam));
            //if (verbose) $display("matchTable %d: lookup ", cycle, fshow(req_bcam));
         endmethod
      endinterface
      interface Get response;
         method ActionValue#(Maybe#(Bit#(actionSz))) get();
            let m <- toGet(bcamMatchFifo).get;
            let act <- ram.portA.response.get;
            if (verbose) $display("(%0d) matchTable: recv ram response ", $time, fshow(act));
            case (m) matches
               True: return tagged Valid act;
               False: return tagged Invalid;
            endcase
         endmethod
      endinterface
   endinterface

   // Interface for write from control-plane
   interface Put add_entry;
      method Action put (Tuple2#(Bit#(keySz), Bit#(actionSz)) v);
         BcamWriteReq#(Bit#(depthSz), Bit#(keySz)) req_bcam = BcamWriteReq{addr: addrIdx, data: pack(tpl_1(v))};
         BRAMRequest#(Bit#(depthSz), Bit#(actionSz)) req_ram = BRAMRequest{write: True, responseOnWrite: False, address: addrIdx, datain: pack(tpl_2(v))};
         bcam.writeServer.put(req_bcam);
         ram.portA.request.put(req_ram);
         $display("(%0d) match_table: add flow %x %x %x", $time, addrIdx, tpl_1(v), tpl_2(v));
         addrIdx <= addrIdx + 1; //FIXME: currently no reuse of address.
      endmethod
   endinterface
   interface Put delete_entry;
      method Action put (Bit#(depthSz) id);
         BcamWriteReq#(Bit#(depthSz), Bit#(keySz)) req_bcam = BcamWriteReq{addr: id, data: 0};
         BRAMRequest#(Bit#(depthSz), Bit#(actionSz)) req_ram = BRAMRequest{write: True, responseOnWrite: False, address: id, datain: 0};
         bcam.writeServer.put(req_bcam);
         ram.portA.request.put(req_ram);
         $display("(%0d) match_table: delete flow %x", $time, id);
      endmethod
   endinterface
   interface Put modify_entry;
      method Action put (Tuple2#(Bit#(depthSz), Bit#(actionSz)) v);
         match { .flowid, .act} = v;
         BRAMRequest#(Bit#(depthSz), Bit#(actionSz)) req_ram = BRAMRequest{write: True, responseOnWrite: False, address: flowid, datain: pack(act)};
         ram.portA.request.put(req_ram);
      endmethod
   endinterface
endmodule

module mkMatchTableBluesim(MatchTable#(id, depth, keySz, actionSz))
   provisos (MatchTable::MatchTableSim#(id, keySz, actionSz),
             Log#(depth, depthSz));
   let verbose = True;

   FIFO#(Tuple2#(Bit#(keySz), Bit#(actionSz))) writeReqFifo <- mkFIFO;
   FIFO#(Bit#(keySz)) readReqFifo <- mkFIFO;
   FIFO#(Maybe#(Bit#(actionSz))) readDataFifo <- mkFIFO;

   Reg#(Bool)      isInitialized   <- mkReg(False);

   rule do_read (isInitialized);
      let v <- toGet(readReqFifo).get;
      $display("(%0d) MatchTable: do_read %h", $time, v);
      Bit#(id) tid = 0;
      let ret <- matchtable_read(tid, pack(v));
      $display("(%0d) MatchTable: read %h", $time, ret);
      if (ret != 0)
         readDataFifo.enq(tagged Valid ret);
      else
         readDataFifo.enq(tagged Invalid);
   endrule

   rule do_init (!isInitialized);
      isInitialized <= True;
      //FIXME: generate initial value for each table
      Bit#(keySz) key = 'h0;
      Bit#(actionSz) v = 'h0;
      Bit#(id) tid = 0;
      matchtable_write(tid, key, v);
      matchtable_write(tid, key, v);
   endrule

   interface Server lookupPort;
      interface Put request = toPut(readReqFifo);
      interface Get response = toGet(readDataFifo);
   endinterface
   interface Put add_entry;
      method Action put (Tuple2#(Bit#(keySz), Bit#(actionSz)) v);
         $display("(%0d) MatchTable: insert %h %h", $time, tpl_1(v), tpl_2(v));
         Bit#(id) tid = 0;
         matchtable_write(tid, tpl_1(v), tpl_2(v));
      endmethod
   endinterface
   interface Put delete_entry;
      method Action put (Bit#(depthSz) id);

      endmethod
   endinterface
   interface Put modify_entry;
      method Action put (Tuple2#(Bit#(depthSz), Bit#(actionSz)) v);

      endmethod
   endinterface
endmodule