enable mkMemReaderPipe to issue multiple simultaneous requests

// Copyright (c) 2015 Connectal Project.

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

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

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

import FIFO::*;
import FIFOF::*;
import GetPut::*;
import ClientServer::*;
import BRAM::*;
import Vector::*;
import Probe::*;

import MemTypes::*;
import Pipe::*;
import ConfigCounter::*;

interface MemReaderPipe#(numeric type dsz);
   interface PipeOut#(MemData#(dsz)) dataPipe;
   interface MemReadClient#(dsz) readClient;
endinterface

module mkMemReaderPipe#(Reg#(SGLId) ptrReg,
			IteratorIfc#(Bit#(addrsz)) addrIterator,
			Bit#(BurstLenSize) burstLen)(MemReaderPipe#(dsz))
   provisos (Add#(a__, addrsz, MemOffsetSize),
	     Add#(b__, BurstLenSize, addrsz));

   let verbose = False;

   ConfigCounter#(addrsz)              counter <- mkConfigCounter(0);
   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFOF#(MemData#(dsz)) readDataFifo <- mkSizedFIFOF(8);
   Reg#(Bit#(MemTagSize)) tagReg <- mkReg(0);

   // 8+1 outstanding reads
   rule startReadReqRule if (counter.read() <= (extend(unpack(burstLen)) << 3));
      counter.increment(extend(unpack(burstLen)));
      let offset <- toGet(addrIterator.pipe).get();
      let tag = tagReg;
      if (addrIterator.isFirst())
	 tag = 0;
      if (verbose) $display("startReadReqRule: offset=%d counter=%d", offset, counter.read() + (extend(unpack(burstLen)) << 3));
      readReqFifo.enq(MemRequest { sglId: ptrReg, offset: extend(offset), burstLen: burstLen, tag: extend(tag) });
      tagReg <= tag + 1;
   endrule

   interface dataPipe = toPipeOut(readDataFifo);
   interface MemReadClient readClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData;
	 method Action put(MemData#(dsz) md);
	    readDataFifo.enq(md);
	    counter.decrement(fromInteger(valueOf(TDiv#(dsz,8))));
	    if (verbose) $display("memreader.readData counter.read() %d", counter.read());
	 endmethod
      endinterface
   endinterface
endmodule

interface MemWriterPipe#(numeric type dsz);
   interface PipeOut#(Bool) lastPipe;
   interface MemWriteClient#(dsz) writeClient;
endinterface

module mkMemWriterPipe#(Reg#(SGLId) ptrReg,
			IteratorIfc#(Bit#(addrsz)) addrIterator,
			PipeOut#(dtype) dataPipe,
			Bit#(BurstLenSize) burstLen)(MemWriterPipe#(dsz))
   provisos (Bits#(dtype, dsz),
	     Add#(a__, addrsz, MemOffsetSize),
	     Add#(b__, BurstLenSize, addrsz));

   ConfigCounter#(addrsz)              counter <- mkConfigCounter(0);
   FIFO#(MemRequest)                   reqFifo <- mkSizedFIFO(4);
   FIFO#(MemRequest)              writeReqFifo <- mkFIFO();
   FIFOF#(MemData#(dsz))         writeDataFifo <- mkSizedFIFOF(4);
   FIFO#(Bit#(MemTagSize))       writeDoneFifo <- mkSizedFIFO(4);
   FIFOF#(Bool)                       lastFifo <- mkSizedFIFOF(4);
   FIFOF#(Bool)                       doneFifo <- mkSizedFIFOF(4);

   rule writeReqRule;
      let offset <- toGet(addrIterator.pipe).get();
      let tag = 22;
      $display("writeReqRule: offset=%h burstLen=%d addrIterator.isLast %d", offset, burstLen, addrIterator.isLast());
      reqFifo.enq(MemRequest { sglId: ptrReg, offset: extend(offset), burstLen: burstLen, tag: tag });
      lastFifo.enq(addrIterator.isLast());
   endrule
   rule writeReqReadyRule if (counter.read() >= extend(unpack(burstLen)));
      let req <- toGet(reqFifo).get();
      writeReqFifo.enq(req);
      counter.decrement(extend(unpack(burstLen)));
   endrule
   rule writeDataRule;
      let tag = 22;
      let v <- toGet(dataPipe).get();
      //$display("MemWriterPipe.writeDataRule: data=%h", v);
      writeDataFifo.enq(MemData { data: pack(v), tag: tag, last: False });
      counter.increment(fromInteger(valueOf(TDiv#(dsz,8))));
   endrule
   rule writeDone;
      let last <- toGet(lastFifo).get();
      let tag <- toGet(writeDoneFifo).get();
      doneFifo.enq(last);
   endrule
   interface PipeOut lastPipe = toPipeOut(doneFifo);
   interface MemWriteClient writeClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   endinterface
endmodule

module mkMemWriterPipe2#(Bool lastOnly,
			 Reg#(SGLId) ptrReg,
			 IteratorIfc#(Bit#(addrsz)) addrIterator,
			 PipeOut#(dtype) dataPipe,
			 Bit#(BurstLenSize) burstLen)(MemWriterPipe#(dsz))
   provisos (Bits#(dtype, dsz),
	     Add#(a__, addrsz, MemOffsetSize),
	     Add#(b__, BurstLenSize, addrsz));

   ConfigCounter#(addrsz)              counter <- mkConfigCounter(0);
   FIFO#(MemRequest)                   reqFifo <- mkSizedFIFO(4);
   FIFO#(MemRequest)              writeReqFifo <- mkFIFO();
   FIFOF#(MemData#(dsz))         writeDataFifo <- mkSizedFIFOF(4);
   FIFO#(Bit#(MemTagSize))       writeDoneFifo <- mkSizedFIFO(4);
   FIFOF#(Bool)                       lastFifo <- mkSizedFIFOF(4);
   FIFOF#(Bool)                       doneFifo <- mkFIFOF();

   rule writeReqRule;
      let offset <- toGet(addrIterator.pipe).get();
      let tag = 22;
      $display("writeReqRule: offset=%h burstLen=%d addrIterator.isLast %d", offset, burstLen, addrIterator.isLast());
      reqFifo.enq(MemRequest { sglId: ptrReg, offset: extend(offset), burstLen: burstLen, tag: tag });
      lastFifo.enq(addrIterator.isLast());
   endrule
   rule writeReqReadyRule if (counter.read() >= extend(unpack(burstLen)));
      let req <- toGet(reqFifo).get();
      writeReqFifo.enq(req);
      counter.decrement(extend(unpack(burstLen)));
   endrule
   rule writeDataRule;
      let tag = 22;
      let v <- toGet(dataPipe).get();
      //$display("MemWriterPipe.writeDataRule: data=%h", v);
      writeDataFifo.enq(MemData { data: pack(v), tag: tag, last: False });
      counter.increment(fromInteger(valueOf(TDiv#(dsz,8))));
   endrule
   rule writeDone;
      let last <- toGet(lastFifo).get();
      let tag <- toGet(writeDoneFifo).get();
      if (!lastOnly || last)
	 doneFifo.enq(last);
   endrule
   interface PipeOut lastPipe = toPipeOut(doneFifo);
   interface MemWriteClient writeClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   endinterface
endmodule

module mkBramReaderPipe#(BRAMServer#(Bit#(addrsz), dataType) bramServer,
			 PipeOut#(IteratorValue#(Bit#(addrsz),void)) addrIterator)(PipeOut#(IteratorValue#(dataType,void)))
   provisos (Add#(a__, addrsz, MemOffsetSize),
	     Bits#(dataType,dsz));

   FIFOF#(Tuple2#(Bool,Bool)) firstlastFifo <- mkFIFOF();
   FIFOF#(IteratorValue#(dataType,void)) readDataFifo <- mkFIFOF();

   let verbose = False;

   rule issueBramReadRequest;
      let item <- toGet(addrIterator).get();
      bramServer.request.put(BRAMRequest{write: False, responseOnWrite: False, address: item.value, datain: unpack(0)});
      firstlastFifo.enq(tuple2(item.first, item.last));
      if (verbose) $display("issueBramReadRequest addr=%h first=%d last=%d", item.value, item.first, item.last);
   endrule

   rule readData;
      let v <- bramServer.response.get();
      match { .first, .last } <- toGet(firstlastFifo).get();
      readDataFifo.enq(IteratorValue { value: v, first: first, last: last });
   endrule
   return toPipeOut(readDataFifo);
endmodule

interface BramWriterPipe#(numeric type dsz);
   interface PipeOut#(Bool) lastPipe;
endinterface

module mkBramWriterPipe#(BRAMServer#(Bit#(addrsz), dtype) bramServer,
			IteratorIfc#(Bit#(addrsz)) addrIterator,
			PipeOut#(dtype) dataPipe)(BramWriterPipe#(dsz))
   provisos (Bits#(dtype, dsz),
	     Add#(a__, addrsz, MemOffsetSize));

   FIFOF#(Bool)                       lastFifo <- mkFIFOF();
   FIFOF#(Bool)                       doneFifo <- mkFIFOF();

   Reg#(Bit#(32)) wrrCount <- mkReg(0);
   Reg#(Bit#(32)) wdoneCount <- mkReg(0);
   rule writeReqRule;
      let offset <- toGet(addrIterator.pipe).get();
      let v <- toGet(dataPipe).get();
      wrrCount <= wrrCount + 1;
      //$display("BramWriter.writeReqRule: offset=%h addrIterator.isLast %d wrr %d", offset, addrIterator.isLast(), wrrCount);
      bramServer.request.put(BRAMRequest { write: True, responseOnWrite: False, address: offset, datain: v });
      lastFifo.enq(addrIterator.isLast());
   endrule
   rule writeDone;
      let last <- toGet(lastFifo).get();
      //$display("writeDone: wdoneCount=%d last=%d", wdoneCount, last);
      wdoneCount <= wdoneCount + 1;
      doneFifo.enq(last);
   endrule
   interface PipeOut lastPipe = toPipeOut(doneFifo);
endmodule

// Copyright (c) 2015 Connectal Project.

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

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

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

import FIFO::*;
import FIFOF::*;
import GetPut::*;
import ClientServer::*;
import BRAM::*;
import Vector::*;

import MemTypes::*;
import Pipe::*;
import ConfigCounter::*;

interface MemReaderPipe#(numeric type dsz);
   interface PipeOut#(MemData#(dsz)) dataPipe;
   interface MemReadClient#(dsz) readClient;
endinterface

module mkMemReaderPipe#(Reg#(SGLId) ptrReg,
			IteratorIfc#(Bit#(addrsz)) addrIterator,
			Bit#(BurstLenSize) burstLen)(MemReaderPipe#(dsz))
   provisos (Add#(a__, addrsz, MemOffsetSize));

   let verbose = False;

   FIFO#(MemRequest) readReqFifo <- mkFIFO();
   FIFOF#(MemData#(dsz)) readDataFifo <- mkSizedFIFOF(8);
   Reg#(Bit#(MemTagSize)) tagReg <- mkReg(0);

   rule startReadReqRule;
      let offset <- toGet(addrIterator.pipe).get();
      let tag = tagReg;
      if (addrIterator.isFirst())
	 tag = 0;
      if (verbose) $display("startReadReqRule: offset=%d", offset);
      readReqFifo.enq(MemRequest { sglId: ptrReg, offset: extend(offset), burstLen: burstLen, tag: extend(tag) });

      tagReg <= tag + 1;
   endrule

   interface dataPipe = toPipeOut(readDataFifo);
   interface MemReadClient readClient;
      interface Get readReq = toGet(readReqFifo);
      interface Put readData = toPut(readDataFifo);
   endinterface
endmodule

interface MemWriterPipe#(numeric type dsz);
   interface PipeOut#(Bool) lastPipe;
   interface MemWriteClient#(dsz) writeClient;
endinterface

module mkMemWriterPipe#(Reg#(SGLId) ptrReg,
			IteratorIfc#(Bit#(addrsz)) addrIterator,
			PipeOut#(dtype) dataPipe,
			Bit#(BurstLenSize) burstLen)(MemWriterPipe#(dsz))
   provisos (Bits#(dtype, dsz),
	     Add#(a__, addrsz, MemOffsetSize),
	     Add#(b__, BurstLenSize, addrsz));

   ConfigCounter#(addrsz)              counter <- mkConfigCounter(0);
   FIFO#(MemRequest)                   reqFifo <- mkSizedFIFO(4);
   FIFO#(MemRequest)              writeReqFifo <- mkFIFO();
   FIFOF#(MemData#(dsz))         writeDataFifo <- mkSizedFIFOF(4);
   FIFO#(Bit#(MemTagSize))       writeDoneFifo <- mkSizedFIFO(4);
   FIFOF#(Bool)                       lastFifo <- mkSizedFIFOF(4);
   FIFOF#(Bool)                       doneFifo <- mkSizedFIFOF(4);

   rule writeReqRule;
      let offset <- toGet(addrIterator.pipe).get();
      let tag = 22;
      $display("writeReqRule: offset=%h burstLen=%d addrIterator.isLast %d", offset, burstLen, addrIterator.isLast());
      reqFifo.enq(MemRequest { sglId: ptrReg, offset: extend(offset), burstLen: burstLen, tag: tag });
      lastFifo.enq(addrIterator.isLast());
   endrule
   rule writeReqReadyRule if (counter.read() >= extend(unpack(burstLen)));
      let req <- toGet(reqFifo).get();
      writeReqFifo.enq(req);
      counter.decrement(extend(unpack(burstLen)));
   endrule
   rule writeDataRule;
      let tag = 22;
      let v <- toGet(dataPipe).get();
      //$display("MemWriterPipe.writeDataRule: data=%h", v);
      writeDataFifo.enq(MemData { data: pack(v), tag: tag, last: False });
      counter.increment(fromInteger(valueOf(TDiv#(dsz,8))));
   endrule
   rule writeDone;
      let last <- toGet(lastFifo).get();
      let tag <- toGet(writeDoneFifo).get();
      doneFifo.enq(last);
   endrule
   interface PipeOut lastPipe = toPipeOut(doneFifo);
   interface MemWriteClient writeClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   endinterface
endmodule

module mkMemWriterPipe2#(Bool lastOnly,
			 Reg#(SGLId) ptrReg,
			 IteratorIfc#(Bit#(addrsz)) addrIterator,
			 PipeOut#(dtype) dataPipe,
			 Bit#(BurstLenSize) burstLen)(MemWriterPipe#(dsz))
   provisos (Bits#(dtype, dsz),
	     Add#(a__, addrsz, MemOffsetSize),
	     Add#(b__, BurstLenSize, addrsz));

   ConfigCounter#(addrsz)              counter <- mkConfigCounter(0);
   FIFO#(MemRequest)                   reqFifo <- mkSizedFIFO(4);
   FIFO#(MemRequest)              writeReqFifo <- mkFIFO();
   FIFOF#(MemData#(dsz))         writeDataFifo <- mkSizedFIFOF(4);
   FIFO#(Bit#(MemTagSize))       writeDoneFifo <- mkSizedFIFO(4);
   FIFOF#(Bool)                       lastFifo <- mkSizedFIFOF(4);
   FIFOF#(Bool)                       doneFifo <- mkFIFOF();

   rule writeReqRule;
      let offset <- toGet(addrIterator.pipe).get();
      let tag = 22;
      $display("writeReqRule: offset=%h burstLen=%d addrIterator.isLast %d", offset, burstLen, addrIterator.isLast());
      reqFifo.enq(MemRequest { sglId: ptrReg, offset: extend(offset), burstLen: burstLen, tag: tag });
      lastFifo.enq(addrIterator.isLast());
   endrule
   rule writeReqReadyRule if (counter.read() >= extend(unpack(burstLen)));
      let req <- toGet(reqFifo).get();
      writeReqFifo.enq(req);
      counter.decrement(extend(unpack(burstLen)));
   endrule
   rule writeDataRule;
      let tag = 22;
      let v <- toGet(dataPipe).get();
      //$display("MemWriterPipe.writeDataRule: data=%h", v);
      writeDataFifo.enq(MemData { data: pack(v), tag: tag, last: False });
      counter.increment(fromInteger(valueOf(TDiv#(dsz,8))));
   endrule
   rule writeDone;
      let last <- toGet(lastFifo).get();
      let tag <- toGet(writeDoneFifo).get();
      if (!lastOnly || last)
	 doneFifo.enq(last);
   endrule
   interface PipeOut lastPipe = toPipeOut(doneFifo);
   interface MemWriteClient writeClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   endinterface
endmodule

module mkBramReaderPipe#(BRAMServer#(Bit#(addrsz), dataType) bramServer,
			 PipeOut#(IteratorValue#(Bit#(addrsz),void)) addrIterator)(PipeOut#(IteratorValue#(dataType,void)))
   provisos (Add#(a__, addrsz, MemOffsetSize),
	     Bits#(dataType,dsz));

   FIFOF#(Tuple2#(Bool,Bool)) firstlastFifo <- mkFIFOF();
   FIFOF#(IteratorValue#(dataType,void)) readDataFifo <- mkFIFOF();

   let verbose = False;

   rule issueBramReadRequest;
      let item <- toGet(addrIterator).get();
      bramServer.request.put(BRAMRequest{write: False, responseOnWrite: False, address: item.value, datain: unpack(0)});
      firstlastFifo.enq(tuple2(item.first, item.last));
      if (verbose) $display("issueBramReadRequest addr=%h first=%d last=%d", item.value, item.first, item.last);
   endrule

   rule readData;
      let v <- bramServer.response.get();
      match { .first, .last } <- toGet(firstlastFifo).get();
      readDataFifo.enq(IteratorValue { value: v, first: first, last: last });
   endrule
   return toPipeOut(readDataFifo);
endmodule

interface BramWriterPipe#(numeric type dsz);
   interface PipeOut#(Bool) lastPipe;
endinterface

module mkBramWriterPipe#(BRAMServer#(Bit#(addrsz), dtype) bramServer,
			IteratorIfc#(Bit#(addrsz)) addrIterator,
			PipeOut#(dtype) dataPipe)(BramWriterPipe#(dsz))
   provisos (Bits#(dtype, dsz),
	     Add#(a__, addrsz, MemOffsetSize));

   FIFOF#(Bool)                       lastFifo <- mkFIFOF();
   FIFOF#(Bool)                       doneFifo <- mkFIFOF();

   Reg#(Bit#(32)) wrrCount <- mkReg(0);
   Reg#(Bit#(32)) wdoneCount <- mkReg(0);
   rule writeReqRule;
      let offset <- toGet(addrIterator.pipe).get();
      let v <- toGet(dataPipe).get();
      wrrCount <= wrrCount + 1;
      //$display("BramWriter.writeReqRule: offset=%h addrIterator.isLast %d wrr %d", offset, addrIterator.isLast(), wrrCount);
      bramServer.request.put(BRAMRequest { write: True, responseOnWrite: False, address: offset, datain: v });
      lastFifo.enq(addrIterator.isLast());
   endrule
   rule writeDone;
      let last <- toGet(lastFifo).get();
      //$display("writeDone: wdoneCount=%d last=%d", wdoneCount, last);
      wdoneCount <= wdoneCount + 1;
      doneFifo.enq(last);
   endrule
   interface PipeOut lastPipe = toPipeOut(doneFifo);
endmodule


compiles now

compiles now

// 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 Complex::*;
import FixedPoint::*;
//import StmtFSM::*;
import BRAM::*;
import Pipe::*;
import Vector::*;

typedef Complex#(FixedPoint#(2,23)) DDSOutType;
typedef FixedPoint#(2,23) PhaseType;

interface DDS;
   method Action setPhaseAdvance(FixedPoint#(10,32) v);
   interface PipeOut#(DDSOutType) osc;
endinterface

module mkDDS(DDS);
   BRAM_Configure cfg = defaultValue;
   cfg.memorySize = 1024;
   cfg.loadFormat = tagged Binary "sine.bin";
   BRAM1Port#(Bit#(10), DDSOutType) ram <-mkBRAM1Server(cfg);
   Reg#(DDSOutType) ddsout <- mkReg(?);
   Reg#(PhaseType) phase <- mkReg(0);
   Reg#(PhaseType) phaseAdvance <- mkReg(0);
      /*
   Reg#(UInt#(12)) idx <- mkReg(0);
   Stmt dumpRam =
   seq
      for (idx <= 0; idx < 1024; idx <= idx + 1)
	 seq
	    ram.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: truncate(pack(idx)), datain: ?});
	   action
	      let v <- ram.portA.response.get();
	      $display("adr %d", idx);
	      $write( "re " ) ; fxptWrite( 10, v.rel ) ; $display("" ) ;
	      $write( "im " ) ; fxptWrite( 10, v.img ) ; $display("" ) ;
	      endaction
	    endseq
   endseq;

   mkAutoFSM (dumpRam);
   */

   rule filter_phase;
      phase <= phase + phaseAdvance;
      ram.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: truncate(rotateBitsBy(phase.f, 10)), datain: ?});
   endrule

   rule ddsoutrule;
      let v <- ram.portA.response.get();
      ddsout <= v;
   endrule

   method Action setPhaseAdvance(FixedPoint#(10,32) v);
      phaseAdvance <= PhaseType{i: truncate(v.i), f: truncate(rotateBitsBy(v.f, 10))};
      endmethod
   interface PipeOut osc = toPipeOut(ddsout);

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 Complex::*;
import FixedPoint::*;
import StmtFSM::*;
import BRAM::*;

interface DDS;
   method Action setPhaseAdvance(FixedPoint#(10,32) v);
   method  Complex#(FixedPoint#(2,23)) getValue();
endinterface

module mkDDS(Empty);
   BRAM_Configure cfg = defaultValue;
   cfg.memorySize = 1024;
   cfg.loadFormat = tagged Binary "sine.bin";
   BRAM1Port#(Bit#(10), Complex#(FixedPoint#(2,23))) ram <-mkBRAM1Server(cfg);
   Reg#(FixedPoint#(2,23)) phase <- mkReg(0);
   Reg#(FixedPoint#(2,23)) phaseAdvance <- mkReg(0);
   Reg#(UInt#(12)) idx <- mkReg(0);
   Stmt dumpRam =
   seq
      for (idx <= 0; idx < 1024; idx <= idx + 1)
	 seq
	    ram.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: truncate(pack(idx)), datain: ?});
	   action
	      let v <- ram.portA.response.get();
	      $display("adr %d", idx);
	      $write( "re " ) ; fxptWrite( 10, v.rel ) ; $display("" ) ;
	      $write( "im " ) ; fxptWrite( 10, v.img ) ; $display("" ) ;
	      endaction
	    endseq
   endseq;

   mkAutoFSM (dumpRam);

   rule filter_phase;
      phase <= phase + phaseAdvance;

   endrule

endmodule


commented examples/zynqpcie

commented examples/zynqpcie

/* 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 FIFO::*;
import Connectable::*;
import GetPutWithClocks::*;
import CtrlMux::*;
import Portal::*;
import HostInterface::*;
import MemTypes::*;
import Leds::*;
import MemPortal::*;
import PcieHost ::*;
`ifndef BSIM
import PCIEWRAPPER          ::*;
import PcieEndpointX7       ::*;
import ConnectalXilinxCells ::*;
`endif

// generated by tool
import Simple::*;
import ZynqPcieTestRequest::*;
import ZynqPcieTestIndication::*;
import ZynqPcieTestIF::*;

// defined by user
import SimpleIF::*;

typedef enum {SimpleRequest, SimpleIndication, ZynqPcieTestRequest, ZynqPcieTestIndication} IfcNames deriving (Eq,Bits);

interface ZynqPcie;
   (* prefix="PCIE" *)
   interface PciewrapPci_exp#(PcieLanes) pcie;
   method Action pcie_sys_clk(Bit#(1) p, Bit#(1) n);
   method Action sys_clk(Bit#(1) p, Bit#(1) n);
   method Action pcie_sys_reset(Bit#(1) n);
   interface Clock deleteme_unused_clockFoo;
   interface Clock deleteme_unused_clockPortal;
   interface Clock deleteme_unused_clock100mhz;
endinterface

//
// This module is just to put a synthesis boundary around
// mkPcieHostTop, which does not currently have one.  The Makefile
// uses this line to synthesize this module into its own
// netlist. We're not going to change it, so on subsequent rebuilds,
// buildcache will use the previous synthesis result.
//
//    CONNECTALFLAGS += -P mkPcieHostTopSynth
//
(* synthesize *)
module mkPcieHostTopSynth#(Clock pcie_sys_clk_p, Clock pcie_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pcie_sys_reset_n)(PcieHostTop);
   (*hide*) let host <- mkPcieHostTop(pcie_sys_clk_p, pcie_sys_clk_n, sys_clk_p, sys_clk_n, pcie_sys_reset_n);
   return host;
endmodule

//
// This design exposes a PCIe interface via its "pins"
//
module mkConnectalTop(ConnectalTop#(PhysAddrWidth,64,ZynqPcie,0));

   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   // Clock laundering facility:
   //
   // Bluespec does have a way for exposed interfaces to be a source
   // for clocks so we use B2C (bit to clock) to convert bits to
   // clocks.
   B2C1 b2c_pcie_sys_clk_p <- mkB2C1();
   B2C1 b2c_pcie_sys_clk_n <- mkB2C1();
   B2C b2c_pcie_sys_reset_n <- mkB2C();
   B2C1 b2c_sys_clk_p <- mkB2C1();
   B2C1 b2c_sys_clk_n <- mkB2C1();

   //
   // Instantiate a PcieHostTop, so that we can connect it to a set of portals.
   //
   PcieHostTop host <- mkPcieHostTopSynth(b2c_pcie_sys_clk_p.c, b2c_pcie_sys_clk_n.c, b2c_sys_clk_p.c, b2c_sys_clk_n.c, b2c_pcie_sys_reset_n.r);

   // The PCIe portals and Zynq portals are in different clock domains, so synchronize bits that are provided to mkZynqPcieTest
   SyncBitIfc#(Bit#(1)) resetBit <- mkSyncBit(b2c_pcie_sys_reset_n.c, b2c_pcie_sys_reset_n.r, defaultClock);
   SyncBitIfc#(Bit#(1)) resetSeenBit <- mkSyncBit(b2c_pcie_sys_reset_n.c, b2c_pcie_sys_reset_n.r, defaultClock);
   SyncBitIfc#(Bit#(1)) linkUpBit <- mkSyncBit(host.portalClock, host.portalReset, defaultClock);
   // This register is in the PCIe portal clock domain
   Reg#(Bit#(1)) resetSeenReg <- mkReg(0, clocked_by b2c_pcie_sys_reset_n.c, reset_by b2c_pcie_sys_reset_n.r);

   // instantiate zynq-side user portals
   ZynqPcieTestIndicationProxy zynqPcieTestIndicationProxy <- mkZynqPcieTestIndicationProxy(ZynqPcieTestIndication);
   ZynqPcieTest zynqPcieTest <- mkZynqPcieTest(linkUpBit, resetBit, resetSeenBit, zynqPcieTestIndicationProxy.ifc);
   ZynqPcieTestRequestWrapper zynqPcieTestRequestWrapper <- mkZynqPcieTestRequestWrapper(ZynqPcieTestRequest,zynqPcieTest.request);

   // Connect the exposed BRAM client to the trace BRAM server in the PcieHost
   mkConnectionWithClocks(zynqPcieTest.traceBramClient, host.tpciehost.traceBramServer, defaultClock, defaultReset, host.portalClock, host.portalReset);

   // send the value of the lnk_up signal from the PCIE endpoint to the Zynq portal clock domain
   rule updateLinkBit;
      linkUpBit.send(host.tep7.user.lnk_up());
   endrule

   // Construct the vector of zynq portals
   Vector#(2,StdPortal) zynqPortals;
   zynqPortals[0] = zynqPcieTestIndicationProxy.portalIfc;
   zynqPortals[1] = zynqPcieTestRequestWrapper.portalIfc;
   let ctrl_mux <- mkSlaveMux(zynqPortals);

   // LED values
   // led0: toggles once a second as a board heartbeat
   // led1: Indicates PCIE link is up
   // led2: Indicates current value of the PCIE reset signal from the host
   // led3: Indicates that a 0-to-1 transition was detected on the PCIE reset signal from the host
   Reg#(Bit#(4)) ledsValue <- mkReg(5);
   Reg#(Bit#(32)) remainingDuration <- mkReg(100000000);

   rule updateLeds;
      let duration = remainingDuration;
      let bits = ledsValue;
      bits[3] = resetSeenBit.read();
      bits[2] = resetBit.read();
      bits[1] = linkUpBit.read();

      if (duration == 0) begin
	 bits[0] = ~bits[0];
	 duration = 100000000;
      end
      else begin
	 duration = duration - 1;
      end
      ledsValue <= bits;
      remainingDuration <= duration;
   endrule

   //
   // Instantiate Simple portals as the test case connected to the x86 host via PCIe
   //
   SimpleProxy simpleIndicationProxy <- mkSimpleProxy(SimpleIndication, clocked_by host.portalClock, reset_by host.portalReset);
   Simple simpleRequest <- mkSimple(simpleIndicationProxy.ifc, clocked_by host.portalClock, reset_by host.portalReset);
   SimpleWrapper simpleRequestWrapper <- mkSimpleWrapper(SimpleRequest,simpleRequest, clocked_by host.portalClock, reset_by host.portalReset);

   Vector#(2,StdPortal) pcieportals;
   pcieportals[0] = simpleIndicationProxy.portalIfc;
   pcieportals[1] = simpleRequestWrapper.portalIfc;
   PhysMemSlave#(32,32) pcie_ctrl_mux <- mkSlaveMux(pcieportals, clocked_by host.portalClock, reset_by host.portalReset);
   // manually connect the PCIe host PhysMemMaster to the pcie_ctrl_mux PhysMemSlave
   mkConnection(host.tpciehost.master, pcie_ctrl_mux, clocked_by host.portalClock, reset_by host.portalReset);

   // Construct the ZynqPcie interface
   ZynqPcie zpcie = (interface ZynqPcie;
		     method Action pcie_sys_clk(Bit#(1) p, Bit#(1) n);

		        // This is a bit of sleight of hand. We depend
		        // on the way the bluespec compiler generates
		        // verilog for the following method invocation
		        // to connect the input clock signals via the
		        // B2C to the 100Mhz pcie system clock input
		        // of the PcieHost.
			b2c_pcie_sys_clk_p.inputclock(p);
			b2c_pcie_sys_clk_n.inputclock(n);
		     endmethod
		     method Action sys_clk(Bit#(1) p, Bit#(1) n);
			// same for the 200MHz system clock
			b2c_sys_clk_p.inputclock(p);
			b2c_sys_clk_n.inputclock(n);
		     endmethod
		     method Action pcie_sys_reset(Bit#(1) n);
			if (n == 1)
			   resetSeenReg <= 1;
			resetSeenBit.send(resetSeenReg);
			resetBit.send(n);
			// same for the reset
			b2c_pcie_sys_reset_n.inputreset(n);
		     endmethod
		     interface pcie = host.tep7.pcie;
		     // The Bluespec compiler requires that the clocks
		     // used by exported methods/pins also be
		     // exposed. Because of the b2c, some of the clock
		     // pins were not visible to the compiler, so we
		     // export them here.
		     //
		     // We do not have FPGA pins to which we want to
		     // connect them, so we rename them
		     // "deleteme_unused_clock..." so that the
		     // synthesis script will disconnect them from the
		     // ports of the toplevel netlist.
		     interface Clock deleteme_unused_clockFoo = b2c_pcie_sys_reset_n.c;
		     interface Clock deleteme_unused_clockPortal = host.portalClock;
		     interface Clock deleteme_unused_clock100mhz = host.tpci_clk_100mhz_buf;
		     endinterface);

   // connect the standard LEDS interface
   LEDS ledsIF = (interface LEDS; method Bit#(LedsWidth) leds(); return truncate(ledsValue); endmethod endinterface);

   // export the interfaces from the Zynq portals
   interface interrupt = getInterruptVector(zynqPortals);
   interface slave = ctrl_mux;
   interface masters = nil;
   interface leds = ledsIF;

   // expose the pcie interface as pins
   interface pins = zpcie;

endmodule : mkConnectalTop

/* 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 FIFO::*;
import Connectable::*;
import GetPutWithClocks::*;
import CtrlMux::*;
import Portal::*;
import HostInterface::*;
import MemTypes::*;
import Leds::*;
import MemPortal::*;
import PcieHost ::*;
`ifndef BSIM
import PCIEWRAPPER          ::*;
import PcieEndpointX7       ::*;
import ConnectalXilinxCells ::*;
`endif

// generated by tool
import Simple::*;
import ZynqPcieTestRequest::*;
import ZynqPcieTestIndication::*;
import ZynqPcieTestIF::*;

// defined by user
import SimpleIF::*;

typedef enum {SimpleRequest, SimpleIndication, ZynqPcieTestRequest, ZynqPcieTestIndication} IfcNames deriving (Eq,Bits);

interface ZynqPcie;
   (* prefix="PCIE" *)
   interface PciewrapPci_exp#(PcieLanes) pcie;
   method Action pcie_sys_clk(Bit#(1) p, Bit#(1) n);
   method Action sys_clk(Bit#(1) p, Bit#(1) n);
   method Action pcie_sys_reset(Bit#(1) n);
   interface Clock deleteme_unused_clockFoo;
   interface Clock deleteme_unused_clockPortal;
   interface Clock deleteme_unused_clock100mhz;
endinterface

(* synthesize *)
module mkPcieHostTopSynth#(Clock pcie_sys_clk_p, Clock pcie_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pcie_sys_reset_n)(PcieHostTop);
   (*hide*) let host <- mkPcieHostTop(pcie_sys_clk_p, pcie_sys_clk_n, sys_clk_p, sys_clk_n, pcie_sys_reset_n);
   return host;
endmodule

module mkConnectalTop(ConnectalTop#(PhysAddrWidth,64,ZynqPcie,0));

   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   B2C1 b2c_pcie_sys_clk_p <- mkB2C1();
   B2C1 b2c_pcie_sys_clk_n <- mkB2C1();
   B2C b2c_pcie_sys_reset_n <- mkB2C();
   B2C1 b2c_sys_clk_p <- mkB2C1();
   B2C1 b2c_sys_clk_n <- mkB2C1();

   PcieHostTop host <- mkPcieHostTopSynth(b2c_pcie_sys_clk_p.c, b2c_pcie_sys_clk_n.c, b2c_sys_clk_p.c, b2c_sys_clk_n.c, b2c_pcie_sys_reset_n.r);

   // instantiate user portals
   SyncBitIfc#(Bit#(1)) resetBit <- mkSyncBit(b2c_pcie_sys_reset_n.c, b2c_pcie_sys_reset_n.r, defaultClock);
   SyncBitIfc#(Bit#(1)) resetSeenBit <- mkSyncBit(b2c_pcie_sys_reset_n.c, b2c_pcie_sys_reset_n.r, defaultClock);
   SyncBitIfc#(Bit#(1)) linkUpBit <- mkSyncBit(host.portalClock, host.portalReset, defaultClock);
   ZynqPcieTestIndicationProxy zynqPcieTestIndicationProxy <- mkZynqPcieTestIndicationProxy(ZynqPcieTestIndication);
   ZynqPcieTest zynqPcieTest <- mkZynqPcieTest(linkUpBit, resetBit, resetSeenBit, zynqPcieTestIndicationProxy.ifc);
   ZynqPcieTestRequestWrapper zynqPcieTestRequestWrapper <- mkZynqPcieTestRequestWrapper(ZynqPcieTestRequest,zynqPcieTest.request);

   mkConnectionWithClocks(zynqPcieTest.traceBramClient, host.tpciehost.traceBramServer, defaultClock, defaultReset, host.portalClock, host.portalReset);

   Vector#(2,StdPortal) portals;
   portals[0] = zynqPcieTestIndicationProxy.portalIfc;
   portals[1] = zynqPcieTestRequestWrapper.portalIfc;
   let ctrl_mux <- mkSlaveMux(portals);

   Reg#(Bit#(8)) ledsValue <- mkReg(5);
   Reg#(Bit#(32)) remainingDuration <- mkReg(100000000);

   rule updateLeds;
      let duration = remainingDuration;
      let bits = ledsValue;
      bits[3] = resetSeenBit.read();
      bits[2] = resetBit.read();
      bits[1] = linkUpBit.read();

      if (duration == 0) begin
	 bits[0] = ~bits[0];
	 duration = 100000000;
      end
      else begin
	 duration = duration - 1;
      end
      ledsValue <= bits;
      remainingDuration <= duration;
   endrule

   rule updateLinkBit;
      linkUpBit.send(host.tep7.user.lnk_up());
   endrule
   SimpleProxy simpleIndicationProxy <- mkSimpleProxy(SimpleIndication, clocked_by host.portalClock, reset_by host.portalReset);
   Simple simpleRequest <- mkSimple(simpleIndicationProxy.ifc, clocked_by host.portalClock, reset_by host.portalReset);
   SimpleWrapper simpleRequestWrapper <- mkSimpleWrapper(SimpleRequest,simpleRequest, clocked_by host.portalClock, reset_by host.portalReset);

   Vector#(2,StdPortal) pcieportals;
   pcieportals[0] = simpleIndicationProxy.portalIfc;
   pcieportals[1] = simpleRequestWrapper.portalIfc;
   PhysMemSlave#(32,32) pcie_ctrl_mux <- mkSlaveMux(pcieportals, clocked_by host.portalClock, reset_by host.portalReset);
   mkConnection(host.tpciehost.master, pcie_ctrl_mux, clocked_by host.portalClock, reset_by host.portalReset);

   Reg#(Bit#(1)) resetSeenReg <- mkReg(0);
   ZynqPcie zpcie = (interface ZynqPcie;
		     method Action pcie_sys_clk(Bit#(1) p, Bit#(1) n);
			b2c_pcie_sys_clk_p.inputclock(p);
			b2c_pcie_sys_clk_n.inputclock(n);
		     endmethod
		     method Action sys_clk(Bit#(1) p, Bit#(1) n);
			b2c_sys_clk_p.inputclock(p);
			b2c_sys_clk_n.inputclock(n);
		     endmethod
		     method Action pcie_sys_reset(Bit#(1) n);
			if (n == 1)
			   resetSeenReg <= 1;
			resetSeenBit.send(resetSeenReg);
			resetBit.send(n);
			b2c_pcie_sys_reset_n.inputreset(n);
		     endmethod
		     interface pcie = host.tep7.pcie;
		     interface Clock deleteme_unused_clockFoo = b2c_pcie_sys_reset_n.c;
		     interface Clock deleteme_unused_clockPortal = host.portalClock;
		     interface Clock deleteme_unused_clock100mhz = host.tpci_clk_100mhz_buf;
		     endinterface);

   LEDS ledsIF = (interface LEDS; method Bit#(LedsWidth) leds(); return truncate(ledsValue); endmethod endinterface);

   interface interrupt = getInterruptVector(portals);
   interface slave = ctrl_mux;
   interface masters = nil;
   interface leds = ledsIF;
   interface pins = zpcie;

endmodule : mkConnectalTop


added support for odd number of words per method invocation

Change-Id: Ib272a81033f6442b854d535728ff86cb1eaf5db6

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

import Pipe::*;
import Portal::*;
import MemTypes::*;
import AddressGenerator::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import ConnectalMemory::*;

typedef struct {
    Bit#(1) select;
    Bit#(6) tag;
} ReadReqInfo deriving (Bits);

interface PortalCtrlMemSlave#(numeric type addrWidth, numeric type dataWidth);
   interface PhysMemSlave#(addrWidth, dataWidth) memSlave;
   interface ReadOnly#(Bool) interrupt;
   interface WriteOnly#(Bool) top;
endinterface

module mkPortalCtrlMemSlave#(Bit#(dataWidth) ifcId,
			     Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indicationPipes)(PortalCtrlMemSlave#(addrWidth, dataWidth))
   provisos( Add#(a__, 1, dataWidth)
	    ,Div#(dataWidth, 8, b__)
	    ,Bits#(MemData#(dataWidth), c__)
	    ,Add#(d__, dataWidth, TMul#(dataWidth, 2))
	    );

   AddressGenerator#(addrWidth,dataWidth) ctrlReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth,dataWidth) ctrlWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(MemData#(dataWidth))        ctrlWriteDataFifo <- mkFIFO();
   FIFO#(Bit#(MemTagSize))        ctrlWriteDoneFifo <- mkFIFO();
   Reg#(Bool) topR <- mkReg(True);

   function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
   Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, indicationPipes);

   Reg#(Bool) interruptEnableReg <- mkReg(False);
   Bool      interruptStatus = False;
   Reg#(Bit#(TMul#(dataWidth,2))) cycle_count <- mkReg(0);
   Reg#(Bit#(dataWidth))    snapshot <- mkReg(0);

   Bit#(dataWidth)  readyChannel = -1;
   for (Integer i = 0; i < valueOf(numIndications); i = i + 1) begin
      if (readyBits[i]) begin
         interruptStatus = True;
         readyChannel = fromInteger(i);
      end
   end

   rule count;
      cycle_count <= cycle_count+1;
   endrule

   rule writeDataRule;
      let d <- toGet(ctrlWriteDataFifo).get();
      let b <- ctrlWriteAddrGenerator.addrBeat.get();
      //$display("mkCtrl.writeData addr=%h data=%h last=%d", b.addr, d.data, b.last);
      let v = d.data;
      let addr = b.addr;
      if (addr == 'h000)
	 noAction;
      if (addr == 'h004)
	 interruptEnableReg <= v[0] == 1'd1;
      if (b.last)
	 ctrlWriteDoneFifo.enq(b.tag);
   endrule

   interface PhysMemSlave memSlave;
      interface PhysMemReadServer read_server;
	 interface Put readReq = ctrlReadAddrGenerator.request;
	 interface Get readData;
	    method ActionValue#(MemData#(dataWidth)) get();
	       let b <- ctrlReadAddrGenerator.addrBeat.get();
	       let addr = b.addr;
	       let v = 'h05a05a0;
	       if (addr == 'h000)
		  v = interruptStatus ? 1 : 0;
	       if (addr == 'h004)
		  v = interruptEnableReg ? 1 : 0;
	       if (addr == 'h008)
		  v = 7;
               if (addr == 'h00C) begin
		  if (interruptStatus)
		     v = readyChannel+1;
		  else
		     v = 0;
               end
	       if (addr == 'h010)
		  v = ifcId;
	       if (addr == 'h014)
		  v = extend(pack(topR));
	       if (addr == 'h018) begin
		  snapshot <= truncate(cycle_count);
		  v = truncate(cycle_count>>valueOf(dataWidth));
	       end
	       if (addr == 'h01C)
		  v = snapshot;
	       //$display("mkCtrl.readData addr=%h data=%h", b.addr, v);
	       return MemData { data: v, tag: b.tag, last: b.last };
	    endmethod
	 endinterface
      endinterface: read_server
      interface PhysMemWriteServer write_server;
	 interface Put writeReq = ctrlWriteAddrGenerator.request;
	 interface Put writeData;
	    method Action put(MemData#(dataWidth) d);
	       ctrlWriteDataFifo.enq(d);
	    endmethod
	 endinterface
	 interface Get writeDone;
	    method ActionValue#(Bit#(MemTagSize)) get();
	       let tag <- toGet(ctrlWriteDoneFifo).get();
	       return tag;
	    endmethod
	 endinterface
      endinterface: write_server
   endinterface: memSlave
   interface ReadOnly interrupt;
      method Bool _read();
	 return interruptStatus && interruptEnableReg;
      endmethod
   endinterface
   interface WriteOnly top;
      method Action _write(Bool v);
	 topR <= v;
      endmethod
   endinterface
endmodule

module mkPipeInMemSlave#(PipeIn#(Bit#(dataWidth)) methodPipe)(PhysMemSlave#(addrWidth, dataWidth))
   provisos (Add#(1,a__,dataWidth));

   AddressGenerator#(addrWidth,dataWidth) fifoReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))        fifoWriteDoneFifo <- mkFIFO();

   interface PhysMemReadServer read_server;
      interface Put readReq = fifoReadAddrGenerator.request;
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let b <- fifoReadAddrGenerator.addrBeat.get();
	    let v = 0;
	    if (b.addr[7:0] == 4)
	       v = extend(pack(methodPipe.notFull()));
	    return MemData { data: v, tag: b.tag, last: b.last };
	 endmethod
      endinterface
   endinterface
   interface PhysMemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeInMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	    methodPipe.enq(d.data);
	    // this used to be where we triggered putFailed
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkPipeOutMemSlave#(PipeOut#(Bit#(dataWidth)) methodPipe)(PhysMemSlave#(addrWidth, dataWidth))
   provisos (Add#(1,a__,dataWidth));
   AddressGenerator#(addrWidth,dataWidth) fifoReadAddrGenerator <- mkAddressGenerator();
   AddressGenerator#(addrWidth,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))                  fifoWriteDoneFifo <- mkFIFO();
   FIFO#(MemData#(dataWidth))                   fifoReadDataFifo <- mkFIFO();
   rule readDataRule;
      let b <- fifoReadAddrGenerator.addrBeat.get();
      let v = 0;
      if (b.addr[7:0] == 0)
	 v <- toGet(methodPipe).get();
      else if (b.addr[7:0] == 4)
	 v = extend(pack(methodPipe.notEmpty()));
      //$display("mkPipeOutMemSlave.readData.get addr=%h data=%h", b.addr, data);
      fifoReadDataFifo.enq(MemData { data: v, tag: b.tag, last: b.last });
   endrule

   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth) req);
	    fifoReadAddrGenerator.request.put(req);
	    if (!methodPipe.notEmpty())
	       $display("***\n\n mkPipeOutMemSlave.read_server.underflow! \n\n****");
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let d <- toGet(fifoReadDataFifo).get();
	    return d;
	 endmethod
      endinterface
   endinterface
   interface PhysMemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeOutMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkMemPortal#(Bit#(slaveDataWidth) ifcId,
		    PipePortal#(numRequests, numIndications, slaveDataWidth) portal)(MemPortal#(slaveAddrWidth, slaveDataWidth))
   provisos ( Add#(1, i__, slaveDataWidth)
	     ,Add#(c__, 8, slaveAddrWidth)
	     ,Add#(d__, 1, c__)
	     ,Add#(a__, TLog#(TAdd#(1, TAdd#(numRequests, numIndications))), c__)
	     ,Add#(b__, slaveDataWidth, TMul#(slaveDataWidth, 2))
	     );

   Vector#(numRequests,    PipeIn#(Bit#(slaveDataWidth)))     requestPipes = portal.requests;
   Vector#(numIndications, PipeOut#(Bit#(slaveDataWidth))) indicationPipes = portal.indications;
   Vector#(numRequests,    PhysMemSlave#(8, slaveDataWidth))    requestMemSlaves <- mapM(mkPipeInMemSlave, requestPipes);
   Vector#(numIndications, PhysMemSlave#(8, slaveDataWidth)) indicationMemSlaves <- mapM(mkPipeOutMemSlave, indicationPipes);

   PortalCtrlMemSlave#(8,slaveDataWidth) ctrlPort <- mkPortalCtrlMemSlave(ifcId, indicationPipes);
   PhysMemSlave#(slaveAddrWidth,slaveDataWidth) memslave  <- mkMemSlaveMux(cons(ctrlPort.memSlave,append(requestMemSlaves, indicationMemSlaves)));
   interface PhysMemSlave slave = memslave;
   interface ReadOnly interrupt = ctrlPort.interrupt;
   interface WriteOnly top = ctrlPort.top;
endmodule

typedef enum {
   Idle,
   HeadRequested,
   TailRequested,
   RequestMessage,
   MessageHeaderRequested,
   MessageRequested,
   UpdateTail,
   Waiting,
   Stop
   } SharedMemoryPortalState deriving (Bits,Eq);

module mkSharedMemoryPortal#(PipePortal#(numRequests, numIndications, 32) portal)(SharedMemoryPortal#(64));

   MemreadEngineV#(64,2,2) readEngine <- mkMemreadEngine();
   MemwriteEngineV#(64,2,2) writeEngine <- mkMemwriteEngine();

   Reg#(Bit#(32)) sglIdReg <- mkReg(0);
   Reg#(Bool)     readyReg   <- mkReg(False);

   if (valueOf(numRequests) > 0) begin
      // read the head and tail pointers, if they are different, then read a request
      Reg#(Bit#(32)) limitReg <- mkReg(0);
      Reg#(Bit#(32)) reqHeadReg <- mkReg(0);
      Reg#(Bit#(32)) reqTailReg <- mkReg(0);
      Reg#(Bit#(16)) wordCountReg <- mkReg(0);
      Reg#(Bit#(16)) demuxCountReg <- mkReg(0);
      Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
      Reg#(Bit#(16)) methodIdReg <- mkReg(0);
      Reg#(SharedMemoryPortalState) reqState <- mkReg(Idle);

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

      rule receiveReqHead if (reqState == HeadRequested || reqState == TailRequested);
	 let data <- toGet(readEngine.dataPipes[0]).get();
	 let w0 = data[31:0];
	 let w1 = data[63:32];
	 let head = reqHeadReg;
	 let tail = reqTailReg;
	 $display("receiveReqHead state=%d w0=%x w1=%x", reqState, w0, w1);
	 if (reqState == HeadRequested) begin
	    reqHeadReg <= w1;
	    reqState <= TailRequested;
	 end
	 else begin
	    tail = w0;
	    if (reqTailReg == 0)
	       reqTailReg <= tail;
	     if (tail != reqHeadReg)
		reqState <= RequestMessage;
	     else
		reqState <= Idle;
	 end
      endrule

      rule requestMessage if (reqState == RequestMessage);
	 Bit#(32) wordCount = reqHeadReg - reqTailReg;
	 $display("requestMessage id=%d tail=%h wordCount=%d", sglIdReg, reqTailReg, wordCount);
	 reqTailReg <= reqTailReg + wordCount;
	 wordCountReg <= truncate(wordCount);
	 demuxCountReg <= truncate(wordCount);
	 let fetchCount = wordCount;
	 if ((fetchCount & 1) == 1)
	    fetchCount = fetchCount + 1;
	 readEngine.readServers[0].request.put(MemengineCmd {sglId: sglIdReg,
							     base: extend(reqTailReg << 2),
							     burstLen: 16,
							     len: fetchCount << 2
							     });
	 reqState <= MessageHeaderRequested;
      endrule

      FIFOF#(Vector#(2,Maybe#(Bit#(32)))) readFifo <- mkFIFOF();
      rule demuxwords;
	 let data <- toGet(readEngine.dataPipes[0]).get();
	 Vector#(2,Bit#(32)) dvec = unpack(data);
	 function Maybe#(a) toValid(a in); return tagged Valid in; endfunction
	 Vector#(2,Maybe#(Bit#(32))) mvec = map(toValid, dvec);
	 let demuxCount = demuxCountReg - 2;
	 if (demuxCountReg == 1) begin
	    mvec[1] = tagged Invalid;
	    demuxCount = 0;
	 end
	 demuxCountReg <= demuxCount;
	 readFifo.enq(mvec);
      endrule

      PipeOut#(Vector#(2,Maybe#(Bit#(32)))) readPipe2 = toPipeOut(readFifo);
      PipeOut#(Maybe#(Bit#(32))) readPipe <- mkFunnel1(readPipe2);

      rule receiveMessageHeader if (reqState == MessageHeaderRequested);
	 let maybehdr <- toGet(readPipe).get();
	 if (maybehdr matches tagged Valid .hdr) begin
	     let methodId = hdr[31:16];
	     let messageWords = hdr[15:0];
	     methodIdReg <= methodId;
	     $display("receiveMessageHeader hdr=%x methodId=%x messageWords=%d wordCount=%d", hdr, methodId, messageWords, wordCountReg);
	     wordCountReg <= wordCountReg - 1;
	     messageWordsReg <= messageWords - 1;
	     if (wordCountReg <= 1)
		reqState <= UpdateTail;
	     else if (messageWords <= 1)
		reqState <= MessageHeaderRequested;
	     else
		reqState <= MessageRequested;
	 end
	 else begin
	    $display("receiveMessageHeader tagged invalid %x", maybehdr);
	 end
      endrule

      //GearBox(Bit#(64),Bit#(32)) gb <- mkNto1Gearbox(defaultClock,defaultReset,defaultClock,defaultReset);
      rule receiveMessage if (reqState == MessageRequested);
	 let maybedata <- toGet(readPipe).get();
	 if (maybedata matches tagged Valid .data) begin
	     $display("receiveMessage data=%x messageWords=%d wordCount=%d", data, messageWordsReg, wordCountReg);
	     portal.requests[methodIdReg].enq(data);

	     messageWordsReg <= messageWordsReg - 1;
	     wordCountReg <= wordCountReg - 1;
	     if (messageWordsReg <= 1)
		reqState <= MessageHeaderRequested;
	     else if (wordCountReg <= 1)
		reqState <= UpdateTail;
	 end
	 else begin
	    $display("receiveMessage tagged Invalid %x", maybedata);
	 end
      endrule

      rule updateTail if (reqState == UpdateTail);
	 $display("updateTail: tail=%d", reqTailReg);
	 // update the tail pointer
	 writeEngine.writeServers[0].request.put(MemengineCmd {sglId: sglIdReg,
							       base: 8,
							       len: 8,
							       burstLen: 8
							       });
	 writeEngine.dataPipes[0].enq(extend(reqTailReg));
	 reqState <= Waiting;
      endrule
      rule waiting if (reqState == Waiting);
	 let done <- writeEngine.writeServers[0].response.get();
	 $display("done waiting for write");
	 reqState <= Idle;
      endrule

      rule consumeResponse;
	 let response <- readEngine.readServers[0].response.get();
      endrule

   end
   if (valueOf(numIndications) > 0) begin
   end
   interface SharedMemoryPortalConfig cfg;
      method Action setSglId(Bit#(32) id);
	 $display("setSglId id=%d", id);
	 sglIdReg <= id;
	 readyReg <= True;
      endmethod
   endinterface
   interface MemReadClient  readClient = readEngine.dmaClient;
   interface MemWriteClient writeClient = writeEngine.dmaClient;
   interface ReadOnly interrupt;
   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 FIFO::*;
import FIFOF::*;
import GetPut::*;
import ClientServer::*;
import CtrlMux::*;

import Pipe::*;
import Portal::*;
import MemTypes::*;
import AddressGenerator::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import ConnectalMemory::*;

typedef struct {
    Bit#(1) select;
    Bit#(6) tag;
} ReadReqInfo deriving (Bits);

interface PortalCtrlMemSlave#(numeric type addrWidth, numeric type dataWidth);
   interface PhysMemSlave#(addrWidth, dataWidth) memSlave;
   interface ReadOnly#(Bool) interrupt;
   interface WriteOnly#(Bool) top;
endinterface

module mkPortalCtrlMemSlave#(Bit#(dataWidth) ifcId,
			     Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indicationPipes)(PortalCtrlMemSlave#(addrWidth, dataWidth))
   provisos( Add#(a__, 1, dataWidth)
	    ,Div#(dataWidth, 8, b__)
	    ,Bits#(MemData#(dataWidth), c__)
	    ,Add#(d__, dataWidth, TMul#(dataWidth, 2))
	    );

   AddressGenerator#(addrWidth,dataWidth) ctrlReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth,dataWidth) ctrlWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(MemData#(dataWidth))        ctrlWriteDataFifo <- mkFIFO();
   FIFO#(Bit#(MemTagSize))        ctrlWriteDoneFifo <- mkFIFO();
   Reg#(Bool) topR <- mkReg(True);

   function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
   Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, indicationPipes);

   Reg#(Bool) interruptEnableReg <- mkReg(False);
   Bool      interruptStatus = False;
   Reg#(Bit#(TMul#(dataWidth,2))) cycle_count <- mkReg(0);
   Reg#(Bit#(dataWidth))    snapshot <- mkReg(0);

   Bit#(dataWidth)  readyChannel = -1;
   for (Integer i = 0; i < valueOf(numIndications); i = i + 1) begin
      if (readyBits[i]) begin
         interruptStatus = True;
         readyChannel = fromInteger(i);
      end
   end

   rule count;
      cycle_count <= cycle_count+1;
   endrule

   rule writeDataRule;
      let d <- toGet(ctrlWriteDataFifo).get();
      let b <- ctrlWriteAddrGenerator.addrBeat.get();
      //$display("mkCtrl.writeData addr=%h data=%h last=%d", b.addr, d.data, b.last);
      let v = d.data;
      let addr = b.addr;
      if (addr == 'h000)
	 noAction;
      if (addr == 'h004)
	 interruptEnableReg <= v[0] == 1'd1;
      if (b.last)
	 ctrlWriteDoneFifo.enq(b.tag);
   endrule

   interface PhysMemSlave memSlave;
      interface PhysMemReadServer read_server;
	 interface Put readReq = ctrlReadAddrGenerator.request;
	 interface Get readData;
	    method ActionValue#(MemData#(dataWidth)) get();
	       let b <- ctrlReadAddrGenerator.addrBeat.get();
	       let addr = b.addr;
	       let v = 'h05a05a0;
	       if (addr == 'h000)
		  v = interruptStatus ? 1 : 0;
	       if (addr == 'h004)
		  v = interruptEnableReg ? 1 : 0;
	       if (addr == 'h008)
		  v = 7;
               if (addr == 'h00C) begin
		  if (interruptStatus)
		     v = readyChannel+1;
		  else
		     v = 0;
               end
	       if (addr == 'h010)
		  v = ifcId;
	       if (addr == 'h014)
		  v = extend(pack(topR));
	       if (addr == 'h018) begin
		  snapshot <= truncate(cycle_count);
		  v = truncate(cycle_count>>valueOf(dataWidth));
	       end
	       if (addr == 'h01C)
		  v = snapshot;
	       //$display("mkCtrl.readData addr=%h data=%h", b.addr, v);
	       return MemData { data: v, tag: b.tag, last: b.last };
	    endmethod
	 endinterface
      endinterface: read_server
      interface PhysMemWriteServer write_server;
	 interface Put writeReq = ctrlWriteAddrGenerator.request;
	 interface Put writeData;
	    method Action put(MemData#(dataWidth) d);
	       ctrlWriteDataFifo.enq(d);
	    endmethod
	 endinterface
	 interface Get writeDone;
	    method ActionValue#(Bit#(MemTagSize)) get();
	       let tag <- toGet(ctrlWriteDoneFifo).get();
	       return tag;
	    endmethod
	 endinterface
      endinterface: write_server
   endinterface: memSlave
   interface ReadOnly interrupt;
      method Bool _read();
	 return interruptStatus && interruptEnableReg;
      endmethod
   endinterface
   interface WriteOnly top;
      method Action _write(Bool v);
	 topR <= v;
      endmethod
   endinterface
endmodule

module mkPipeInMemSlave#(PipeIn#(Bit#(dataWidth)) methodPipe)(PhysMemSlave#(addrWidth, dataWidth))
   provisos (Add#(1,a__,dataWidth));

   AddressGenerator#(addrWidth,dataWidth) fifoReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))        fifoWriteDoneFifo <- mkFIFO();

   interface PhysMemReadServer read_server;
      interface Put readReq = fifoReadAddrGenerator.request;
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let b <- fifoReadAddrGenerator.addrBeat.get();
	    let v = 0;
	    if (b.addr[7:0] == 4)
	       v = extend(pack(methodPipe.notFull()));
	    return MemData { data: v, tag: b.tag, last: b.last };
	 endmethod
      endinterface
   endinterface
   interface PhysMemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeInMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	    methodPipe.enq(d.data);
	    // this used to be where we triggered putFailed
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkPipeOutMemSlave#(PipeOut#(Bit#(dataWidth)) methodPipe)(PhysMemSlave#(addrWidth, dataWidth))
   provisos (Add#(1,a__,dataWidth));
   AddressGenerator#(addrWidth,dataWidth) fifoReadAddrGenerator <- mkAddressGenerator();
   AddressGenerator#(addrWidth,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))                  fifoWriteDoneFifo <- mkFIFO();
   FIFO#(MemData#(dataWidth))                   fifoReadDataFifo <- mkFIFO();
   rule readDataRule;
      let b <- fifoReadAddrGenerator.addrBeat.get();
      let v = 0;
      if (b.addr[7:0] == 0)
	 v <- toGet(methodPipe).get();
      else if (b.addr[7:0] == 4)
	 v = extend(pack(methodPipe.notEmpty()));
      //$display("mkPipeOutMemSlave.readData.get addr=%h data=%h", b.addr, data);
      fifoReadDataFifo.enq(MemData { data: v, tag: b.tag, last: b.last });
   endrule

   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth) req);
	    fifoReadAddrGenerator.request.put(req);
	    if (!methodPipe.notEmpty())
	       $display("***\n\n mkPipeOutMemSlave.read_server.underflow! \n\n****");
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let d <- toGet(fifoReadDataFifo).get();
	    return d;
	 endmethod
      endinterface
   endinterface
   interface PhysMemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeOutMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkMemPortal#(Bit#(slaveDataWidth) ifcId,
		    PipePortal#(numRequests, numIndications, slaveDataWidth) portal)(MemPortal#(slaveAddrWidth, slaveDataWidth))
   provisos ( Add#(1, i__, slaveDataWidth)
	     ,Add#(c__, 8, slaveAddrWidth)
	     ,Add#(d__, 1, c__)
	     ,Add#(a__, TLog#(TAdd#(1, TAdd#(numRequests, numIndications))), c__)
	     ,Add#(b__, slaveDataWidth, TMul#(slaveDataWidth, 2))
	     );

   Vector#(numRequests,    PipeIn#(Bit#(slaveDataWidth)))     requestPipes = portal.requests;
   Vector#(numIndications, PipeOut#(Bit#(slaveDataWidth))) indicationPipes = portal.indications;
   Vector#(numRequests,    PhysMemSlave#(8, slaveDataWidth))    requestMemSlaves <- mapM(mkPipeInMemSlave, requestPipes);
   Vector#(numIndications, PhysMemSlave#(8, slaveDataWidth)) indicationMemSlaves <- mapM(mkPipeOutMemSlave, indicationPipes);

   PortalCtrlMemSlave#(8,slaveDataWidth) ctrlPort <- mkPortalCtrlMemSlave(ifcId, indicationPipes);
   PhysMemSlave#(slaveAddrWidth,slaveDataWidth) memslave  <- mkMemSlaveMux(cons(ctrlPort.memSlave,append(requestMemSlaves, indicationMemSlaves)));
   interface PhysMemSlave slave = memslave;
   interface ReadOnly interrupt = ctrlPort.interrupt;
   interface WriteOnly top = ctrlPort.top;
endmodule

typedef enum {
   Idle,
   HeadRequested,
   TailRequested,
   RequestMessage,
   MessageHeaderRequested,
   MessageRequested,
   UpdateTail,
   Waiting,
   Stop
   } SharedMemoryPortalState deriving (Bits,Eq);

module mkSharedMemoryPortal#(PipePortal#(numRequests, numIndications, 32) portal)(SharedMemoryPortal#(64));

   MemreadEngineV#(64,2,2) readEngine <- mkMemreadEngine();
   MemwriteEngineV#(64,2,2) writeEngine <- mkMemwriteEngine();

   Reg#(Bit#(32)) sglIdReg <- mkReg(0);
   Reg#(Bool)     readyReg   <- mkReg(False);

   if (valueOf(numRequests) > 0) begin
      // read the head and tail pointers, if they are different, then read a request
      Reg#(Bit#(32)) limitReg <- mkReg(0);
      Reg#(Bit#(32)) reqHeadReg <- mkReg(0);
      Reg#(Bit#(32)) reqTailReg <- mkReg(0);
      Reg#(Bit#(16)) wordCountReg <- mkReg(0);
      Reg#(Bit#(16)) methodIdReg <- mkReg(0);
      Reg#(SharedMemoryPortalState) reqState <- mkReg(Idle);

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

      rule receiveReqHead if (reqState == HeadRequested || reqState == TailRequested);
	 let data <- toGet(readEngine.dataPipes[0]).get();
	 let w0 = data[31:0];
	 let w1 = data[63:32];
	 let head = reqHeadReg;
	 let tail = reqTailReg;
	 $display("receiveReqHead state=%d w0=%x w1=%x", reqState, w0, w1);
	 if (reqState == HeadRequested) begin
	    reqHeadReg <= w1;
	    reqState <= TailRequested;
	 end
	 else begin
	    tail = w0;
	    if (reqTailReg == 0)
	       reqTailReg <= tail;
	     if (tail != reqHeadReg)
		reqState <= RequestMessage;
	     else
		reqState <= Idle;
	 end
      endrule

      rule requestMessage if (reqState == RequestMessage);
	 Bit#(32) wordCount = reqHeadReg - reqTailReg;
	 $display("requestMessage id=%d tail=%h wordCount=%d", sglIdReg, reqTailReg, wordCount);
	 reqTailReg <= reqTailReg + wordCount;
	 readEngine.readServers[0].request.put(MemengineCmd {sglId: sglIdReg,
							     base: extend(reqTailReg << 2),
							     burstLen: 16,
							     len: wordCount << 2
							     });
	 reqState <= MessageHeaderRequested;
      endrule

      rule receiveMessageHeader if (reqState == MessageHeaderRequested);
	 let data <- toGet(readEngine.dataPipes[0]).get();
	 let hdr = data[31:0];
	 let methodId = hdr[31:16];
	 let wordCount = hdr[15:0];
	 methodIdReg <= methodId;
	 // FIXME: multiword requests
	 let payload0 = data[63:32];
	 $display("receiveMessageHeader data=%x methodId=%x wordCount=%x payload0=%x", data, methodId, wordCount, payload0);
	 portal.requests[methodId].enq(payload0);
	 if (wordCount <= 2)
	    reqState <= UpdateTail;
	 else
	    reqState <= MessageRequested;
      endrule

      //GearBox(Bit#(64),Bit#(32)) gb <- mkNto1Gearbox(defaultClock,defaultReset,defaultClock,defaultReset);
      rule receiveMessage if (reqState == MessageRequested);
	 let data <- toGet(readEngine.dataPipes[0]).get();
	 $display("receiveMessage data=%x wordCount=%d", data, wordCountReg);
	 // FIXME: multiword requests
	 //portal.requests[methodIdReg].enq(truncate(data));
	 let wordCount = wordCountReg - 2;
	 if (wordCountReg <= 2) // fetches two words per cycle
	    reqState <= UpdateTail;
	 wordCountReg <= wordCount;
      endrule

      rule updateTail if (reqState == UpdateTail);
	 $display("updateTail: tail=%d", reqTailReg);
	 // update the tail pointer
	 writeEngine.writeServers[0].request.put(MemengineCmd {sglId: sglIdReg,
							       base: 8,
							       len: 8,
							       burstLen: 8
							       });
	 writeEngine.dataPipes[0].enq(extend(reqTailReg));
	 reqState <= Waiting;
      endrule
      rule waiting if (reqState == Waiting);
	 let done <- writeEngine.writeServers[0].response.get();
	 $display("done waiting for write");
	 reqState <= Idle;
      endrule

      rule consumeResponse;
	 let response <- readEngine.readServers[0].response.get();
      endrule

   end
   if (valueOf(numIndications) > 0) begin
   end
   interface SharedMemoryPortalConfig cfg;
      method Action setSglId(Bit#(32) id);
	 $display("setSglId id=%d", id);
	 sglIdReg <= id;
	 readyReg <= True;
      endmethod
   endinterface
   interface MemReadClient  readClient = readEngine.dmaClient;
   interface MemWriteClient writeClient = writeEngine.dmaClient;
   interface ReadOnly interrupt;
   endinterface
endmodule