added channel number to verbose display in mkMemReadEngine

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

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

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

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import BuildVector::*;
import Cntrs::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;
import ConnectalMemory::*;
import ConnectalBramFifo::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;
import ConnectalConfig::*;

`include "ConnectalProjectConfig.bsv"

typedef 2 MemReadFunnelBPC;

module mkMemReadEngine(MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(busWidth, 8), 8, busWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(busWidth, 0, userWidth)
//	     ,Min#(MemReadFunnelBPC, TLog#(numServers), bpc)
	     ,Add#(0,MemReadFunnelBPC,bpc)
	    ,FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), bpc)
	    ,Pipe::FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, bpc)
	    ,Add#(b__, TLog#(numServers), MemTagSize)
      	    );
   let rv <- mkMemReadEngineBuff(valueOf(cmdQDepth) * valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

typedef struct {
    Bit#(BurstLenSize) len;
    Bool               last;
} NextReq deriving (Bits, Eq);

function NextReq getNext(Bit#(32) len, Bit#(BurstLenSize) burst);
   NextReq v;
   v.last = (len <= extend(burst));
   v.len = v.last ? truncate(len) : burst;
   return v;
endfunction

interface MemReadChannel#(numeric type busWidth, numeric type userWidth, numeric type cmdQDepth);
   interface PipeOut#(MemRequest)        readReq;
   interface PipeIn#(MemData#(busWidth)) readData;
   interface MemReadEngineServer#(userWidth) readServer;
endinterface

module mkMemReadChannel#(Integer bufferSizeBytes, Integer channelNumber, PipeOut#(MemData#(userWidth)) dataPipe)
   (MemReadChannel#(busWidth, userWidth, cmdQDepth))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Log#(busWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Add#(busWidth, 0, userWidth),
	     Add#(1,logCmdQDepth, outCntSz));
   let verbose = False;
   let beatShift = fromInteger(valueOf(beatShift));

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);
   Reg#(Bool)          clientInFlight <- mkReg(False);
   ConfigCounter#(16)  clientAvail <- mkConfigCounter(fromInteger(bufferSizeBeats));
   Reg#(MemengineCmd)  clientCommand <- mkReg(unpack(0));
`ifdef USE_DUAL_CLOCK_FIFOF
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
`else
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkSizedBRAMFIFOF(bufferSizeBeats);
`endif
   FIFO#(MemengineCmd) clientRequest <- mkFIFO();
   Reg#(Bit#(32))      clientLen <- mkReg(unpack(0));
   Reg#(Bit#(32))      clientBase <- mkReg(unpack(0));
   Reg#(NextReq)       clientNext <- mkReg(unpack(0));
   Count#(Bit#(32))    clientCycles <- mkCount(0);
   FIFOF#(MemRequestCycles) clientCyclesFifo <- mkFIFOF();

   FIFO#(Tuple3#(Bool,Bool,Bit#(BurstLenSize))) serverCheckAvail <- mkSizedFIFO(1);
   FIFOF#(MemRequest)                          dmaRequest <- mkSizedFIFOF(valueOf(cmdQDepth));
   FIFO#(Tuple3#(Bit#(8),Bit#(MemTagSize),Bool)) serverProcessing <- mkSizedFIFO(valueOf(cmdQDepth));
   FIFOF#(MemData#(busWidth))                       serverDataFifo <- mkFIFOF;

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(32)) counter <- mkReg(0);

   rule incCounter;
      counter <= counter + 1;
   endrule

   rule rule_cycles;
      clientCycles.incr(1);
   endrule
   rule rule_startNew if (!clientInFlight);
      let cmd <- toGet(clientRequest).get();
      clientInFlight <= True;
      clientCommand <= cmd;
      clientLen <= cmd.len - extend(cmd.burstLen);
      clientBase <= cmd.base;
      clientNext <= getNext(cmd.len, cmd.burstLen);
      clientCycles <= 0;
      if (verbose) $display("mkMemReadEngineBuff::%d rule_startNew %d", counter, clientAvail.read);
   endrule
   rule rule_checkAvail if (clientInFlight);
      let cmd_len = clientNext.len;
      let last_burst = clientNext.last;
      let cond0 <- clientAvail.maybeDecrement(unpack(extend(cmd_len>>beatShift)));
      serverCheckAvail.enq(tuple3(cond0,last_burst,cmd_len));
      if (verbose) $display("mkMemReadEngineBuff::%d chan=%d rule_checkAvail avail %d burstLen %d cond0 %d last_burst %d", counter, channelNumber, clientAvail.read(), cmd_len>>beatShift, cond0, last_burst);
   endrule

   rule rule_requestServer if (clientInFlight);
      match {.cond0,.last_burst,.cmd_len} <- toGet(serverCheckAvail).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemReadEngineBuff::%d chan=%d rule_requestServer clientLen %d cond0 %d last_burst %d",
			       counter, channelNumber, clientLen, cond0, last_burst);
	 serverProcessing.enq(tuple3(truncate(cmd_len>>beatShift), clientCommand.tag, last_burst));
	 if (verbose) $display("MemReadEngine::%d chan=%d readReq idx %d offset %h burstLenBytes %h last %d",
	    counter, channelNumber, 0, clientBase, cmd_len, last_burst);

	 dmaRequest.enq(MemRequest { sglId: clientCommand.sglId, offset: extend(clientBase),
	    burstLen:cmd_len, tag: fromInteger(channelNumber)
	    `ifdef BYTE_ENABLES
				    , firstbe: maxBound, lastbe: maxBound
	    `endif
	    });
         clientBase <= clientBase + extend(cmd_len);
         clientLen <= clientLen - extend(cmd_len);
         clientNext <= getNext(clientLen, clientCommand.burstLen);
	 if (last_burst) begin
	    if (verbose) $display("mkMemReadEngineBuff::%d chan=%d rule_requestServer last_burst %d", counter, channelNumber, last_burst);
	    clientInFlight <= False;
	    `ifdef MEMENGINE_REQUEST_CYCLES
	    $display("clientCycles = %d", clientCycles);
	    clientCyclesFifo.enq(MemRequestCycles { tag:clientCommand.tag, cycles: clientCycles });
	    `endif
	 end
      end
   endrule

   rule read_data_rule;
      let d <- toGet(dataPipe).get();
      match {.rc, .tag, .last_burst} = serverProcessing.first;
      let new_respCnt = respCnt+1;
      let l = False;
      if (verbose) $display("mkMemReadEngineBuff::%d chan=%d data %h new_respCnt %d rc %d last_burst %d tag %d clientInFlight %d eob %d",
	 counter, channelNumber, d.data, new_respCnt, rc, last_burst, tag, clientInFlight, d.last);
      if (new_respCnt == rc) begin
	 respCnt <= 0;
	 serverProcessing.deq;
	 l = last_burst;
      end
      else begin
	 respCnt <= new_respCnt;
      end
      clientDataFifo.enq(MemDataF { data: d.data, tag: d.tag, first: (respCnt == 0), last: l});
   endrule

   MemReadEngineServer#(userWidth) rs = (interface MemReadEngineServer#(userWidth);
		  interface Put request;
		     method Action put(MemengineCmd cmd);
`ifdef SIMULATION
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
			Bit#(32) dw = fromInteger(valueOf(busWidthBytes));
			let mdw = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported len %d", cmd.len);
			end
			else
`endif
                           begin
			   clientRequest.enq(cmd);
			   end
 		     endmethod
		  endinterface
                  interface data = interface PipeOut;
	             method MemDataF#(userWidth) first;
	                return clientDataFifo.first;
	             endmethod
	             method Action deq;
	                if (verbose) $display("mkMemReadEngineBuff::check_out: chan=%d data %h clientAvail %d eob %d",
					      channelNumber, clientDataFifo.first.data, clientAvail.read(), clientDataFifo.first.last);
	                clientDataFifo.deq;
	                clientAvail.increment(1);
	             endmethod
	             method Bool notEmpty = clientDataFifo.notEmpty;
                  endinterface;
	       interface requestCycles = toPipeOut(clientCyclesFifo);
               endinterface);
   interface readServer = rs;
   interface PipeOut readReq = toPipeOut(dmaRequest);
endmodule

module mkMemReadEngineBuff#(Integer bufferSizeBytes) (MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Add#(busWidth, 0, userWidth),
//	     Min#(MemReadFunnelBPC, TLog#(numServers), bpc),
	     Add#(0,MemReadFunnelBPC,bpc),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), bpc),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, bpc),
	     Add#(a__, TLog#(numServers), MemTagSize)
      );
   let verbose = False;

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);

   FIFOF#(MemData#(busWidth)) readDataFifo <- mkFIFOF();
   function Tuple2#(Bit#(TLog#(numServers)),MemData#(userWidth)) tagData(MemData#(userWidth) md);
      return tuple2(truncate(md.tag), md);
   endfunction
   UnFunnelPipe#(1,numServers,MemData#(userWidth),bpc) dataPipes <- mkUnFunnelPipesPipelined(vec(mapPipe(tagData, toPipeOut(readDataFifo))));

   Vector#(numServers, MemReadChannel#(busWidth,userWidth,cmdQDepth)) readChannels <- zipWithM(mkMemReadChannel(bufferSizeBytes),
											       genVector(), dataPipes);
   Vector#(numServers, FIFOF#(Bit#(MemTagSize))) readtagFifos <- replicateM(mkSizedFIFOF(valueOf(cmdQDepth)));
   function PipeOut#(MemRequest) readChannelDmaReadReq(Integer i);
      return readChannels[i].readReq;
   endfunction
   function MemReadEngineServer#(userWidth) readChannelServer(Integer i);
      return readChannels[i].readServer;
   endfunction

   FIFOF#(MemRequest) reqFifo <- mkFIFOF();
   FunnelPipe#(1,numServers,MemRequest,bpc) reqFunnel <- mkFunnelPipesPipelined(genWith(readChannelDmaReadReq));

   interface Vector  readServers = genWith(readChannelServer);
   interface MemReadClient dmaClient;
      interface Get    readReq = toGet(reqFunnel[0]);
      interface Put   readData = toPut(readDataFifo);
   endinterface

endmodule

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

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

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

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import BuildVector::*;
import Cntrs::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;
import ConnectalMemory::*;
import ConnectalBramFifo::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;
import ConnectalConfig::*;

`include "ConnectalProjectConfig.bsv"

typedef 2 MemReadFunnelBPC;

module mkMemReadEngine(MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(busWidth, 8), 8, busWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(busWidth, 0, userWidth)
//	     ,Min#(MemReadFunnelBPC, TLog#(numServers), bpc)
	     ,Add#(0,MemReadFunnelBPC,bpc)
	    ,FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), bpc)
	    ,Pipe::FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, bpc)
	    ,Add#(b__, TLog#(numServers), MemTagSize)
      	    );
   let rv <- mkMemReadEngineBuff(valueOf(cmdQDepth) * valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

typedef struct {
    Bit#(BurstLenSize) len;
    Bool               last;
} NextReq deriving (Bits, Eq);

function NextReq getNext(Bit#(32) len, Bit#(BurstLenSize) burst);
   NextReq v;
   v.last = (len <= extend(burst));
   v.len = v.last ? truncate(len) : burst;
   return v;
endfunction

interface MemReadChannel#(numeric type busWidth, numeric type userWidth, numeric type cmdQDepth);
   interface PipeOut#(MemRequest)        readReq;
   interface PipeIn#(MemData#(busWidth)) readData;
   interface MemReadEngineServer#(userWidth) readServer;
endinterface

module mkMemReadChannel#(Integer bufferSizeBytes, Integer channelNumber, PipeOut#(MemData#(userWidth)) dataPipe)
   (MemReadChannel#(busWidth, userWidth, cmdQDepth))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Log#(busWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Add#(busWidth, 0, userWidth),
	     Add#(1,logCmdQDepth, outCntSz));
   let verbose = False;
   let beatShift = fromInteger(valueOf(beatShift));

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);
   Reg#(Bool)          clientInFlight <- mkReg(False);
   ConfigCounter#(16)  clientAvail <- mkConfigCounter(fromInteger(bufferSizeBeats));
   Reg#(MemengineCmd)  clientCommand <- mkReg(unpack(0));
`ifdef USE_DUAL_CLOCK_FIFOF
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
`else
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkSizedBRAMFIFOF(bufferSizeBeats);
`endif
   FIFO#(MemengineCmd) clientRequest <- mkFIFO();
   Reg#(Bit#(32))      clientLen <- mkReg(unpack(0));
   Reg#(Bit#(32))      clientBase <- mkReg(unpack(0));
   Reg#(NextReq)       clientNext <- mkReg(unpack(0));
   Count#(Bit#(32))    clientCycles <- mkCount(0);
   FIFOF#(MemRequestCycles) clientCyclesFifo <- mkFIFOF();

   FIFO#(Tuple3#(Bool,Bool,Bit#(BurstLenSize))) serverCheckAvail <- mkSizedFIFO(1);
   FIFOF#(MemRequest)                          dmaRequest <- mkSizedFIFOF(valueOf(cmdQDepth));
   FIFO#(Tuple3#(Bit#(8),Bit#(MemTagSize),Bool)) serverProcessing <- mkSizedFIFO(valueOf(cmdQDepth));
   FIFOF#(MemData#(busWidth))                       serverDataFifo <- mkFIFOF;

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(32)) counter <- mkReg(0);

   rule incCounter;
      counter <= counter + 1;
   endrule

   rule rule_cycles;
      clientCycles.incr(1);
   endrule
   rule rule_startNew if (!clientInFlight);
      let cmd <- toGet(clientRequest).get();
      clientInFlight <= True;
      clientCommand <= cmd;
      clientLen <= cmd.len - extend(cmd.burstLen);
      clientBase <= cmd.base;
      clientNext <= getNext(cmd.len, cmd.burstLen);
      clientCycles <= 0;
      if (verbose) $display("mkMemReadEngineBuff::%d rule_startNew %d", counter, clientAvail.read);
   endrule
   rule rule_checkAvail if (clientInFlight);
      let cmd_len = clientNext.len;
      let last_burst = clientNext.last;
      let cond0 <- clientAvail.maybeDecrement(unpack(extend(cmd_len>>beatShift)));
      serverCheckAvail.enq(tuple3(cond0,last_burst,cmd_len));
      if (verbose) $display("mkMemReadEngineBuff::%d rule_checkAvail avail %d burstLen %d cond0 %d last_burst %d", counter, clientAvail.read(), cmd_len>>beatShift, cond0, last_burst);
   endrule

   rule rule_requestServer if (clientInFlight);
      match {.cond0,.last_burst,.cmd_len} <- toGet(serverCheckAvail).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemReadEngineBuff::%d rule_requestServer clientLen %d cond0 %d last_burst %d", counter, clientLen, cond0, last_burst);
	 serverProcessing.enq(tuple3(truncate(cmd_len>>beatShift), clientCommand.tag, last_burst));
	 if (verbose) $display("MemReadEngine::%d readReq idx %d offset %h burstLenBytes %h last %d", counter, 0, clientBase, cmd_len, last_burst);

	 dmaRequest.enq(MemRequest { sglId: clientCommand.sglId, offset: extend(clientBase),
	    burstLen:cmd_len, tag: fromInteger(channelNumber)
	    `ifdef BYTE_ENABLES
				    , firstbe: maxBound, lastbe: maxBound
	    `endif
	    });
         clientBase <= clientBase + extend(cmd_len);
         clientLen <= clientLen - extend(cmd_len);
         clientNext <= getNext(clientLen, clientCommand.burstLen);
	 if (last_burst) begin
	    if (verbose) $display("mkMemReadEngineBuff::%d rule_requestServer last_burst %d", counter, last_burst);
	    clientInFlight <= False;
	    `ifdef MEMENGINE_REQUEST_CYCLES
	    $display("clientCycles = %d", clientCycles);
	    clientCyclesFifo.enq(MemRequestCycles { tag:clientCommand.tag, cycles: clientCycles });
	    `endif
	 end
      end
   endrule

   rule read_data_rule;
      let d <- toGet(dataPipe).get();
      match {.rc, .tag, .last_burst} = serverProcessing.first;
      let new_respCnt = respCnt+1;
      let l = False;
      if (verbose) $display("mkMemReadEngineBuff::%d data %h new_respCnt %d rc %d last_burst %d tag %d clientInFlight %d eob %d", counter, d.data, new_respCnt, rc, last_burst, tag, clientInFlight, d.last);
      if (new_respCnt == rc) begin
	 respCnt <= 0;
	 serverProcessing.deq;
	 l = last_burst;
      end
      else begin
	 respCnt <= new_respCnt;
      end
      clientDataFifo.enq(MemDataF { data: d.data, tag: d.tag, first: (respCnt == 0), last: l});
   endrule

   MemReadEngineServer#(userWidth) rs = (interface MemReadEngineServer#(userWidth);
		  interface Put request;
		     method Action put(MemengineCmd cmd);
`ifdef SIMULATION
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
			Bit#(32) dw = fromInteger(valueOf(busWidthBytes));
			let mdw = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported len %d", cmd.len);
			end
			else
`endif
                           begin
			   clientRequest.enq(cmd);
			   end
 		     endmethod
		  endinterface
                  interface data = interface PipeOut;
	             method MemDataF#(userWidth) first;
	                return clientDataFifo.first;
	             endmethod
	             method Action deq;
	                if (verbose) $display("mkMemReadEngineBuff::check_out: data %h clientAvail %d eob %d", clientDataFifo.first.data, clientAvail.read(), clientDataFifo.first.last);
	                clientDataFifo.deq;
	                clientAvail.increment(1);
	             endmethod
	             method Bool notEmpty = clientDataFifo.notEmpty;
                  endinterface;
	       interface requestCycles = toPipeOut(clientCyclesFifo);
               endinterface);
   interface readServer = rs;
   interface PipeOut readReq = toPipeOut(dmaRequest);
endmodule

module mkMemReadEngineBuff#(Integer bufferSizeBytes) (MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Add#(busWidth, 0, userWidth),
//	     Min#(MemReadFunnelBPC, TLog#(numServers), bpc),
	     Add#(0,MemReadFunnelBPC,bpc),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), bpc),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, bpc),
	     Add#(a__, TLog#(numServers), MemTagSize)
      );
   let verbose = False;

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);

   FIFOF#(MemData#(busWidth)) readDataFifo <- mkFIFOF();
   function Tuple2#(Bit#(TLog#(numServers)),MemData#(userWidth)) tagData(MemData#(userWidth) md);
      return tuple2(truncate(md.tag), md);
   endfunction
   UnFunnelPipe#(1,numServers,MemData#(userWidth),bpc) dataPipes <- mkUnFunnelPipesPipelined(vec(mapPipe(tagData, toPipeOut(readDataFifo))));

   Vector#(numServers, MemReadChannel#(busWidth,userWidth,cmdQDepth)) readChannels <- zipWithM(mkMemReadChannel(bufferSizeBytes),
											       genVector(), dataPipes);
   Vector#(numServers, FIFOF#(Bit#(MemTagSize))) readtagFifos <- replicateM(mkSizedFIFOF(valueOf(cmdQDepth)));
   function PipeOut#(MemRequest) readChannelDmaReadReq(Integer i);
      return readChannels[i].readReq;
   endfunction
   function MemReadEngineServer#(userWidth) readChannelServer(Integer i);
      return readChannels[i].readServer;
   endfunction

   FIFOF#(MemRequest) reqFifo <- mkFIFOF();
   FunnelPipe#(1,numServers,MemRequest,bpc) reqFunnel <- mkFunnelPipesPipelined(genWith(readChannelDmaReadReq));

   interface Vector  readServers = genWith(readChannelServer);
   interface MemReadClient dmaClient;
      interface Get    readReq = toGet(reqFunnel[0]);
      interface Put   readData = toPut(readDataFifo);
   endinterface

endmodule


add getPhase for testing.  It will be a couple of cycles earlier than the output

// 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 FIFOF::*;
import Pipe::*;
import Vector::*;

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

interface DDS;
   method Action setPhaseAdvance(PhaseType v);
   method PhaseType getPhase();
   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);
   FIFOF#(DDSOutType) ddsout <- mkFIFOF();
   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;
      Bit#(10) addr;
      phase <= phase + phaseAdvance;
      addr = phase.i;
      ram.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: ?});
      //$display("dds addr %x\n", addr);
   endrule

   rule ddsoutrule;
      let v <- ram.portA.response.get();
      //$display("ddsout ph %d" , phase);
	// $display(fshow(v));
      ddsout.enq(v);
   endrule

   method Action setPhaseAdvance(PhaseType v);
      phaseAdvance <= v;
   endmethod

   method PhaseType getPhase();
      return(phase);
   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::*;
import FIFOF::*;
import Pipe::*;
import Vector::*;

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

interface DDS;
   method Action setPhaseAdvance(PhaseType 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);
   FIFOF#(DDSOutType) ddsout <- mkFIFOF();
   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;
      Bit#(10) addr;
      phase <= phase + phaseAdvance;
      addr = phase.i;
      ram.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: addr, datain: ?});
      //$display("dds addr %x\n", addr);
   endrule

   rule ddsoutrule;
      let v <- ram.portA.response.get();
      //$display("ddsout ph %d" , phase);
	// $display(fshow(v));
      ddsout.enq(v);
   endrule

   method Action setPhaseAdvance(PhaseType v);
      phaseAdvance <= v;
   endmethod

   interface PipeOut osc = toPipeOut(ddsout);

endmodule


Updated address hashing so we don't exceed flash model boundaries

import FShow::*;

//**********************************************************
// Type definitions of the flash controller and submodules
//**********************************************************

//NAND geometry
//Actual page size is 8640B, but we don't need the last 40B for ECC
//Valid Data: 2 x (243B x 16 + 208B) = 8192B;
//With ECC: 2 x (255B x 16 + 220B) = 8600
typedef 8600 			PageSize;
typedef 8192 			PageSizeUser;
typedef 17 				PageECCBlks; //16 blocks of k=243; 1 block of k=208
`ifdef BSIM
	typedef 16 			PagesPerBlock;
	typedef 128			BlocksPerCE;
	typedef 8 			ChipsPerBus;
`elsif SLC
	typedef 128 		PagesPerBlock;
	typedef 8192 		BlocksPerCE;
	typedef 4 			ChipsPerBus;
`else //MLC
	typedef 256		 	PagesPerBlock;
	typedef 4096 		BlocksPerCE;
	typedef 8 			ChipsPerBus;
`endif

typedef 2											BusWordBytes;
typedef TMul#(8, BusWordBytes) 				BusWordSz;
typedef 16 											WordBytes;
typedef TLog#(WordBytes)						WordBytesLog;
typedef TMul#(8,WordBytes) 					WordSz;
//Each burst is 128 bits via the controller interface
typedef TDiv#(PageSizeUser, WordBytes) 	PageWords;
typedef TMul#(TMul#(BlocksPerCE, PagesPerBlock), PageWords) WordsPerChip;

typedef WordSz FlashDataWidth;
typedef 44  FlashAddrWidth;

Integer pageSize 			= valueOf(PageSize); //bytes
Integer pageSizeUser 	= valueOf(PageSizeUser); //usable page size is 8k
Integer pageECCBlks 		= valueOf(PageECCBlks); //16 blocks of k=243; 1 block of k=208
Integer pagesPerBlock 	= valueOf(PagesPerBlock);
Integer blocksPerCE 		= valueOf(BlocksPerCE);
Integer chipsPerBus		= valueOf(ChipsPerBus);
Integer wordBytes 		= valueOf(WordBytes);
Integer pageWords 		= valueOf(PageWords);
//Integer blocksPerPlane = 2048;
//Integer planesPerLun = 2;
//Integer lunsPerTarget = 1; //1 for SLC, 2 for MLC

`ifdef NAND_SIM
	typedef 1 NUM_CHIPBUSES;
`else
	typedef 4 NUM_CHIPBUSES;
`endif

typedef 2 BUSES_PER_CHIPBUS;
typedef TMul#(NUM_CHIPBUSES, BUSES_PER_CHIPBUS) NUM_BUSES;

typedef 8 NUM_DEBUG_ILAS;

typedef TMul#(NUM_BUSES, ChipsPerBus) NUM_TOTAL_CHIPS;

typedef 128 NumTags;
//typedef Bit#(TLog#(TDiv#(NumTags, NUM_BUSES))) BusTagT;
typedef Bit#(TLog#(NumTags)) TagT;
typedef Bit#(TLog#(ChipsPerBus)) ChipT;
typedef Bit#(TLog#(NUM_BUSES)) BusT;

Integer num_tags = valueOf(NumTags);
Integer num_buses = valueOf(NUM_BUSES);

typedef enum {
	SRC_HOST,
	SRC_USER_HW
} SourceT deriving (Bits, Eq, FShow);

typedef enum {
	ERASE_ERROR,
   ERASE_DONE,
	WRITE_DONE
} StatusT deriving (Bits, Eq);

//----------------------
//Aurora related types
//----------------------
typedef enum {
	F_CMD,
	F_DATA,
	F_ACK,
	F_WR_REQ
} PacketClass deriving (Bits, Eq);

//----------------------
//Phy related types
//----------------------
typedef enum {
	PHY_CHIP_SEL,
	PHY_DESELECT_ALL,
	PHY_CMD,
	PHY_READ,
	PHY_WRITE,
	PHY_ADDR,
	PHY_SYNC_CALIB,
	PHY_ENABLE_NAND_CLK
} PhyCycle deriving (Bits, Eq);

typedef enum {
	N_RESET = 8'hFF,
	N_READ_STATUS = 8'h70,
	N_SET_FEATURES = 8'hEF,
	N_PROGRAM_PAGE = 8'h80,
	N_PROGRAM_PAGE_END = 8'h10,
	N_READ_MODE = 8'h00,
	N_READ_PAGE_END = 8'h30,
	N_ERASE_BLOCK = 8'h60,
	N_ERASE_BLOCK_END = 8'hD0,
	N_READ_ID = 8'h90

} ONFICmd deriving (Bits, Eq);

//using tagged union
typedef union tagged {
	ONFICmd OnfiCmd;
	ChipT ChipSel;
} NandCmd deriving (Bits);

typedef struct {
	Bool inSyncMode;
	PhyCycle phyCycle;
	NandCmd nandCmd;
	Bit#(16) numBurst;
	Bit#(32) postCmdWait; //number of cycles to wait after the command
} PhyCmd deriving (Bits);

//---------------------------------
// Bus controller related types
//---------------------------------
typedef enum {
	//INIT_BUS,
	//EN_SYNC,
	//INIT_SYNC,
	//READ_DATA,
	READ_CMD,
	GET_STATUS_READ_DATA,
	WRITE_DATA_BUF_REQ,
	WRITE_CMD_DATA,
	WRITE_GET_STATUS,
	ERASE_CMD,
	ERASE_GET_STATUS,
	INVALID
} BusOp deriving (Bits, Eq);

typedef struct {
	TagT tag;
	BusOp busOp;
	ChipT chip;
	Bit#(16) block;
	Bit#(8) page;
} BusCmd deriving (Bits, Eq);

//---------------------------------
// Flash controller related types
//---------------------------------

typedef enum {
	INIT_BUS,
	EN_SYNC,
	INIT_SYNC,
	READ_PAGE,
	WRITE_PAGE,
	ERASE_BLOCK,
	INVALID
} FlashOp deriving (Bits, Eq);

typedef struct {
	TagT tag;
	FlashOp op;
	BusT bus; //3
	ChipT chip; //3
	Bit#(16) block;
	Bit#(8) page;
} FlashCmd deriving (Bits, Eq);

typedef struct {
	Bit#(8) page;
	Bit#(16) block;
	ChipT chip;
	BusT bus;
} FlashAddr deriving (Bits, Eq);

// Flash Controller User Ifc
interface FlashCtrlUser;
	method Action sendCmd (FlashCmd cmd);
	method Action writeWord (Tuple2#(Bit#(128), TagT) taggedData);
	method ActionValue#(Tuple2#(Bit#(128), TagT)) readWord ();
	method ActionValue#(TagT) writeDataReq();
	method ActionValue#(Tuple2#(TagT, StatusT)) ackStatus ();
endinterface

instance FShow#(BusOp);
	function Fmt fshow (BusOp label);
		case(label)
			READ_CMD: return fshow("BUSOP READ_CMD");
			GET_STATUS_READ_DATA: return fshow("BUSOP GET_STATUS_READ_DATA");
			WRITE_CMD_DATA: return fshow("BUSOP WRITE_CMD_DATA");
			ERASE_CMD: return fshow("BUSOP ERASE_CMD");
			WRITE_GET_STATUS: return fshow("BUSOP WRITE_GET_STATUS");
			WRITE_DATA_BUF_REQ: return fshow("BUSOP WRITE_DATA_BUF_REQ");
			ERASE_GET_STATUS: return fshow("BUSOP ERASE_GET_STATUS");
			INVALID: return fshow("BUSOP INVALID");
		endcase
	endfunction
endinstance

import FShow::*;

//**********************************************************
// Type definitions of the flash controller and submodules
//**********************************************************

//NAND geometry
//Actual page size is 8640B, but we don't need the last 40B for ECC
//Valid Data: 2 x (243B x 16 + 208B) = 8192B;
//With ECC: 2 x (255B x 16 + 220B) = 8600
typedef 8600 			PageSize;
typedef 8192 			PageSizeUser;
typedef 17 				PageECCBlks; //16 blocks of k=243; 1 block of k=208
`ifdef BSIM
	typedef 16 			PagesPerBlock;
	typedef 128			BlocksPerCE;
	typedef 8 			ChipsPerBus;
`elsif SLC
	typedef 128 		PagesPerBlock;
	typedef 8192 		BlocksPerCE;
	typedef 4 			ChipsPerBus;
`else //MLC
	typedef 256		 	PagesPerBlock;
	typedef 4096 		BlocksPerCE;
	typedef 8 			ChipsPerBus;
`endif

typedef 2											BusWordBytes;
typedef TMul#(8, BusWordBytes) 				BusWordSz;
typedef 16 											WordBytes;
typedef TLog#(WordBytes)						WordBytesLog;
typedef TMul#(8,WordBytes) 					WordSz;
//Each burst is 128 bits via the controller interface
typedef TDiv#(PageSizeUser, WordBytes) 	PageWords;
typedef TMul#(TMul#(BlocksPerCE, PagesPerBlock), PageWords) WordsPerChip;

typedef WordSz FlashDataWidth;
typedef 44  FlashAddrWidth;

Integer pageSize 			= valueOf(PageSize); //bytes
Integer pageSizeUser 	= valueOf(PageSizeUser); //usable page size is 8k
Integer pageECCBlks 		= valueOf(PageECCBlks); //16 blocks of k=243; 1 block of k=208
Integer pagesPerBlock 	= valueOf(PagesPerBlock);
Integer blocksPerCE 		= valueOf(BlocksPerCE);
Integer chipsPerBus		= valueOf(ChipsPerBus);
Integer wordBytes 		= valueOf(WordBytes);
Integer pageWords 		= valueOf(PageWords);
//Integer blocksPerPlane = 2048;
//Integer planesPerLun = 2;
//Integer lunsPerTarget = 1; //1 for SLC, 2 for MLC

`ifdef NAND_SIM
	typedef 1 NUM_CHIPBUSES;
`else
	typedef 4 NUM_CHIPBUSES;
`endif

typedef 2 BUSES_PER_CHIPBUS;
typedef TMul#(NUM_CHIPBUSES, BUSES_PER_CHIPBUS) NUM_BUSES;

typedef 8 NUM_DEBUG_ILAS;

typedef TMul#(NUM_BUSES, ChipsPerBus) NUM_TOTAL_CHIPS;

typedef 128 NumTags;
//typedef Bit#(TLog#(TDiv#(NumTags, NUM_BUSES))) BusTagT;
typedef Bit#(TLog#(NumTags)) TagT;
typedef Bit#(TLog#(ChipsPerBus)) ChipT;
typedef Bit#(TLog#(NUM_BUSES)) BusT;

Integer num_tags = valueOf(NumTags);
Integer num_buses = valueOf(NUM_BUSES);

typedef enum {
	SRC_HOST,
	SRC_USER_HW
} SourceT deriving (Bits, Eq, FShow);

typedef enum {
	ERASE_ERROR,
   ERASE_DONE,
	WRITE_DONE
} StatusT deriving (Bits, Eq);

//----------------------
//Aurora related types
//----------------------
typedef enum {
	F_CMD,
	F_DATA,
	F_ACK,
	F_WR_REQ
} PacketClass deriving (Bits, Eq);

//----------------------
//Phy related types
//----------------------
typedef enum {
	PHY_CHIP_SEL,
	PHY_DESELECT_ALL,
	PHY_CMD,
	PHY_READ,
	PHY_WRITE,
	PHY_ADDR,
	PHY_SYNC_CALIB,
	PHY_ENABLE_NAND_CLK
} PhyCycle deriving (Bits, Eq);

typedef enum {
	N_RESET = 8'hFF,
	N_READ_STATUS = 8'h70,
	N_SET_FEATURES = 8'hEF,
	N_PROGRAM_PAGE = 8'h80,
	N_PROGRAM_PAGE_END = 8'h10,
	N_READ_MODE = 8'h00,
	N_READ_PAGE_END = 8'h30,
	N_ERASE_BLOCK = 8'h60,
	N_ERASE_BLOCK_END = 8'hD0,
	N_READ_ID = 8'h90

} ONFICmd deriving (Bits, Eq);

//using tagged union
typedef union tagged {
	ONFICmd OnfiCmd;
	ChipT ChipSel;
} NandCmd deriving (Bits);

typedef struct {
	Bool inSyncMode;
	PhyCycle phyCycle;
	NandCmd nandCmd;
	Bit#(16) numBurst;
	Bit#(32) postCmdWait; //number of cycles to wait after the command
} PhyCmd deriving (Bits);

//---------------------------------
// Bus controller related types
//---------------------------------
typedef enum {
	//INIT_BUS,
	//EN_SYNC,
	//INIT_SYNC,
	//READ_DATA,
	READ_CMD,
	GET_STATUS_READ_DATA,
	WRITE_DATA_BUF_REQ,
	WRITE_CMD_DATA,
	WRITE_GET_STATUS,
	ERASE_CMD,
	ERASE_GET_STATUS,
	INVALID
} BusOp deriving (Bits, Eq);

typedef struct {
	TagT tag;
	BusOp busOp;
	ChipT chip;
	Bit#(16) block;
	Bit#(8) page;
} BusCmd deriving (Bits, Eq);

//---------------------------------
// Flash controller related types
//---------------------------------

typedef enum {
	INIT_BUS,
	EN_SYNC,
	INIT_SYNC,
	READ_PAGE,
	WRITE_PAGE,
	ERASE_BLOCK,
	INVALID
} FlashOp deriving (Bits, Eq);

typedef struct {
	TagT tag;
	FlashOp op;
	BusT bus; //3
	ChipT chip; //3
	Bit#(16) block;
	Bit#(8) page;
} FlashCmd deriving (Bits, Eq);

typedef struct {
	BusT bus;
	ChipT chip;
	Bit#(16) block;
	Bit#(8) page;
} FlashAddr deriving (Bits, Eq);

// Flash Controller User Ifc
interface FlashCtrlUser;
	method Action sendCmd (FlashCmd cmd);
	method Action writeWord (Tuple2#(Bit#(128), TagT) taggedData);
	method ActionValue#(Tuple2#(Bit#(128), TagT)) readWord ();
	method ActionValue#(TagT) writeDataReq();
	method ActionValue#(Tuple2#(TagT, StatusT)) ackStatus ();
endinterface

instance FShow#(BusOp);
	function Fmt fshow (BusOp label);
		case(label)
			READ_CMD: return fshow("BUSOP READ_CMD");
			GET_STATUS_READ_DATA: return fshow("BUSOP GET_STATUS_READ_DATA");
			WRITE_CMD_DATA: return fshow("BUSOP WRITE_CMD_DATA");
			ERASE_CMD: return fshow("BUSOP ERASE_CMD");
			WRITE_GET_STATUS: return fshow("BUSOP WRITE_GET_STATUS");
			WRITE_DATA_BUF_REQ: return fshow("BUSOP WRITE_DATA_BUF_REQ");
			ERASE_GET_STATUS: return fshow("BUSOP ERASE_GET_STATUS");
			INVALID: return fshow("BUSOP INVALID");
		endcase
	endfunction
endinstance


connect dma_write_server

connect dma_write_server

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

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

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

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

import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import GetPut::*;
import StmtFSM::*;
//import Vector::*;
import BRAM::*;
//import Gearbox::*;
import Connectable::*;

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

/* This module solves the maximum common subsequence problem.
 * It finds the longest subsequence of characters present in both input strings
 * the subsequence does not have to be contiguous and the characters can have different locations
 * and offsets in the two strings, just so long as they occur in the same order
 *
 *  To initialize, load string A with request setupA, and wait for indication setup complete
 * Then load string B with request setupB, and wait for indication setup complete
 * To start the unit, signal start, and wait for searchResult, which will tell you the length
 * To retreive the result, use fetch and wait for fetchComplete
 */

/* First pass implements Hirschberg Algorithm A and the fetch call returns the L matrix
 */
interface MaxcommonsubseqRequest;
   method Action setupA(Bit#(32) strPointer, Bit#(32) strLen);
   method Action setupB(Bit#(32) strPointer, Bit#(32) strLen);
   method Action fetch(Bit#(32) strPointer, Bit#(32) strLen);
   method Action start();
endinterface

interface MaxcommonsubseqIndication;
   method Action searchResult(Int#(32) v);
   method Action setupAComplete();
   method Action setupBComplete();
   method Action fetchComplete();
endinterface

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

typedef 128 MaxStringLen;
typedef 16384 MaxFetchLen;
typedef Bit#(TLog#(MaxStringLen)) StringIdx;
typedef Bit#(TLog#(MaxFetchLen)) LIdx;

module mkMaxcommonsubseqRequest#(MaxcommonsubseqIndication indication,
			DmaReadServer#(busWidth)   setupA_read_server,
			DmaReadServer#(busWidth)   setupB_read_server,
			DmaWriteServer#(busWidth)   fetch_write_server )(MaxcommonsubseqRequest)

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

  Reg#(Bit#(32)) aLenReg <- mkReg(0);
  Reg#(Bit#(32)) bLenReg <- mkReg(0);
  Reg#(Bit#(32)) rLenReg <- mkReg(0);
  Reg#(Bit#(32)) ii <- mkReg(0);
   Reg#(Char) aData <- mkReg(0);
   Reg#(Char) bData <- mkReg(0);
   BRAM2Port#(StringIdx, Char) strA  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(StringIdx, Char) strB <- mkBRAM2Server(defaultValue);
   BRAM2Port#(LIdx, Bit#(16)) matL <- mkBRAM2Server(defaultValue);

   BRAMReadClient#(StringIdx,busWidth) n2a <- mkBRAMReadClient(strA.portB);
   mkConnection(n2a.dmaClient, setupA_read_server);
   BRAMReadClient#(StringIdx,busWidth) n2b <- mkBRAMReadClient(strB.portB);
   mkConnection(n2b.dmaClient, setupB_read_server);
   BRAMWriteClient#(LIdx, 16) l2n <- mkBRAMWriteClient(matL.portB);
   mkConnection(l2n.dmaClient, fetch_write_server);

   FIFOF#(void) aReady <- mkFIFOF;
   FIFOF#(void) bReady <- mkFIFOF;
   FIFOF#(void) mReady <- mkFIFOF;
   Stmt splice =
   seq while(True)
   seq
      action
	 let ra <- aReady.deq();
	 $display("Splice A Ready");
      endaction
      action
	 let rb <- bReady.deq();
	 $display("Splice B Ready");
      endaction
      if (aLenReg > bLenReg)
	 rLenReg <= aLenReg;
      else
	 rLenReg <= bLenReg;
      for (ii <= 0; ii < rLenReg; ii <= ii + 1)
	 seq
	    $display("Splice ii %d ", ii);
	    strA.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: truncate(ii), datain: 0});
	    strB.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: truncate(ii), datain: 0});
	    action
	       let left <- strA.portA.response.get();
	       let right <- strB.portA.response.get();
	       aData <= left;
	       bData <= right;
	    endaction
	    $display("aData %d bData %d", aData, bData);
	    matL.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: truncate(ii), datain: {aData, bData}});
	 endseq
      indication.searchResult(unpack(rLenReg));
   endseq
   endseq;

   // create BRAM Write client for matL

   rule finish_setupA;
      $display("finish setupA");
      let x <- n2a.finish;
      aReady.enq(?);
      indication.setupAComplete();
   endrule

   rule finish_setupB;
      $display("finish setupB");
      let x <- n2b.finish;
      bReady.enq(?);
      indication.setupBComplete();
   endrule

   rule finish_fetch;
      $display("finish fetch");
      let x <- l2n.finish;
      indication.fetchComplete();
   endrule

   mkAutoFSM(splice);

   method Action setupA(Bit#(32) strPointer, Bit#(32) strLen);
      aLenReg <= strLen;
      $display("setupA %h %d", strPointer, strLen);
      n2a.start(strPointer, 0, pack(truncate(strLen)), 0);
   endmethod

   method Action setupB(Bit#(32) strPointer, Bit#(32) strLen);
      bLenReg <= strLen;
      $display("setupB %h %d", strPointer, strLen);
      n2b.start(strPointer, 0, pack(truncate(strLen)), 0);
   endmethod

   method Action fetch(Bit#(32) strPointer, Bit#(32) strLen);
      rLenReg <= strLen;
      $display("fetch %h %d", strPointer, strLen);
      l2n.start(strPointer, 0, pack(truncate(strLen)), 0);
   endmethod

   method Action start();
   endmethod

endmodule

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

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

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

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

import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import GetPut::*;
import StmtFSM::*;
//import Vector::*;
import BRAM::*;
//import Gearbox::*;
import Connectable::*;

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

/* This module solves the maximum common subsequence problem.
 * It finds the longest subsequence of characters present in both input strings
 * the subsequence does not have to be contiguous and the characters can have different locations
 * and offsets in the two strings, just so long as they occur in the same order
 *
 *  To initialize, load string A with request setupA, and wait for indication setup complete
 * Then load string B with request setupB, and wait for indication setup complete
 * To start the unit, signal start, and wait for searchResult, which will tell you the length
 * To retreive the result, use fetch and wait for fetchComplete
 */

/* First pass implements Hirschberg Algorithm A and the fetch call returns the L matrix
 */
interface MaxcommonsubseqRequest;
   method Action setupA(Bit#(32) strPointer, Bit#(32) strLen);
   method Action setupB(Bit#(32) strPointer, Bit#(32) strLen);
   method Action fetch(Bit#(32) strPointer, Bit#(32) strLen);
   method Action start();
endinterface

interface MaxcommonsubseqIndication;
   method Action searchResult(Int#(32) v);
   method Action setupAComplete();
   method Action setupBComplete();
   method Action fetchComplete();
endinterface

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

typedef 128 MaxStringLen;
typedef 16384 MaxFetchLen;
typedef Bit#(TLog#(MaxStringLen)) StringIdx;
typedef Bit#(TLog#(MaxFetchLen)) LIdx;

module mkMaxcommonsubseqRequest#(MaxcommonsubseqIndication indication,
			DmaReadServer#(busWidth)   setupA_read_server,
			DmaReadServer#(busWidth)   setupB_read_server,
			DmaWriteServer#(busWidth)   fetch_write_server )(MaxcommonsubseqRequest)

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

  Reg#(Bit#(32)) aLenReg <- mkReg(0);
  Reg#(Bit#(32)) bLenReg <- mkReg(0);
  Reg#(Bit#(32)) rLenReg <- mkReg(0);
  Reg#(Bit#(32)) ii <- mkReg(0);
   Reg#(Char) aData <- mkReg(0);
   Reg#(Char) bData <- mkReg(0);
   BRAM2Port#(StringIdx, Char) strA  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(StringIdx, Char) strB <- mkBRAM2Server(defaultValue);
   BRAM2Port#(LIdx, Bit#(16)) matL <- mkBRAM2Server(defaultValue);

   BRAMReadClient#(StringIdx,busWidth) n2a <- mkBRAMReadClient(strA.portB);
   mkConnection(n2a.dmaClient, setupA_read_server);
   BRAMReadClient#(StringIdx,busWidth) n2b <- mkBRAMReadClient(strB.portB);
   mkConnection(n2b.dmaClient, setupB_read_server);
   BRAMWriteClient#(LIdx, 16) l2n <- mkBRAMWriteClient(matL.portB);

   FIFOF#(void) aReady <- mkFIFOF;
   FIFOF#(void) bReady <- mkFIFOF;
   FIFOF#(void) mReady <- mkFIFOF;
   Stmt splice =
   seq while(True)
   seq
      action
	 let ra <- aReady.deq();
	 $display("Splice A Ready");
      endaction
      action
	 let rb <- bReady.deq();
	 $display("Splice B Ready");
      endaction
      if (aLenReg > bLenReg)
	 rLenReg <= aLenReg;
      else
	 rLenReg <= bLenReg;
      for (ii <= 0; ii < rLenReg; ii <= ii + 1)
	 seq
	    $display("Splice ii %d ", ii);
	    strA.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: truncate(ii), datain: 0});
	    strB.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: truncate(ii), datain: 0});
	    action
	       let left <- strA.portA.response.get();
	       let right <- strB.portA.response.get();
	       aData <= left;
	       bData <= right;
	    endaction
	    $display("aData %d bData %d", aData, bData);
	    matL.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: truncate(ii), datain: {aData, bData}});
	 endseq
      indication.searchResult(unpack(rLenReg));
   endseq
   endseq;

   // create BRAM Write client for matL

   rule finish_setupA;
      $display("finish setupA");
      let x <- n2a.finish;
      aReady.enq(?);
      indication.setupAComplete();
   endrule

   rule finish_setupB;
      $display("finish setupB");
      let x <- n2b.finish;
      bReady.enq(?);
      indication.setupBComplete();
   endrule

   rule finish_fetch;
      $display("finish fetch");
      let x <- l2n.finish;
      indication.fetchComplete();
   endrule

   mkAutoFSM(splice);

   method Action setupA(Bit#(32) strPointer, Bit#(32) strLen);
      aLenReg <= strLen;
      $display("setupA %h %d", strPointer, strLen);
      n2a.start(strPointer, 0, pack(truncate(strLen)), 0);
   endmethod

   method Action setupB(Bit#(32) strPointer, Bit#(32) strLen);
      bLenReg <= strLen;
      $display("setupB %h %d", strPointer, strLen);
      n2b.start(strPointer, 0, pack(truncate(strLen)), 0);
   endmethod

   method Action fetch(Bit#(32) strPointer, Bit#(32) strLen);
      rLenReg <= strLen;
      $display("fetch %h %d", strPointer, strLen);
      l2n.start(strPointer, 0, pack(truncate(strLen)), 0);
   endmethod

   method Action start();
   endmethod

endmodule


choose writes over reads to eliminate scheduling warning in MemSlaveEngine

Change-Id: I52d29a150664124b2a2a3c136cebef9775c7dad1

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

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

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

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

import FIFOF        :: *;
import GetPut       :: *;
import Connectable  :: *;
import PCIE         :: *;
import DefaultValue :: *;
import MIMO         :: *;
import Vector       :: *;
import ClientServer :: *;
import MemTypes     :: *;

import AxiMasterSlave :: *;

interface MemSlaveEngine#(numeric type buswidth);
    interface Client#(TLPData#(16), TLPData#(16)) tlp;
    interface MemSlave#(40,buswidth) slave;
    method Bool tlpOutFifoNotEmpty();
    interface Reg#(Bool) use4dw;
endinterface: MemSlaveEngine

module mkMemSlaveEngine#(PciId my_id)(MemSlaveEngine#(buswidth))
   provisos (Div#(buswidth, 8, busWidthBytes),
	     Div#(buswidth, 32, busWidthWords),
	     Bits#(Vector#(busWidthWords, Bit#(32)), buswidth),
            Log#(busWidthBytes,beatShift),
	     Add#(aaa, 32, buswidth),
	     Add#(bbb, buswidth, 256),
	     Add#(ccc, TMul#(8, busWidthWords), 64),
	     Add#(ddd, TMul#(32, busWidthWords), 256),
	     Add#(eee, busWidthWords, 8));

    let beat_shift = fromInteger(valueOf(beatShift));

    FIFOF#(TLPData#(16)) tlpOutFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpInFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpWriteHeaderFifo <- mkFIFOF;

    Reg#(Bit#(7)) hitReg <- mkReg(0);
    Reg#(Bool) use4dwReg <- mkReg(True);

    // default configuration for MIMO is for guarded enq() and deq().
    // However, the implicit guard only checks for space for 1 element for enq(), and availability of 1 element for deq().
    MIMOConfiguration mimoCfg = defaultValue;
    MIMO#(4,busWidthWords,8,Bit#(32)) completionMimo <- mkMIMO(mimoCfg);
    MIMO#(4,busWidthWords,8,TLPTag) completionTagMimo <- mkMIMO(mimoCfg);
    MIMO#(busWidthWords,4,8,Bit#(32)) writeDataMimo <- mkMIMO(mimoCfg);
    Reg#(Bit#(9)) writeBurstCount <- mkReg(0);
    Reg#(TLPLength)  writeDwCount <- mkReg(0);
    FIFOF#(TLPTag) writeTag <- mkSizedFIFOF(16);
    FIFOF#(TLPTag) doneTag <- mkSizedFIFOF(16);

    function Integer tlpWordCount(TLPData#(16) tlp);
       if (tlp.be == 16'h0000)
	  return 0;
       else if (tlp.be == 16'h000f || tlp.be == 16'hf000)
	  return 1;
       else if (tlp.be == 16'h00ff || tlp.be == 16'hff00)
	  return 2;
       else if (tlp.be == 16'h0fff || tlp.be == 16'hfff0)
	  return 3;
       else if (tlp.be == 16'hffff)
	  return 4;
       else
	  return 0;
    endfunction

   rule writeHeaderTlp if (writeDwCount == 0 && writeDataMimo.deqReadyN(1));
      let tlp = tlpWriteHeaderFifo.first;

      TLPMemory4DWHeader hdr_4dw = unpack(tlp.data);
      TLPLength dwCount = hdr_4dw.length;

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      Bool sendit = False;
      if (hdr_3dw.format == MEM_WRITE_3DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 dwCount = hdr_3dw.length;
	 Vector#(4, Bit#(32)) v = writeDataMimo.first();
	 writeDataMimo.deq(1);
	 hdr_3dw.data = byteSwap(v[0]);
	 tlp.be = 16'hffff;
	 if (dwCount == 1)
	    tlp.eof = True;
	 dwCount = dwCount - 1;
	 tlp.data = pack(hdr_3dw);
	 sendit = True;
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA) begin
	 // retry until the data is available in writeDataMimo
      end
      if (hdr_4dw.format == MEM_WRITE_4DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (hdr_4dw.format == MEM_WRITE_4DW_DATA) begin
	 sendit = False;
      end
      else begin
	 sendit = True;
      end
      if (sendit) begin
	 tlpWriteHeaderFifo.deq();
	 tlpOutFifo.enq(tlp);
	 $display("writeHeaderTlp dwCount=%d", dwCount);
	 writeDwCount <= dwCount;
	 if (dwCount == 0) begin
	    doneTag.enq(writeTag.first());
	    writeTag.deq();
	 end
      end
   endrule

   rule writeTlps if (writeDwCount > 0);
      TLPData#(16) tlp = defaultValue;
      tlp.sof = False;
      Vector#(4, Bit#(32)) v = unpack(0);
      Bool sendit = False;
      // The MIMO implicit guard only checks for availability of 1 element
      // so we explicitly check for the number of elements required
      if (writeDwCount > 4 && writeDataMimo.deqReadyN(4)) begin
	 v = writeDataMimo.first();

	 writeDataMimo.deq(4);
	 writeDwCount <= writeDwCount - 4;
	 tlp.eof = False;
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (writeDwCount <= 4 && writeDataMimo.deqReadyN(unpack(truncate(writeDwCount)))) begin
	 v = writeDataMimo.first();
	 writeDataMimo.deq(unpack(truncate(writeDwCount)));
	 writeDwCount <= 0;
	 doneTag.enq(writeTag.first());
	 writeTag.deq();
	 $display("writeDwCount=%d will be zero", writeDwCount);
	 tlp.eof = True;
	 if (writeDwCount == 4)
	    tlp.be = 16'hffff;
	 else if (writeDwCount == 3)
	    tlp.be = 16'hfff0;
	 else if (writeDwCount == 2)
	    tlp.be = 16'hff00;
	 else if (writeDwCount == 1)
	    tlp.be = 16'hf000;
	 sendit = True;
      end
      else begin
	 // wait for more data in writeDataMimo
	 $display("waiting for more data dwCount=%d count=%d writeBurstCount=%d enqReady=%d",
	    writeDwCount, writeDataMimo.count(), writeBurstCount, writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));
      end
      if (sendit) begin
	 for (Integer i = 0; i < 4; i = i + 1)
	    tlp.data[(i+1)*32-1:i*32] = byteSwap(v[3-i]);
	 tlpOutFifo.enq(tlp);
      end
   endrule

   Reg#(TLPTag) lastTag <- mkReg(0);
   rule handleTlpIn;
      let tlp = tlpInFifo.first;
      Bool handled = False;
      TLPMemoryIO3DWHeader h = unpack(tlp.data);
      hitReg <= tlp.hit;
      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      TLPCompletionHeader hdr_completion = unpack(tlp.data);
      Vector#(4, Bit#(32)) vec = unpack(0);
      Vector#(4, Bit#(32)) tlpvec = unpack(tlp.data);

      if (!tlp.sof) begin
	 let count = tlpWordCount(tlp);
	 // if sof is false, then count will be at least 1
	 function Bit#(32) f(Integer i);
	    begin
	       if (i < count)
		  return tlpvec[3-i];
	       else
		  return 32'hbad0beef;
	    end
	 endfunction
	 vec = genWith(f);
	 // The MIMO implicit guard only checks for space to enqueue 1 element
	 // so we explicitly check for the number of elements required
	 // otherwise elements in the queue will be overwritten.
	 if (completionMimo.enqReadyN(fromInteger(count))
	    && completionTagMimo.enqReadyN(fromInteger(count)))
	    begin
	       completionMimo.enq(fromInteger(count), vec);
	       Vector#(4, TLPTag) tagvec = replicate(lastTag);
	       completionTagMimo.enq(fromInteger(count), tagvec);
	       handled = True;
	    end
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA
	       && hdr_3dw.pkttype == COMPLETION
	       && completionMimo.enqReadyN(1)
	       && completionTagMimo.enqReadyN(1)) begin
	    vec[0] = hdr_3dw.data;
	    completionMimo.enq(1, vec);
            TLPTag tag = hdr_completion.tag;
	    lastTag <= tag;
	    completionTagMimo.enq(1, replicate(tag));
	    handled = True;
      end
      //$display("tlpIn handled=%d tlp=%h\n", handled, tlp);
      if (handled)
	 tlpInFifo.deq();
   endrule

    interface Client        tlp;
        interface request = toGet(tlpOutFifo);
        interface response = toPut(tlpInFifo);
    endinterface
    interface MemSlave slave;
   interface MemWriteServer write_server;
      interface Put writeReq;
         method Action put(MemRequest#(40) req)
	      if (writeBurstCount == 0);

	      let burstLen = req.burstLen >> beat_shift;
	      let addr = req.addr;
	      let awid = req.tag;

	      TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen));
	      TLPData#(16) tlp = defaultValue;
	      tlp.sof = True;
	      tlp.eof = False;
	      tlp.hit = 7'h00;
	      tlp.be = 16'hffff;

	      $display("slave.writeAddr tlplen=%d burstLen=%d", tlplen, burstLen);
	      if ((addr >> 32) != 0) begin
		 TLPMemory4DWHeader hdr_4dw = defaultValue;
		 hdr_4dw.format = MEM_WRITE_4DW_DATA;
		 hdr_4dw.tag = extend(awid);
		 hdr_4dw.reqid = my_id;
		 hdr_4dw.nosnoop = SNOOPING_REQD;
		 hdr_4dw.addr = addr[40-1:2];
		 hdr_4dw.length = tlplen;
		 hdr_4dw.firstbe = 4'hf;
		 hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		 tlp.data = pack(hdr_4dw);
	      end
	      else begin
		 TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		 hdr_3dw.format = MEM_WRITE_3DW_DATA;
		 hdr_3dw.tag = extend(awid);
		 hdr_3dw.reqid = my_id;
		 hdr_3dw.nosnoop = SNOOPING_REQD;
		 hdr_3dw.addr = addr[32-1:2];
		 hdr_3dw.length = tlplen;
		 hdr_3dw.firstbe = 4'hf;
		 hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;

		 tlp.be = 16'hfff0; // no data word in this TLP

		 tlp.data = pack(hdr_3dw);
	      end
	      tlpWriteHeaderFifo.enq(tlp);
	      writeBurstCount <= zeroExtend(burstLen);
	      writeTag.enq(extend(awid));
           endmethod
	endinterface
      interface Put writeData;
         method Action put(MemData#(buswidth) wdata)
	      provisos (Bits#(Vector#(busWidthWords, Bit#(32)), busWidth)) if (writeBurstCount > 0 && writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));

	      writeBurstCount <= writeBurstCount - 1;
	      Vector#(busWidthWords, Bit#(32)) v = unpack(wdata.data);
	      writeDataMimo.enq(fromInteger(valueOf(busWidthWords)), v);
           endmethod
       endinterface
      interface Get writeDone;
         method ActionValue#(Bit#(ObjectTagSize)) get();
	      let tag = doneTag.first();
	      doneTag.deq();
	      return truncate(tag);
           endmethod
	endinterface
   endinterface
   interface MemReadServer read_server;
      interface Put readReq;
         method Action put(MemRequest#(40) req) if (writeDwCount == 0 && !writeDataMimo.deqReadyN(1));
	      let burstLen = req.burstLen >> beat_shift;
	      let addr = req.addr;
	      let arid = req.tag;

	       TLPData#(16) tlp = defaultValue;
	       tlp.sof = True;
	       tlp.eof = True;
	       tlp.hit = 7'h00;
	       TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen));
	       if (addr[39:32] != 0) begin
		   TLPMemory4DWHeader hdr_4dw = defaultValue;
		   hdr_4dw.format = MEM_READ_4DW_NO_DATA;
		   hdr_4dw.tag = extend(arid);
		   hdr_4dw.reqid = my_id;
		   hdr_4dw.nosnoop = SNOOPING_REQD;
		   hdr_4dw.addr = addr[40-1:2];
		   hdr_4dw.length = tlplen;
		   hdr_4dw.firstbe = 4'hf;
		   hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_4dw);
		   tlp.be = 16'hffff;
	       end
	       else begin
		   TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		   hdr_3dw.format = MEM_READ_3DW_NO_DATA;
		   hdr_3dw.tag = extend(arid);
		   hdr_3dw.reqid = my_id;
		   hdr_3dw.nosnoop = SNOOPING_REQD;
		   hdr_3dw.addr = addr[32-1:2];
		   hdr_3dw.length = tlplen;
		   hdr_3dw.firstbe = 4'hf;
		   hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_3dw);
		   tlp.be = 16'hfff0;
	       end
	       tlpOutFifo.enq(tlp);
           endmethod
       endinterface
      interface Get     readData;
         method ActionValue#(MemData#(buswidth)) get() if (completionMimo.deqReadyN(fromInteger(valueOf(busWidthWords)))
									&& completionTagMimo.deqReadyN(fromInteger(valueOf(busWidthWords))));
	      let data_v = completionMimo.first;
	      let tag_v = completionTagMimo.first;
	      completionMimo.deq(fromInteger(valueOf(busWidthWords)));
	      completionTagMimo.deq(fromInteger(valueOf(busWidthWords)));
              Bit#(buswidth) v = 0;
	      for (Integer i = 0; i < valueOf(busWidthWords); i = i+1)
		 v[(i+1)*32-1:i*32] = byteSwap(data_v[i]);
	      return MemData { data: v, tag: truncate(tag_v[0])};
           endmethod
	endinterface
   endinterface
    endinterface: slave
   method Bool tlpOutFifoNotEmpty() = tlpOutFifo.notEmpty;
   interface Reg use4dw = use4dwReg;
endmodule: mkMemSlaveEngine

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

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

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

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

import FIFOF        :: *;
import GetPut       :: *;
import Connectable  :: *;
import PCIE         :: *;
import DefaultValue :: *;
import MIMO         :: *;
import Vector       :: *;
import ClientServer :: *;
import MemTypes     :: *;

import AxiMasterSlave :: *;

interface MemSlaveEngine#(numeric type buswidth);
    interface Client#(TLPData#(16), TLPData#(16)) tlp;
    interface MemSlave#(40,buswidth) slave;
    method Bool tlpOutFifoNotEmpty();
    interface Reg#(Bool) use4dw;
endinterface: MemSlaveEngine

module mkMemSlaveEngine#(PciId my_id)(MemSlaveEngine#(buswidth))
   provisos (Div#(buswidth, 8, busWidthBytes),
	     Div#(buswidth, 32, busWidthWords),
	     Bits#(Vector#(busWidthWords, Bit#(32)), buswidth),
            Log#(busWidthBytes,beatShift),
	     Add#(aaa, 32, buswidth),
	     Add#(bbb, buswidth, 256),
	     Add#(ccc, TMul#(8, busWidthWords), 64),
	     Add#(ddd, TMul#(32, busWidthWords), 256),
	     Add#(eee, busWidthWords, 8));

    let beat_shift = fromInteger(valueOf(beatShift));

    FIFOF#(TLPData#(16)) tlpOutFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpInFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpWriteHeaderFifo <- mkFIFOF;

    Reg#(Bit#(7)) hitReg <- mkReg(0);
    Reg#(Bool) use4dwReg <- mkReg(True);

    // default configuration for MIMO is for guarded enq() and deq().
    // However, the implicit guard only checks for space for 1 element for enq(), and availability of 1 element for deq().
    MIMOConfiguration mimoCfg = defaultValue;
    MIMO#(4,busWidthWords,8,Bit#(32)) completionMimo <- mkMIMO(mimoCfg);
    MIMO#(4,busWidthWords,8,TLPTag) completionTagMimo <- mkMIMO(mimoCfg);
    MIMO#(busWidthWords,4,8,Bit#(32)) writeDataMimo <- mkMIMO(mimoCfg);
    Reg#(Bit#(9)) writeBurstCount <- mkReg(0);
    Reg#(TLPLength)  writeDwCount <- mkReg(0);
    FIFOF#(TLPTag) writeTag <- mkSizedFIFOF(16);
    FIFOF#(TLPTag) doneTag <- mkSizedFIFOF(16);

    function Integer tlpWordCount(TLPData#(16) tlp);
       if (tlp.be == 16'h0000)
	  return 0;
       else if (tlp.be == 16'h000f || tlp.be == 16'hf000)
	  return 1;
       else if (tlp.be == 16'h00ff || tlp.be == 16'hff00)
	  return 2;
       else if (tlp.be == 16'h0fff || tlp.be == 16'hfff0)
	  return 3;
       else if (tlp.be == 16'hffff)
	  return 4;
       else
	  return 0;
    endfunction

   rule writeHeaderTlp if (writeDwCount == 0);
      let tlp = tlpWriteHeaderFifo.first;

      TLPMemory4DWHeader hdr_4dw = unpack(tlp.data);
      TLPLength dwCount = hdr_4dw.length;

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      Bool sendit = False;
      if (hdr_3dw.format == MEM_WRITE_3DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 dwCount = hdr_3dw.length;
	 Vector#(4, Bit#(32)) v = writeDataMimo.first();
	 writeDataMimo.deq(1);
	 hdr_3dw.data = byteSwap(v[0]);
	 tlp.be = 16'hffff;
	 if (dwCount == 1)
	    tlp.eof = True;
	 dwCount = dwCount - 1;
	 tlp.data = pack(hdr_3dw);
	 sendit = True;
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA) begin
	 // retry until the data is available in writeDataMimo
      end
      if (hdr_4dw.format == MEM_WRITE_4DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (hdr_4dw.format == MEM_WRITE_4DW_DATA) begin
	 sendit = False;
      end
      else begin
	 sendit = True;
      end
      if (sendit) begin
	 tlpWriteHeaderFifo.deq();
	 tlpOutFifo.enq(tlp);
	 $display("writeHeaderTlp dwCount=%d", dwCount);
	 writeDwCount <= dwCount;
	 if (dwCount == 0) begin
	    doneTag.enq(writeTag.first());
	    writeTag.deq();
	 end
      end
   endrule

   rule writeTlps if (writeDwCount > 0);
      TLPData#(16) tlp = defaultValue;
      tlp.sof = False;
      Vector#(4, Bit#(32)) v = unpack(0);
      Bool sendit = False;
      // The MIMO implicit guard only checks for availability of 1 element
      // so we explicitly check for the number of elements required
      if (writeDwCount > 4 && writeDataMimo.deqReadyN(4)) begin
	 v = writeDataMimo.first();

	 writeDataMimo.deq(4);
	 writeDwCount <= writeDwCount - 4;
	 tlp.eof = False;
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (writeDwCount <= 4 && writeDataMimo.deqReadyN(unpack(truncate(writeDwCount)))) begin
	 v = writeDataMimo.first();
	 writeDataMimo.deq(unpack(truncate(writeDwCount)));
	 writeDwCount <= 0;
	 doneTag.enq(writeTag.first());
	 writeTag.deq();
	 $display("writeDwCount=%d will be zero", writeDwCount);
	 tlp.eof = True;
	 if (writeDwCount == 4)
	    tlp.be = 16'hffff;
	 else if (writeDwCount == 3)
	    tlp.be = 16'hfff0;
	 else if (writeDwCount == 2)
	    tlp.be = 16'hff00;
	 else if (writeDwCount == 1)
	    tlp.be = 16'hf000;
	 sendit = True;
      end
      else begin
	 // wait for more data in writeDataMimo
	 $display("waiting for more data dwCount=%d count=%d writeBurstCount=%d enqReady=%d",
	    writeDwCount, writeDataMimo.count(), writeBurstCount, writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));
      end
      if (sendit) begin
	 for (Integer i = 0; i < 4; i = i + 1)
	    tlp.data[(i+1)*32-1:i*32] = byteSwap(v[3-i]);
	 tlpOutFifo.enq(tlp);
      end
   endrule

   Reg#(TLPTag) lastTag <- mkReg(0);
   rule handleTlpIn;
      let tlp = tlpInFifo.first;
      Bool handled = False;
      TLPMemoryIO3DWHeader h = unpack(tlp.data);
      hitReg <= tlp.hit;
      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      TLPCompletionHeader hdr_completion = unpack(tlp.data);
      Vector#(4, Bit#(32)) vec = unpack(0);
      Vector#(4, Bit#(32)) tlpvec = unpack(tlp.data);

      if (!tlp.sof) begin
	 let count = tlpWordCount(tlp);
	 // if sof is false, then count will be at least 1
	 function Bit#(32) f(Integer i);
	    begin
	       if (i < count)
		  return tlpvec[3-i];
	       else
		  return 32'hbad0beef;
	    end
	 endfunction
	 vec = genWith(f);
	 // The MIMO implicit guard only checks for space to enqueue 1 element
	 // so we explicitly check for the number of elements required
	 // otherwise elements in the queue will be overwritten.
	 if (completionMimo.enqReadyN(fromInteger(count))
	    && completionTagMimo.enqReadyN(fromInteger(count)))
	    begin
	       completionMimo.enq(fromInteger(count), vec);
	       Vector#(4, TLPTag) tagvec = replicate(lastTag);
	       completionTagMimo.enq(fromInteger(count), tagvec);
	       handled = True;
	    end
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA
	       && hdr_3dw.pkttype == COMPLETION
	       && completionMimo.enqReadyN(1)
	       && completionTagMimo.enqReadyN(1)) begin
	    vec[0] = hdr_3dw.data;
	    completionMimo.enq(1, vec);
            TLPTag tag = hdr_completion.tag;
	    lastTag <= tag;
	    completionTagMimo.enq(1, replicate(tag));
	    handled = True;
      end
      //$display("tlpIn handled=%d tlp=%h\n", handled, tlp);
      if (handled)
	 tlpInFifo.deq();
   endrule

    interface Client        tlp;
        interface request = toGet(tlpOutFifo);
        interface response = toPut(tlpInFifo);
    endinterface
    interface MemSlave slave;
   interface MemWriteServer write_server;
      interface Put writeReq;
         method Action put(MemRequest#(40) req)
	      if (writeBurstCount == 0);

	      let burstLen = req.burstLen >> beat_shift;
	      let addr = req.addr;
	      let awid = req.tag;

	      TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen));
	      TLPData#(16) tlp = defaultValue;
	      tlp.sof = True;
	      tlp.eof = False;
	      tlp.hit = 7'h00;
	      tlp.be = 16'hffff;

	      $display("slave.writeAddr tlplen=%d burstLen=%d", tlplen, burstLen);
	      if ((addr >> 32) != 0) begin
		 TLPMemory4DWHeader hdr_4dw = defaultValue;
		 hdr_4dw.format = MEM_WRITE_4DW_DATA;
		 hdr_4dw.tag = extend(awid);
		 hdr_4dw.reqid = my_id;
		 hdr_4dw.nosnoop = SNOOPING_REQD;
		 hdr_4dw.addr = addr[40-1:2];
		 hdr_4dw.length = tlplen;
		 hdr_4dw.firstbe = 4'hf;
		 hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		 tlp.data = pack(hdr_4dw);
	      end
	      else begin
		 TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		 hdr_3dw.format = MEM_WRITE_3DW_DATA;
		 hdr_3dw.tag = extend(awid);
		 hdr_3dw.reqid = my_id;
		 hdr_3dw.nosnoop = SNOOPING_REQD;
		 hdr_3dw.addr = addr[32-1:2];
		 hdr_3dw.length = tlplen;
		 hdr_3dw.firstbe = 4'hf;
		 hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;

		 tlp.be = 16'hfff0; // no data word in this TLP

		 tlp.data = pack(hdr_3dw);
	      end
	      tlpWriteHeaderFifo.enq(tlp);
	      writeBurstCount <= zeroExtend(burstLen);
	      writeTag.enq(extend(awid));
           endmethod
	endinterface
      interface Put writeData;
         method Action put(MemData#(buswidth) wdata)
	      provisos (Bits#(Vector#(busWidthWords, Bit#(32)), busWidth)) if (writeBurstCount > 0 && writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));

	      writeBurstCount <= writeBurstCount - 1;
	      Vector#(busWidthWords, Bit#(32)) v = unpack(wdata.data);
	      writeDataMimo.enq(fromInteger(valueOf(busWidthWords)), v);
           endmethod
       endinterface
      interface Get writeDone;
         method ActionValue#(Bit#(ObjectTagSize)) get();
	      let tag = doneTag.first();
	      doneTag.deq();
	      return truncate(tag);
           endmethod
	endinterface
   endinterface
   interface MemReadServer read_server;
      interface Put readReq;
         method Action put(MemRequest#(40) req) if (writeDwCount == 0);
	      let burstLen = req.burstLen >> beat_shift;
	      let addr = req.addr;
	      let arid = req.tag;

	       TLPData#(16) tlp = defaultValue;
	       tlp.sof = True;
	       tlp.eof = True;
	       tlp.hit = 7'h00;
	       TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen));
	       if (addr[39:32] != 0) begin
		   TLPMemory4DWHeader hdr_4dw = defaultValue;
		   hdr_4dw.format = MEM_READ_4DW_NO_DATA;
		   hdr_4dw.tag = extend(arid);
		   hdr_4dw.reqid = my_id;
		   hdr_4dw.nosnoop = SNOOPING_REQD;
		   hdr_4dw.addr = addr[40-1:2];
		   hdr_4dw.length = tlplen;
		   hdr_4dw.firstbe = 4'hf;
		   hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_4dw);
		   tlp.be = 16'hffff;
	       end
	       else begin
		   TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		   hdr_3dw.format = MEM_READ_3DW_NO_DATA;
		   hdr_3dw.tag = extend(arid);
		   hdr_3dw.reqid = my_id;
		   hdr_3dw.nosnoop = SNOOPING_REQD;
		   hdr_3dw.addr = addr[32-1:2];
		   hdr_3dw.length = tlplen;
		   hdr_3dw.firstbe = 4'hf;
		   hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_3dw);
		   tlp.be = 16'hfff0;
	       end
	       tlpOutFifo.enq(tlp);
           endmethod
       endinterface
      interface Get     readData;
         method ActionValue#(MemData#(buswidth)) get() if (completionMimo.deqReadyN(fromInteger(valueOf(busWidthWords)))
									&& completionTagMimo.deqReadyN(fromInteger(valueOf(busWidthWords))));
	      let data_v = completionMimo.first;
	      let tag_v = completionTagMimo.first;
	      completionMimo.deq(fromInteger(valueOf(busWidthWords)));
	      completionTagMimo.deq(fromInteger(valueOf(busWidthWords)));
              Bit#(buswidth) v = 0;
	      for (Integer i = 0; i < valueOf(busWidthWords); i = i+1)
		 v[(i+1)*32-1:i*32] = byteSwap(data_v[i]);
	      return MemData { data: v, tag: truncate(tag_v[0])};
           endmethod
	endinterface
   endinterface
    endinterface: slave
   method Bool tlpOutFifoNotEmpty() = tlpOutFifo.notEmpty;
   interface Reg use4dw = use4dwReg;
endmodule: mkMemSlaveEngine