third argument to DmaSourceVector.start is now length rather than limit (since change to using MemreadEngine

// 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 FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import BRAM::*;
import DmaVector::*;
import PortalMemory::*;
import Dma::*;
import FloatingPoint::*;
import Pipe::*;
import FloatOps::*;
import Timer::*;
import RbmTypes::*;

module [Module] mkDotProd#(PipeOut#(Vector#(n,Float)) xpipe, PipeOut#(Vector#(n,Float)) ypipe, UInt#(nwidth) numElts, UInt#(TLog#(n)) label)(PipeOut#(Float))
   provisos (
      Add#(1,a__,n),
      ReducePipe#(n,Float)
      );

   Bool verbose = False;

   PipeOut#(Tuple2#(Vector#(n,Float),Vector#(n,Float))) xypipe <- mkJoin(tuple2, xpipe, ypipe);

   Vector#(n, Server#(Tuple4#(Maybe#(Float), Float, Float, RoundMode), Tuple2#(Float,Exception))) macs <- replicateM(mkFloatMac);
   Vector#(n, FIFO#(Maybe#(Float))) accumFifo <- replicateM(mkFIFO());
   Reg#(UInt#(nwidth)) countInReg <- mkReg(0);
   Reg#(UInt#(nwidth)) countOutReg <- mkReg(0);
   FIFOF#(Vector#(n,Float)) resultFifo <- mkFIFOF();

   rule mul;
      let v = xypipe.first;
      xypipe.deq;
      let x = tpl_1(v);
      let y = tpl_2(v);

      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 Maybe#(Float) accum = tagged Invalid;
	 if (countInReg > 0) begin
	    accum = accumFifo[i].first();
	    accumFifo[i].deq();
	 end
	 if (verbose) $display($format(fshow("label=")+fshow(label)+fshow(" dotprod x=") + fshow(x) + fshow(" y=") + fshow(y) + fshow(" accum=")+fshow(pack(accum))));
         macs[i].request.put(tuple4(accum, x[i], y[i], defaultValue));
	 if (i == 0) begin
	    let c = countInReg + fromInteger(valueOf(n));
	    if (c >= numElts)
	       c = 0;
	    countInReg <= c;
	 end
      end
   endrule

   rule macout;
      Vector#(n,Float) vs = unpack(0);
      let c = countOutReg + fromInteger(valueOf(n));
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 let resp <- macs[i].response.get();
	 vs[i] = tpl_1(resp);
	 if (c < numElts) begin
	    accumFifo[i].enq(tagged Valid tpl_1(resp));
	 end
      end
      if (c >= numElts) begin
	 Vector#(n,Bit#(32)) ivs = unpack(pack(vs));
	 resultFifo.enq(vs);
	 c = 0;
	 if (verbose) $display(fshow("label=")+fshow(label)+fshow(" countOutReg=")+fshow(countOutReg)+fshow(" numElts=")+fshow(numElts)+fshow(" mul=") + fshow(ivs));
      end
      countOutReg <= c;
   endrule

   PipeOut#(Vector#(n,Float)) resultPipe = toPipeOut(resultFifo);
   PipeOut#(Float) dotpipe <- mkReducePipe(mkFloatAddPipe, resultPipe);
   PipeOut#(Float) displaydotpipe = (interface PipeOut#(Float);
      method Float first(); return dotpipe.first(); endmethod
      method Action deq(); $display(fshow("label=")+fshow(label)+fshow(" dotpipe=")+fshow(dotpipe.first())); dotpipe.deq(); endmethod
      method Bool notEmpty(); return dotpipe.notEmpty(); endmethod
      endinterface);
   return dotpipe;
endmodule

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

interface XYRangePipeIfc#(type a);
   interface PipeOut#(Tuple2#(a,a)) pipe;
   method Action start(XYRangeConfig#(a) cfg);
   method Action display();
endinterface

module mkXYRangePipeOut(XYRangePipeIfc#(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);

   interface PipeOut pipe;
      method Tuple2#(a,a) first() if (x < xlimit && y < ylimit);
	 return tuple2(x,y);
      endmethod
      method Action deq if (x < xlimit && y < ylimit);
	 let newx = x;
	 let newy = y+ystep;
	 if (newy >= ylimit && x < xlimit) begin
	    newy = ybase;
	    newx = newx + xstep;
	 end
	 x <= newx;
	 y <= newy;
      endmethod
      method Bool notEmpty();
	 return (x < xlimit && y < ylimit);
      endmethod
   endinterface
   method Action start(XYRangeConfig#(a) cfg) if (x >= xlimit);
      $display("XYRangePipe x=%d xlimit=%d xstep=%d y=%d ylimit=%d ystep=%d", cfg.xbase, cfg.xlimit, cfg.xstep, cfg.ybase, cfg.ylimit, cfg.ystep);
      x <= cfg.xbase;
      y <= cfg.ybase;
      xbase <= cfg.xbase;
      ybase <= cfg.ybase;
      xstep <= cfg.xstep;
      ystep <= cfg.ystep;
      xlimit <= cfg.xlimit;
      ylimit <= cfg.ylimit;
   endmethod
   method Action display();
      $display("XYRangePipe x=%d xlimit=%d y=%d ylimit=%d xstep=%d ystep=%d", x, xlimit, xstep, y, ylimit, ystep);
   endmethod
endmodule: mkXYRangePipeOut

typedef struct {
   ObjectPointer pointer;
   addrtype base;
   addrtype numRows;
   addrtype numColumns;
} MatrixDescriptor#(type addrtype) deriving (Bits);

// row major layout
interface DmaMatrixMultiplyIfc#(numeric type addrwidth, numeric type dsz);
   interface ObjectWriteClient#(dsz) dmaClient;
   method Action start(ObjectPointer pointerA, UInt#(addrwidth) numRowsA, UInt#(addrwidth) numColumnsA,
		       ObjectPointer pointerB, UInt#(addrwidth) numRowsB, UInt#(addrwidth) numColumnsB,
		       ObjectPointer pointerC);
   method ActionValue#(Bool) finish();
endinterface

typedef enum {
   Idle, Ready, Running, Done
   } MMState deriving (Bits, Eq);

typedef 2 N;

module [Module] mkDmaMatrixMultiply#(Vector#(1, VectorSource#(dsz, Vector#(N, Float))) sourceA,
				     Vector#(N, VectorSource#(dsz, Vector#(N, Float))) sourceB,
				     function Module#(DmaVectorSink#(dsz, Vector#(N, Float))) mkSink(PipeOut#(Vector#(N, Float)) pipe_in)
				     )(DmaMatrixMultiplyIfc#(addrwidth, dsz))
   provisos (Add#(a__,ObjectOffsetSize,addrwidth)
	     , Add#(N,0,n)
	     , FShow#(Float)
	     , Arith#(Float)
	     , Bits#(Vector#(N, Float), dsz));

   let n = valueOf(n);
   Bool verbose = True;

   Reg#(Bool) doneReg <- mkReg(False);
   Reg#(MatrixDescriptor#(UInt#(addrwidth))) descriptorA <- mkReg(unpack(0));
   Reg#(MatrixDescriptor#(UInt#(addrwidth))) descriptorB <- mkReg(unpack(0));
   Reg#(MatrixDescriptor#(UInt#(addrwidth))) descriptorC <- mkReg(unpack(0));
   Reg#(UInt#(addrwidth)) dotprodCount <- mkReg(0);

   Vector#(n, PipeOut#(Vector#(N,Float))) aPipes <- mkForkVector(sourceA[0].pipe);

   function Module#(PipeOut#(Float)) mkFxDotProd(Integer i);
      return mkDotProd(aPipes[i], sourceB[i].pipe, descriptorA.numColumns, fromInteger(i));
   endfunction
   Vector#(n, PipeOut#(Float)) fxdotprods <- genWithM(mkFxDotProd);

   FIFOF#(Vector#(N,Float)) dfifo <- mkFIFOF();
   let sinkC <- mkSink(toPipeOut(dfifo));

   XYRangePipeIfc#(UInt#(addrwidth)) xypipeifc <- mkXYRangePipeOut();

   Vector#(TAdd#(n,1), PipeOut#(Tuple2#(UInt#(addrwidth),UInt#(addrwidth)))) xypipes <- mkForkVector(xypipeifc.pipe);

   Reg#(Bool) running <- mkReg(False);
   FIFOF#(Bool) doneFifo <- mkFIFOF();

   Reg#(UInt#(32)) cycles <- mkReg(0);
   rule countCycles;
      cycles <= cycles+1;
   endrule

   for (Integer i = 0; i < n; i = i + 1) begin
      rule startDotProd;
	  Tuple2#(UInt#(addrwidth),UInt#(addrwidth)) xy = xypipes[i].first;
	  xypipes[i].deq();

	  let row = tpl_1(xy);
	  let col = tpl_2(xy)+fromInteger(i);

	  let startA = row*descriptorA.numColumns; // row major
	  let startB = col*descriptorB.numColumns; // col major layout (pre-transposed)
	  let startC = row*descriptorC.numColumns + col; // row major
	 if (verbose) $display($format(fshow(cycles)+fshow("    startDotProd xy=")+fshow(tuple2(row,col))
				       +fshow(" startA=")+fshow(startA)
				       +fshow(" startB=")+fshow(startB)
				       +fshow(" startC=")+fshow(startC)));

	  if (i == 0) begin
	     sourceA[0].start(descriptorA.pointer, pack(truncate(startA)), pack(truncate(descriptorA.numColumns)));
	     $display($format(fshow(cycles)+fshow("    sourceA[0].start")+fshow(startA)));
	  end
	 sourceB[i].start(descriptorB.pointer, pack(truncate(startB)), pack(truncate(descriptorB.numColumns)));
	 UInt#(TLog#(n)) in = fromInteger(i);
	 $display($format(fshow(cycles)+fshow("    sourceB[")+fshow(in)+fshow("].start")+fshow(startB)));
	  if (i == 0)
	     sinkC.vector.start(descriptorC.pointer, pack(truncate(startC)), extend(pack(in)));
       endrule
       if (i == 0)
	  rule finishSourceA;
	     $display($format(fshow(cycles)+fshow("    sourceA[0].finish ")));
	     let b <- sourceA[0].finish();
	  endrule
      rule finishSourceB;
	 UInt#(TLog#(n)) in = fromInteger(i);
	 $display($format(fshow(cycles)+fshow("    sourceB[")+fshow(in)+fshow("].finish")));
	 let b <- sourceB[i].finish();
      endrule

   end

   rule dotProdValue;
      Tuple2#(UInt#(addrwidth),UInt#(addrwidth)) xy = xypipes[n].first;
      xypipes[n].deq;
      Vector#(N,Float) vs;
      for (Integer i = 0; i < n; i = i + 1) begin
	 let v = fxdotprods[i].first;
	 fxdotprods[i].deq;
	 let xyi = tuple2(tpl_1(xy), tpl_2(xy)+fromInteger(i));
	 if (verbose) $display($format(fshow(cycles)+fshow("    dotprodvalue xy=")+fshow(xyi)+fshow(" dotprod=")+fshow(v)));
	 vs[i] = v;
      end
      dfifo.enq(vs);
   endrule

   rule sinkDone;
      // each time we write a burst of k values via sinkC
      let b <- sinkC.vector.finish();
      let c = dotprodCount-fromInteger(n);
      $display($format(fshow(cycles)+fshow("    sinkDone c")+fshow(c)));
      dotprodCount <= c;
      if (c == 0) begin
	 running <= False;
	 doneFifo.enq(?);
      end
   endrule

   method Action start(ObjectPointer pointerA, UInt#(addrwidth) numRowsA, UInt#(addrwidth) numColumnsA,
		       ObjectPointer pointerB, UInt#(addrwidth) numRowsB, UInt#(addrwidth) numColumnsB,
		       ObjectPointer pointerC) if (dotprodCount == 0);
      XYRangeConfig#(UInt#(addrwidth)) xycfg = XYRangeConfig {xbase: 0, xlimit: numRowsA, xstep: 1,
							      ybase: 0, ylimit: numRowsB, ystep: fromInteger(n) };
      descriptorA <= MatrixDescriptor { pointer: pointerA, base: 0, numRows: numRowsA, numColumns: numColumnsA};
      descriptorB <= MatrixDescriptor { pointer: pointerB, base: 0, numRows: numRowsB, numColumns: numColumnsB};
      descriptorC <= MatrixDescriptor { pointer: pointerC, base: 0, numRows: numRowsA, numColumns: numRowsB};
      dotprodCount <= numRowsA*numRowsB;

      $display("mm pointerA=%d pointerB=%d pointerC=%d\n", pointerA, pointerB, pointerC);
      if (verbose) $display("mm.start ra=%d ca=%d rb=%d cb=%d dotprodCount=%d", numRowsA, numColumnsA, numRowsB, numColumnsB, dotprodCount);
      if (verbose) $display($format(fshow("mm.start ")+fshow(xycfg)));
      xypipeifc.start(xycfg);
   endmethod
   method ActionValue#(Bool) finish();
      $display("mm.finish()");
      doneFifo.deq();
      return True;
   endmethod

   interface ObjectWriteClient dmaClient = sinkC.dmaClient;
endmodule

interface DramMatrixMultiply#(numeric type n, numeric type dmasz);
   interface Vector#(TAdd#(N,1), ObjectReadClient#(dmasz)) readClients;
   interface Vector#(1, ObjectWriteClient#(dmasz)) writeClients;
   method Action start(ObjectPointer pointerA, UInt#(ObjectOffsetSize) numRowsA, UInt#(ObjectOffsetSize) numColumnsA,
		       ObjectPointer pointerB, UInt#(ObjectOffsetSize) numRowsB, UInt#(ObjectOffsetSize) numColumnsB,
		       ObjectPointer pointerC);
   method ActionValue#(Bool) finish();
   method Bit#(32) dbg();
endinterface

(* synthesize *)
module [Module] mkDramMatrixMultiply(DramMatrixMultiply#(N,TMul#(N,32)));
   Vector#(TAdd#(N,1), DmaVectorSource#(DmaSz, Vector#(N,Float))) vfsources <- replicateM(mkDmaVectorSource());
   Vector#(1, VectorSource#(DmaSz, Vector#(N,Float))) xvfsources = cons(vfsources[0].vector, nil);
   Vector#(N, VectorSource#(DmaSz, Vector#(N,Float))) yvfsources = takeAt(1, map(dmaVectorSourceVector, vfsources));
   DmaMatrixMultiplyIfc#(ObjectOffsetSize,DmaSz) dmaMMF <- mkDmaMatrixMultiply(xvfsources, yvfsources, mkDmaVectorSink);
   interface Vector readClients = map(getSourceReadClient, vfsources);
   interface Vector writeClients = cons(dmaMMF.dmaClient, nil);
   method start = dmaMMF.start;
   method finish = dmaMMF.finish;
   method Bit#(32) dbg();
      Bit#(32) d = 0;
      d[0] = pack(vfsources[0].vector.pipe.notEmpty());
      d[1] = pack(vfsources[1].vector.pipe.notEmpty());
      return d;
   endmethod
endmodule

interface Mm#(numeric type n);
   interface MmRequest mmRequest;
   interface TimerRequest timerRequest;
   interface Vector#(TAdd#(1,N), ObjectReadClient#(TMul#(32,N))) readClients;
   interface Vector#(1, ObjectWriteClient#(TMul#(32,n))) writeClients;
endinterface

module [Module] mkMm#(MmIndication ind, TimerIndication timerInd)(Mm#(N))
   provisos (Add#(1,a__,N),
	     Add#(N,0,n),
	     Mul#(N,32,DmaSz)
      );

   let n = valueOf(n);

   DramMatrixMultiply#(N, TMul#(N,32)) dmaMMF <- mkDramMatrixMultiply();

   FIFOF#(Bool) busyFifo <- mkFIFOF();
   rule mmfDone;
      $display("mmfDone");
      let d <- dmaMMF.finish();
      busyFifo.deq();
      ind.mmfDone();
   endrule

   FIFOF#(Bool) timerRunning <- mkFIFOF();
   Reg#(Bit#(64)) cycleCount <- mkReg(0);
   Reg#(Bit#(64)) idleCount <- mkReg(0);
   rule countCycles if (timerRunning.notEmpty());
      cycleCount <= cycleCount + 1;
      if (!busyFifo.notEmpty())
	 idleCount <= idleCount + 1;
   endrule

   interface TimerRequest timerRequest;
         method Action startTimer() if (!timerRunning.notEmpty());
	 cycleCount <= 0;
	 idleCount <= 0;
	 timerRunning.enq(True);
      endmethod
      method Action stopTimer();
	 timerRunning.deq();
	 timerInd.elapsedCycles(cycleCount, idleCount);
      endmethod
   endinterface

   interface MmRequest mmRequest;
      method Action mmf(Bit#(32) h1, Bit#(32) r1, Bit#(32) c1,
			Bit#(32) h2, Bit#(32) r2, Bit#(32) c2,
			Bit#(32) h3);
	 dmaMMF.start(h1, unpack(extend(r1)), unpack(extend(c1)),
		      h2, unpack(extend(r2)), unpack(extend(c2)),
		      h3);
	 busyFifo.enq(True);
      endmethod
   endinterface

   interface Vector readClients = dmaMMF.readClients;
   interface Vector writeClients =  dmaMMF.writeClients;

endmodule

// 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 FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import BRAM::*;
import DmaVector::*;
import PortalMemory::*;
import Dma::*;
import FloatingPoint::*;
import Pipe::*;
import FloatOps::*;
import Timer::*;
import RbmTypes::*;

module [Module] mkDotProd#(PipeOut#(Vector#(n,Float)) xpipe, PipeOut#(Vector#(n,Float)) ypipe, UInt#(nwidth) numElts, UInt#(TLog#(n)) label)(PipeOut#(Float))
   provisos (
      Add#(1,a__,n),
      ReducePipe#(n,Float)
      );

   Bool verbose = False;

   PipeOut#(Tuple2#(Vector#(n,Float),Vector#(n,Float))) xypipe <- mkJoin(tuple2, xpipe, ypipe);

   Vector#(n, Server#(Tuple4#(Maybe#(Float), Float, Float, RoundMode), Tuple2#(Float,Exception))) macs <- replicateM(mkFloatMac);
   Vector#(n, FIFO#(Maybe#(Float))) accumFifo <- replicateM(mkFIFO());
   Reg#(UInt#(nwidth)) countInReg <- mkReg(0);
   Reg#(UInt#(nwidth)) countOutReg <- mkReg(0);
   FIFOF#(Vector#(n,Float)) resultFifo <- mkFIFOF();

   rule mul;
      let v = xypipe.first;
      xypipe.deq;
      let x = tpl_1(v);
      let y = tpl_2(v);

      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 Maybe#(Float) accum = tagged Invalid;
	 if (countInReg > 0) begin
	    accum = accumFifo[i].first();
	    accumFifo[i].deq();
	 end
	 if (verbose) $display($format(fshow("label=")+fshow(label)+fshow(" dotprod x=") + fshow(x) + fshow(" y=") + fshow(y) + fshow(" accum=")+fshow(pack(accum))));
         macs[i].request.put(tuple4(accum, x[i], y[i], defaultValue));
	 if (i == 0) begin
	    let c = countInReg + fromInteger(valueOf(n));
	    if (c >= numElts)
	       c = 0;
	    countInReg <= c;
	 end
      end
   endrule

   rule macout;
      Vector#(n,Float) vs = unpack(0);
      let c = countOutReg + fromInteger(valueOf(n));
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 let resp <- macs[i].response.get();
	 vs[i] = tpl_1(resp);
	 if (c < numElts) begin
	    accumFifo[i].enq(tagged Valid tpl_1(resp));
	 end
      end
      if (c >= numElts) begin
	 Vector#(n,Bit#(32)) ivs = unpack(pack(vs));
	 resultFifo.enq(vs);
	 c = 0;
	 if (verbose) $display(fshow("label=")+fshow(label)+fshow(" countOutReg=")+fshow(countOutReg)+fshow(" numElts=")+fshow(numElts)+fshow(" mul=") + fshow(ivs));
      end
      countOutReg <= c;
   endrule

   PipeOut#(Vector#(n,Float)) resultPipe = toPipeOut(resultFifo);
   PipeOut#(Float) dotpipe <- mkReducePipe(mkFloatAddPipe, resultPipe);
   PipeOut#(Float) displaydotpipe = (interface PipeOut#(Float);
      method Float first(); return dotpipe.first(); endmethod
      method Action deq(); $display(fshow("label=")+fshow(label)+fshow(" dotpipe=")+fshow(dotpipe.first())); dotpipe.deq(); endmethod
      method Bool notEmpty(); return dotpipe.notEmpty(); endmethod
      endinterface);
   return dotpipe;
endmodule

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

interface XYRangePipeIfc#(type a);
   interface PipeOut#(Tuple2#(a,a)) pipe;
   method Action start(XYRangeConfig#(a) cfg);
   method Action display();
endinterface

module mkXYRangePipeOut(XYRangePipeIfc#(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);

   interface PipeOut pipe;
      method Tuple2#(a,a) first() if (x < xlimit && y < ylimit);
	 return tuple2(x,y);
      endmethod
      method Action deq if (x < xlimit && y < ylimit);
	 let newx = x;
	 let newy = y+ystep;
	 if (newy >= ylimit && x < xlimit) begin
	    newy = ybase;
	    newx = newx + xstep;
	 end
	 x <= newx;
	 y <= newy;
      endmethod
      method Bool notEmpty();
	 return (x < xlimit && y < ylimit);
      endmethod
   endinterface
   method Action start(XYRangeConfig#(a) cfg) if (x >= xlimit);
      $display("XYRangePipe x=%d xlimit=%d xstep=%d y=%d ylimit=%d ystep=%d", cfg.xbase, cfg.xlimit, cfg.xstep, cfg.ybase, cfg.ylimit, cfg.ystep);
      x <= cfg.xbase;
      y <= cfg.ybase;
      xbase <= cfg.xbase;
      ybase <= cfg.ybase;
      xstep <= cfg.xstep;
      ystep <= cfg.ystep;
      xlimit <= cfg.xlimit;
      ylimit <= cfg.ylimit;
   endmethod
   method Action display();
      $display("XYRangePipe x=%d xlimit=%d y=%d ylimit=%d xstep=%d ystep=%d", x, xlimit, xstep, y, ylimit, ystep);
   endmethod
endmodule: mkXYRangePipeOut

typedef struct {
   ObjectPointer pointer;
   addrtype base;
   addrtype numRows;
   addrtype numColumns;
} MatrixDescriptor#(type addrtype) deriving (Bits);

// row major layout
interface DmaMatrixMultiplyIfc#(numeric type addrwidth, numeric type dsz);
   interface ObjectWriteClient#(dsz) dmaClient;
   method Action start(ObjectPointer pointerA, UInt#(addrwidth) numRowsA, UInt#(addrwidth) numColumnsA,
		       ObjectPointer pointerB, UInt#(addrwidth) numRowsB, UInt#(addrwidth) numColumnsB,
		       ObjectPointer pointerC);
   method ActionValue#(Bool) finish();
endinterface

typedef enum {
   Idle, Ready, Running, Done
   } MMState deriving (Bits, Eq);

typedef 2 N;

module [Module] mkDmaMatrixMultiply#(Vector#(1, VectorSource#(dsz, Vector#(N, Float))) sourceA,
				     Vector#(N, VectorSource#(dsz, Vector#(N, Float))) sourceB,
				     function Module#(DmaVectorSink#(dsz, Vector#(N, Float))) mkSink(PipeOut#(Vector#(N, Float)) pipe_in)
				     )(DmaMatrixMultiplyIfc#(addrwidth, dsz))
   provisos (Add#(a__,ObjectOffsetSize,addrwidth)
	     , Add#(N,0,n)
	     , FShow#(Float)
	     , Arith#(Float)
	     , Bits#(Vector#(N, Float), dsz));

   let n = valueOf(n);
   Bool verbose = True;

   Reg#(Bool) doneReg <- mkReg(False);
   Reg#(MatrixDescriptor#(UInt#(addrwidth))) descriptorA <- mkReg(unpack(0));
   Reg#(MatrixDescriptor#(UInt#(addrwidth))) descriptorB <- mkReg(unpack(0));
   Reg#(MatrixDescriptor#(UInt#(addrwidth))) descriptorC <- mkReg(unpack(0));
   Reg#(UInt#(addrwidth)) dotprodCount <- mkReg(0);

   Vector#(n, PipeOut#(Vector#(N,Float))) aPipes <- mkForkVector(sourceA[0].pipe);

   function Module#(PipeOut#(Float)) mkFxDotProd(Integer i);
      return mkDotProd(aPipes[i], sourceB[i].pipe, descriptorA.numColumns, fromInteger(i));
   endfunction
   Vector#(n, PipeOut#(Float)) fxdotprods <- genWithM(mkFxDotProd);

   FIFOF#(Vector#(N,Float)) dfifo <- mkFIFOF();
   let sinkC <- mkSink(toPipeOut(dfifo));

   XYRangePipeIfc#(UInt#(addrwidth)) xypipeifc <- mkXYRangePipeOut();

   Vector#(TAdd#(n,1), PipeOut#(Tuple2#(UInt#(addrwidth),UInt#(addrwidth)))) xypipes <- mkForkVector(xypipeifc.pipe);

   Reg#(Bool) running <- mkReg(False);
   FIFOF#(Bool) doneFifo <- mkFIFOF();

   Reg#(UInt#(32)) cycles <- mkReg(0);
   rule countCycles;
      cycles <= cycles+1;
   endrule

   for (Integer i = 0; i < n; i = i + 1) begin
      rule startDotProd;
	  Tuple2#(UInt#(addrwidth),UInt#(addrwidth)) xy = xypipes[i].first;
	  xypipes[i].deq();

	  let row = tpl_1(xy);
	  let col = tpl_2(xy)+fromInteger(i);

	  let startA = row*descriptorA.numColumns; // row major
	  let startB = col*descriptorB.numColumns; // col major layout (pre-transposed)
	  let startC = row*descriptorC.numColumns + col; // row major
	 if (verbose) $display($format(fshow(cycles)+fshow("    startDotProd xy=")+fshow(tuple2(row,col))
				       +fshow(" startA=")+fshow(startA)
				       +fshow(" startB=")+fshow(startB)
				       +fshow(" startC=")+fshow(startC)));

	  if (i == 0) begin
	     sourceA[0].start(descriptorA.pointer, pack(truncate(startA)), pack(truncate(startA + descriptorA.numColumns)));
	     $display($format(fshow(cycles)+fshow("    sourceA[0].start")+fshow(startA)));
	  end
	 sourceB[i].start(descriptorB.pointer, pack(truncate(startB)), pack(truncate(startB + descriptorB.numColumns)));
	 UInt#(TLog#(n)) in = fromInteger(i);
	 $display($format(fshow(cycles)+fshow("    sourceB[")+fshow(in)+fshow("].start")+fshow(startB)));
	  if (i == 0)
	     sinkC.vector.start(descriptorC.pointer, pack(truncate(startC)), pack(truncate(startC + fromInteger(n))));
       endrule
       if (i == 0)
	  rule finishSourceA;
	     $display($format(fshow(cycles)+fshow("    sourceA[0].finish ")));
	     let b <- sourceA[0].finish();
	  endrule
      rule finishSourceB;
	 UInt#(TLog#(n)) in = fromInteger(i);
	 $display($format(fshow(cycles)+fshow("    sourceB[")+fshow(in)+fshow("].finish")));
	 let b <- sourceB[i].finish();
      endrule

   end

   rule dotProdValue;
      Tuple2#(UInt#(addrwidth),UInt#(addrwidth)) xy = xypipes[n].first;
      xypipes[n].deq;
      Vector#(N,Float) vs;
      for (Integer i = 0; i < n; i = i + 1) begin
	 let v = fxdotprods[i].first;
	 fxdotprods[i].deq;
	 let xyi = tuple2(tpl_1(xy), tpl_2(xy)+fromInteger(i));
	 if (verbose) $display($format(fshow(cycles)+fshow("    dotprodvalue xy=")+fshow(xyi)+fshow(" dotprod=")+fshow(v)));
	 vs[i] = v;
      end
      dfifo.enq(vs);
   endrule

   rule sinkDone;
      // each time we write a burst of k values via sinkC
      let b <- sinkC.vector.finish();
      let c = dotprodCount-fromInteger(n);
      $display($format(fshow(cycles)+fshow("    sinkDone c")+fshow(c)));
      dotprodCount <= c;
      if (c == 0) begin
	 running <= False;
	 doneFifo.enq(?);
      end
   endrule

   method Action start(ObjectPointer pointerA, UInt#(addrwidth) numRowsA, UInt#(addrwidth) numColumnsA,
		       ObjectPointer pointerB, UInt#(addrwidth) numRowsB, UInt#(addrwidth) numColumnsB,
		       ObjectPointer pointerC) if (dotprodCount == 0);
      XYRangeConfig#(UInt#(addrwidth)) xycfg = XYRangeConfig {xbase: 0, xlimit: numRowsA, xstep: 1,
							      ybase: 0, ylimit: numRowsB, ystep: fromInteger(n) };
      descriptorA <= MatrixDescriptor { pointer: pointerA, base: 0, numRows: numRowsA, numColumns: numColumnsA};
      descriptorB <= MatrixDescriptor { pointer: pointerB, base: 0, numRows: numRowsB, numColumns: numColumnsB};
      descriptorC <= MatrixDescriptor { pointer: pointerC, base: 0, numRows: numRowsA, numColumns: numRowsB};
      dotprodCount <= numRowsA*numRowsB;

      $display("mm pointerA=%d pointerB=%d pointerC=%d\n", pointerA, pointerB, pointerC);
      if (verbose) $display("mm.start ra=%d ca=%d rb=%d cb=%d dotprodCount=%d", numRowsA, numColumnsA, numRowsB, numColumnsB, dotprodCount);
      if (verbose) $display($format(fshow("mm.start ")+fshow(xycfg)));
      xypipeifc.start(xycfg);
   endmethod
   method ActionValue#(Bool) finish();
      $display("mm.finish()");
      doneFifo.deq();
      return True;
   endmethod

   interface ObjectWriteClient dmaClient = sinkC.dmaClient;
endmodule

interface DramMatrixMultiply#(numeric type n, numeric type dmasz);
   interface Vector#(TAdd#(N,1), ObjectReadClient#(dmasz)) readClients;
   interface Vector#(1, ObjectWriteClient#(dmasz)) writeClients;
   method Action start(ObjectPointer pointerA, UInt#(ObjectOffsetSize) numRowsA, UInt#(ObjectOffsetSize) numColumnsA,
		       ObjectPointer pointerB, UInt#(ObjectOffsetSize) numRowsB, UInt#(ObjectOffsetSize) numColumnsB,
		       ObjectPointer pointerC);
   method ActionValue#(Bool) finish();
   method Bit#(32) dbg();
endinterface

(* synthesize *)
module [Module] mkDramMatrixMultiply(DramMatrixMultiply#(N,TMul#(N,32)));
   Vector#(TAdd#(N,1), DmaVectorSource#(DmaSz, Vector#(N,Float))) vfsources <- replicateM(mkDmaVectorSource());
   Vector#(1, VectorSource#(DmaSz, Vector#(N,Float))) xvfsources = cons(vfsources[0].vector, nil);
   Vector#(N, VectorSource#(DmaSz, Vector#(N,Float))) yvfsources = takeAt(1, map(dmaVectorSourceVector, vfsources));
   DmaMatrixMultiplyIfc#(ObjectOffsetSize,DmaSz) dmaMMF <- mkDmaMatrixMultiply(xvfsources, yvfsources, mkDmaVectorSink);
   interface Vector readClients = map(getSourceReadClient, vfsources);
   interface Vector writeClients = cons(dmaMMF.dmaClient, nil);
   method start = dmaMMF.start;
   method finish = dmaMMF.finish;
   method Bit#(32) dbg();
      Bit#(32) d = 0;
      d[0] = pack(vfsources[0].vector.pipe.notEmpty());
      d[1] = pack(vfsources[1].vector.pipe.notEmpty());
      return d;
   endmethod
endmodule

interface Mm#(numeric type n);
   interface MmRequest mmRequest;
   interface TimerRequest timerRequest;
   interface Vector#(TAdd#(1,N), ObjectReadClient#(TMul#(32,N))) readClients;
   interface Vector#(1, ObjectWriteClient#(TMul#(32,n))) writeClients;
endinterface

module [Module] mkMm#(MmIndication ind, TimerIndication timerInd)(Mm#(N))
   provisos (Add#(1,a__,N),
	     Add#(N,0,n),
	     Mul#(N,32,DmaSz)
      );

   let n = valueOf(n);

   DramMatrixMultiply#(N, TMul#(N,32)) dmaMMF <- mkDramMatrixMultiply();

   FIFOF#(Bool) busyFifo <- mkFIFOF();
   rule mmfDone;
      $display("mmfDone");
      let d <- dmaMMF.finish();
      busyFifo.deq();
      ind.mmfDone();
   endrule

   FIFOF#(Bool) timerRunning <- mkFIFOF();
   Reg#(Bit#(64)) cycleCount <- mkReg(0);
   Reg#(Bit#(64)) idleCount <- mkReg(0);
   rule countCycles if (timerRunning.notEmpty());
      cycleCount <= cycleCount + 1;
      if (!busyFifo.notEmpty())
	 idleCount <= idleCount + 1;
   endrule

   interface TimerRequest timerRequest;
         method Action startTimer() if (!timerRunning.notEmpty());
	 cycleCount <= 0;
	 idleCount <= 0;
	 timerRunning.enq(True);
      endmethod
      method Action stopTimer();
	 timerRunning.deq();
	 timerInd.elapsedCycles(cycleCount, idleCount);
      endmethod
   endinterface

   interface MmRequest mmRequest;
      method Action mmf(Bit#(32) h1, Bit#(32) r1, Bit#(32) c1,
			Bit#(32) h2, Bit#(32) r2, Bit#(32) c2,
			Bit#(32) h3);
	 dmaMMF.start(h1, unpack(extend(r1)), unpack(extend(c1)),
		      h2, unpack(extend(r2)), unpack(extend(c2)),
		      h3);
	 busyFifo.enq(True);
      endmethod
   endinterface

   interface Vector readClients = dmaMMF.readClients;
   interface Vector writeClients =  dmaMMF.writeClients;

endmodule


first step towards running conv on every element in row

// Copyright (c) 2015 Connectal Project

// 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 GetPut::*;
import Clocks::*;
import Vector::*;
import BuildVector::*;
import FIFO::*;
import FIFOF::*;
import Gearbox::*;
import GetPut::*;
import Connectable::*;
import BRAM::*;
import BRAMFIFO::*;
import DefaultValue::*;

import MemTypes::*;
import Pipe::*;
import HostInterface::*;
import Dsp48E1::*;
import InnerProdInterface::*;
import ConnectalBramFifo::*;

`ifdef NUMBER_OF_TILES
typedef `NUMBER_OF_TILES NumTiles;
`else
typedef 256 NumTiles;
`endif
`ifdef TILES_PER_MACRO
typedef `TILES_PER_MACRO NumTilesPerMacro;
`else
typedef 16 NumTilesPerMacro;
`endif
typedef TAdd#(1,TLog#(NumTiles)) TileNumSize;
typedef struct {
   Bit#(TileNumSize) tile;
   Int#(16) v;
   Bool first;
   Bool last;
   Bool update;
   } InnerProdParam deriving (Bits);
typedef Tuple2#(Int#(16),Int#(16))           InnerProdResponse;
typedef TDiv#(NumTiles,NumTilesPerMacro) NumMacroTiles;

interface InnerProdSynth;
   interface InnerProdRequest request;
   interface Get#(InnerProdResponse)   response;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClients;
endinterface

interface InnerProd;
   interface InnerProdRequest request;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClients;
endinterface

interface InnerProdTile;
   interface PipeIn#(InnerProdParam) request;
   interface PipeOut#(InnerProdResponse) response;
   interface PipeOut#(InnerProdParam) requestNext;
   interface PipeIn#(InnerProdResponse) responseNext;
   interface Reset                  resetOut;
endinterface

function PipeOut#(InnerProdResponse) getInnerProdResponsePipe(InnerProdTile tile); return tile.response; endfunction

(* synthesize *)
module mkTileRequestFifo#(Clock derivedClock, Reset derivedReset, Clock defaultClock, Reset defaultReset)(FIFOF#(InnerProdParam));
   let dualClockFifo <- mkDualClockBramFIFOF(defaultClock, defaultReset, derivedClock, derivedReset);
   return dualClockFifo;
endmodule

(* synthesize *)
module mkTileResponseFifo#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(InnerProdResponse));
   let dualClockFifo <- mkDualClockBramFIFOF(srcClock, srcReset, dstClock, dstReset, clocked_by srcClock, reset_by srcReset);
   return dualClockFifo;
endmodule

(* synthesize *)
module mkInnerProdTile#(Bit#(TileNumSize) tile, Bool hasNext)(InnerProdTile);

   let clock <- exposeCurrentClock;
   let reset <- exposeCurrentReset;
   let ipReset <- mkAsyncReset(10, reset, clock);
   let dsp <- mkDsp48E1(reset_by ipReset);

   //FIFOF#(InnerProdParam) reqFifo <- mkDualClockBramFIFOF(clock, ipReset, clock, ipReset);
   FIFOF#(InnerProdParam) reqFifo <- mkFIFOF();
   FIFOF#(InnerProdParam) req1Fifo <- mkFIFOF();
   BRAM1Port#(Bit#(10),Int#(16)) kernelBram <- mkBRAM1Server(defaultValue);
   Reg#(Bit#(10)) addrReg <- mkReg(0);
   FIFOF#(InnerProdResponse) responseFifo <- mkFIFOF(); //mkDualClockBramFIFOF(clock, ipReset, clock, ipReset);

   Reg#(InnerProdParam)     nextReqReg <- mkReg(unpack(0), reset_by ipReset);
   FIFOF#(InnerProdResponse) nextRespFifo <- mkFIFOF1(reset_by ipReset);

   Reg#(InnerProdParam) requestPipeReg <- mkReg(unpack(0), reset_by ipReset);
   Reg#(Bool)        isMyRequestReg <- mkReg(False, reset_by ipReset);
   Reg#(Bool)        validReg       <- mkReg(False, reset_by ipReset);

   rule request_rule;
      if (reqFifo.notEmpty()) begin
	 let req <- toGet(reqFifo).get();

	 //$display("tile %d req.tile=%d update=%d", tile, req.tile, req.update);
	 let addr = addrReg + 1;
	 if (req.update && req.tile == tile) begin
	    $display("tile %d: writing kernel addr=%d value=%d", tile, addrReg, req.v);
	    kernelBram.portA.request.put(BRAMRequest {write:True, responseOnWrite:False, address: addrReg, datain: req.v});
	 end
	 else if (req.tile == tile || req.tile == fromInteger(valueOf(NumTiles))) begin
	    kernelBram.portA.request.put(BRAMRequest {write:False, responseOnWrite:False, address: addrReg, datain: 0});
	    req1Fifo.enq(req);
	 end

	 if (req.tile != tile || req.tile == fromInteger(valueOf(NumTiles))) begin
	    nextReqReg <= req;
	    validReg <= True;
	 end

	 if (req.last)
	    addr = 0;
	 addrReg <= addr;
      end
      else begin
	 validReg <= False;
      end
   endrule

   rule process_rule;
      let req <- toGet(req1Fifo).get();
      let b <- kernelBram.portA.response.get();
      $display("tile %d: inner prod a=%h b=%h last=%d", tile, req.v, b, req.last);

      dsp.a(extend(pack(req.v)));
      dsp.b(extend(pack(b)));
      dsp.c(0);
      dsp.d(0);
      let opmode = 7'h25;
      if (req.first) opmode = 7'h05;
      dsp.opmode(opmode);
      dsp.inmode(0);
      dsp.alumode(0);
      dsp.last(pack(req.last));
   endrule

   rule responseRule;
      InnerProdResponse v = unpack(0);
      let valid = False;
      if (dsp.notEmpty() && !responseFifo.notFull()) begin
	 $display("tile %d dropping dsp.p due to full responseFifo", tile);
      end
      if (dsp.notEmpty()) begin
	 $display("InnerProdTile tile=%d dsp.p %h", tile, dsp.p());
	 Int#(16) uintTile = extend(unpack(tile));
	 v = tuple2(uintTile, unpack(dsp.p()[23:8]));
	 valid = True;
      end
      else if (nextRespFifo.notEmpty()) begin
	 v <- toGet(nextRespFifo).get();
	 $display("tile %d: nextResp tile=%d v=%h", tile, tpl_1(v), tpl_2(v));
	 valid = True;
      end
      if (valid)
	 responseFifo.enq(v);
   endrule

   interface PipeIn request = toPipeIn(reqFifo);
   interface PipeOut response = toPipeOut(responseFifo);
   interface PipeOut requestNext;
      method InnerProdParam first() if (validReg);
	 return nextReqReg;
      endmethod
      method Bool notEmpty();
	 return validReg;
      endmethod
   endinterface
   interface PipeIn responseNext = toPipeIn(nextRespFifo);
   interface ResetOut resetOut = ipReset;
endmodule

interface ReqPipes#(numeric type numPipes, numeric type typeNumSize, type reqType);
   interface PipeIn#(reqType)                     inPipe;
   interface Vector#(numPipes, PipeOut#(reqType)) outPipes;
endinterface

module mkRequestPipes(ReqPipes#(numPipes,TileNumSize,reqType))
   provisos (Bits#(reqType, a__));
   FIFOF#(reqType) syncIn <- mkFIFOF();

   PipeOut#(reqType)                   reqPipe = toPipeOut(syncIn);
   Vector#(numPipes,PipeOut#(reqType)) opipes <- mkForkVector(reqPipe);

   interface PipeIn inPipe = toPipeIn(syncIn);
   interface Vector outPipes = opipes;
endmodule

interface ResponsePipes#(numeric type numPipes);
   interface Vector#(numPipes,PipeIn#(InnerProdResponse)) inPipes;
   interface PipeOut#(InnerProdResponse)                  outPipe;
endinterface

module mkResponsePipes(ResponsePipes#(numPipes))
   provisos (FunnelPipesPipelined#(1, numPipes, InnerProdResponse, 2));

   Vector#(numPipes, FIFOF#(InnerProdResponse))                fifos <- replicateM(mkFIFOF);
   Vector#(numPipes, PipeOut#(InnerProdResponse))      responsePipes = map(toPipeOut, fifos);
   FunnelPipe#(1,numPipes,InnerProdResponse,2) funnelResponsePipe <- mkFunnelPipesPipelined(responsePipes);

   interface Vector  inPipes = map(toPipeIn, fifos);
   interface PipeOut outPipe = funnelResponsePipe[0];
endmodule

interface MacroTile;
   interface PipeIn#(InnerProdParam) inPipe;
   interface PipeOut#(InnerProdResponse) outPipe;
   interface Reset                  resetOut;
endinterface

(* synthesize *)
module mkMacroTile#(Bit#(TileNumSize) mt)(MacroTile);
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   let trpReset <- mkAsyncReset(10, reset, clock);
   let topReset <- mkAsyncReset(10, reset, clock);

   Reset tileReset <- mkAsyncReset(10, reset, clock);

   PipeIn#(InnerProdParam) tileRequestPipe;
   PipeOut#(InnerProdResponse) tileResponsePipe;
   PipeOut#(InnerProdParam) tileRequestNextPipe;
   PipeIn#(InnerProdResponse) tileResponseNextPipe;

   for (Integer t = 0; t < valueOf(NumTilesPerMacro); t = t + 1) begin
      let hasNext = t != (valueOf(NumTilesPerMacro) - 1);
      let tile <- mkInnerProdTile(mt * fromInteger(valueOf(NumTilesPerMacro)) + fromInteger(t), hasNext, reset_by tileReset);
      if (t == 0) begin
	 tileRequestPipe = tile.request;
	 tileResponsePipe = tile.response;
      end
      else begin
	 mkConnection(tileRequestNextPipe, tile.request, reset_by trpReset);
	 mkConnection(tile.response, tileResponseNextPipe, reset_by trpReset);
      end
      tileReset = tile.resetOut;
      tileRequestNextPipe = tile.requestNext;
      tileResponseNextPipe = tile.responseNext;
   end

   interface PipeIn inPipe = tileRequestPipe;
   interface PipeOut outPipe = tileResponsePipe;
   interface Reset resetOut = tileReset;
endmodule

interface InnerProdDriver;
   interface Reg#(SGLId)                 readPointer;
   interface Put#(RangeConfig#(Bit#(16))) rowRequest;
   interface Put#(XYRangeConfig#(Bit#(10))) convRequest; // for testing
   interface BRAMClient#(Bit#(10),Int#(16)) bramReadClient;
   interface BRAMClient#(Bit#(10),Int#(16)) bramWriteClient;
   interface PipeOut#(InnerProdParam) innerProdRequest;
   interface MemReadClient#(DataBusWidth) readClient;
endinterface

(* synthesize *)
module mkIPDriver(InnerProdDriver);
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   FIFOF#(BRAMRequest#(Bit#(10),Int#(16))) bramRequestFifo <- mkFIFOF();
   FIFOF#(BRAMRequest#(Bit#(10),Int#(16))) bramWriteFifo <- mkFIFOF();
   FIFOF#(Int#(16)) bramResponseFifo <- mkFIFOF();
   FIFOF#(InnerProdParam) innerProdRequestFifo <- mkFIFOF();
   FIFOF#(Tuple4#(Bool,Bool,Bit#(10),Bit#(10))) lastFifo <- mkFIFOF();

   FIFOF#(RangeConfig#(Bit#(16))) rowRequestFifo <- mkFIFOF();
   FIFOF#(XYRangeConfig#(Bit#(10))) convRequestFifo <- mkFIFOF();
   XYRangePipeIfc#(Bit#(10)) convRangePipe <- mkXYRangePipeOut();
   XYRangePipeIfc#(Bit#(10)) ipRangePipe <- mkXYRangePipeOut();
   FIFOF#(XYRangeConfig#(Bit#(10))) rpMutex <- mkFIFOF1();

   RangePipeIfc#(Bit#(16)) rowRangePipe <- mkRangePipeOut();
   RangePipeIfc#(Bit#(16)) colRangePipe <- mkRangePipeOut();
   RangePipeIfc#(Bit#(16)) bramWriteRangePipe <- mkRangePipeOut();

   Reg#(SGLId)           readPointerReg <- mkReg(0);
   Reg#(Bit#(10))             writeAddr <- mkReg(0);
   Reg#(Bit#(3))                   tag <- mkReg(0);

   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFO#(MemData#(DataBusWidth)) dataFifo <- mkFIFO();
   Gearbox#(4, 1, Bit#(16))  dataGearbox <- mkNto1Gearbox(clock, reset, clock, reset);

   // fixme
   let kernelHeight = 4;
   let kernelColAddrBits = 2;
   Reg#(Bit#(16))           rowLenBytes <- mkReg(16);
   Reg#(Bit#(BurstLenSize)) burstLenBytes <- mkReg(16);
   Reg#(Bool)         enoughRowsCachedReg <- mkReg(False);

   rule startRowsRule;
      let req <- toGet(rowRequestFifo).get();
      $display("startRowsRule: xbase=%d xlimit=%d xstep=%d", req.xbase, req.xlimit, req.xstep);
      rowRangePipe.start(req);
   endrule
   rule startReadRowRule;
      let rowStartBytes <- toGet(rowRangePipe.pipe).get();
      $display("startReadRowRule: rowStartBytes=%d rowLenBytes=%d burstLenBytes=%d", rowStartBytes, rowLenBytes, burstLenBytes);
      // start iterator of DRAM read addresses
      colRangePipe.start(RangeConfig {xbase: rowStartBytes, xlimit: rowStartBytes+rowLenBytes, xstep: extend(burstLenBytes) });
      // start iterator of BRAM write addresses
      bramWriteRangePipe.start(RangeConfig {xbase: rowStartBytes>>1, xlimit: (rowStartBytes+rowLenBytes)>>1, xstep: 1 });
   endrule

   rule startReadReqRule;
      let colOffsetBytes <- toGet(colRangePipe.pipe).get();
      $display("startReadReqRule: colOffsetBytes=%d", colOffsetBytes);
      readReqFifo.enq(MemRequest { sglId: readPointerReg, offset: extend(colOffsetBytes), burstLen: burstLenBytes, tag: extend(tag) });

      tag <= tag + 1;
   endrule
   rule dataGearboxRule;
      let d <- toGet(dataFifo).get();
      dataGearbox.enq(unpack(d.data));
   endrule
   rule bramWriteRule;
      let bramAddr <- toGet(bramWriteRangePipe.pipe).get();
      Vector#(1,Bit#(16)) v = dataGearbox.first(); dataGearbox.deq();
      $display("bramWriteRule bramAddr=%d row=%d col=%d v=%h", bramAddr, bramAddr>>4, bramAddr & 'hf, v);
      bramWriteFifo.enq(BRAMRequest{write: True, responseOnWrite: False, address: truncate(bramAddr), datain: unpack(v[0])});
      if (bramWriteRangePipe.isLast()) begin
	 // now OK to start convolutions for rows up to here
	 let rowNumber = bramAddr >> 4; // imageColAddrBits
	 let enoughRowsCached = enoughRowsCachedReg;
	 if (!enoughRowsCached) begin
	    enoughRowsCached = rowNumber >= (kernelHeight-1);
	    enoughRowsCachedReg <= enoughRowsCached;
	 end
	 $display("bramWriteRule: islast rowNumber=%d bramAddr=%d kernelHeight=%d enoughRowsCached=%d", rowNumber, bramAddr, kernelHeight, enoughRowsCached);
	 if (enoughRowsCached) begin
	    convRangePipe.start(XYRangeConfig {
					       xbase: truncate(rowNumber), xlimit: truncate(rowNumber+1), xstep: 1,
					       ybase: 0, ylimit: 1, ystep: 1
					       });
	 end
      end
   endrule

   rule convRowRule;
      match { .rowNumber, .colNumber } <- toGet(convRangePipe.pipe).get();
      let req = XYRangeConfig {
			       xbase: truncate(rowNumber-kernelHeight+1), xlimit: truncate(rowNumber+1), xstep: 1,
			       ybase: colNumber, ylimit: colNumber+kernelHeight, ystep: 1
			       };
      ipRangePipe.start(req);
      $display("range startRule row=%d col=%d", rowNumber, colNumber);
      rpMutex.enq(req);
   endrule

   rule issueBramReadRequest;
      match { .x, .y } <- toGet(ipRangePipe.pipe).get();
      // fixme: placeholder address computation
      let addr = (x << kernelColAddrBits) | y;
      bramRequestFifo.enq(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: 0});
      lastFifo.enq(tuple4(ipRangePipe.isFirst(), ipRangePipe.isLast(), x, y));
      if (ipRangePipe.isLast && !rpMutex.notEmpty()) begin
	 $display("range finished but rpMutex empty");
      end
      if (ipRangePipe.isLast && rpMutex.notEmpty()) begin
	 let req <- toGet(rpMutex).get();
	 $display("range finished x=%d y=%d", req.xbase, req.ybase);
      end
      $display("issueBramReadRequest x=%d y=%d first=%d last=%d", x, y, ipRangePipe.isFirst(), ipRangePipe.isLast());
   endrule
   rule issueInnerProdRequest;
      let v <- toGet(bramResponseFifo).get();
      match { .first, .last, .x, .y } <- toGet(lastFifo).get();
      let allTiles = fromInteger(valueOf(NumTiles));
      $display("issueInnerProdRequest v=%d x=%d y=%d first=%d last=%d", v, x, y, first, last);
      if (innerProdRequestFifo.notFull())
      innerProdRequestFifo.enq(InnerProdParam { tile: allTiles, v: v, first: first, last: last, update: False });
      else
	 $display("innerProdRequestFifo full");
   endrule

   interface rowRequest = toPut(rowRequestFifo);
   interface convRequest = toPut(convRequestFifo);
   interface innerProdRequest = toPipeOut(innerProdRequestFifo);
   interface BRAMClient bramReadClient;
      interface request = toGet(bramRequestFifo);
      interface response = toPut(bramResponseFifo);
   endinterface
   interface BRAMClient bramWriteClient;
      interface request = toGet(bramWriteFifo);
      interface Put response;
	 method Action put(Int#(16) v);
	    $display("bramWriteClient.response.put should never be called. v=%h", v);
	 endmethod
      endinterface
   endinterface
   interface MemReadClient readClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData = toPut(dataFifo);
   endinterface
endmodule

(* synthesize *)
module mkRequestPipesSynth(ReqPipes#(NumMacroTiles,TileNumSize,InnerProdParam));
   let rp <- mkRequestPipes();
   return rp;
endmodule

(* synthesize *)
module mkResponsePipesSynth(ResponsePipes#(NumMacroTiles));
   let op <- mkResponsePipes();
   return op;
endmodule

(* synthesize *)
module mkInnerProdSynth#(Clock derivedClock)(InnerProdSynth);
   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;

   let derivedReset <- mkAsyncReset(10, defaultReset, derivedClock);

   let optionalReset = derivedReset; // noReset

   BRAM2Port#(Bit#(10),Int#(16)) lineBuffer <- mkBRAM2Server(defaultValue);

   FIFOF#(InnerProdParam) inputFifo <- mkDualClockBramFIFOF(defaultClock, defaultReset, derivedClock, derivedReset);
   FIFOF#(InnerProdResponse) bramFifo <- mkTileResponseFifo(derivedClock, derivedReset, defaultClock, defaultReset);

   Reg#(Bit#(32)) cycles <- mkReg(0, clocked_by derivedClock, reset_by derivedReset);
   rule cyclesRule;
      cycles <= cycles+1;
   endrule

   let rpReset <- mkAsyncReset(10, defaultReset, derivedClock);
   let opReset <- mkAsyncReset(10, defaultReset, derivedClock);
   ReqPipes#(NumMacroTiles,TileNumSize,InnerProdParam) rp <- mkRequestPipesSynth(clocked_by derivedClock, reset_by rpReset);
   ResponsePipes#(NumMacroTiles) op <- mkResponsePipesSynth(clocked_by derivedClock, reset_by opReset);

   Reset mtReset <- mkAsyncReset(10, derivedReset, derivedClock);
   for (Integer mt = 0; mt < valueOf(NumMacroTiles); mt = mt + 1) begin
      let macroTile <- mkMacroTile(fromInteger(mt), clocked_by derivedClock, reset_by mtReset);
      mtReset = macroTile.resetOut;
      mkConnection(rp.outPipes[mt], macroTile.inPipe, clocked_by derivedClock, reset_by mtReset);
      mkConnection(macroTile.outPipe, op.inPipes[mt], clocked_by derivedClock, reset_by mtReset);
   end

   mkConnection(toPipeOut(inputFifo), rp.inPipe, clocked_by derivedClock, reset_by derivedReset);
   mkConnection(op.outPipe, toPipeIn(bramFifo), clocked_by derivedClock, reset_by derivedReset);

   Reg#(Bit#(TileNumSize)) tReg <- mkReg(0);
   Reg#(Bit#(TileNumSize)) mReg <- mkReg(0);
   Wire#(Bool) bWire <- mkDWire(False);
   rule foo if (bWire);
      $display("m=%d t=%d", mReg, tReg);
   endrule

   let ipDriver <- mkIPDriver();
   mkConnection(ipDriver.bramReadClient, lineBuffer.portB);
   mkConnection(ipDriver.bramWriteClient, lineBuffer.portA);
   mkConnection(ipDriver.innerProdRequest, toPipeIn(inputFifo));

   interface InnerProdRequest request;
      method Action write(Bit#(16) addr, Bit#(16) val);
	 lineBuffer.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: truncate(addr), datain: unpack(val)});
      endmethod
      method Action innerProd(Bit#(16) tile, Bit#(16) a, Bool first, Bool last, Bool update);
	 Bit#(TileNumSize) t = truncate(tile);
	 Bit#(TileNumSize) m = truncate(tile >> valueOf(TLog#(NumTilesPerMacro)));
	 tReg <= t;
	 mReg <= m;
	 bWire <= True;
	 $display("request.innerProd m=%d t=%d a=%h first=%d last=%d", m, t, a, first, last);
         // broadcast to all tiles
	 inputFifo.enq(InnerProdParam { tile: t, v: unpack(a), first: first, last: last, update: update});
      endmethod
      method Action startIndividualConv(Bit#(16) xbase, Bit#(16) xlimit, Bit#(16) ybase, Bit#(16) ylimit);
	 ipDriver.convRequest.put(XYRangeConfig { xbase: truncate(xbase), xlimit: truncate(xlimit), xstep: 1,
						 ybase: truncate(ybase), ylimit: truncate(ylimit), ystep: 1 });
      endmethod
      method Action startConv(Bit#(32) ptr, Bit#(16) xbase, Bit#(16) xlimit, Bit#(16) ybase, Bit#(16) ylimit);
	 ipDriver.readPointer <= truncate(ptr);
      // fixme column bytes
	 ipDriver.rowRequest.put(RangeConfig { xbase: truncate(xbase), xlimit: truncate(xlimit)<<4, xstep: 1<<4 });
      endmethod
      method Action finish();
	 $dumpflush();
	 $finish();
      endmethod
   endinterface
   interface Get response = toGet(bramFifo);
   interface Vector readClients = vec(ipDriver.readClient);
endmodule

module mkInnerProd#(
`ifdef IMPORT_HOSTIF
		    HostInterface host,
`endif
		    InnerProdIndication ind)(InnerProd);

   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;
`ifdef IMPORT_HOSTIF
   let derivedClock = host.derivedClock;
`else
   let derivedClock = defaultClock;
`endif

   let ip <- mkInnerProdSynth(derivedClock);
   rule indRule;
      match { .t, .v } <- ip.response.get();
      $display("indRule tile=%d v=%h", t, v);
      ind.innerProd(pack(t), pack(v));
   endrule

   interface InnerProdRequest request = ip.request;
   interface Vector       readClients = ip.readClients;
endmodule

// Copyright (c) 2015 Connectal Project

// 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 GetPut::*;
import Clocks::*;
import Vector::*;
import BuildVector::*;
import FIFO::*;
import FIFOF::*;
import Gearbox::*;
import GetPut::*;
import Connectable::*;
import BRAM::*;
import BRAMFIFO::*;
import DefaultValue::*;

import MemTypes::*;
import Pipe::*;
import HostInterface::*;
import Dsp48E1::*;
import InnerProdInterface::*;
import ConnectalBramFifo::*;

`ifdef NUMBER_OF_TILES
typedef `NUMBER_OF_TILES NumTiles;
`else
typedef 256 NumTiles;
`endif
`ifdef TILES_PER_MACRO
typedef `TILES_PER_MACRO NumTilesPerMacro;
`else
typedef 16 NumTilesPerMacro;
`endif
typedef TAdd#(1,TLog#(NumTiles)) TileNumSize;
typedef struct {
   Bit#(TileNumSize) tile;
   Int#(16) v;
   Bool first;
   Bool last;
   Bool update;
   } InnerProdParam deriving (Bits);
typedef Tuple2#(Int#(16),Int#(16))           InnerProdResponse;
typedef TDiv#(NumTiles,NumTilesPerMacro) NumMacroTiles;

interface InnerProdSynth;
   interface InnerProdRequest request;
   interface Get#(InnerProdResponse)   response;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClients;
endinterface

interface InnerProd;
   interface InnerProdRequest request;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClients;
endinterface

interface InnerProdTile;
   interface PipeIn#(InnerProdParam) request;
   interface PipeOut#(InnerProdResponse) response;
   interface PipeOut#(InnerProdParam) requestNext;
   interface PipeIn#(InnerProdResponse) responseNext;
   interface Reset                  resetOut;
endinterface

function PipeOut#(InnerProdResponse) getInnerProdResponsePipe(InnerProdTile tile); return tile.response; endfunction

(* synthesize *)
module mkTileRequestFifo#(Clock derivedClock, Reset derivedReset, Clock defaultClock, Reset defaultReset)(FIFOF#(InnerProdParam));
   let dualClockFifo <- mkDualClockBramFIFOF(defaultClock, defaultReset, derivedClock, derivedReset);
   return dualClockFifo;
endmodule

(* synthesize *)
module mkTileResponseFifo#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(InnerProdResponse));
   let dualClockFifo <- mkDualClockBramFIFOF(srcClock, srcReset, dstClock, dstReset, clocked_by srcClock, reset_by srcReset);
   return dualClockFifo;
endmodule

(* synthesize *)
module mkInnerProdTile#(Bit#(TileNumSize) tile, Bool hasNext)(InnerProdTile);

   let clock <- exposeCurrentClock;
   let reset <- exposeCurrentReset;
   let ipReset <- mkAsyncReset(10, reset, clock);
   let dsp <- mkDsp48E1(reset_by ipReset);

   //FIFOF#(InnerProdParam) reqFifo <- mkDualClockBramFIFOF(clock, ipReset, clock, ipReset);
   FIFOF#(InnerProdParam) reqFifo <- mkFIFOF();
   FIFOF#(InnerProdParam) req1Fifo <- mkFIFOF();
   BRAM1Port#(Bit#(10),Int#(16)) kernelBram <- mkBRAM1Server(defaultValue);
   Reg#(Bit#(10)) addrReg <- mkReg(0);
   FIFOF#(InnerProdResponse) responseFifo <- mkFIFOF(); //mkDualClockBramFIFOF(clock, ipReset, clock, ipReset);

   Reg#(InnerProdParam)     nextReqReg <- mkReg(unpack(0), reset_by ipReset);
   FIFOF#(InnerProdResponse) nextRespFifo <- mkFIFOF1(reset_by ipReset);

   Reg#(InnerProdParam) requestPipeReg <- mkReg(unpack(0), reset_by ipReset);
   Reg#(Bool)        isMyRequestReg <- mkReg(False, reset_by ipReset);
   Reg#(Bool)        validReg       <- mkReg(False, reset_by ipReset);

   rule request_rule;
      if (reqFifo.notEmpty()) begin
	 let req <- toGet(reqFifo).get();

	 //$display("tile %d req.tile=%d update=%d", tile, req.tile, req.update);
	 let addr = addrReg + 1;
	 if (req.update && req.tile == tile) begin
	    $display("tile %d: writing kernel addr=%d value=%d", tile, addrReg, req.v);
	    kernelBram.portA.request.put(BRAMRequest {write:True, responseOnWrite:False, address: addrReg, datain: req.v});
	 end
	 else if (req.tile == tile || req.tile == fromInteger(valueOf(NumTiles))) begin
	    kernelBram.portA.request.put(BRAMRequest {write:False, responseOnWrite:False, address: addrReg, datain: 0});
	    req1Fifo.enq(req);
	 end

	 if (req.tile != tile || req.tile == fromInteger(valueOf(NumTiles))) begin
	    nextReqReg <= req;
	    validReg <= True;
	 end

	 if (req.last)
	    addr = 0;
	 addrReg <= addr;
      end
      else begin
	 validReg <= False;
      end
   endrule

   rule process_rule;
      let req <- toGet(req1Fifo).get();
      let b <- kernelBram.portA.response.get();
      $display("tile %d: inner prod a=%h b=%h last=%d", tile, req.v, b, req.last);

      dsp.a(extend(pack(req.v)));
      dsp.b(extend(pack(b)));
      dsp.c(0);
      dsp.d(0);
      let opmode = 7'h25;
      if (req.first) opmode = 7'h05;
      dsp.opmode(opmode);
      dsp.inmode(0);
      dsp.alumode(0);
      dsp.last(pack(req.last));
   endrule

   rule responseRule;
      InnerProdResponse v = unpack(0);
      let valid = False;
      if (dsp.notEmpty() && !responseFifo.notFull()) begin
	 $display("tile %d dropping dsp.p due to full responseFifo", tile);
      end
      if (dsp.notEmpty()) begin
	 $display("InnerProdTile tile=%d dsp.p %h", tile, dsp.p());
	 Int#(16) uintTile = extend(unpack(tile));
	 v = tuple2(uintTile, unpack(dsp.p()[23:8]));
	 valid = True;
      end
      else if (nextRespFifo.notEmpty()) begin
	 v <- toGet(nextRespFifo).get();
	 $display("tile %d: nextResp tile=%d v=%h", tile, tpl_1(v), tpl_2(v));
	 valid = True;
      end
      if (valid)
	 responseFifo.enq(v);
   endrule

   interface PipeIn request = toPipeIn(reqFifo);
   interface PipeOut response = toPipeOut(responseFifo);
   interface PipeOut requestNext;
      method InnerProdParam first() if (validReg);
	 return nextReqReg;
      endmethod
      method Bool notEmpty();
	 return validReg;
      endmethod
   endinterface
   interface PipeIn responseNext = toPipeIn(nextRespFifo);
   interface ResetOut resetOut = ipReset;
endmodule

interface ReqPipes#(numeric type numPipes, numeric type typeNumSize, type reqType);
   interface PipeIn#(reqType)                     inPipe;
   interface Vector#(numPipes, PipeOut#(reqType)) outPipes;
endinterface

module mkRequestPipes(ReqPipes#(numPipes,TileNumSize,reqType))
   provisos (Bits#(reqType, a__));
   FIFOF#(reqType) syncIn <- mkFIFOF();

   PipeOut#(reqType)                   reqPipe = toPipeOut(syncIn);
   Vector#(numPipes,PipeOut#(reqType)) opipes <- mkForkVector(reqPipe);

   interface PipeIn inPipe = toPipeIn(syncIn);
   interface Vector outPipes = opipes;
endmodule

interface ResponsePipes#(numeric type numPipes);
   interface Vector#(numPipes,PipeIn#(InnerProdResponse)) inPipes;
   interface PipeOut#(InnerProdResponse)                  outPipe;
endinterface

module mkResponsePipes(ResponsePipes#(numPipes))
   provisos (FunnelPipesPipelined#(1, numPipes, InnerProdResponse, 2));

   Vector#(numPipes, FIFOF#(InnerProdResponse))                fifos <- replicateM(mkFIFOF);
   Vector#(numPipes, PipeOut#(InnerProdResponse))      responsePipes = map(toPipeOut, fifos);
   FunnelPipe#(1,numPipes,InnerProdResponse,2) funnelResponsePipe <- mkFunnelPipesPipelined(responsePipes);

   interface Vector  inPipes = map(toPipeIn, fifos);
   interface PipeOut outPipe = funnelResponsePipe[0];
endmodule

interface MacroTile;
   interface PipeIn#(InnerProdParam) inPipe;
   interface PipeOut#(InnerProdResponse) outPipe;
   interface Reset                  resetOut;
endinterface

(* synthesize *)
module mkMacroTile#(Bit#(TileNumSize) mt)(MacroTile);
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   let trpReset <- mkAsyncReset(10, reset, clock);
   let topReset <- mkAsyncReset(10, reset, clock);

   Reset tileReset <- mkAsyncReset(10, reset, clock);

   PipeIn#(InnerProdParam) tileRequestPipe;
   PipeOut#(InnerProdResponse) tileResponsePipe;
   PipeOut#(InnerProdParam) tileRequestNextPipe;
   PipeIn#(InnerProdResponse) tileResponseNextPipe;

   for (Integer t = 0; t < valueOf(NumTilesPerMacro); t = t + 1) begin
      let hasNext = t != (valueOf(NumTilesPerMacro) - 1);
      let tile <- mkInnerProdTile(mt * fromInteger(valueOf(NumTilesPerMacro)) + fromInteger(t), hasNext, reset_by tileReset);
      if (t == 0) begin
	 tileRequestPipe = tile.request;
	 tileResponsePipe = tile.response;
      end
      else begin
	 mkConnection(tileRequestNextPipe, tile.request, reset_by trpReset);
	 mkConnection(tile.response, tileResponseNextPipe, reset_by trpReset);
      end
      tileReset = tile.resetOut;
      tileRequestNextPipe = tile.requestNext;
      tileResponseNextPipe = tile.responseNext;
   end

   interface PipeIn inPipe = tileRequestPipe;
   interface PipeOut outPipe = tileResponsePipe;
   interface Reset resetOut = tileReset;
endmodule

interface InnerProdDriver;
   interface Reg#(SGLId)                 readPointer;
   interface Put#(RangeConfig#(Bit#(16))) rowRequest;
   interface Put#(XYRangeConfig#(Bit#(10))) convRequest; // for testing
   interface BRAMClient#(Bit#(10),Int#(16)) bramReadClient;
   interface BRAMClient#(Bit#(10),Int#(16)) bramWriteClient;
   interface PipeOut#(InnerProdParam) innerProdRequest;
   interface MemReadClient#(DataBusWidth) readClient;
endinterface

(* synthesize *)
module mkIPDriver(InnerProdDriver);
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   FIFOF#(BRAMRequest#(Bit#(10),Int#(16))) bramRequestFifo <- mkFIFOF();
   FIFOF#(BRAMRequest#(Bit#(10),Int#(16))) bramWriteFifo <- mkFIFOF();
   FIFOF#(Int#(16)) bramResponseFifo <- mkFIFOF();
   FIFOF#(InnerProdParam) innerProdRequestFifo <- mkFIFOF();
   FIFOF#(Tuple2#(Bool,Bool)) lastFifo <- mkFIFOF();

   FIFOF#(RangeConfig#(Bit#(16))) rowRequestFifo <- mkFIFOF();
   FIFOF#(XYRangeConfig#(Bit#(10))) convRequestFifo <- mkFIFOF();
   XYRangePipeIfc#(Bit#(10)) rangePipe <- mkXYRangePipeOut();
   FIFOF#(XYRangeConfig#(Bit#(10))) rpMutex <- mkFIFOF1();

   RangePipeIfc#(Bit#(16)) rowRangePipe <- mkRangePipeOut();
   RangePipeIfc#(Bit#(16)) colRangePipe <- mkRangePipeOut();
   RangePipeIfc#(Bit#(16)) bramWriteRangePipe <- mkRangePipeOut();

   Reg#(SGLId)           readPointerReg <- mkReg(0);
   Reg#(Bit#(10))             writeAddr <- mkReg(0);
   Reg#(Bit#(3))                   tag <- mkReg(0);

   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFO#(MemData#(DataBusWidth)) dataFifo <- mkFIFO();
   Gearbox#(4, 1, Bit#(16))  dataGearbox <- mkNto1Gearbox(clock, reset, clock, reset);

   // fixme
   let kernelHeight = 4;
   let kernelColAddrBits = 2;
   Reg#(Bit#(16))           rowLenBytes <- mkReg(16);
   Reg#(Bit#(BurstLenSize)) burstLenBytes <- mkReg(16);
   Reg#(Bool)         enoughRowsCachedReg <- mkReg(False);

   rule startRowsRule;
      let req <- toGet(rowRequestFifo).get();
      $display("startRowsRule: xbase=%d xlimit=%d xstep=%d", req.xbase, req.xlimit, req.xstep);
      rowRangePipe.start(req);
   endrule
   rule startReadRowRule;
      let rowStartBytes <- toGet(rowRangePipe.pipe).get();
      $display("startReadRowRule: rowStartBytes=%d rowLenBytes=%d burstLenBytes=%d", rowStartBytes, rowLenBytes, burstLenBytes);
      // start iterator of DRAM read addresses
      colRangePipe.start(RangeConfig {xbase: rowStartBytes, xlimit: rowStartBytes+rowLenBytes, xstep: extend(burstLenBytes) });
      // start iterator of BRAM write addresses
      bramWriteRangePipe.start(RangeConfig {xbase: rowStartBytes>>1, xlimit: (rowStartBytes+rowLenBytes)>>1, xstep: 1 });
   endrule

   rule startReadReqRule;
      let colOffsetBytes <- toGet(colRangePipe.pipe).get();
      $display("startReadReqRule: colOffsetBytes=%d", colOffsetBytes);
      readReqFifo.enq(MemRequest { sglId: readPointerReg, offset: extend(colOffsetBytes), burstLen: burstLenBytes, tag: extend(tag) });

      tag <= tag + 1;
   endrule
   rule dataGearboxRule;
      let d <- toGet(dataFifo).get();
      dataGearbox.enq(unpack(d.data));
   endrule
   rule bramWriteRule;
      let bramAddr <- toGet(bramWriteRangePipe.pipe).get();
      Vector#(1,Bit#(16)) v = dataGearbox.first(); dataGearbox.deq();
      $display("bramWriteRule bramAddr=%d row=%d col=%d v=%h", bramAddr, bramAddr>>4, bramAddr & 'hf, v);
      bramWriteFifo.enq(BRAMRequest{write: True, responseOnWrite: False, address: truncate(bramAddr), datain: unpack(v[0])});
      if (bramWriteRangePipe.isLast()) begin
	 // now OK to start convolutions for rows up to here
	 let rowNumber = bramAddr >> 4; // imageColAddrBits
	 let enoughRowsCached = enoughRowsCachedReg;
	 if (!enoughRowsCached) begin
	    enoughRowsCached = rowNumber >= (kernelHeight-1);
	    enoughRowsCachedReg <= enoughRowsCached;
	 end
	 $display("bramWriteRule: islast rowNumber=%d bramAddr=%d kernelHeight=%d enoughRowsCached=%d", rowNumber, bramAddr, kernelHeight, enoughRowsCached);
	 if (enoughRowsCached) begin
	    // FIXME: start another rangeconfig that runs the convolutions for this row
	    convRequestFifo.enq(XYRangeConfig {
					       xbase: truncate(rowNumber-kernelHeight+1), xlimit: truncate(rowNumber+1), xstep: 1,
					       ybase: 0, ylimit: kernelHeight, ystep: 1
					       });
	 end
      end
   endrule

   rule convStartRule;
      let req <- toGet(convRequestFifo).get();
      $display("range startRule x=%d xlimit=%d y=%d ylimit=%d", req.xbase, req.xlimit, req.ybase, req.ylimit);
      rangePipe.start(req);
      rpMutex.enq(req);
   endrule

   rule issueBramReadRequest;
      match { .x, .y } <- toGet(rangePipe.pipe).get();
      // fixme: placeholder address computation
      let addr = (x << kernelColAddrBits) | y;
      bramRequestFifo.enq(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: 0});
      lastFifo.enq(tuple2(rangePipe.isFirst(), rangePipe.isLast()));
      if (rangePipe.isLast && !rpMutex.notEmpty()) begin
	 $display("range finished but rpMutex empty");
      end
      if (rangePipe.isLast && rpMutex.notEmpty()) begin
	 let req <- toGet(rpMutex).get();
	 $display("range finished x=%d y=%d", req.xbase, req.ybase);
      end
      $display("issueBramReadRequest x=%d y=%d first=%d last=%d", x, y, rangePipe.isFirst(), rangePipe.isLast());
   endrule
   rule issueInnerProdRequest;
      let v <- toGet(bramResponseFifo).get();
      match { .first, .last } <- toGet(lastFifo).get();
      let allTiles = fromInteger(valueOf(NumTiles));
      $display("issueInnerProdRequest v=%d first=%d last=%d", v, first, last);
      if (innerProdRequestFifo.notFull())
      innerProdRequestFifo.enq(InnerProdParam { tile: allTiles, v: v, first: first, last: last, update: False });
      else
	 $display("innerProdRequestFifo full");
   endrule

   interface rowRequest = toPut(rowRequestFifo);
   interface convRequest = toPut(convRequestFifo);
   interface innerProdRequest = toPipeOut(innerProdRequestFifo);
   interface BRAMClient bramReadClient;
      interface request = toGet(bramRequestFifo);
      interface response = toPut(bramResponseFifo);
   endinterface
   interface BRAMClient bramWriteClient;
      interface request = toGet(bramWriteFifo);
      interface Put response;
	 method Action put(Int#(16) v);
	    $display("bramWriteClient.response.put should never be called. v=%h", v);
	 endmethod
      endinterface
   endinterface
   interface MemReadClient readClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData = toPut(dataFifo);
   endinterface
endmodule

(* synthesize *)
module mkRequestPipesSynth(ReqPipes#(NumMacroTiles,TileNumSize,InnerProdParam));
   let rp <- mkRequestPipes();
   return rp;
endmodule

(* synthesize *)
module mkResponsePipesSynth(ResponsePipes#(NumMacroTiles));
   let op <- mkResponsePipes();
   return op;
endmodule

(* synthesize *)
module mkInnerProdSynth#(Clock derivedClock)(InnerProdSynth);
   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;

   let derivedReset <- mkAsyncReset(10, defaultReset, derivedClock);

   let optionalReset = derivedReset; // noReset

   BRAM2Port#(Bit#(10),Int#(16)) lineBuffer <- mkBRAM2Server(defaultValue);

   FIFOF#(InnerProdParam) inputFifo <- mkDualClockBramFIFOF(defaultClock, defaultReset, derivedClock, derivedReset);
   FIFOF#(InnerProdResponse) bramFifo <- mkTileResponseFifo(derivedClock, derivedReset, defaultClock, defaultReset);

   Reg#(Bit#(32)) cycles <- mkReg(0, clocked_by derivedClock, reset_by derivedReset);
   rule cyclesRule;
      cycles <= cycles+1;
   endrule

   let rpReset <- mkAsyncReset(10, defaultReset, derivedClock);
   let opReset <- mkAsyncReset(10, defaultReset, derivedClock);
   ReqPipes#(NumMacroTiles,TileNumSize,InnerProdParam) rp <- mkRequestPipesSynth(clocked_by derivedClock, reset_by rpReset);
   ResponsePipes#(NumMacroTiles) op <- mkResponsePipesSynth(clocked_by derivedClock, reset_by opReset);

   Reset mtReset <- mkAsyncReset(10, derivedReset, derivedClock);
   for (Integer mt = 0; mt < valueOf(NumMacroTiles); mt = mt + 1) begin
      let macroTile <- mkMacroTile(fromInteger(mt), clocked_by derivedClock, reset_by mtReset);
      mtReset = macroTile.resetOut;
      mkConnection(rp.outPipes[mt], macroTile.inPipe, clocked_by derivedClock, reset_by mtReset);
      mkConnection(macroTile.outPipe, op.inPipes[mt], clocked_by derivedClock, reset_by mtReset);
   end

   mkConnection(toPipeOut(inputFifo), rp.inPipe, clocked_by derivedClock, reset_by derivedReset);
   mkConnection(op.outPipe, toPipeIn(bramFifo), clocked_by derivedClock, reset_by derivedReset);

   Reg#(Bit#(TileNumSize)) tReg <- mkReg(0);
   Reg#(Bit#(TileNumSize)) mReg <- mkReg(0);
   Wire#(Bool) bWire <- mkDWire(False);
   rule foo if (bWire);
      $display("m=%d t=%d", mReg, tReg);
   endrule

   let ipDriver <- mkIPDriver();
   mkConnection(ipDriver.bramReadClient, lineBuffer.portB);
   mkConnection(ipDriver.bramWriteClient, lineBuffer.portA);
   mkConnection(ipDriver.innerProdRequest, toPipeIn(inputFifo));

   interface InnerProdRequest request;
      method Action write(Bit#(16) addr, Bit#(16) val);
	 lineBuffer.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: truncate(addr), datain: unpack(val)});
      endmethod
      method Action innerProd(Bit#(16) tile, Bit#(16) a, Bool first, Bool last, Bool update);
	 Bit#(TileNumSize) t = truncate(tile);
	 Bit#(TileNumSize) m = truncate(tile >> valueOf(TLog#(NumTilesPerMacro)));
	 tReg <= t;
	 mReg <= m;
	 bWire <= True;
	 $display("request.innerProd m=%d t=%d a=%h first=%d last=%d", m, t, a, first, last);
         // broadcast to all tiles
	 inputFifo.enq(InnerProdParam { tile: t, v: unpack(a), first: first, last: last, update: update});
      endmethod
      method Action startIndividualConv(Bit#(16) xbase, Bit#(16) xlimit, Bit#(16) ybase, Bit#(16) ylimit);
	 ipDriver.convRequest.put(XYRangeConfig { xbase: truncate(xbase), xlimit: truncate(xlimit), xstep: 1,
						 ybase: truncate(ybase), ylimit: truncate(ylimit), ystep: 1 });
      endmethod
      method Action startConv(Bit#(32) ptr, Bit#(16) xbase, Bit#(16) xlimit, Bit#(16) ybase, Bit#(16) ylimit);
	 ipDriver.readPointer <= truncate(ptr);
      // fixme column bytes
	 ipDriver.rowRequest.put(RangeConfig { xbase: truncate(xbase), xlimit: truncate(xlimit)<<4, xstep: 1<<4 });
      endmethod
      method Action finish();
	 $dumpflush();
	 $finish();
      endmethod
   endinterface
   interface Get response = toGet(bramFifo);
   interface Vector readClients = vec(ipDriver.readClient);
endmodule

module mkInnerProd#(
`ifdef IMPORT_HOSTIF
		    HostInterface host,
`endif
		    InnerProdIndication ind)(InnerProd);

   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;
`ifdef IMPORT_HOSTIF
   let derivedClock = host.derivedClock;
`else
   let derivedClock = defaultClock;
`endif

   let ip <- mkInnerProdSynth(derivedClock);
   rule indRule;
      match { .t, .v } <- ip.response.get();
      $display("indRule tile=%d v=%h", t, v);
      ind.innerProd(pack(t), pack(v));
   endrule

   interface InnerProdRequest request = ip.request;
   interface Vector       readClients = ip.readClients;
endmodule


add toServer and toClient to Util

add toServer and toClient to Util

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

package Utils;

import GetPut::*;
import FIFOF::*;
import Vector::*;
import ClientServer::*;

typedef 8'h07 Idle;
typedef 8'h55 Preamble;
typedef 8'hfb Start;
typedef 8'hd5 Sfd;
typedef 8'hfd Terminate;
typedef 8'hfe Error;
typedef 8'h9c Sequence;

typedef 8'd1  LocalFault;
typedef 8'd2  RemoteFault;

typedef 48'h010000c28001 PauseFrame;

typedef 2'd0  LINK_FAULT_OK;
typedef 2'd1  LINK_FAULT_LOCAL;
typedef 2'd2  LINK_FAULT_REMOTE;
typedef 1'b0  FAULT_SEQ_LOCAL;
typedef 1'b1  FAULT_SEQ_REMOTE;

typedef struct {
   Bit#(64) data;
   Bit#(8) ctrl;
} XgmiiTup deriving (Eq, Bits);

function alpha byteSwap(alpha w)
   provisos (Bits#(alpha, asz),
             Div#(asz, 8, avec),
             Bits#(Vector::Vector#(avec, Bit#(8)), asz));
   Vector#(avec, Bit#(8)) bytes = unpack(pack(w));
   return unpack(pack(reverse(bytes)));
endfunction

function alpha apply_changes (alpha data, alpha metadata, alpha mask)
   provisos (Bits#(alpha, asz),
             Bitwise#(alpha));
   return (data & mask) | metadata;
endfunction

function Bool fifoNotEmpty(FIFOF#(a) fifo);
   return fifo.notEmpty();
endfunction

typeclass DefaultMask#(type t);
   t defaultMask;
endtypeclass

typeclass ToClient #(type req_t, type rsp_t, type ifc1_t, type ifc2_t);
   function Client #(req_t, rsp_t) toClient (ifc1_t ifc1, ifc2_t ifc2);
endtypeclass

typeclass ToServer #(type req_t, type rsp_t, type ifc1_t, type ifc2_t);
   function Server #(req_t, rsp_t) toServer (ifc1_t  ifc1, ifc2_t  ifc2);
endtypeclass

instance ToClient #(req_t, rsp_t, ifc1_t, ifc2_t)
   provisos (ToGet #(ifc1_t, req_t), ToPut #(ifc2_t, rsp_t));
   function Client #(req_t, rsp_t) toClient (ifc1_t ifc1, ifc2_t ifc2);
      return interface Client;
                interface Get request  = toGet (ifc1);
                interface Put response = toPut (ifc2);
             endinterface;
   endfunction
endinstance

instance ToServer #(req_t, rsp_t, ifc1_t, ifc2_t)
   provisos (ToPut #(ifc1_t, req_t), ToGet #(ifc2_t, rsp_t));
   function Server #(req_t, rsp_t) toServer (ifc1_t ifc1, ifc2_t ifc2);
      return interface Server;
                interface Put request  = toPut (ifc1);
                interface Get response = toGet (ifc2);
             endinterface;
   endfunction
endinstance

endpackage

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

package Utils;

import FIFOF::*;
import Vector::*;

typedef 8'h07 Idle;
typedef 8'h55 Preamble;
typedef 8'hfb Start;
typedef 8'hd5 Sfd;
typedef 8'hfd Terminate;
typedef 8'hfe Error;
typedef 8'h9c Sequence;

typedef 8'd1  LocalFault;
typedef 8'd2  RemoteFault;

typedef 48'h010000c28001 PauseFrame;

typedef 2'd0  LINK_FAULT_OK;
typedef 2'd1  LINK_FAULT_LOCAL;
typedef 2'd2  LINK_FAULT_REMOTE;
typedef 1'b0  FAULT_SEQ_LOCAL;
typedef 1'b1  FAULT_SEQ_REMOTE;

typedef struct {
   Bit#(64) data;
   Bit#(8) ctrl;
} XgmiiTup deriving (Eq, Bits);

function alpha byteSwap(alpha w)
   provisos (Bits#(alpha, asz),
             Div#(asz, 8, avec),
             Bits#(Vector::Vector#(avec, Bit#(8)), asz));
   Vector#(avec, Bit#(8)) bytes = unpack(pack(w));
   return unpack(pack(reverse(bytes)));
endfunction

function Bool fifoNotEmpty(FIFOF#(a) fifo);
   return fifo.notEmpty();
endfunction

typeclass DefaultMask#(type t);
   t defaultMask;
endtypeclass

endpackage