run conv on every cached element of row

run conv on every cached element of 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 kernelWidth = 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 / (rowLenBytes/2);
	 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-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(convRangePipe.pipe).get();
      let req = XYRangeConfig {
			       xbase: truncate(rowNumber), 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#(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


remove generated bsv code

remove generated bsv code

import BuildVector::*;
import ClientServer::*;
import Connectable::*;
import DbgDefs::*;
import DefaultValue::*;
import Ethernet::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import MatchTable::*;
import MatchTableSim::*;
import PacketBuffer::*;
import Pipe::*;
import Register::*;
import SpecialFIFOs::*;
import StmtFSM::*;
import TxRx::*;
import Utils::*;
import Vector::*;
import MainDefs::*;

typedef struct {
  Bit#(9) ingress_port;
  Bit#(32) packet_length;
  Bit#(9) egress_spec;
  Bit#(9) egress_port;
  Bit#(32) egress_instance;
  Bit#(32) instance_type;
  Bit#(32) clone_spec;
  Bit#(5) _padding;
} StandardMetadataT deriving (Bits, Eq);
instance DefaultValue#(StandardMetadataT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(StandardMetadataT);
  defaultMask = unpack(maxBound);
endinstance
function StandardMetadataT extract_standard_metadata_t(Bit#(160) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(48) dstAddr;
  Bit#(48) srcAddr;
  Bit#(16) etherType;
} EthernetT deriving (Bits, Eq);
instance DefaultValue#(EthernetT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(EthernetT);
  defaultMask = unpack(maxBound);
endinstance
function EthernetT extract_ethernet_t(Bit#(112) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(4) version;
  Bit#(4) ihl;
  Bit#(8) diffserv;
  Bit#(16) totalLen;
  Bit#(16) identification;
  Bit#(3) flags;
  Bit#(13) fragOffset;
  Bit#(8) ttl;
  Bit#(8) protocol;
  Bit#(16) hdrChecksum;
  Bit#(32) srcAddr;
  Bit#(32) dstAddr;
} Ipv4T deriving (Bits, Eq);
instance DefaultValue#(Ipv4T);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(Ipv4T);
  defaultMask = unpack(maxBound);
endinstance
function Ipv4T extract_ipv4_t(Bit#(160) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) hrd;
  Bit#(16) pro;
  Bit#(8) hln;
  Bit#(8) pln;
  Bit#(16) op;
  Bit#(48) sha;
  Bit#(32) spa;
  Bit#(48) tha;
  Bit#(32) tpa;
} ArpT deriving (Bits, Eq);
instance DefaultValue#(ArpT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(ArpT);
  defaultMask = unpack(maxBound);
endinstance
function ArpT extract_arp_t(Bit#(224) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(8) icmptype;
  Bit#(8) code;
  Bit#(16) checksum;
  Bit#(32) quench;
} IcmpT deriving (Bits, Eq);
instance DefaultValue#(IcmpT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(IcmpT);
  defaultMask = unpack(maxBound);
endinstance
function IcmpT extract_icmp_t(Bit#(64) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) srcPort;
  Bit#(16) dstPort;
  Bit#(16) length_;
  Bit#(16) checksum;
} UdpT deriving (Bits, Eq);
instance DefaultValue#(UdpT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(UdpT);
  defaultMask = unpack(maxBound);
endinstance
function UdpT extract_udp_t(Bit#(64) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) msgtype;
  Bit#(32) inst;
} PaxosT deriving (Bits, Eq);
instance DefaultValue#(PaxosT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(PaxosT);
  defaultMask = unpack(maxBound);
endinstance
function PaxosT extract_paxos_t(Bit#(48) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) ballot;
} Phase1AT deriving (Bits, Eq);
instance DefaultValue#(Phase1AT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(Phase1AT);
  defaultMask = unpack(maxBound);
endinstance
function Phase1AT extract_phase1a_t(Bit#(16) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) ballot;
  Bit#(16) vballot;
  Bit#(256) paxosval;
  Bit#(16) acptid;
} Phase1BT deriving (Bits, Eq);
instance DefaultValue#(Phase1BT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(Phase1BT);
  defaultMask = unpack(maxBound);
endinstance
function Phase1BT extract_phase1b_t(Bit#(304) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) ballot;
  Bit#(256) paxosval;
} Phase2AT deriving (Bits, Eq);
instance DefaultValue#(Phase2AT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(Phase2AT);
  defaultMask = unpack(maxBound);
endinstance
function Phase2AT extract_phase2a_t(Bit#(272) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) ballot;
  Bit#(256) paxosval;
  Bit#(16) acptid;
} Phase2BT deriving (Bits, Eq);
instance DefaultValue#(Phase2BT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(Phase2BT);
  defaultMask = unpack(maxBound);
endinstance
function Phase2BT extract_phase2b_t(Bit#(288) data);
  return unpack(data);
endfunction

typedef struct {
  Bit#(16) ballot;
} LocalMetadataT deriving (Bits, Eq);
instance DefaultValue#(LocalMetadataT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(LocalMetadataT);
  defaultMask = unpack(maxBound);
endinstance
function LocalMetadataT extract_local_metadata_t(Bit#(16) data);
  return unpack(data);
endfunction

typedef struct {
  PacketInstance pkt;
  MetadataT meta;
} MetadataRequest deriving (Bits, Eq);
typedef struct {
  PacketInstance pkt;
  MetadataT meta;
} MetadataResponse deriving (Bits, Eq);
typedef struct {
  Maybe#(Bit#(9)) standard_metadata$egress_spec;
  Maybe#(Bit#(9)) runtime_port;
  Maybe#(Bit#(32)) paxos$inst;
  Maybe#(Bit#(16)) paxos1a$ballot;
  Maybe#(Bit#(16)) paxos1b$vballot;
  Maybe#(Bit#(16)) paxos1b$ballot;
  Maybe#(Bit#(16)) udp$checksum;
  Maybe#(Bit#(256)) paxos1b$paxosval;
  Maybe#(Bit#(16)) paxos1b$acptid;
  Maybe#(Bit#(256)) paxos2a$paxosval;
  Maybe#(Bit#(16)) paxos2a$ballot;
  Maybe#(Bit#(16)) paxos2b$ballot;
  Maybe#(Bit#(16)) paxos2b$acptid;
  Maybe#(Bit#(256)) paxos2b$paxosval;
  Maybe#(Bit#(16)) paxos_ballot$ballot;
  Maybe#(Bit#(9)) standard_metadata$ingress_port;
  Maybe#(Bit#(32)) ipv4$dstAddr;
  Maybe#(Bit#(16)) ethernet$etherType;
  Maybe#(Bool) valid_ipv4;
  Maybe#(Bool) valid_paxos;
  Maybe#(Bool) valid_paxos1a;
  Maybe#(Bool) valid_paxos2a;
} MetadataT deriving (Bits, Eq);
typedef union tagged {
  struct {
    PacketInstance pkt;
    Bit#(9) runtime_port;
  } ForwardReqT;
  struct {
    PacketInstance pkt;
    Bit#(32) paxos$inst;
    Bit#(16) paxos1a$ballot;
  } HandlePhase1AReqT;
  struct {
    PacketInstance pkt;
    Bit#(256) paxos2a$paxosval;
    Bit#(16) paxos2a$ballot;
    Bit#(32) paxos$inst;
  } HandlePhase2AReqT;
  struct {
    PacketInstance pkt;
    Bit#(32) paxos$inst;
  } ReadBallotReqT;
  struct {
    PacketInstance pkt;
  } DropReqT;
} BBRequest deriving (Bits, Eq);
typedef union tagged {
  struct {
    PacketInstance pkt;
    Bit#(9) standard_metadata$egress_spec;
  } ForwardRspT;
  struct {
    PacketInstance pkt;
    Bit#(16) paxos1b$vballot;
    Bit#(16) paxos1b$ballot;
    Bit#(16) udp$checksum;
    Bit#(256) paxos1b$paxosval;
    Bit#(16) paxos1b$acptid;
  } HandlePhase1ARspT;
  struct {
    PacketInstance pkt;
    Bit#(16) paxos2b$ballot;
    Bit#(16) udp$checksum;
    Bit#(16) paxos2b$acptid;
    Bit#(256) paxos2b$paxosval;
  } HandlePhase2ARspT;
  struct {
    PacketInstance pkt;
    Bit#(16) paxos_ballot$ballot;
  } ReadBallotRspT;
  struct {
    PacketInstance pkt;
  } DropRspT;
} BBResponse deriving (Bits, Eq);
interface Forward;
  interface Server#(BBRequest, BBResponse) prev_control_state;
endinterface
module mkForward(Forward);
  RX #(BBRequest) rx_prev_control_state <- mkRX;
  TX #(BBResponse) tx_prev_control_state <- mkTX;
  let rx_info_prev_control_state = rx_prev_control_state.u;
  let tx_info_prev_control_state = tx_prev_control_state.u;
  FIFOF#(PacketInstance) curr_packet_ff <- mkFIFOF;
  Reg#(Bit#(9)) standard_metadata$egress_spec <- mkReg(0);
  rule forward_request;
    let v = rx_info_prev_control_state.first;
    rx_info_prev_control_state.deq;
    case (v) matches
      tagged ForwardReqT {pkt: .pkt, runtime_port: .runtime_port}: begin
        curr_packet_ff.enq(pkt);
        standard_metadata$egress_spec <= runtime_port;
      end
    endcase
  endrule

  rule forward_response;
    let pkt <- toGet(curr_packet_ff).get;
    BBResponse rsp = tagged ForwardRspT {pkt: pkt, standard_metadata$egress_spec: standard_metadata$egress_spec};
    tx_info_prev_control_state.enq(rsp);
  endrule

  interface prev_control_state = toServer(rx_prev_control_state.e, tx_prev_control_state.e);
endmodule
interface HandlePhase1A;
  interface Client#(RegRequest#(10, 16), RegResponse#(16)) ballots_register;
  interface Client#(RegRequest#(10, 16), RegResponse#(16)) vballots_register;
  interface Client#(RegRequest#(10, 256), RegResponse#(256)) values_register;
  interface Client#(RegRequest#(1, 16), RegResponse#(16)) acceptor_id;
  interface Server#(BBRequest, BBResponse) prev_control_state;
endinterface
module mkHandlePhase1A(HandlePhase1A);
  RX #(BBRequest) rx_prev_control_state <- mkRX;
  TX #(BBResponse) tx_prev_control_state <- mkTX;
  let rx_info_prev_control_state = rx_prev_control_state.u;
  let tx_info_prev_control_state = tx_prev_control_state.u;
  FIFOF#(PacketInstance) curr_packet_ff <- mkFIFOF;
  TX #(RegRequest#(10, 16)) tx_ballots_register <- mkTX;
  RX #(RegResponse#(16)) rx_ballots_register <- mkRX;
  let tx_info_ballots_register = tx_ballots_register.u;
  let rx_info_ballots_register = rx_ballots_register.u;
  TX #(RegRequest#(10, 16)) tx_vballots_register <- mkTX;
  RX #(RegResponse#(16)) rx_vballots_register <- mkRX;
  let tx_info_vballots_register = tx_vballots_register.u;
  let rx_info_vballots_register = rx_vballots_register.u;
  TX #(RegRequest#(10, 256)) tx_values_register <- mkTX;
  RX #(RegResponse#(256)) rx_values_register <- mkRX;
  let tx_info_values_register = tx_values_register.u;
  let rx_info_values_register = rx_values_register.u;
  TX #(RegRequest#(1, 16)) tx_acceptor_id <- mkTX;
  RX #(RegResponse#(16)) rx_acceptor_id <- mkRX;
  let tx_info_acceptor_id = tx_acceptor_id.u;
  let rx_info_acceptor_id = rx_acceptor_id.u;

  Reg#(Bit#(16)) rg_ballot <- mkReg(0);
  rule handle_phase1a_request;
    let v = rx_info_prev_control_state.first;
    rx_info_prev_control_state.deq;
    case (v) matches
      tagged HandlePhase1AReqT {pkt: .pkt, paxos$inst: .paxos$inst, paxos1a$ballot: .paxos1a$ballot}: begin
        let ballots_register_req = RegRequest { addr: truncate(paxos$inst), data: paxos1a$ballot, write: True };
        tx_info_ballots_register.enq(ballots_register_req);
        let vballots_register_req = RegRequest { addr: truncate(paxos$inst), data: ?, write: False };
        tx_info_vballots_register.enq(vballots_register_req);
        let values_register_req = RegRequest { addr: truncate(paxos$inst), data: ?, write: False };
        tx_info_values_register.enq(values_register_req);
        let acceptor_id_req = RegRequest { addr: 0, data: ?, write: False };
        tx_info_acceptor_id.enq(acceptor_id_req);
        rg_ballot <= paxos1a$ballot;
        curr_packet_ff.enq(pkt);
      end
    endcase
  endrule

  rule handle_phase1a_response;
    let v_vballot = rx_info_vballots_register.first;
    rx_info_vballots_register.deq;
    let vballot = v_vballot.data;
    let v_paxosval = rx_info_values_register.first;
    rx_info_values_register.deq;
    let paxosval = v_paxosval.data;
    let v_acptid = rx_info_acceptor_id.first;
    rx_info_acceptor_id.deq;
    let acptid = v_acptid.data;
    let pkt <- toGet(curr_packet_ff).get;

    BBResponse rsp = tagged HandlePhase1ARspT {pkt: pkt, paxos1b$vballot: vballot, paxos1b$ballot: rg_ballot, udp$checksum: 0, paxos1b$paxosval: paxosval, paxos1b$acptid: acptid};
    tx_info_prev_control_state.enq(rsp);
  endrule

  interface ballots_register = toClient(tx_ballots_register.e, rx_ballots_register.e);
  interface vballots_register = toClient(tx_vballots_register.e, rx_vballots_register.e);
  interface values_register = toClient(tx_values_register.e, rx_values_register.e);
  interface acceptor_id = toClient(tx_acceptor_id.e, rx_acceptor_id.e);
  interface prev_control_state = toServer(rx_prev_control_state.e, tx_prev_control_state.e);
endmodule
interface HandlePhase2A;
  interface Client#(RegRequest#(10, 16), RegResponse#(16)) ballots_register;
  interface Client#(RegRequest#(10, 16), RegResponse#(16)) vballots_register;
  interface Client#(RegRequest#(10, 256), RegResponse#(256)) values_register;
  interface Client#(RegRequest#(1, 16), RegResponse#(16)) acceptor_id;
  interface Server#(BBRequest, BBResponse) prev_control_state;
endinterface
module mkHandlePhase2A(HandlePhase2A);
  RX #(BBRequest) rx_prev_control_state <- mkRX;
  TX #(BBResponse) tx_prev_control_state <- mkTX;
  let rx_info_prev_control_state = rx_prev_control_state.u;
  let tx_info_prev_control_state = tx_prev_control_state.u;
  FIFOF#(PacketInstance) curr_packet_ff <- mkFIFOF;
  TX #(RegRequest#(10, 16)) tx_ballots_register <- mkTX;
  RX #(RegResponse#(16)) rx_ballots_register <- mkRX;
  let tx_info_ballots_register = tx_ballots_register.u;
  let rx_info_ballots_register = rx_ballots_register.u;
  TX #(RegRequest#(10, 16)) tx_vballots_register <- mkTX;
  RX #(RegResponse#(16)) rx_vballots_register <- mkRX;
  let tx_info_vballots_register = tx_vballots_register.u;
  let rx_info_vballots_register = rx_vballots_register.u;
  TX #(RegRequest#(10, 256)) tx_values_register <- mkTX;
  RX #(RegResponse#(256)) rx_values_register <- mkRX;
  let tx_info_values_register = tx_values_register.u;
  let rx_info_values_register = rx_values_register.u;
  TX #(RegRequest#(1, 16)) tx_acceptor_id <- mkTX;
  RX #(RegResponse#(16)) rx_acceptor_id <- mkRX;
  let tx_info_acceptor_id = tx_acceptor_id.u;
  let rx_info_acceptor_id = rx_acceptor_id.u;
  Reg#(Bit#(16)) rg_ballot <- mkReg(0);
  Reg#(Bit#(256)) rg_paxosval <- mkReg(0);
  rule handle_phase2a_request;
    let v = rx_info_prev_control_state.first;
    rx_info_prev_control_state.deq;
    case (v) matches
      tagged HandlePhase2AReqT {pkt: .pkt, paxos2a$paxosval: .paxos2a$paxosval, paxos2a$ballot: .paxos2a$ballot, paxos$inst: .paxos$inst}: begin
        let ballots_register_req = RegRequest { addr: truncate(paxos$inst), data: paxos2a$ballot, write: True };
        tx_info_ballots_register.enq(ballots_register_req);
        let vballots_register_req = RegRequest { addr: truncate(paxos$inst), data: paxos2a$ballot, write: True };
        tx_info_vballots_register.enq(vballots_register_req);
        let values_register_req = RegRequest { addr: truncate(paxos$inst), data: paxos2a$paxosval, write: True };
        tx_info_values_register.enq(values_register_req);
        let acceptor_id_req = RegRequest { addr: 0, data: ?, write: False };
        tx_info_acceptor_id.enq(acceptor_id_req);
        curr_packet_ff.enq(pkt);
      end
    endcase
  endrule

  rule handle_phase2a_response;
    let v_acptid = rx_info_acceptor_id.first;
    rx_info_acceptor_id.deq;
    let acptid = v_acptid.data;
    let pkt <- toGet(curr_packet_ff).get;

    BBResponse rsp = tagged HandlePhase2ARspT {pkt: pkt, paxos2b$ballot: rg_ballot, udp$checksum: 0, paxos2b$acptid: acptid, paxos2b$paxosval: rg_paxosval};
    tx_info_prev_control_state.enq(rsp);
  endrule

  interface ballots_register = toClient(tx_ballots_register.e, rx_ballots_register.e);
  interface vballots_register = toClient(tx_vballots_register.e, rx_vballots_register.e);
  interface values_register = toClient(tx_values_register.e, rx_values_register.e);
  interface acceptor_id = toClient(tx_acceptor_id.e, rx_acceptor_id.e);
  interface prev_control_state = toServer(rx_prev_control_state.e, tx_prev_control_state.e);
endmodule
interface ReadBallot;
  interface Client#(RegRequest#(10, 16), RegResponse#(16)) ballots_register;
  interface Server#(BBRequest, BBResponse) prev_control_state;
endinterface
module mkReadBallot(ReadBallot);
  RX #(BBRequest) rx_prev_control_state <- mkRX;
  TX #(BBResponse) tx_prev_control_state <- mkTX;
  let rx_info_prev_control_state = rx_prev_control_state.u;
  let tx_info_prev_control_state = tx_prev_control_state.u;
  FIFOF#(PacketInstance) curr_packet_ff <- mkFIFOF;
  TX #(RegRequest#(10, 16)) tx_ballots_register <- mkTX;
  RX #(RegResponse#(16)) rx_ballots_register <- mkRX;
  let tx_info_ballots_register = tx_ballots_register.u;
  let rx_info_ballots_register = rx_ballots_register.u;
  rule read_ballot_request;
    let v = rx_info_prev_control_state.first;
    rx_info_prev_control_state.deq;
    case (v) matches
      tagged ReadBallotReqT {pkt: .pkt, paxos$inst: .paxos$inst}: begin
        let ballots_register_req = RegRequest { addr: truncate(paxos$inst), data: ?, write: False };
        tx_info_ballots_register.enq(ballots_register_req);
        curr_packet_ff.enq(pkt);
      end
    endcase
  endrule

  rule read_ballot_response;
    let v_ballot = rx_info_ballots_register.first;
    rx_info_ballots_register.deq;
    let ballot = v_ballot.data;
    let pkt <- toGet(curr_packet_ff).get;
    BBResponse rsp = tagged ReadBallotRspT {pkt: pkt, paxos_ballot$ballot: ballot};
    tx_info_prev_control_state.enq(rsp);
  endrule

  interface ballots_register = toClient(tx_ballots_register.e, rx_ballots_register.e);
  interface prev_control_state = toServer(rx_prev_control_state.e, tx_prev_control_state.e);
endmodule
interface Drop;
  interface Server#(BBRequest, BBResponse) prev_control_state;
endinterface
module mkDrop(Drop);
  RX #(BBRequest) rx_prev_control_state <- mkRX;
  TX #(BBResponse) tx_prev_control_state <- mkTX;
  let rx_info_prev_control_state = rx_prev_control_state.u;
  let tx_info_prev_control_state = tx_prev_control_state.u;
  FIFOF#(PacketInstance) curr_packet_ff <- mkFIFOF;
  rule _drop_request;
    let v = rx_info_prev_control_state.first;
    rx_info_prev_control_state.deq;
    case (v) matches
      tagged DropReqT {pkt: .pkt}: begin
        curr_packet_ff.enq(pkt);
      end
    endcase
  endrule

  rule _drop_response;
    let pkt <- toGet(curr_packet_ff).get;
    BBResponse rsp = tagged DropRspT {pkt: pkt};
    tx_info_prev_control_state.enq(rsp);
  endrule

  interface prev_control_state = toServer(rx_prev_control_state.e, tx_prev_control_state.e);
endmodule
typedef struct {
  Bit#(9) standard_metadata$ingress_port;
} FwdTblReqT deriving (Bits, Eq);
typedef struct {
  FwdTblActionT _action;
  Bit#(9) runtime_port;
} FwdTblRspT deriving (Bits, Eq);
interface FwdTbl;
  interface Server #(MetadataRequest, MetadataResponse) prev_control_state_0;
  interface Client #(BBRequest, BBResponse) next_control_state_0;
endinterface
module mkFwdTbl(FwdTbl);
  RX #(MetadataRequest) rx_metadata <- mkRX;
  let rx_info_metadata = rx_metadata.u;
  TX #(MetadataResponse) tx_metadata <- mkTX;
  let tx_info_metadata = tx_metadata.u;
  Vector#(1, FIFOF#(BBRequest)) bbReqFifo <- replicateM(mkFIFOF);
  Vector#(1, FIFOF#(BBResponse)) bbRspFifo <- replicateM(mkFIFOF);
  FIFOF#(PacketInstance) packet_ff <- mkFIFOF;
  MatchTable#(256, SizeOf#(FwdTblReqT), SizeOf#(FwdTblRspT)) matchTable <- mkMatchTable();
  Vector#(1, Bool) readyBits = map(fifoNotEmpty, bbRspFifo);
  Bool interruptStatus = False;
  Bit#(1) readyChannel = -1;
  for (Integer i=0; i>=0; i=i-1) begin
      if (readyBits[i]) begin
          interruptStatus = True;
          readyChannel = fromInteger(i);
      end
  end

  Vector#(2, FIFOF#(MetadataT)) metadata_ff <- replicateM(mkFIFOF);
  rule rl_handle_request;
    let data = rx_info_metadata.first;
    rx_info_metadata.deq;
    let meta = data.meta;
    let pkt = data.pkt;
    let standard_metadata$ingress_port = fromMaybe(?, meta.standard_metadata$ingress_port);
    FwdTblReqT req = FwdTblReqT {standard_metadata$ingress_port: standard_metadata$ingress_port};
    matchTable.lookupPort.request.put(pack(req));
    packet_ff.enq(pkt);
    metadata_ff[0].enq(meta);
  endrule

  rule rl_handle_execute;
    let rsp <- matchTable.lookupPort.response.get;
    let pkt <- toGet(packet_ff).get;
    let meta <- toGet(metadata_ff[0]).get;
    if (rsp matches tagged Valid .data) begin
      FwdTblRspT resp = unpack(data);
      case (resp._action) matches
        FORWARD: begin
          BBRequest req = tagged ForwardReqT {pkt: pkt, runtime_port: resp.runtime_port};
          bbReqFifo[0].enq(req); //FIXME: replace with RXTX.
        end
      endcase
      // forward metadata to next stage.
      metadata_ff[1].enq(meta);
    end
  endrule

  rule rl_handle_response if (interruptStatus);
    let v <- toGet(bbRspFifo[readyChannel]).get;
    let meta <- toGet(metadata_ff[1]).get;
    case (v) matches
      tagged ForwardRspT {pkt: .pkt, standard_metadata$egress_spec: .standard_metadata$egress_spec}: begin
        meta.standard_metadata$egress_spec = tagged Valid standard_metadata$egress_spec;
        MetadataResponse rsp = MetadataResponse {pkt: pkt, meta: meta};
        tx_info_metadata.enq(rsp);
      end
    endcase
  endrule

  interface prev_control_state_0 = toServer(rx_metadata.e, tx_metadata.e);
  interface next_control_state_0 = toClient(bbReqFifo[0], bbRspFifo[0]);
endmodule
typedef struct {
  Bit#(9) padding;
} BallotTblReqT deriving (Bits, Eq);
typedef enum {
  READBALLOT
} BallotTblActionT deriving (Bits, Eq);
typedef struct {
  BallotTblActionT _action;
} BallotTblRspT deriving (Bits, Eq);
interface BallotTbl;
  interface Server #(MetadataRequest, MetadataResponse) prev_control_state_0;
  interface Client #(BBRequest, BBResponse) next_control_state_0;
endinterface
module mkBallotTbl(BallotTbl);
  RX #(MetadataRequest) rx_metadata <- mkRX;
  let rx_info_metadata = rx_metadata.u;
  TX #(MetadataResponse) tx_metadata <- mkTX;
  let tx_info_metadata = tx_metadata.u;
  Vector#(1, FIFOF#(BBRequest)) bbReqFifo <- replicateM(mkFIFOF);
  Vector#(1, FIFOF#(BBResponse)) bbRspFifo <- replicateM(mkFIFOF);
  FIFOF#(PacketInstance) packet_ff <- mkFIFOF;
  Vector#(1, Bool) readyBits = map(fifoNotEmpty, bbRspFifo);
  Bool interruptStatus = False;
  Bit#(1) readyChannel = -1;
  for (Integer i=0; i>=0; i=i-1) begin
      if (readyBits[i]) begin
          interruptStatus = True;
          readyChannel = fromInteger(i);
      end
  end

  FIFOF#(MetadataT) metadata_ff <- mkFIFOF;
  rule rl_handle_action_request;
    let data = rx_info_metadata.first;
    rx_info_metadata.deq;
    let meta = data.meta;
    let pkt = data.pkt;
    packet_ff.enq(pkt);
    metadata_ff.enq(meta);
    let paxos$inst = fromMaybe(?, meta.paxos$inst);
    BBRequest req = tagged ReadBallotReqT {pkt: pkt, paxos$inst: paxos$inst};
    bbReqFifo[0].enq(req); //FIXME: replace with RXTX.
  endrule

  rule rl_handle_action_response if (interruptStatus);
    let v <- toGet(bbRspFifo[readyChannel]).get;
    let meta <- toGet(metadata_ff).get;
    case (v) matches
      tagged ReadBallotRspT {pkt: .pkt, paxos_ballot$ballot: .paxos_ballot$ballot}: begin
        meta.paxos_ballot$ballot = tagged Valid paxos_ballot$ballot;
        MetadataResponse rsp = MetadataResponse {pkt: pkt, meta: meta};
        tx_info_metadata.enq(rsp);
      end
    endcase
  endrule

  interface prev_control_state_0 = toServer(rx_metadata.e, tx_metadata.e);
  interface next_control_state_0 = toClient(bbReqFifo[0], bbRspFifo[0]);
endmodule
typedef struct {
  Bit#(9) padding;
} DropTblReqT deriving (Bits, Eq);
typedef enum {
  DROP
} DropTblActionT deriving (Bits, Eq);
typedef struct {
  DropTblActionT _action;
} DropTblRspT deriving (Bits, Eq);
interface DropTbl;
  interface Server #(MetadataRequest, MetadataResponse) prev_control_state_0;
  interface Client #(BBRequest, BBResponse) next_control_state_0;
endinterface
module mkDropTbl(DropTbl);
  RX #(MetadataRequest) rx_metadata <- mkRX;
  let rx_info_metadata = rx_metadata.u;
  TX #(MetadataResponse) tx_metadata <- mkTX;
  let tx_info_metadata = tx_metadata.u;
  Vector#(1, FIFOF#(BBRequest)) bbReqFifo <- replicateM(mkFIFOF);
  Vector#(1, FIFOF#(BBResponse)) bbRspFifo <- replicateM(mkFIFOF);
  FIFOF#(PacketInstance) packet_ff <- mkFIFOF;
  Vector#(1, Bool) readyBits = map(fifoNotEmpty, bbRspFifo);
  Bool interruptStatus = False;
  Bit#(1) readyChannel = -1;
  for (Integer i=0; i>=0; i=i-1) begin
      if (readyBits[i]) begin
          interruptStatus = True;
          readyChannel = fromInteger(i);
      end
  end

  FIFOF#(MetadataT) metadata_ff <- mkFIFOF;
  rule rl_handle_action_request;
    let data = rx_info_metadata.first;
    rx_info_metadata.deq;
    let meta = data.meta;
    let pkt = data.pkt;
    packet_ff.enq(pkt);
    metadata_ff.enq(meta);
    BBRequest req = tagged DropReqT {pkt: pkt};
    bbReqFifo[0].enq(req); //FIXME: replace with RXTX.
  endrule

  rule rl_handle_action_response if (interruptStatus);
    let v <- toGet(bbRspFifo[readyChannel]).get;
    let meta <- toGet(metadata_ff).get;
    case (v) matches
      tagged DropRspT {pkt: .pkt}: begin
        MetadataResponse rsp = MetadataResponse {pkt: pkt, meta: meta};
        tx_info_metadata.enq(rsp);
      end
    endcase
  endrule

  interface prev_control_state_0 = toServer(rx_metadata.e, tx_metadata.e);
  interface next_control_state_0 = toClient(bbReqFifo[0], bbRspFifo[0]);
endmodule
typedef struct {
  Bit#(9) padding;
} Paxos1ATblReqT deriving (Bits, Eq);
typedef enum {
  HANDLEPHASE1A
} Paxos1ATblActionT deriving (Bits, Eq);
typedef struct {
  Paxos1ATblActionT _action;
} Paxos1ATblRspT deriving (Bits, Eq);
interface Paxos1ATbl;
  interface Server #(MetadataRequest, MetadataResponse) prev_control_state_0;
  interface Client #(BBRequest, BBResponse) next_control_state_0;
endinterface
module mkPaxos1ATbl(Paxos1ATbl);
  RX #(MetadataRequest) rx_metadata <- mkRX;
  let rx_info_metadata = rx_metadata.u;
  TX #(MetadataResponse) tx_metadata <- mkTX;
  let tx_info_metadata = tx_metadata.u;
  Vector#(1, FIFOF#(BBRequest)) bbReqFifo <- replicateM(mkFIFOF);
  Vector#(1, FIFOF#(BBResponse)) bbRspFifo <- replicateM(mkFIFOF);
  FIFOF#(PacketInstance) packet_ff <- mkFIFOF;
  Vector#(1, Bool) readyBits = map(fifoNotEmpty, bbRspFifo);
  Bool interruptStatus = False;
  Bit#(1) readyChannel = -1;
  for (Integer i=0; i>=0; i=i-1) begin
      if (readyBits[i]) begin
          interruptStatus = True;
          readyChannel = fromInteger(i);
      end
  end

  FIFOF#(MetadataT) metadata_ff <- mkFIFOF;
  rule rl_handle_action_request;
    let data = rx_info_metadata.first;
    rx_info_metadata.deq;
    let meta = data.meta;
    let pkt = data.pkt;
    packet_ff.enq(pkt);
    metadata_ff.enq(meta);
    let paxos$inst = fromMaybe(?, meta.paxos$inst);
    let paxos1a$ballot = fromMaybe(?, meta.paxos1a$ballot);
    BBRequest req = tagged HandlePhase1AReqT {pkt: pkt, paxos$inst: paxos$inst, paxos1a$ballot: paxos1a$ballot};
    bbReqFifo[0].enq(req); //FIXME: replace with RXTX.
  endrule

  rule rl_handle_action_response if (interruptStatus);
    let v <- toGet(bbRspFifo[readyChannel]).get;
    let meta <- toGet(metadata_ff).get;
    case (v) matches
      tagged HandlePhase1ARspT {pkt: .pkt, paxos1b$vballot: .paxos1b$vballot, paxos1b$ballot: .paxos1b$ballot, udp$checksum: .udp$checksum, paxos1b$paxosval: .paxos1b$paxosval, paxos1b$acptid: .paxos1b$acptid}: begin
        meta.paxos1b$vballot = tagged Valid paxos1b$vballot;
        meta.paxos1b$ballot = tagged Valid paxos1b$ballot;
        meta.udp$checksum = tagged Valid udp$checksum;
        meta.paxos1b$paxosval = tagged Valid paxos1b$paxosval;
        meta.paxos1b$acptid = tagged Valid paxos1b$acptid;
        MetadataResponse rsp = MetadataResponse {pkt: pkt, meta: meta};
        tx_info_metadata.enq(rsp);
      end
    endcase
  endrule

  interface prev_control_state_0 = toServer(rx_metadata.e, tx_metadata.e);
  interface next_control_state_0 = toClient(bbReqFifo[0], bbRspFifo[0]);
endmodule
typedef struct {
  Bit#(9) padding;
} Paxos2ATblReqT deriving (Bits, Eq);
typedef enum {
  HANDLEPHASE2A
} Paxos2ATblActionT deriving (Bits, Eq);
typedef struct {
  Paxos2ATblActionT _action;
} Paxos2ATblRspT deriving (Bits, Eq);
interface Paxos2ATbl;
  interface Server #(MetadataRequest, MetadataResponse) prev_control_state_0;
  interface Client #(BBRequest, BBResponse) next_control_state_0;
endinterface
module mkPaxos2ATbl(Paxos2ATbl);
  RX #(MetadataRequest) rx_metadata <- mkRX;
  let rx_info_metadata = rx_metadata.u;
  TX #(MetadataResponse) tx_metadata <- mkTX;
  let tx_info_metadata = tx_metadata.u;
  Vector#(1, FIFOF#(BBRequest)) bbReqFifo <- replicateM(mkFIFOF);
  Vector#(1, FIFOF#(BBResponse)) bbRspFifo <- replicateM(mkFIFOF);
  FIFOF#(PacketInstance) packet_ff <- mkFIFOF;
  Vector#(1, Bool) readyBits = map(fifoNotEmpty, bbRspFifo);
  Bool interruptStatus = False;
  Bit#(1) readyChannel = -1;
  for (Integer i=0; i>=0; i=i-1) begin
      if (readyBits[i]) begin
          interruptStatus = True;
          readyChannel = fromInteger(i);
      end
  end

  FIFOF#(MetadataT) metadata_ff <- mkFIFOF;
  rule rl_handle_action_request;
    let data = rx_info_metadata.first;
    rx_info_metadata.deq;
    let meta = data.meta;
    let pkt = data.pkt;
    packet_ff.enq(pkt);
    metadata_ff.enq(meta);
    let paxos2a$paxosval = fromMaybe(?, meta.paxos2a$paxosval);
    let paxos2a$ballot = fromMaybe(?, meta.paxos2a$ballot);
    let paxos$inst = fromMaybe(?, meta.paxos$inst);
    BBRequest req = tagged HandlePhase2AReqT {pkt: pkt, paxos2a$paxosval: paxos2a$paxosval, paxos2a$ballot: paxos2a$ballot, paxos$inst: paxos$inst};
    bbReqFifo[0].enq(req); //FIXME: replace with RXTX.
  endrule

  rule rl_handle_action_response if (interruptStatus);
    let v <- toGet(bbRspFifo[readyChannel]).get;
    let meta <- toGet(metadata_ff).get;
    case (v) matches
      tagged HandlePhase2ARspT {pkt: .pkt, paxos2b$ballot: .paxos2b$ballot, udp$checksum: .udp$checksum, paxos2b$acptid: .paxos2b$acptid, paxos2b$paxosval: .paxos2b$paxosval}: begin
        meta.paxos2b$ballot = tagged Valid paxos2b$ballot;
        meta.udp$checksum = tagged Valid udp$checksum;
        meta.paxos2b$acptid = tagged Valid paxos2b$acptid;
        meta.paxos2b$paxosval = tagged Valid paxos2b$paxosval;
        MetadataResponse rsp = MetadataResponse {pkt: pkt, meta: meta};
        tx_info_metadata.enq(rsp);
      end
    endcase
  endrule

  interface prev_control_state_0 = toServer(rx_metadata.e, tx_metadata.e);
  interface next_control_state_0 = toClient(bbReqFifo[0], bbRspFifo[0]);
endmodule
interface Ingress;
  interface PipeOut#(MetadataRequest) eventPktSend;
  // FIXME
  method Action routingTable_add_entry(Bit#(32) dstAddr, FwdTblActionT act, Bit#(9) port_);
endinterface
module mkIngress#(Vector#(numClients, Client#(MetadataRequest, MetadataResponse)) mdc)(Ingress);
  FIFOF#(MetadataRequest) default_req_ff <- mkFIFOF;
  FIFOF#(MetadataResponse) default_rsp_ff <- mkFIFOF;
  FIFOF#(MetadataRequest) fwd_tbl_req_ff <- mkFIFOF;
  FIFOF#(MetadataResponse) fwd_tbl_rsp_ff <- mkFIFOF;
  FIFOF#(MetadataRequest) ballot_tbl_req_ff <- mkFIFOF;
  FIFOF#(MetadataResponse) ballot_tbl_rsp_ff <- mkFIFOF;
  FIFOF#(MetadataRequest) drop_tbl_req_ff <- mkFIFOF;
  FIFOF#(MetadataResponse) drop_tbl_rsp_ff <- mkFIFOF;
  FIFOF#(MetadataRequest) paxos1a_tbl_req_ff <- mkFIFOF;
  FIFOF#(MetadataResponse) paxos1a_tbl_rsp_ff <- mkFIFOF;
  FIFOF#(MetadataRequest) paxos2a_tbl_req_ff <- mkFIFOF;
  FIFOF#(MetadataResponse) paxos2a_tbl_rsp_ff <- mkFIFOF;
  Vector#(numClients, Server#(MetadataRequest, MetadataResponse)) mds = replicate(toServer(default_req_ff, default_rsp_ff));
  mkConnection(mds, mdc);
  FwdTbl fwd_tbl <- mkFwdTbl();
  BallotTbl ballot_tbl <- mkBallotTbl();
  DropTbl drop_tbl <- mkDropTbl();
  Paxos1ATbl paxos1a_tbl <- mkPaxos1ATbl();
  Paxos2ATbl paxos2a_tbl <- mkPaxos2ATbl();
  mkConnection(toClient(fwd_tbl_req_ff, fwd_tbl_rsp_ff), fwd_tbl.prev_control_state_0);
  mkConnection(toClient(ballot_tbl_req_ff, ballot_tbl_rsp_ff), ballot_tbl.prev_control_state_0);
  mkConnection(toClient(drop_tbl_req_ff, drop_tbl_rsp_ff), drop_tbl.prev_control_state_0);
  mkConnection(toClient(paxos1a_tbl_req_ff, paxos1a_tbl_rsp_ff), paxos1a_tbl.prev_control_state_0);
  mkConnection(toClient(paxos2a_tbl_req_ff, paxos2a_tbl_rsp_ff), paxos2a_tbl.prev_control_state_0);
  // Basic Blocks
  Forward forward <- mkForward();
  ReadBallot read_ballot <- mkReadBallot();
  Drop _drop <- mkDrop();
  HandlePhase1A handle_phase1a <- mkHandlePhase1A();
  HandlePhase2A handle_phase2a <- mkHandlePhase2A();
  RegisterIfc#(1, 16) acceptor_id <- mkP4Register(vec(handle_phase1a.acceptor_id));
  RegisterIfc#(10, 16) ballots_register <- mkP4Register(vec(handle_phase1a.ballots_register));
  RegisterIfc#(10, 16) vballots_register <- mkP4Register(vec(handle_phase1a.vballots_register));
  RegisterIfc#(10, 256) values_register <- mkP4Register(vec(handle_phase1a.values_register));
  mkConnection(fwd_tbl.next_control_state_0, forward.prev_control_state);
  mkConnection(ballot_tbl.next_control_state_0, read_ballot.prev_control_state);
  mkConnection(drop_tbl.next_control_state_0, _drop.prev_control_state);
  mkConnection(paxos1a_tbl.next_control_state_0, handle_phase1a.prev_control_state);
  mkConnection(paxos2a_tbl.next_control_state_0, handle_phase2a.prev_control_state);
  rule default_next_state if (default_req_ff.notEmpty);
    default_req_ff.deq;
    let _req = default_req_ff.first;
    let meta = _req.meta;
    let pkt = _req.pkt;
    if (isValid(meta.valid_ipv4)) begin
      MetadataRequest req = MetadataRequest {pkt: pkt, meta: meta};
      fwd_tbl_req_ff.enq(req);
    end
  endrule

  rule fwd_tbl_next_state if (fwd_tbl_rsp_ff.notEmpty);
    fwd_tbl_rsp_ff.deq;
    let _req = fwd_tbl_rsp_ff.first;
    let meta = _req.meta;
    let pkt = _req.pkt;
    if (isValid(meta.valid_paxos)) begin
      MetadataRequest req = MetadataRequest {pkt: pkt, meta: meta};
      ballot_tbl_req_ff.enq(req);
    end
  endrule

  rule ballot_tbl_next_state if (ballot_tbl_rsp_ff.notEmpty);
    ballot_tbl_rsp_ff.deq;
    let _req = ballot_tbl_rsp_ff.first;
    let meta = _req.meta;
    let pkt = _req.pkt;
    let paxos_ballot$ballot = fromMaybe(?, meta.paxos_ballot$ballot);
    let paxos1a$ballot = fromMaybe(?, meta.paxos1a$ballot);
    let paxos2a$ballot = fromMaybe(?, meta.paxos2a$ballot);
    if (isValid(meta.valid_paxos1a)) begin
      if (( paxos_ballot$ballot <= paxos1a$ballot )) begin
        MetadataRequest req = MetadataRequest {pkt: pkt, meta: meta};
        paxos1a_tbl_req_ff.enq(req);
      end
      else begin
        MetadataRequest req = MetadataRequest {pkt: pkt, meta: meta};
        drop_tbl_req_ff.enq(req);
      end
    end
    else begin
      if (isValid(meta.valid_paxos2a)) begin
        if (( paxos_ballot$ballot <= paxos2a$ballot )) begin
          MetadataRequest req = MetadataRequest {pkt: pkt, meta: meta};
          paxos2a_tbl_req_ff.enq(req);
        end
      end
    end
  endrule

  rule drop_tbl_next_state if (drop_tbl_rsp_ff.notEmpty);
    drop_tbl_rsp_ff.deq;
    let _req = drop_tbl_rsp_ff.first;
    let meta = _req.meta;
    let pkt = _req.pkt;
  endrule

  rule paxos1a_tbl_next_state if (paxos1a_tbl_rsp_ff.notEmpty);
    paxos1a_tbl_rsp_ff.deq;
    let _req = paxos1a_tbl_rsp_ff.first;
    let meta = _req.meta;
    let pkt = _req.pkt;
  endrule

  rule paxos2a_tbl_next_state if (paxos2a_tbl_rsp_ff.notEmpty);
    paxos2a_tbl_rsp_ff.deq;
    let _req = paxos2a_tbl_rsp_ff.first;
    let meta = _req.meta;
    let pkt = _req.pkt;
  endrule

endmodule
interface Egress;
  interface PipeOut#(MetadataRequest) eventPktSend;
endinterface
module mkEgress#(Vector#(numClients, Client#(MetadataRequest, MetadataResponse)) mdc)(Egress);
  FIFOF#(MetadataRequest) default_req_ff <- mkFIFOF;
  FIFOF#(MetadataResponse) default_rsp_ff <- mkFIFOF;
  Vector#(numClients, Server#(MetadataRequest, MetadataResponse)) mds = replicate(toServer(default_req_ff, default_rsp_ff));
  mkConnection(mds, mdc);
  // Basic Blocks
  rule default_next_state if (default_req_ff.notEmpty);
    default_req_ff.deq;
    let _req = default_req_ff.first;
    let meta = _req.meta;
    let pkt = _req.pkt;
    MetadataRequest request = MetadataRequest {pkt: pkt, meta: meta};
  endrule

endmodule
// Copyright (c) 2016 Cornell University

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

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

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