remove gratuitous "<<4"

remove gratuitous "<<4"

// 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

// TLog#(kernelheight * rowlenbytes)
typedef 11 LineBufferAddrSize;

interface InnerProdDriver;
   interface Reg#(SGLId)                 readPointer;
   interface Put#(IteratorConfig#(Bit#(16))) rowRequest;
   interface Put#(XYIteratorConfig#(Bit#(LineBufferAddrSize))) convRequest; // for testing
   interface BRAMClient#(Bit#(LineBufferAddrSize),Int#(16)) bramReadClient;
   interface BRAMClient#(Bit#(LineBufferAddrSize),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#(LineBufferAddrSize),Int#(16))) bramRequestFifo <- mkFIFOF();
   FIFOF#(BRAMRequest#(Bit#(LineBufferAddrSize),Int#(16))) bramWriteFifo <- mkFIFOF();
   FIFOF#(Int#(16)) bramResponseFifo <- mkFIFOF();
   FIFOF#(InnerProdParam) innerProdRequestFifo <- mkFIFOF();
   FIFOF#(Tuple4#(Bool,Bool,Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize))) lastFifo <- mkFIFOF();

   FIFOF#(IteratorConfig#(Bit#(16))) rowRequestFifo <- mkFIFOF();
   FIFOF#(XYIteratorConfig#(Bit#(LineBufferAddrSize))) convRequestFifo <- mkFIFOF();
   XYIteratorIfc#(Bit#(LineBufferAddrSize)) convIterator <- mkXYIterator();
   XYIteratorIfc#(Bit#(LineBufferAddrSize)) ipIterator <- mkXYIterator();

   IteratorIfc#(Bit#(16)) imageIterator <- mkIterator();
   IteratorIfc#(Bit#(16)) rowIterator <- mkIterator();
   IteratorIfc#(Bit#(16)) colIterator <- mkIterator();
   IteratorWithContext#(Bit#(16),Bit#(16)) bramWriteIterator <- mkIteratorWithContext();

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

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

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

   rule startImagesRule;
      let req <- toGet(rowRequestFifo).get();
      $display("startImagesRule: xbase=%d xlimit=%d xstep=%d", req.xbase, req.xlimit, req.xstep);
      imageIterator.start(req);
   endrule
   rule startRowsRule;
      let imageNumber <- toGet(imageIterator.pipe).get();
      let req = IteratorConfig { xbase: imageNumber << imageSizeShift, xlimit: (imageNumber+1) << imageSizeShift, xstep: rowLenBytes };
      $display("startRowsRule: xbase=%d xlimit=%d xstep=%d", req.xbase, req.xlimit, req.xstep);
      rowIterator.start(req);
   endrule
   rule startReadRowRule;
      let rowStartBytes <- toGet(rowIterator.pipe).get();
      let rowNumber = rowIterator.count();
      $display("startReadRowRule: rowStartBytes=%d rowLenBytes=%d burstLenBytes=%d", rowStartBytes, rowLenBytes, burstLenBytes);
      // start iterator of DRAM read addresses
      colIterator.start(IteratorConfig {xbase: rowStartBytes, xlimit: rowStartBytes+rowLenBytes, xstep: extend(burstLenBytes) });
      // start iterator of BRAM write addresses
      bramWriteIterator.start(IteratorConfig {xbase: rowStartBytes>>1, xlimit: (rowStartBytes+rowLenBytes)>>1, xstep: 1 },
			      rowNumber);
   endrule

   rule startReadReqRule;
      let colOffsetBytes <- toGet(colIterator.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(bramWriteIterator.pipe).get();
      let rowNumber = bramWriteIterator.ctxt();
      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 (bramWriteIterator.isLast()) begin
	 // now OK to start convolutions for rows up to here
	 let enoughRowsCached = enoughRowsCachedReg;
	 if (!enoughRowsCached) begin
	    enoughRowsCached = rowNumber >= (kernelHeight-2);
	    if (!enoughRowsCachedReg && enoughRowsCached)
	       $display("bramWriteRule rowNumber=%d enoughRowsCached=%d enoughRowsCachedReg=%d", rowNumber, enoughRowsCached, enoughRowsCachedReg);
	    enoughRowsCachedReg <= enoughRowsCached;
	 end
	 $display("bramWriteRule: islast rowNumber=%d bramAddr=%d kernelHeight=%d enoughRowsCachedReg=%d", rowNumber, bramAddr, kernelHeight, enoughRowsCachedReg);
	 if (enoughRowsCachedReg) begin
	    convIterator.start(XYIteratorConfig {
					       xbase: truncate(rowNumber-kernelHeight+1), xlimit: truncate(rowNumber-kernelHeight+2), xstep: 1,
					       ybase: 0, ylimit: (truncate(rowLenBytes>>1)-kernelWidth), ystep: 1
					       });
	 end
      end
   endrule

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

   rule issueBramReadRequest;
      match { .x, .y } <- toGet(ipIterator.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(ipIterator.isFirst(), ipIterator.isLast(), x, y));
      $display("issueBramReadRequest x=%d y=%d first=%d last=%d", x, y, ipIterator.isFirst(), ipIterator.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#(LineBufferAddrSize),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(XYIteratorConfig { 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(IteratorConfig { xbase: truncate(xbase), xlimit: truncate(xlimit), xstep: 1 });
      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

// TLog#(kernelheight * rowlenbytes)
typedef 11 LineBufferAddrSize;

interface InnerProdDriver;
   interface Reg#(SGLId)                 readPointer;
   interface Put#(IteratorConfig#(Bit#(16))) rowRequest;
   interface Put#(XYIteratorConfig#(Bit#(LineBufferAddrSize))) convRequest; // for testing
   interface BRAMClient#(Bit#(LineBufferAddrSize),Int#(16)) bramReadClient;
   interface BRAMClient#(Bit#(LineBufferAddrSize),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#(LineBufferAddrSize),Int#(16))) bramRequestFifo <- mkFIFOF();
   FIFOF#(BRAMRequest#(Bit#(LineBufferAddrSize),Int#(16))) bramWriteFifo <- mkFIFOF();
   FIFOF#(Int#(16)) bramResponseFifo <- mkFIFOF();
   FIFOF#(InnerProdParam) innerProdRequestFifo <- mkFIFOF();
   FIFOF#(Tuple4#(Bool,Bool,Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize))) lastFifo <- mkFIFOF();

   FIFOF#(IteratorConfig#(Bit#(16))) rowRequestFifo <- mkFIFOF();
   FIFOF#(XYIteratorConfig#(Bit#(LineBufferAddrSize))) convRequestFifo <- mkFIFOF();
   XYIteratorIfc#(Bit#(LineBufferAddrSize)) convIterator <- mkXYIterator();
   XYIteratorIfc#(Bit#(LineBufferAddrSize)) ipIterator <- mkXYIterator();

   IteratorIfc#(Bit#(16)) imageIterator <- mkIterator();
   IteratorIfc#(Bit#(16)) rowIterator <- mkIterator();
   IteratorIfc#(Bit#(16)) colIterator <- mkIterator();
   IteratorWithContext#(Bit#(16),Bit#(16)) bramWriteIterator <- mkIteratorWithContext();

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

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

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

   rule startImagesRule;
      let req <- toGet(rowRequestFifo).get();
      $display("startImagesRule: xbase=%d xlimit=%d xstep=%d", req.xbase, req.xlimit, req.xstep);
      imageIterator.start(req);
   endrule
   rule startRowsRule;
      let imageNumber <- toGet(imageIterator.pipe).get();
      let req = IteratorConfig { xbase: imageNumber << imageSizeShift, xlimit: (imageNumber+1) << imageSizeShift, xstep: rowLenBytes };
      $display("startRowsRule: xbase=%d xlimit=%d xstep=%d", req.xbase, req.xlimit, req.xstep);
      rowIterator.start(req);
   endrule
   rule startReadRowRule;
      let rowStartBytes <- toGet(rowIterator.pipe).get();
      let rowNumber = rowIterator.count();
      $display("startReadRowRule: rowStartBytes=%d rowLenBytes=%d burstLenBytes=%d", rowStartBytes, rowLenBytes, burstLenBytes);
      // start iterator of DRAM read addresses
      colIterator.start(IteratorConfig {xbase: rowStartBytes, xlimit: rowStartBytes+rowLenBytes, xstep: extend(burstLenBytes) });
      // start iterator of BRAM write addresses
      bramWriteIterator.start(IteratorConfig {xbase: rowStartBytes>>1, xlimit: (rowStartBytes+rowLenBytes)>>1, xstep: 1 },
			      rowNumber);
   endrule

   rule startReadReqRule;
      let colOffsetBytes <- toGet(colIterator.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(bramWriteIterator.pipe).get();
      let rowNumber = bramWriteIterator.ctxt();
      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 (bramWriteIterator.isLast()) begin
	 // now OK to start convolutions for rows up to here
	 let enoughRowsCached = enoughRowsCachedReg;
	 if (!enoughRowsCached) begin
	    enoughRowsCached = rowNumber >= (kernelHeight-2);
	    if (!enoughRowsCachedReg && enoughRowsCached)
	       $display("bramWriteRule rowNumber=%d enoughRowsCached=%d enoughRowsCachedReg=%d", rowNumber, enoughRowsCached, enoughRowsCachedReg);
	    enoughRowsCachedReg <= enoughRowsCached;
	 end
	 $display("bramWriteRule: islast rowNumber=%d bramAddr=%d kernelHeight=%d enoughRowsCachedReg=%d", rowNumber, bramAddr, kernelHeight, enoughRowsCachedReg);
	 if (enoughRowsCachedReg) begin
	    convIterator.start(XYIteratorConfig {
					       xbase: truncate(rowNumber-kernelHeight+1), xlimit: truncate(rowNumber-kernelHeight+2), xstep: 1,
					       ybase: 0, ylimit: (truncate(rowLenBytes>>1)-kernelWidth), ystep: 1
					       });
	 end
      end
   endrule

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

   rule issueBramReadRequest;
      match { .x, .y } <- toGet(ipIterator.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(ipIterator.isFirst(), ipIterator.isLast(), x, y));
      $display("issueBramReadRequest x=%d y=%d first=%d last=%d", x, y, ipIterator.isFirst(), ipIterator.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#(LineBufferAddrSize),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(XYIteratorConfig { 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(IteratorConfig { 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


updated read_server cmd interface

updated read_server cmd interface

// 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.

import Arith ::*;
import BuildVector::*;
import ClientServer::*;
import ConfigCounter::*;
import Connectable::*;
import DefaultValue::*;
import FIFO ::*;
import FIFOF ::*;
import GetPut ::*;
import Gearbox ::*;
import Pipe ::*;
import SpecialFIFOs ::*;
import Vector ::*;

import Ethernet::*;
import HostInterface::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import PacketBuffer::*;

interface SonicPins;
   method Action osc_50(Bit#(1) b3b, Bit#(1) b3d, Bit#(1) b4a, Bit#(1) b4d, Bit#(1) b7a, Bit#(1) b7d, Bit#(1) b8a, Bit#(1) b8d);
   (* prefix="" *)
   method Action pcie_perst_n(Bit#(1) pcie_perst_n);
   method Action user_reset_n(Bit#(1) user_reset_n);
endinterface

typedef 512    TxCredTotal;
typedef 32     TxCredThres;
typedef 100000 TxCredTimeout;

typedef 512    RxCredTotal;
typedef 32     RxCredThres;
typedef 100000 RxCredTimeout;

typedef TDiv#(DataBusWidth,32) DataBusWords;
typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} TxDesc deriving (Eq, Bits);

typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} RxDesc deriving (Eq, Bits);

typedef 16 RxMetadataLen;
typedef struct {
   Bit#(32) filled;
   Bit#(32) len;
   Bit#(64) reserved;
} RxMeta deriving (Eq, Bits);

interface SonicTopRequest;
   method Action sonic_read_version();
   method Action startRead(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numBytes, Bit#(32) burstLen);
   method Action startWrite(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numWords, Bit#(32) burstLen);
   method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
   method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
   method Action resetWrite(Bit#(32) qid);
endinterface

interface SonicTopIndication;
   method Action sonic_read_version_resp(Bit#(32) version);
   method Action readDone(Bit#(32) mismatchCnt);
   method Action writeDone(Bit#(32) v);
   method Action writeTxCred(UInt#(32) v);
endinterface

interface SonicTop;
   interface SonicTopRequest request;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) dmaWriteClient;
   interface Vector#(1, MemReadClient#(DataBusWidth)) dmaReadClient;
   interface SonicPins pins;
endinterface

typedef 12 NumOutstandingRequests;
typedef TMul#(NumOutstandingRequests, TMul#(32, 4)) BufferSizeBytes;
module mkSonicTop#(Clock derivedClock, Reset derivedReset, SonicTopIndication indication)(SonicTop);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   let verbose = True;

   // Tx Path
   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(TxDesc) newTxDesc <- mkReg(unpack(0));
   FIFOF#(TxDesc) txDescQueue <- mkSizedFIFOF(valueof(TxCredTotal));
   ConfigCounter#(32) txCredFreed <- mkConfigCounter(0);
   FIFOF#(UInt#(32)) txCredCf <- mkSizedFIFOF(8);

   FIFOF#(void) xmitInProgress <- mkSizedFIFOF(1);
   Gearbox#(2, 1, PacketData#(64)) fifoTxData <- mkNto1Gearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   MemreadEngine#(DataBusWidth,NumOutstandingRequests,1) re <- mkMemreadEngineBuff(valueOf(BufferSizeBytes));
   PacketBuffer txPktBuff <- mkPacketBuffer();

   rule everyCycle;
      cycle <= cycle + 1;
   endrule

   rule enqTxDesc (txDescQueue.notFull && newTxDesc.nDesc > 0);
      if (verbose) $display("Test: Enqueue TxDesc %x %d", newTxDesc.offset, newTxDesc.len);
      txDescQueue.enq(newTxDesc);
      newTxDesc.nDesc <= newTxDesc.nDesc-1;
   endrule

   // Credit Writeback is triggered by either timeout or threshold.
   rule txCreditWritebackTimeout ((cycle % fromInteger(valueOf(TxCredTimeout)) == 0) && !txCredCf.notEmpty);
      if (txCredFreed.read != 0) begin
         indication.writeTxCred(txCredFreed.read);
         txCredFreed.decrement(txCredFreed.read);
      end
   endrule

   rule txCreditWritebackThreshold;
      let v <- toGet(txCredCf).get;
      indication.writeTxCred(v);
      if (verbose) $display("Write back cred %d", v);
   endrule

   rule dmaRead;
      let v <- toGet(txDescQueue).get;
      //FIXME: burstlen
      re.read_servers[0].request.put(MemengineCmd{tag:0, sglId:v.sglId, base:v.offset, len:v.len,
                                               burstLen:truncate(v.len)});
   endrule

   rule readData;
      if (re.read_servers[0].memDataPipe.notEmpty()) begin
         let v <- toGet(re.read_servers[0].memDataPipe).get;
         txPktBuff.writeServer.writeData.put(EtherData{sop: v.first, eop: v.last, data:v.data});
      end
   endrule

   rule txMacStart;
      let pktLen <- txPktBuff.readServer.readLen.get;
      txPktBuff.readServer.readReq.put(EtherReq{len: pktLen});
      xmitInProgress.enq(?);
   endrule

   //FIXME::
   function Vector#(2, PacketData#(64)) split(EtherData in);
      Vector#(2, PacketData#(64)) v = defaultValue;
      v[0].sop = in.sop;
      v[0].eop = False;
      v[0].err = 0;
      v[0].empty = 0;
      v[0].data = in.data[63:0];
      v[0].valid = True;
      v[1].sop = False;
      v[1].eop = in.eop;
      v[1].err = 0;
      v[1].empty = 0;
      v[1].data = in.data[127:64];
      v[1].valid = True;
      return v;
   endfunction
   rule txMacInProgress if (xmitInProgress.notEmpty);
      let v <- txPktBuff.readServer.readData.get;
      //if (verbose) $display("Send: data=%x sop=%x eop=%x", v.data, v.sop, v.eop);
      // rule to read 128 bit packet from buffer, and enqueue 64 bit twice to txfifo
      Vector#(2, PacketData#(64)) txdata = split(v);
      fifoTxData.enq(txdata);

      if (v.eop) begin
         txCredFreed.increment(1);
         // tigger writeback is freed txcred is more than threshold
         if ((txCredFreed.read>=fromInteger(valueOf(TxCredThres))) && txCredCf.notFull) begin
            txCredCf.enq(txCredFreed.read);
            txCredFreed.decrement(txCredFreed.read);
         end
         xmitInProgress.deq;
      end
   endrule

 //  rule txMacClockCrossing if (fifoTxData.notEmpty);
 //     let data = fifoTxData.first;
 //     let temp = head(data);
 //     //if(verbose) $display("Send %d data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
 //     fifoTxData.deq;
 //  endrule

   //SyncFIFOIfc#(Bit#(64)) txMacSyncFifo <- mkSyncFIFO(8, clk_156_25, rst_156_n, host.portalClock);
   //PipeOut#(PacketData) txMacSyncPipeOut = toPipeOut(txMacSyncFifo);
   //PipeIn#(PacketData) txMacSyncPipeIn = toPipeIn(txMacSyncFifo);
   //mkConnection(toPipeOut(txMacFifo), txMacSyncPipeIn);
   //mkConnection(txMacSyncPipeOut, );

   // Rx Path
   FIFOF#(RxDesc)            rxDescQueue <- mkSizedFIFOF(valueof(RxCredTotal));
   FIFOF#(RxMeta)         currRxMetadata <- mkSizedFIFOF(1);
   FIFOF#(Bit#(EtherLen)) recvInProgress <- mkSizedFIFOF(1);
   FIFOF#(Bool)               finishFifo <- mkFIFOF;
   Reg#(RxDesc)                newRxDesc <- mkReg(unpack(0));
   Reg#(Bit#(32))                totalRx <- mkReg(0);
   Reg#(Bit#(EtherLen))     currDmaWrLen <- mkReg(0);
   PacketBuffer                rxPktBuff <- mkPacketBuffer();
   MemwriteEngine#(DataBusWidth,2,1) we <- mkMemwriteEngine;
   Gearbox#(1, 2, PacketData#(64)) fifoRxData <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   rule enqRxDesc(rxDescQueue.notFull && newRxDesc.nDesc>0);
      rxDescQueue.enq(newRxDesc);
      newRxDesc.nDesc <= newRxDesc.nDesc-1;
      totalRx <= totalRx+1;
   endrule
   rule dmaWriteStart;
      let rxDesc <- toGet(rxDescQueue).get;
      let pktLen <- rxPktBuff.readServer.readLen.get;
      // write packet metadata
      we.write_servers[0].cmdServer.request.put(MemengineCmd{tag:0, sglId:rxDesc.sglId,
                                                base:extend(rxDesc.offset - fromInteger(valueOf(RxMetadataLen))),
                                                len:extend(pktLen + fromInteger(valueOf(RxMetadataLen))),
                                                burstLen:truncate(pktLen + fromInteger(valueOf(RxMetadataLen)))});
      $display("SonicTop::dmaWriteStart offset=%x pktlen=%d", rxDesc.offset - fromInteger(valueof(RxMetadataLen)), pktLen);
      currRxMetadata.enq(RxMeta{filled: 1, len: extend(pktLen), reserved: 0});
   endrule
   rule dmaWriteMeta;
      let meta <- toGet(currRxMetadata).get;
      $display("SonicTop::dmaWriteMeta %d: filled=%x, len=%x", cycle, meta.filled, meta.len);
      we.write_servers[0].dataPipe.enq(pack(meta));
      rxPktBuff.readServer.readReq.put(EtherReq{len: truncate(meta.len)});
      recvInProgress.enq(truncate(meta.len));
   endrule
   rule dmaWriteInProgress if (recvInProgress.notEmpty);
      let v <- rxPktBuff.readServer.readData.get;
      $display("SonicTop::dmaWriteInProgress %d: data=%x sop=%x eop=%x", cycle, v.data, v.sop, v.eop);
      we.write_servers[0].dataPipe.enq(extend(v.data));
      if (v.eop) begin
         recvInProgress.deq;
      end
   endrule
   rule dmaWriteFinish;
      let rv <- we.write_servers[0].cmdServer.response.get;
      $display("SonicTop::dmaWriteFinish");
   endrule
   // rule to clock crossing from 156.25MHz to 250MHz with 64bit
   function EtherData combine(Vector#(2, PacketData#(64)) in);
      return EtherData {sop: in[0].sop, eop: in[1].eop, data: {in[1].data, in[0].data}};
   endfunction

   rule rxWriteData;
      // rule to pack 2x64 read from MAC interface to 128bit
      let data = fifoRxData.first; fifoRxData.deq;
      let temp = combine(data);
      $display("Recv %d: data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
      rxPktBuff.writeServer.writeData.put(temp);
   endrule

//   rule rxMacClockCrossing;
//      fifoRxData.enq(data);
//   endrule

   // Network MAC and PHY

   // Loopback
   mkConnection(toPipeOut(fifoTxData), toPipeIn(fifoRxData));

   Reg#(Bit#(32)) tx_cnt <- mkReg(0);
   interface pins = (interface SonicPins;
      // Clocks

      // Resets
      // SFP+
   endinterface);
   interface dmaWriteClient = vec(we.dmaClient);
   interface dmaReadClient = vec(re.dmaClient);
   interface SonicTopRequest request;
      method Action sonic_read_version();
         let v = `SonicVersion; //Defined in Makefile as time of compilation.
         indication.sonic_read_version_resp(v);
      endmethod
      method Action startRead(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         $display("rp=%x offset=%x len=%x burstLen=%x, tx_cnt=%d", rp, off, nb, bl, tx_cnt);
         newTxDesc <= TxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
         tx_cnt <= tx_cnt + 1;
      endmethod
      method Action startWrite(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         //$display("NewRxDesc: rp=%x offset=%x len=%x burstLen=%x", rp, off, nb, bl);
         newRxDesc <= RxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
      endmethod
      method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
         let d = EtherData{data: {upper, lower}, sop: unpack(sop), eop: unpack(eop)};
         rxPktBuff.writeServer.writeData.put(d);
      endmethod
      method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
         PacketData#(64) v = defaultValue;
         v.data = data;
         v.sop = unpack(sop);
         v.eop = unpack(eop);
         fifoRxData.enq(vec(v));
      endmethod
      method Action resetWrite(Bit#(32) qid);
         rxDescQueue.clear();
         currRxMetadata.clear();
         recvInProgress.clear();
         finishFifo.clear();
      endmethod
   endinterface
endmodule

// 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.

import Arith ::*;
import BuildVector::*;
import ClientServer::*;
import ConfigCounter::*;
import Connectable::*;
import DefaultValue::*;
import FIFO ::*;
import FIFOF ::*;
import GetPut ::*;
import Gearbox ::*;
import Pipe ::*;
import SpecialFIFOs ::*;
import Vector ::*;

import Ethernet::*;
import HostInterface::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import PacketBuffer::*;

interface SonicPins;
   method Action osc_50(Bit#(1) b3b, Bit#(1) b3d, Bit#(1) b4a, Bit#(1) b4d, Bit#(1) b7a, Bit#(1) b7d, Bit#(1) b8a, Bit#(1) b8d);
   (* prefix="" *)
   method Action pcie_perst_n(Bit#(1) pcie_perst_n);
   method Action user_reset_n(Bit#(1) user_reset_n);
endinterface

typedef 512    TxCredTotal;
typedef 32     TxCredThres;
typedef 100000 TxCredTimeout;

typedef 512    RxCredTotal;
typedef 32     RxCredThres;
typedef 100000 RxCredTimeout;

typedef TDiv#(DataBusWidth,32) DataBusWords;
typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} TxDesc deriving (Eq, Bits);

typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} RxDesc deriving (Eq, Bits);

typedef 16 RxMetadataLen;
typedef struct {
   Bit#(32) filled;
   Bit#(32) len;
   Bit#(64) reserved;
} RxMeta deriving (Eq, Bits);

interface SonicTopRequest;
   method Action sonic_read_version();
   method Action startRead(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numBytes, Bit#(32) burstLen);
   method Action startWrite(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numWords, Bit#(32) burstLen);
   method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
   method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
   method Action resetWrite(Bit#(32) qid);
endinterface

interface SonicTopIndication;
   method Action sonic_read_version_resp(Bit#(32) version);
   method Action readDone(Bit#(32) mismatchCnt);
   method Action writeDone(Bit#(32) v);
   method Action writeTxCred(UInt#(32) v);
endinterface

interface SonicTop;
   interface SonicTopRequest request;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) dmaWriteClient;
   interface Vector#(1, MemReadClient#(DataBusWidth)) dmaReadClient;
   interface SonicPins pins;
endinterface

typedef 12 NumOutstandingRequests;
typedef TMul#(NumOutstandingRequests, TMul#(32, 4)) BufferSizeBytes;
module mkSonicTop#(Clock derivedClock, Reset derivedReset, SonicTopIndication indication)(SonicTop);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   let verbose = True;

   // Tx Path
   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(TxDesc) newTxDesc <- mkReg(unpack(0));
   FIFOF#(TxDesc) txDescQueue <- mkSizedFIFOF(valueof(TxCredTotal));
   ConfigCounter#(32) txCredFreed <- mkConfigCounter(0);
   FIFOF#(UInt#(32)) txCredCf <- mkSizedFIFOF(8);

   FIFOF#(void) xmitInProgress <- mkSizedFIFOF(1);
   Gearbox#(2, 1, PacketData#(64)) fifoTxData <- mkNto1Gearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   MemreadEngine#(DataBusWidth,NumOutstandingRequests,1) re <- mkMemreadEngineBuff(valueOf(BufferSizeBytes));
   PacketBuffer txPktBuff <- mkPacketBuffer();

   rule everyCycle;
      cycle <= cycle + 1;
   endrule

   rule enqTxDesc (txDescQueue.notFull && newTxDesc.nDesc > 0);
      if (verbose) $display("Test: Enqueue TxDesc %x %d", newTxDesc.offset, newTxDesc.len);
      txDescQueue.enq(newTxDesc);
      newTxDesc.nDesc <= newTxDesc.nDesc-1;
   endrule

   // Credit Writeback is triggered by either timeout or threshold.
   rule txCreditWritebackTimeout ((cycle % fromInteger(valueOf(TxCredTimeout)) == 0) && !txCredCf.notEmpty);
      if (txCredFreed.read != 0) begin
         indication.writeTxCred(txCredFreed.read);
         txCredFreed.decrement(txCredFreed.read);
      end
   endrule

   rule txCreditWritebackThreshold;
      let v <- toGet(txCredCf).get;
      indication.writeTxCred(v);
      if (verbose) $display("Write back cred %d", v);
   endrule

   rule dmaRead;
      let v <- toGet(txDescQueue).get;
      //FIXME: burstlen
      re.read_servers[0].cmdServer.request.put(MemengineCmd{tag:0, sglId:v.sglId, base:v.offset, len:v.len,
                                               burstLen:truncate(v.len)});
   endrule

   rule readData;
      if (re.read_servers[0].memDataPipe.notEmpty()) begin
         let v <- toGet(re.read_servers[0].memDataPipe).get;
         txPktBuff.writeServer.writeData.put(EtherData{sop: v.first, eop: v.last, data:v.data});
      end
   endrule

   rule txMacStart;
      let pktLen <- txPktBuff.readServer.readLen.get;
      txPktBuff.readServer.readReq.put(EtherReq{len: pktLen});
      xmitInProgress.enq(?);
   endrule

   //FIXME::
   function Vector#(2, PacketData#(64)) split(EtherData in);
      Vector#(2, PacketData#(64)) v = defaultValue;
      v[0].sop = in.sop;
      v[0].eop = False;
      v[0].err = 0;
      v[0].empty = 0;
      v[0].data = in.data[63:0];
      v[0].valid = True;
      v[1].sop = False;
      v[1].eop = in.eop;
      v[1].err = 0;
      v[1].empty = 0;
      v[1].data = in.data[127:64];
      v[1].valid = True;
      return v;
   endfunction
   rule txMacInProgress if (xmitInProgress.notEmpty);
      let v <- txPktBuff.readServer.readData.get;
      //if (verbose) $display("Send: data=%x sop=%x eop=%x", v.data, v.sop, v.eop);
      // rule to read 128 bit packet from buffer, and enqueue 64 bit twice to txfifo
      Vector#(2, PacketData#(64)) txdata = split(v);
      fifoTxData.enq(txdata);

      if (v.eop) begin
         txCredFreed.increment(1);
         // tigger writeback is freed txcred is more than threshold
         if ((txCredFreed.read>=fromInteger(valueOf(TxCredThres))) && txCredCf.notFull) begin
            txCredCf.enq(txCredFreed.read);
            txCredFreed.decrement(txCredFreed.read);
         end
         xmitInProgress.deq;
      end
   endrule

 //  rule txMacClockCrossing if (fifoTxData.notEmpty);
 //     let data = fifoTxData.first;
 //     let temp = head(data);
 //     //if(verbose) $display("Send %d data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
 //     fifoTxData.deq;
 //  endrule

   //SyncFIFOIfc#(Bit#(64)) txMacSyncFifo <- mkSyncFIFO(8, clk_156_25, rst_156_n, host.portalClock);
   //PipeOut#(PacketData) txMacSyncPipeOut = toPipeOut(txMacSyncFifo);
   //PipeIn#(PacketData) txMacSyncPipeIn = toPipeIn(txMacSyncFifo);
   //mkConnection(toPipeOut(txMacFifo), txMacSyncPipeIn);
   //mkConnection(txMacSyncPipeOut, );

   rule dmaReadFinish;
      // clear cmdServer response
      let rv <- re.read_servers[0].cmdServer.response.get;
   endrule

   // Rx Path
   FIFOF#(RxDesc)            rxDescQueue <- mkSizedFIFOF(valueof(RxCredTotal));
   FIFOF#(RxMeta)         currRxMetadata <- mkSizedFIFOF(1);
   FIFOF#(Bit#(EtherLen)) recvInProgress <- mkSizedFIFOF(1);
   FIFOF#(Bool)               finishFifo <- mkFIFOF;
   Reg#(RxDesc)                newRxDesc <- mkReg(unpack(0));
   Reg#(Bit#(32))                totalRx <- mkReg(0);
   Reg#(Bit#(EtherLen))     currDmaWrLen <- mkReg(0);
   PacketBuffer                rxPktBuff <- mkPacketBuffer();
   MemwriteEngine#(DataBusWidth,2,1) we <- mkMemwriteEngine;
   Gearbox#(1, 2, PacketData#(64)) fifoRxData <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   rule enqRxDesc(rxDescQueue.notFull && newRxDesc.nDesc>0);
      rxDescQueue.enq(newRxDesc);
      newRxDesc.nDesc <= newRxDesc.nDesc-1;
      totalRx <= totalRx+1;
   endrule
   rule dmaWriteStart;
      let rxDesc <- toGet(rxDescQueue).get;
      let pktLen <- rxPktBuff.readServer.readLen.get;
      // write packet metadata
      we.write_servers[0].cmdServer.request.put(MemengineCmd{tag:0, sglId:rxDesc.sglId,
                                                base:extend(rxDesc.offset - fromInteger(valueOf(RxMetadataLen))),
                                                len:extend(pktLen + fromInteger(valueOf(RxMetadataLen))),
                                                burstLen:truncate(pktLen + fromInteger(valueOf(RxMetadataLen)))});
      $display("SonicTop::dmaWriteStart offset=%x pktlen=%d", rxDesc.offset - fromInteger(valueof(RxMetadataLen)), pktLen);
      currRxMetadata.enq(RxMeta{filled: 1, len: extend(pktLen), reserved: 0});
   endrule
   rule dmaWriteMeta;
      let meta <- toGet(currRxMetadata).get;
      $display("SonicTop::dmaWriteMeta %d: filled=%x, len=%x", cycle, meta.filled, meta.len);
      we.write_servers[0].dataPipe.enq(pack(meta));
      rxPktBuff.readServer.readReq.put(EtherReq{len: truncate(meta.len)});
      recvInProgress.enq(truncate(meta.len));
   endrule
   rule dmaWriteInProgress if (recvInProgress.notEmpty);
      let v <- rxPktBuff.readServer.readData.get;
      $display("SonicTop::dmaWriteInProgress %d: data=%x sop=%x eop=%x", cycle, v.data, v.sop, v.eop);
      we.write_servers[0].dataPipe.enq(extend(v.data));
      if (v.eop) begin
         recvInProgress.deq;
      end
   endrule
   rule dmaWriteFinish;
      let rv <- we.write_servers[0].cmdServer.response.get;
      $display("SonicTop::dmaWriteFinish");
   endrule
   // rule to clock crossing from 156.25MHz to 250MHz with 64bit
   function EtherData combine(Vector#(2, PacketData#(64)) in);
      return EtherData {sop: in[0].sop, eop: in[1].eop, data: {in[1].data, in[0].data}};
   endfunction

   rule rxWriteData;
      // rule to pack 2x64 read from MAC interface to 128bit
      let data = fifoRxData.first; fifoRxData.deq;
      let temp = combine(data);
      $display("Recv %d: data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
      rxPktBuff.writeServer.writeData.put(temp);
   endrule

//   rule rxMacClockCrossing;
//      fifoRxData.enq(data);
//   endrule

   // Network MAC and PHY

   // Loopback
   mkConnection(toPipeOut(fifoTxData), toPipeIn(fifoRxData));

   Reg#(Bit#(32)) tx_cnt <- mkReg(0);
   interface pins = (interface SonicPins;
      // Clocks

      // Resets
      // SFP+
   endinterface);
   interface dmaWriteClient = vec(we.dmaClient);
   interface dmaReadClient = vec(re.dmaClient);
   interface SonicTopRequest request;
      method Action sonic_read_version();
         let v = `SonicVersion; //Defined in Makefile as time of compilation.
         indication.sonic_read_version_resp(v);
      endmethod
      method Action startRead(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         $display("rp=%x offset=%x len=%x burstLen=%x, tx_cnt=%d", rp, off, nb, bl, tx_cnt);
         newTxDesc <= TxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
         tx_cnt <= tx_cnt + 1;
      endmethod
      method Action startWrite(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         //$display("NewRxDesc: rp=%x offset=%x len=%x burstLen=%x", rp, off, nb, bl);
         newRxDesc <= RxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
      endmethod
      method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
         let d = EtherData{data: {upper, lower}, sop: unpack(sop), eop: unpack(eop)};
         rxPktBuff.writeServer.writeData.put(d);
      endmethod
      method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
         PacketData#(64) v = defaultValue;
         v.data = data;
         v.sop = unpack(sop);
         v.eop = unpack(eop);
         fifoRxData.enq(vec(v));
      endmethod
      method Action resetWrite(Bit#(32) qid);
         rxDescQueue.clear();
         currRxMetadata.clear();
         recvInProgress.clear();
         finishFifo.clear();
      endmethod
   endinterface
endmodule


specify rule urgency so that tag returns are not starved by the completion rule

// 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.

// BSV Libraries
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;
import BRAM::*;
import RegFile::*;

// CONNECTAL Libraries
import MemTypes::*;
import ConnectalMemory::*;
import ConfigCounter::*;

interface TagGen#(numeric type numTags);
   method ActionValue#(Bit#(TLog#(numTags))) getTag;
   method Action returnTag(Bit#(TLog#(numTags)) tag);
   method ActionValue#(Bit#(TLog#(numTags))) complete;
endinterface

module mkTagGen(TagGen#(numTags))
   provisos(Log#(numTags,tsz));

   BRAM_Configure cfg = defaultValue;
   cfg.outFIFODepth = 1;
   //BRAM2Port#(Bit#(tsz),Bool) tags <- mkBRAM2Server(cfg);
   Vector#(numTags, Reg#(Bool)) tags <- replicateM(mkReg(False));
   //RegFile#(Bit#(tsz),Bool)     tags <- mkRegFile(0, fromInteger(valueOf(numTags)-1));
   Reg#(Bit#(tsz))        head_ptr <- mkReg(0);
   Reg#(Bit#(tsz))        tail_ptr <- mkReg(0);
   Reg#(Bool)               inited <- mkReg(False);
   FIFO#(Bit#(tsz))      comp_fifo <- mkFIFO;
   Reg#(Bit#(numTags))  comp_state <- mkReg(0);
   ConfigCounter#(TAdd#(tsz,1)) counter <- mkConfigCounter(fromInteger(valueOf(numTags)));

   let retFifo <- mkFIFO;
   let tagFifo <- mkFIFO;

   //rule complete_rule0 (comp_state[0] != 0);
      //tags.portB.request.put(BRAMRequest{write:False, address:tail_ptr, datain: ?, responseOnWrite: ?});
   //endrule

   rule complete_rule1 (comp_state[0] != 0);
      //let rv <- tags.portB.response.get;
      //let rv = tags.sub(tail_ptr);
      let rv = tags[tail_ptr];
      if (!rv) begin
	 tail_ptr <= tail_ptr+1;
	 counter.increment(1);
	 comp_state <= comp_state >> 1;
	 comp_fifo.enq(tail_ptr);
      end
   endrule

   // this used to be in the body of returnTag, but form some reason bsc does not
   // consider access to portA and portB to be conflict free **sigh**
   (* descending_urgency = "ret_rule, complete_rule1" *)
   rule ret_rule;
      let tag <- toGet(retFifo).get;
      //tags.portB.request.put(BRAMRequest{write:True, responseOnWrite:False, address:tag, datain:False});
      //tags.upd(tag, False);
      tags[tag] <= False;
      comp_state <= 1 | (comp_state << 1);
   endrule

   rule init_rule(!inited);
      //tags.portA.request.put(BRAMRequest{write:True,address:head_ptr,responseOnWrite:False,datain:False});
      //tags.upd(head_ptr, False);
      //Not needed: tags[head_ptr] <= False;
      head_ptr <= head_ptr+1;
      inited <= head_ptr+1==0;
   endrule

   rule tag_rule if (inited && counter.positive);
      //tags.portA.request.put(BRAMRequest{write:True, responseOnWrite:False, address:head_ptr, datain:True});
      //tags.upd(head_ptr, True);
      tags[head_ptr] <= True;
      head_ptr <= head_ptr+1;
      tagFifo.enq(head_ptr);
      counter.decrement(1);
   endrule

   method ActionValue#(Bit#(tsz)) getTag();
      let tag <- toGet(tagFifo).get();
      return tag;
   endmethod

   method Action returnTag(Bit#(tsz) tag) if (inited);
      retFifo.enq(tag);
   endmethod

   method ActionValue#(Bit#(tsz)) complete;
      comp_fifo.deq;
      return comp_fifo.first;
   endmethod

endmodule

// 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.

// BSV Libraries
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;
import BRAM::*;
import RegFile::*;

// CONNECTAL Libraries
import MemTypes::*;
import ConnectalMemory::*;
import ConfigCounter::*;

interface TagGen#(numeric type numTags);
   method ActionValue#(Bit#(TLog#(numTags))) getTag;
   method Action returnTag(Bit#(TLog#(numTags)) tag);
   method ActionValue#(Bit#(TLog#(numTags))) complete;
endinterface

module mkTagGen(TagGen#(numTags))
   provisos(Log#(numTags,tsz));

   BRAM_Configure cfg = defaultValue;
   cfg.outFIFODepth = 1;
   //BRAM2Port#(Bit#(tsz),Bool) tags <- mkBRAM2Server(cfg);
   Vector#(numTags, Reg#(Bool)) tags <- replicateM(mkReg(False));
   //RegFile#(Bit#(tsz),Bool)     tags <- mkRegFile(0, fromInteger(valueOf(numTags)-1));
   Reg#(Bit#(tsz))        head_ptr <- mkReg(0);
   Reg#(Bit#(tsz))        tail_ptr <- mkReg(0);
   Reg#(Bool)               inited <- mkReg(False);
   FIFO#(Bit#(tsz))      comp_fifo <- mkFIFO;
   Reg#(Bit#(numTags))  comp_state <- mkReg(0);
   ConfigCounter#(TAdd#(tsz,1)) counter <- mkConfigCounter(fromInteger(valueOf(numTags)));

   let retFifo <- mkFIFO;
   let tagFifo <- mkFIFO;

   //rule complete_rule0 (comp_state[0] != 0);
      //tags.portB.request.put(BRAMRequest{write:False, address:tail_ptr, datain: ?, responseOnWrite: ?});
   //endrule

   rule complete_rule1 (comp_state[0] != 0);
      //let rv <- tags.portB.response.get;
      //let rv = tags.sub(tail_ptr);
      let rv = tags[tail_ptr];
      if (!rv) begin
	 tail_ptr <= tail_ptr+1;
	 counter.increment(1);
	 comp_state <= comp_state >> 1;
	 comp_fifo.enq(tail_ptr);
      end
   endrule

   // this used to be in the body of returnTag, but form some reason bsc does not
   // consider access to portA and portB to be conflict free **sigh**
   rule ret_rule;
      let tag <- toGet(retFifo).get;
      //tags.portB.request.put(BRAMRequest{write:True, responseOnWrite:False, address:tag, datain:False});
      //tags.upd(tag, False);
      tags[tag] <= False;
      comp_state <= 1 | (comp_state << 1);
   endrule

   rule init_rule(!inited);
      //tags.portA.request.put(BRAMRequest{write:True,address:head_ptr,responseOnWrite:False,datain:False});
      //tags.upd(head_ptr, False);
      //Not needed: tags[head_ptr] <= False;
      head_ptr <= head_ptr+1;
      inited <= head_ptr+1==0;
   endrule

   rule tag_rule if (inited && counter.positive);
      //tags.portA.request.put(BRAMRequest{write:True, responseOnWrite:False, address:head_ptr, datain:True});
      //tags.upd(head_ptr, True);
      tags[head_ptr] <= True;
      head_ptr <= head_ptr+1;
      tagFifo.enq(head_ptr);
      counter.decrement(1);
   endrule

   method ActionValue#(Bit#(tsz)) getTag();
      let tag <- toGet(tagFifo).get();
      return tag;
   endmethod

   method Action returnTag(Bit#(tsz) tag) if (inited);
      retFifo.enq(tag);
   endmethod

   method ActionValue#(Bit#(tsz)) complete;
      comp_fifo.deq;
      return comp_fifo.first;
   endmethod

endmodule