dummy of null_phys_mem_read_server and null_phys_mem_write_server

// Copyright (c) 2013 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 Adapter::*;
import Vector::*;
import Connectable::*;
import BRAMFIFO::*;
import GetPut::*;
import ClientServer::*;

import ToolConfig::*;
import Pipe::*;
import ToolMemory::*;
import DefaultValue::*;

`include "ProjectConfig.bsv"

typedef 44 MemOffsetSize;
`ifdef MemTagSize
typedef `MemTagSize MemTagSize;
`else
typedef 6 MemTagSize;
`endif
typedef `BurstLenSize BurstLenSize;

`ifndef USE_ACP
`ifdef PCIE3
// as configured, the Xilinx gen3 PCIe core supports 5 bit tags, and
// we need to use unique tags for all transactions in flight. Since
// MemServer uses the same tag numbers for reads and writes, we use
// tag[4] to distinguish the two, leaving 4 bits for unique tags.
// TODO: There is an option for longer tags in the gen3 core.
typedef 16 MemServerTags;
`else
typedef 32 MemServerTags;
`endif
`else
typedef 8 MemServerTags;
`endif
`ifdef DataBusWidth
typedef TDiv#(`DataBusWidth,8) ByteEnableSize;
`else
typedef TDiv#(64,8) ByteEnableSize;
`endif

// memory request with physical addresses.
// these can be transmitted directly to the bus master
typedef struct {
   Bit#(addrWidth) addr;
   Bit#(BurstLenSize) burstLen;
   Bit#(MemTagSize) tag;
`ifdef BYTE_ENABLES
   Bit#(TDiv#(dataBusWidth,8)) firstbe; // maybe we only need lastbe
   Bit#(TDiv#(dataBusWidth,8)) lastbe;
`endif
   } PhysMemRequest#(numeric type addrWidth, numeric type dataBusWidth) deriving (Bits);

instance DefaultValue#(PhysMemRequest#(addrWidth,dataBusWidth));
   defaultValue = PhysMemRequest { addr: 0, burstLen: 0, tag: 0
`ifdef BYTE_ENABLES
                                  , firstbe: maxBound, lastbe: maxBound
`endif
      };
endinstance

// memory payload
typedef struct {
   Bit#(dsz) data;
   Bit#(MemTagSize) tag;
   Bool last;
   } MemData#(numeric type dsz) deriving (Bits);

typeclass ReqByteEnables#(type t, numeric type besz);
   function Bit#(besz) reqFirstByteEnable(t req);
   function Bit#(besz) reqLastByteEnable(t req);
endtypeclass
instance ReqByteEnables#(PhysMemRequest#(addrWidth,dataBusWidth),TDiv#(dataBusWidth,8));
`ifdef BYTE_ENABLES
   function Bit#(TDiv#(dataBusWidth,8)) reqFirstByteEnable(PhysMemRequest#(addrWidth,dataBusWidth) req); return req.firstbe; endfunction
   function Bit#(TDiv#(dataBusWidth,8)) reqLastByteEnable(PhysMemRequest#(addrWidth,dataBusWidth) req); return req.lastbe; endfunction
`else
   function Bit#(TDiv#(dataBusWidth,8)) reqFirstByteEnable(PhysMemRequest#(addrWidth,dataBusWidth) req); return maxBound; endfunction
   function Bit#(TDiv#(dataBusWidth,8)) reqLastByteEnable(PhysMemRequest#(addrWidth,dataBusWidth) req); return maxBound; endfunction
`endif
endinstance

///////////////////////////////////////////////////////////////////////////////////
//

typedef struct {
   Bit#(dsz) data;
   Bit#(MemTagSize) tag;
   Bool first;
   Bool last;
   } MemDataF#(numeric type dsz) deriving (Bits);

//
///////////////////////////////////////////////////////////////////////////////////

//
///////////////////////////////////////////////////////////////////////////////////
//

interface PhysMemSlave#(numeric type addrWidth, numeric type dataWidth);
   interface PhysMemReadServer#(addrWidth, dataWidth) read_server;
   interface PhysMemWriteServer#(addrWidth, dataWidth) write_server;
endinterface

interface PhysMemMaster#(numeric type addrWidth, numeric type dataWidth);
   interface PhysMemReadClient#(addrWidth, dataWidth) read_client;
   interface PhysMemWriteClient#(addrWidth, dataWidth) write_client;
endinterface

interface PhysMemReadClient#(numeric type asz, numeric type dsz);
   interface Get#(PhysMemRequest#(asz,dsz))    readReq;
   interface Put#(MemData#(dsz)) readData;
endinterface

interface PhysMemWriteClient#(numeric type asz, numeric type dsz);
   interface Get#(PhysMemRequest#(asz,dsz))    writeReq;
   interface Get#(MemData#(dsz)) writeData;
   interface Put#(Bit#(MemTagSize))       writeDone;
endinterface

interface PhysMemReadServer#(numeric type asz, numeric type dsz);
   interface Put#(PhysMemRequest#(asz,dsz)) readReq;
   interface Get#(MemData#(dsz))     readData;
endinterface

interface PhysMemWriteServer#(numeric type asz, numeric type dsz);
   interface Put#(PhysMemRequest#(asz,dsz)) writeReq;
   interface Put#(MemData#(dsz))            writeData;
   interface Get#(Bit#(MemTagSize))         writeDone;
endinterface

//
///////////////////////////////////////////////////////////////////////////////////

instance Connectable#(PhysMemReadClient#(asz,dsz), PhysMemReadServer#(asz,dsz));
   module mkConnection#(PhysMemReadClient#(asz,dsz) source, PhysMemReadServer#(asz,dsz) sink)(Empty);
      rule mr_request;
         let req <- source.readReq.get();
         sink.readReq.put(req);
      endrule
      rule mr_response;
         let resp <- sink.readData.get();
         source.readData.put(resp);
      endrule
   endmodule
endinstance

instance Connectable#(PhysMemWriteClient#(asz,dsz), PhysMemWriteServer#(asz,dsz));
   module mkConnection#(PhysMemWriteClient#(asz,dsz) source, PhysMemWriteServer#(asz,dsz) sink)(Empty);
      rule mw_request;
         let req <- source.writeReq.get();
         sink.writeReq.put(req);
      endrule
      rule mw_response;
         let resp <- source.writeData.get();
         sink.writeData.put(resp);
      endrule
      rule mw_done;
         let resp <- sink.writeDone.get();
         source.writeDone.put(resp);
      endrule
   endmodule
endinstance

instance Connectable#(PhysMemMaster#(addrWidth, busWidth), PhysMemSlave#(addrWidth, busWidth));
   module mkConnection#(PhysMemMaster#(addrWidth, busWidth) m, PhysMemSlave#(addrWidth, busWidth) s)(Empty);
      mkConnection(m.read_client.readReq, s.read_server.readReq);
      mkConnection(s.read_server.readData, m.read_client.readData);
      mkConnection(m.write_client.writeReq, s.write_server.writeReq);
      mkConnection(m.write_client.writeData, s.write_server.writeData);
      mkConnection(s.write_server.writeDone, m.write_client.writeDone);
   endmodule
endinstance

// this is used for debugging MemToPcie/PcieToMem in BsimTop.bsv
instance Connectable#(PhysMemMaster#(32, busWidth), PhysMemSlave#(40, busWidth));
   module mkConnection#(PhysMemMaster#(32, busWidth) m, PhysMemSlave#(40, busWidth) s)(Empty);
      //mkConnection(m.read_client.readReq, s.read_server.readReq);
      rule readreq;
         let req <- m.read_client.readReq.get();
         s.read_server.readReq.put(PhysMemRequest { addr: extend(req.addr), burstLen: req.burstLen, tag: req.tag
`ifdef BYTE_ENABLES
                                                   , firstbe: req.firstbe, lastbe: req.lastbe
`endif
            });
      endrule

      mkConnection(s.read_server.readData, m.read_client.readData);
      //mkConnection(m.write_client.writeReq, s.write_server.writeReq);
      rule writereq;
         let req <- m.write_client.writeReq.get();
         s.write_server.writeReq.put(PhysMemRequest { addr: extend(req.addr), burstLen: req.burstLen, tag: req.tag
`ifdef BYTE_ENABLES
                                                     , firstbe: req.firstbe, lastbe: req.lastbe
`endif
        });
      endrule
      mkConnection(m.write_client.writeData, s.write_server.writeData);
      mkConnection(s.write_server.writeDone, m.write_client.writeDone);
   endmodule
endinstance

function Bool isQuadWordAligned(Bit#(7) lower_addr);
   return (lower_addr[2:0]==3'b0);
endfunction

function Put#(t) null_put();
   return (interface Put;
              method Action put(t x) if (False);
                 noAction;
              endmethod
           endinterface);
endfunction

function Get#(t) null_get();
   return (interface Get;
              method ActionValue#(t) get() if (False);
                 return ?;
              endmethod
           endinterface);
endfunction

function  PhysMemWriteClient#(addrWidth, busWidth) null_phys_mem_write_client();
   return (interface PhysMemWriteClient;
              interface Get writeReq = null_get;
              interface Get writeData = null_get;
              interface Put writeDone = null_put;
           endinterface);
endfunction

function  PhysMemReadClient#(addrWidth, busWidth) null_phys_mem_read_client();
   return (interface PhysMemReadClient;
              interface Get readReq = null_get;
              interface Put readData = null_put;
           endinterface);
endfunction

function  PhysMemWriteServer#(addrWidth, busWidth) null_phys_mem_write_server();
   return (interface PhysMemWriteServer;
              interface Put writeReq = null_put;
              interface Put writeData = null_put;
              interface Get writeDone = null_get;
           endinterface);
endfunction

function  PhysMemReadServer#(addrWidth, busWidth) null_phys_mem_read_server();
   return (interface PhysMemReadServer;
              interface Put readReq = null_put;
              interface Get readData = null_get;
           endinterface);
endfunction


shortent critical path in updating "last" in mkIterator

// 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 BuildVector::*;
import MIMO::*;
import DefaultValue::*;
import Gearbox::*;
import Clocks::*;

typedef Tuple3#(x,x,x) Tripple#(type x);
typedef Tuple2#(x,x)   Pair#(type x);

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

function Bool pipeInNotFull(PipeIn#(a) pipein); return pipein.notFull(); endfunction
function Bool pipeOutNotEmpty(PipeOut#(a) pipein); return pipein.notEmpty(); endfunction

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

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

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

typeclass MkPipeIn#(type a, type b) dependencies ( b determines a);
   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#(a, Gearbox#(1, n, a));
   function PipeIn#(a) toPipeIn(Gearbox#(1, n, a) in);
      return (interface PipeIn#(a);
		 method Action enq(a v); in.enq(vec(v)); endmethod
		 method notFull = in.notFull;
	      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 ToPipeOut#(a, Gearbox#(m, 1, a));
   function PipeOut#(a) toPipeOut(Gearbox#(m, 1, a) in);
      return (interface PipeOut#(a);
		 method a first(); return in.first[0]; endmethod
		 method deq = in.deq;
		 method notEmpty = in.notEmpty;
	      endinterface);
   endfunction
endinstance

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

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

instance MkPipeOut#(a, ActionValue#(a))
   provisos (Bits#(a, asz));
   module mkPipeOut#(ActionValue#(a) in)(PipeOut#(a));
      FIFOF#(a) fifo <- mkFIFOF();
      rule connect;
	 let v <- in;
	 fifo.enq(v);
      endrule
      return toPipeOut(fifo);
   endmodule
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

module mkFunnelNode#(Vector#(n, PipeOut#(a)) inpipes, Integer numPipes, Put#(a) outpipe)(Empty);
   rule funnel;
      a v = ?;
      Bool send = False;
      for (Integer i = 0; i < valueOf(n) && i < numPipes; i = i+1)
	 if (!send && inpipes[i].notEmpty) begin
	    v <- toGet(inpipes[i]).get();
	    send = True;
	 end
      if (send)
	 outpipe.put(v);
   endrule
endmodule

instance FunnelPipesPipelined#(1,k,a,bpc)
   provisos (Log#(k, logk),
	     Bits#(a,a__),
	     Add#(1,b__,k),
	     Div#(logk,bpc,stages),
	     Add#(k, c__, TExp#(logk)),
	     Add#(TExp#(bpc), d__, TExp#(logk))
	     );
   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), vec(in));
      for(Integer j = valueOf(stages); j > 0; j=j-1) begin
	 Integer width = 2**(j*valueOf(bpc));
	 Integer stride = 2**valueOf(bpc);
	 for(Integer i = 0; i < width && i < valueOf(k); i=i+stride) begin
	    Vector#(TExp#(logk),PipeOut#(a)) paddedPipes = append(infss[j], replicate(?));
	    Vector#(TExp#(bpc),PipeOut#(a)) inpipes = takeAt(i, paddedPipes);
	    Integer numPipes = stride;
	    if (i + stride > valueOf(k))
	       numPipes = valueOf(k) - i;
	    mkFunnelNode(inpipes, numPipes, toPut(buffs[j-1][i/stride]));
	 end
      end
      return vec(infss[0][0]);
   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));
	 //$display("mkUnFunnelPipesPipelinedInternal::fill %d", dest);
      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 mkForkVectorPipelined#(PipeOut#(a) inpipe)(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 myand(Bool a, Bool b); return a && b; endfunction
      return foldl(myand, True, map(pipeOutNotEmpty, 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;
} IteratorConfig#(type a) deriving (Bits, FShow);

typedef struct {
   a value;
   Bool first;
   Bool last;
   b ctxt;
} IteratorValue#(type a, type b) deriving (Bits);

interface IteratorWithContext#(type a, type c);
   interface PipeOut#(a) pipe;
   interface PipeOut#(IteratorValue#(a,c)) ivpipe;
   method a count();
   method Bool isFirst();
   method Bool isLast();
   method Action start(IteratorConfig#(a) cfg, c ctxt);
   method c ctxt();
endinterface

interface IteratorIfc#(type a);
   interface PipeOut#(a) pipe;
   interface PipeOut#(IteratorValue#(a,void)) ivpipe;
   method a count();
   method Bool isFirst();
   method Bool isLast();
   method Action start(IteratorConfig#(a) cfg);
endinterface

module mkIteratorWithContext(IteratorWithContext#(a,c)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a), Bits#(c,cwidth));
   Reg#(c) ctxtReg <- mkReg(unpack(0));
   Reg#(a) countReg <- mkReg(0);
   Reg#(a) x <- mkReg(0);
   Reg#(a) xbase <- mkReg(0);
   Reg#(a) xstep <- mkReg(0);
   // inclusive limit
   Reg#(a) xlimit <- mkReg(0);
   Reg#(a) xdown <- mkReg(0);
   Reg#(Bool) first <- mkReg(False);
   Reg#(Bool) last <- mkReg(False);
   Reg#(Bool) idle <- mkReg(True);
   Bool verbose = False;
   interface PipeOut pipe;
      method a first();
	 return x;
      endmethod
      method Action deq if (!idle);
	 let next_x = x + xstep;
	 countReg <= countReg + 1;
	 x <= x + xstep;
	 first <= False;
	 last <= (next_x+xstep >= xlimit);
	 idle <= last;
      endmethod
      method Bool notEmpty();
	 return (x < xlimit);
      endmethod
   endinterface
   interface PipeOut ivpipe;
      method IteratorValue#(a,c) first();
	 return IteratorValue { value: x, first: first, last: last, ctxt: ctxtReg };
      endmethod
      method Action deq if (!idle);
	 let next_x = x + xstep;
	 countReg <= countReg + 1;
	 x <= x + xstep;
	 xdown <= xdown - xstep;
	 first <= False;
	 last <= (xdown <= xstep);//(next_x+xstep >= xlimit);
	 idle <= last;
      endmethod
      method Bool notEmpty();
	 return (x < xlimit);
      endmethod
   endinterface
   method Action start(IteratorConfig#(a) cfg, c ctxt) if (idle);
      countReg <= 0;
      x <= cfg.xbase;
      xbase <= cfg.xbase;
      xstep <= cfg.xstep;
      xlimit <= cfg.xlimit;
      xdown <= cfg.xlimit - cfg.xbase;

      first <= True;
      last <= (cfg.xbase+cfg.xstep >= cfg.xlimit);
      idle <= False;
      ctxtReg <= ctxt;
      if (verbose) $display("mkIterator xbase=%d xstep=%d xlimit=%d last=%d notEmpty=%d", cfg.xbase, cfg.xstep, cfg.xlimit, (cfg.xbase+cfg.xstep >= cfg.xlimit),
	 (cfg.xbase < cfg.xlimit));
   endmethod
   method Bool isFirst() = first;
   method Bool isLast() = last;
   method a count() = countReg;
   method c ctxt() = ctxtReg;
endmodule: mkIteratorWithContext

module mkIterator(IteratorIfc#(a)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a));
   IteratorWithContext#(a,void) iter <- mkIteratorWithContext();
   interface PipeOut pipe = iter.pipe;
   interface PipeOut ivpipe = iter.ivpipe;
   method Action start(IteratorConfig#(a) cfg);
      iter.start(cfg, ?);
   endmethod
   method a count() = iter.count();
   method isFirst = iter.isFirst;
   method isLast = iter.isLast;
endmodule

typedef struct {
   a xbase;
   a xlimit;
   a xstep;
   a ybase;
   a ylimit;
   a ystep;
} XYIteratorConfig#(type a) deriving (Bits, FShow);

interface XYIteratorIfc#(type a);
   interface PipeOut#(Tuple2#(a,a)) pipe;
   method Bool isFirst();
   method Bool isLast();
   method Action start(XYIteratorConfig#(a) cfg);
   method Action display();
endinterface

module mkXYIterator(XYIteratorIfc#(a)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a));
   Reg#(a) x <- mkReg(0);
   Reg#(a) y <- mkReg(0);
   Reg#(a) xbase <- mkReg(0);
   Reg#(a) ybase <- mkReg(0);
   Reg#(a) xstep <- mkReg(0);
   Reg#(a) ystep <- mkReg(0);
   Reg#(a) xlimit <- mkReg(0);
   Reg#(a) ylimit <- mkReg(0);

   Reg#(Bool) isFirstReg <- mkReg(False);
   Reg#(Bool) isLastReg <- mkReg(False);

   let guard = x < xlimit && y < ylimit;

   interface PipeOut pipe;
      method Tuple2#(a,a) first() if (guard);
	 return tuple2(x,y);
      endmethod
      method Action deq if (guard);
	 let newx = x;
	 let newy = y+ystep;
	 if (newy >= ylimit && x < xlimit) begin
	    newy = ybase;
	    newx = newx + xstep;
	 end
	 x <= newx;
	 y <= newy;
	 isLastReg <= (newx+xstep >= xlimit && newy+ystep >= ylimit);
	 isFirstReg <= False;
      endmethod
      method Bool notEmpty();
	 return guard;
      endmethod
   endinterface
   method Action start(XYIteratorConfig#(a) cfg) if (!guard);
      x <= cfg.xbase;
      y <= cfg.ybase;
      xbase <= cfg.xbase;
      ybase <= cfg.ybase;
      xstep <= cfg.xstep;
      ystep <= cfg.ystep;
      xlimit <= cfg.xlimit;
      ylimit <= cfg.ylimit;
      isFirstReg <= True;
      isLastReg <= (cfg.xbase+cfg.xstep) > cfg.xlimit && (cfg.ybase+cfg.ystep) > cfg.ylimit;
   endmethod
   method Bool isFirst(); return isFirstReg; endmethod
   method Bool isLast(); return isLastReg; endmethod
   method Action display();
      $display("XYIterator x=%d xlimit=%d y=%d ylimit=%d xstep=%d ystep=%d", x, xlimit, xstep, y, ylimit, ystep);
   endmethod
endmodule: mkXYIterator

// 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 BuildVector::*;
import MIMO::*;
import DefaultValue::*;
import Gearbox::*;
import Clocks::*;

typedef Tuple3#(x,x,x) Tripple#(type x);
typedef Tuple2#(x,x)   Pair#(type x);

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

function Bool pipeInNotFull(PipeIn#(a) pipein); return pipein.notFull(); endfunction
function Bool pipeOutNotEmpty(PipeOut#(a) pipein); return pipein.notEmpty(); endfunction

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

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

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

typeclass MkPipeIn#(type a, type b) dependencies ( b determines a);
   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#(a, Gearbox#(1, n, a));
   function PipeIn#(a) toPipeIn(Gearbox#(1, n, a) in);
      return (interface PipeIn#(a);
		 method Action enq(a v); in.enq(vec(v)); endmethod
		 method notFull = in.notFull;
	      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 ToPipeOut#(a, Gearbox#(m, 1, a));
   function PipeOut#(a) toPipeOut(Gearbox#(m, 1, a) in);
      return (interface PipeOut#(a);
		 method a first(); return in.first[0]; endmethod
		 method deq = in.deq;
		 method notEmpty = in.notEmpty;
	      endinterface);
   endfunction
endinstance

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

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

instance MkPipeOut#(a, ActionValue#(a))
   provisos (Bits#(a, asz));
   module mkPipeOut#(ActionValue#(a) in)(PipeOut#(a));
      FIFOF#(a) fifo <- mkFIFOF();
      rule connect;
	 let v <- in;
	 fifo.enq(v);
      endrule
      return toPipeOut(fifo);
   endmodule
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

module mkFunnelNode#(Vector#(n, PipeOut#(a)) inpipes, Integer numPipes, Put#(a) outpipe)(Empty);
   rule funnel;
      a v = ?;
      Bool send = False;
      for (Integer i = 0; i < valueOf(n) && i < numPipes; i = i+1)
	 if (!send && inpipes[i].notEmpty) begin
	    v <- toGet(inpipes[i]).get();
	    send = True;
	 end
      if (send)
	 outpipe.put(v);
   endrule
endmodule

instance FunnelPipesPipelined#(1,k,a,bpc)
   provisos (Log#(k, logk),
	     Bits#(a,a__),
	     Add#(1,b__,k),
	     Div#(logk,bpc,stages),
	     Add#(k, c__, TExp#(logk)),
	     Add#(TExp#(bpc), d__, TExp#(logk))
	     );
   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), vec(in));
      for(Integer j = valueOf(stages); j > 0; j=j-1) begin
	 Integer width = 2**(j*valueOf(bpc));
	 Integer stride = 2**valueOf(bpc);
	 for(Integer i = 0; i < width && i < valueOf(k); i=i+stride) begin
	    Vector#(TExp#(logk),PipeOut#(a)) paddedPipes = append(infss[j], replicate(?));
	    Vector#(TExp#(bpc),PipeOut#(a)) inpipes = takeAt(i, paddedPipes);
	    Integer numPipes = stride;
	    if (i + stride > valueOf(k))
	       numPipes = valueOf(k) - i;
	    mkFunnelNode(inpipes, numPipes, toPut(buffs[j-1][i/stride]));
	 end
      end
      return vec(infss[0][0]);
   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));
	 //$display("mkUnFunnelPipesPipelinedInternal::fill %d", dest);
      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 mkForkVectorPipelined#(PipeOut#(a) inpipe)(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 myand(Bool a, Bool b); return a && b; endfunction
      return foldl(myand, True, map(pipeOutNotEmpty, 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;
} IteratorConfig#(type a) deriving (Bits, FShow);

typedef struct {
   a value;
   Bool first;
   Bool last;
   b ctxt;
} IteratorValue#(type a, type b) deriving (Bits);

interface IteratorWithContext#(type a, type c);
   interface PipeOut#(a) pipe;
   interface PipeOut#(IteratorValue#(a,c)) ivpipe;
   method a count();
   method Bool isFirst();
   method Bool isLast();
   method Action start(IteratorConfig#(a) cfg, c ctxt);
   method c ctxt();
endinterface

interface IteratorIfc#(type a);
   interface PipeOut#(a) pipe;
   interface PipeOut#(IteratorValue#(a,void)) ivpipe;
   method a count();
   method Bool isFirst();
   method Bool isLast();
   method Action start(IteratorConfig#(a) cfg);
endinterface

module mkIteratorWithContext(IteratorWithContext#(a,c)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a), Bits#(c,cwidth));
   Reg#(c) ctxtReg <- mkReg(unpack(0));
   Reg#(a) countReg <- mkReg(0);
   Reg#(a) x <- mkReg(0);
   Reg#(a) xbase <- mkReg(0);
   Reg#(a) xstep <- mkReg(0);
   // inclusive limit
   Reg#(a) xlimit <- mkReg(0);
   Reg#(Bool) first <- mkReg(False);
   Reg#(Bool) last <- mkReg(False);
   Reg#(Bool) idle <- mkReg(True);
   Bool verbose = False;
   interface PipeOut pipe;
      method a first();
	 return x;
      endmethod
      method Action deq if (!idle);
	 let next_x = x + xstep;
	 countReg <= countReg + 1;
	 x <= x + xstep;
	 first <= False;
	 last <= (next_x+xstep >= xlimit);
	 idle <= last;
      endmethod
      method Bool notEmpty();
	 return (x < xlimit);
      endmethod
   endinterface
   interface PipeOut ivpipe;
      method IteratorValue#(a,c) first();
	 return IteratorValue { value: x, first: first, last: last, ctxt: ctxtReg };
      endmethod
      method Action deq if (!idle);
	 let next_x = x + xstep;
	 countReg <= countReg + 1;
	 x <= x + xstep;
	 first <= False;
	 last <= (next_x+xstep >= xlimit);
	 idle <= last;
      endmethod
      method Bool notEmpty();
	 return (x < xlimit);
      endmethod
   endinterface
   method Action start(IteratorConfig#(a) cfg, c ctxt) if (idle);
      countReg <= 0;
      x <= cfg.xbase;
      xbase <= cfg.xbase;
      xstep <= cfg.xstep;
      xlimit <= cfg.xlimit;

      first <= True;
      last <= (cfg.xbase+cfg.xstep >= cfg.xlimit);
      idle <= False;
      ctxtReg <= ctxt;
      if (verbose) $display("mkIterator xbase=%d xstep=%d xlimit=%d last=%d notEmpty=%d", cfg.xbase, cfg.xstep, cfg.xlimit, (cfg.xbase+cfg.xstep >= cfg.xlimit),
	 (cfg.xbase < cfg.xlimit));
   endmethod
   method Bool isFirst() = first;
   method Bool isLast() = last;
   method a count() = countReg;
   method c ctxt() = ctxtReg;
endmodule: mkIteratorWithContext

module mkIterator(IteratorIfc#(a)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a));
   IteratorWithContext#(a,void) iter <- mkIteratorWithContext();
   interface PipeOut pipe = iter.pipe;
   interface PipeOut ivpipe = iter.ivpipe;
   method Action start(IteratorConfig#(a) cfg);
      iter.start(cfg, ?);
   endmethod
   method a count() = iter.count();
   method isFirst = iter.isFirst;
   method isLast = iter.isLast;
endmodule

typedef struct {
   a xbase;
   a xlimit;
   a xstep;
   a ybase;
   a ylimit;
   a ystep;
} XYIteratorConfig#(type a) deriving (Bits, FShow);

interface XYIteratorIfc#(type a);
   interface PipeOut#(Tuple2#(a,a)) pipe;
   method Bool isFirst();
   method Bool isLast();
   method Action start(XYIteratorConfig#(a) cfg);
   method Action display();
endinterface

module mkXYIterator(XYIteratorIfc#(a)) provisos (Arith#(a), Bits#(a,awidth), Eq#(a), Ord#(a));
   Reg#(a) x <- mkReg(0);
   Reg#(a) y <- mkReg(0);
   Reg#(a) xbase <- mkReg(0);
   Reg#(a) ybase <- mkReg(0);
   Reg#(a) xstep <- mkReg(0);
   Reg#(a) ystep <- mkReg(0);
   Reg#(a) xlimit <- mkReg(0);
   Reg#(a) ylimit <- mkReg(0);

   Reg#(Bool) isFirstReg <- mkReg(False);
   Reg#(Bool) isLastReg <- mkReg(False);

   let guard = x < xlimit && y < ylimit;

   interface PipeOut pipe;
      method Tuple2#(a,a) first() if (guard);
	 return tuple2(x,y);
      endmethod
      method Action deq if (guard);
	 let newx = x;
	 let newy = y+ystep;
	 if (newy >= ylimit && x < xlimit) begin
	    newy = ybase;
	    newx = newx + xstep;
	 end
	 x <= newx;
	 y <= newy;
	 isLastReg <= (newx+xstep >= xlimit && newy+ystep >= ylimit);
	 isFirstReg <= False;
      endmethod
      method Bool notEmpty();
	 return guard;
      endmethod
   endinterface
   method Action start(XYIteratorConfig#(a) cfg) if (!guard);
      x <= cfg.xbase;
      y <= cfg.ybase;
      xbase <= cfg.xbase;
      ybase <= cfg.ybase;
      xstep <= cfg.xstep;
      ystep <= cfg.ystep;
      xlimit <= cfg.xlimit;
      ylimit <= cfg.ylimit;
      isFirstReg <= True;
      isLastReg <= (cfg.xbase+cfg.xstep) > cfg.xlimit && (cfg.ybase+cfg.ystep) > cfg.ylimit;
   endmethod
   method Bool isFirst(); return isFirstReg; endmethod
   method Bool isLast(); return isLastReg; endmethod
   method Action display();
      $display("XYIterator x=%d xlimit=%d y=%d ylimit=%d xstep=%d ystep=%d", x, xlimit, xstep, y, ylimit, ystep);
   endmethod
endmodule: mkXYIterator


remove unused parser.bsv

remove unused parser.bsv

// Copyright (c) 2015 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.

/* DO NOT MODIFY, AUTO GENERATED BY P4 COMPILER */

import BuildVector::*;
import Connectable::*;
import DefaultValue::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import Pipe::*;
import StmtFSM::*;
import SpecialFIFOs::*;
import Utils::*;
import Vector::*;

import Ethernet::*;
import P4Types::*;

interface Parser;
   // derive parseReset from start of packet
   method Action parserReset();
   interface PipeIn#(EtherData) frameIn;
   interface PipeOut#(void) parseDone;
   //interface PipeOut#(Bit#(8)) parsedOut_ipv4_ttl;
   //interface PipeOut#(Bit#(48)) parsedOut_ethernet_srcAddr;
   //interface PipeOut#(Bit#(48)) parsedOut_ethernet_dstAddr;
   interface PipeOut#(Bit#(128)) payloadOut;
endinterface

interface ParseEthernet;
   interface PipeIn#(Bit#(128)) packetIn;
   interface PipeOut#(Bit#(48)) parsedOut_ethernet_srcAddr;
   interface PipeOut#(Bit#(48)) parsedOut_ethernet_dstAddr;
   interface PipeOut#(Bit#(16)) unparsedOutIpv4; // number is parse state id
   interface PipeOut#(Bit#(16)) unparsedOutVlan; // number is parse state id
   interface PipeOut#(ParserState) nextState;
   method Action start;
   method Action clear;
endinterface

interface ParseVlan;
   interface PipeIn#(Bit#(128)) packetIn;
   interface PipeIn#(Bit#(16)) unparsedIn;
   interface PipeOut#(Vlan_tag_t) parsedOut;
   interface PipeOut#(Bit#(112)) unparsedOutVlan0;
   //interface PipeOut#(Bit#(80)) unparsedOutVlan1;
   interface PipeOut#(ParserState) nextState;
   method Action start;
   method Action clear;
endinterface

interface ParseIpv4;
   interface PipeIn#(Bit#(128)) packetIn;
   interface PipeIn#(Bit#(16)) unparsedIn;
   interface PipeOut#(Ipv4_t) parsedOut;
   interface PipeIn#(Bit#(112)) unparsedInVlan0;
   //interface PipeIn#(Bit#(80)) unparsedInVlan1;
   interface PipeOut#(Bit#(112)) unparsedOut;
   interface PipeOut#(Bit#(8)) parsedOut_ipv4_ttl;
   interface PipeOut#(ParserState) nextState;
   method Action start;
   method Action clear;
endinterface

typedef enum {S0, S1, S2, S3, S4} ParserState deriving (Bits, Eq);
instance FShow#(ParserState);
   function Fmt fshow (ParserState state);
      return $format(" State %x", state);
   endfunction
endinstance

module mkParseEthernet(ParseEthernet);
   FIFOF#(Bit#(128)) packet_in_fifo <- mkBypassFIFOF;
   FIFOF#(Bit#(48)) parsed_out_ethernet_srcAddr_fifo <- mkFIFOF1;
   FIFOF#(Bit#(48)) parsed_out_ethernet_dstAddr_fifo <- mkFIFOF1;
   FIFOF#(Bit#(16))  unparsed_out_parse_ipv4_fifo <- mkSizedFIFOF(1);
   FIFOF#(Bit#(16))  unparsed_out_parse_vlan_fifo <- mkSizedFIFOF(1);
   FIFOF#(ParserState) next_state_fifo <- mkBypassFIFOF;
   Wire#(Bit#(128)) packet_in_wire <- mkDWire(0);

   let verbose = True;
   Reg#(Cycle_t) cycle <- mkReg(defaultValue);
   rule cycleRule if (verbose);
      cycle.cnt <= cycle.cnt + 1;
   endrule

   rule load_packet_in;
      let data_current <- toGet(packet_in_fifo).get;
      packet_in_wire <= data_current;
   endrule

   Stmt parse_ethernet =
   seq
   action // parse_ethernet
      let data = packet_in_wire;
      Vector#(128, Bit#(1)) dataVec = unpack(data);
      let ethernet = extract_ethernet(pack(takeAt(0, dataVec)));
      Vector#(16, Bit#(1)) unparsed = takeAt(112, dataVec);
      if (verbose) $display(fshow(cycle)
                            +fshow(" ether.dstAddr=")+fshow(ethernet.dstAddr)
                            +fshow(" ether.srcAddr=")+fshow(ethernet.srcAddr)
                            +fshow(" ether.etherType=")+fshow(ethernet.etherType));

      //parsed_out_ethernet_srcAddr_fifo.enq(ethernet.srcAddr);
      //parsed_out_ethernet_dstAddr_fifo.enq(ethernet.dstAddr);
      ParserState nextState = S0;
      case (byteSwap(ethernet.etherType)) matches
         'h_8100: begin
            unparsed_out_parse_vlan_fifo.enq(pack(unparsed));
            nextState = S2;
         end
         'h_9100: begin
            nextState = S2;
         end
         'h_0800: begin
            unparsed_out_parse_ipv4_fifo.enq(pack(unparsed));
            nextState = S3;
         end
         default: begin
            $display("null");
         end
      endcase
      next_state_fifo.enq(nextState);
   endaction
   endseq;

   FSM fsm_parse_ethernet <- mkFSM(parse_ethernet);

   method Action start();
      fsm_parse_ethernet.start;
   endmethod
   method Action clear();
      fsm_parse_ethernet.abort;
   endmethod
   interface packetIn = toPipeIn(packet_in_fifo);
   interface unparsedOutIpv4 = toPipeOut(unparsed_out_parse_ipv4_fifo);
   interface unparsedOutVlan = toPipeOut(unparsed_out_parse_vlan_fifo);
   interface parsedOut_ethernet_srcAddr = toPipeOut(parsed_out_ethernet_srcAddr_fifo);
   interface parsedOut_ethernet_dstAddr = toPipeOut(parsed_out_ethernet_dstAddr_fifo);
   interface nextState = toPipeOut(next_state_fifo);
endmodule

interface ParseVlanArbiter;
   interface PipeOut#(ParserState) outToFSM;
   interface Vector#(2, PipeIn#(ParserState)) in;
endinterface

(* synthesize *)
module mkParseVlanArbiter(ParseVlanArbiter);
   FIFOF#(ParserState) state_out_fifo <- mkBypassFIFOF;
   Vector#(2, FIFOF#(ParserState)) state_in_fifo <- replicateM(mkBypassFIFOF);

   (* fire_when_enabled *)
   rule arbitrate_outgoing_state;
      Bool stateSet = False;
      for (Integer port=0; port<2; port=port+1) begin
         if (!stateSet && state_in_fifo[port].notEmpty()) begin
            ParserState state <- toGet(state_in_fifo[port]).get;
            stateSet = True;
            state_out_fifo.enq(state);
         end
      end
   endrule
   interface in = map(toPipeIn, state_in_fifo);
   interface outToFSM = toPipeOut(state_out_fifo);
endmodule

// Parse second VLAN
module mkParseVlan(ParseVlan);
   FIFOF#(Bit#(128)) packet_in_fifo <- mkBypassFIFOF;
   FIFOF#(Bit#(16)) unparsed_in_fifo <- mkBypassFIFOF;
   FIFOF#(Bit#(16)) unparsed_in_fifo_vlan0 <- mkBypassFIFOF;
//   FIFOF#(Bit#(16)) unparsed_in_fifo_vlan1 <- mkBypassFIFOF;
   FIFOF#(Vlan_tag_t) parsed_out_fifo <- mkSizedFIFOF(1);
   FIFOF#(Bit#(112)) unparsed_out_parse_vlan0_fifo <- mkSizedFIFOF(1);
   FIFOF#(Bit#(80)) unparsed_out_parse_vlan1_fifo <- mkSizedFIFOF(1);

   FIFOF#(ParserState) next_state_fifo <- mkBypassFIFOF;
   Wire#(Bit#(128)) packet_in_wire <- mkDWire(0);

   let verbose = True;
   Reg#(Cycle_t) cycle <- mkReg(defaultValue);
   rule every if (verbose);
      cycle.cnt <= cycle.cnt + 1;
   endrule

   ParseVlanArbiter nextStateArbiter <- mkParseVlanArbiter();

   function ParserState compute_next_state(Bit#(16) etherType);
      ParserState nextState = S0;
      case (byteSwap(etherType)) matches
         'h_8100: begin
            nextState = S2;
         end
         'h_9100: begin
            nextState = S2;
         end
         'h_0800: begin
            nextState = S3;
         end
      endcase
      return nextState;
   endfunction

   rule load_packet_in;
      let data_current <- toGet(packet_in_fifo).get;
      packet_in_wire <= data_current;
      if (verbose) $display(fshow(cycle) + fshow("load packet in"));
   endrule

   rule load_unparsed_in;
      let data_delayed <- toGet(unparsed_in_fifo).get;
      unparsed_in_fifo_vlan0.enq(data_delayed);
//      unparsed_in_fifo_vlan1.enq(data_delayed);
      if (verbose) $display(fshow(cycle) + fshow("load unparsed in"));
   endrule

   mkConnection(nextStateArbiter.outToFSM, toPipeIn(next_state_fifo));

   Stmt parse_vlan_0 =
   // VLAN
   seq
   action
      let data_delayed <- toGet(unparsed_in_fifo_vlan0).get;
      let data_current = packet_in_wire;
      $display("VLAN %x", data_current);
      Bit#(144) data = {data_current, data_delayed};
      Vector#(144, Bit#(1)) dataVec = unpack(data);
      let vlan0 = extract_vlan(pack(takeAt(0, dataVec)));
      let nextState0 = compute_next_state(vlan0.etherType);
      let residue0 = takeAt(32, dataVec);
      if (nextState0 == S3) begin
         if (verbose) $display(fshow(cycle) + fshow("Vlan etherType=") + fshow(vlan0.etherType));
         unparsed_out_parse_vlan0_fifo.enq(pack(residue0));
         nextStateArbiter.in[0].enq(nextState0);
      end
   endaction
   endseq;

//   Stmt parse_vlan_1 =
//   // VLAN|VLAN
//   seq
//   action
//      let data_delayed <- toGet(unparsed_in_fifo_vlan1).get;
//      let data_current = packet_in_wire;
//      $display("VLAN|VLAN %x", data_current);
//      Bit#(144) data = {data_current, data_delayed};
//      Vector#(144, Bit#(1)) dataVec = unpack(data);
//      let vlan0 = extract_vlan(pack(takeAt(0, dataVec)));
//      let nextState0 = compute_next_state(vlan0.etherType);
//      let residue0 = takeAt(32, dataVec);
//      let vlan1 = extract_vlan(pack(takeAt(32, dataVec)));
//      let nextState1 = compute_next_state(vlan1.etherType);
//      let residue1 = takeAt(64, dataVec);
//      if (nextState0 == S2 && nextState1 == S3) begin
//         if (verbose) $display(fshow(cycle) + fshow("Vlan etherType=") + fshow(vlan0.etherType));
//         if (verbose) $display(fshow(cycle) + fshow("Vlan etherType=") + fshow(vlan1.etherType));
//         unparsed_out_parse_vlan1_fifo.enq(pack(residue1));
//         nextStateArbiter.in[1].enq(nextState1);
//      end
//   endaction
//   endseq;

   FSM fsm_parse_vlan_0 <- mkFSM(parse_vlan_0);
//   FSM fsm_parse_vlan_1 <- mkFSM(parse_vlan_0);

   method Action start;
      fsm_parse_vlan_0.start;
//      fsm_parse_vlan_1.start;
   endmethod
   method Action clear;
      fsm_parse_vlan_0.abort;
//      fsm_parse_vlan_1.abort;
   endmethod

   interface packetIn = toPipeIn(packet_in_fifo);
   interface unparsedIn = toPipeIn(unparsed_in_fifo);
   interface unparsedOutVlan0 = toPipeOut(unparsed_out_parse_vlan0_fifo);
//   interface unparsedOutVlan1 = toPipeOut(unparsed_out_parse_vlan1_fifo);
   interface parsedOut = toPipeOut(parsed_out_fifo);
   interface nextState = toPipeOut(next_state_fifo);
endmodule

interface ParseIpv4Arbiter;
   interface PipeOut#(ParserState) outToFSM;
   interface Vector#(3, PipeIn#(ParserState)) in;
endinterface

(* synthesize *)
module mkParseIpv4Arbiter(ParseIpv4Arbiter);
   FIFOF#(ParserState) state_out_fifo <- mkBypassFIFOF;
   Vector#(3, FIFOF#(ParserState)) state_in_fifo <- replicateM(mkBypassFIFOF);

   (* fire_when_enabled *)
   rule arbitrate_outgoing_state;
      Bool stateSet = False;
      for (Integer port=0; port<3; port=port+1) begin
         if (!stateSet && state_in_fifo[port].notEmpty()) begin
            ParserState state <- toGet(state_in_fifo[port]).get;
            stateSet = True;
            state_out_fifo.enq(state);
         end
      end
   endrule
   interface in = map(toPipeIn, state_in_fifo);
   interface outToFSM = toPipeOut(state_out_fifo);
endmodule

module mkParseIpv4(ParseIpv4);
   FIFOF#(Bit#(128)) packet_in_fifo <- mkBypassFIFOF;

   FIFOF#(Bit#(8)) parsed_out_ipv4_ttl_fifo <- mkFIFOF1;
   FIFOF#(Bit#(16)) unparsed_in_fifo <- mkBypassFIFOF;
   FIFOF#(Bit#(112)) unparsed_in_vlan0_fifo <- mkBypassFIFOF;
//   FIFOF#(Bit#(80)) unparsed_in_vlan1_fifo <- mkBypassFIFOF;
   FIFOF#(Bit#(112)) unparsed_out_fifo <- mkSizedFIFOF(1);
   FIFOF#(Bit#(144)) internal_fifo <- mkSizedFIFOF(1);

   FIFOF#(ParserState) next_state_fifo <- mkBypassFIFOF;
   Wire#(Bit#(128)) packet_in_wire <- mkDWire(0);

   let verbose = True;
   Reg#(Cycle_t) cycle <- mkReg(defaultValue);
   rule every if (verbose);
      cycle.cnt <= cycle.cnt + 1;
   endrule

   ParseIpv4Arbiter nextStateArbiter <- mkParseIpv4Arbiter();

   rule load_packet_in;
      let data_current <- toGet(packet_in_fifo).get;
      packet_in_wire <= data_current;
      $display(fshow("loading packet") + fshow(data_current));
   endrule

   mkConnection(nextStateArbiter.outToFSM, toPipeIn(next_state_fifo));

   Stmt parse_ipv4_0 =
   // IP
   seq
   action // parse_ipv4
      let residue_last <- toGet(unparsed_in_fifo).get; // 16-bit
      let data_current = packet_in_wire;
      Bit#(144) data = {data_current, residue_last};
      Vector#(144, Bit#(1)) dataVec = unpack(data);
      internal_fifo.enq(data);
      if (verbose) $display(fshow(cycle) + fshow("wait one cycle!"));
   endaction
   action // parse_ipv4 0
      let data_delayed <- toGet(internal_fifo).get;
      let data_current = packet_in_wire;
      Bit#(272) data = {data_current, data_delayed};
      Vector#(272, Bit#(1)) dataVec = unpack(data);
      Vector#(112, Bit#(1)) residue = takeAt(160, dataVec);
      let ipv4 = extract_ipv4(data[159:0]);
      //parsed_out_ipv4_ttl_fifo.enq(ipv4.ttl);
      if (verbose) $display(fshow(cycle)+
                            $format(" ipv4.srcAddr=%x", ipv4.srcAddr)+
                            $format(" ipv4.dstAddr=%x", ipv4.dstAddr));
      nextStateArbiter.in[0].enq(S4);
   endaction
   endseq;

//   Stmt parse_ipv4_1 =
//   // VLAN|IP
//   seq
//   action
//      let residue_last <- toGet(unparsed_in_vlan0_fifo).get; // 112-bit;
//      let data_current = packet_in_wire;
//      Bit#(240) data = {data_current, residue_last};
//      Vector#(240, Bit#(1)) dataVec = unpack(data);
//      Vector#(80, Bit#(1)) residue = takeAt(160, dataVec);
//      let ipv4 = extract_ipv4(data[159:0]);
//      if (verbose) $display(fshow(cycle)+
//                            $format(" ipv4.srcAddr=%x", ipv4.srcAddr)+
//                            $format(" ipv4.dstAddr=%x", ipv4.dstAddr));
//      nextStateArbiter.in[1].enq(S4);
//   endaction
//   endseq;

//   Stmt parse_ipv4_2 =
//   // VLAN|VLAN|IP
//   seq
//   action
//      let residue_last <- toGet(unparsed_in_vlan1_fifo).get; // 112-bit;
//      let data_current = packet_in_wire;
//      Bit#(208) data = {data_current, residue_last};
//      Vector#(208, Bit#(1)) dataVec = unpack(data);
//      Vector#(48, Bit#(1)) residue = takeAt(160, dataVec);
//      let ipv4 = extract_ipv4(data[159:0]);
//      if (verbose) $display(fshow(cycle)+
//                            $format(" ipv4.srcAddr=%x", ipv4.srcAddr)+
//                            $format(" ipv4.dstAddr=%x", ipv4.dstAddr));
//      nextStateArbiter.in[2].enq(S4);
//   endaction
//   endseq;

   FSM fsm_parse_ipv4_0 <- mkFSM(parse_ipv4_0);
//   FSM fsm_parse_ipv4_1 <- mkFSM(parse_ipv4_1);
//   FSM fsm_parse_ipv4_2 <- mkFSM(parse_ipv4_2);

   method Action start();
      fsm_parse_ipv4_0.start;
//      fsm_parse_ipv4_1.start;
//      fsm_parse_ipv4_2.start;
   endmethod
   method Action clear();
      fsm_parse_ipv4_0.abort;
//      fsm_parse_ipv4_1.abort;
//      fsm_parse_ipv4_2.abort;
   endmethod
   interface packetIn = toPipeIn(packet_in_fifo);
   interface unparsedIn = toPipeIn(unparsed_in_fifo);
//   interface unparsedInVlan0 = toPipeIn(unparsed_in_vlan0_fifo);
//   interface unparsedInVlan1 = toPipeIn(unparsed_in_vlan1_fifo);
   interface unparsedOut = toPipeOut(unparsed_out_fifo);
   interface parsedOut_ipv4_ttl = toPipeOut(parsed_out_ipv4_ttl_fifo);
   interface nextState = toPipeOut(next_state_fifo);
endmodule

(* synthesize *)
module mkParser(Parser);
   FIFOF#(EtherData) data_in_fifo <- mkSizedFIFOF(6);

   ParseEthernet parse_ethernet <- mkParseEthernet();
   ParseVlan parse_vlan <- mkParseVlan();
   ParseIpv4 parse_ipv4 <- mkParseIpv4();

   List#(Reg#(Vlan_tag_t)) vlan_tag_stack <- List::replicateM(2, mkReg(defaultValue));

   mkConnection(parse_ethernet.unparsedOutIpv4, parse_ipv4.unparsedIn);
   mkConnection(parse_ethernet.unparsedOutVlan, parse_vlan.unparsedIn);
   mkConnection(parse_vlan.unparsedOutVlan0, parse_ipv4.unparsedInVlan0);
   //mkConnection(parse_vlan.unparsedOutVlan1, parse_ipv4.unparsedInVlan1);

   let verbose = True;
   Reg#(Cycle_t) cycle <- mkReg(defaultValue);
   rule every if (verbose);
      cycle.cnt <= cycle.cnt + 1;
   endrule

   Reg#(ParserState) curr_state <- mkReg(S0);
   Reg#(Bool) started <- mkReg(False);

   rule start_fsm (curr_state == S0 && data_in_fifo.notEmpty);
      if (!started) begin
         parse_ethernet.start;
         parse_vlan.start;
         parse_ipv4.start;
         started <= True;
      end
   endrule

   rule stop_fsm (curr_state == S4);
      if (started) begin
         parse_ethernet.clear;
         parse_ipv4.clear;
         parse_vlan.clear;
         started <= False;
      end
   endrule

   // Parsing Graph
   (* fire_when_enabled *)
   rule state_S0 (curr_state == S0 && data_in_fifo.notEmpty);
      let v = data_in_fifo.first;
      if (v.sop) begin
         curr_state <= S1;
         if (verbose) $display(fshow(cycle) + fshow("Done with") + fshow(curr_state));
      end
      else begin
         data_in_fifo.deq;
      end
   endrule
   (* fire_when_enabled *)
   rule state_S1 (curr_state == S1);
      let v <- toGet(parse_ethernet.nextState).get;
      curr_state <= v;
      if (verbose) $display(fshow(cycle) + fshow("Done with") + fshow(curr_state));
   endrule
   (* fire_when_enabled *)
   rule state_S2 (curr_state == S2);
      let v <- toGet(parse_vlan.nextState).get;
      curr_state <= v;
      if (verbose) $display(fshow(cycle) + fshow("Done with") + fshow(curr_state));
   endrule
   (* fire_when_enabled *)
   rule state_S3 (curr_state == S3);
      let v <- toGet(parse_ipv4.nextState).get;
      curr_state <= v;
      if (verbose) $display(fshow(cycle) + fshow("Done with") + fshow(curr_state));
   endrule
   (* fire_when_enabled *)
   rule state_S4 (curr_state == S4);
      let v <- toGet(data_in_fifo).get;
      if (v.eop) begin
         curr_state <= S0;
         if (verbose) $display(fshow(cycle) + fshow("Done with") + fshow(curr_state));
      end
   endrule

   // Data dispatcher.
   rule state_S1_input (curr_state == S1);
      let v <- toGet(data_in_fifo).get;
      parse_ethernet.packetIn.enq(v.data);
      if (verbose) $display(fshow(cycle) + fshow("parse_ethernet enqueue ")+ fshow(v));
   endrule
   rule state_S2_input (curr_state == S2);
      let v <- toGet(data_in_fifo).get;
      parse_vlan.packetIn.enq(v.data);
      if (verbose) $display(fshow(cycle) + fshow("parse_vlan enqueue ")+ fshow(v));
   endrule
   rule state_S3_input (curr_state == S3);
      let v <- toGet(data_in_fifo).get;
      parse_ipv4.packetIn.enq(v.data);
      if (verbose) $display(fshow(cycle) + fshow("parse_ipv4 enqueue ") + fshow(v));
   endrule

   // derive parse done from state machine
   interface frameIn = toPipeIn(data_in_fifo);
//   interface parsedOut_ipv4_ttl = parse_ipv4.parsedOut_ipv4_ttl;
//   interface parsedOut_ethernet_srcAddr = parse_ethernet.parsedOut_ethernet_srcAddr;
//   interface parsedOut_ethernet_dstAddr = parse_ethernet.parsedOut_ethernet_dstAddr;
endmodule