removing unneeded complexity in computation of the tag

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

/*
 * Implementation of:
 *    MP algorithm on pages 7-11 from "Pattern Matching Algorithms" by
 *       Alberto Apostolico, Zvi Galil, 1997
 */

import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import GetPutF::*;
import Vector::*;
import BRAM::*;
import Gearbox::*;
import Connectable::*;

import AxiMasterSlave::*;
import Dma::*;
import DmaUtils::*;
import Dma2BRAM::*;

interface MPEngine;
   method Action setup(Bit#(32) needlePointer, Bit#(32) mpNextPointer, Bit#(32) needle_len);
   method Action search(Bit#(32) haystackPointer, Bit#(32) haystack_len, Bit#(32) haystack_base);
endinterface

typedef Bit#(8) Char;
typedef Bit#(64) DWord;
typedef Bit#(32) Word;

typedef 1024 MaxNeedleLen;
typedef Bit#(TLog#(MaxNeedleLen)) NeedleIdx;

typedef enum {Idle, Ready, Run} Stage deriving (Eq, Bits);

module mkMPEngine#(FIFOF#(void) compf,
		   FIFOF#(void) conff,
		   FIFOF#(Int#(32)) locf,
		   DmaReadServer#(busWidth)   haystack_read_server,
		   DmaReadServer#(busWidth)     needle_read_server,
		   DmaReadServer#(busWidth)    mp_next_read_server )(MPEngine)

   provisos(Add#(a__, 8, busWidth),
	    Div#(busWidth,8,nc),
	    Mul#(nc,8,busWidth),
	    Add#(1, b__, nc),
	    Add#(c__, 32, busWidth),
	    Add#(1, d__, TDiv#(busWidth, 32)),
	    Mul#(TDiv#(busWidth, 32), 32, busWidth));

   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   BRAM2Port#(NeedleIdx, Char) needle  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(NeedleIdx, Bit#(32)) mpNext <- mkBRAM2Server(defaultValue);
   Gearbox#(nc,1,Char) haystack <- mkNto1Gearbox(clk,rst,clk,rst);

   Reg#(Stage)    stage <- mkReg(Idle);
   Reg#(Bit#(32)) needleLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackBase <- mkReg(0);
   Reg#(Bit#(32)) jReg <- mkReg(0); // offset in haystack
   Reg#(Bit#(32)) iReg <- mkReg(0); // offset in needle
   Reg#(Bit#(2))  epochReg <- mkReg(0);
   Reg#(Bit#(6))  dmaTag <- mkReg(0);
   Reg#(Bit#(32)) haystackOff <- mkReg(0);
   Reg#(DmaPointer) haystackPointer <- mkReg(0);

   BRAMReadClient#(NeedleIdx,busWidth) n2b <- mkBRAMReadClient(needle.portB);
   mkConnection(n2b.dmaClient, needle_read_server);
   BRAMReadClient#(NeedleIdx,busWidth) mp2b <- mkBRAMReadClient(mpNext.portB);
   mkConnection(mp2b.dmaClient, mp_next_read_server);
   FIFOF#(Tuple2#(Bit#(2),Bit#(32))) efifo <- mkSizedFIFOF(2);

   rule finish_setup;
      $display("finish_setup");
      let x <- n2b.finish;
      let y <- mp2b.finish;
      stage <= Ready;
      conff.enq(?);
   endrule

   rule haystackReq (stage == Run && haystackOff < extend(haystackLenReg));
      //$display("haystackReq %x", haystackOff);
      haystack_read_server.readReq.put(DmaRequest {pointer: haystackPointer, offset: extend(haystackBase+haystackOff), burstLen: 1, tag: dmaTag});
      haystackOff <= haystackOff + fromInteger(valueOf(nc));
   endrule

   rule haystackResp;
      //$display("haystackResp");
      let rv <- haystack_read_server.readData.get;
      Vector#(nc,Char) pv = unpack(rv.data);
      if(rv.tag == dmaTag)
	 haystack.enq(pv);
   endrule

   rule haystackDrain(stage != Run);
      //$display("haystackDrain");
      haystack.deq;
   endrule

   rule bramDrain(stage != Run);
      //$display("mpNextDrain");
      let x <- mpNext.portA.response.get;
      let y <- needle.portA.response.get;
      efifo.deq;
   endrule

   rule matchNeedleReq(stage == Run);
      //$display(" matchNeedleReq %d %d", epochReg, iReg);
      needle.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1)});
      mpNext.portA.request.put(BRAMRequest{write:False, address: truncate(iReg)});
      efifo.enq(tuple2(epochReg,iReg));
      iReg <= iReg+1;
   endrule

   rule matchNeedleResp(stage == Run);
      let nv <- needle.portA.response.get;
      let mp <- mpNext.portA.response.get;
      let epoch = tpl_1(efifo.first);
      efifo.deq;
      //$display("matchNeedleResp %d %d", epochReg, epoch);
      if (epoch == epochReg) begin
	 let n = haystackLenReg;
	 let m = needleLenReg;
	 let hv = haystack.first;
	 let i = tpl_2(efifo.first);
	 let j = jReg;
	 //$display("feck %d %d %d %d %c", n, m, i, j, hv[0]);
	 if (j > n) begin
	    // jReg points to the end of the haystack; we are done
	    compf.enq(?);
	    stage <= Ready;
	    //$display("end of search %d", j);
	 end
	 else if (i==m+1) begin
	    // iReg points to the end of the needle; we have a match
	    //$display("string match %d", j);
	    locf.enq(unpack(haystackBase+j-i));
	    epochReg <= epochReg + 1;
	    iReg <= 1;
	 end
	 else if ((i>0) && (nv != hv[0])) begin
	    // mismatch betwen head of haystack and head of needle; rewind iReg
	    //$display("char mismatch %d %d MP_Next[i]=%d", i, j, mp);
	    epochReg <= epochReg + 1;
	    iReg <= mp;
	 end
	 else begin
	    // match between head of needle and head of haystack; increment haystack
	    //$display("char match %d %d", i, j);
	    jReg <= j+1;
	    haystack.deq;
	 end
      end
      else begin
	 //$display("discard");
	 noAction;
      end
   endrule

   method Action setup(Bit#(32) needle_pointer, Bit#(32) mpNext_pointer, Bit#(32) needle_len);
      needleLenReg <= extend(needle_len);
      n2b.start(needle_pointer, 0, pack(truncate(needle_len+1)), 0);
      mp2b.start(mpNext_pointer, 0, pack(truncate(needle_len+1)), 0);
      jReg <= 0;
      iReg <= 0;
   endmethod

   method Action search(Bit#(32) haystack_pointer, Bit#(32) haystack_len, Bit#(32) haystack_base) if (stage == Ready && !efifo.notEmpty && !haystack.notEmpty);
      $display("search %d %d",  haystack_base, haystack_len);
      haystackLenReg <= extend(haystack_len);
      haystackPointer <= haystack_pointer;
      haystackBase <= extend(haystack_base);
      dmaTag   <= dmaTag+1;
      haystackOff <= 0;
      stage <= Run;
      iReg <= 1;
      jReg <= 1;
      efifo.clear;
      epochReg <= 0;
   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.

/*
 * Implementation of:
 *    MP algorithm on pages 7-11 from "Pattern Matching Algorithms" by
 *       Alberto Apostolico, Zvi Galil, 1997
 */

import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import GetPutF::*;
import Vector::*;
import BRAM::*;
import Gearbox::*;
import Connectable::*;

import AxiMasterSlave::*;
import Dma::*;
import DmaUtils::*;
import Dma2BRAM::*;

interface MPEngine;
   method Action setup(Bit#(32) needlePointer, Bit#(32) mpNextPointer, Bit#(32) needle_len);
   method Action search(Bit#(32) haystackPointer, Bit#(32) haystack_len, Bit#(32) haystack_base);
endinterface

typedef Bit#(8) Char;
typedef Bit#(64) DWord;
typedef Bit#(32) Word;

typedef 1024 MaxNeedleLen;
typedef Bit#(TLog#(MaxNeedleLen)) NeedleIdx;

typedef enum {Idle, Ready, Run} Stage deriving (Eq, Bits);

module mkMPEngine#(FIFOF#(void) compf,
		   FIFOF#(void) conff,
		   FIFOF#(Int#(32)) locf,
		   DmaReadServer#(busWidth)   haystack_read_server,
		   DmaReadServer#(busWidth)     needle_read_server,
		   DmaReadServer#(busWidth)    mp_next_read_server )(MPEngine)

   provisos(Add#(a__, 8, busWidth),
	    Div#(busWidth,8,nc),
	    Mul#(nc,8,busWidth),
	    Add#(1, b__, nc),
	    Add#(c__, 32, busWidth),
	    Add#(1, d__, TDiv#(busWidth, 32)),
	    Mul#(TDiv#(busWidth, 32), 32, busWidth));

   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   BRAM2Port#(NeedleIdx, Char) needle  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(NeedleIdx, Bit#(32)) mpNext <- mkBRAM2Server(defaultValue);
   Gearbox#(nc,1,Char) haystack <- mkNto1Gearbox(clk,rst,clk,rst);

   Reg#(Stage)    stage <- mkReg(Idle);
   Reg#(Bit#(32)) needleLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackBase <- mkReg(0);
   Reg#(Bit#(32)) jReg <- mkReg(0); // offset in haystack
   Reg#(Bit#(32)) iReg <- mkReg(0); // offset in needle
   Reg#(Bit#(2))  epochReg <- mkReg(0);
   Reg#(Bit#(6))  dmaTag <- mkReg(2); // 0 and 1 are reserved for the BRAMReadClients
   Reg#(Bit#(32)) haystackOff <- mkReg(0);
   Reg#(DmaPointer) haystackPointer <- mkReg(0);

   BRAMReadClient#(NeedleIdx,busWidth) n2b <- mkBRAMReadClient(needle.portB);
   mkConnection(n2b.dmaClient, needle_read_server);
   BRAMReadClient#(NeedleIdx,busWidth) mp2b <- mkBRAMReadClient(mpNext.portB);
   mkConnection(mp2b.dmaClient, mp_next_read_server);
   FIFOF#(Tuple2#(Bit#(2),Bit#(32))) efifo <- mkSizedFIFOF(2);

   rule finish_setup;
      $display("finish_setup");
      let x <- n2b.finish;
      let y <- mp2b.finish;
      stage <= Ready;
      conff.enq(?);
   endrule

   rule haystackReq (stage == Run && haystackOff < extend(haystackLenReg));
      //$display("haystackReq %x", haystackOff);
      haystack_read_server.readReq.put(DmaRequest {pointer: haystackPointer, offset: extend(haystackBase+haystackOff), burstLen: 1, tag: dmaTag});
      haystackOff <= haystackOff + fromInteger(valueOf(nc));
   endrule

   rule haystackResp;
      //$display("haystackResp");
      let rv <- haystack_read_server.readData.get;
      Vector#(nc,Char) pv = unpack(rv.data);
      if(rv.tag == dmaTag)
	 haystack.enq(pv);
   endrule

   rule haystackDrain(stage != Run);
      //$display("haystackDrain");
      haystack.deq;
   endrule

   rule bramDrain(stage != Run);
      //$display("mpNextDrain");
      let x <- mpNext.portA.response.get;
      let y <- needle.portA.response.get;
      efifo.deq;
   endrule

   rule matchNeedleReq(stage == Run);
      //$display(" matchNeedleReq %d %d", epochReg, iReg);
      needle.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1)});
      mpNext.portA.request.put(BRAMRequest{write:False, address: truncate(iReg)});
      efifo.enq(tuple2(epochReg,iReg));
      iReg <= iReg+1;
   endrule

   rule matchNeedleResp(stage == Run);
      let nv <- needle.portA.response.get;
      let mp <- mpNext.portA.response.get;
      let epoch = tpl_1(efifo.first);
      efifo.deq;
      //$display("matchNeedleResp %d %d", epochReg, epoch);
      if (epoch == epochReg) begin
	 let n = haystackLenReg;
	 let m = needleLenReg;
	 let hv = haystack.first;
	 let i = tpl_2(efifo.first);
	 let j = jReg;
	 //$display("feck %d %d %d %d %c", n, m, i, j, hv[0]);
	 if (j > n) begin
	    // jReg points to the end of the haystack; we are done
	    compf.enq(?);
	    stage <= Ready;
	    //$display("end of search %d", j);
	 end
	 else if (i==m+1) begin
	    // iReg points to the end of the needle; we have a match
	    //$display("string match %d", j);
	    locf.enq(unpack(haystackBase+j-i));
	    epochReg <= epochReg + 1;
	    iReg <= 1;
	 end
	 else if ((i>0) && (nv != hv[0])) begin
	    // mismatch betwen head of haystack and head of needle; rewind iReg
	    //$display("char mismatch %d %d MP_Next[i]=%d", i, j, mp);
	    epochReg <= epochReg + 1;
	    iReg <= mp;
	 end
	 else begin
	    // match between head of needle and head of haystack; increment haystack
	    //$display("char match %d %d", i, j);
	    jReg <= j+1;
	    haystack.deq;
	 end
      end
      else begin
	 //$display("discard");
	 noAction;
      end
   endrule

   method Action setup(Bit#(32) needle_pointer, Bit#(32) mpNext_pointer, Bit#(32) needle_len);
      needleLenReg <= extend(needle_len);
      n2b.start(needle_pointer, 0, pack(truncate(needle_len+1)), 0);
      mp2b.start(mpNext_pointer, 0, pack(truncate(needle_len+1)), 0);
      jReg <= 0;
      iReg <= 0;
   endmethod

   method Action search(Bit#(32) haystack_pointer, Bit#(32) haystack_len, Bit#(32) haystack_base) if (stage == Ready && !efifo.notEmpty && !haystack.notEmpty);
      $display("search %d %d",  haystack_base, haystack_len);
      haystackLenReg <= extend(haystack_len);
      haystackPointer <= haystack_pointer;
      haystackBase <= extend(haystack_base);
      dmaTag   <= dmaTag == maxBound ? 2 : dmaTag+1; // 0 and 1 are reserved for the BRAMReadClients
      haystackOff <= 0;
      stage <= Run;
      iReg <= 1;
      jReg <= 1;
      efifo.clear;
      epochReg <= 0;
   endmethod

endmodule


corresponding CLOCK_DECL/CLOCK_ARG for bsv/Zc706.bsv

Change-Id: I6987fccc3657efa1ce41437ff0734a3c6541eb28

// 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 XilinxCells       :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import FIFOF::*;
import ConnectableWithTrace::*;
import CtrlMux::*;
import AxiMasterSlave    :: *;
import AxiDma            :: *;

interface I2C_Pins;
   interface Inout#(Bit#(1)) scl;
   interface Inout#(Bit#(1)) sda;
endinterface

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
//   (* prefix="GPIO" *)
//   interface LEDS             leds;
//   (* prefix="XADC" *)
//   interface XADC             xadc;
`ifdef USE_I2C
   (* prefix="I2C" *)
   interface I2C_Pins         i2c;
`endif
   (* prefix="" *)
   interface pins             pins;
   interface Vector#(4, Clock) deleteme_unused_clock;
   interface Vector#(4, Reset) deleteme_unused_reset;
endinterface

`ifdef USES_FCLK1
`define CLOCK_DECL Clock clk1
`define CLOCK_ARG  ps7.fclkclk[1],
`else
`define CLOCK_DECL
`define CLOCK_ARG
`endif

typedef (function Module#(PortalTop#(32, 64, ipins, nMasters)) mkpt(`CLOCK_DECL)) MkPortalTop#(type ipins, numeric type nMasters);

module [Module] mkZynqTopFromPortal#(MkPortalTop#(ipins,nMasters) constructor)(ZynqTop#(ipins))
   provisos(Add#(a__,nMasters,4));

   PS7 ps7 <- mkPS7();
   Clock mainclock = ps7.fclkclk[0];
   Clock clock200 = ps7.fclkclk[3];
   Reset mainreset = ps7.fclkreset[0];
   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by clock200, reset_by mainreset);

   let tscl <- mkIOBUF(~ps7.i2c[1].scltn, ps7.i2c[1].sclo, clocked_by mainclock, reset_by mainreset);
   let tsda <- mkIOBUF(~ps7.i2c[1].sdatn, ps7.i2c[1].sdao, clocked_by mainclock, reset_by mainreset);
   rule sdai;
      ps7.i2c[1].sdai(tsda.o);
      ps7.i2c[1].scli(tscl.o);
   endrule

   let top <- constructor(`CLOCK_ARG clocked_by mainclock, reset_by mainreset);
   mkConnection(ps7, top, clocked_by mainclock, reset_by mainreset);

   let intr_mux <- mkInterruptMux(top.interrupt);
   rule send_int_rule;
      ps7.interrupt(pack(intr_mux));
   endrule

   module bufferClock#(Integer i)(Clock); let bc <- mkClockBUFG(clocked_by ps7.fclkclk[i]); return bc; endmodule
   module bufferReset#(Integer i)(Reset); let rc <- mkAsyncReset(2, ps7.fclkreset[i], ps7.fclkclk[0]); return rc; endmodule
   Vector#(4, Clock) unused_clock <- genWithM(bufferClock);
   Vector#(4, Reset) unused_reset <- genWithM(bufferReset);
   interface zynq = ps7.pins;
   // interface leds = top.leds;
   // interface XADC xadc;
   //     method Bit#(4) gpio;
   //         return 0;
   //     endmethod
   // endinterface
`ifdef USE_I2C
   interface I2C_Pins i2c;
      interface Inout scl = tscl.io;
      interface Inout sda = tsda.io;
   endinterface
`endif
   interface pins = top.pins;
   interface deleteme_unused_clock = unused_clock;
   interface deleteme_unused_reset = unused_reset;
endmodule

`ifndef PinType
`define PinType Empty
`endif
module mkZynqTop(ZynqTop#(`PinType));
   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 XilinxCells       :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import FIFOF::*;
import ConnectableWithTrace::*;
import CtrlMux::*;
import AxiMasterSlave    :: *;
import AxiDma            :: *;

interface I2C_Pins;
   interface Inout#(Bit#(1)) scl;
   interface Inout#(Bit#(1)) sda;
endinterface

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
//   (* prefix="GPIO" *)
//   interface LEDS             leds;
//   (* prefix="XADC" *)
//   interface XADC             xadc;
`ifdef USE_I2C
   (* prefix="I2C" *)
   interface I2C_Pins         i2c;
`endif
   (* prefix="" *)
   interface pins             pins;
   interface Vector#(4, Clock) deleteme_unused_clock;
   interface Vector#(4, Reset) deleteme_unused_reset;
endinterface

typedef (function Module#(PortalTop#(32, 64, ipins, nMasters)) mkpt()) MkPortalTop#(type ipins, numeric type nMasters);

module [Module] mkZynqTopFromPortal#(MkPortalTop#(ipins,nMasters) constructor)(ZynqTop#(ipins))
   provisos(Add#(a__,nMasters,4));

   PS7 ps7 <- mkPS7();
   Clock mainclock = ps7.fclkclk[0];
   Clock clock200 = ps7.fclkclk[3];
   Reset mainreset = ps7.fclkreset[0];
   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by clock200, reset_by mainreset);

   let tscl <- mkIOBUF(~ps7.i2c[1].scltn, ps7.i2c[1].sclo, clocked_by mainclock, reset_by mainreset);
   let tsda <- mkIOBUF(~ps7.i2c[1].sdatn, ps7.i2c[1].sdao, clocked_by mainclock, reset_by mainreset);
   rule sdai;
      ps7.i2c[1].sdai(tsda.o);
      ps7.i2c[1].scli(tscl.o);
   endrule

   let top <- constructor(clocked_by mainclock, reset_by mainreset);
   mkConnection(ps7, top, clocked_by mainclock, reset_by mainreset);

   let intr_mux <- mkInterruptMux(top.interrupt);
   rule send_int_rule;
      ps7.interrupt(pack(intr_mux));
   endrule

   module bufferClock#(Integer i)(Clock); let bc <- mkClockBUFG(clocked_by ps7.fclkclk[i]); return bc; endmodule
   module bufferReset#(Integer i)(Reset); let rc <- mkAsyncReset(2, ps7.fclkreset[i], ps7.fclkclk[0]); return rc; endmodule
   Vector#(4, Clock) unused_clock <- genWithM(bufferClock);
   Vector#(4, Reset) unused_reset <- genWithM(bufferReset);
   interface zynq = ps7.pins;
   // interface leds = top.leds;
   // interface XADC xadc;
   //     method Bit#(4) gpio;
   //         return 0;
   //     endmethod
   // endinterface
`ifdef USE_I2C
   interface I2C_Pins i2c;
      interface Inout scl = tscl.io;
      interface Inout sda = tsda.io;
   endinterface
`endif
   interface pins = top.pins;
   interface deleteme_unused_clock = unused_clock;
   interface deleteme_unused_reset = unused_reset;
endmodule

`ifndef PinType
`define PinType Empty
`endif
module mkZynqTop(ZynqTop#(`PinType));
   let top <- mkZynqTopFromPortal(mkPortalTop);
   return top;
endmodule


PcieTracer: changed FIFO1 to FIFO

PcieTracer: changed FIFO1 to FIFO

// 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 Vector            :: *;
import Clocks          :: *;
import GetPut            :: *;
import Connectable    :: *;
import FIFO              :: *;
import FIFOF        :: *;
import PCIE               :: *;
import BRAM         :: *;
import BramMux        :: *;

typedef 11 TlpTraceAddrSize;
typedef TAdd#(TlpTraceAddrSize,1) TlpTraceAddrSize1;

typedef struct {
    Bit#(32) timestamp;
    Bit#(7) source;   // 4==frombus 8=tobus
    TLPData#(16) tlp; // 153 bits
} TimestampedTlpData deriving (Bits);
typedef SizeOf#(TimestampedTlpData) TimestampedTlpDataSize;
typedef SizeOf#(TLPData#(16)) TlpData16Size;
typedef SizeOf#(TLPCompletionHeader) TLPCompletionHeaderSize;
interface TlpTrace;
   interface Get#(TimestampedTlpData) tlp;
endinterface

interface TlpTraceServer;
   interface Reg#(Bool)     tlpTracing;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceLimit;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddr;
   interface BRAMServer#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) bramServer;
`ifdef PCIE_BSCAN
   interface BRAMServer#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) bscanBramServer;
`endif
endinterface
interface TlpTraceClient;
   interface Reg#(Bool)     tlpTracing;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceLimit;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddr;
   interface BRAMClient#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) bramClient;
endinterface

interface PcieTracer;
   interface Client#(TLPData#(16), TLPData#(16)) pci;
   interface Put#(TimestampedTlpData) trace;
   interface Server#(TLPData#(16), TLPData#(16)) bus;
   interface TlpTraceServer traceServer;
endinterface: PcieTracer

// The PCIe-to-AXI bridge puts all of the elements together
(* synthesize *)
module mkPcieTracer(PcieTracer);
   // Trace Support
   Reg#(Bool) tlpTracingReg        <- mkReg(False);
   Reg#(Bit#(TlpTraceAddrSize)) tlpTraceLimitReg <- mkReg(0);
   FIFOF#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddrFifo <- mkFIFOF();
   Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddrReg <- mkReg(0);
   Integer memorySize = 2**valueOf(TlpTraceAddrSize);

   BRAM_Configure bramCfg = defaultValue;
   bramCfg.memorySize = memorySize;
   bramCfg.latency = 1;
`ifdef PCIE_BSCAN
   BRAM2Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) fromPcieTraceBram <- mkBRAM2Server(bramCfg);
   BRAM2Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) toPcieTraceBram <- mkBRAM2Server(bramCfg);
`else
   BRAM1Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) fromPcieTraceBram <- mkBRAM1Server(bramCfg);
   BRAM1Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) toPcieTraceBram <- mkBRAM1Server(bramCfg);
`endif
   Vector#(2, BRAMServer#(Bit#(TlpTraceAddrSize), TimestampedTlpData)) bramServers;
   bramServers[0] = fromPcieTraceBram.portA;
   bramServers[1] =   toPcieTraceBram.portA;
   BramServerMux#(TAdd#(TlpTraceAddrSize,1), TimestampedTlpData) bramMuxReg <- mkBramServerMux(bramServers);

`ifdef PCIE_BSCAN
   Vector#(2, BRAMServer#(Bit#(TlpTraceAddrSize), TimestampedTlpData)) bscanBramServers;
   bscanBramServers[0] = fromPcieTraceBram.portB;
   bscanBramServers[1] =   toPcieTraceBram.portB;
   BramServerMux#(TAdd#(TlpTraceAddrSize,1), TimestampedTlpData) bscanBramMux <- mkBramServerMux(bscanBramServers);
`endif

   Reg#(Bit#(32)) timestamp <- mkReg(0);
   rule incTimestamp;
       timestamp <= timestamp + 1;
   endrule
//   rule endTrace if (tlpTracingReg && tlpTraceLimitReg != 0 && tlpTraceBramWrAddr > truncate(tlpTraceLimitReg));
//       tlpTracingReg <= False;
//   endrule

   FIFO#(TLPData#(16)) tlpFromBusFifo <- mkFIFO();
   FIFO#(TLPData#(16)) tlpToBusFifo <- mkFIFO();
   FIFO#(TLPData#(16)) tlpBusResponseFifo <- mkFIFO();

   Reg#(Bool) skippingIncomingTlps <- mkReg(False);
   FIFO#(Bool) isRootBroadcastMessage <- mkFIFO();
   PulseWire fromPcie <- mkPulseWire;
   PulseWire   toPcie <- mkPulseWire;
   Wire#(TLPData#(16)) fromPcieTlp <- mkDWire(unpack(0));
   Wire#(TLPData#(16))   toPcieTlp <- mkDWire(unpack(0));

   rule sniffTlpFromBus;
      let tlp <- toGet(tlpFromBusFifo).get();
      tlpBusResponseFifo.enq(tlp);

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      // skip root_broadcast_messages sent to tlp.hit 0
      isRootBroadcastMessage.enq(tlp.sof && tlp.hit == 0 && hdr_3dw.pkttype != COMPLETION);

   endrule

   rule doTracing if (fromPcie || toPcie);
      TimestampedTlpData fromttd = fromPcie ? TimestampedTlpData { timestamp: timestamp, source: 7'h04, tlp: fromPcieTlp } : unpack(0);
      let writeAddr = tlpTraceBramWrAddrReg;
      if (tlpTraceBramWrAddrFifo.notEmpty)
	 writeAddr <- toGet(tlpTraceBramWrAddrFifo).get();

      fromPcieTraceBram.portA.request.put(BRAMRequest{ write: True, responseOnWrite: False, address: writeAddr, datain: fromttd });

      TimestampedTlpData   tottd = toPcie ? TimestampedTlpData { timestamp: timestamp, source: 7'h08, tlp: toPcieTlp } : unpack(0);
      toPcieTraceBram.portA.request.put(BRAMRequest{ write: True, responseOnWrite: False, address: writeAddr, datain: tottd });

      tlpTraceBramWrAddrReg <= writeAddr + 1;
   endrule

   interface Server     bus;
      interface Get response;
           method ActionValue#(TLPData#(16)) get();
	      let tlp <- toGet(tlpBusResponseFifo).get();

	      if (tlpTracingReg) begin
		 if (tlp.sof && isRootBroadcastMessage.first) begin
 		    skippingIncomingTlps <= True;
		 end
		 else if (skippingIncomingTlps && !tlp.sof) begin
		    // do nothing
		 end
		 else begin
		    fromPcie.send();
		    fromPcieTlp <= tlp;
		    skippingIncomingTlps <= False;
		 end
	      end

	      isRootBroadcastMessage.deq();
	      return tlp;
	   endmethod
      endinterface

       interface Put request;
           method Action put(TLPData#(16) tlp);
           tlpToBusFifo.enq(tlp);
           if (tlpTracingReg) begin
	      toPcie.send();
	      toPcieTlp <= tlp;
           end
           endmethod
       endinterface
   endinterface

   interface Client    pci;
      interface request = toGet(tlpToBusFifo);
      interface response = toPut(tlpFromBusFifo);
   endinterface
   interface Put trace;
       method Action put(TimestampedTlpData ttd) if (!fromPcie && !toPcie);
	   if (tlpTracingReg) begin
	       ttd.timestamp = timestamp;
	       toPcieTraceBram.portA.request.put(BRAMRequest{ write: True, responseOnWrite: False, address: truncate(tlpTraceBramWrAddrReg), datain: ttd });
	       tlpTraceBramWrAddrReg <= tlpTraceBramWrAddrReg + 1;
	   end
       endmethod
   endinterface: trace
   interface TlpTraceServer traceServer;
      interface Reg tlpTracing    = tlpTracingReg;
      interface Reg tlpTraceLimit = tlpTraceLimitReg;
      interface Reg tlpTraceBramWrAddr;
	 method Bit#(TlpTraceAddrSize) _read(); return tlpTraceBramWrAddrReg; endmethod
	 method Action _write(Bit#(TlpTraceAddrSize) v); tlpTraceBramWrAddrFifo.enq(v); endmethod
      endinterface
      interface Server bramServer = bramMuxReg.bramServer;
`ifdef PCIE_BSCAN
      interface Server bscanBramServer = bscanBramMux.bramServer;
`endif
   endinterface
endmodule: mkPcieTracer

instance Connectable#(TlpTraceClient,TlpTraceServer);
   module mkConnection#(TlpTraceClient client, TlpTraceServer tracer)(Empty);
      mkConnection(client.bramClient, tracer.bramServer);
      Reg#(Bool)                           tlpTracingReg <- mkReg(False);
      Reg#(Bit#(TlpTraceAddrSize))      tlpTraceLimitReg <- mkReg(0);
      Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddrReg <- mkReg(0);
      rule tracingRule if (tlpTracingReg != client.tlpTracing);
	 tracer.tlpTracing <= client.tlpTracing;
	 tlpTracingReg <= client.tlpTracing;
      endrule
      rule traceLimitRule if (tlpTraceLimitReg != client.tlpTraceLimit);
	 tracer.tlpTraceLimit <= client.tlpTraceLimit;
	 tlpTraceLimitReg <= client.tlpTraceLimit;
      endrule
      // both client and server update the tlpBramWrAddr
      rule traceBramWrAddrRule if (tlpTraceBramWrAddrReg != client.tlpTraceBramWrAddr);
	 tracer.tlpTraceBramWrAddr <= client.tlpTraceBramWrAddr;
	 tlpTraceBramWrAddrReg <= client.tlpTraceBramWrAddr;
      endrule
   endmodule
endinstance

// 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 Vector            :: *;
import Clocks          :: *;
import GetPut            :: *;
import Connectable    :: *;
import FIFO              :: *;
import FIFOF        :: *;
import PCIE               :: *;
import BRAM         :: *;
import BramMux        :: *;

typedef 11 TlpTraceAddrSize;
typedef TAdd#(TlpTraceAddrSize,1) TlpTraceAddrSize1;

typedef struct {
    Bit#(32) timestamp;
    Bit#(7) source;   // 4==frombus 8=tobus
    TLPData#(16) tlp; // 153 bits
} TimestampedTlpData deriving (Bits);
typedef SizeOf#(TimestampedTlpData) TimestampedTlpDataSize;
typedef SizeOf#(TLPData#(16)) TlpData16Size;
typedef SizeOf#(TLPCompletionHeader) TLPCompletionHeaderSize;
interface TlpTrace;
   interface Get#(TimestampedTlpData) tlp;
endinterface

interface TlpTraceServer;
   interface Reg#(Bool)     tlpTracing;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceLimit;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddr;
   interface BRAMServer#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) bramServer;
`ifdef PCIE_BSCAN
   interface BRAMServer#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) bscanBramServer;
`endif
endinterface
interface TlpTraceClient;
   interface Reg#(Bool)     tlpTracing;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceLimit;
   interface Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddr;
   interface BRAMClient#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) bramClient;
endinterface

interface PcieTracer;
   interface Client#(TLPData#(16), TLPData#(16)) pci;
   interface Put#(TimestampedTlpData) trace;
   interface Server#(TLPData#(16), TLPData#(16)) bus;
   interface TlpTraceServer traceServer;
endinterface: PcieTracer

// The PCIe-to-AXI bridge puts all of the elements together
(* synthesize *)
module mkPcieTracer(PcieTracer);
   // Trace Support
   Reg#(Bool) tlpTracingReg        <- mkReg(False);
   Reg#(Bit#(TlpTraceAddrSize)) tlpTraceLimitReg <- mkReg(0);
   FIFOF#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddrFifo <- mkFIFOF();
   Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddrReg <- mkReg(0);
   Integer memorySize = 2**valueOf(TlpTraceAddrSize);

   BRAM_Configure bramCfg = defaultValue;
   bramCfg.memorySize = memorySize;
   bramCfg.latency = 1;
`ifdef PCIE_BSCAN
   BRAM2Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) fromPcieTraceBram <- mkBRAM2Server(bramCfg);
   BRAM2Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) toPcieTraceBram <- mkBRAM2Server(bramCfg);
`else
   BRAM1Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) fromPcieTraceBram <- mkBRAM1Server(bramCfg);
   BRAM1Port#(Bit#(TlpTraceAddrSize), TimestampedTlpData) toPcieTraceBram <- mkBRAM1Server(bramCfg);
`endif
   Vector#(2, BRAMServer#(Bit#(TlpTraceAddrSize), TimestampedTlpData)) bramServers;
   bramServers[0] = fromPcieTraceBram.portA;
   bramServers[1] =   toPcieTraceBram.portA;
   BramServerMux#(TAdd#(TlpTraceAddrSize,1), TimestampedTlpData) bramMuxReg <- mkBramServerMux(bramServers);

`ifdef PCIE_BSCAN
   Vector#(2, BRAMServer#(Bit#(TlpTraceAddrSize), TimestampedTlpData)) bscanBramServers;
   bscanBramServers[0] = fromPcieTraceBram.portB;
   bscanBramServers[1] =   toPcieTraceBram.portB;
   BramServerMux#(TAdd#(TlpTraceAddrSize,1), TimestampedTlpData) bscanBramMux <- mkBramServerMux(bscanBramServers);
`endif

   Reg#(Bit#(32)) timestamp <- mkReg(0);
   rule incTimestamp;
       timestamp <= timestamp + 1;
   endrule
//   rule endTrace if (tlpTracingReg && tlpTraceLimitReg != 0 && tlpTraceBramWrAddr > truncate(tlpTraceLimitReg));
//       tlpTracingReg <= False;
//   endrule

   FIFO#(TLPData#(16)) tlpFromBusFifo <- mkFIFO();
   FIFO#(TLPData#(16)) tlpToBusFifo <- mkFIFO();
   FIFO#(TLPData#(16)) tlpBusResponseFifo <- mkFIFO();

   Reg#(Bool) skippingIncomingTlps <- mkReg(False);
   FIFO#(Bool) isRootBroadcastMessage <- mkFIFO1();
   PulseWire fromPcie <- mkPulseWire;
   PulseWire   toPcie <- mkPulseWire;
   Wire#(TLPData#(16)) fromPcieTlp <- mkDWire(unpack(0));
   Wire#(TLPData#(16))   toPcieTlp <- mkDWire(unpack(0));

   rule sniffTlpFromBus;
      let tlp <- toGet(tlpFromBusFifo).get();
      tlpBusResponseFifo.enq(tlp);

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      // skip root_broadcast_messages sent to tlp.hit 0
      isRootBroadcastMessage.enq(tlp.sof && tlp.hit == 0 && hdr_3dw.pkttype != COMPLETION);

   endrule

   rule doTracing if (fromPcie || toPcie);
      TimestampedTlpData fromttd = fromPcie ? TimestampedTlpData { timestamp: timestamp, source: 7'h04, tlp: fromPcieTlp } : unpack(0);
      let writeAddr = tlpTraceBramWrAddrReg;
      if (tlpTraceBramWrAddrFifo.notEmpty)
	 writeAddr <- toGet(tlpTraceBramWrAddrFifo).get();

      fromPcieTraceBram.portA.request.put(BRAMRequest{ write: True, responseOnWrite: False, address: writeAddr, datain: fromttd });

      TimestampedTlpData   tottd = toPcie ? TimestampedTlpData { timestamp: timestamp, source: 7'h08, tlp: toPcieTlp } : unpack(0);
      toPcieTraceBram.portA.request.put(BRAMRequest{ write: True, responseOnWrite: False, address: writeAddr, datain: tottd });

      tlpTraceBramWrAddrReg <= writeAddr + 1;
   endrule

   interface Server     bus;
      interface Get response;
           method ActionValue#(TLPData#(16)) get();
	      let tlp <- toGet(tlpBusResponseFifo).get();

	      if (tlpTracingReg) begin
		 if (tlp.sof && isRootBroadcastMessage.first) begin
 		    skippingIncomingTlps <= True;
		 end
		 else if (skippingIncomingTlps && !tlp.sof) begin
		    // do nothing
		 end
		 else begin
		    fromPcie.send();
		    fromPcieTlp <= tlp;
		    skippingIncomingTlps <= False;
		 end
	      end

	      isRootBroadcastMessage.deq();
	      return tlp;
	   endmethod
      endinterface

       interface Put request;
           method Action put(TLPData#(16) tlp);
           tlpToBusFifo.enq(tlp);
           if (tlpTracingReg) begin
	      toPcie.send();
	      toPcieTlp <= tlp;
           end
           endmethod
       endinterface
   endinterface

   interface Client    pci;
      interface request = toGet(tlpToBusFifo);
      interface response = toPut(tlpFromBusFifo);
   endinterface
   interface Put trace;
       method Action put(TimestampedTlpData ttd) if (!fromPcie && !toPcie);
	   if (tlpTracingReg) begin
	       ttd.timestamp = timestamp;
	       toPcieTraceBram.portA.request.put(BRAMRequest{ write: True, responseOnWrite: False, address: truncate(tlpTraceBramWrAddrReg), datain: ttd });
	       tlpTraceBramWrAddrReg <= tlpTraceBramWrAddrReg + 1;
	   end
       endmethod
   endinterface: trace
   interface TlpTraceServer traceServer;
      interface Reg tlpTracing    = tlpTracingReg;
      interface Reg tlpTraceLimit = tlpTraceLimitReg;
      interface Reg tlpTraceBramWrAddr;
	 method Bit#(TlpTraceAddrSize) _read(); return tlpTraceBramWrAddrReg; endmethod
	 method Action _write(Bit#(TlpTraceAddrSize) v); tlpTraceBramWrAddrFifo.enq(v); endmethod
      endinterface
      interface Server bramServer = bramMuxReg.bramServer;
`ifdef PCIE_BSCAN
      interface Server bscanBramServer = bscanBramMux.bramServer;
`endif
   endinterface
endmodule: mkPcieTracer

instance Connectable#(TlpTraceClient,TlpTraceServer);
   module mkConnection#(TlpTraceClient client, TlpTraceServer tracer)(Empty);
      mkConnection(client.bramClient, tracer.bramServer);
      Reg#(Bool)                           tlpTracingReg <- mkReg(False);
      Reg#(Bit#(TlpTraceAddrSize))      tlpTraceLimitReg <- mkReg(0);
      Reg#(Bit#(TlpTraceAddrSize)) tlpTraceBramWrAddrReg <- mkReg(0);
      rule tracingRule if (tlpTracingReg != client.tlpTracing);
	 tracer.tlpTracing <= client.tlpTracing;
	 tlpTracingReg <= client.tlpTracing;
      endrule
      rule traceLimitRule if (tlpTraceLimitReg != client.tlpTraceLimit);
	 tracer.tlpTraceLimit <= client.tlpTraceLimit;
	 tlpTraceLimitReg <= client.tlpTraceLimit;
      endrule
      // both client and server update the tlpBramWrAddr
      rule traceBramWrAddrRule if (tlpTraceBramWrAddrReg != client.tlpTraceBramWrAddr);
	 tracer.tlpTraceBramWrAddr <= client.tlpTraceBramWrAddr;
	 tlpTraceBramWrAddrReg <= client.tlpTraceBramWrAddr;
      endrule
   endmodule
endinstance


generalized mkMemServerRW to take nil for read and/or write clients

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

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

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

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

// BSV Libraries
import FIFO::*;
import Vector::*;
import List::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;
import StmtFSM::*;

// CONNECTAL Libraries
import HostInterface::*;
import MemTypes::*;
import ConnectalMemory::*;
import MMU::*;
import MemServerInternal::*;

function Put#(t) null_put();
   return (interface Put;
              method Action put(t x) if (False);
                 noAction;
              endmethod
           endinterface);
endfunction

function Get#(t) null_get();
   return (interface Get;
              method ActionValue#(t) get() if (False);
                 return ?;
              endmethod
           endinterface);
endfunction

function  PhysMemWriteClient#(addrWidth, busWidth) null_phys_mem_write_client();
   return (interface PhysMemWriteClient;
              interface Get writeReq = null_get;
              interface Get writeData = null_get;
              interface Put writeDone = null_put;
           endinterface);
endfunction

function  PhysMemReadClient#(addrWidth, busWidth) null_phys_mem_read_client();
   return (interface PhysMemReadClient;
              interface Get readReq = null_get;
              interface Put readData = null_put;
           endinterface);
endfunction

function  MemWriteClient#(busWidth) null_mem_write_client();
   return (interface MemWriteClient;
              interface Get writeReq = null_get;
              interface Get writeData = null_get;
              interface Put writeDone = null_put;
           endinterface);
endfunction

function  MemReadClient#(busWidth) null_mem_read_client();
   return (interface MemReadClient;
              interface Get readReq = null_get;
              interface Put readData = null_put;
           endinterface);
endfunction

interface MemServer#(numeric type addrWidth, numeric type dataWidth, numeric type nMasters);
   interface MemServerRequest request;
   interface Vector#(nMasters,PhysMemMaster#(addrWidth, dataWidth)) masters;
endinterface

typedef struct {
   DmaErrorType errorType;
   Bit#(32) pref;
   } DmaError deriving (Bits);

module mkMemServerRW#(MemServerIndication indication,
		      Vector#(numReadClients, MemReadClient#(dataWidth)) readClients,
		      Vector#(numWriteClients, MemWriteClient#(dataWidth)) writeClients,
		      Vector#(numMMUs,MMU#(PhysAddrWidth)) mmus)
   (MemServer#(PhysAddrWidth, dataWidth, nMasters))

   provisos (Max#(nMasters,numReadClients,numReadClientsActual),
	     Add#(numReadClients,numReadClientsNull,numReadClientsActual),
	     Mul#(a__, nMasters, numReadClientsActual),
	     Max#(nMasters,numWriteClients,numWriteClientsActual),
	     Add#(numWriteClients,numWriteClientsNull,numWriteClientsActual),
	     Mul#(b__, nMasters, numWriteClientsActual),
	     Add#(1,c__,dataWidth),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Add#(TLog#(TDiv#(dataWidth, 8)), d__, BurstLenSize),
	     Mul#(h__, nMasters, TMax#(nMasters, numReadClientsActual)),
	     Mul#(i__, nMasters, TMax#(nMasters, numWriteClientsActual)),
	     Add#(e__, TLog#(b__), 6),
	     Add#(f__, TLog#(a__), 6)
      );

   Vector#(numReadClientsNull, MemReadClient#(dataWidth)) nullReadClients = replicate(null_mem_read_client());
   Vector#(numWriteClientsNull, MemWriteClient#(dataWidth)) nullWriteClients = replicate(null_mem_write_client());

   MemServer#(PhysAddrWidth,dataWidth,nMasters) reader <- mkMemServerR(indication, append(readClients,nullReadClients), mmus);
   MemServer#(PhysAddrWidth,dataWidth,nMasters) writer <- mkMemServerW(indication, append(writeClients,nullWriteClients), mmus);

   function PhysMemMaster#(PhysAddrWidth,dataWidth) mkm(Integer i) = (interface PhysMemMaster#(PhysAddrWidth,dataWidth);
								 interface PhysMemReadClient read_client = reader.masters[i].read_client;
								 interface PhysMemWriteClient write_client = writer.masters[i].write_client;
							      endinterface);

   interface MemServerRequest request;
      method Action stateDbg(ChannelType rc);
	 if (rc == Read)
	    reader.request.stateDbg(rc);
	 else
	    writer.request.stateDbg(rc);
      endmethod
      method Action memoryTraffic(ChannelType rc);
	 if (rc == Read)
	    reader.request.memoryTraffic(rc);
	 else
	    writer.request.memoryTraffic(rc);
      endmethod
      method Action addrTrans(Bit#(32) pointer, Bit#(32) offset);
	 writer.request.addrTrans(pointer,offset);
      endmethod
   endinterface
   interface masters = map(mkm,genVector);
endmodule

module mkMemServerR#(MemServerIndication indication,
		       Vector#(numReadClients, MemReadClient#(dataWidth)) readClients,
		       Vector#(numMMUs,MMU#(addrWidth)) mmus)
   (MemServer#(addrWidth, dataWidth, nMasters))

   provisos (Add#(1,a__,dataWidth),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Mul#(nrc, nMasters, numReadClients),
	     Add#(b__, TLog#(nrc), 6),
	     Add#(TLog#(TDiv#(dataWidth, 8)), c__, BurstLenSize)
	     ,Add#(d__, addrWidth, 64)
	     ,Add#(e__, 12, addrWidth)
	     ,Add#(1, e__, f__)
	     );

   FIFO#(Bit#(32))   addrReqFifo <- mkFIFO;
   Reg#(Bit#(8)) dbgPtr <- mkReg(0);
   Reg#(Bit#(8)) trafficPtr <- mkReg(0);
   Reg#(Bit#(64)) trafficAccum <- mkReg(0);

   function a selectClient(Vector#(n, a) in, Integer r, Integer i, Integer j); return in[j * r + i]; endfunction
   function Vector#(nrc, a) selectClients(Vector#(numReadClients, a) vec, Integer m);
      return genWith(selectClient(vec, valueOf(nMasters), m));
   endfunction
   Vector#(nMasters,Vector#(nrc, MemReadClient#(dataWidth))) client_bins = genWith(selectClients(readClients));

   module foo#(Integer i) (MMUAddrServer#(addrWidth,nMasters));
      let rv <- mkMMUAddrServer(mmus[i].addr[0]);
      return rv;
   endmodule
   Vector#(numMMUs,MMUAddrServer#(addrWidth,nMasters)) mmu_servers <- mapM(foo,genVector);

   Vector#(nMasters,MemReadInternal#(addrWidth,dataWidth,MemServerTags)) readers;
   for(Integer i = 0; i < valueOf(nMasters); i = i+1) begin
      Vector#(numMMUs,Server#(ReqTup,Bit#(addrWidth))) ss;
      for(Integer j = 0; j < valueOf(numMMUs); j=j+1)
	 ss[j] = mmu_servers[j].servers[i];
      readers[i] <- mkMemReadInternal(client_bins[i],indication,ss);
   end

   rule mmuEntry;
      addrReqFifo.deq;
      let physAddr <- mmus[addrReqFifo.first[31:16]].addr[0].response.get;
      indication.addrResponse(zeroExtend(physAddr));
   endrule

   function PhysMemMaster#(addrWidth,dataWidth) mkm(Integer i) = (interface PhysMemMaster#(addrWidth,dataWidth);
								 interface PhysMemReadClient read_client = readers[i].read_client;
								 interface PhysMemWriteClient write_client = null_phys_mem_write_client;
							      endinterface);

   Stmt dbgStmt = seq
		     for(dbgPtr <= 0; dbgPtr < fromInteger(valueOf(nMasters)); dbgPtr <= dbgPtr+1)
			(action
			    let rv <- readers[dbgPtr].dbg.dbg;
			    indication.reportStateDbg(rv);
			 endaction);
		  endseq;
   FSM dbgFSM <- mkFSM(dbgStmt);

   Stmt trafficStmt = seq
			 trafficAccum <= 0;
			 for(trafficPtr <= 0; trafficPtr < fromInteger(valueOf(nMasters)); trafficPtr <= trafficPtr+1)
			    (action
				let rv <- readers[trafficPtr].dbg.getMemoryTraffic();
				trafficAccum <= trafficAccum + rv;
			     endaction);
			 indication.reportMemoryTraffic(trafficAccum);
		      endseq;
   FSM trafficFSM <- mkFSM(trafficStmt);

   interface MemServerRequest request;
      method Action stateDbg(ChannelType rc);
	 if (rc == Read)
	    dbgFSM.start;
      endmethod
      method Action memoryTraffic(ChannelType rc);
	 if (rc == Read)
	    trafficFSM.start;
      endmethod
      method Action addrTrans(Bit#(32) pointer, Bit#(32) offset);
	 addrReqFifo.enq(pointer);
	 mmus[pointer[31:16]].addr[0].request.put(ReqTup{id:truncate(pointer), off:extend(offset)});
      endmethod
   endinterface
   interface masters = map(mkm,genVector);
endmodule

module mkMemServerW#(MemServerIndication indication,
		     Vector#(numWriteClients, MemWriteClient#(dataWidth)) writeClients,
		     Vector#(numMMUs,MMU#(PhysAddrWidth)) mmus)
   (MemServer#(PhysAddrWidth, dataWidth, nMasters))

   provisos (Add#(1,a__,dataWidth),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Mul#(nwc, nMasters, numWriteClients),
	     Add#(b__, TLog#(nwc), 6)
	     );

   FIFO#(Bit#(32))   addrReqFifo <- mkFIFO;
   Reg#(Bit#(8)) dbgPtr <- mkReg(0);
   Reg#(Bit#(8)) trafficPtr <- mkReg(0);
   Reg#(Bit#(64)) trafficAccum <- mkReg(0);

   function a selectClient(Vector#(n, a) in, Integer r, Integer i, Integer j); return in[j * r + i]; endfunction
   function Vector#(nwc, a) selectClients(Vector#(numWriteClients, a) vec, Integer m);
      return genWith(selectClient(vec, valueOf(nMasters), m));
   endfunction
   Vector#(nMasters,Vector#(nwc, MemWriteClient#(dataWidth))) client_bins = genWith(selectClients(writeClients));

   module foo#(Integer i) (MMUAddrServer#(PhysAddrWidth,nMasters));
      let rv <- mkMMUAddrServer(mmus[i].addr[1]);
      return rv;
   endmodule
   Vector#(numMMUs,MMUAddrServer#(PhysAddrWidth,nMasters)) mmu_servers <- mapM(foo,genVector);

   Vector#(nMasters,MemWriteInternal#(PhysAddrWidth,dataWidth,MemServerTags)) writers;
   for(Integer i = 0; i < valueOf(nMasters); i = i+1) begin
      Vector#(numMMUs,Server#(ReqTup,Bit#(PhysAddrWidth))) ss;
      for(Integer j = 0; j < valueOf(numMMUs); j=j+1)
	 ss[j] = mmu_servers[j].servers[i];
      writers[i] <- mkMemWriteInternal(client_bins[i], indication, ss);
   end

   rule mmuEntry;
      addrReqFifo.deq;
      let physAddr <- mmus[addrReqFifo.first[31:16]].addr[1].response.get;
      indication.addrResponse(zeroExtend(physAddr));
   endrule

   function PhysMemMaster#(PhysAddrWidth,dataWidth) mkm(Integer i) = (interface PhysMemMaster#(PhysAddrWidth,dataWidth);
								 interface PhysMemReadClient read_client = null_phys_mem_read_client;
								 interface PhysMemWriteClient write_client = writers[i].write_client;
							      endinterface);

   Stmt dbgStmt = seq
		     for(dbgPtr <= 0; dbgPtr < fromInteger(valueOf(nMasters)); dbgPtr <= dbgPtr+1)
			(action
			    let rv <- writers[dbgPtr].dbg.dbg;
			    indication.reportStateDbg(rv);
			 endaction);
		  endseq;
   FSM dbgFSM <- mkFSM(dbgStmt);

   Stmt trafficStmt = seq
			 trafficAccum <= 0;
			 for(trafficPtr <= 0; trafficPtr < fromInteger(valueOf(nMasters)); trafficPtr <= trafficPtr+1)
			    (action
				let rv <- writers[trafficPtr].dbg.getMemoryTraffic();
				trafficAccum <= trafficAccum + rv;
			     endaction);
			 indication.reportMemoryTraffic(trafficAccum);
		      endseq;
   FSM trafficFSM <- mkFSM(trafficStmt);

   interface MemServerRequest request;
      method Action stateDbg(ChannelType rc);
	 if (rc == Write)
	    dbgFSM.start;
      endmethod
      method Action memoryTraffic(ChannelType rc);
	 if (rc == Write)
	    trafficFSM.start;
      endmethod
      method Action addrTrans(Bit#(32) pointer, Bit#(32) offset);
	 addrReqFifo.enq(pointer);
	 mmus[pointer[31:16]].addr[1].request.put(ReqTup{id:truncate(pointer), off:extend(offset)});
      endmethod
   endinterface
   interface masters = map(mkm,genVector);
endmodule

interface SimpleMemServer#(numeric type addrWidth, numeric type dataWidth, numeric type nMasters);
   interface MemServerRequest memServerRequest;
   interface MMURequest mmuRequest;
   interface Vector#(nMasters,PhysMemMaster#(addrWidth, dataWidth)) masters;
   interface Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addr;
endinterface

module mkSimpleMemServer#(Vector#(numReadClients, MemReadClient#(dataWidth)) readClients,
			  Vector#(numWriteClients, MemWriteClient#(dataWidth)) writeClients,
			  MemServerIndication indication,
			  MMUIndication mmuIndication)(SimpleMemServer#(PhysAddrWidth, dataWidth,nMasters))
   provisos (Mul#(a__, nMasters, TMax#(nMasters, numReadClients)),
	     Mul#(b__, nMasters, TMax#(nMasters, numWriteClients)),
	     Add#(1, c__, dataWidth),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Add#(TLog#(TDiv#(dataWidth, 8)), d__, BurstLenSize),
	     Mul#(e__, nMasters, TMax#(nMasters, TMax#(nMasters, numReadClients))),
	     Mul#(f__, nMasters, TMax#(nMasters, TMax#(nMasters, numWriteClients))),
	     Add#(g__, TLog#(b__), 6),
	     Add#(h__, TLog#(a__), 6)
	     );

   MMU#(PhysAddrWidth) hostMMU <- mkMMU(0, True, mmuIndication);

   MemServer#(PhysAddrWidth,dataWidth,nMasters) dma <- mkMemServerRW(indication, readClients, writeClients, cons(hostMMU,nil));

   interface MemServerRequest memServerRequest = dma.request;
   interface MMURequest mmuRequest = hostMMU.request;
   interface Vector masters = dma.masters;
   interface Vector addr = hostMMU.addr;
endmodule

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

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

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

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

// BSV Libraries
import FIFO::*;
import Vector::*;
import List::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;
import StmtFSM::*;

// CONNECTAL Libraries
import HostInterface::*;
import MemTypes::*;
import ConnectalMemory::*;
import MMU::*;
import MemServerInternal::*;

function Put#(t) null_put();
   return (interface Put;
              method Action put(t x) if (False);
                 noAction;
              endmethod
           endinterface);
endfunction

function Get#(t) null_get();
   return (interface Get;
              method ActionValue#(t) get() if (False);
                 return ?;
              endmethod
           endinterface);
endfunction

function  PhysMemWriteClient#(addrWidth, busWidth) null_phys_mem_write_client();
   return (interface PhysMemWriteClient;
              interface Get writeReq = null_get;
              interface Get writeData = null_get;
              interface Put writeDone = null_put;
           endinterface);
endfunction

function  PhysMemReadClient#(addrWidth, busWidth) null_phys_mem_read_client();
   return (interface PhysMemReadClient;
              interface Get readReq = null_get;
              interface Put readData = null_put;
           endinterface);
endfunction

function  MemWriteClient#(busWidth) null_mem_write_client();
   return (interface MemWriteClient;
              interface Get writeReq = null_get;
              interface Get writeData = null_get;
              interface Put writeDone = null_put;
           endinterface);
endfunction

function  MemReadClient#(busWidth) null_mem_read_client();
   return (interface MemReadClient;
              interface Get readReq = null_get;
              interface Put readData = null_put;
           endinterface);
endfunction

interface MemServer#(numeric type addrWidth, numeric type dataWidth, numeric type nMasters);
   interface MemServerRequest request;
   interface Vector#(nMasters,PhysMemMaster#(addrWidth, dataWidth)) masters;
endinterface

typedef struct {
   DmaErrorType errorType;
   Bit#(32) pref;
   } DmaError deriving (Bits);

module mkMemServerRW#(MemServerIndication indication,
		      Vector#(numReadClients, MemReadClient#(dataWidth)) readClients,
		      Vector#(numWriteClients, MemWriteClient#(dataWidth)) writeClients,
		      Vector#(numMMUs,MMU#(PhysAddrWidth)) mmus)
   (MemServer#(PhysAddrWidth, dataWidth, nMasters))

   provisos (Add#(1,a__,dataWidth),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Mul#(nwc, nMasters, numWriteClients),
	     Mul#(nrc, nMasters, numReadClients),
	     Add#(b__, TLog#(nrc), 6),
	     Add#(c__, TLog#(nwc), 6),
	     Add#(TLog#(TDiv#(dataWidth, 8)), d__, BurstLenSize)
	     );

   MemServer#(PhysAddrWidth,dataWidth,nMasters) reader <- mkMemServerR(indication,  readClients, mmus);
   MemServer#(PhysAddrWidth,dataWidth,nMasters) writer <- mkMemServerW(indication, writeClients, mmus);

   function PhysMemMaster#(PhysAddrWidth,dataWidth) mkm(Integer i) = (interface PhysMemMaster#(PhysAddrWidth,dataWidth);
								 interface PhysMemReadClient read_client = reader.masters[i].read_client;
								 interface PhysMemWriteClient write_client = writer.masters[i].write_client;
							      endinterface);

   interface MemServerRequest request;
      method Action stateDbg(ChannelType rc);
	 if (rc == Read)
	    reader.request.stateDbg(rc);
	 else
	    writer.request.stateDbg(rc);
      endmethod
      method Action memoryTraffic(ChannelType rc);
	 if (rc == Read)
	    reader.request.memoryTraffic(rc);
	 else
	    writer.request.memoryTraffic(rc);
      endmethod
      method Action addrTrans(Bit#(32) pointer, Bit#(32) offset);
	 writer.request.addrTrans(pointer,offset);
      endmethod
   endinterface
   interface masters = map(mkm,genVector);
endmodule

module mkMemServerR#(MemServerIndication indication,
		       Vector#(numReadClients, MemReadClient#(dataWidth)) readClients,
		       Vector#(numMMUs,MMU#(addrWidth)) mmus)
   (MemServer#(addrWidth, dataWidth, nMasters))

   provisos (Add#(1,a__,dataWidth),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Mul#(nrc, nMasters, numReadClients),
	     Add#(b__, TLog#(nrc), 6),
	     Add#(TLog#(TDiv#(dataWidth, 8)), c__, BurstLenSize)
	     ,Add#(d__, addrWidth, 64)
	     ,Add#(e__, 12, addrWidth)
	     ,Add#(1, e__, f__)
	     );

   FIFO#(Bit#(32))   addrReqFifo <- mkFIFO;
   Reg#(Bit#(8)) dbgPtr <- mkReg(0);
   Reg#(Bit#(8)) trafficPtr <- mkReg(0);
   Reg#(Bit#(64)) trafficAccum <- mkReg(0);

   function a selectClient(Vector#(n, a) in, Integer r, Integer i, Integer j); return in[j * r + i]; endfunction
   function Vector#(nrc, a) selectClients(Vector#(numReadClients, a) vec, Integer m);
      return genWith(selectClient(vec, valueOf(nMasters), m));
   endfunction
   Vector#(nMasters,Vector#(nrc, MemReadClient#(dataWidth))) client_bins = genWith(selectClients(readClients));

   module foo#(Integer i) (MMUAddrServer#(addrWidth,nMasters));
      let rv <- mkMMUAddrServer(mmus[i].addr[0]);
      return rv;
   endmodule
   Vector#(numMMUs,MMUAddrServer#(addrWidth,nMasters)) mmu_servers <- mapM(foo,genVector);

   Vector#(nMasters,MemReadInternal#(addrWidth,dataWidth,MemServerTags)) readers;
   for(Integer i = 0; i < valueOf(nMasters); i = i+1) begin
      Vector#(numMMUs,Server#(ReqTup,Bit#(addrWidth))) ss;
      for(Integer j = 0; j < valueOf(numMMUs); j=j+1)
	 ss[j] = mmu_servers[j].servers[i];
      readers[i] <- mkMemReadInternal(client_bins[i],indication,ss);
   end

   rule mmuEntry;
      addrReqFifo.deq;
      let physAddr <- mmus[addrReqFifo.first[31:16]].addr[0].response.get;
      indication.addrResponse(zeroExtend(physAddr));
   endrule

   function PhysMemMaster#(addrWidth,dataWidth) mkm(Integer i) = (interface PhysMemMaster#(addrWidth,dataWidth);
								 interface PhysMemReadClient read_client = readers[i].read_client;
								 interface PhysMemWriteClient write_client = null_phys_mem_write_client;
							      endinterface);

   Stmt dbgStmt = seq
		     for(dbgPtr <= 0; dbgPtr < fromInteger(valueOf(nMasters)); dbgPtr <= dbgPtr+1)
			(action
			    let rv <- readers[dbgPtr].dbg.dbg;
			    indication.reportStateDbg(rv);
			 endaction);
		  endseq;
   FSM dbgFSM <- mkFSM(dbgStmt);

   Stmt trafficStmt = seq
			 trafficAccum <= 0;
			 for(trafficPtr <= 0; trafficPtr < fromInteger(valueOf(nMasters)); trafficPtr <= trafficPtr+1)
			    (action
				let rv <- readers[trafficPtr].dbg.getMemoryTraffic();
				trafficAccum <= trafficAccum + rv;
			     endaction);
			 indication.reportMemoryTraffic(trafficAccum);
		      endseq;
   FSM trafficFSM <- mkFSM(trafficStmt);

   interface MemServerRequest request;
      method Action stateDbg(ChannelType rc);
	 if (rc == Read)
	    dbgFSM.start;
      endmethod
      method Action memoryTraffic(ChannelType rc);
	 if (rc == Read)
	    trafficFSM.start;
      endmethod
      method Action addrTrans(Bit#(32) pointer, Bit#(32) offset);
	 addrReqFifo.enq(pointer);
	 mmus[pointer[31:16]].addr[0].request.put(ReqTup{id:truncate(pointer), off:extend(offset)});
      endmethod
   endinterface
   interface masters = map(mkm,genVector);
endmodule

module mkMemServerW#(MemServerIndication indication,
		     Vector#(numWriteClients, MemWriteClient#(dataWidth)) writeClients,
		     Vector#(numMMUs,MMU#(PhysAddrWidth)) mmus)
   (MemServer#(PhysAddrWidth, dataWidth, nMasters))

   provisos (Add#(1,a__,dataWidth),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Mul#(nwc, nMasters, numWriteClients),
	     Add#(b__, TLog#(nwc), 6)
	     );

   FIFO#(Bit#(32))   addrReqFifo <- mkFIFO;
   Reg#(Bit#(8)) dbgPtr <- mkReg(0);
   Reg#(Bit#(8)) trafficPtr <- mkReg(0);
   Reg#(Bit#(64)) trafficAccum <- mkReg(0);

   function a selectClient(Vector#(n, a) in, Integer r, Integer i, Integer j); return in[j * r + i]; endfunction
   function Vector#(nwc, a) selectClients(Vector#(numWriteClients, a) vec, Integer m);
      return genWith(selectClient(vec, valueOf(nMasters), m));
   endfunction
   Vector#(nMasters,Vector#(nwc, MemWriteClient#(dataWidth))) client_bins = genWith(selectClients(writeClients));

   module foo#(Integer i) (MMUAddrServer#(PhysAddrWidth,nMasters));
      let rv <- mkMMUAddrServer(mmus[i].addr[1]);
      return rv;
   endmodule
   Vector#(numMMUs,MMUAddrServer#(PhysAddrWidth,nMasters)) mmu_servers <- mapM(foo,genVector);

   Vector#(nMasters,MemWriteInternal#(PhysAddrWidth,dataWidth,MemServerTags)) writers;
   for(Integer i = 0; i < valueOf(nMasters); i = i+1) begin
      Vector#(numMMUs,Server#(ReqTup,Bit#(PhysAddrWidth))) ss;
      for(Integer j = 0; j < valueOf(numMMUs); j=j+1)
	 ss[j] = mmu_servers[j].servers[i];
      writers[i] <- mkMemWriteInternal(client_bins[i], indication, ss);
   end

   rule mmuEntry;
      addrReqFifo.deq;
      let physAddr <- mmus[addrReqFifo.first[31:16]].addr[1].response.get;
      indication.addrResponse(zeroExtend(physAddr));
   endrule

   function PhysMemMaster#(PhysAddrWidth,dataWidth) mkm(Integer i) = (interface PhysMemMaster#(PhysAddrWidth,dataWidth);
								 interface PhysMemReadClient read_client = null_phys_mem_read_client;
								 interface PhysMemWriteClient write_client = writers[i].write_client;
							      endinterface);

   Stmt dbgStmt = seq
		     for(dbgPtr <= 0; dbgPtr < fromInteger(valueOf(nMasters)); dbgPtr <= dbgPtr+1)
			(action
			    let rv <- writers[dbgPtr].dbg.dbg;
			    indication.reportStateDbg(rv);
			 endaction);
		  endseq;
   FSM dbgFSM <- mkFSM(dbgStmt);

   Stmt trafficStmt = seq
			 trafficAccum <= 0;
			 for(trafficPtr <= 0; trafficPtr < fromInteger(valueOf(nMasters)); trafficPtr <= trafficPtr+1)
			    (action
				let rv <- writers[trafficPtr].dbg.getMemoryTraffic();
				trafficAccum <= trafficAccum + rv;
			     endaction);
			 indication.reportMemoryTraffic(trafficAccum);
		      endseq;
   FSM trafficFSM <- mkFSM(trafficStmt);

   interface MemServerRequest request;
      method Action stateDbg(ChannelType rc);
	 if (rc == Write)
	    dbgFSM.start;
      endmethod
      method Action memoryTraffic(ChannelType rc);
	 if (rc == Write)
	    trafficFSM.start;
      endmethod
      method Action addrTrans(Bit#(32) pointer, Bit#(32) offset);
	 addrReqFifo.enq(pointer);
	 mmus[pointer[31:16]].addr[1].request.put(ReqTup{id:truncate(pointer), off:extend(offset)});
      endmethod
   endinterface
   interface masters = map(mkm,genVector);
endmodule

interface SimpleMemServer#(numeric type addrWidth, numeric type dataWidth, numeric type nMasters);
   interface MemServerRequest memServerRequest;
   interface MMURequest mmuRequest;
   interface Vector#(nMasters,PhysMemMaster#(addrWidth, dataWidth)) masters;
   interface Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addr;
endinterface

module mkSimpleMemServer#(Vector#(numReadClients, MemReadClient#(dataWidth)) readClients,
			  Vector#(numWriteClients, MemWriteClient#(dataWidth)) writeClients,
			  MemServerIndication indication,
			  MMUIndication mmuIndication)(SimpleMemServer#(PhysAddrWidth, dataWidth,nMasters))
   provisos (Max#(nMasters,numReadClients,numReadClientsActual),
	     Add#(numReadClients,numReadClientsNull,numReadClientsActual),
	     Mul#(a__, nMasters, numReadClientsActual),
	     Max#(nMasters,numWriteClients,numWriteClientsActual),
	     Add#(numWriteClients,numWriteClientsNull,numWriteClientsActual),
	     Mul#(b__, nMasters, numWriteClientsActual),
	     Add#(TLog#(TDiv#(dataWidth, 8)), c__, 10),
	     Add#(d__, TLog#(b__), 6),
	     Add#(e__, TLog#(a__), 6),
	     Mul#(TDiv#(dataWidth, 8), 8, dataWidth),
	     Add#(1, f__, dataWidth),
	     Add#(TLog#(TDiv#(dataWidth, 8)), g__, 8)
	     );

   MMU#(PhysAddrWidth) hostMMU <- mkMMU(0, True, mmuIndication);

   Vector#(numReadClientsNull, MemReadClient#(dataWidth)) nullReadClients = replicate(null_mem_read_client());
   Vector#(numWriteClientsNull, MemWriteClient#(dataWidth)) nullWriteClients = replicate(null_mem_write_client());

   MemServer#(PhysAddrWidth,dataWidth,nMasters) dma <- mkMemServerRW(indication, append(readClients,nullReadClients),
								     append(writeClients,nullWriteClients),
								     cons(hostMMU,nil));

   interface MemServerRequest memServerRequest = dma.request;
   interface MMURequest mmuRequest = hostMMU.request;
   interface Vector masters = dma.masters;
   interface Vector addr = hostMMU.addr;
endmodule


output of SharedMemoryPipeOut is a vector of PipeOuts, plus renaming head/tail

// 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 Vector::*;
import GetPut::*;
import ClientServer::*;
import Gearbox::*;
import DefaultValue::*;
import FIFO::*;
import FIFOF::*;
import MIMO::*;
import Pipe::*;
import Portal::*;
import MemTypes::*;
import ConnectalMemory::*;

interface SharedMemoryPipeOut#(numeric type dataBusWidth, numeric type pipeCount);
   interface SharedMemoryPortalConfig cfg;
   interface Vector#(pipeCount, PipeOut#(Bit#(32))) data;
endinterface
interface SharedMemoryPipeIn#(numeric type dataBusWidth);
   interface SharedMemoryPortalConfig cfg;
   interface PipeIn#(Bit#(32)) data;
endinterface

typedef enum {
   Idle,
   WrPtrRequested, // 1
   RdPtrRequested, // 2
   RequestMessage, // 3
   MessageHeaderRequested, // 4
   MessageRequested, // 5
   Drain,
   UpdateRdPtr,
   UpdateWrPtr,
   UpdateWrPtr2,
   Waiting,
   SendHeader,
   SendMessage,
   SendPadding,
   Stop
   } SharedMemoryPortalState deriving (Bits,Eq);

module mkSharedMemoryRequestPipeOut#(Vector#(2, MemreadServer#(64)) readEngine, Vector#(2, MemwriteServer#(64)) writeEngine)(SharedMemoryPipeOut#(64,pipeCount));
   // read the wrPtr and rdPtr pointers, if they are different, then read a request
   Reg#(Bit#(32)) limitReg <- mkReg(0);
   Reg#(Bit#(32)) wrPtrReg <- mkReg(0);
   Reg#(Bit#(32)) rdPtrReg <- mkReg(0);
   Reg#(Bit#(16)) countReg <- mkReg(0);
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(16)) methodIdReg <- mkReg(0);
   Reg#(SharedMemoryPortalState) state <- mkReg(Idle);
   Reg#(Bit#(32)) sglIdReg <- mkReg(0);
   Reg#(Bool)     readyReg   <- mkReg(False);
   MIMOConfiguration mimoConfig = defaultValue;
   MIMO#(2,1,2,Bit#(32)) readMimo <- mkMIMO(mimoConfig);
   Vector#(pipeCount, FIFOF#(Bit#(32))) dataFifo <- replicateM(mkFIFOF);

   let verbose = True;

   rule updateReqWrPtrRdPtr if (state == Idle && readyReg);
      if (verbose) $display("updateReqWrPtrRdPtr");
      readEngine[0].request.put(
          MemengineCmd {sglId: sglIdReg, base: 0, burstLen: 16, len: 16, tag: 0});
      state <= WrPtrRequested;
   endrule

   rule receiveReqWrPtrRdPtr if (state == WrPtrRequested || state == RdPtrRequested);
      let v <- toGet(readEngine[0].data).get();
      let w0 = v.data[31:0];
      let w1 = v.data[63:32];
      let wrPtr = wrPtrReg;
      let rdPtr = rdPtrReg;
      let limit = limitReg;
      if (state == WrPtrRequested) begin
	 limit = w0;
         limitReg <= w0;
         wrPtrReg <= w1;
         wrPtr = w1;
         state <= RdPtrRequested;
      end
      else begin
         if (rdPtrReg == 0) begin
            rdPtr = w0;
            rdPtrReg <= rdPtr;
         end
         if (rdPtr != wrPtrReg)
            state <= RequestMessage;
         else
            state <= Idle;
      end
      if (verbose)
         $display("receiveReqWrPtrRdPtr state=%d w0=%x w1=%x wrPtr=%d rdPtr=%d limit=%d", state, w0, w1, wrPtr, rdPtr, limit);
   endrule

   rule requestMessage if (state == RequestMessage);
      Bit#(32) wordCount = wrPtrReg - rdPtrReg;
      if ((rdPtrReg & 1) == 1) begin
         $display("WARNING requestMessage: reqRdPtr=%d is odd.", rdPtrReg);
      end
      let rdPtr = rdPtrReg + wordCount;
      if (wrPtrReg < rdPtrReg) begin
         $display("requestMessage wrapped: wrPtr=%d rdPtr=%d", wrPtrReg, rdPtrReg);
         wordCount = limitReg - rdPtrReg;
         rdPtr = 4;
      end
      if (verbose) $display("requestMessage id=%d rdPtr=%h wrPtr=%h wordCount=%d", sglIdReg, rdPtrReg, wrPtrReg, wordCount);
      rdPtrReg <= rdPtr;
      countReg <= truncate(wordCount);
      readEngine[1].request.put( MemengineCmd
          {sglId: sglIdReg, base: extend(rdPtrReg << 2), burstLen: 16, len: wordCount << 2, tag: 0});
      state <= MessageHeaderRequested;
   endrule

   let enqCount = 2;
   rule demuxwords if (readMimo.enqReadyN(enqCount));
      let v <- toGet(readEngine[1].data).get();
      Vector#(2,Bit#(32)) dvec = unpack(v.data);
      readMimo.enq(enqCount, dvec);
   endrule

   rule receiveMessageHeader if (state == MessageHeaderRequested);
      let vec <- toGet(toPipeOut(readMimo)).get();
      let hdr = vec[0];
      let methodId = hdr[31:16];
      let messageWords = hdr[15:0];
      methodIdReg <= methodId;
      if (verbose)
         $display("receiveMessageHeader hdr=%x methodId=%x messageWords=%d wordCount=%d", hdr, methodId, messageWords, countReg);
      countReg <= countReg - 1;
      messageWordsReg <= messageWords - 1;
      if (hdr == 0) begin
         if (countReg == 1)
            state <= UpdateRdPtr;
         else
            state <= Drain;
      end
      else if (countReg == 1)
         state <= UpdateRdPtr;
      else if (messageWords == 1)
         state <= MessageHeaderRequested;
      else
         state <= MessageRequested;
   endrule

   rule drain if (state == Drain);
      let vec <- toGet(readMimo).get();
      if (countReg == 1)
         state <= UpdateRdPtr;
      countReg <= countReg - 1;
   endrule

   rule receiveMessage if (state == MessageRequested);
      let vec <- toGet(toPipeOut(readMimo)).get();
      let data = vec[0];
      if (verbose)
         $display("receiveMessage data=%x messageWords=%d wordCount=%d", data, messageWordsReg, countReg);
      if (methodIdReg != 16'hFFFF)
         dataFifo[methodIdReg].enq(data);
      messageWordsReg <= messageWordsReg - 1;
      countReg <= countReg - 1;
      if (countReg <= 1)
         state <= UpdateRdPtr;
      else if (messageWordsReg == 1)
         state <= MessageHeaderRequested;
   endrule

   rule updateRdPtr if (state == UpdateRdPtr);
      if (verbose)
         $display("updateRdPtr: rdPtr=%d", rdPtrReg);
      // update the rdPtr pointer
      writeEngine[0].request.put(
          MemengineCmd {sglId: sglIdReg, base: 8, len: 8, burstLen: 8, tag: 0});
      writeEngine[0].data.enq(extend(rdPtrReg));
      state <= Waiting;
   endrule

   rule waiting if (state == Waiting);
      let done <- writeEngine[0].done.get();
      state <= Idle;
   endrule

   interface SharedMemoryPortalConfig cfg;
      method Action setSglId(Bit#(32) id);
	 if (verbose) $display("setSglId id=%d", id);
         sglIdReg <= id;
         readyReg <= True;
      endmethod
   endinterface
   interface data = map(toPipeOut, dataFifo);
endmodule

module mkSharedMemoryIndicationPortal#(PipePortal#(numRequests, numIndications, 32) portal,
    Vector#(2,MemReadServer#(64)) readEngine, Vector#(2, MemWriteServer#(64)) writeEngine)(SharedMemoryPortal#(64));
   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;
   // read the wrPtr and rdPtr pointers, if they are different, then read a request
   Reg#(Bit#(16)) limitReg <- mkReg(0);
   Reg#(Bit#(16)) wrPtrReg <- mkReg(0);
   Reg#(Bit#(16)) rdPtrReg <- mkReg(0);
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(16)) methodIdReg <- mkReg(0);
   Reg#(Bool) paddingReg <- mkReg(False);
   Reg#(SharedMemoryPortalState) state <- mkReg(Idle);
   Reg#(Bit#(32)) sglIdReg <- mkReg(0);
   Reg#(Bool)     readyReg   <- mkReg(False);
   Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, portal.indications);
   Bool      interruptStatus = False;
   Bit#(16)  readyChannel = -1;
   function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
   Gearbox#(1,2,Bit#(32)) gb <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   let verbose = True;

   for (Integer i = valueOf(numIndications) - 1; i >= 0; i = i - 1) begin
      if (readyBits[i]) begin
         interruptStatus = True;
         readyChannel = fromInteger(i);
      end
   end

   rule updateIndWrPtrRdPtr if (state == Idle && readyReg);
      readEngine[0].request.put(
          MemengineCmd {sglId: sglIdReg, base: 0, burstLen: 16, len: 16, tag: 0});
      state <= WrPtrRequested;
   endrule

   rule receiveIndWrPtrRdPtr if (state == WrPtrRequested || state == RdPtrRequested);
      let md <- toGet(readEngine[0].data).get();
      let data = md.data;
      let w0 = data[31:0];
      let w1 = data[63:32];
      let wrPtr = wrPtrReg;
      let rdPtr = rdPtrReg;
      if (state == WrPtrRequested) begin
         limitReg <= truncate(w0);
         wrPtrReg <= truncate(w1);
         wrPtr = truncate(w1);
         state <= RdPtrRequested;
      end
      else begin
         if (rdPtrReg == 0) begin
            rdPtr = truncate(w0);
            rdPtrReg <= rdPtr;
         end
         //if (rdPtr != wrPtrReg)
            state <= SendHeader;
         //else
            //state <= Idle;
      end
      if (verbose)
         $display("receiveIndWrPtrRdPtr state=%d w0=%x w1=%x wrPtr=%d rdPtr=%d limit=%d", state, w0, w1, wrPtr, rdPtr, limitReg);
   endrule

   rule send64bits;
      let v = gb.first;
      gb.deq();
      writeEngine[0].data.enq(pack(v));
   endrule

   rule sendHeader if (state == SendHeader && interruptStatus);
      Bit#(16) messageBits = portal.messageSize.size(readyChannel);
      Bit#(16) roundup = messageBits[4:0] == 0 ? 0 : 1;
      Bit#(16) numWords = (messageBits >> 5) + roundup;
      Bit#(16) totalWords = numWords + 1;
      Bit#(32) hdr = extend(readyChannel) << 16 | extend(numWords + 1);
      if (numWords[0] == 0)
         totalWords = numWords + 2;
      paddingReg <= numWords[0] == 0;
      $display("sendHeader hdr=%h messageBits=%d numWords=%d totalWords=%d paddingReg=%d wrPtrReg=%h", hdr, messageBits, numWords, totalWords, paddingReg, wrPtrReg);
      wrPtrReg <= wrPtrReg + totalWords;
      messageWordsReg <= numWords;
      methodIdReg <= readyChannel;
      gb.enq(replicate(hdr));
      writeEngine[0].request.put( MemengineCmd
          {sglId: sglIdReg, base: extend(wrPtrReg) << 2, burstLen: 8, len: extend(totalWords) << 2, tag: 0});
      state <= SendMessage;
   endrule

   rule sendMessage if (state == SendMessage);
      messageWordsReg <= messageWordsReg - 1;
      let v = portal.indications[methodIdReg].first;
      portal.indications[methodIdReg].deq();
      gb.enq(replicate(v));
      $display("sendMessage v=%h messageWords=%d", v, messageWordsReg);
      if (messageWordsReg == 1) begin
         if (paddingReg)
            state <= SendPadding;
         else
            state <= UpdateWrPtr;
      end
   endrule

   rule sendPadding if (state == SendPadding);
      $display("sendPadding");
      gb.enq(replicate(32'hffff0001));
      state <= UpdateWrPtr;
   endrule

   rule updateWrPtr if (state == UpdateWrPtr);
      $display("updateIndWrPtr limit=%d wrPtr=%d", limitReg, wrPtrReg);
      gb.enq(replicate(extend(limitReg)));
      writeEngine[0].request.put(
             MemengineCmd {sglId: sglIdReg, base: 0 << 2, burstLen: 8, len: 2 << 2, tag: 0});
      state <= UpdateWrPtr2;
   endrule

   rule updateWrPtr2 if (state == UpdateWrPtr2);
      $display("updateIndWrPtr2");
      gb.enq(replicate(extend(wrPtrReg)));
      state <= SendHeader;
   endrule

   rule done;
      let done <- writeEngine[0].done.get();
   endrule
   rule done2;
      let done <- writeEngine[1].done.get();
   endrule

   interface SharedMemoryPortalConfig cfg;
      method Action setSglId(Bit#(32) id);
         sglIdReg <= id;
         readyReg <= True;
      endmethod
   endinterface
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 Vector::*;
import GetPut::*;
import ClientServer::*;
import Gearbox::*;
import DefaultValue::*;
import FIFO::*;
import FIFOF::*;
import MIMO::*;
import Pipe::*;
import Portal::*;
import MemTypes::*;
import ConnectalMemory::*;

interface SharedMemoryPipeOut#(numeric type dataBusWidth);
   interface SharedMemoryPortalConfig cfg;
   interface PipeOut#(Bit#(32)) data;
endinterface
interface SharedMemoryPipeIn#(numeric type dataBusWidth);
   interface SharedMemoryPortalConfig cfg;
   interface PipeIn#(Bit#(32)) data;
endinterface

typedef enum {
   Idle,
   HeadRequested, // 1
   TailRequested, // 2
   RequestMessage, // 3
   MessageHeaderRequested, // 4
   MessageRequested, // 5
   Drain,
   UpdateTail,
   UpdateHead,
   UpdateHead2,
   Waiting,
   SendHeader,
   SendMessage,
   SendPadding,
   Stop
   } SharedMemoryPortalState deriving (Bits,Eq);

module mkSharedMemoryRequestPipeOut#(Vector#(2, MemReadServer#(64)) readEngine, Vector#(2, MemWriteServer#(64)) writeEngine)(SharedMemoryPipeOut#(64));
   // read the head and tail pointers, if they are different, then read a request
   Reg#(Bit#(32)) limitReg <- mkReg(0);
   Reg#(Bit#(32)) headReg <- mkReg(0);
   Reg#(Bit#(32)) tailReg <- mkReg(0);
   Reg#(Bit#(16)) countReg <- mkReg(0);
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(16)) methodIdReg <- mkReg(0);
   Reg#(SharedMemoryPortalState) state <- mkReg(Idle);
   Reg#(Bit#(32)) sglIdReg <- mkReg(0);
   Reg#(Bool)     readyReg   <- mkReg(False);
   MIMOConfiguration mimoConfig = defaultValue;
   MIMO#(2,1,2,Bit#(32)) readMimo <- mkMIMO(mimoConfig);
   FIFOF#(Bit#(32)) dataFifo <- mkSizedFIFOF(32);

   let verbose = True;

   rule updateReqHeadTail if (state == Idle && readyReg);
      if (verbose) $display("updateReqHeadTail");
      readEngine[0].request.put(
          MemengineCmd {sglId: sglIdReg, base: 0, burstLen: 16, len: 16, tag: 0});
      state <= HeadRequested;
   endrule

   rule receiveReqHeadTail if (state == HeadRequested || state == TailRequested);
      let v <- toGet(readEngine[0].data).get();
      let w0 = v.data[31:0];
      let w1 = v.data[63:32];
      let head = headReg;
      let tail = tailReg;
      let limit = limitReg;
      if (state == HeadRequested) begin
	 limit = w0;
         limitReg <= w0;
         headReg <= w1;
         head = w1;
         state <= TailRequested;
      end
      else begin
         if (tailReg == 0) begin
            tail = w0;
            tailReg <= tail;
         end
         if (tail != headReg)
            state <= RequestMessage;
         else
            state <= Idle;
      end
      if (verbose)
         $display("receiveReqHeadTail state=%d w0=%x w1=%x head=%d tail=%d limit=%d", state, w0, w1, head, tail, limit);
   endrule

   rule requestMessage if (state == RequestMessage);
      Bit#(32) wordCount = headReg - tailReg;
      if ((tailReg & 1) == 1) begin
         $display("WARNING requestMessage: reqTail=%d is odd.", tailReg);
      end
      let tail = tailReg + wordCount;
      if (headReg < tailReg) begin
         $display("requestMessage wrapped: head=%d tail=%d", headReg, tailReg);
         wordCount = limitReg - tailReg;
         tail = 4;
      end
      if (verbose) $display("requestMessage id=%d tail=%h head=%h wordCount=%d", sglIdReg, tailReg, headReg, wordCount);
      tailReg <= tail;
      countReg <= truncate(wordCount);
      readEngine[1].request.put( MemengineCmd
          {sglId: sglIdReg, base: extend(tailReg << 2), burstLen: 16, len: wordCount << 2, tag: 0});
      state <= MessageHeaderRequested;
   endrule

   let enqCount = 2;
   rule demuxwords if (readMimo.enqReadyN(enqCount));
      let v <- toGet(readEngine[1].data).get();
      Vector#(2,Bit#(32)) dvec = unpack(v.data);
      readMimo.enq(enqCount, dvec);
   endrule

   rule receiveMessageHeader if (state == MessageHeaderRequested);
      let vec <- toGet(toPipeOut(readMimo)).get();
      let hdr = vec[0];
      let methodId = hdr[31:16];
      let messageWords = hdr[15:0];
      methodIdReg <= methodId;
      if (verbose)
         $display("receiveMessageHeader hdr=%x methodId=%x messageWords=%d wordCount=%d", hdr, methodId, messageWords, countReg);
      countReg <= countReg - 1;
      messageWordsReg <= messageWords - 1;
      if (hdr == 0) begin
         if (countReg == 1)
            state <= UpdateTail;
         else
            state <= Drain;
      end
      else if (countReg == 1)
         state <= UpdateTail;
      else if (messageWords == 1)
         state <= MessageHeaderRequested;
      else
         state <= MessageRequested;
   endrule

   rule drain if (state == Drain);
      let vec <- toGet(readMimo).get();
      if (countReg == 1)
         state <= UpdateTail;
      countReg <= countReg - 1;
   endrule

   rule receiveMessage if (state == MessageRequested);
      let vec <- toGet(toPipeOut(readMimo)).get();
      let data = vec[0];
      if (verbose)
         $display("receiveMessage data=%x messageWords=%d wordCount=%d", data, messageWordsReg, countReg);
      if (methodIdReg != 16'hFFFF)
         dataFifo.enq(data);
      messageWordsReg <= messageWordsReg - 1;
      countReg <= countReg - 1;
      if (countReg <= 1)
         state <= UpdateTail;
      else if (messageWordsReg == 1)
         state <= MessageHeaderRequested;
   endrule

   rule updateTail if (state == UpdateTail);
      if (verbose)
         $display("updateTail: tail=%d", tailReg);
      // update the tail pointer
      writeEngine[0].request.put(
          MemengineCmd {sglId: sglIdReg, base: 8, len: 8, burstLen: 8, tag: 0});
      writeEngine[0].data.enq(extend(tailReg));
      state <= Waiting;
   endrule

   rule waiting if (state == Waiting);
      let done <- writeEngine[0].done.get();
      state <= Idle;
   endrule

   interface SharedMemoryPortalConfig cfg;
      method Action setSglId(Bit#(32) id);
	 if (verbose) $display("setSglId id=%d", id);
         sglIdReg <= id;
         readyReg <= True;
      endmethod
   endinterface
   interface data = toPipeOut(dataFifo);
endmodule

module mkSharedMemoryIndicationPortal#(PipePortal#(numRequests, numIndications, 32) portal,
    Vector#(2,MemReadServer#(64)) readEngine, Vector#(2, MemWriteServer#(64)) writeEngine)(SharedMemoryPortal#(64));
   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;
   // read the head and tail pointers, if they are different, then read a request
   Reg#(Bit#(16)) limitReg <- mkReg(0);
   Reg#(Bit#(16)) headReg <- mkReg(0);
   Reg#(Bit#(16)) tailReg <- mkReg(0);
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(16)) methodIdReg <- mkReg(0);
   Reg#(Bool) paddingReg <- mkReg(False);
   Reg#(SharedMemoryPortalState) state <- mkReg(Idle);
   Reg#(Bit#(32)) sglIdReg <- mkReg(0);
   Reg#(Bool)     readyReg   <- mkReg(False);
   Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, portal.indications);
   Bool      interruptStatus = False;
   Bit#(16)  readyChannel = -1;
   function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
   Gearbox#(1,2,Bit#(32)) gb <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   let verbose = True;

   for (Integer i = valueOf(numIndications) - 1; i >= 0; i = i - 1) begin
      if (readyBits[i]) begin
         interruptStatus = True;
         readyChannel = fromInteger(i);
      end
   end

   rule updateIndHeadTail if (state == Idle && readyReg);
      readEngine[0].request.put(
          MemengineCmd {sglId: sglIdReg, base: 0, burstLen: 16, len: 16, tag: 0});
      state <= HeadRequested;
   endrule

   rule receiveIndHeadTail if (state == HeadRequested || state == TailRequested);
      let md <- toGet(readEngine[0].data).get();
      let data = md.data;
      let w0 = data[31:0];
      let w1 = data[63:32];
      let head = headReg;
      let tail = tailReg;
      if (state == HeadRequested) begin
         limitReg <= truncate(w0);
         headReg <= truncate(w1);
         head = truncate(w1);
         state <= TailRequested;
      end
      else begin
         if (tailReg == 0) begin
            tail = truncate(w0);
            tailReg <= tail;
         end
         //if (tail != headReg)
            state <= SendHeader;
         //else
            //state <= Idle;
      end
      if (verbose)
         $display("receiveIndHeadTail state=%d w0=%x w1=%x head=%d tail=%d limit=%d", state, w0, w1, head, tail, limitReg);
   endrule

   rule send64bits;
      let v = gb.first;
      gb.deq();
      writeEngine[0].data.enq(pack(v));
   endrule

   rule sendHeader if (state == SendHeader && interruptStatus);
      Bit#(16) messageBits = portal.messageSize.size(readyChannel);
      Bit#(16) roundup = messageBits[4:0] == 0 ? 0 : 1;
      Bit#(16) numWords = (messageBits >> 5) + roundup;
      Bit#(16) totalWords = numWords + 1;
      Bit#(32) hdr = extend(readyChannel) << 16 | extend(numWords + 1);
      if (numWords[0] == 0)
         totalWords = numWords + 2;
      paddingReg <= numWords[0] == 0;
      $display("sendHeader hdr=%h messageBits=%d numWords=%d totalWords=%d paddingReg=%d headReg=%h", hdr, messageBits, numWords, totalWords, paddingReg, headReg);
      headReg <= headReg + totalWords;
      messageWordsReg <= numWords;
      methodIdReg <= readyChannel;
      gb.enq(replicate(hdr));
      writeEngine[0].request.put( MemengineCmd
          {sglId: sglIdReg, base: extend(headReg) << 2, burstLen: 8, len: extend(totalWords) << 2, tag: 0});
      state <= SendMessage;
   endrule

   rule sendMessage if (state == SendMessage);
      messageWordsReg <= messageWordsReg - 1;
      let v = portal.indications[methodIdReg].first;
      portal.indications[methodIdReg].deq();
      gb.enq(replicate(v));
      $display("sendMessage v=%h messageWords=%d", v, messageWordsReg);
      if (messageWordsReg == 1) begin
         if (paddingReg)
            state <= SendPadding;
         else
            state <= UpdateHead;
      end
   endrule

   rule sendPadding if (state == SendPadding);
      $display("sendPadding");
      gb.enq(replicate(32'hffff0001));
      state <= UpdateHead;
   endrule

   rule updateHead if (state == UpdateHead);
      $display("updateIndHead limit=%d head=%d", limitReg, headReg);
      gb.enq(replicate(extend(limitReg)));
      writeEngine[0].request.put(
             MemengineCmd {sglId: sglIdReg, base: 0 << 2, burstLen: 8, len: 2 << 2, tag: 0});
      state <= UpdateHead2;
   endrule

   rule updateHead2 if (state == UpdateHead2);
      $display("updateIndHead2");
      gb.enq(replicate(extend(headReg)));
      state <= SendHeader;
   endrule

   rule done;
      let done <- writeEngine[0].done.get();
   endrule
   rule done2;
      let done <- writeEngine[1].done.get();
   endrule

   interface SharedMemoryPortalConfig cfg;
      method Action setSglId(Bit#(32) id);
         sglIdReg <= id;
         readyReg <= True;
      endmethod
   endinterface
endmodule