cleanup

cleanup

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

// bsv libraries
import Vector::*;
import FIFO::*;
import DefaultValue::*;

// portz libraries
import Portal::*;
import Directory::*;
import CtrlMux::*;
import Portal::*;
import Leds::*;
import Dma::*;

// generated by tool
import ImageCaptureIndicationProxy::*;
import ImageCaptureRequestWrapper::*;
import ImageonSerdesIndicationProxy::*;
import HdmiInternalIndicationProxy::*;
import ImageonSerdesRequestWrapper::*;
import HdmiInternalRequestWrapper::*;
import ImageonSensorRequestWrapper::*;
import ImageCaptureRequestWrapper::*;

// defined by user
import FrequencyCounter::*;
import ImageCapture::*;
import GetPut::*;
import Connectable :: *;
import Clocks :: *;
import XbsvSpi :: *;

import Imageon::*;
import IserdesDatadeser::*;
import HDMI::*;
import XilinxCells::*;
import XbsvXilinxCells::*;
import YUV::*;
import PS7LIB :: *;
import Imageon :: *;

typedef enum { ImageCapture, ImageonSerdesRequest, HdmiInternalRequest, ImageonSensorRequest,
    ImageCaptureIndication, ImageonSerdesIndication, HdmiInternalIndication} IfcNames deriving (Eq,Bits);

interface ImageCapturePins;
   interface SpiPins spi;
   interface ImageonSensorPins pins;
   interface ImageonSerdesPins serpins;
   interface HDMI hdmi;
   method Bit#(1) i2c_mux_reset_n();
endinterface

module mkPortalTop#(Clock clock200, Clock fmc_imageon_clk1)(PortalTop#(addrWidth,64,ImageCapturePins,0));
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();
   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by clock200);
   ClockGenerator7AdvParams clockParams = defaultValue;
   clockParams.bandwidth          = "OPTIMIZED";
   clockParams.compensation       = "ZHOLD";
   clockParams.clkfbout_mult_f    = 8.000;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkin1_period      = 6.734007; // 148.5 MHz
   clockParams.clkin2_period      = 6.734007;
   clockParams.clkout0_divide_f   = 8.000;    // 148.5 MHz
   clockParams.clkout0_duty_cycle = 0.5;
   clockParams.clkout0_phase      = 0.0000;
   clockParams.clkout1_divide     = 32;       // 37.125 MHz
   clockParams.clkout1_duty_cycle = 0.5;
   clockParams.clkout1_phase      = 0.0000;
   clockParams.divclk_divide      = 1;
   clockParams.ref_jitter1        = 0.010;
   clockParams.ref_jitter2        = 0.010;

   ClockGenerator7 clockGen <- mkClockGenerator7Adv(clockParams, clocked_by fmc_imageon_clk1);
   Clock hdmi_clock <- mkClockBUFG(clocked_by clockGen.clkout0);    // 148.5   MHz
   Clock imageon_clock <- mkClockBUFG(clocked_by clockGen.clkout1); //  37.125 MHz

   Reset fmc_imageon_reset <- mkAsyncReset(2, defaultReset, fmc_imageon_clk1);
   Reset hdmi_reset <- mkAsyncReset(2, defaultReset, hdmi_clock);
   Reset imageon_reset <- mkAsyncReset(2, defaultReset, imageon_clock);
   SPI#(Bit#(26)) spiController <- mkSPI(1000);
   SyncPulseIfc vsyncPulse <- mkSyncHandshake(hdmi_clock, hdmi_reset, imageon_clock);
   Reg#(Bit#(1)) i2c_mux_reset_n_reg <- mkReg(0);

   // instantiate user portals
   ImageonSerdesIndicationProxy serdesIndicationProxy <- mkImageonSerdesIndicationProxy(ImageonSerdesIndication);
   ISerdes serdes <- mkISerdes(defaultClock, defaultReset, serdesIndicationProxy.ifc,
			clocked_by imageon_clock, reset_by imageon_reset);
   ImageonSerdesRequestWrapper serdesRequestWrapper <- mkImageonSerdesRequestWrapper(ImageonSerdesRequest,serdes.control);

   HdmiInternalIndicationProxy hdmiIndicationProxy <- mkHdmiInternalIndicationProxy(HdmiInternalIndication);
   HdmiGenerator hdmiGen <- mkHdmiGenerator(defaultClock, defaultReset,
       vsyncPulse, hdmiIndicationProxy.ifc, clocked_by hdmi_clock, reset_by hdmi_reset);
   HdmiInternalRequestWrapper hdmiRequestWrapper <- mkHdmiInternalRequestWrapper(HdmiInternalRequest,hdmiGen.control);

   ImageonSensor fromSensor <- mkImageonSensor(defaultClock, defaultReset, serdes.data, vsyncPulse.pulse(),
       hdmiGen.control, hdmi_clock, hdmi_reset, clocked_by imageon_clock, reset_by imageon_reset);
   ImageonSensorRequestWrapper sensorRequestWrapper <- mkImageonSensorRequestWrapper(ImageonSensorRequest,fromSensor.control);

   ImageCaptureIndicationProxy captureIndicationProxy <- mkImageCaptureIndicationProxy(ImageCaptureIndication);
   FrequencyCounter axiFreqCounter <- mkFrequencyCounter(defaultClock, defaultReset);
   FrequencyCounter hdmiFreqCounter <- mkFrequencyCounter(hdmi_clock, hdmi_reset);
   FrequencyCounter imageonFreqCounter <- mkFrequencyCounter(imageon_clock, imageon_reset);
   FrequencyCounter fmcFreqCounter <- mkFrequencyCounter(fmc_imageon_clk1, fmc_imageon_reset);
   ImageCaptureRequest imageCaptureRequest = (interface ImageCaptureRequest;
      method Action get_debugind();
         captureIndicationProxy.ifc.debugind(fromSensor.control.get_debugind());
      endmethod
      method Action put_spi_request(Bit#(32) v);
         spiController.request.put(truncate(v));
      endmethod
      method Action set_i2c_mux_reset_n(Bit#(1) v);
	 i2c_mux_reset_n_reg <= v;
      endmethod
      method Action measure_axi_clock_period(Bit#(32) cycles_100mhz);
	 axiFreqCounter.start(cycles_100mhz);
      endmethod
      method Action measure_hdmi_clock_period(Bit#(32) cycles_100mhz);
         hdmiFreqCounter.start(cycles_100mhz);
      endmethod
      method Action measure_imageon_clock_period(Bit#(32) cycles_100mhz);
         imageonFreqCounter.start(cycles_100mhz);
      endmethod
      method Action measure_fmc_clock_period(Bit#(32) cycles_100mhz);
         fmcFreqCounter.start(cycles_100mhz);
      endmethod
      endinterface);
   ImageCaptureRequestWrapper captureRequestWrapper <- mkImageCaptureRequestWrapper(ImageCapture, imageCaptureRequest);

   rule spiControllerResponse;
       Bit#(26) v <- spiController.response.get();
       captureIndicationProxy.ifc.spi_response(extend(v));
   endrule

   rule gotAxiClockPeriod;
      let cycles <- axiFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.axi_clock_period(cycles);
   endrule
   rule gotHdmiClockPeriod;
      let cycles <- hdmiFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.hdmi_clock_period(cycles);
   endrule
   rule gotImageonClockPeriod;
      let cycles <- imageonFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.imageon_clock_period(cycles);
   endrule
   rule gotFmcClockPeriod;
      let cycles <- fmcFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.fmc_clock_period(cycles);
   endrule

   Reg#(Bool) frameStart <- mkReg(False, clocked_by imageon_clock, reset_by imageon_reset);
   Reg#(Bit#(32)) frameCount <- mkReg(0, clocked_by imageon_clock, reset_by imageon_reset);
   SyncFIFOIfc#(Tuple2#(Bit#(2),Bit#(32))) frameStartSynchronizer <- mkSyncFIFO(2, imageon_clock, imageon_reset, defaultClock);

   rule frameStartRule;
       let monitor = fromSensor.monitor();
       Bool fs = unpack(monitor[0]);
       if (fs && !frameStart) begin
	  // start of frame?
	  // need to cross the clock domain
	  frameStartSynchronizer.enq(tuple2(monitor, frameCount));
	  frameCount <= frameCount + 1;
       end
      frameStart <= fs;
   endrule
   rule frameStartIndication;
      let tpl = frameStartSynchronizer.first();
      frameStartSynchronizer.deq();
      let monitor = tpl_1(tpl);
      let count = tpl_2(tpl);
      captureIndicationProxy.ifc.frameStart(monitor, count);
   endrule

   rule xsviConnection;
       let xsvi <- fromSensor.get_data();
       Bit#(32) pixel = {8'b0, xsvi[9:2], xsvi[9:2], xsvi[9:2]};
       hdmiGen.request.put(pixel);
   endrule

   Vector#(7,StdPortal) portals;
   portals[0] = captureRequestWrapper.portalIfc;
   portals[1] = captureIndicationProxy.portalIfc;
   portals[2] = serdesRequestWrapper.portalIfc;
   portals[3] = serdesIndicationProxy.portalIfc;
   portals[4] = hdmiRequestWrapper.portalIfc;
   portals[5] = hdmiIndicationProxy.portalIfc;
   portals[6] = sensorRequestWrapper.portalIfc;

   // instantiate system directory
   StdDirectory dir <- mkStdDirectory(portals);
   let ctrl_mux <- mkSlaveMux(dir,portals);

   interface interrupt = getInterruptVector(portals);
   interface slave = ctrl_mux;
   interface masters = nil;
   //interface leds = captureRequestInternal.leds;

   interface ImageCapturePins pins;
       interface SpiPins spi = spiController.pins;
       interface ImageonSensorPins pins = fromSensor.pins;
       interface ImageonSerdesPins serpins = serdes.pins;
       interface HDMI hdmi = hdmiGen.hdmi;
       method Bit#(1) i2c_mux_reset_n(); return i2c_mux_reset_n_reg; endmethod
   endinterface

endmodule : mkPortalTop

// bsv libraries
import Vector::*;
import FIFO::*;
import Connectable::*;
import DefaultValue::*;

// portz libraries
import Portal::*;
import Directory::*;
import CtrlMux::*;
import Portal::*;
import Leds::*;
import Dma::*;

// generated by tool
import ImageCaptureIndicationProxy::*;
import ImageCaptureRequestWrapper::*;
import ImageonSerdesIndicationProxy::*;
import HdmiInternalIndicationProxy::*;
import ImageonSerdesRequestWrapper::*;
import HdmiInternalRequestWrapper::*;
import ImageonSensorRequestWrapper::*;
import ImageCaptureRequestWrapper::*;

// defined by user
import FrequencyCounter::*;
import ImageCapture::*;
import GetPut::*;
import Connectable :: *;
//import PCIE :: *; // ConnectableWithClocks
import Clocks :: *;
import XbsvSpi :: *;

import GetPutWithClocks :: *;
import Imageon::*;
import IserdesDatadeser::*;
import HDMI::*;
import XilinxCells::*;
import XbsvXilinxCells::*;
import YUV::*;
import PS7LIB :: *;
import Imageon :: *;

typedef enum { ImageCapture, ImageonSerdesRequest, HdmiInternalRequest, ImageonSensorRequest,
    ImageCaptureIndication, ImageonSerdesIndication, HdmiInternalIndication} IfcNames deriving (Eq,Bits);

interface ImageCapturePins;
   interface SpiPins spi;
   interface ImageonSensorPins pins;
   interface ImageonSerdesPins serpins;
   interface HDMI hdmi;
   method Bit#(1) i2c_mux_reset_n();
endinterface

module mkPortalTop#(Clock clock200, Clock fmc_imageon_clk1)(PortalTop#(addrWidth,64,ImageCapturePins,0));
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   // instantiate user portals
   ImageCaptureIndicationProxy captureIndicationProxy <- mkImageCaptureIndicationProxy(ImageCaptureIndication);
   ImageonSerdesIndicationProxy serdesIndicationProxy <- mkImageonSerdesIndicationProxy(ImageonSerdesIndication);
   HdmiInternalIndicationProxy hdmiIndicationProxy <- mkHdmiInternalIndicationProxy(HdmiInternalIndication);

   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by clock200);
   ClockGenerator7AdvParams clockParams = defaultValue;
   clockParams.bandwidth          = "OPTIMIZED";
   clockParams.compensation       = "ZHOLD";
   clockParams.clkfbout_mult_f    = 8.000;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkin1_period      = 6.734007; // 148.5 MHz
   clockParams.clkin2_period      = 6.734007;
   clockParams.clkout0_divide_f   = 8.000;    // 148.5 MHz
   clockParams.clkout0_duty_cycle = 0.5;
   clockParams.clkout0_phase      = 0.0000;
   clockParams.clkout1_divide     = 32;       // 37.125 MHz
   clockParams.clkout1_duty_cycle = 0.5;
   clockParams.clkout1_phase      = 0.0000;
   clockParams.divclk_divide      = 1;
   clockParams.ref_jitter1        = 0.010;
   clockParams.ref_jitter2        = 0.010;

   ClockGenerator7 clockGen <- mkClockGenerator7Adv(clockParams, clocked_by fmc_imageon_clk1);
   Clock hdmi_clock <- mkClockBUFG(clocked_by clockGen.clkout0);    // 148.5   MHz
   Clock imageon_clock <- mkClockBUFG(clocked_by clockGen.clkout1); //  37.125 MHz

   Reset fmc_imageon_reset <- mkAsyncReset(2, defaultReset, fmc_imageon_clk1);
   Reset hdmi_reset <- mkAsyncReset(2, defaultReset, hdmi_clock);
   Reset imageon_reset <- mkAsyncReset(2, defaultReset, imageon_clock);
   SyncPulseIfc vsyncPulse <- mkSyncHandshake(hdmi_clock, hdmi_reset, imageon_clock);

   ISerdes serdes <- mkISerdes(defaultClock, defaultReset, serdesIndicationProxy.ifc,
			clocked_by imageon_clock, reset_by imageon_reset);
   SPI#(Bit#(26)) spiController <- mkSPI(1000);
   HdmiGenerator hdmiGen <- mkHdmiGenerator(defaultClock, defaultReset,
       vsyncPulse, hdmiIndicationProxy.ifc, clocked_by hdmi_clock, reset_by hdmi_reset);
   ImageonSensor fromSensor <- mkImageonSensor(defaultClock, defaultReset, serdes.data, vsyncPulse.pulse(),
       hdmiGen.control, hdmi_clock, hdmi_reset, clocked_by imageon_clock, reset_by imageon_reset);

   rule spiControllerResponse;
       Bit#(26) v <- spiController.response.get();
       captureIndicationProxy.ifc.spi_response(extend(v));
   endrule

   Reg#(Bit#(1)) i2c_mux_reset_n_reg <- mkReg(0);
   FrequencyCounter axiFreqCounter <- mkFrequencyCounter(defaultClock, defaultReset);
   FrequencyCounter hdmiFreqCounter <- mkFrequencyCounter(hdmi_clock, hdmi_reset);
   FrequencyCounter imageonFreqCounter <- mkFrequencyCounter(imageon_clock, imageon_reset);
   FrequencyCounter fmcFreqCounter <- mkFrequencyCounter(fmc_imageon_clk1, fmc_imageon_reset);

   rule gotAxiClockPeriod;
      let cycles <- axiFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.axi_clock_period(cycles);
   endrule
   rule gotHdmiClockPeriod;
      let cycles <- hdmiFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.hdmi_clock_period(cycles);
   endrule
   rule gotImageonClockPeriod;
      let cycles <- imageonFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.imageon_clock_period(cycles);
   endrule
   rule gotFmcClockPeriod;
      let cycles <- fmcFreqCounter.elapsedCycles();
      captureIndicationProxy.ifc.fmc_clock_period(cycles);
   endrule

   ImageCaptureRequest imageCaptureRequest = (interface ImageCaptureRequest;
      method Action get_debugind();
         captureIndicationProxy.ifc.debugind(fromSensor.control.get_debugind());
      endmethod
      method Action put_spi_request(Bit#(32) v);
         spiController.request.put(truncate(v));
      endmethod
      method Action set_i2c_mux_reset_n(Bit#(1) v);
	 i2c_mux_reset_n_reg <= v;
      endmethod
      method Action measure_axi_clock_period(Bit#(32) cycles_100mhz);
	 axiFreqCounter.start(cycles_100mhz);
      endmethod
      method Action measure_hdmi_clock_period(Bit#(32) cycles_100mhz);
         hdmiFreqCounter.start(cycles_100mhz);
      endmethod
      method Action measure_imageon_clock_period(Bit#(32) cycles_100mhz);
         imageonFreqCounter.start(cycles_100mhz);
      endmethod
      method Action measure_fmc_clock_period(Bit#(32) cycles_100mhz);
         fmcFreqCounter.start(cycles_100mhz);
      endmethod
      endinterface);

    ImageCaptureRequestWrapper captureRequestWrapper <- mkImageCaptureRequestWrapper(ImageCapture, imageCaptureRequest);
    ImageonSerdesRequestWrapper serdesRequestWrapper <- mkImageonSerdesRequestWrapper(ImageonSerdesRequest,serdes.control);
    HdmiInternalRequestWrapper hdmiRequestWrapper <- mkHdmiInternalRequestWrapper(HdmiInternalRequest,hdmiGen.control);
    ImageonSensorRequestWrapper sensorRequestWrapper <- mkImageonSensorRequestWrapper(ImageonSensorRequest,fromSensor.control);

   Reg#(Bool) frameStart <- mkReg(False, clocked_by imageon_clock, reset_by imageon_reset);
   Reg#(Bit#(32)) frameCount <- mkReg(0, clocked_by imageon_clock, reset_by imageon_reset);
   SyncFIFOIfc#(Tuple2#(Bit#(2),Bit#(32))) frameStartSynchronizer <- mkSyncFIFO(2, imageon_clock, imageon_reset, defaultClock);

   rule frameStartRule;
       let monitor = fromSensor.monitor();
       Bool fs = unpack(monitor[0]);
       if (fs && !frameStart) begin
	  // start of frame?
	  // need to cross the clock domain
	  frameStartSynchronizer.enq(tuple2(monitor, frameCount));
	  frameCount <= frameCount + 1;
       end
      frameStart <= fs;
   endrule
   rule frameStartIndication;
      let tpl = frameStartSynchronizer.first();
      frameStartSynchronizer.deq();
      let monitor = tpl_1(tpl);
      let count = tpl_2(tpl);
      captureIndicationProxy.ifc.frameStart(monitor, count);
   endrule

    rule xsviConnection;
        let xsvi <- fromSensor.get_data();
        Bit#(32) pixel = {8'b0, xsvi[9:2], xsvi[9:2], xsvi[9:2]};
        hdmiGen.request.put(pixel);
    endrule

   Vector#(7,StdPortal) portals;
   portals[0] = captureRequestWrapper.portalIfc;
   portals[1] = captureIndicationProxy.portalIfc;
   portals[2] = serdesRequestWrapper.portalIfc;
   portals[3] = serdesIndicationProxy.portalIfc;
   portals[4] = hdmiRequestWrapper.portalIfc;
   portals[5] = hdmiIndicationProxy.portalIfc;
   portals[6] = sensorRequestWrapper.portalIfc;

   // instantiate system directory
   StdDirectory dir <- mkStdDirectory(portals);
   let ctrl_mux <- mkSlaveMux(dir,portals);

   interface interrupt = getInterruptVector(portals);
   interface slave = ctrl_mux;
   interface masters = nil;
   //interface leds = captureRequestInternal.leds;

   interface ImageCapturePins pins;
       interface SpiPins spi = spiController.pins;
       interface ImageonSensorPins pins = fromSensor.pins;
       interface ImageonSerdesPins serpins = serdes.pins;
       interface HDMI hdmi = hdmiGen.hdmi;
       method Bit#(1) i2c_mux_reset_n(); return i2c_mux_reset_n_reg; endmethod
   endinterface

endmodule : mkPortalTop


no prefix for pins

no prefix for pins

Change-Id: Ie827fcccb91121445768961f189e14afc8d1ab27

// 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 Clocks :: *;
import Vector            :: *;
import Connectable       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import AxiMasterSlave    :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import BRAM::*;
import Bscan::*;
import FIFOF::*;
import HDMI::*;
import Imageon::*;

//`define TRACE_AXI
//`define AXI_READ_TIMING

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
   (* prefix="GPIO" *)
   interface LEDS             leds;
   (* prefix="XADC" *)
   interface XADC             xadc;
   (* prefix="" *)
   interface pins             pins;
   interface Clock unused_clock0;
   interface Clock unused_clock1;
   interface Clock unused_clock2;
   interface Clock unused_clock3;
   interface Reset unused_reset0;
   interface Reset unused_reset1;
   interface Reset unused_reset2;
   interface Reset unused_reset3;
endinterface

typedef (function Module#(PortalTop#(32, 64, ipins)) mkpt(FromPS7 fromPS7)) MkPortalTop#(type ipins);

module [Module] mkZynqTopFromPortal#(MkPortalTop#(ipins) constructor)(ZynqTop#(ipins));
   // B2C converts a bit to a clock, enabling us to break the apparent cycle
   Vector#(4, B2C) fclk <- replicateM(mkB2C());
   B2C mainclock = fclk[0];
   PS7 ps7 <- mkPS7(mainclock.c, mainclock.r, clocked_by mainclock.c, reset_by mainclock.r);

   FromPS7 fromPS7 = (interface FromPS7;
			 interface Clock processing_system7_1_fclk_clk3 = fclk[3].c;
			 interface Clock fmc_imageon_video_clk1 = fclk[2].c; // FIXME
		      endinterface);
   let top <- constructor(fromPS7, clocked_by mainclock.c, reset_by mainclock.r);
   Reg#(Bit#(8)) addrReg <- mkReg(9, clocked_by mainclock.c, reset_by mainclock.r);
   BscanBram#(Bit#(8), Bit#(64)) bscanBram <- mkBscanBram(1, addrReg, clocked_by mainclock.c, reset_by mainclock.r);
   BRAM_Configure bramCfg = defaultValue;
   bramCfg.memorySize = 256;
   bramCfg.latency = 1;
   BRAM2Port#(Bit#(8), Bit#(64)) traceBram <- mkSyncBRAM2Server(bramCfg, mainclock.c, mainclock.r,
								bscanBram.jtagClock, bscanBram.jtagReset);
   mkConnection(bscanBram.bramClient, traceBram.portB);

   ReadOnly#(Bit#(4)) debugReg <- mkNullCrossingWire(mainclock.c, bscanBram.debug());

   let interrupt_bit = top.interrupt ? 1'b1 : 1'b0;

`ifndef TRACE_AXI
   mkConnection(ps7.m_axi_gp[0].client, top.ctrl);
`else

   Vector#(5, FIFOF#(Bit#(64))) bscan_fifos <- replicateM(mkFIFOF(clocked_by mainclock.c, reset_by mainclock.r));

   rule write_bscanBram;
      Bit#(64) data = ?;
      if (bscan_fifos[0].notEmpty) begin
	 data = bscan_fifos[0].first;
	 bscan_fifos[0].deq;
      end
      else if (bscan_fifos[1].notEmpty) begin
	 data = bscan_fifos[1].first;
	 bscan_fifos[1].deq;
      end
      else if (bscan_fifos[2].notEmpty) begin
	 data = bscan_fifos[2].first;
	 bscan_fifos[2].deq;
      end
      else if (bscan_fifos[3].notEmpty) begin
	 data = bscan_fifos[3].first;
	 bscan_fifos[3].deq;
      end
      else begin
	 data = bscan_fifos[4].first;
	 bscan_fifos[4].deq;
      end
      traceBram.portA.request.put(BRAMRequest {write:True, responseOnWrite:False, address:addrReg, datain:data});
      addrReg <= addrReg + 1;
   endrule
   Reg#(Bit#(16)) seqCounter <- mkReg(0, clocked_by mainclock.c, reset_by mainclock.r);
   rule seqinc;
       seqCounter <= seqCounter + 1;
   endrule

   // AXI trace for JTAG
   let m = ps7.m_axi_gp[0].client;
   let s = top.ctrl;
   //mkConnection(m.req_ar, s.req_ar);
   rule connect_req_ar;
       let req <- m.req_ar.get();
       s.req_ar.put(req);
       bscan_fifos[0].enq(
	   {3'h1, interrupt_bit, req.id,
`ifdef AXI_READ_TIMING
                    seqCounter,
`else
                    req.len, req.cache, req.prot, req.size,
                    pack(req.burst == 2'b01), pack(req.lock == 0 && req.qos == 0),
`endif
                    req.address});
   endrule
   //mkConnection(s.resp_read, m.resp_read);
   rule connect_resp_read;
       let resp <- s.resp_read.get();
       m.resp_read.put(resp);
       bscan_fifos[1].enq(
           {3'h2, interrupt_bit, resp.id, resp.resp, resp.last, 13'b0, resp.data});
   endrule
   //mkConnection(m.req_aw, s.req_aw);
   rule connect_req_aw;
       let req <- m.req_aw.get();
       s.req_aw.put(req);
       bscan_fifos[2].enq(
           {3'h3, interrupt_bit, req.id, req.len, req.cache, req.prot, req.size,
                    pack(req.burst == 2'b01), pack(req.lock == 0 && req.qos == 0), req.address});
   endrule
   //mkConnection(m.resp_write, s.resp_write);
   rule connect_resp_write;
       let resp <- m.resp_write.get();
       s.resp_write.put(resp);
       bscan_fifos[3].enq(
           {3'h4, interrupt_bit, resp.id, resp.last, resp.byteEnable, 11'b0, resp.data});
   endrule
   //mkConnection(s.resp_b, m.resp_b);
   rule connect_resp_b;
       let resp <- s.resp_b.get();
       m.resp_b.put(resp);
       bscan_fifos[4].enq(
           {3'h5, interrupt_bit, resp.id, resp.resp, 46'b0});
   endrule
`endif

   mkConnection(top.m_axi, ps7.s_axi_hp[0].axi.server);
   rule send_int_rule;
       ps7.interrupt(interrupt_bit);
   endrule

   // this rule connects the bits to the clock net via B2C
   for (Integer i = 0; i < 4; i = i + 1) begin
      ReadOnly#(Bit#(4)) fclkclk;
      if (i == 0) begin
	 fclkclk = (interface ReadOnly; method Bit#(4) _read(); return ps7.fclkclk; endmethod endinterface);
      end
      else begin
	 fclkclk <- mkNullCrossingWire(fclk[i].c, ps7.fclkclk);
      end
       rule b2c_rule1;
	   fclk[i].inputclock(fclkclk[i]);
	   fclk[i].inputreset(fclkclk[i]);
       endrule
   end

   interface zynq = ps7.pins;
   interface leds = top.leds;
   interface XADC xadc;
       method Bit#(4) gpio;
           return debugReg;
       endmethod
   endinterface
   interface pins = top.pins;

   // these are exported to make bsc happy, and then the ports are disconnected after synthesis
   interface unused_clock0 = fclk[0].c;
   interface unused_reset0 = fclk[0].r;
   interface unused_clock1 = fclk[1].c;
   interface unused_reset1 = fclk[1].r;
   interface unused_clock2 = fclk[2].c;
   interface unused_reset2 = fclk[2].r;
   interface unused_clock3 = fclk[3].c;
   interface unused_reset3 = fclk[3].r;
endmodule

module mkImageonZynqTop(ZynqTop#(ImageonVita));
   let top <- mkZynqTopFromPortal(mkPortalTop);
   return top;
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 Clocks :: *;
import Vector            :: *;
import Connectable       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import AxiMasterSlave    :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import BRAM::*;
import Bscan::*;
import FIFOF::*;
import HDMI::*;
import Imageon::*;

//`define TRACE_AXI
//`define AXI_READ_TIMING

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
   (* prefix="GPIO" *)
   interface LEDS             leds;
   (* prefix="XADC" *)
   interface XADC             xadc;
   (* prefix="hdmi" *)
   interface pins             pins;
   interface Clock unused_clock0;
   interface Clock unused_clock1;
   interface Clock unused_clock2;
   interface Clock unused_clock3;
   interface Reset unused_reset0;
   interface Reset unused_reset1;
   interface Reset unused_reset2;
   interface Reset unused_reset3;
endinterface

typedef (function Module#(PortalTop#(32, 64, ipins)) mkpt(FromPS7 fromPS7)) MkPortalTop#(type ipins);

module [Module] mkZynqTopFromPortal#(MkPortalTop#(ipins) constructor)(ZynqTop#(ipins));
   // B2C converts a bit to a clock, enabling us to break the apparent cycle
   Vector#(4, B2C) fclk <- replicateM(mkB2C());
   B2C mainclock = fclk[0];
   PS7 ps7 <- mkPS7(mainclock.c, mainclock.r, clocked_by mainclock.c, reset_by mainclock.r);

   FromPS7 fromPS7 = (interface FromPS7;
			 interface Clock processing_system7_1_fclk_clk3 = fclk[3].c;
			 interface Clock fmc_imageon_video_clk1 = fclk[2].c; // FIXME
		      endinterface);
   let top <- constructor(fromPS7, clocked_by mainclock.c, reset_by mainclock.r);
   Reg#(Bit#(8)) addrReg <- mkReg(9, clocked_by mainclock.c, reset_by mainclock.r);
   BscanBram#(Bit#(8), Bit#(64)) bscanBram <- mkBscanBram(1, addrReg, clocked_by mainclock.c, reset_by mainclock.r);
   BRAM_Configure bramCfg = defaultValue;
   bramCfg.memorySize = 256;
   bramCfg.latency = 1;
   BRAM2Port#(Bit#(8), Bit#(64)) traceBram <- mkSyncBRAM2Server(bramCfg, mainclock.c, mainclock.r,
								bscanBram.jtagClock, bscanBram.jtagReset);
   mkConnection(bscanBram.bramClient, traceBram.portB);

   ReadOnly#(Bit#(4)) debugReg <- mkNullCrossingWire(mainclock.c, bscanBram.debug());

   let interrupt_bit = top.interrupt ? 1'b1 : 1'b0;

`ifndef TRACE_AXI
   mkConnection(ps7.m_axi_gp[0].client, top.ctrl);
`else

   Vector#(5, FIFOF#(Bit#(64))) bscan_fifos <- replicateM(mkFIFOF(clocked_by mainclock.c, reset_by mainclock.r));

   rule write_bscanBram;
      Bit#(64) data = ?;
      if (bscan_fifos[0].notEmpty) begin
	 data = bscan_fifos[0].first;
	 bscan_fifos[0].deq;
      end
      else if (bscan_fifos[1].notEmpty) begin
	 data = bscan_fifos[1].first;
	 bscan_fifos[1].deq;
      end
      else if (bscan_fifos[2].notEmpty) begin
	 data = bscan_fifos[2].first;
	 bscan_fifos[2].deq;
      end
      else if (bscan_fifos[3].notEmpty) begin
	 data = bscan_fifos[3].first;
	 bscan_fifos[3].deq;
      end
      else begin
	 data = bscan_fifos[4].first;
	 bscan_fifos[4].deq;
      end
      traceBram.portA.request.put(BRAMRequest {write:True, responseOnWrite:False, address:addrReg, datain:data});
      addrReg <= addrReg + 1;
   endrule
   Reg#(Bit#(16)) seqCounter <- mkReg(0, clocked_by mainclock.c, reset_by mainclock.r);
   rule seqinc;
       seqCounter <= seqCounter + 1;
   endrule

   // AXI trace for JTAG
   let m = ps7.m_axi_gp[0].client;
   let s = top.ctrl;
   //mkConnection(m.req_ar, s.req_ar);
   rule connect_req_ar;
       let req <- m.req_ar.get();
       s.req_ar.put(req);
       bscan_fifos[0].enq(
	   {3'h1, interrupt_bit, req.id,
`ifdef AXI_READ_TIMING
                    seqCounter,
`else
                    req.len, req.cache, req.prot, req.size,
                    pack(req.burst == 2'b01), pack(req.lock == 0 && req.qos == 0),
`endif
                    req.address});
   endrule
   //mkConnection(s.resp_read, m.resp_read);
   rule connect_resp_read;
       let resp <- s.resp_read.get();
       m.resp_read.put(resp);
       bscan_fifos[1].enq(
           {3'h2, interrupt_bit, resp.id, resp.resp, resp.last, 13'b0, resp.data});
   endrule
   //mkConnection(m.req_aw, s.req_aw);
   rule connect_req_aw;
       let req <- m.req_aw.get();
       s.req_aw.put(req);
       bscan_fifos[2].enq(
           {3'h3, interrupt_bit, req.id, req.len, req.cache, req.prot, req.size,
                    pack(req.burst == 2'b01), pack(req.lock == 0 && req.qos == 0), req.address});
   endrule
   //mkConnection(m.resp_write, s.resp_write);
   rule connect_resp_write;
       let resp <- m.resp_write.get();
       s.resp_write.put(resp);
       bscan_fifos[3].enq(
           {3'h4, interrupt_bit, resp.id, resp.last, resp.byteEnable, 11'b0, resp.data});
   endrule
   //mkConnection(s.resp_b, m.resp_b);
   rule connect_resp_b;
       let resp <- s.resp_b.get();
       m.resp_b.put(resp);
       bscan_fifos[4].enq(
           {3'h5, interrupt_bit, resp.id, resp.resp, 46'b0});
   endrule
`endif

   mkConnection(top.m_axi, ps7.s_axi_hp[0].axi.server);
   rule send_int_rule;
       ps7.interrupt(interrupt_bit);
   endrule

   // this rule connects the bits to the clock net via B2C
   for (Integer i = 0; i < 4; i = i + 1) begin
      ReadOnly#(Bit#(4)) fclkclk;
      if (i == 0) begin
	 fclkclk = (interface ReadOnly; method Bit#(4) _read(); return ps7.fclkclk; endmethod endinterface);
      end
      else begin
	 fclkclk <- mkNullCrossingWire(fclk[i].c, ps7.fclkclk);
      end
       rule b2c_rule1;
	   fclk[i].inputclock(fclkclk[i]);
	   fclk[i].inputreset(fclkclk[i]);
       endrule
   end

   interface zynq = ps7.pins;
   interface leds = top.leds;
   interface XADC xadc;
       method Bit#(4) gpio;
           return debugReg;
       endmethod
   endinterface
   interface pins = top.pins;

   // these are exported to make bsc happy, and then the ports are disconnected after synthesis
   interface unused_clock0 = fclk[0].c;
   interface unused_reset0 = fclk[0].r;
   interface unused_clock1 = fclk[1].c;
   interface unused_reset1 = fclk[1].r;
   interface unused_clock2 = fclk[2].c;
   interface unused_reset2 = fclk[2].r;
   interface unused_clock3 = fclk[3].c;
   interface unused_reset3 = fclk[3].r;
endmodule

module mkImageonZynqTop(ZynqTop#(ImageonVita));
   let top <- mkZynqTopFromPortal(mkPortalTop);
   return top;
endmodule


delete unused dmabuffers

delete unused dmabuffers

// 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 RingTypes::*;
import RingBuffer::*;
import CopyEngine::*;
import EchoEngine::*;
import NopEngine::*;

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

interface RingIndication;
   method Action setResult(Bit#(1) cmd, Bit#(3) regist, Bit#(32) addr);
   method Action getResult(Bit#(1) cmd, Bit#(3) regist, Bit#(32) addr);
   method Action completion(Bit#(32) command, Bit#(32) tag);
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();

   RingBuffer cmdRing <- mkRingBuffer;
   RingBuffer statusRing <- mkRingBuffer;
   Reg#(Bool) hwenabled <- mkReg(False);
   Reg#(Bool) cmdBusy <- mkReg(False);
   Reg#(Bit#(64)) cmd <- mkReg(0);
   Reg#(Bit#(4)) ii <- mkReg(0);
   Reg#(Bit#(4)) respCtr <- mkReg(0);
   Reg#(Bit#(4)) dispCtr <- mkReg(0);
   Reg#(Bit#(6)) cmdFetchTag <- mkReg(0);
   Reg#(Bool) cmdFetchEn <- mkReg(False);

   let engineselect = pack(cmd)[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.bufferfirst != cmdRing.bufferlast)
		  seq
		     $display ("cmdFetch handle=%h address=%d burst=%h tag=%h", cmdRing.memhandle, cmdRing.bufferlast, 8, cmdFetchTag);
		     cmd_read_chan.readReq.put(
			DmaRequest{handle: cmdRing.memhandle,
			   address: cmdRing.bufferlast, burstLen: 8, tag: cmdFetchTag});
		     cmdRing.pop(64);
		     cmdFetchTag <= cmdFetchTag + 1;
		  endseq
	       endseq
	 endseq
   endseq;

   Stmt cmdDispatch =
   seq
      while (True) seq
	 $display("cmdDispatch FSM TOP");
	 action
	    let rv <- cmd_read_chan.readData.get();
	    cmd <= rv.data;
	    $display("cmdDispatch %h", cmd);
	    //cmdifc.request.put(rv.data);
	 endaction
	 for (dispCtr <= 1; dispCtr < 8; dispCtr <= dispCtr + 1)
	    action
	       let rv <- cmd_read_chan.readData.get();
	       //cmdifc.request.put(rv.data);
	    endaction
      endseq
   endseq;

   Stmt cmdCompletion =
   seq
      while(True) seq
	 while(!(hwenabled && statusRing.notFull())) noAction;
      endseq
   endseq;

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

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

      endseq
   endseq;

   mkAutoFSM (cmdFetch);
   mkAutoFSM (cmdDispatch);
   mkAutoFSM (cmdCompletion);
   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#(32) addr);
	 //cmd_read_chan.readReq.put(addr);
      endmethod

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

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

      method Action get(Bit#(1) _cmd, Bit#(3) regist);
	 if (_cmd == 0)
	    indication.getResult(0, regist,
	       cmdRing.configifc.get(regist));
	 else
	    indication.getResult(0, regist,
	       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 RingTypes::*;
import RingBuffer::*;
import CopyEngine::*;
import EchoEngine::*;
import NopEngine::*;

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

interface RingIndication;
   method Action setResult(Bit#(1) cmd, Bit#(3) regist, Bit#(32) addr);
   method Action getResult(Bit#(1) cmd, Bit#(3) regist, Bit#(32) addr);
   method Action completion(Bit#(32) command, Bit#(32) tag);
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);
   DmaReadServer#(64,8) copy_read_chan <- mkDmaReadBuffer();
   DmaWriteServer#(64,8) copy_write_chan <- mkDmaWriteBuffer();

   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();

   RingBuffer cmdRing <- mkRingBuffer;
   RingBuffer statusRing <- mkRingBuffer;
   Reg#(Bool) hwenabled <- mkReg(False);
   Reg#(Bool) cmdBusy <- mkReg(False);
   Reg#(Bit#(64)) cmd <- mkReg(0);
   Reg#(Bit#(4)) ii <- mkReg(0);
   Reg#(Bit#(4)) respCtr <- mkReg(0);
   Reg#(Bit#(4)) dispCtr <- mkReg(0);
   Reg#(Bit#(6)) cmdFetchTag <- mkReg(0);
   Reg#(Bool) cmdFetchEn <- mkReg(False);

   let engineselect = pack(cmd)[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.bufferfirst != cmdRing.bufferlast)
		  seq
		     $display ("cmdFetch handle=%h address=%d burst=%h tag=%h", cmdRing.memhandle, cmdRing.bufferlast, 8, cmdFetchTag);
		     cmd_read_chan.readReq.put(
			DmaRequest{handle: cmdRing.memhandle,
			   address: cmdRing.bufferlast, burstLen: 8, tag: cmdFetchTag});
		     cmdRing.pop(64);
		     cmdFetchTag <= cmdFetchTag + 1;
		  endseq
	       endseq
	 endseq
   endseq;

   Stmt cmdDispatch =
   seq
      while (True) seq
	 $display("cmdDispatch FSM TOP");
	 action
	    let rv <- cmd_read_chan.readData.get();
	    cmd <= rv.data;
	    $display("cmdDispatch %h", cmd);
	    //cmdifc.request.put(rv.data);
	 endaction
	 for (dispCtr <= 1; dispCtr < 8; dispCtr <= dispCtr + 1)
	    action
	       let rv <- cmd_read_chan.readData.get();
	       //cmdifc.request.put(rv.data);
	    endaction
      endseq
   endseq;

   Stmt cmdCompletion =
   seq
      while(True) seq
	 while(!(hwenabled && statusRing.notFull())) noAction;
      endseq
   endseq;

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

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

      endseq
   endseq;

   mkAutoFSM (cmdFetch);
   mkAutoFSM (cmdDispatch);
   mkAutoFSM (cmdCompletion);
   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#(32) addr);
	 //cmd_read_chan.readReq.put(addr);
      endmethod

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

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

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

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

endmodule


added a comment about J, K, and N

added a comment about J, K, and N

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

   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)));

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

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)
	     , 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;

   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)));

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