working on detecting completion

working on detecting completion

// 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 Vector#(1, MemReadClient#(DataBusWidth)) readClients;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) writeClients;
endinterface

interface InnerProd;
   interface InnerProdRequest request;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClients;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) writeClients;
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) tilebase)(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(tilebase + 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 ConvDriver;
   interface Reg#(SGLId)                 readPointer;
   interface Reg#(SGLId)                 writePointer;
   interface Put#(IteratorConfig#(Bit#(16))) rowRequest;
   interface Put#(XYIteratorConfig#(Bit#(LineBufferAddrSize))) convRequest; // for testing
   interface BRAMClient#(Bit#(LineBufferAddrSize),Int#(16)) lineBufferReadClient;
   interface BRAMClient#(Bit#(LineBufferAddrSize),Int#(16)) lineBufferWriteClient;
   interface PipeOut#(InnerProdParam) innerProdRequest;
   interface PipeIn#(InnerProdResponse) innerProdResponse;
   interface MemReadClient#(DataBusWidth) readClient;
   interface MemWriteClient#(DataBusWidth) writeClient;
endinterface

(* synthesize *)
module mkConvDriver(ConvDriver);
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   FIFOF#(BRAMRequest#(Bit#(LineBufferAddrSize),Int#(16))) lineBufferRequestFifo <- mkFIFOF();
   FIFOF#(Int#(16))                                        lineBufferResponseFifo <- mkFIFOF();
   FIFOF#(BRAMRequest#(Bit#(LineBufferAddrSize),Int#(16))) lineBufferWriteFifo <- mkFIFOF();
   BRAM2Port#(Bit#(LineBufferAddrSize),Int#(16)) topBuffer <- mkBRAM2Server(defaultValue);

   FIFOF#(InnerProdParam) innerProdRequestFifo <- mkFIFOF();
   FIFOF#(InnerProdResponse) innerProdResponseFifo <- mkFIFOF();
   FIFOF#(Tuple4#(Bool,Bool,Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize))) lastFifo <- mkFIFOF();

   FIFOF#(IteratorConfig#(Bit#(16))) rowRequestFifo <- mkFIFOF();
   IteratorWithContext#(Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize)) convIterator <- mkIteratorWithContext(); // rowNumber is the context
   IteratorWithContext#(Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize)) ipIterator <- mkIteratorWithContext(); // rowNumber is the context

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

   IteratorIfc#(Bit#(16)) imageWriteBackIterator <- mkIterator();
   IteratorIfc#(Bit#(16)) rowWriteBackIterator <- mkIterator();
   IteratorIfc#(Bit#(16)) colWriteBackIterator <- mkIterator();
   IteratorIfc#(Bit#(LineBufferAddrSize)) topBufferReadIterator <- mkIterator();

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

   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFO#(MemData#(DataBusWidth)) readDataFifo <- mkSizedFIFO(8);
   Gearbox#(TDiv#(DataBusWidth,16), 1, Bit#(16))  readDataGearbox <- mkNto1Gearbox(clock, reset, clock, reset);

   Gearbox#(1, TDiv#(DataBusWidth,16), Bit#(16))  writeDataGearbox <- mk1toNGearbox(clock, reset, clock, reset);
   FIFO#(MemRequest)              writeReqFifo <- mkFIFO();
   FIFOF#(MemData#(DataBusWidth)) writeDataFifo <- mkSizedFIFOF(8);
   FIFO#(Bit#(MemTagSize))       writeDoneFifo <- mkFIFO();

   let verbose = False;

   // 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);
      imageWriteBackIterator.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();
      if (verbose) $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(readDataFifo).get();
      readDataGearbox.enq(unpack(d.data));
   endrule
   rule bramWriteRule;
      let bramAddr <- toGet(bramWriteIterator.pipe).get();
      let rowNumber = bramWriteIterator.ctxt();
      Vector#(1,Bit#(16)) v = readDataGearbox.first(); readDataGearbox.deq();
      if (verbose) $display("bramWriteRule bramAddr=%d row=%d col=%d v=%h", bramAddr, bramAddr>>4, bramAddr & 'hf, v);
      lineBufferWriteFifo.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)
	       if (verbose) $display("bramWriteRule rowNumber=%d enoughRowsCached=%d enoughRowsCachedReg=%d", rowNumber, enoughRowsCached, enoughRowsCachedReg);
	    enoughRowsCachedReg <= enoughRowsCached;
	 end
	 if (verbose) $display("bramWriteRule: islast rowNumber=%d bramAddr=%d kernelHeight=%d enoughRowsCachedReg=%d", rowNumber, bramAddr, kernelHeight, enoughRowsCachedReg);
	 if (enoughRowsCachedReg) begin
	    convIterator.start(IteratorConfig {xbase: 0,
					       //xlimit: (truncate(rowLenBytes>>1)-kernelWidth),
					       xlimit: (truncate(rowLenBytes>>1)),
					       xstep: 1 },
			       truncate(rowNumber-kernelHeight+1));
	 end
      end
   endrule

   Reg#(Bit#(32)) crrCount <- mkReg(0);
   rule convRowRule;
      let rowNumber = convIterator.ctxt();
      let colNumber <- toGet(convIterator.pipe).get();
      let req = IteratorConfig { xbase: colNumber, xlimit: colNumber+kernelHeight, xstep: 1 };
      //fixme: this should be a 2D iterator
      ipIterator.start(req, rowNumber);
      $display("range startRule row=%d col=%d crrCount=%d", rowNumber, colNumber, crrCount);
      crrCount <= crrCount + 1;
      rowWriteBackIterator.start(IteratorConfig {xbase: 0, xlimit: fromInteger(valueOf(NumTiles)/valueOf(TDiv#(DataBusWidth,8))), xstep: 1 });
   endrule

   rule issueBramReadRequest;
      let x = ipIterator.ctxt();
      let y <- toGet(ipIterator.pipe).get();
      // fixme: placeholder address computation
      let addr = (x << kernelColAddrBits) | y;
      lineBufferRequestFifo.enq(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: 0});
      lastFifo.enq(tuple4(ipIterator.isFirst(), ipIterator.isLast(), x, y));
      if (verbose) $display("issueBramReadRequest x=%d y=%d first=%d last=%d", x, y, ipIterator.isFirst(), ipIterator.isLast());
   endrule
   rule issueInnerProdRequest;
      let v <- toGet(lineBufferResponseFifo).get();
      match { .first, .last, .x, .y } <- toGet(lastFifo).get();
      let allTiles = fromInteger(valueOf(NumTiles));
      if (verbose) $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

   Reg#(Bit#(TileNumSize)) responseCountReg <- mkReg(0);
   rule innerProdResponseRule;
      // gets back one response for each tile for each conv started
      let responseCount = responseCountReg;
      if (responseCount == 0)
	 responseCount = fromInteger(valueOf(NumTiles));
      match { .tile, .data } <- toGet(innerProdResponseFifo).get();
      if (verbose) $display("innerProdResponseRule count=%d tile=%d data=%h", responseCount, tile, data);
      topBuffer.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: truncate(pack(tile)), datain: data});

      if (responseCount == 1) begin
	 colWriteBackIterator.start(IteratorConfig {xbase: 0, xlimit: fromInteger(valueOf(NumTiles)*2), xstep: fromInteger(valueOf(DataBusWidth)/8)});
	 topBufferReadIterator.start(IteratorConfig {xbase: 0, xlimit: fromInteger(valueOf(NumTiles)), xstep: 1});
      end
      responseCountReg <= responseCount - 1;
   endrule

   rule topBufferReadRule;
      let addr <- toGet(topBufferReadIterator.pipe).get();
      if (verbose) $display("topBufferReadRule: addr=%h", addr);
      topBuffer.portB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: 0});
   endrule
   rule topBufferResponseRule;
      let v <- topBuffer.portB.response.get();
      writeDataGearbox.enq(vec(pack(v)));
   endrule
   Reg#(Bit#(32)) wrrCount <- mkReg(0);
   rule writeReqRule;
      let offset <- toGet(colWriteBackIterator.pipe).get();
      let tag = 22;
      if (colWriteBackIterator.isLast()) begin
	 //let unused <- toGet(rowWriteBackIterator.pipe).get();
      end
      wrrCount <= wrrCount + 1;
      $display("writeReqRule: offset=%h cwbi.isLast %d rwbi.isLast %d wrr %d", offset, colWriteBackIterator.isLast(), rowWriteBackIterator.isLast(), wrrCount);
      writeReqFifo.enq(MemRequest { sglId: writePointerReg, offset: extend(offset), burstLen: fromInteger(valueOf(DataBusWidth)/8), tag: tag });
   endrule
   rule writeDataRule;
      let tag = 22;
      let v = pack(writeDataGearbox.first()); writeDataGearbox.deq();
      $display("writeDataRule: data=%h", v);
      writeDataFifo.enq(MemData { data: v, tag: tag });
   endrule
   Reg#(Bit#(32)) wdoneCount <- mkReg(0);
   rule writeDone;
      let n <- toGet(rowWriteBackIterator.pipe).get();
      // if (rowWriteBackIterator.isLast()) begin
      // 	 imageWriteBackIterator.pipe.deq();
      // 	 if (imageWriteBackIterator.isLast())
      // 	    $display("Finished!!");
      // end

      $display("writeDone: wdoneCount=%d rowWriteBackIterator.isLast %d", wdoneCount, rowWriteBackIterator.isLast);
      wdoneCount <= wdoneCount + 1;
      let tag <- toGet(writeDoneFifo).get();
   endrule

   interface Reg readPointer = readPointerReg;
   interface Reg writePointer = writePointerReg;
   interface rowRequest = toPut(rowRequestFifo);
   interface innerProdRequest = toPipeOut(innerProdRequestFifo);
   interface innerProdResponse = toPipeIn(innerProdResponseFifo);
   interface BRAMClient lineBufferReadClient;
      interface request = toGet(lineBufferRequestFifo);
      interface response = toPut(lineBufferResponseFifo);
   endinterface
   interface BRAMClient lineBufferWriteClient;
      interface request = toGet(lineBufferWriteFifo);
      interface Put response;
	 method Action put(Int#(16) v);
	    $display("lineBufferWriteClient.response.put should never be called. v=%h", v);
	 endmethod
      endinterface
   endinterface
   interface MemReadClient readClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData = toPut(readDataFifo);
   endinterface
   interface MemWriteClient writeClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   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)*fromInteger(valueOf(NumTilesPerMacro)), 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);

   let ipDriver <- mkConvDriver();
   mkConnection(ipDriver.lineBufferReadClient, lineBuffer.portB);
   mkConnection(ipDriver.lineBufferWriteClient, lineBuffer.portA);
   mkConnection(ipDriver.innerProdRequest, toPipeIn(inputFifo));
   mkConnection(toPipeOut(bramFifo), ipDriver.innerProdResponse);

   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)));
	 $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 startConv(Bit#(32) rdptr, Bit#(32) wrptr, Bit#(16) xbase, Bit#(16) xlimit, Bit#(16) ybase, Bit#(16) ylimit);
	 ipDriver.readPointer <= truncate(rdptr);
	 ipDriver.writePointer <= truncate(wrptr);
      // 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);
   interface Vector writeClients = vec(ipDriver.writeClient);
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);

   interface InnerProdRequest request = ip.request;
   interface Vector       readClients = ip.readClients;
   interface Vector      writeClients = ip.writeClients;
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 Vector#(1, MemReadClient#(DataBusWidth)) readClients;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) writeClients;
endinterface

interface InnerProd;
   interface InnerProdRequest request;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClients;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) writeClients;
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) tilebase)(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(tilebase + 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 ConvDriver;
   interface Reg#(SGLId)                 readPointer;
   interface Reg#(SGLId)                 writePointer;
   interface Put#(IteratorConfig#(Bit#(16))) rowRequest;
   interface Put#(XYIteratorConfig#(Bit#(LineBufferAddrSize))) convRequest; // for testing
   interface BRAMClient#(Bit#(LineBufferAddrSize),Int#(16)) lineBufferReadClient;
   interface BRAMClient#(Bit#(LineBufferAddrSize),Int#(16)) lineBufferWriteClient;
   interface PipeOut#(InnerProdParam) innerProdRequest;
   interface PipeIn#(InnerProdResponse) innerProdResponse;
   interface MemReadClient#(DataBusWidth) readClient;
   interface MemWriteClient#(DataBusWidth) writeClient;
endinterface

(* synthesize *)
module mkConvDriver(ConvDriver);
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   FIFOF#(BRAMRequest#(Bit#(LineBufferAddrSize),Int#(16))) lineBufferRequestFifo <- mkFIFOF();
   FIFOF#(Int#(16))                                        lineBufferResponseFifo <- mkFIFOF();
   FIFOF#(BRAMRequest#(Bit#(LineBufferAddrSize),Int#(16))) lineBufferWriteFifo <- mkFIFOF();
   BRAM2Port#(Bit#(LineBufferAddrSize),Int#(16)) topBuffer <- mkBRAM2Server(defaultValue);

   FIFOF#(InnerProdParam) innerProdRequestFifo <- mkFIFOF();
   FIFOF#(InnerProdResponse) innerProdResponseFifo <- mkFIFOF();
   FIFOF#(Tuple4#(Bool,Bool,Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize))) lastFifo <- mkFIFOF();

   FIFOF#(IteratorConfig#(Bit#(16))) rowRequestFifo <- mkFIFOF();
   IteratorWithContext#(Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize)) convIterator <- mkIteratorWithContext(); // rowNumber is the context
   IteratorWithContext#(Bit#(LineBufferAddrSize),Bit#(LineBufferAddrSize)) ipIterator <- mkIteratorWithContext(); // rowNumber is the context

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

   IteratorIfc#(Bit#(16)) rowWriteBackIterator <- mkIterator();
   IteratorIfc#(Bit#(16)) colWriteBackIterator <- mkIterator();
   IteratorIfc#(Bit#(LineBufferAddrSize)) topBufferReadIterator <- mkIterator();

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

   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFO#(MemData#(DataBusWidth)) readDataFifo <- mkSizedFIFO(8);
   Gearbox#(TDiv#(DataBusWidth,16), 1, Bit#(16))  readDataGearbox <- mkNto1Gearbox(clock, reset, clock, reset);

   Gearbox#(1, TDiv#(DataBusWidth,16), Bit#(16))  writeDataGearbox <- mk1toNGearbox(clock, reset, clock, reset);
   FIFO#(MemRequest)              writeReqFifo <- mkFIFO();
   FIFOF#(MemData#(DataBusWidth)) writeDataFifo <- mkSizedFIFOF(8);
   FIFO#(Bit#(MemTagSize))       writeDoneFifo <- mkFIFO();

   let verbose = False;

   // 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();
      if (verbose) $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(readDataFifo).get();
      readDataGearbox.enq(unpack(d.data));
   endrule
   rule bramWriteRule;
      let bramAddr <- toGet(bramWriteIterator.pipe).get();
      let rowNumber = bramWriteIterator.ctxt();
      Vector#(1,Bit#(16)) v = readDataGearbox.first(); readDataGearbox.deq();
      if (verbose) $display("bramWriteRule bramAddr=%d row=%d col=%d v=%h", bramAddr, bramAddr>>4, bramAddr & 'hf, v);
      lineBufferWriteFifo.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)
	       if (verbose) $display("bramWriteRule rowNumber=%d enoughRowsCached=%d enoughRowsCachedReg=%d", rowNumber, enoughRowsCached, enoughRowsCachedReg);
	    enoughRowsCachedReg <= enoughRowsCached;
	 end
	 if (verbose) $display("bramWriteRule: islast rowNumber=%d bramAddr=%d kernelHeight=%d enoughRowsCachedReg=%d", rowNumber, bramAddr, kernelHeight, enoughRowsCachedReg);
	 if (enoughRowsCachedReg) begin
	    convIterator.start(IteratorConfig {xbase: 0,
					       //xlimit: (truncate(rowLenBytes>>1)-kernelWidth),
					       xlimit: (truncate(rowLenBytes>>1)),
					       xstep: 1 },
			       truncate(rowNumber-kernelHeight+1));
	 end
      end
   endrule

   rule convRowRule;
      let rowNumber = convIterator.ctxt();
      let colNumber <- toGet(convIterator.pipe).get();
      let req = IteratorConfig { xbase: colNumber, xlimit: colNumber+kernelHeight, xstep: 1 };
      //fixme: this should be a 2D iterator
      ipIterator.start(req, rowNumber);
      $display("range startRule row=%d col=%d", rowNumber, colNumber);
   endrule

   rule issueBramReadRequest;
      let x = ipIterator.ctxt();
      let y <- toGet(ipIterator.pipe).get();
      // fixme: placeholder address computation
      let addr = (x << kernelColAddrBits) | y;
      lineBufferRequestFifo.enq(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: 0});
      lastFifo.enq(tuple4(ipIterator.isFirst(), ipIterator.isLast(), x, y));
      if (verbose) $display("issueBramReadRequest x=%d y=%d first=%d last=%d", x, y, ipIterator.isFirst(), ipIterator.isLast());
   endrule
   rule issueInnerProdRequest;
      let v <- toGet(lineBufferResponseFifo).get();
      match { .first, .last, .x, .y } <- toGet(lastFifo).get();
      let allTiles = fromInteger(valueOf(NumTiles));
      if (verbose) $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

   Reg#(Bit#(TileNumSize)) responseCountReg <- mkReg(0);
   rule innerProdResponseRule;
      // gets back one response for each tile for each conv started
      let responseCount = responseCountReg;
      if (responseCount == 0)
	 responseCount = fromInteger(valueOf(NumTiles));
      match { .tile, .data } <- toGet(innerProdResponseFifo).get();
      if (verbose) $display("innerProdResponseRule count=%d tile=%d data=%h", responseCount, tile, data);
      topBuffer.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: truncate(pack(tile)), datain: data});

      if (responseCount == 1) begin
	 colWriteBackIterator.start(IteratorConfig {xbase: 0, xlimit: fromInteger(valueOf(NumTiles)*2), xstep: fromInteger(valueOf(DataBusWidth)/8)});
	 topBufferReadIterator.start(IteratorConfig {xbase: 0, xlimit: fromInteger(valueOf(NumTiles)), xstep: 1});
      end
      responseCountReg <= responseCount - 1;
   endrule

   rule topBufferReadRule;
      let addr <- toGet(topBufferReadIterator.pipe).get();
      if (verbose) $display("topBufferReadRule: addr=%h", addr);
      topBuffer.portB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: 0});
   endrule
   rule topBufferResponseRule;
      let v <- topBuffer.portB.response.get();
      writeDataGearbox.enq(vec(pack(v)));
   endrule
   Reg#(Bit#(32)) wrrCount <- mkReg(0);
   rule writeReqRule;
      let offset <- toGet(colWriteBackIterator.pipe).get();
      let tag = 22;
      if (colWriteBackIterator.isLast()) begin
	 //let unused <- toGet(rowWriteBackIterator.pipe).get();
      end
      wrrCount <= wrrCount + 1;
      $display("writeReqRule: offset=%h cwbi.isLast %d rwbi.isLast %d wrr %d", offset, colWriteBackIterator.isLast(), rowWriteBackIterator.isLast(), wrrCount);
      writeReqFifo.enq(MemRequest { sglId: writePointerReg, offset: extend(offset), burstLen: fromInteger(valueOf(DataBusWidth)/8), tag: tag });
   endrule
   rule writeDataRule;
      let tag = 22;
      let v = pack(writeDataGearbox.first()); writeDataGearbox.deq();
      $display("writeDataRule: data=%h", v);
      writeDataFifo.enq(MemData { data: v, tag: tag });
   endrule
   Reg#(Bit#(32)) wdoneCount <- mkReg(0);
   rule writeDone;
      $display("writeDone: wdoneCount=%d", wdoneCount);
      wdoneCount <= wdoneCount + 1;
      let tag <- toGet(writeDoneFifo).get();
   endrule

   interface Reg readPointer = readPointerReg;
   interface Reg writePointer = writePointerReg;
   interface rowRequest = toPut(rowRequestFifo);
   interface innerProdRequest = toPipeOut(innerProdRequestFifo);
   interface innerProdResponse = toPipeIn(innerProdResponseFifo);
   interface BRAMClient lineBufferReadClient;
      interface request = toGet(lineBufferRequestFifo);
      interface response = toPut(lineBufferResponseFifo);
   endinterface
   interface BRAMClient lineBufferWriteClient;
      interface request = toGet(lineBufferWriteFifo);
      interface Put response;
	 method Action put(Int#(16) v);
	    $display("lineBufferWriteClient.response.put should never be called. v=%h", v);
	 endmethod
      endinterface
   endinterface
   interface MemReadClient readClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData = toPut(readDataFifo);
   endinterface
   interface MemWriteClient writeClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   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)*fromInteger(valueOf(NumTilesPerMacro)), 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);

   let ipDriver <- mkConvDriver();
   mkConnection(ipDriver.lineBufferReadClient, lineBuffer.portB);
   mkConnection(ipDriver.lineBufferWriteClient, lineBuffer.portA);
   mkConnection(ipDriver.innerProdRequest, toPipeIn(inputFifo));
   mkConnection(toPipeOut(bramFifo), ipDriver.innerProdResponse);

   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)));
	 $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 startConv(Bit#(32) rdptr, Bit#(32) wrptr, Bit#(16) xbase, Bit#(16) xlimit, Bit#(16) ybase, Bit#(16) ylimit);
	 ipDriver.readPointer <= truncate(rdptr);
	 ipDriver.writePointer <= truncate(wrptr);
      // 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);
   interface Vector writeClients = vec(ipDriver.writeClient);
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);

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


updated periodic example to current portal interface conventions

// Copyright (c) 2013 Nokia, Inc.
// Copyright (c) 2013 Quanta Research Cambridge, Inc.

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

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

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

import FIFO::*;

import Zynq::*;

interface CorePeriodicRequest;
    method Action setPeriod(Bit#(32) v);
    method Action start();
    method Action stop();
endinterface

interface PeriodicRequest;
    interface CorePeriodicRequest coreRequest;
    interface LEDS leds;
endinterface

interface CorePeriodicIndication;
    method Action fired(Bit#(8) leds);
    method Action timerUpdated(Bit#(32) v);
endinterface

interface PeriodicIndication;
    interface CorePeriodicIndication coreIndication;
endinterface

module mkPeriodicRequest#(PeriodicIndication indication)(PeriodicRequest);
    Reg#(Bit#(8)) ledsReg <- mkReg(0);
    Reg#(Bit#(32)) counter <- mkReg(0);
    Reg#(Bit#(32)) limit <- mkReg(0);
    Reg#(Bool) running <- mkReg(False);

    rule count if (running);
        let count = counter + 1;
	if (count >= limit)
	begin
	    count = 0;
	    indication.coreIndication.fired(ledsReg);
	    ledsReg <= ledsReg + 1;
	end
	counter <= count;
    endrule

    interface CorePeriodicRequest coreRequest;
	method Action start();
	    running <= True;
	endmethod

	method Action stop();
	    running <= False;
	endmethod

	method Action setPeriod(Bit#(32) v);
	    limit <= v;
	    indication.coreIndication.timerUpdated(v);
	endmethod
    endinterface: coreRequest

    interface LEDS leds;
        method Bit#(8) leds();
            return ledsReg;
	endmethod
    endinterface
endmodule

// Copyright (c) 2013 Nokia, Inc.
// Copyright (c) 2013 Quanta Research Cambridge, Inc.

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

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

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

import FIFO::*;

import Zynq::*;

interface PeriodicIndications;
    method Action fired(Bit#(8) leds);
    method Action timerUpdated(Bit#(32) v);
endinterface

interface Periodic;
    method Action setPeriod(Bit#(32) v);
    method Action start();
    method Action stop();
    interface LEDS leds;
endinterface

module mkPeriodic#(PeriodicIndications indications)(Periodic);
    Reg#(Bit#(8)) ledsReg <- mkReg(0);
    Reg#(Bit#(32)) counter <- mkReg(0);
    Reg#(Bit#(32)) limit <- mkReg(0);
    Reg#(Bool) running <- mkReg(False);

    rule count if (running);
        let count = counter + 1;
	if (count >= limit)
	begin
	    count = 0;
	    indications.fired(ledsReg);
	    ledsReg <= ledsReg + 1;
	end
	counter <= count;
    endrule

    method Action start();
        running <= True;
    endmethod

    method Action stop();
        running <= False;
    endmethod

    method Action setPeriod(Bit#(32) v);
        limit <= v;
	indications.timerUpdated(v);
    endmethod

    interface LEDS leds;
        method Bit#(8) leds();
            return ledsReg;
	endmethod
    endinterface
endmodule


added stats to hdmidisplay

added stats to hdmidisplay

Change-Id: I53a1a70e876363453f5908f66132000cb664c43b

// Copyright (c) 2012 Nokia, Inc.
// Copyright (c) 2013 Quanta Research Cambridge, Inc.

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

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

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

import FIFOF::*;
import Clocks::*;
import GetPut::*;
import PCIE::*;
import GetPutWithClocks::*;
import Connectable::*;
import PortalMemory::*;
import Dma::*;
import DmaUtils::*;
import AxiMasterSlave::*;
import MemreadEngine::*;
import HDMI::*;
import XADC::*;
import YUV::*;

interface HdmiDisplayRequest;
   method Action startFrameBuffer0(Int#(32) base);
   method Action getTransferStats();
endinterface
interface HdmiDisplayIndication;
   method Action transferStarted(Bit#(32) count);
   method Action transferFinished(Bit#(32) count);
   method Action transferStats(Bit#(32) count, Bit#(32) transferCycles, Bit#(64) sumOfCycles);
endinterface

interface HdmiDisplay;
    interface HdmiDisplayRequest displayRequest;
    interface HdmiInternalRequest internalRequest;
    interface ObjectReadClient#(64) dmaClient;
    interface HDMI hdmi;
    interface XADC xadc;
endinterface

module mkHdmiDisplay#(Clock hdmi_clock,
		      HdmiDisplayIndication hdmiDisplayIndication,
		      HdmiInternalIndication hdmiInternalIndication)(HdmiDisplay);
    Clock defaultClock <- exposeCurrentClock;
    Reset defaultReset <- exposeCurrentReset;
    Reset hdmi_reset <- mkAsyncReset(2, defaultReset, hdmi_clock);
    Reg#(Bool) sendVsyncIndication <- mkReg(False);
    SyncPulseIfc vsyncPulse <- mkSyncHandshake(hdmi_clock, hdmi_reset, defaultClock);
    Reg#(Bit#(1)) bozobit <- mkReg(0, clocked_by hdmi_clock, reset_by hdmi_reset);

    Reg#(Maybe#(Bit#(32))) referenceReg <- mkReg(tagged Invalid);
    FIFOF#(Bit#(64))   mrFifo  <- mkSizedFIFOF(32);
    MemreadEngine#(64) memreadEngine <- mkMemreadEngine(8, mrFifo);

    HdmiGenerator hdmiGen <- mkHdmiGenerator(defaultClock, defaultReset,
					     vsyncPulse, hdmiInternalIndication, clocked_by hdmi_clock, reset_by hdmi_reset);

   SyncFIFOIfc#(Bit#(64)) synchronizer <- mkSyncFIFO(32, defaultClock, defaultReset, hdmi_clock);
   rule doGet;
      let v = mrFifo.first();
      mrFifo.deq();
      synchronizer.enq(v);
   endrule
   rule doPut;
      let v = synchronizer.first;
      synchronizer.deq;
      hdmiGen.request.put(v);
   endrule

   FIFOF#(Bool) vsyncFifo <- mkFIFOF();
   rule vsyncrule if (vsyncPulse.pulse());
      if (vsyncFifo.notFull())
	 vsyncFifo.enq(True);
   endrule
   Reg#(Bit#(32)) transferCount <- mkReg(0);
   Reg#(Bit#(32)) transferCycles <- mkReg(0);
   Reg#(Bit#(48)) transferSumOfCycles<- mkReg(0);

   rule notransfer if (referenceReg matches tagged Invalid);
      vsyncFifo.deq();
   endrule
   rule startTransfer if (referenceReg matches tagged Valid .reference);
      //memreadEngine.start(reference, 0, (1080*1920)*4, 8);
      memreadEngine.start(reference, 0, (64800)*4, 8);
      hdmiDisplayIndication.transferStarted(transferCount);
      transferCycles <= 0;
      vsyncFifo.deq();
   endrule
   rule countCycles;
      transferCycles <= transferCycles + 1;
   endrule
   rule finishTransferRule;
      let b <- memreadEngine.finish();
      transferCount <= transferCount + 1;
      transferSumOfCycles <= transferSumOfCycles + extend(transferCycles);
      hdmiDisplayIndication.transferFinished(transferCount);
   endrule

    rule bozobit_rule;
        bozobit <= ~bozobit;
    endrule

    interface HdmiDisplayRequest displayRequest;
	method Action startFrameBuffer0(Int#(32) base);
	    $display("startFrameBuffer %h", base);
            referenceReg <= tagged Valid truncate(pack(base));
	    hdmiGen.control.setTestPattern(0);
	endmethod
       method Action getTransferStats();
          hdmiDisplayIndication.transferStats(transferCount, transferCycles, extend(transferSumOfCycles));
       endmethod
    endinterface: displayRequest

    interface ObjectReadClient dmaClient = memreadEngine.dmaClient;
    interface HDMI hdmi = hdmiGen.hdmi;
    interface HdmiInternalRequest internalRequest = hdmiGen.control;
    interface XADC xadc;
        method Bit#(4) gpio;
            return { bozobit, hdmiGen.hdmi.vsync,
                hdmiGen.hdmi.data[8], hdmiGen.hdmi.data[0]};
                //hdmiGen.hdmi.hsync, hdmi_de};
        endmethod
    endinterface: xadc
endmodule

// Copyright (c) 2012 Nokia, Inc.
// Copyright (c) 2013 Quanta Research Cambridge, Inc.

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

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

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

import FIFOF::*;
import Clocks::*;
import GetPut::*;
import PCIE::*;
import GetPutWithClocks::*;
import Connectable::*;
import PortalMemory::*;
import Dma::*;
import DmaUtils::*;
import AxiMasterSlave::*;
import MemreadEngine::*;
import HDMI::*;
import XADC::*;
import YUV::*;

interface HdmiDisplayRequest;
    method Action startFrameBuffer0(Int#(32) base);
endinterface
interface HdmiDisplayIndication;
   method Action transferStarted(Bit#(32) count);
endinterface

interface HdmiDisplay;
    interface HdmiDisplayRequest displayRequest;
    interface HdmiInternalRequest internalRequest;
    interface ObjectReadClient#(64) dmaClient;
    interface HDMI hdmi;
    interface XADC xadc;
endinterface

module mkHdmiDisplay#(Clock hdmi_clock,
		      HdmiDisplayIndication hdmiDisplayIndication,
		      HdmiInternalIndication hdmiInternalIndication)(HdmiDisplay);
    Clock defaultClock <- exposeCurrentClock;
    Reset defaultReset <- exposeCurrentReset;
    Reset hdmi_reset <- mkAsyncReset(2, defaultReset, hdmi_clock);
    Reg#(Bool) sendVsyncIndication <- mkReg(False);
    SyncPulseIfc vsyncPulse <- mkSyncHandshake(hdmi_clock, hdmi_reset, defaultClock);
    Reg#(Bit#(1)) bozobit <- mkReg(0, clocked_by hdmi_clock, reset_by hdmi_reset);

    Reg#(Maybe#(Bit#(32))) referenceReg <- mkReg(tagged Invalid);
    FIFOF#(Bit#(64))   mrFifo  <- mkSizedFIFOF(32);
    MemreadEngine#(64) memreadEngine <- mkMemreadEngine(8, mrFifo);

    HdmiGenerator hdmiGen <- mkHdmiGenerator(defaultClock, defaultReset,
					     vsyncPulse, hdmiInternalIndication, clocked_by hdmi_clock, reset_by hdmi_reset);

   if (False) begin
      rule consumeit;
	 mrFifo.deq();
      endrule
      rule produceit;
	 hdmiGen.request.put(64'hff0000);
      endrule
   end
   else begin
       SyncFIFOIfc#(Bit#(64)) synchronizer <- mkSyncFIFO(32, defaultClock, defaultReset, hdmi_clock);
       rule doGet;
          let v = mrFifo.first();
	  mrFifo.deq();
	  synchronizer.enq(v);
       endrule
       rule doPut;
          let v = synchronizer.first;
	  synchronizer.deq;
	  hdmiGen.request.put(v);
       endrule
   end

   FIFOF#(Bool) vsyncFifo <- mkFIFOF();
   rule vsyncrule if (vsyncPulse.pulse());
      if (vsyncFifo.notFull())
	 vsyncFifo.enq(True);
   endrule
   Reg#(Bit#(32)) transferCount <- mkReg(0);

   rule startTransfer if (referenceReg matches tagged Valid .reference);
      memreadEngine.start(reference, 0, (1080*1920)*4, 64);
      //hdmiDisplayIndication.transferStarted(transferCount);
      transferCount <= transferCount + 1;
      //vsyncFifo.deq();
   endrule

   rule finishTransferRule;
      let b <- memreadEngine.finish();
   endrule

    rule bozobit_rule;
        bozobit <= ~bozobit;
    endrule

    interface HdmiDisplayRequest displayRequest;
	method Action startFrameBuffer0(Int#(32) base);
	    $display("startFrameBuffer %h", base);
            referenceReg <= tagged Valid truncate(pack(base));
	    hdmiGen.control.setTestPattern(0);
	endmethod
    endinterface: displayRequest

    interface ObjectReadClient dmaClient = memreadEngine.dmaClient;
    interface HDMI hdmi = hdmiGen.hdmi;
    interface HdmiInternalRequest internalRequest = hdmiGen.control;
    interface XADC xadc;
        method Bit#(4) gpio;
            return { bozobit, hdmiGen.hdmi.vsync,
                hdmiGen.hdmi.data[8], hdmiGen.hdmi.data[0]};
                //hdmiGen.hdmi.hsync, hdmi_de};
        endmethod
    endinterface: xadc
endmodule