add completion buffer to fix out of order read acks

// 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 FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;
import StmtFSM::*;
import ClientServer::*;

import PortalMemory::*;
import Dma::*;
import GetPutF::*;
import CompletionBuffer::*;

import RingTypes::*;
import RingBuffer::*;
import CopyEngine::*;
import EchoEngine::*;
import NopEngine::*;

interface RingRequest;
   method Action set(Bit#(1) cmd, Bit#(3) regist, Bit#(64) addr);
   method Action setCmdLast(Bit#(64) addr);
   method Action setCmdFirst(Bit#(64) addr);
   method Action setStatusLast(Bit#(64) addr);
   method Action setStatusFirst(Bit#(64) addr);
   method Action get(Bit#(1) cmd, Bit#(3) regist);
   method Action hwenable(Bit#(1) en);
   method Action doCommandIndirect(Bit#(64) pointer, Bit#(64) addr);
   method Action doCommandImmediate(Bit#(64) data);
endinterface

interface RingIndication;
   method Action setResult(Bit#(1) cmd, Bit#(3) regist, Bit#(64) addr);
   method Action getResult(Bit#(1) cmd, Bit#(3) regist, Bit#(64) addr);
endinterface

module mkRingRequest#(RingIndication indication,
		      DmaReadServer#(64) dma_read_chan,
		      DmaWriteServer#(64) dma_write_chan,
		      DmaReadServer#(64) cmd_read_chan,
		      DmaWriteServer#(64) status_write_chan )(RingRequest);

   ServerF#(Bit#(64), Bit#(64)) copyEngine <- mkCopyEngine(dma_read_chan, dma_write_chan);
   ServerF#(Bit#(64), Bit#(64)) nopEngine <- mkNopEngine();
   ServerF#(Bit#(64), Bit#(64)) echoEngine <- mkEchoEngine();
   CompletionBuffer#(4,void) fetchComplete <- mkCompletionBuffer;

   RingBuffer cmdRing <- mkRingBuffer;
   RingBuffer statusRing <- mkRingBuffer;
   Reg#(Bool) hwenabled <- mkReg(False);
   Reg#(Bool) cmdBusy <- mkReg(False);
   Reg#(DmaData#(64)) cmd <- mkReg(DmaData{data:0, tag:0});
   Reg#(Bit#(4)) ii <- mkReg(0);
   Reg#(Bit#(4)) respCtr <- mkReg(0);
   Reg#(Bit#(4)) dispCtr <- mkReg(0);
   Reg#(Bit#(6)) statusTag <- mkReg(0);
   Reg#(Bool) cmdFetchEn <- mkReg(False);

   let engineselect = cmd.data[63:56];
   function ServerF#(Bit#(64), Bit#(64)) cmdifc();
      if (engineselect == zeroExtend(pack(CmdNOP)))
	 return nopEngine;
      else if (engineselect == zeroExtend(pack(CmdCOPY)))
	 return copyEngine;
      else if (engineselect == zeroExtend(pack(CmdECHO)))
	 return echoEngine;
      else
	 return nopEngine;
   endfunction

   Stmt cmdFetch =
   seq
      $display("cmdFetch FSM TOP");
      while (True)
	 seq
	    if (hwenabled)
	       seq
	       if (cmdRing.notEmpty())
		  action
		     let ct <-  fetchComplete.reserve.get();
		     $display ("cmdFetch handle=%h address=%h burst=%h tag=%h",
		      cmdRing.mempointer, cmdRing.bufferlastfetch, 8, ct );
		     cmd_read_chan.readReq.put(
			DmaRequest{pointer: cmdRing.mempointer,
			   offset: cmdRing.bufferlastfetch, burstLen: 8, tag: zeroExtend(unpack(ct))});
		     cmdRing.popfetch();
		  endaction
	       endseq
	 endseq
   endseq;

   Stmt cmdDispatch =
   seq
      while (True) seq
	 $display("cmdDispatch FSM TOP");
	 seq
	    action
	       let rv <- cmd_read_chan.readData.get();
	       cmd <= rv;
	       fetchComplete.complete.put(tuple2(pack(truncate(rv.tag)),?));
	    endaction
	    // wait a cycle so cmd is valid!
	    $display("cmdDispatch 0 tag=%h %h", cmd.tag, cmd.data);
	    cmdifc.request.put(cmd.data);
	 endseq
	 for (dispCtr <= 1; dispCtr < 8; dispCtr <= dispCtr + 1)
	    action
	       let rv <- cmd_read_chan.readData.get();
	       $display("  cmdDispatch %h tag=%h %h", dispCtr, rv.tag, rv.data);
	       cmdifc.request.put(rv.data);
	    endaction
      endseq
   endseq;

   rule finishCmdReads;
      let v <- fetchComplete.drain.get();
      cmdRing.popack();
      $display("pop ack");
   endrule

   Stmt responseArbiter =
   seq
      while(True) seq
	 if (statusRing.notFull() && copyEngine.response.notEmpty())
	    seq
//	       $display("responseArbiter copyEngine completion");
//	       $display("status write handle=%d address=%h burst=%h tag=%h",
//		  statusRing.mempointer, statusRing.bufferfirst, 8, statusTag);
	       status_write_chan.writeReq.put(
		  DmaRequest{pointer: statusRing.mempointer,
		     offset: statusRing.bufferfirst, burstLen: 8, tag: statusTag});
	       for (respCtr <= 0; respCtr < 8; respCtr <= respCtr + 1)
		  action
		     let rv <- copyEngine.response.get();
		     status_write_chan.writeData.put(DmaData{data: rv, tag: statusTag});
		  endaction
	       statusRing.push();
	       statusTag <= statusTag + 1;
	    endseq

	 if (statusRing.notFull() && echoEngine.response.notEmpty())
	    seq
//	       $display("responseArbiter echoEngine completion");
//	       $display("status write handle=%d address=%h burst=%h tag=%h",
//		  statusRing.mempointer, statusRing.bufferfirst, 8, statusTag);
	       status_write_chan.writeReq.put(
		  DmaRequest{pointer: statusRing.mempointer,
		     offset: statusRing.bufferfirst, burstLen: 8, tag: statusTag});
	       for (respCtr <= 0; respCtr < 8; respCtr <= respCtr + 1)
		  action
		     let rv <- echoEngine.response.get();
		     status_write_chan.writeData.put(DmaData{data: rv, tag: statusTag});
		  endaction
	       statusRing.push();
	       statusTag <= statusTag + 1;
	    endseq

      endseq
   endseq;

   rule writeAck;
      let tag <- status_write_chan.writeDone.get();
      //$display("status write done tag=%h", tag);
   endrule

   mkAutoFSM (cmdFetch);
   mkAutoFSM (cmdDispatch);
   mkAutoFSM (responseArbiter);

      // to start a command, doCommand fires off a memory read to the
      // specified address. when it comes back, the doCommandRule will
      // handle it
      method Action doCommandIndirect(Bit#(64) pointer, Bit#(64) addr);
	 let ct <- fetchComplete.reserve.get();
	 cmd_read_chan.readReq.put(
				   DmaRequest{pointer: truncate(pointer),
	 offset: truncate(addr), burstLen: 8, tag: zeroExtend(unpack(ct))});
      endmethod

      method Action doCommandImmediate(Bit#(64) data);
      	 $display("doCommandImmediate %h", data);
      endmethod

      method Action set(Bit#(1) _cmd, Bit#(3) regist, Bit#(64) addr);
	 if (_cmd == 0)
	    cmdRing.configifc.set(regist, truncate(addr));
	 else
	    statusRing.configifc.set(regist, truncate(addr));
	 indication.setResult(_cmd, regist, addr);
      endmethod

      method Action setCmdFirst(Bit#(64) addr);
	    cmdRing.configifc.setFirst(truncate(addr));
      endmethod

      method Action setStatusFirst(Bit#(64) addr);
	    statusRing.configifc.setFirst(truncate(addr));
      endmethod

      method Action setCmdLast(Bit#(64) addr);
	    cmdRing.configifc.setLast(truncate(addr));
      endmethod

      method Action setStatusLast(Bit#(64) addr);
	    statusRing.configifc.setLast(truncate(addr));
      endmethod

      method Action get(Bit#(1) _cmd, Bit#(3) regist);
	 if (_cmd == 0)
	    indication.getResult(0, regist,
	       zeroExtend(cmdRing.configifc.get(regist)));
	 else
	    indication.getResult(1, regist,
	       zeroExtend(statusRing.configifc.get(regist)));
      endmethod

      method Action hwenable(Bit#(1) en);
	 $display ("hwenable set to %h", en);
	 hwenabled <= en == 1;
      endmethod

endmodule

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

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

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

import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;
import StmtFSM::*;
import ClientServer::*;

import PortalMemory::*;
import Dma::*;
import GetPutF::*;
import CompletionBuffer::*;

import RingTypes::*;
import RingBuffer::*;
import CopyEngine::*;
import EchoEngine::*;
import NopEngine::*;

interface RingRequest;
   method Action set(Bit#(1) cmd, Bit#(3) regist, Bit#(64) addr);
   method Action setCmdLast(Bit#(64) addr);
   method Action setCmdFirst(Bit#(64) addr);
   method Action setStatusLast(Bit#(64) addr);
   method Action setStatusFirst(Bit#(64) addr);
   method Action get(Bit#(1) cmd, Bit#(3) regist);
   method Action hwenable(Bit#(1) en);
   method Action doCommandIndirect(Bit#(64) pointer, Bit#(64) addr);
   method Action doCommandImmediate(Bit#(64) data);
endinterface

interface RingIndication;
   method Action setResult(Bit#(1) cmd, Bit#(3) regist, Bit#(64) addr);
   method Action getResult(Bit#(1) cmd, Bit#(3) regist, Bit#(64) addr);
endinterface

module mkRingRequest#(RingIndication indication,
		      DmaReadServer#(64) dma_read_chan,
		      DmaWriteServer#(64) dma_write_chan,
		      DmaReadServer#(64) cmd_read_chan,
		      DmaWriteServer#(64) status_write_chan )(RingRequest);

   ServerF#(Bit#(64), Bit#(64)) copyEngine <- mkCopyEngine(dma_read_chan, dma_write_chan);
   ServerF#(Bit#(64), Bit#(64)) nopEngine <- mkNopEngine();
   ServerF#(Bit#(64), Bit#(64)) echoEngine <- mkEchoEngine();
   CompletionBuffer#(4,Void) fetchComplete <mkCompletionBuffer;

   RingBuffer cmdRing <- mkRingBuffer;
   RingBuffer statusRing <- mkRingBuffer;
   Reg#(Bool) hwenabled <- mkReg(False);
   Reg#(Bool) cmdBusy <- mkReg(False);
   Reg#(DmaData#(64)) cmd <- mkReg(DmaData{data:0, tag:0});
   Reg#(Bit#(4)) ii <- mkReg(0);
   Reg#(Bit#(4)) respCtr <- mkReg(0);
   Reg#(Bit#(4)) dispCtr <- mkReg(0);
   Reg#(Bit#(6)) statusTag <- mkReg(0);
   Reg#(Bool) cmdFetchEn <- mkReg(False);

   let engineselect = cmd.data[63:56];
   function ServerF#(Bit#(64), Bit#(64)) cmdifc();
      if (engineselect == zeroExtend(pack(CmdNOP)))
	 return nopEngine;
      else if (engineselect == zeroExtend(pack(CmdCOPY)))
	 return copyEngine;
      else if (engineselect == zeroExtend(pack(CmdECHO)))
	 return echoEngine;
      else
	 return nopEngine;
   endfunction

   Stmt cmdFetch =
   seq
      $display("cmdFetch FSM TOP");
      while (True)
	 seq
	    if (hwenabled)
	       seq
	       if (cmdRing.notEmpty())
		  seq
		     $display ("cmdFetch handle=%h address=%h burst=%h tag=%h",
		      cmdRing.mempointer, cmdRing.bufferlast, 8, cmdFetchTag);
		     cmd_read_chan.readReq.put(
			DmaRequest{pointer: cmdRing.mempointer,
			   offset: cmdRing.bufferlast, burstLen: 8, tag: cmdFetchTagfetchCompletion.reserve()});
		     cmdRing.popfetch();
		  endseq
	       endseq
	 endseq
   endseq;

   Stmt cmdDispatch =
   seq
      while (True) seq
	 $display("cmdDispatch FSM TOP");
	 seq
	    action
	       let rv <- cmd_read_chan.readData.get();
	       cmd <= rv;
	       completionBuffer.complete(Tuple2(rv.tag,void));
	    endaction
	    // wait a cycle so cmd is valid!
//	    $display("cmdDispatch 0 tag=%h %h", cmd.tag, cmd.data);
	    cmdifc.request.put(cmd.data);
	 endseq
	 for (dispCtr <= 1; dispCtr < 8; dispCtr <= dispCtr + 1)
	    action
	       let rv <- cmd_read_chan.readData.get();
//	       $display("  cmdDispatch %h tag=%h %h", dispCtr, rv.tag, rv.data);
	       cmdifc.request.put(rv.data);
	    endaction
      endseq
   endseq;

   rule finishCmdReads;
      let v <- completionBuffer.drain.get();
      cmdRing.popAck();
      $display("pop ack");
   endrule

   Stmt responseArbiter =
   seq
      while(True) seq
	 if (statusRing.notFull() && copyEngine.response.notEmpty())
	    seq
//	       $display("responseArbiter copyEngine completion");
//	       $display("status write handle=%d address=%h burst=%h tag=%h",
//		  statusRing.mempointer, statusRing.bufferfirst, 8, statusTag);
	       status_write_chan.writeReq.put(
		  DmaRequest{pointer: statusRing.mempointer,
		     offset: statusRing.bufferfirst, burstLen: 8, tag: statusTag});
	       for (respCtr <= 0; respCtr < 8; respCtr <= respCtr + 1)
		  action
		     let rv <- copyEngine.response.get();
		     status_write_chan.writeData.put(DmaData{data: rv, tag: statusTag});
		  endaction
	       statusRing.push(64);
	       statusTag <= statusTag + 1;
	    endseq

	 if (statusRing.notFull() && echoEngine.response.notEmpty())
	    seq
//	       $display("responseArbiter echoEngine completion");
//	       $display("status write handle=%d address=%h burst=%h tag=%h",
//		  statusRing.mempointer, statusRing.bufferfirst, 8, statusTag);
	       status_write_chan.writeReq.put(
		  DmaRequest{pointer: statusRing.mempointer,
		     offset: statusRing.bufferfirst, burstLen: 8, tag: statusTag});
	       for (respCtr <= 0; respCtr < 8; respCtr <= respCtr + 1)
		  action
		     let rv <- echoEngine.response.get();
		     status_write_chan.writeData.put(DmaData{data: rv, tag: statusTag});
		  endaction
	       statusRing.push(64);
	       statusTag <= statusTag + 1;
	    endseq

      endseq
   endseq;

   rule writeAck;
      let tag <- status_write_chan.writeDone.get();
      //$display("status write done tag=%h", tag);
   endrule

   mkAutoFSM (cmdFetch);
   mkAutoFSM (cmdDispatch);
   mkAutoFSM (responseArbiter);

      // to start a command, doCommand fires off a memory read to the
      // specified address. when it comes back, the doCommandRule will
      // handle it
      method Action doCommandIndirect(Bit#(64) pointer, Bit#(64) addr);
	 cmd_read_chan.readReq.put(
				   DmaRequest{pointer: truncate(pointer),
	 offset: truncate(addr), burstLen: 8, tag: cmdFetchTag});
   	 cmdFetchTag <= cmdFetchTag + 1;
      endmethod

      method Action doCommandImmediate(Bit#(64) data);
      	 $display("doCommandImmediate %h", data);
      endmethod

      method Action set(Bit#(1) _cmd, Bit#(3) regist, Bit#(64) addr);
	 if (_cmd == 0)
	    cmdRing.configifc.set(regist, truncate(addr));
	 else
	    statusRing.configifc.set(regist, truncate(addr));
	 indication.setResult(_cmd, regist, addr);
      endmethod

      method Action setCmdFirst(Bit#(64) addr);
	    cmdRing.configifc.setFirst(truncate(addr));
      endmethod

      method Action setStatusFirst(Bit#(64) addr);
	    statusRing.configifc.setFirst(truncate(addr));
      endmethod

      method Action setCmdLast(Bit#(64) addr);
	    cmdRing.configifc.setLast(truncate(addr));
      endmethod

      method Action setStatusLast(Bit#(64) addr);
	    statusRing.configifc.setLast(truncate(addr));
      endmethod

      method Action get(Bit#(1) _cmd, Bit#(3) regist);
	 if (_cmd == 0)
	    indication.getResult(0, regist,
	       zeroExtend(cmdRing.configifc.get(regist)));
	 else
	    indication.getResult(1, regist,
	       zeroExtend(statusRing.configifc.get(regist)));
      endmethod

      method Action hwenable(Bit#(1) en);
	 $display ("hwenable set to %h", en);
	 hwenabled <= en == 1;
      endmethod

endmodule


fix widths

fix widths

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

// Idea:
// BlueScopeEvent allows one to record values only when they change,
// along with timestamps of the time of change.
// The input is some collection of bits, in some clock domain
// A trigger signal is generated when the input, anded with a trigger
// mask, changes from clock to clock
// Whenever the trigger happens, the new value and a timestamp are saved
// into a SyncBRAMFiFo.  The output of the FiFo is in the system clock
// domain, as is a counter value that says how many events have happened.
// One can request a DMA write from the FiFo to system memory, at a given
// address, for a given count.
// One can reset the fifo and counter.

import Clocks::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;
import Connectable::*;

import MemTypes::*;
import MemwriteEngine::*;
import ClientServer::*;

// This version records timestamped events
interface BlueScopeEventRequest;
   // emtpy fifo and reset event counter
   method Action doReset();
   // changes in bits selected by the mask will trigger events
   method Action setTriggerMask(Bit#(32) mask);
   // generate a report pointer indication
   method Action getCounterValue();
   // copy from fifo to memory
   method Action startDma(Bit#(32) pointer, Bit#(32) len);
endinterface

interface BlueScopeEventIndication;
   // report number of events since last reset,
   method Action counterValue(Bit#(32) v);
   // dma operation complete
   method Action dmaDone();
endinterface

interface BlueScopeEvent#(numeric type dataWidth);
   method Action dataIn(Bit#(dataWidth) d);
   interface BlueScopeRequest requestIfc;
   interface MemWriteClient#(Bits#(64)) writeClient;
endinterface

module mkBlueScopeEvent#(Integer samples, BlueScopeIndication indication)(BlueScopeEvent#(dataWidth))
   provisos(Add#(0,dataWidth,32));

   let clk <- exposeCurrentClock;
   let rst <- exposeCurrentReset;
   let rv  <- mkSyncBlueScopeEvent(samples, indication, clk, rst, clk,rst);
   return rv;
endmodule

// sClk is the source? sample? side, input samples come here
// dClk is the destination? dma? side, this will generally be the system clock

module mkSyncBlueScopeEvent#(Integer samples, BlueScopeEventIndication indication, Clock sClk, Reset sRst, Clock dClk, Reset dRst)(BlueScopeEvent#(dataWidth))
   provisos(Add#(0, dataWidth, 32));

   // the idea here is that we let events pour into the Bram continually,
   // then reset them before starting an acquisition interval
   // we reset both halves of the fifo, not clear that is needed
   MakeResetIfc sFifoReset = mkReset(2, True, sClk);
   MakeResetIfc dFifoReset = mkReset(2, True, dClk);
   SyncFIFOIfc#(Bit#(64)) dfifo <- mkSyncBRAMFIFO(samples, sClk, sFifoReset.new_rst, dClk, dFifoReset.new_rst);
   // mask reg is set from a request in the dClk domain but used in the
   // sClk domain to determine triggering
   Reg#(Bit#(dataWidth))       maskReg <- mkSyncReg(0, dClk, dRst, sClk);
   // countReg counts accumulated samples
   Reg#(Bit#(32)) countReg <- mkReg(0, clocked by sClk, reset_by sRst);
   // countSyncReg repeats that value into the dClk domain
   Reg#(Bit#(32)) countSyncReg <- mkSyncReg(0 sClk, sFifoReset.new_rst);
   // oldData is used in the sample domain, to save the previous value
   Reg#(Bit#(dataWidth)) olddata <- mkReg(0, clocked_by sClk, reset_by sRst);

   MemwriteEngine#(Bits#(64), 2) mwriter <- mkMemwriteEngine;

   (* descending_urgency = "resetState, startState" *)

   mkConnection(toGet(dfifo), toPut(mwriter.dataPipes[0]));

   rule writeDone;
      let tag <- mwriter.writeServers[0].response.get();
      indication.done;
   endrule

   method Action dataIn(Bit#(dataWidth) data;// if (stateReg != Idle);
      let c = countReg;
      if (maskReg & (data ^ olddata))
         begin
	 if (dfifo.notFull())
	    begin
	       dfifo.enq({data, c});
	       countReg <= c + 1;
               countSyncReg <= c + 1;
	    end
	 else
	    $display("bluescope.stall c=%d", c);
	 end
      end
      olddata <= data;
   endmethod

   interface BlueScopeEventRequest requestIfc;

      method Action doReset();
	 sFifoReset.assertReset();
	 dFifoReset.assertReset();
      endmethod

      method Action setTriggerMask(Bit#(32) mask);
	 maskReg <= truncate(mask);
      endmethod

      method Action getCounterValue();
         indication.counterValue(countSyncReg);
      endmethod

      method Action startDma(Bit#(32) pointer, Bit#(32) len);
	 mwriter.writeServers[0].request.put(MemengineCmd {sglId: pointer, base: 0, burstLen: 8*fromInteger(valueOf(TDiv#(dataWidth,8))), len: len});
      endmethod

   endinterface
   interface writeClient = mwriter.dmaClient;
endmodule

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

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

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

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

// Idea:
// BlueScopeEvent allows one to record values only when they change,
// along with timestamps of the time of change.
// The input is some collection of bits, in some clock domain
// A trigger signal is generated when the input, anded with a trigger
// mask, changes from clock to clock
// Whenever the trigger happens, the new value and a timestamp are saved
// into a SyncBRAMFiFo.  The output of the FiFo is in the system clock
// domain, as is a counter value that says how many events have happened.
// One can request a DMA write from the FiFo to system memory, at a given
// address, for a given count.
// One can reset the fifo and counter.

import Clocks::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;
import Connectable::*;

import MemTypes::*;
import MemwriteEngine::*;
import ClientServer::*;

// This version records timestamped events
interface BlueScopeEventRequest;
   // emtpy fifo and reset event counter
   method Action doReset();
   // changes in bits selected by the mask will trigger events
   method Action setTriggerMask(Bit#(32) mask);
   // generate a report pointer indication
   method Action getCounterValue();
   // copy from fifo to memory
   method Action startDma(Bit#(32) pointer, Bit#(32) len);
endinterface

interface BlueScopeEventIndication;
   // report number of events since last reset,
   method Action counterValue(Bit#(32) v);
   // dma operation complete
   method Action dmaDone();
endinterface

interface BlueScopeEvent#(numeric type dataWidth);
   method Action dataIn(Bit#(dataWidth) d);
   interface BlueScopeRequest requestIfc;
   interface MemWriteClient#(dataWidth) writeClient;
endinterface

module mkBlueScopeEvent#(Integer samples, BlueScopeIndication indication)(BlueScope#(dataWidth))
   provisos(Add#(0,dataWidth,32));

   let clk <- exposeCurrentClock;
   let rst <- exposeCurrentReset;
   let rv  <- mkSyncBlueScopeEvent(samples, indication, clk, rst, clk,rst);
   return rv;
endmodule

// sClk is the source? sample? side, input samples come here
// dClk is the destination? dma? side, this will generally be the system clock

module mkSyncBlueScopeEvent#(Integer samples, BlueScopeEventIndication indication, Clock sClk, Reset sRst, Clock dClk, Reset dRst)(BlueScopeEvent#(dataWidth))
   provisos(Add#(0, dataWidth, 32));

   // the idea here is that we let events pour into the Bram continually,
   // then reset them before starting an acquisition interval
   // we reset both halves of the fifo, not clear that is needed
   MakeResetIfc sFifoReset = mkReset(2, True, sClk);
   MakeResetIfc dFifoReset = mkReset(2, True, dClk);
   SyncFIFOIfc#(Bit#(64)) dfifo <- mkSyncBRAMFIFO(samples, sClk, sFifoReset.new_rst, dClk, dFifoReset.new_rst);
   // mask reg is set from a request in the dClk domain but used in the
   // sClk domain to determine triggering
   Reg#(Bit#(dataWidth))       maskReg <- mkSyncReg(0, dClk, dRst, sClk);
   // countReg counts accumulated samples
   Reg#(Bit#(32)) countReg <- mkReg(0, clocked by sClk, reset_by sRst);
   // countSyncReg repeats that value into the dClk domain
   Reg#(Bit#(32)) countSyncReg <- mkSyncReg(0 sClk, sFifoReset.new_rst);
   // oldData is used in the sample domain, to save the previous value
   Reg#(Bit#(dataWidth)) olddata <- mkReg(0, clocked_by sClk, reset_by sRst);

   MemwriteEngine#(dataWidth, 2) mwriter <- mkMemwriteEngine;

   (* descending_urgency = "resetState, startState" *)

   mkConnection(toGet(dfifo), toPut(mwriter.dataPipes[0]));

   rule writeDone;
      let tag <- mwriter.writeServers[0].response.get();
      indication.done;
   endrule

   method Action dataIn(Bit#(dataWidth) data;// if (stateReg != Idle);
      let c = countReg;
      if (maskReg & (data ^ olddata))
         begin
	 if (dfifo.notFull())
	    begin
	       dfifo.enq({data, c});
	       countReg <= c + 1;
               countSyncReg <= c + 1;
	    end
	 else
	    $display("bluescope.stall c=%d", c);
	 end
      end
      olddata <= data;
   endmethod

   interface BlueScopeEventRequest requestIfc;

      method Action doReset();
	 sFifoReset.assertReset();
	 dFifoReset.assertReset();
      endmethod

      method Action setTriggerMask(Bit#(32) mask);
	 maskReg <= truncate(mask);
      endmethod

      method Action getCounterValue();
         indication.counterValue(countSyncReg);
      endmethod

      method Action startDma(Bit#(32) pointer, Bit#(32) len);
	 mwriter.writeServers[0].request.put(MemengineCmd {sglId: pointer, base: 0, burstLen: 8*fromInteger(valueOf(TDiv#(dataWidth,8))), len: len});
      endmethod

   endinterface
   interface writeClient = mwriter.dmaClient;
endmodule


eliminated some warnings about shadowing

// 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 Clocks::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;

import AxiDMA::*;
import ClientServer::*;

interface BlueScopeIndication;
   method Action triggerFired();
//   method Action reportStateDbg(Bit#(64) mask, Bit#(64) value);
endinterface

interface BlueScopeRequest;
   method Action start();
   method Action reset();
   method Action setTriggerMask(Bit#(64) mask);
   method Action setTriggerValue(Bit#(64) value);
// accesses dataIn clock domain without synchronizer
//   method Action getStateDbg();
endinterface

interface BlueScopeInternal;
   method Action dataIn(Bit#(64) d, Bit#(64) t);
   interface BlueScopeRequest requestIfc;
endinterface

typedef enum { Idle, Enabled, Triggered } State deriving (Bits,Eq);

module mkBlueScopeInternal#(Integer samples, WriteChan wchan, BlueScopeIndication indication)(BlueScopeInternal);
   let clk <- exposeCurrentClock;
   let rst <- exposeCurrentReset;
   let rv  <- mkSyncBlueScopeInternal(samples, wchan, indication, clk, rst, clk,rst);
   return rv;
endmodule

module mkSyncBlueScopeInternal#(Integer samples, WriteChan wchan, BlueScopeIndication indication, Clock sClk, Reset sRst, Clock dClk, Reset dRst)(BlueScopeInternal);
   SyncFIFOIfc#(Bit#(64)) dfifo <- mkSyncBRAMFIFO(samples, sClk, sRst, dClk, dRst);
   Reg#(Bit#(64))    maskReg <- mkSyncReg(0, dClk, dRst, sClk);
   Reg#(Bit#(64))   valueReg <- mkSyncReg(0, dClk, dRst, sClk);
   Reg#(Bit#(1))          triggeredReg <- mkReg(0,    clocked_by sClk, reset_by sRst);
   Reg#(State)                stateReg <- mkReg(Idle, clocked_by sClk, reset_by sRst);
   Reg#(Bit#(32))             countReg <- mkReg(0,    clocked_by sClk, reset_by sRst);
   //FIFOF#(void)                  tfifo <- mkFIFOF(    clocked_by sClk, reset_by sRst);
   SyncPulseIfc             startPulse <- mkSyncPulse(dClk, dRst, sClk);
   SyncPulseIfc             resetPulse <- mkSyncPulse(dClk, dRst, sClk);
   SyncPulseIfc         triggeredPulse <- mkSyncPulse(sClk, sRst, dClk);

   (* descending_urgency = "resetState, startState" *)
   rule resetState if (resetPulse.pulse);
      stateReg <= Idle;
      countReg <= 0;
   endrule

   rule startState if (startPulse.pulse && !resetPulse.pulse);
      stateReg <= Enabled;
   endrule

   rule writeReq if (dfifo.notEmpty);
      wchan.writeReq.put(?);
   endrule

   rule  writeData;
      dfifo.deq;
      wchan.writeData.put(dfifo.first);
   endrule

   rule writeDone;
      wchan.writeDone.get;
   endrule

   rule triggerRule if (triggeredPulse.pulse);
      indication.triggerFired;
   endrule

   method Action dataIn(Bit#(64) data, Bit#(64) trigger) if (stateReg != Idle);
      let e = False;
      let s = stateReg;
      let c = countReg;
      let t = False;

      // if 'Enabled', we can transition to 'Triggered'
      if (s == Enabled && ((trigger & maskReg) == (valueReg & maskReg)))
	 begin
	    s = Triggered;
	    e = True;
	    c = c+1;
	    t = True;
         end
      // if 'Triggered', we can transition to 'Enabled'
      else if (s == Triggered && c == fromInteger(samples))
	 begin
	    s = Enabled;
	    e = False;
	    c = 0;
	    t = False;
	 end
      // if 'Triggered', we can remain in 'Triggered'
      else if (s == Triggered && c <= fromInteger(samples))
	 begin
	    s = Triggered;
	    e = True;
	    c = c+1;
	    t = False;
	 end
      // else we must be enabled waiting for a Trigger
      else
	 begin
	    s = s;
	    e = e;
	    c = c;
	    t = t;
	 end

      if(e && dfifo.notFull)
	 dfifo.enq(data);
      if(t)
	 triggeredPulse.send();
      countReg <= c;
      stateReg <= s;
   endmethod

   interface BlueScopeRequest requestIfc;
      method Action start();
	 startPulse.send();
      endmethod

      method Action reset();
          resetPulse.send();
      endmethod

      method Action setTriggerMask(Bit#(64) mask);
	 maskReg <= mask;
      endmethod

      method Action setTriggerValue(Bit#(64) value);
	 valueReg <= value;
      endmethod

// accesses dataIn clock domain without synchronizer
//      method Action getStateDbg();
//	 indication.reportStateDbg(maskReg,valueReg);
//      endmethod

   endinterface

endmodule

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

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

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

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

import Clocks::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;

import AxiDMA::*;
import ClientServer::*;

interface BlueScopeIndication;
   method Action triggerFired();
//   method Action reportStateDbg(Bit#(64) mask, Bit#(64) value);
endinterface

interface BlueScopeRequest;
   method Action start();
   method Action reset();
   method Action setTriggerMask(Bit#(64) mask);
   method Action setTriggerValue(Bit#(64) value);
// accesses dataIn clock domain without synchronizer
//   method Action getStateDbg();
endinterface

interface BlueScopeInternal;
   method Action dataIn(Bit#(64) d, Bit#(64) t);
   interface BlueScopeRequest requestIfc;
endinterface

typedef enum { Idle, Enabled, Triggered } State deriving (Bits,Eq);

module mkBlueScopeInternal#(Integer samples, WriteChan wchan, BlueScopeIndication indication)(BlueScopeInternal);
   let clk <- exposeCurrentClock;
   let rst <- exposeCurrentReset;
   let rv  <- mkSyncBlueScopeInternal(samples, wchan, indication, clk, rst, clk,rst);
   return rv;
endmodule

module mkSyncBlueScopeInternal#(Integer samples, WriteChan wchan, BlueScopeIndication indication, Clock sClk, Reset sRst, Clock dClk, Reset dRst)(BlueScopeInternal);
   SyncFIFOIfc#(Bit#(64)) dfifo <- mkSyncBRAMFIFO(samples, sClk, sRst, dClk, dRst);
   Reg#(Bit#(64))    maskReg <- mkSyncReg(0, dClk, dRst, sClk);
   Reg#(Bit#(64))   valueReg <- mkSyncReg(0, dClk, dRst, sClk);
   Reg#(Bit#(1))          triggeredReg <- mkReg(0,    clocked_by sClk, reset_by sRst);
   Reg#(State)                stateReg <- mkReg(Idle, clocked_by sClk, reset_by sRst);
   Reg#(Bit#(32))             countReg <- mkReg(0,    clocked_by sClk, reset_by sRst);
   //FIFOF#(void)                  tfifo <- mkFIFOF(    clocked_by sClk, reset_by sRst);
   SyncPulseIfc             startPulse <- mkSyncPulse(dClk, dRst, sClk);
   SyncPulseIfc             resetPulse <- mkSyncPulse(dClk, dRst, sClk);
   SyncPulseIfc         triggeredPulse <- mkSyncPulse(sClk, sRst, dClk);

   rule resetState if (resetPulse.pulse);
      stateReg <= Idle;
      countReg <= 0;
   endrule

   rule startState if (startPulse.pulse);
      stateReg <= Enabled;
   endrule
   rule writeReq if (dfifo.notEmpty);
      wchan.writeReq.put(?);
   endrule

   rule  writeData;
      dfifo.deq;
      wchan.writeData.put(dfifo.first);
   endrule

   rule writeDone;
      wchan.writeDone.get;
   endrule

   rule triggerRule if (triggeredPulse.pulse);
      indication.triggerFired;
   endrule

   method Action dataIn(Bit#(64) data, Bit#(64) trigger) if (stateReg != Idle);
      let e = False;
      let s = stateReg;
      let c = countReg;
      let t = False;

      // if 'Enabled', we can transition to 'Triggered'
      if (s == Enabled && ((trigger & maskReg) == (valueReg & maskReg)))
	 begin
	    s = Triggered;
	    e = True;
	    c = c+1;
	    t = True;
         end
      // if 'Triggered', we can transition to 'Enabled'
      else if (s == Triggered && c == fromInteger(samples))
	 begin
	    s = Enabled;
	    e = False;
	    c = 0;
	    t = False;
	 end
      // if 'Triggered', we can remain in 'Triggered'
      else if (s == Triggered && c <= fromInteger(samples))
	 begin
	    s = Triggered;
	    e = True;
	    c = c+1;
	    t = False;
	 end
      // else we must be enabled waiting for a Trigger
      else
	 begin
	    s = s;
	    e = e;
	    c = c;
	    t = t;
	 end

      if(e && dfifo.notFull)
	 dfifo.enq(data);
      if(t)
	 triggeredPulse.send();
      countReg <= c;
      stateReg <= s;
   endmethod

   interface BlueScopeRequest requestIfc;
      method Action start();
	 startPulse.send();
      endmethod

      method Action reset();
          resetPulse.send();
      endmethod

      method Action setTriggerMask(Bit#(64) mask);
	 maskReg <= mask;
      endmethod

      method Action setTriggerValue(Bit#(64) value);
	 valueReg <= value;
      endmethod

// accesses dataIn clock domain without synchronizer
//      method Action getStateDbg();
//	 indication.reportStateDbg(maskReg,valueReg);
//      endmethod

   endinterface

endmodule


updated HdmiDisplay to current request/indication conventions

// Copyright (c) 2012 Nokia, 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 BRAMFIFO::*;
import Clocks::*;
import GetPut::*;
import Connectable::*;

import AxiMasterSlave::*;
import AxiClientServer::*;
import HDMI::*;
import FrameBufferBram::*;
import YUV::*;

interface HdmiControlRequest;
    method Action setPatternReg(Bit#(32) yuv422);
    method Action startFrameBuffer0(Bit#(32) base);
    method Action startFrameBuffer1(Bit#(32) base);

    method Action waitForVsync(Bit#(32) unused);

    method Action hdmiLinesPixels(Bit#(32) value);
    method Action hdmiBlankLinesPixels(Bit#(32) value);
    method Action hdmiStrideBytes(Bit#(32) strideBytes);
    method Action hdmiLineCountMinMax(Bit#(32) value);
    method Action hdmiPixelCountMinMax(Bit#(32) value);
    method Action hdmiSyncWidths(Bit#(32) value);

    method Action beginTranslationTable(Bit#(8) index);
    method Action addTranslationEntry(Bit#(20) address, Bit#(12) length); // shift address and length left 12 bits
endinterface

interface HdmiDisplayRequest;
    interface HdmiControlRequest coreRequest;
    interface Axi3Client#(32,32,4,6) m_axi;
    interface HDMI hdmi;
endinterface

interface HdmiControlIndication;
    method Action vsync(Bit#(64) v);
endinterface

interface HdmiDisplayIndication;
    interface HdmiControlIndication coreIndication;
endinterface

function Put#(item_t) syncFifoToPut( SyncFIFOIfc#(item_t) f);
    return (
        interface Put;
            method Action put (item_t item);
                f.enq(item);
            endmethod
        endinterface
    );
endfunction

module mkHdmiDisplayRequest#(Clock hdmi_clk, HdmiDisplayIndication indication)(HdmiDisplayRequest);

    let busWidthBytes=8;

    Reg#(Bit#(32)) vsyncPulseCountReg <- mkReg(0);
    Reg#(Bit#(32)) frameCountReg <- mkReg(0);

    Reg#(Bool) waitingForVsync <- mkReg(False);
    Reg#(Bool) sendVsyncIndication <- mkReg(False);

    Clock clock <- exposeCurrentClock;
    Reset reset <- exposeCurrentReset;

    Reset hdmi_reset <- mkAsyncReset(2, reset, hdmi_clk);

    Reg#(Bit#(11)) linesReg <- mkReg(1080);
    Reg#(Bit#(12)) pixelsReg <- mkReg(1920);
    Reg#(Bit#(14)) strideBytesReg <- mkReg(1920*4);

    SyncPulseIfc vsyncPulse <- mkSyncHandshake(hdmi_clk, hdmi_reset, clock);
    SyncPulseIfc hsyncPulse <- mkSyncHandshake(hdmi_clk, hdmi_reset, clock);
    SyncFIFOIfc#(HdmiCommand) commandFifo <- mkSyncFIFOFromCC(1, hdmi_clk);

    Reg#(Bit#(8)) segmentIndexReg <- mkReg(0);
    Reg#(Bit#(24)) segmentOffsetReg <- mkReg(0);

    Reg#(Bool) frameBufferEnabled <- mkReg(False);
    FrameBufferBram frameBuffer <- mkFrameBufferBram(hdmi_clk, hdmi_reset);

    HdmiGenerator hdmiGen <- mkHdmiGenerator(clocked_by hdmi_clk, reset_by hdmi_reset,
                                             commandFifo, frameBuffer.buffer,
					     vsyncPulse, hsyncPulse);

    (* descending_urgency = "vsync, hsync" *)
    rule vsync if (vsyncPulse.pulse());
        $display("vsync pulse received %h", frameBufferEnabled);
        vsyncPulseCountReg <= vsyncPulseCountReg + 1;
        if (waitingForVsync)
        begin
            waitingForVsync <= False;
            sendVsyncIndication <= True;
        end
        if (frameBufferEnabled)
        begin
            $display("frame started");
            frameCountReg <= frameCountReg + 1;
            frameBuffer.startFrame();
        end
    endrule
    rule hsync if (hsyncPulse.pulse());
        frameBuffer.startLine();
    endrule

    rule vsyncReceived if (sendVsyncIndication);
        sendVsyncIndication <= False;
        Bit#(64) v = 0;
        v[31:0] = vsyncPulseCountReg;
        v[47:32] = extend(pixelsReg);
        v[63:48] = extend(linesReg);
        indication.coreIndication.vsync(v);
    endrule

    interface HdmiControlRequest coreRequest;
	method Action setPatternReg(Bit#(32) yuv422);
	    commandFifo.enq(tagged PatternColor {yuv422: yuv422});
	endmethod
	method Action hdmiLinesPixels(Bit#(32) value);
	    linesReg <= value[10:0];
	    pixelsReg <= value[27:16];
	    commandFifo.enq(tagged LinesPixels {value: value});
	endmethod
	method Action hdmiStrideBytes(Bit#(32) value);
	    strideBytesReg <= value[13:0];
	endmethod
	method Action hdmiBlankLinesPixels(Bit#(32) value);
	    commandFifo.enq(tagged BlankLinesPixels {value: value});
	endmethod
	method Action hdmiLineCountMinMax(Bit#(32) value);
	    commandFifo.enq(tagged LineCountMinMax {value: value});
	endmethod
	method Action hdmiPixelCountMinMax(Bit#(32) value);
	    commandFifo.enq(tagged PixelCountMinMax {value: value});
	endmethod
	method Action hdmiSyncWidths(Bit#(32) value);
	    commandFifo.enq(tagged SyncWidths {value: value});
	endmethod

	method Action startFrameBuffer0(Bit#(32) base);
	    $display("startFrameBuffer %h", base);
	    frameBufferEnabled <= True;
	    FrameBufferConfig fbc;
	    fbc.base = base;
	    fbc.pixels = pixelsReg;
	    fbc.lines = linesReg;
	    Bit#(14) stridebytes = strideBytesReg;
	    $display("startFrameBuffer lines %d pixels %d bytesperpixel %d stridebytes %d",
		     linesReg, pixelsReg, bytesperpixel, stridebytes);
	    fbc.stridebytes = stridebytes;
	    frameBuffer.configure(fbc);
	    commandFifo.enq(tagged TestPattern {enabled: False});
	    waitingForVsync <= True;
	endmethod

	method Action startFrameBuffer1(Bit#(32) base);
	endmethod

	method Action waitForVsync(Bit#(32) unused);
	    waitingForVsync <= True;
	endmethod

	method Action beginTranslationTable(Bit#(8) index);
	    segmentIndexReg <= index;
	    segmentOffsetReg <= 0;
	endmethod
	method Action addTranslationEntry(Bit#(20) address, Bit#(12) length);
	    frameBuffer.setSgEntry(segmentIndexReg, segmentOffsetReg, address, extend(length));
	    segmentIndexReg <= segmentIndexReg + 1;
	    segmentOffsetReg <= segmentOffsetReg + {length,12'd0};
	endmethod
    endinterface: coreRequest

    interface Axi3Client m_axi = frameBuffer.axi;
    interface HDMI hdmi = hdmiGen.hdmi;
endmodule

// Copyright (c) 2012 Nokia, 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 BRAMFIFO::*;
import Clocks::*;
import GetPut::*;
import Connectable::*;

import AxiMasterSlave::*;
import AxiClientServer::*;
import HDMI::*;
import FrameBufferBram::*;
import YUV::*;

interface HdmiDisplay;
    method Action setPatternReg(Bit#(32) yuv422);
    method Action startFrameBuffer0(Bit#(32) base);
    method Action startFrameBuffer1(Bit#(32) base);

    method Action waitForVsync(Bit#(32) unused);

    method Action hdmiLinesPixels(Bit#(32) value);
    method Action hdmiBlankLinesPixels(Bit#(32) value);
    method Action hdmiStrideBytes(Bit#(32) strideBytes);
    method Action hdmiLineCountMinMax(Bit#(32) value);
    method Action hdmiPixelCountMinMax(Bit#(32) value);
    method Action hdmiSyncWidths(Bit#(32) value);

    method Action beginTranslationTable(Bit#(8) index);
    method Action addTranslationEntry(Bit#(20) address, Bit#(12) length); // shift address and length left 12 bits

    interface Axi3Client#(32,4,6) m_axi;
    interface HDMI hdmi;
endinterface

interface HdmiDisplayIndications;
    method Action vsync(Bit#(64) v);
endinterface

function Put#(item_t) syncFifoToPut( SyncFIFOIfc#(item_t) f);
    return (
        interface Put;
            method Action put (item_t item);
                f.enq(item);
            endmethod
        endinterface
    );
endfunction

module mkHdmiDisplay#(Clock hdmi_clk, HdmiDisplayIndications indications)(HdmiDisplay);

    let busWidthBytes=8;

    Reg#(Bit#(32)) vsyncPulseCountReg <- mkReg(0);
    Reg#(Bit#(32)) frameCountReg <- mkReg(0);

    Reg#(Bool) waitingForVsync <- mkReg(False);
    Reg#(Bool) sendVsyncIndication <- mkReg(False);

    Clock clock <- exposeCurrentClock;
    Reset reset <- exposeCurrentReset;

    Reset hdmi_reset <- mkAsyncReset(2, reset, hdmi_clk);

    Reg#(Bit#(11)) linesReg <- mkReg(1080);
    Reg#(Bit#(12)) pixelsReg <- mkReg(1920);
    Reg#(Bit#(14)) strideBytesReg <- mkReg(1920*4);

    SyncPulseIfc vsyncPulse <- mkSyncHandshake(hdmi_clk, hdmi_reset, clock);
    SyncPulseIfc hsyncPulse <- mkSyncHandshake(hdmi_clk, hdmi_reset, clock);
    SyncFIFOIfc#(HdmiCommand) commandFifo <- mkSyncFIFOFromCC(1, hdmi_clk);

    Reg#(Bit#(8)) segmentIndexReg <- mkReg(0);
    Reg#(Bit#(24)) segmentOffsetReg <- mkReg(0);

    Reg#(Bool) frameBufferEnabled <- mkReg(False);
    FrameBufferBram frameBuffer <- mkFrameBufferBram(hdmi_clk, hdmi_reset);

    HdmiGenerator hdmiGen <- mkHdmiGenerator(clocked_by hdmi_clk, reset_by hdmi_reset,
                                             commandFifo, frameBuffer.buffer,
					     vsyncPulse, hsyncPulse);

    (* descending_urgency = "vsync, hsync" *)
    rule vsync if (vsyncPulse.pulse());
        $display("vsync pulse received %h", frameBufferEnabled);
        vsyncPulseCountReg <= vsyncPulseCountReg + 1;
        if (waitingForVsync)
        begin
            waitingForVsync <= False;
            sendVsyncIndication <= True;
        end
        if (frameBufferEnabled)
        begin
            $display("frame started");
            frameCountReg <= frameCountReg + 1;
            frameBuffer.startFrame();
        end
    endrule
    rule hsync if (hsyncPulse.pulse());
        frameBuffer.startLine();
    endrule

    rule vsyncReceived if (sendVsyncIndication);
        sendVsyncIndication <= False;
        Bit#(64) v = 0;
        v[31:0] = vsyncPulseCountReg;
        v[47:32] = extend(pixelsReg);
        v[63:48] = extend(linesReg);
        indications.vsync(v);
    endrule

    method Action setPatternReg(Bit#(32) yuv422);
        commandFifo.enq(tagged PatternColor {yuv422: yuv422});
    endmethod
    method Action hdmiLinesPixels(Bit#(32) value);
        linesReg <= value[10:0];
        pixelsReg <= value[27:16];
        commandFifo.enq(tagged LinesPixels {value: value});
    endmethod
    method Action hdmiStrideBytes(Bit#(32) value);
        strideBytesReg <= value[13:0];
    endmethod
    method Action hdmiBlankLinesPixels(Bit#(32) value);
        commandFifo.enq(tagged BlankLinesPixels {value: value});
    endmethod
    method Action hdmiLineCountMinMax(Bit#(32) value);
        commandFifo.enq(tagged LineCountMinMax {value: value});
    endmethod
    method Action hdmiPixelCountMinMax(Bit#(32) value);
        commandFifo.enq(tagged PixelCountMinMax {value: value});
    endmethod
    method Action hdmiSyncWidths(Bit#(32) value);
        commandFifo.enq(tagged SyncWidths {value: value});
    endmethod

    method Action startFrameBuffer0(Bit#(32) base);
        $display("startFrameBuffer %h", base);
        frameBufferEnabled <= True;
        FrameBufferConfig fbc;
        fbc.base = base;
        fbc.pixels = pixelsReg;
        fbc.lines = linesReg;
        Bit#(14) stridebytes = strideBytesReg;
        $display("startFrameBuffer lines %d pixels %d bytesperpixel %d stridebytes %d",
                 linesReg, pixelsReg, bytesperpixel, stridebytes);
        fbc.stridebytes = stridebytes;
        frameBuffer.configure(fbc);
        commandFifo.enq(tagged TestPattern {enabled: False});
        waitingForVsync <= True;
    endmethod

    method Action startFrameBuffer1(Bit#(32) base);
    endmethod

    method Action waitForVsync(Bit#(32) unused);
        waitingForVsync <= True;
    endmethod

    method Action beginTranslationTable(Bit#(8) index);
        segmentIndexReg <= index;
        segmentOffsetReg <= 0;
    endmethod
    method Action addTranslationEntry(Bit#(20) address, Bit#(12) length);
        frameBuffer.setSgEntry(segmentIndexReg, segmentOffsetReg, address, extend(length));
        segmentIndexReg <= segmentIndexReg + 1;
        segmentOffsetReg <= segmentOffsetReg + {length,12'd0};
    endmethod

    interface Axi3Client m_axi = frameBuffer.axi;
    interface HDMI hdmi = hdmiGen.hdmi;
endmodule


removed mkDotProd, refactored mkDotProdServer a bit

// Copyright (c) 2014 Quanta Research Cambridge, Inc.

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

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

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

import FIFO::*;
import FIFOF::*;
import MIMO::*;
import DefaultValue::*;
import SpecialFIFOs::*;
import Vector::*;
import BRAM::*;
import DmaVector::*;
import PortalMemory::*;
import Dma::*;
import FloatingPoint::*;
import Pipe::*;
import FloatOps::*;
import Timer::*;
import RbmTypes::*;

interface DotProdServer#(numeric type n);
   interface Reg#(UInt#(20)) numElts;
   interface Put#(Tuple2#(Vector#(n,Float),Vector#(n,Float))) request;
   interface PipeOut#(Float) pipe;
endinterface

(* synthesize *)
module [Module] mkDotProdServer#(UInt#(TLog#(K)) label)(DotProdServer#(N));

   let n = valueOf(N);
   Bool verbose = False;

   Reg#(UInt#(20)) numEltsReg <- mkReg(0);
   Reg#(UInt#(20)) countInReg <- mkReg(0);
   Vector#(N,FIFOF#(Tuple3#(Bool,Float,Float))) abfifos <- replicateM(mkFIFOF());
   FIFOF#(Bool) lastFifo <- mkFIFOF();
   Vector#(N, PipeOut#(Tuple3#(Bool,Float,Float))) abpipes = map(toPipeOut,abfifos);

   Vector#(N, Server#(Tuple4#(Maybe#(Float), Float, Float, RoundMode), Tuple2#(Float,Exception))) macs <- replicateM(mkFloatMac);
   Vector#(N, FIFO#(Float)) accumFifo <- replicateM(mkFIFO());
   Vector#(N, FIFOF#(Float)) resultFifos <- replicateM(mkFIFOF());

   rule mul;
      for (Integer i = 0; i < valueOf(N); i = i + 1) begin
	 let v = abpipes[i].first;
	 abpipes[i].deq;
	 let isFirst = tpl_1(v);
	 let x = tpl_2(v);
	 let y = tpl_3(v);

	 Maybe#(Float) accum = tagged Invalid;
	 if (!isFirst) begin
	    accum = tagged Valid accumFifo[i].first();
	    accumFifo[i].deq();
	 end
            macs[i].request.put(tuple4(accum, x, y, defaultValue));
	 if (verbose) $display($format(fshow("label=")+fshow(label)+fshow(" dotprod x=") + fshow(x) + fshow(" y=") + fshow(y)
				       + fshow(" isFirst=") + fshow(isFirst) + fshow(" accum=")+fshow(pack(accum))));
      end
   endrule

   rule macout;
      let isLast = lastFifo.first();
      lastFifo.deq();
      for (Integer i = 0; i < valueOf(N); i = i + 1) begin
	 Float vi;
	 let resp <- macs[i].response.get();
	 vi = tpl_1(resp);
	 if (!isLast) begin
	    accumFifo[i].enq(vi);
	 end
	 else begin
	    resultFifos[i].enq(vi);
	    if (verbose) $display(fshow("label=")+fshow(label)+fshow(" isLast=")+fshow(isLast)+fshow(" mul=") + fshow(vi));
	 end
      end
   endrule

   Vector#(N, PipeOut#(Float)) resultPipes = map(toPipeOut, resultFifos);
   PipeOut#(Vector#(N,Float)) resultPipe <- mkJoinVector(id, resultPipes);
   PipeOut#(Float) dotpipe <- mkReducePipe(mkFloatAddPipe, resultPipe);
   PipeOut#(Float) displaydotpipe = (interface PipeOut#(Float);
      method Float first(); return dotpipe.first(); endmethod
      method Action deq(); $display(fshow("label=")+fshow(label)+fshow(" dotpipe=")+fshow(dotpipe.first())); dotpipe.deq(); endmethod
      method Bool notEmpty(); return dotpipe.notEmpty(); endmethod
      endinterface);
   interface Put request;
      method Action put(Tuple2#(Vector#(N,Float),Vector#(N,Float)) tpl);
	 Bool isFirst = (countInReg == 0);
	 UInt#(20) n = fromInteger(valueOf(N));
	 let c = countInReg+n;
	 Bool isLast = (c >= numEltsReg);
	 if (isLast)
	    c = 0;
	 countInReg <= c;
	 for (Integer i = 0; i < valueOf(N); i = i + 1) begin
	    let avec = tpl_1(tpl);
	    let bvec = tpl_2(tpl);
	    abfifos[i].enq(tuple3(isFirst,avec[i], bvec[i]));
	 end
	 lastFifo.enq(isLast);
      endmethod
   endinterface : request
   interface PipeOut pipe = dotpipe;
   interface Reg numElts = numEltsReg;
endmodule : mkDotProdServer

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

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

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

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

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

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

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

/*!
 * Multiplies two matrices A and B and writes the result to memory.
 * Fetches J rows at a time from A and K rows at a time from B.
 * Each cycle, it can fetch N elements of a row or column.
 *
 * Just considering memory bandwidth, every J+K cycles it is ready to perform J*K*N multiply accumulates.
 *
 * Currently, requires J == 1.
 */
module [Module] mkDmaMatrixMultiply#(Vector#(J, VectorSource#(dsz, Vector#(N, Float))) sourceA,
				     Vector#(K, VectorSource#(dsz, Vector#(N, Float))) sourceB,
				     function Module#(DmaVectorSink#(dsz, Vector#(N, Float))) mkSink(PipeOut#(Vector#(N, Float)) pipe_in)
				     )(DmaMatrixMultiplyIfc#(addrwidth, dsz))
   provisos (Add#(a__,ObjectOffsetSize,addrwidth)
	     , Add#(N,n__,K)
	     , Mul#(N,m__,K)
	     , Add#(1,0,J)
	     , Log#(N,nshift)
	     , FShow#(Float)
	     , Arith#(Float)
	     , Bits#(Vector#(N, Float), dsz)
	     , Bits#(MatrixDescriptor#(UInt#(addrwidth)), mdsz)
	     , Bits#(Tuple2#(UInt#(addrwidth), UInt#(addrwidth)), tplsz)
	     , Add#(b__, 20, addrwidth)
      );

   let n = valueOf(N);
   let k = valueOf(K);
   let nshift = valueOf(nshift);
   Bool verbose = False;
   Bool verbose1 = True;

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

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

   function Module#(DotProdServer#(N)) mkFxDotProd(Integer i);
      return mkDotProdServer(fromInteger(i));
   endfunction
   Vector#(K, DotProdServer#(N)) fxdotprods <- genWithM(mkFxDotProd);
   for (Integer i = 0; i < k; i = i+1)
      rule connectDotProd;
	 let a = aPipes[i].first();
	 aPipes[i].deq();
	 let b = sourceB[i].pipe.first();
	 sourceB[i].pipe.deq();
	 fxdotprods[i].request.put(tuple2(a, b));
      endrule

   MIMOConfiguration mimoCfg = defaultValue;
   MIMO#(K,N,K,Float) dfifo <- mkMIMO(mimoCfg);
   let sinkC <- mkSink(toPipeOut(dfifo));

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

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

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

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

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

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

	 let startA = row*descriptorA.numColumns; // row major
	 let startB = col*descriptorB.numColumns; // col major layout (pre-transposed)
	 let startC = row*descriptorC.numColumns + col; // row major

	 if (verbose) $display($format(fshow(cycles)+fshow("    startDotProd xy=")+fshow(tuple2(row,col))
	    +fshow(" startA=")+fshow(startA)
	    +fshow(" startB=")+fshow(startB)
	    +fshow(" startC=")+fshow(startC)));

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

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

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

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

      if (verbose) $display("mm pointerA=%d pointerB=%d pointerC=%d\n", pointerA, pointerB, pointerC);
      if (verbose) $display("mm.start ra=%d ca=%d rb=%d cb=%d dotprodCount=%d", numRowsA, numColumnsA, numRowsB, numColumnsB, dotprodCount);
      if (verbose) $display($format(fshow("mm.start ")+fshow(xycfg)));
      xypipeifc.start(xycfg);
      for (Integer i = 0; i < k; i = i + 1) begin
	 fxdotprods[i].numElts <= truncate(numColumnsA);
      end
   endmethod
   method ActionValue#(Bool) finish();
      if (verbose) $display("mm.finish()");
      doneFifo.deq();
      return True;
   endmethod

   interface ObjectWriteClient dmaClient = sinkC.dmaClient;
endmodule

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

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

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

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

   let n = valueOf(n);

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

   Reg#(Bit#(64)) mmfCycles <- mkReg(0);
   rule countMmfCycles;
      mmfCycles <= mmfCycles + 1;
   endrule

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

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

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

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

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

endmodule

// Copyright (c) 2014 Quanta Research Cambridge, Inc.

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

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

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

import FIFO::*;
import FIFOF::*;
import MIMO::*;
import DefaultValue::*;
import SpecialFIFOs::*;
import Vector::*;
import BRAM::*;
import DmaVector::*;
import PortalMemory::*;
import Dma::*;
import FloatingPoint::*;
import Pipe::*;
import FloatOps::*;
import Timer::*;
import RbmTypes::*;

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

   Bool verbose = False;
   Bool useMac = True;

   Reg#(UInt#(nwidth)) countInReg <- mkReg(0);
   FIFOF#(Tuple3#(Bool,Vector#(n,Float),Vector#(n,Float))) xyfifo <- mkFIFOF();
   FIFOF#(Bool) firstFifo <- mkFIFOF();
   FIFOF#(Bool) lastFifo <- mkFIFOF();
   PipeOut#(Tuple3#(Bool, Vector#(n,Float),Vector#(n,Float))) xypipe = toPipeOut(xyfifo);
   rule xyin;
      Bool isFirst = (countInReg == 0);
      UInt#(nwidth) n = fromInteger(valueOf(n));
      let c = countInReg+n;
      Bool isLast = (c >= numElts);
      if (isLast)
	 c = 0;
      countInReg <= c;
      xpipe.deq();
      ypipe.deq();
      xyfifo.enq(tuple3(isFirst,xpipe.first(), ypipe.first()));
      if (!useMac)
	 firstFifo.enq(isFirst);
      lastFifo.enq(isLast);
   endrule

   Vector#(n, Server#(Tuple4#(Maybe#(Float), Float, Float, RoundMode), Tuple2#(Float,Exception))) macs <- replicateM(mkFloatMac);
   Vector#(n, FloatServer2#(Float)) muls <- replicateM(mkFloatMultiplier);
   Vector#(n, FloatServer2#(Float)) adders <- replicateM(mkFloatAdder);
   Vector#(n, FIFO#(Float)) accumFifo <- replicateM(mkFIFO());
   FIFOF#(Vector#(n,Float)) resultFifo <- mkFIFOF();

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

      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 Maybe#(Float) accum = tagged Invalid;
	 if (useMac) begin
	     if (!isFirst) begin
		accum = tagged Valid accumFifo[i].first();
		accumFifo[i].deq();
	     end
            macs[i].request.put(tuple4(accum, x[i], y[i], defaultValue));
	 end
	 else begin
	    muls[i].request.put(tuple3(x[i],y[i],defaultValue));
	 end
	 if (verbose) $display($format(fshow("label=")+fshow(label)+fshow(" dotprod x=") + fshow(x) + fshow(" y=") + fshow(y) + fshow(" accum=")+fshow(pack(accum))));
      end
   endrule

   rule add;
      if (!useMac) begin
	 Float accum = unpack(0);
	 let isFirst = firstFifo.first();
	 firstFifo.deq();
	 for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	    if (!isFirst) begin
	       accum = accumFifo[i].first();
	       accumFifo[i].deq();
	    end
	    let resp <- adders[i].response.get();
	    adders[i].request.put(tuple3(accum,tpl_1(resp),defaultValue));
	 end
      end
   endrule

   rule macout;
      Vector#(n,Float) vs = unpack(0);
      let isLast = lastFifo.first();
      lastFifo.deq();
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 Float vi;
	 if (useMac) begin
	    let resp <- macs[i].response.get();
	    vi = tpl_1(resp);
	 end
	 else begin
	    let resp <- adders[i].response.get();
	    vi = tpl_1(resp);
	 end
	 vs[i] = vi;
	 if (!isLast) begin
	    accumFifo[i].enq(vi);
	 end
      end
      if (isLast) begin
	 Vector#(n,Bit#(32)) ivs = unpack(pack(vs));
	 resultFifo.enq(vs);
	 if (verbose) $display(fshow("label=")+fshow(label)+fshow(" isLast=")+fshow(isLast)+fshow(" numElts=")+fshow(numElts)+fshow(" mul=") + fshow(ivs));
      end
   endrule

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

interface DotProdServer#(numeric type n);
   interface Reg#(UInt#(20)) numElts;
   interface Put#(Tuple2#(Vector#(n,Float),Vector#(n,Float))) request;
   interface PipeOut#(Float) pipe;
endinterface

(* synthesize *)
module [Module] mkDotProdServer#(UInt#(TLog#(K)) label)(DotProdServer#(N));

   let n = valueOf(N);
   Bool verbose = False;
   Bool useMac = True;

   Reg#(UInt#(20)) numEltsReg <- mkReg(0);
   Reg#(UInt#(20)) countInReg <- mkReg(0);
   FIFOF#(Tuple3#(Bool,Vector#(N,Float),Vector#(N,Float))) xyfifo <- mkFIFOF();
   FIFOF#(Bool) firstFifo <- mkFIFOF();
   FIFOF#(Bool) lastFifo <- mkFIFOF();
   PipeOut#(Tuple3#(Bool, Vector#(N,Float),Vector#(N,Float))) xypipe = toPipeOut(xyfifo);

   Vector#(N, Server#(Tuple4#(Maybe#(Float), Float, Float, RoundMode), Tuple2#(Float,Exception))) macs <- replicateM(mkFloatMac);
   Vector#(N, FloatServer2#(Float)) muls <- replicateM(mkFloatMultiplier);
   Vector#(N, FloatServer2#(Float)) adders <- replicateM(mkFloatAdder);
   Vector#(N, FIFO#(Float)) accumFifo <- replicateM(mkFIFO());
   FIFOF#(Vector#(N,Float)) resultFifo <- mkFIFOF();

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

      for (Integer i = 0; i < valueOf(N); i = i + 1) begin
	 Maybe#(Float) accum = tagged Invalid;
	 if (useMac) begin
	     if (!isFirst) begin
		accum = tagged Valid accumFifo[i].first();
		accumFifo[i].deq();
	     end
            macs[i].request.put(tuple4(accum, x[i], y[i], defaultValue));
	 end
	 else begin
	    muls[i].request.put(tuple3(x[i],y[i],defaultValue));
	 end
	 if (verbose) $display($format(fshow("label=")+fshow(label)+fshow(" dotprod x=") + fshow(x) + fshow(" y=") + fshow(y) + fshow(" accum=")+fshow(pack(accum))));
      end
   endrule

   rule add;
      if (!useMac) begin
	 Float accum = unpack(0);
	 let isFirst = firstFifo.first();
	 firstFifo.deq();
	 for (Integer i = 0; i < valueOf(N); i = i + 1) begin
	    if (!isFirst) begin
	       accum = accumFifo[i].first();
	       accumFifo[i].deq();
	    end
	    let resp <- adders[i].response.get();
	    adders[i].request.put(tuple3(accum,tpl_1(resp),defaultValue));
	 end
      end
   endrule

   rule macout;
      Vector#(N,Float) vs = unpack(0);
      let isLast = lastFifo.first();
      lastFifo.deq();
      for (Integer i = 0; i < valueOf(N); i = i + 1) begin
	 Float vi;
	 if (useMac) begin
	    let resp <- macs[i].response.get();
	    vi = tpl_1(resp);
	 end
	 else begin
	    let resp <- adders[i].response.get();
	    vi = tpl_1(resp);
	 end
	 vs[i] = vi;
	 if (!isLast) begin
	    accumFifo[i].enq(vi);
	 end
      end
      if (isLast) begin
	 Vector#(N,Bit#(32)) ivs = unpack(pack(vs));
	 resultFifo.enq(vs);
	 if (verbose) $display(fshow("label=")+fshow(label)+fshow(" isLast=")+fshow(isLast)+fshow(" numElts=")+fshow(numEltsReg)+fshow(" mul=") + fshow(ivs));
      end
   endrule

   PipeOut#(Vector#(N,Float)) resultPipe = toPipeOut(resultFifo);
   PipeOut#(Float) dotpipe <- mkReducePipe(mkFloatAddPipe, resultPipe);
   PipeOut#(Float) displaydotpipe = (interface PipeOut#(Float);
      method Float first(); return dotpipe.first(); endmethod
      method Action deq(); $display(fshow("label=")+fshow(label)+fshow(" dotpipe=")+fshow(dotpipe.first())); dotpipe.deq(); endmethod
      method Bool notEmpty(); return dotpipe.notEmpty(); endmethod
      endinterface);
   interface Put request;
      method Action put(Tuple2#(Vector#(N,Float),Vector#(N,Float)) tpl);
	 Bool isFirst = (countInReg == 0);
	 UInt#(20) n = fromInteger(valueOf(N));
	 let c = countInReg+n;
	 Bool isLast = (c >= numEltsReg);
	 if (isLast)
	    c = 0;
	 countInReg <= c;
	 xyfifo.enq(tuple3(isFirst,tpl_1(tpl), tpl_2(tpl)));
	 if (!useMac)
	    firstFifo.enq(isFirst);
	 lastFifo.enq(isLast);
      endmethod
   endinterface : request
   interface PipeOut pipe = dotpipe;
   interface Reg numElts = numEltsReg;
endmodule

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

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

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

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

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

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

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

/*!
 * Multiplies two matrices A and B and writes the result to memory.
 * Fetches J rows at a time from A and K rows at a time from B.
 * Each cycle, it can fetch N elements of a row or column.
 *
 * Just considering memory bandwidth, every J+K cycles it is ready to perform J*K*N multiply accumulates.
 *
 * Currently, requires J == 1.
 */
module [Module] mkDmaMatrixMultiply#(Vector#(J, VectorSource#(dsz, Vector#(N, Float))) sourceA,
				     Vector#(K, VectorSource#(dsz, Vector#(N, Float))) sourceB,
				     function Module#(DmaVectorSink#(dsz, Vector#(N, Float))) mkSink(PipeOut#(Vector#(N, Float)) pipe_in)
				     )(DmaMatrixMultiplyIfc#(addrwidth, dsz))
   provisos (Add#(a__,ObjectOffsetSize,addrwidth)
	     , Add#(N,n__,K)
	     , Mul#(N,m__,K)
	     , Add#(1,0,J)
	     , Log#(N,nshift)
	     , FShow#(Float)
	     , Arith#(Float)
	     , Bits#(Vector#(N, Float), dsz)
	     , Bits#(MatrixDescriptor#(UInt#(addrwidth)), mdsz)
	     , Bits#(Tuple2#(UInt#(addrwidth), UInt#(addrwidth)), tplsz)
	     , Add#(b__, 20, addrwidth)
      );

   let n = valueOf(N);
   let k = valueOf(K);
   let nshift = valueOf(nshift);
   Bool verbose = False;
   Bool verbose1 = True;

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

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

   function Module#(DotProdServer#(N)) mkFxDotProd(Integer i);
      return mkDotProdServer(fromInteger(i));
   endfunction
   Vector#(K, DotProdServer#(N)) fxdotprods <- genWithM(mkFxDotProd);
   for (Integer i = 0; i < k; i = i+1)
      rule connectDotProd;
	 let a = aPipes[i].first();
	 aPipes[i].deq();
	 let b = sourceB[i].pipe.first();
	 sourceB[i].pipe.deq();
	 fxdotprods[i].request.put(tuple2(a, b));
      endrule

   MIMOConfiguration mimoCfg = defaultValue;
   MIMO#(K,N,K,Float) dfifo <- mkMIMO(mimoCfg);
   let sinkC <- mkSink(toPipeOut(dfifo));

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

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

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

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

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

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

	 let startA = row*descriptorA.numColumns; // row major
	 let startB = col*descriptorB.numColumns; // col major layout (pre-transposed)
	 let startC = row*descriptorC.numColumns + col; // row major

	 if (verbose) $display($format(fshow(cycles)+fshow("    startDotProd xy=")+fshow(tuple2(row,col))
	    +fshow(" startA=")+fshow(startA)
	    +fshow(" startB=")+fshow(startB)
	    +fshow(" startC=")+fshow(startC)));

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

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

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

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

      if (verbose) $display("mm pointerA=%d pointerB=%d pointerC=%d\n", pointerA, pointerB, pointerC);
      if (verbose) $display("mm.start ra=%d ca=%d rb=%d cb=%d dotprodCount=%d", numRowsA, numColumnsA, numRowsB, numColumnsB, dotprodCount);
      if (verbose) $display($format(fshow("mm.start ")+fshow(xycfg)));
      xypipeifc.start(xycfg);
      for (Integer i = 0; i < k; i = i + 1) begin
	 fxdotprods[i].numElts <= truncate(numColumnsA);
      end
   endmethod
   method ActionValue#(Bool) finish();
      if (verbose) $display("mm.finish()");
      doneFifo.deq();
      return True;
   endmethod

   interface ObjectWriteClient dmaClient = sinkC.dmaClient;
endmodule

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

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

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

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

   let n = valueOf(n);

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

   Reg#(Bit#(64)) mmfCycles <- mkReg(0);
   rule countMmfCycles;
      mmfCycles <= mmfCycles + 1;
   endrule

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

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

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

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

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

endmodule