add missing fields to BRAMRequest constructors

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

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

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

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

// BSV Libraries
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;
import BRAM::*;

// XBSV Libraries
import MemTypes::*;
import PortalMemory::*;
import SGList::*;

typedef 9 SGL_PIPELINE_DEPTH;

interface TagGen#(numeric type numTags);
   method ActionValue#(Bit#(TLog#(numTags))) getTag;
   method Action returnTag(Bit#(TLog#(numTags)) tag);
   method ActionValue#(Bit#(TLog#(numTags))) complete;
endinterface

module mkTagGen(TagGen#(numTags))
   provisos(Log#(numTags,tsz));

   BRAM_Configure cfg = defaultValue;
   cfg.outFIFODepth = 1;
   BRAM2Port#(Bit#(tsz),Bool) tags <- mkBRAM2Server(cfg);
   Reg#(Bit#(tsz))        head_ptr <- mkReg(0);
   Reg#(Bit#(tsz))        tail_ptr <- mkReg(0);
   Reg#(Bool)               inited <- mkReg(False);
   FIFO#(Bit#(tsz))      comp_fifo <- mkFIFO;
   Reg#(Bit#(numTags))  comp_state <- mkReg(0);

   let retFifo <- mkFIFO;

   rule complete_rule0 (comp_state[0] != 0);
      tags.portB.request.put(BRAMRequest{write:False, address:tail_ptr, datain: ?, responseOnWrite: ?});
   endrule

   rule complete_rule1 (comp_state[0] != 0);
      let rv <- tags.portB.response.get;
      if (!rv) begin
	 tail_ptr <= tail_ptr+1;
	 comp_state <= comp_state >> 1;
	 comp_fifo.enq(tail_ptr);
      end
   endrule

   // this used to be in the body of returnTag, but form some reason bsc does not
   // consider access to portA and portB to be conflict free **sigh**
   rule ret;
      let tag <- toGet(retFifo).get;
      tags.portB.request.put(BRAMRequest{write:True, responseOnWrite:False, address:tag, datain:False});
      comp_state <= 1 | (comp_state << 1);
   endrule

   rule init(!inited);
      tags.portA.request.put(BRAMRequest{write:True,address:head_ptr,responseOnWrite:False,datain:False});
      head_ptr <= head_ptr+1;
      inited <= head_ptr+1==0;
   endrule

   method ActionValue#(Bit#(tsz)) getTag() if (inited && (head_ptr+1 != tail_ptr));
      tags.portA.request.put(BRAMRequest{write:True, responseOnWrite:False, address:head_ptr, datain:True});
      head_ptr <= head_ptr+1;
      return head_ptr;
   endmethod

   method Action returnTag(Bit#(tsz) tag) if (inited);
      retFifo.enq(tag);
   endmethod

   method ActionValue#(Bit#(tsz)) complete;
      comp_fifo.deq;
      return comp_fifo.first;
   endmethod

endmodule

interface MemWriteInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemWriteClient#(addrWidth,dataWidth) write_client;
endinterface

interface MemReadInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemReadClient#(addrWidth,dataWidth) read_client;
endinterface

function Bool bad_pointer(ObjectPointer p);
   return ((p >>8) > fromInteger(valueOf(MaxNumSGLists)));
endfunction

typedef struct {ObjectRequest req;
		Bit#(TLog#(numClients)) client; } LRec#(numeric type numClients, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(addrWidth) pa;
		Bit#(6) rename_tag;
		Bit#(TLog#(numClients)) client; } RRec#(numeric type numClients, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(6) rename_tag;
		Bit#(TLog#(numClients)) client;
		Bool last; } DRec#(numeric type numClients, numeric type addrWidth) deriving(Bits);

typedef struct {Bit#(6) orig_tag;
		Bit#(TLog#(numClients)) client; } RResp#(numeric type numClients, numeric type addrWidth) deriving(Bits);

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

typedef 32 NumTags;

module mkMemReadInternal#(Integer id,
			  Vector#(numClients, ObjectReadClient#(dataWidth)) readClients,
			  DmaIndication dmaIndication,
			  Server#(ReqTup,Bit#(addrWidth)) sgl)
   (MemReadInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numClients), 6),
	    Add#(beatShift, e__, 8)
      );

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: read commands
   BRAM2Port#(Bit#(TLog#(NumTags)), DRec#(numClients,addrWidth)) dreqFifos <- mkBRAM2Server(defaultValue);
   BRAM2Port#(Bit#(TAdd#(TLog#(NumTags),TSub#(8,beatShift))), ObjectData#(dataWidth)) read_buffer <- mkBRAM2Server(defaultValue);
   // stage 3: read data
   FIFO#(MemData#(dataWidth)) readDataPipelineFifo <- mkFIFO;

   let debug = False;

   Reg#(Bit#(8))           burstReg <- mkReg(0);
   Reg#(Bool)              firstReg <- mkReg(True);
   Reg#(Bool)               lastReg <- mkReg(False);
   Reg#(Bit#(64))  beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   TagGen#(NumTags) tag_gen <- mkTagGen;

   Reg#(Bit#(8))                 compReg0 <- mkReg(0);
   Reg#(Bit#(TLog#(NumTags)))    compReg1 <- mkReg(0);
   Reg#(Bit#(TLog#(numClients))) compReg2 <- mkReg(0);
   FIFO#(Bit#(TLog#(numClients)))compFifo0 <- mkFIFO;
   FIFO#(Bit#(TLog#(NumTags)))   compFifo1 <- mkFIFO;

   // performance analytics
   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_comp <- mkReg(0);
   Reg#(Bit#(64)) last_readReq <- mkReg(0);
   Reg#(Bit#(64)) last_readData <- mkReg(0);
   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
      rule loadClient;
      	 if (debug) $display("mkMemReadInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	 last_loadClient <= cycle_cnt;
   	 ObjectRequest req <- readClients[selectReg].readReq.get();
   	 if (bad_pointer(req.pointer))
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer4, pref: req.pointer });
   	 else begin
   	    lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	    sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	 end
      endrule

   rule complete_burst0;
      let tag <- tag_gen.complete;
      dreqFifos.portB.request.put(BRAMRequest{write:False, address:tag, datain: ?, responseOnWrite: ?});
      compFifo1.enq(tag);
   endrule

   rule complete_burst1a if (compReg0==0);
      let drq <- dreqFifos.portB.response.get;
      let cnt = drq.req.burstLen >> beat_shift;
      let cli = drq.client;
      let tag <- toGet(compFifo1).get;
      compFifo0.enq(cli);
      read_buffer.portB.request.put(BRAMRequest{write:False, address:{tag,truncate(cnt)}, datain: ?, responseOnWrite: ?});
      compReg0 <= cnt-1;
      compReg1 <= tag;
      compReg2 <= cli;
   endrule

   rule complete_burst1b if (compReg0 > 0);
      let cnt = compReg0;
      let tag = compReg1;
      let cli = compReg2;
      compFifo0.enq(cli);
      read_buffer.portB.request.put(BRAMRequest{write:False, address:{tag,truncate(cnt)}, datain: ?, responseOnWrite: ?});
      compReg0 <= cnt-1;
   endrule

   rule complete_burst2;
      let client <- toGet(compFifo0).get;
      let data <- read_buffer.portB.response.get;
      readClients[client].readData.put(data);
      if (debug) $display("mkMemReadInternal::comp %d  %x %d", client, data.data, cycle_cnt-last_comp);
      last_comp <= cycle_cnt;
   endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.getTag;
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
      if (debug) $display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      if (debug) $display("mkMemReadInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      last_sglResp <= cycle_cnt;
   endrule

   rule read_data;
      let response <- toGet(readDataPipelineFifo).get();
      Bit#(6) response_tag = response.tag;
      let drq <- dreqFifos.portA.response.get;
      let req = drq.req;
      let burstLen = burstReg;
      let first =    firstReg;
      let last  =    lastReg;
      if (first) begin
	 burstLen = drq.req.burstLen >> beat_shift;
	 last = drq.last;
	 dynamicAssert(last == (burstLen==1), "Last incorrect");
	 //$display("burstLen=%d dreqFifo.first.last=%d last=%d\n", burstLen, dreqFifo.first.last, last);
      end
      Bit#(TLog#(NumTags)) tt = truncate(response_tag);
      read_buffer.portA.request.put(BRAMRequest{write:True, responseOnWrite:False, datain:ObjectData{data: response.data, tag: req.tag, last: last}, address:{tt,truncate(burstLen)}});
      if (last) begin
	 tag_gen.returnTag(truncate(response_tag));
      end
      last_readData <= cycle_cnt;
      if (debug) $display("read_data %d", cycle_cnt-last_readData);
      burstReg <= burstLen-1;
      firstReg <= (burstLen-1 == 0);
      lastReg <= (burstLen-1 == 1);
   endrule

   interface MemReadClient read_client;
      interface Get readReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    reqFifo.deq;
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    if (False && physAddr[31:24] != 0)
	       $display("req_ar: funny physAddr req.pointer=%d req.offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	    dreqFifos.portB.request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(rename_tag), datain:DRec{req:req, client:client, rename_tag:rename_tag, last:(req.burstLen == fromInteger(valueOf(dataWidthBytes)))}});
	    //$display("readReq: client=%d, rename_tag=%d, physAddr=%h req.burstLen=%d beat_shift=%d last=%d", client,rename_tag,physAddr, req.burstLen, beat_shift, req.burstLen == beat_shift);
	    if (debug) $display("read_client.readReq %d", cycle_cnt-last_readReq);
	    last_readReq <= cycle_cnt;
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:rename_tag};
	 endmethod
      endinterface
      interface Put readData;
	 method Action put(MemData#(dataWidth) response);
	    readDataPipelineFifo.enq(response);
	    dreqFifos.portA.request.put(BRAMRequest{write:False, address:truncate(response.tag), datain: ?, responseOnWrite: ?});
	    beatCount <= beatCount+1;
	 endmethod
      endinterface
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 return DmaDbgRec{x:0, y:0, z:0, w:0};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   endinterface
endmodule

module mkMemWriteInternal#(Integer iid,
			   Vector#(numClients, ObjectWriteClient#(dataWidth)) writeClients,
			   DmaIndication dmaIndication,
			   Server#(ReqTup,Bit#(addrWidth)) sgl)
   (MemWriteInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numClients), 6));

   let debug = False;

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: write commands
   FIFO#(DRec#(numClients, addrWidth)) dreqFifo <- mkSizedBRAMFIFO(valueOf(TMul#(numClients,NumTags)));
   // stage 3: write data
   BRAM2Port#(Bit#(TLog#(NumTags)), RResp#(numClients,addrWidth)) respFifos <- mkBRAM2Server(defaultValue);
   TagGen#(NumTags) tag_gen <- mkTagGen;

   Reg#(Bit#(8)) burstReg <- mkReg(0);
   Reg#(Bool)    firstReg <- mkReg(True);
   Reg#(Bool)     lastReg <- mkReg(False);
   Reg#(Bit#(64)) beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));

   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_eob <- mkReg(0);

   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
       rule loadClient;
      	  //$display("mkMemWriteInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	  //last_loadClient <= cycle_cnt;
   	  ObjectRequest req <- writeClients[selectReg].writeReq.get();
   	  if (bad_pointer(req.pointer))
	     dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer5, pref: req.pointer });
   	  else begin
   	     lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	     sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	  end
       endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.getTag;
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
      if (debug) $display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      //$display("mkMemWriteInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

   rule writeDoneComp0;
      let tag <- tag_gen.complete;
      respFifos.portB.request.put(BRAMRequest{write:False, address:tag, datain: ?, responseOnWrite: ?});
   endrule

   rule writeDoneComp1;
      let rv <- respFifos.portB.response.get;
      let client = rv.client;
      let orig_tag = rv.orig_tag;
      writeClients[client].writeDone.put(orig_tag);
   endrule

   FIFO#(MemData#(dataWidth)) memDataFifo <- mkFIFO();
   Vector#(numClients, FIFO#(ObjectData#(dataWidth))) clientWriteData <- replicateM(mkFIFO);
   // Pipeline client data:
   // The .get() operation seems to be long latency, so get it into a local FIFO
   for (Integer client = 0; client < valueOf(numClients); client = client + 1)
      rule clientdata;
	 let d <- writeClients[client].writeData.get();
	 clientWriteData[client].enq(d);
      endrule

   rule memdata;
      let client = dreqFifo.first.client;
      let req = dreqFifo.first.req;
      let rename_tag = dreqFifo.first.rename_tag;
      ObjectData#(dataWidth) tagdata <- toGet(clientWriteData[client]).get();
      let burstLen = burstReg;
      let first    = firstReg;
      let last     = lastReg;
      if (first) begin
	 burstLen = req.burstLen >> beat_shift;
	 last     = dreqFifo.first.last;
	 respFifos.portA.request.put(BRAMRequest{write:True,responseOnWrite:False, address:truncate(rename_tag), datain:RResp{orig_tag:req.tag, client:client}});
      end
      burstReg <= burstLen-1;
      firstReg <= (burstLen-1 == 0);
      lastReg  <= (burstLen-1 == 1);
      beatCount <= beatCount+1;
      if (last)
	 dreqFifo.deq();
      //$display("writeData: client=%d, rename_tag=%d", client, rename_tag);
      memDataFifo.enq(MemData { data: tagdata.data,  tag:extend(rename_tag), last: False });
   endrule

   interface MemWriteClient write_client;
      interface Get writeReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    reqFifo.deq;
	    dreqFifo.enq(DRec{req:req, client:client, rename_tag:rename_tag, last: (req.burstLen == fromInteger(valueOf(dataWidthBytes))) });
	    //$display("writeReq: client=%d, rename_tag=%d", client,rename_tag);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:extend(rename_tag)};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get();
            let d <- toGet(memDataFifo).get();
            return d;
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(6) resp);
	    tag_gen.returnTag(truncate(resp));
	    if (debug) $display("writeDone: resp=%d", resp);
	 endmethod
      endinterface
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 return DmaDbgRec{x:fromInteger(valueOf(numClients)), y:?, z:?, w:?};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   endinterface
endmodule

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

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

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

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

// BSV Libraries
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;
import BRAM::*;

// XBSV Libraries
import MemTypes::*;
import PortalMemory::*;
import SGList::*;

typedef 9 SGL_PIPELINE_DEPTH;

interface TagGen#(numeric type numTags);
   method ActionValue#(Bit#(TLog#(numTags))) getTag;
   method Action returnTag(Bit#(TLog#(numTags)) tag);
   method ActionValue#(Bit#(TLog#(numTags))) complete;
endinterface

module mkTagGen(TagGen#(numTags))
   provisos(Log#(numTags,tsz));

   BRAM_Configure cfg = defaultValue;
   cfg.outFIFODepth = 1;
   BRAM2Port#(Bit#(tsz),Bool) tags <- mkBRAM2Server(cfg);
   Reg#(Bit#(tsz))        head_ptr <- mkReg(0);
   Reg#(Bit#(tsz))        tail_ptr <- mkReg(0);
   Reg#(Bool)               inited <- mkReg(False);
   FIFO#(Bit#(tsz))      comp_fifo <- mkFIFO;
   Reg#(Bit#(numTags))  comp_state <- mkReg(0);

   let retFifo <- mkFIFO;

   rule complete_rule0 (comp_state[0] != 0);
      tags.portB.request.put(BRAMRequest{write:False, address:tail_ptr});
   endrule

   rule complete_rule1 (comp_state[0] != 0);
      let rv <- tags.portB.response.get;
      if (!rv) begin
	 tail_ptr <= tail_ptr+1;
	 comp_state <= comp_state >> 1;
	 comp_fifo.enq(tail_ptr);
      end
   endrule

   // this used to be in the body of returnTag, but form some reason bsc does not
   // consider access to portA and portB to be conflict free **sigh**
   rule ret;
      let tag <- toGet(retFifo).get;
      tags.portB.request.put(BRAMRequest{write:True, responseOnWrite:False, address:tag, datain:False});
      comp_state <= 1 | (comp_state << 1);
   endrule

   rule init(!inited);
      tags.portA.request.put(BRAMRequest{write:True,address:head_ptr,responseOnWrite:False,datain:False});
      head_ptr <= head_ptr+1;
      inited <= head_ptr+1==0;
   endrule

   method ActionValue#(Bit#(tsz)) getTag() if (inited && (head_ptr+1 != tail_ptr));
      tags.portA.request.put(BRAMRequest{write:True, responseOnWrite:False, address:head_ptr, datain:True});
      head_ptr <= head_ptr+1;
      return head_ptr;
   endmethod

   method Action returnTag(Bit#(tsz) tag) if (inited);
      retFifo.enq(tag);
   endmethod

   method ActionValue#(Bit#(tsz)) complete;
      comp_fifo.deq;
      return comp_fifo.first;
   endmethod

endmodule

interface MemWriteInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemWriteClient#(addrWidth,dataWidth) write_client;
endinterface

interface MemReadInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemReadClient#(addrWidth,dataWidth) read_client;
endinterface

function Bool bad_pointer(ObjectPointer p);
   return ((p >>8) > fromInteger(valueOf(MaxNumSGLists)));
endfunction

typedef struct {ObjectRequest req;
		Bit#(TLog#(numClients)) client; } LRec#(numeric type numClients, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(addrWidth) pa;
		Bit#(6) rename_tag;
		Bit#(TLog#(numClients)) client; } RRec#(numeric type numClients, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(6) rename_tag;
		Bit#(TLog#(numClients)) client;
		Bool last; } DRec#(numeric type numClients, numeric type addrWidth) deriving(Bits);

typedef struct {Bit#(6) orig_tag;
		Bit#(TLog#(numClients)) client; } RResp#(numeric type numClients, numeric type addrWidth) deriving(Bits);

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

typedef 32 NumTags;

module mkMemReadInternal#(Integer id,
			  Vector#(numClients, ObjectReadClient#(dataWidth)) readClients,
			  DmaIndication dmaIndication,
			  Server#(ReqTup,Bit#(addrWidth)) sgl)
   (MemReadInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numClients), 6),
	    Add#(beatShift, e__, 8)
      );

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: read commands
   BRAM2Port#(Bit#(TLog#(NumTags)), DRec#(numClients,addrWidth)) dreqFifos <- mkBRAM2Server(defaultValue);
   BRAM2Port#(Bit#(TAdd#(TLog#(NumTags),TSub#(8,beatShift))), ObjectData#(dataWidth)) read_buffer <- mkBRAM2Server(defaultValue);
   // stage 3: read data
   FIFO#(MemData#(dataWidth)) readDataPipelineFifo <- mkFIFO;

   let debug = False;

   Reg#(Bit#(8))           burstReg <- mkReg(0);
   Reg#(Bool)              firstReg <- mkReg(True);
   Reg#(Bool)               lastReg <- mkReg(False);
   Reg#(Bit#(64))  beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   TagGen#(NumTags) tag_gen <- mkTagGen;

   Reg#(Bit#(8))                 compReg0 <- mkReg(0);
   Reg#(Bit#(TLog#(NumTags)))    compReg1 <- mkReg(0);
   Reg#(Bit#(TLog#(numClients))) compReg2 <- mkReg(0);
   FIFO#(Bit#(TLog#(numClients)))compFifo0 <- mkFIFO;
   FIFO#(Bit#(TLog#(NumTags)))   compFifo1 <- mkFIFO;

   // performance analytics
   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_comp <- mkReg(0);
   Reg#(Bit#(64)) last_readReq <- mkReg(0);
   Reg#(Bit#(64)) last_readData <- mkReg(0);
   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
      rule loadClient;
      	 if (debug) $display("mkMemReadInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	 last_loadClient <= cycle_cnt;
   	 ObjectRequest req <- readClients[selectReg].readReq.get();
   	 if (bad_pointer(req.pointer))
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer4, pref: req.pointer });
   	 else begin
   	    lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	    sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	 end
      endrule

   rule complete_burst0;
      let tag <- tag_gen.complete;
      dreqFifos.portB.request.put(BRAMRequest{write:False, address:tag});
      compFifo1.enq(tag);
   endrule

   rule complete_burst1a if (compReg0==0);
      let drq <- dreqFifos.portB.response.get;
      let cnt = drq.req.burstLen >> beat_shift;
      let cli = drq.client;
      let tag <- toGet(compFifo1).get;
      compFifo0.enq(cli);
      read_buffer.portB.request.put(BRAMRequest{write:False, address:{tag,truncate(cnt)}});
      compReg0 <= cnt-1;
      compReg1 <= tag;
      compReg2 <= cli;
   endrule

   rule complete_burst1b if (compReg0 > 0);
      let cnt = compReg0;
      let tag = compReg1;
      let cli = compReg2;
      compFifo0.enq(cli);
      read_buffer.portB.request.put(BRAMRequest{write:False, address:{tag,truncate(cnt)}});
      compReg0 <= cnt-1;
   endrule

   rule complete_burst2;
      let client <- toGet(compFifo0).get;
      let data <- read_buffer.portB.response.get;
      readClients[client].readData.put(data);
      if (debug) $display("mkMemReadInternal::comp %d  %x %d", client, data.data, cycle_cnt-last_comp);
      last_comp <= cycle_cnt;
   endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.getTag;
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
      if (debug) $display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      if (debug) $display("mkMemReadInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      last_sglResp <= cycle_cnt;
   endrule

   rule read_data;
      let response <- toGet(readDataPipelineFifo).get();
      Bit#(6) response_tag = response.tag;
      let drq <- dreqFifos.portA.response.get;
      let req = drq.req;
      let burstLen = burstReg;
      let first =    firstReg;
      let last  =    lastReg;
      if (first) begin
	 burstLen = drq.req.burstLen >> beat_shift;
	 last = drq.last;
	 dynamicAssert(last == (burstLen==1), "Last incorrect");
	 //$display("burstLen=%d dreqFifo.first.last=%d last=%d\n", burstLen, dreqFifo.first.last, last);
      end
      Bit#(TLog#(NumTags)) tt = truncate(response_tag);
      read_buffer.portA.request.put(BRAMRequest{write:True, responseOnWrite:False, datain:ObjectData{data: response.data, tag: req.tag, last: last}, address:{tt,truncate(burstLen)}});
      if (last) begin
	 tag_gen.returnTag(truncate(response_tag));
      end
      last_readData <= cycle_cnt;
      if (debug) $display("read_data %d", cycle_cnt-last_readData);
      burstReg <= burstLen-1;
      firstReg <= (burstLen-1 == 0);
      lastReg <= (burstLen-1 == 1);
   endrule

   interface MemReadClient read_client;
      interface Get readReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    reqFifo.deq;
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    if (False && physAddr[31:24] != 0)
	       $display("req_ar: funny physAddr req.pointer=%d req.offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	    dreqFifos.portB.request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(rename_tag), datain:DRec{req:req, client:client, rename_tag:rename_tag, last:(req.burstLen == fromInteger(valueOf(dataWidthBytes)))}});
	    //$display("readReq: client=%d, rename_tag=%d, physAddr=%h req.burstLen=%d beat_shift=%d last=%d", client,rename_tag,physAddr, req.burstLen, beat_shift, req.burstLen == beat_shift);
	    if (debug) $display("read_client.readReq %d", cycle_cnt-last_readReq);
	    last_readReq <= cycle_cnt;
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:rename_tag};
	 endmethod
      endinterface
      interface Put readData;
	 method Action put(MemData#(dataWidth) response);
	    readDataPipelineFifo.enq(response);
	    dreqFifos.portA.request.put(BRAMRequest{write:False, address:truncate(response.tag)});
	    beatCount <= beatCount+1;
	 endmethod
      endinterface
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 return DmaDbgRec{x:0, y:0, z:0, w:0};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   endinterface
endmodule

module mkMemWriteInternal#(Integer iid,
			   Vector#(numClients, ObjectWriteClient#(dataWidth)) writeClients,
			   DmaIndication dmaIndication,
			   Server#(ReqTup,Bit#(addrWidth)) sgl)
   (MemWriteInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numClients), 6));

   let debug = False;

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: write commands
   FIFO#(DRec#(numClients, addrWidth)) dreqFifo <- mkSizedBRAMFIFO(valueOf(TMul#(numClients,NumTags)));
   // stage 3: write data
   BRAM2Port#(Bit#(TLog#(NumTags)), RResp#(numClients,addrWidth)) respFifos <- mkBRAM2Server(defaultValue);
   TagGen#(NumTags) tag_gen <- mkTagGen;

   Reg#(Bit#(8)) burstReg <- mkReg(0);
   Reg#(Bool)    firstReg <- mkReg(True);
   Reg#(Bool)     lastReg <- mkReg(False);
   Reg#(Bit#(64)) beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));

   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_eob <- mkReg(0);

   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
       rule loadClient;
      	  //$display("mkMemWriteInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	  //last_loadClient <= cycle_cnt;
   	  ObjectRequest req <- writeClients[selectReg].writeReq.get();
   	  if (bad_pointer(req.pointer))
	     dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer5, pref: req.pointer });
   	  else begin
   	     lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	     sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	  end
       endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.getTag;
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
      if (debug) $display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      //$display("mkMemWriteInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

   rule writeDoneComp0;
      let tag <- tag_gen.complete;
      respFifos.portB.request.put(BRAMRequest{write:False, address:tag});
   endrule

   rule writeDoneComp1;
      let rv <- respFifos.portB.response.get;
      let client = rv.client;
      let orig_tag = rv.orig_tag;
      writeClients[client].writeDone.put(orig_tag);
   endrule

   FIFO#(MemData#(dataWidth)) memDataFifo <- mkFIFO();
   Vector#(numClients, FIFO#(ObjectData#(dataWidth))) clientWriteData <- replicateM(mkFIFO);
   // Pipeline client data:
   // The .get() operation seems to be long latency, so get it into a local FIFO
   for (Integer client = 0; client < valueOf(numClients); client = client + 1)
      rule clientdata;
	 let d <- writeClients[client].writeData.get();
	 clientWriteData[client].enq(d);
      endrule

   rule memdata;
      let client = dreqFifo.first.client;
      let req = dreqFifo.first.req;
      let rename_tag = dreqFifo.first.rename_tag;
      ObjectData#(dataWidth) tagdata <- toGet(clientWriteData[client]).get();
      let burstLen = burstReg;
      let first    = firstReg;
      let last     = lastReg;
      if (first) begin
	 burstLen = req.burstLen >> beat_shift;
	 last     = dreqFifo.first.last;
	 respFifos.portA.request.put(BRAMRequest{write:True,responseOnWrite:False, address:truncate(rename_tag), datain:RResp{orig_tag:req.tag, client:client}});
      end
      burstReg <= burstLen-1;
      firstReg <= (burstLen-1 == 0);
      lastReg  <= (burstLen-1 == 1);
      beatCount <= beatCount+1;
      if (last)
	 dreqFifo.deq();
      //$display("writeData: client=%d, rename_tag=%d", client, rename_tag);
      memDataFifo.enq(MemData { data: tagdata.data,  tag:extend(rename_tag), last: False });
   endrule

   interface MemWriteClient write_client;
      interface Get writeReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    reqFifo.deq;
	    dreqFifo.enq(DRec{req:req, client:client, rename_tag:rename_tag, last: (req.burstLen == fromInteger(valueOf(dataWidthBytes))) });
	    //$display("writeReq: client=%d, rename_tag=%d", client,rename_tag);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:extend(rename_tag)};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get();
            let d <- toGet(memDataFifo).get();
            return d;
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(6) resp);
	    tag_gen.returnTag(truncate(resp));
	    if (debug) $display("writeDone: resp=%d", resp);
	 endmethod
      endinterface
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 return DmaDbgRec{x:fromInteger(valueOf(numClients)), y:?, z:?, w:?};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   endinterface
endmodule


added mkPcieDmaTop to Xilinx7PcieBridge.bsv

// Copyright (c) 2008- 2009 Bluespec, Inc.  All rights reserved.
// $Revision$
// $Date$
// Copyright (c) 2013 Quanta Research Cambridge, Inc.

package Xilinx7PcieBridge;

// PCI-Express for Xilinx 7
// FPGAs.

import Clocks               :: *;
import Connectable          :: *;
import TieOff               :: *;
import DefaultValue         :: *;
import BUtils               :: *;
import Xilinx               :: *;
import XilinxPCIE           :: *;
import XilinxCells          :: *;
import GetPut               :: *;
import ClientServer         :: *;
import Memory               :: *;
import BlueNoC              :: *;
import PcieToAxiBridge      :: *;
import XbsvXilinx7Pcie      :: *;
//import XbsvXilinx7DDR3      :: *;
import AxiMasterSlave       :: *;
import Portal               :: *;

// from SceMiDefines
typedef 4 BPB;

// Interface wrapper for PCIE
interface X7PcieBridgeIfc#(numeric type lanes);
   interface PCIE_EXP#(lanes) pcie;
   (* always_ready *)
   method Bool isLinkUp();
   method Bool isCalibrated();
   (* always_ready *)
   method Action interrupt();
   interface Clock clock250;
   interface Reset reset250;
   interface Clock clock125;
   interface Reset reset125;
   (* prefix = "" *)
   //interface DDR3_Pins_X7      ddr3;
   interface Axi3Master#(32,32,4,12) portal0;
   interface GetPut#(TLPData#(16)) slave; // to the axi slave engine
   interface Put#(TimestampedTlpData) trace;
   interface Reg#(Bit#(4)) numPortals;
   interface ReadOnly#(PciId) pciId;
endinterface

// This module builds the transactor hierarchy, the clock
// generation logic and the PCIE-to-port logic.
(* no_default_clock, no_default_reset *)
module mkX7PcieBridge#( Clock pci_sys_clk_p, Clock pci_sys_clk_n
		       , Clock sys_clk_p,    Clock sys_clk_n
		       , Reset pci_sys_reset
                       , Bit#(64) contentId
		       )
		       (X7PcieBridgeIfc#(lanes))
   provisos(Add#(1,_,lanes), XbsvXilinx7Pcie::SelectXilinx7PCIE#(lanes));

   if (valueOf(lanes) != 8)
      errorM("Only 8-lane PCIe is supported on X7.");

   Clock sys_clk_200mhz <- mkClockIBUFDS(sys_clk_p, sys_clk_n);

   ClockGenerator7Params clk_params = defaultValue();
   clk_params.clkin1_period     = 5.000;       // 200 MHz reference
   clk_params.clkin_buffer      = False;       // necessary buffer is instanced above
   clk_params.reset_stages      = 0;           // no sync on reset so input clock has pll as only load
   clk_params.clkfbout_mult_f   = 5.000;       // 1000 MHz VCO
   clk_params.clkout0_divide_f  = 10;          // unused clock
   clk_params.clkout1_divide    = 5;           // ddr3 reference clock (200 MHz)

   ClockGenerator7 clk_gen <- mkClockGenerator7(clk_params, clocked_by sys_clk_200mhz, reset_by pci_sys_reset);

   Clock clk = clk_gen.clkout0;
   Reset rst_n <- mkAsyncReset( 1, pci_sys_reset, clk );
   Reset ddr3ref_rst_n <- mkAsyncReset( 1, rst_n, clk_gen.clkout1 );

   //DDR3_Configure_X7 ddr3_cfg;
   //ddr3_cfg.num_reads_in_flight = 2;   // adjust as needed
   //ddr3_cfg.fast_train_sim_only = False; // adjust if simulating

   //DDR3_Controller_X7 ddr3_ctrl <- mkXilinx7DDR3Controller(ddr3_cfg, clocked_by clk_gen.clkout1, reset_by ddr3ref_rst_n);

   // ddr3_ctrl.user needs to connect to user logic and should use ddr3clk and ddr3rstn
   //Clock ddr3clk = ddr3_ctrl.user.clock;
   //Reset ddr3rstn = ddr3_ctrl.user.reset_n;

   // Buffer clocks and reset before they are used
   Clock pci_clk_100mhz_buf <- mkClockIBUFDS_GTE2(True, pci_sys_clk_p, pci_sys_clk_n);

   // Instantiate the PCIE endpoint
   XbsvXilinx7Pcie::PCIExpressX7#(lanes) _ep
       <- XbsvXilinx7Pcie::mkPCIExpressEndpointX7( defaultValue
						  , clocked_by pci_clk_100mhz_buf
						  , reset_by pci_sys_reset
						  );
   mkTieOff(_ep.cfg);
   mkTieOff(_ep.cfg_interrupt);
   mkTieOff(_ep.cfg_err);
   mkTieOff(_ep.pl);

   // note our PCI ID
   PciId my_id = PciId { bus:  _ep.cfg.bus_number()
		       , dev:  _ep.cfg.device_number()
		       , func: _ep.cfg.function_number()
		       };

   // The PCIE endpoint is processing TLPWord#(8)s at 250MHz.  The
   // NoC bridge is accepting TLPWord#(16)s at 125 MHz. The
   // connection between the endpoint and the NoC contains GearBox
   // instances for the TLPWord#(8)@250 <--> TLPWord#(16)@125
   // conversion.

   // The PCIe endpoint exports full (250MHz) and half-speed (125MHz) clocks
   Clock epClock250 = _ep.trn.clk;
   Reset epReset250 <- mkAsyncReset(4, _ep.trn.reset_n, epClock250);
   Clock epClock125 = _ep.trn.clk2;
   Reset epReset125 <- mkAsyncReset(4, _ep.trn.reset_n, epClock125);

   // Extract some status info from the PCIE endpoint. These values are
   // all in the epClock250 domain, so we have to cross them into the
   // epClock125 domain.

   Bool link_is_up = _ep.trn.link_up();
   UInt#(13) max_read_req_bytes_250       = 128 << _ep.cfg.dcommand[14:12];
   UInt#(13) max_payload_bytes_250        = 128 << _ep.cfg.dcommand[7:5];
   UInt#(8)  read_completion_boundary_250 = 64 << _ep.cfg.lcommand[3];
   Bool      msix_enable_250              = (_ep.cfg_interrupt.msixenable() == 1);
   Bool      msix_masked_250              = (_ep.cfg_interrupt.msixfm()     == 1);

   CrossingReg#(UInt#(13)) max_rd_req_cr  <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(UInt#(13)) max_payload_cr <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(UInt#(8))  rcb_cr         <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(Bool)      msix_enable_cr <- mkNullCrossingReg(epClock125, False, clocked_by epClock250, reset_by epReset250);
   CrossingReg#(Bool)      msix_masked_cr <- mkNullCrossingReg(epClock125, True,  clocked_by epClock250, reset_by epReset250);

   Reg#(UInt#(13)) max_read_req_bytes <- mkReg(128,   clocked_by epClock125, reset_by epReset125);
   Reg#(UInt#(13)) max_payload_bytes  <- mkReg(128,   clocked_by epClock125, reset_by epReset125);
   Reg#(Bit#(7))   rcb_mask           <- mkReg(7'h3f, clocked_by epClock125, reset_by epReset125);
   Reg#(Bool)      msix_enable        <- mkReg(False, clocked_by epClock125, reset_by epReset125);
   Reg#(Bool)      msix_masked        <- mkReg(True,  clocked_by epClock125, reset_by epReset125);

   (* fire_when_enabled, no_implicit_conditions *)
   rule cross_config_values;
      max_rd_req_cr  <= max_read_req_bytes_250;
      max_payload_cr <= max_payload_bytes_250;
      rcb_cr         <= read_completion_boundary_250;
      msix_enable_cr <= msix_enable_250;
      msix_masked_cr <= msix_masked_250;
   endrule

   (* fire_when_enabled, no_implicit_conditions *)
   rule register_config_values;
      max_read_req_bytes <= max_rd_req_cr.crossed();
      max_payload_bytes  <= max_payload_cr.crossed();
      rcb_mask           <= (rcb_cr.crossed() == 64) ? 7'h3f : 7'h7f;
      msix_enable        <= msix_enable_cr.crossed();
      msix_masked        <= msix_masked_cr.crossed();
   endrule

   // setup PCIe interrupt for MSI-X
   // this rule executes in the epClock250 domain
   (* fire_when_enabled, no_implicit_conditions *)
   rule intr_ifc_ctl;
      _ep.cfg_interrupt.di('0);      // tied off for MSI-X
      _ep.cfg_interrupt.assrt('0);  // tied off for MSI-X
      _ep.cfg_interrupt.req(0);      // tied off for MSI-X
   endrule: intr_ifc_ctl

   // Build the PCIe-to-NoC bridge
   PcieToAxiBridge#(BPB)  bridge <- mkPcieToAxiBridge_4( contentId
						       , my_id
						       , max_read_req_bytes
						       , max_payload_bytes
						       , rcb_mask
						       , msix_enable
						       , msix_masked
						       , False // no MSI, only MSI-X
						       , clocked_by epClock125, reset_by epReset125
						       );
   mkConnectionWithClocks(_ep.trn_rx, tpl_2(bridge.tlps), epClock250, epReset250, epClock125, epReset125);
   mkConnectionWithClocks(_ep.trn_tx, tpl_1(bridge.tlps), epClock250, epReset250, epClock125, epReset125);

   //mkConnection(_dut.noc_src,bridge.noc);
   mkTieOff(bridge.noc);

   //SyncFIFOIfc#(MemoryRequest#(32,256)) fMemReq <- mkSyncFIFO(1, clk, rst_n, ddr3clk);
   //SyncFIFOIfc#(MemoryResponse#(256))   fMemResp <- mkSyncFIFO(1, ddr3clk, ddr3rstn, clk);

   //let memclient = interface Client;
   //		      interface request  = toGet(fMemReq);
   //		      interface response = toPut(fMemResp);
   //		   endinterface;

   //mkConnection( memclient, ddr3_ctrl.user, clocked_by ddr3clk, reset_by ddr3rstn );

   interface pcie     = _ep.pcie;
   //interface ddr3     = ddr3_ctrl.ddr3;
   interface portal0  = bridge.portal0;
   interface slave    = bridge.slave;
   interface trace    = bridge.trace;
   interface numPortals = bridge.numPortals;
   interface ReadOnly pciId;
      method PciId _read();
         return my_id;
      endmethod
   endinterface
   interface clock250 = epClock250;
   interface reset250 = epReset250;
   interface clock125 = epClock125;
   interface reset125 = epReset125;

   method Bool isLinkUp        = link_is_up;
//   method Bool isCalibrated  = ddr3_ctrl.user.init_done;
   method Action interrupt     = bridge.interrupt;

endmodule: mkX7PcieBridge

typedef (function Module#(PortalDmaTop) mkPortalDmaTop()) MkPortalDmaTop;

(* no_default_clock, no_default_reset *)
module [Module] mkPcieDmaTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n,
			       Clock sys_clk_p,     Clock sys_clk_n,
			       Reset pci_sys_reset_n,
			       Bit#(64) contentId,
			       MkPortalDmaTop mkPortalDmaTop
			       )
   (VC707_FPGA);

   X7PcieBridgeIfc#(8) x7pcie <- mkX7PcieBridge( pci_sys_clk_p, pci_sys_clk_n, sys_clk_p, sys_clk_n, pci_sys_reset_n,
                                                 contentId );

   Reg#(Bool) interruptRequested <- mkReg(False, clocked_by x7pcie.clock125, reset_by x7pcie.reset125);

   // instantiate user portals
   let portalTop <- mkPortalDmaTop(clocked_by x7pcie.clock125, reset_by x7pcie.reset125);

   // connect them to PCIE
   mkConnection(x7pcie.portal0, portalTop.ctrl, clocked_by x7pcie.clock125, reset_by x7pcie.reset125);

   AxiSlaveEngine#(64,8) axiSlaveEngine <- mkAxiSlaveEngine(x7pcie.pciId(), clocked_by x7pcie.clock125, reset_by x7pcie.reset125);
   mkConnection(tpl_1(x7pcie.slave), tpl_2(axiSlaveEngine.tlps), clocked_by x7pcie.clock125, reset_by x7pcie.reset125);
   mkConnection(tpl_1(axiSlaveEngine.tlps), tpl_2(x7pcie.slave), clocked_by x7pcie.clock125, reset_by x7pcie.reset125);
   mkConnection(portalTop.m_axi, axiSlaveEngine.slave3, clocked_by x7pcie.clock125, reset_by x7pcie.reset125);

   rule requestInterrupt;
      if (portalTop.interrupt && !interruptRequested)
	 x7pcie.interrupt();
      interruptRequested <= portalTop.interrupt;
   endrule

   interface pcie = x7pcie.pcie;
   //interface ddr3 = x7pcie.ddr3;
   method leds = zeroExtend({  pack(x7pcie.isCalibrated)
			     , pack(True)
			     , pack(False)
			     , pack(x7pcie.isLinkUp)
			     });

endmodule: mkPcieDmaTop

endpackage: Xilinx7PcieBridge

// Copyright (c) 2008- 2009 Bluespec, Inc.  All rights reserved.
// $Revision$
// $Date$
// Copyright (c) 2013 Quanta Research Cambridge, Inc.

package Xilinx7PcieBridge;

// PCI-Express for Xilinx 7
// FPGAs.

import Clocks               :: *;
import Connectable          :: *;
import TieOff               :: *;
import DefaultValue         :: *;
import BUtils               :: *;
import Xilinx               :: *;
import XilinxPCIE           :: *;
import XilinxCells          :: *;
import GetPut               :: *;
import ClientServer         :: *;
import Memory               :: *;
import BlueNoC              :: *;
import PcieToAxiBridge      :: *;
import XbsvXilinx7Pcie      :: *;
//import XbsvXilinx7DDR3      :: *;
import AxiMasterSlave       :: *;
// from SceMiDefines
typedef 4 BPB;

// Interface wrapper for PCIE
interface X7PcieBridgeIfc#(numeric type lanes);
   interface PCIE_EXP#(lanes) pcie;
   (* always_ready *)
   method Bool isLinkUp();
   method Bool isCalibrated();
   (* always_ready *)
   method Action interrupt();
   interface Clock clock250;
   interface Reset reset250;
   interface Clock clock125;
   interface Reset reset125;
   (* prefix = "" *)
   //interface DDR3_Pins_X7      ddr3;
   interface Axi3Master#(32,32,4,12) portal0;
   interface GetPut#(TLPData#(16)) slave; // to the axi slave engine
   interface Put#(TimestampedTlpData) trace;
   interface Reg#(Bit#(4)) numPortals;
   interface ReadOnly#(PciId) pciId;
endinterface

// This module builds the transactor hierarchy, the clock
// generation logic and the PCIE-to-port logic.
(* no_default_clock, no_default_reset *)
module mkX7PcieBridge#( Clock pci_sys_clk_p, Clock pci_sys_clk_n
		       , Clock sys_clk_p,    Clock sys_clk_n
		       , Reset pci_sys_reset
                       , Bit#(64) contentId
		       )
		       (X7PcieBridgeIfc#(lanes))
   provisos(Add#(1,_,lanes), XbsvXilinx7Pcie::SelectXilinx7PCIE#(lanes));

   if (valueOf(lanes) != 8)
      errorM("Only 8-lane PCIe is supported on X7.");

   Clock sys_clk_200mhz <- mkClockIBUFDS(sys_clk_p, sys_clk_n);

   ClockGenerator7Params clk_params = defaultValue();
   clk_params.clkin1_period     = 5.000;       // 200 MHz reference
   clk_params.clkin_buffer      = False;       // necessary buffer is instanced above
   clk_params.reset_stages      = 0;           // no sync on reset so input clock has pll as only load
   clk_params.clkfbout_mult_f   = 5.000;       // 1000 MHz VCO
   clk_params.clkout0_divide_f  = 10;          // unused clock
   clk_params.clkout1_divide    = 5;           // ddr3 reference clock (200 MHz)

   ClockGenerator7 clk_gen <- mkClockGenerator7(clk_params, clocked_by sys_clk_200mhz, reset_by pci_sys_reset);

   Clock clk = clk_gen.clkout0;
   Reset rst_n <- mkAsyncReset( 1, pci_sys_reset, clk );
   Reset ddr3ref_rst_n <- mkAsyncReset( 1, rst_n, clk_gen.clkout1 );

   //DDR3_Configure_X7 ddr3_cfg;
   //ddr3_cfg.num_reads_in_flight = 2;   // adjust as needed
   //ddr3_cfg.fast_train_sim_only = False; // adjust if simulating

   //DDR3_Controller_X7 ddr3_ctrl <- mkXilinx7DDR3Controller(ddr3_cfg, clocked_by clk_gen.clkout1, reset_by ddr3ref_rst_n);

   // ddr3_ctrl.user needs to connect to user logic and should use ddr3clk and ddr3rstn
   //Clock ddr3clk = ddr3_ctrl.user.clock;
   //Reset ddr3rstn = ddr3_ctrl.user.reset_n;

   // Buffer clocks and reset before they are used
   Clock pci_clk_100mhz_buf <- mkClockIBUFDS_GTE2(True, pci_sys_clk_p, pci_sys_clk_n);

   // Instantiate the PCIE endpoint
   XbsvXilinx7Pcie::PCIExpressX7#(lanes) _ep
       <- XbsvXilinx7Pcie::mkPCIExpressEndpointX7( defaultValue
						  , clocked_by pci_clk_100mhz_buf
						  , reset_by pci_sys_reset
						  );
   mkTieOff(_ep.cfg);
   mkTieOff(_ep.cfg_interrupt);
   mkTieOff(_ep.cfg_err);
   mkTieOff(_ep.pl);

   // note our PCI ID
   PciId my_id = PciId { bus:  _ep.cfg.bus_number()
		       , dev:  _ep.cfg.device_number()
		       , func: _ep.cfg.function_number()
		       };

   // The PCIE endpoint is processing TLPWord#(8)s at 250MHz.  The
   // NoC bridge is accepting TLPWord#(16)s at 125 MHz. The
   // connection between the endpoint and the NoC contains GearBox
   // instances for the TLPWord#(8)@250 <--> TLPWord#(16)@125
   // conversion.

   // The PCIe endpoint exports full (250MHz) and half-speed (125MHz) clocks
   Clock epClock250 = _ep.trn.clk;
   Reset epReset250 <- mkAsyncReset(4, _ep.trn.reset_n, epClock250);
   Clock epClock125 = _ep.trn.clk2;
   Reset epReset125 <- mkAsyncReset(4, _ep.trn.reset_n, epClock125);

   // Extract some status info from the PCIE endpoint. These values are
   // all in the epClock250 domain, so we have to cross them into the
   // epClock125 domain.

   Bool link_is_up = _ep.trn.link_up();
   UInt#(13) max_read_req_bytes_250       = 128 << _ep.cfg.dcommand[14:12];
   UInt#(13) max_payload_bytes_250        = 128 << _ep.cfg.dcommand[7:5];
   UInt#(8)  read_completion_boundary_250 = 64 << _ep.cfg.lcommand[3];
   Bool      msix_enable_250              = (_ep.cfg_interrupt.msixenable() == 1);
   Bool      msix_masked_250              = (_ep.cfg_interrupt.msixfm()     == 1);

   CrossingReg#(UInt#(13)) max_rd_req_cr  <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(UInt#(13)) max_payload_cr <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(UInt#(8))  rcb_cr         <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(Bool)      msix_enable_cr <- mkNullCrossingReg(epClock125, False, clocked_by epClock250, reset_by epReset250);
   CrossingReg#(Bool)      msix_masked_cr <- mkNullCrossingReg(epClock125, True,  clocked_by epClock250, reset_by epReset250);

   Reg#(UInt#(13)) max_read_req_bytes <- mkReg(128,   clocked_by epClock125, reset_by epReset125);
   Reg#(UInt#(13)) max_payload_bytes  <- mkReg(128,   clocked_by epClock125, reset_by epReset125);
   Reg#(Bit#(7))   rcb_mask           <- mkReg(7'h3f, clocked_by epClock125, reset_by epReset125);
   Reg#(Bool)      msix_enable        <- mkReg(False, clocked_by epClock125, reset_by epReset125);
   Reg#(Bool)      msix_masked        <- mkReg(True,  clocked_by epClock125, reset_by epReset125);

   (* fire_when_enabled, no_implicit_conditions *)
   rule cross_config_values;
      max_rd_req_cr  <= max_read_req_bytes_250;
      max_payload_cr <= max_payload_bytes_250;
      rcb_cr         <= read_completion_boundary_250;
      msix_enable_cr <= msix_enable_250;
      msix_masked_cr <= msix_masked_250;
   endrule

   (* fire_when_enabled, no_implicit_conditions *)
   rule register_config_values;
      max_read_req_bytes <= max_rd_req_cr.crossed();
      max_payload_bytes  <= max_payload_cr.crossed();
      rcb_mask           <= (rcb_cr.crossed() == 64) ? 7'h3f : 7'h7f;
      msix_enable        <= msix_enable_cr.crossed();
      msix_masked        <= msix_masked_cr.crossed();
   endrule

   // setup PCIe interrupt for MSI-X
   // this rule executes in the epClock250 domain
   (* fire_when_enabled, no_implicit_conditions *)
   rule intr_ifc_ctl;
      _ep.cfg_interrupt.di('0);      // tied off for MSI-X
      _ep.cfg_interrupt.assrt('0);  // tied off for MSI-X
      _ep.cfg_interrupt.req(0);      // tied off for MSI-X
   endrule: intr_ifc_ctl

   // Build the PCIe-to-NoC bridge
   PcieToAxiBridge#(BPB)  bridge <- mkPcieToAxiBridge_4( contentId
						       , my_id
						       , max_read_req_bytes
						       , max_payload_bytes
						       , rcb_mask
						       , msix_enable
						       , msix_masked
						       , False // no MSI, only MSI-X
						       , clocked_by epClock125, reset_by epReset125
						       );
   mkConnectionWithClocks(_ep.trn_rx, tpl_2(bridge.tlps), epClock250, epReset250, epClock125, epReset125);
   mkConnectionWithClocks(_ep.trn_tx, tpl_1(bridge.tlps), epClock250, epReset250, epClock125, epReset125);

   //mkConnection(_dut.noc_src,bridge.noc);
   mkTieOff(bridge.noc);

   //SyncFIFOIfc#(MemoryRequest#(32,256)) fMemReq <- mkSyncFIFO(1, clk, rst_n, ddr3clk);
   //SyncFIFOIfc#(MemoryResponse#(256))   fMemResp <- mkSyncFIFO(1, ddr3clk, ddr3rstn, clk);

   //let memclient = interface Client;
   //		      interface request  = toGet(fMemReq);
   //		      interface response = toPut(fMemResp);
   //		   endinterface;

   //mkConnection( memclient, ddr3_ctrl.user, clocked_by ddr3clk, reset_by ddr3rstn );

   interface pcie     = _ep.pcie;
   //interface ddr3     = ddr3_ctrl.ddr3;
   interface portal0  = bridge.portal0;
   interface slave    = bridge.slave;
   interface trace    = bridge.trace;
   interface numPortals = bridge.numPortals;
   interface ReadOnly pciId;
      method PciId _read();
         return my_id;
      endmethod
   endinterface
   interface clock250 = epClock250;
   interface reset250 = epReset250;
   interface clock125 = epClock125;
   interface reset125 = epReset125;

   method Bool isLinkUp        = link_is_up;
//   method Bool isCalibrated  = ddr3_ctrl.user.init_done;
   method Action interrupt     = bridge.interrupt;

endmodule: mkX7PcieBridge

endpackage: Xilinx7PcieBridge


CreditCounter.bsv: Actually rate-limit

CreditCounter.bsv: Actually rate-limit

If DRAM latency is too high and the cache is performing frequent writes,
it would be possible to overflow this counter, which means we don't rate
limit, the cache could erroneously believe it's safe to do I/O
accesses/cache refills/page table walks, and the cache would block due
to the guard on decr when it finally gets enough write responses back.
We should block the incr method to automatically stall the cache until
it receives a new write response.

// Copyright (c) 2015-2019 Bluespec, Inc., All Rights Reserved
// Author: Rishiyur S. Nikhil

// A "credit counter" which can be incremented and decremented concurrently.

package CreditCounter;

// ================================================================
// BSV library imports

import Cur_Cycle :: *;

// ================================================================
// Interface

interface CreditCounter_IFC #(numeric type w);
   // Current value of internal count
   method UInt #(w) value;

   // Increment internal count
   method Action incr;

   // Decrement internal count
   method Action decr;

   // Clear internal count to 0
   method Action clear;
endinterface

// ================================================================
// Module implementation
// Scheduling: value < incr < decr < clear

module mkCreditCounter (CreditCounter_IFC #(w));

   Reg #(UInt #(w)) crg [3] <- mkCReg (3, 0);

   method UInt #(w) value = crg [1];

   method Action incr if (crg [0] != maxBound);
      if (crg [0] == maxBound) begin
	 $display ("%0d: ERROR: CreditCounter: overflow", cur_cycle);
	 $finish (1);    // Assertion failure
      end
      crg [0] <= crg [0] + 1;
   endmethod

   method Action decr () if (crg [1] != 0);
      if (crg [1] == 0) begin
	 $display ("%0d: ERROR: CreditCounter: underflow", cur_cycle);
	 $finish (1);    // Assertion failure
      end
      crg [1] <= crg [1] - 1;
   endmethod

   method Action clear;
      crg [2] <= 0;
   endmethod
endmodule

// ================================================================

endpackage

// Copyright (c) 2015-2019 Bluespec, Inc., All Rights Reserved
// Author: Rishiyur S. Nikhil

// A "credit counter" which can be incremented and decremented concurrently.

package CreditCounter;

// ================================================================
// BSV library imports

import Cur_Cycle :: *;

// ================================================================
// Interface

interface CreditCounter_IFC #(numeric type w);
   // Current value of internal count
   method UInt #(w) value;

   // Increment internal count
   method Action incr;

   // Decrement internal count
   method Action decr;

   // Clear internal count to 0
   method Action clear;
endinterface

// ================================================================
// Module implementation
// Scheduling: value < incr < decr < clear

module mkCreditCounter (CreditCounter_IFC #(w));

   Reg #(UInt #(w)) crg [3] <- mkCReg (3, 0);

   method UInt #(w) value = crg [1];

   method Action incr;
      if (crg [0] == maxBound) begin
	 $display ("%0d: ERROR: CreditCounter: overflow", cur_cycle);
	 $finish (1);    // Assertion failure
      end
      crg [0] <= crg [0] + 1;
   endmethod

   method Action decr () if (crg [1] != 0);
      if (crg [1] == 0) begin
	 $display ("%0d: ERROR: CreditCounter: underflow", cur_cycle);
	 $finish (1);    // Assertion failure
      end
      crg [1] <= crg [1] - 1;
   endmethod

   method Action clear;
      crg [2] <= 0;
   endmethod
endmodule

// ================================================================

endpackage


fix arg mismatches

fix arg mismatches

// 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 FPCMult::*;
import Gearbox::*;
import Pipe::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import BRAM::*;
import BRAMFIFO::*;
import Vector::*;
import Clocks::*;
import DefaultValue::*;
import DDS::*;

/* note rf signal is 64 bits wide, 2 sample of complex fixed point(0,16)
 */
(* always_enabled *)
interface ChannelSelect;
   interface PipeIn#(Vector#(2, Complex#(Signal))) rfreq;
   interface PipeOut#(Complex#(Signal)) ifreq;
   method Action setCoeff(Bit#(11) addr, Complex#(FixedPoint#(2,23)) value);
endinterface

module mkChannelSelect#(Bit#(10) decimation)(ChannelSelect)
   provisos(Bits#(CoeffData, a__),
	    Bits#(ProductData, b__),
            Bits#(MulData, c__));
   BRAM_Configure cfg = defaultValue;
   cfg.memorySize = 1024;
   BRAM2Port#(Bit#(10), Complex#(FixedPoint#(2,23))) coeffRam0 <-
       mkBRAM2Server(cfg);
   BRAM2Port#(Bit#(10), Complex#(FixedPoint#(2,23))) coeffRam1 <-
       mkBRAM2Server(cfg);
   Reg#(Bit#(10)) filterPhase <- mkReg(?);
   FIFO#(Bit#(1)) delayFilterPhase <- mkSizedFIFO(3);  // length > bram read latency
   FIFOF#(Vector#(2, Complex#(Signal))) infifo <- mkFIFOF();
   FIFOF#(Complex#(Signal)) outfifo <- mkFIFOF();
   Vector#(2, FPCMult) mul <- replicateM(mkFPCMult());
   Vector#(2, Reg#(Complex#(Product))) accum <- replicateM(mkReg(?));
   Vector#(2, FIFO#(Complex#(Product))) accumout <- replicateM(mkFIFO());
   FIFO#(Complex#(Product)) ycombined <- mkFIFO();
   DDS dds <- mkDDS();
   FPCMult lo <- mkFPCMult();

   /* could do this with mkForkVector() but we don't need the extra FIFOs
      since everything is ready every cycle
    */
   rule duplicateSignal;
      let v = infifo.first;
      infifo.deq();
      mul[0].x.enq(v[0]);
      mul[1].x.enq(v[1]);
   endrule

   rule filter_phase;
      if (filterPhase == (decimation - 1))
	 begin
	    filterPhase <= 0;
	    delayFilterPhase.enq(1);
	 end
      else
	 begin
	    filterPhase <= filterPhase + 1;
	    delayFilterPhase.enq(0);
	 end
      coeffRam0.portB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: filterPhase, datain: ?});
      coeffRam1.portB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: filterPhase, datain: ?});
   endrule

   rule mulin;
      let c0 <- coeffRam0.portB.response.get();
      let c1 <- coeffRam1.portB.response.get();
      Bit#(1) phase = delayFilterPhase.first();
      delayFilterPhase.deq();
      mul[0].a.enq(CoeffData{a: c0, filterPhase: phase});
      mul[1].a.enq(CoeffData{a: c1, filterPhase: phase});
   endrule

   rule muloutaccumin0;
      ProductData m = mul[0].y.first();
      mul[0].y.deq();
      if (m.filterPhase == 1)
	 begin
	    accum[0] <= m.y;
	    accumout[0].enq(accum[0]);
	 end
      else
	 begin
	    accum[0] <= accum[0] + m.y;
	 end
   endrule

   rule muloutaccumin1;
      ProductData m = mul[1].y.first();
      mul[1].y.deq();
      if (m.filterPhase == 1)
	 begin
	    accum[1] <= m.y;
	    accumout[1].enq(accum[1]);
	 end
      else
	 begin
	    accum[1] <= accum[1] + m.y;
	 end
   endrule

   rule accumoutcombinein;
      Complex#(Product) a0 = accumout[0].first();
      Complex#(Product) a1 = accumout[1].first();
      ycombined.enq(a0 + a1);
      accumout[0].deq();
      accumout[1].deq();
   endrule

   rule combineoutloin;
      Complex#(Product) yin = ycombined.first();
      FixedPoint#(2,16) yrel = fxptTruncate(yin.rel);
      FixedPoint#(2,16) yimg = fxptTruncate(yin.img);
      DDSOutType loin = dds.osc.first();
      dds.osc.deq();
      ycombined.deq();
      lo.x.enq(Complex{rel: yrel, img: yimg});
      lo.a.enq(CoeffData{a: loin, filterPhase: 0});
   endrule

   rule loout;
      Complex#(Product) ifc = lo.y.first().y;
      FixedPoint#(2,16) ifrel = fxptTruncate(ifc.rel);
      FixedPoint#(2,16) ifimg = fxptTruncate(ifc.img);
      outfifo.enq(Complex{rel: ifrel, img: ifimg});
      lo.y.deq();
   endrule

   interface PipeIn rfreq = toPipeIn(infifo);

   method Action setCoeff(Bit#(11) addr, Complex#(FixedPoint#(2,23)) value);
      Bit#(1) idx = addr[0];
      if (idx == 0)
	 coeffRam0.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: addr[10:1], datain: value});
      else
	 coeffRam1.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: addr[10:1], datain: value});
   endmethod

   interface PipeOut ifreq = toPipeOut(outfifo);

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 FPCMult::*;
import Gearbox::*;
import Pipe::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import BRAM::*;
import BRAMFIFO::*;
import Vector::*;
import Clocks::*;
import DefaultValue::*;
import DDS::*;

/* note rf signal is 64 bits wide, 2 sample of complex fixed point(0,16)
 */
(* always_enabled *)
interface ChannelSelect;
   interface PipeIn#(Vector#(2, Complex#(Signal))) rfreq;
   interface PipeOut#(Complex#(Signal)) ifreq;
   method Action setCoeff(Bit#(11) addr, Bit#(32) valre, Bit#(32) valim);
endinterface

module mkChannelSelect#(Bit#(10) decimation)(ChannelSelect)
   provisos(Bits#(CoeffData, a__),
	    Bits#(ProductData, b__),
            Bits#(MulData, c__));
   BRAM_Configure cfg = defaultValue;
   cfg.memorySize = 1024;
   BRAM2Port#(Bit#(10), Complex#(FixedPoint#(2,23))) coeffRam0 <-
       mkBRAM2Server(cfg);
   BRAM2Port#(Bit#(10), Complex#(FixedPoint#(2,23))) coeffRam1 <-
       mkBRAM2Server(cfg);
   Reg#(Bit#(10)) filterPhase <- mkReg(?);
   FIFO#(Bit#(1)) delayFilterPhase <- mkSizedFIFO(3);  // length > bram read latency
   FIFOF#(Vector#(2, Complex#(Signal))) infifo <- mkFIFOF();
   FIFOF#(Complex#(Signal)) outfifo <- mkFIFOF();
   Vector#(2, FPCMult) mul <- replicateM(mkFPCMult());
   Vector#(2, Reg#(Complex#(Product))) accum <- replicateM(mkReg(?));
   Vector#(2, FIFO#(Complex#(Product))) accumout <- replicateM(mkFIFO());
   FIFO#(Complex#(Product)) ycombined <- mkFIFO();
   DDS dds <- mkDDS();
   FPCMult lo <- mkFPCMult();

   /* could do this with mkForkVector() but we don't need the extra FIFOs
      since everything is ready every cycle
    */
   rule duplicateSignal;
      let v = infifo.first;
      infifo.deq();
      mul[0].x.enq(v[0]);
      mul[1].x.enq(v[1]);
   endrule

   rule filter_phase;
      if (filterPhase == (decimation - 1))
	 begin
	    filterPhase <= 0;
	    delayFilterPhase.enq(1);
	 end
      else
	 begin
	    filterPhase <= filterPhase + 1;
	    delayFilterPhase.enq(0);
	 end
      coeffRam0.portB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: filterPhase, datain: ?});
      coeffRam1.portB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: filterPhase, datain: ?});
   endrule

   rule mulin;
      let c0 <- coeffRam0.portB.response.get();
      let c1 <- coeffRam1.portB.response.get();
      Bit#(1) phase = delayFilterPhase.first();
      delayFilterPhase.deq();
      mul[0].a.enq(CoeffData{a: c0, filterPhase: phase});
      mul[1].a.enq(CoeffData{a: c1, filterPhase: phase});
   endrule

   rule muloutaccumin0;
      ProductData m = mul[0].y.first();
      mul[0].y.deq();
      if (m.filterPhase == 1)
	 begin
	    accum[0] <= m.y;
	    accumout[0].enq(accum[0]);
	 end
      else
	 begin
	    accum[0] <= accum[0] + m.y;
	 end
   endrule

   rule muloutaccumin1;
      ProductData m = mul[1].y.first();
      mul[1].y.deq();
      if (m.filterPhase == 1)
	 begin
	    accum[1] <= m.y;
	    accumout[1].enq(accum[1]);
	 end
      else
	 begin
	    accum[1] <= accum[1] + m.y;
	 end
   endrule

   rule accumoutcombinein;
      Complex#(Product) a0 = accumout[0].first();
      Complex#(Product) a1 = accumout[1].first();
      accumout[0].deq();
      accumout[1].deq();
      ycombined.enq(a0 + a1);
      accumout[0].deq();
      accumout[1].deq();
   endrule

   rule combineoutloin;
      Complex#(Product) yin = ycombined.first();
      FixedPoint#(2,16) yrel = fxptTruncate(yin.rel);
      FixedPoint#(2,16) yimg = fxptTruncate(yin.img);
      DDSOutType loin = dds.osc.first();
      dds.osc.deq();
      ycombined.deq();
      lo.x.enq(Complex{rel: yrel, img: yimg});
      lo.a.enq(CoeffData{a: loin, filterPhase: 0});
   endrule

   rule loout;
      Complex#(Product) ifc = lo.y.first().y;
      FixedPoint#(2,16) ifrel = fxptTruncate(ifc.rel);
      FixedPoint#(2,16) ifimg = fxptTruncate(ifc.img);
      outfifo.enq(Complex{rel: ifrel, img: ifimg});
      lo.y.deq();
   endrule

   interface PipeIn rfreq = toPipeIn(infifo);

   method Action setCoeff(Bit#(10) addr, Complex#(FixedPoint#(2,23)) value);
      Bit#(1) idx = addr[0];
      if (idx == 0)
	 coeffRam0.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: addr[10:1], datain: value});
      else
	 coeffRam1.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: addr[10:1], datain: value});
   endmethod

   interface PipeOut ifreq = toPipeOut(outfifo);

endmodule


add FILL

add FILL

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

// types needed by both hardware and software
import GetPutF::*;

interface ServerF#(type intype, type outtype);
	  interface PutF#(intype) request;
	  interface GetF#(outtype) response;
endinterface

typedef enum {
 CmdNOP = 0,
 CmdCOPY = 1,
 CmdECHO = 2,
 CmdFILL = 3
   } OpCode deriving (Bits);

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

// types needed by both hardware and software
import GetPutF::*;

interface ServerF#(type intype, type outtype);
	  interface PutF#(intype) request;
	  interface GetF#(outtype) response;
endinterface

typedef enum {
 CmdNOP = 0,
 CmdCOPY = 1,
 CmdECHO = 2
   } OpCode deriving (Bits);


make it easier to have (* synthesis *) boundaries for mkSerialPortalMux and mkSerialPortalDemux

// 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 BuildVector::*;
import ClientServer::*;
import Connectable::*;
import DefaultValue::*;
import FIFOF::*;
import Gearbox::*;
import GetPut::*;
import MIMO::*;
import Probe::*;
import Vector::*;

import Pipe::*;
import Portal::*;
import MemTypes::*;
import ConnectalMemory::*;
import SharedMemoryFifo::*;

module mkSharedMemoryRequestPortal#(PipePortal#(numRequests, numIndications, 32) portal,
   function Bit#(16) messageSize(Bit#(16) methodNumber),
   Vector#(2, MemReadEngineServer#(64)) readEngine, Vector#(2, MemWriteEngineServer#(64)) writeEngine)(SharedMemoryPortal#(64));
   SharedMemoryPipeOut#(64,numRequests) pipeOut <- mkSharedMemoryPipeOut(readEngine, writeEngine);
   mapM_(uncurry(mkConnection), zip(pipeOut.data, portal.requests));

   interface SharedMemoryPortalConfig cfg = pipeOut.cfg;
endmodule

// adds a header word to each message out of an indication pipe
module mkFramedMessagePipe#(Integer portalNumber,
			    PipePortal#(numRequests,numIndications,32) pipePortal,
			    function Bit#(16) messageSize(Bit#(16) methodNumber),
			    Integer i)(PipeOut#(Bit#(32)));

   let pipeOut = pipePortal.indications[i];
   Bit#(16) messageBits = messageSize(fromInteger(i));
   Bit#(16) roundup = messageBits[4:0] == 0 ? 0 : 1;
   Bit#(16) numWords = (messageBits >> 5) + roundup;
   Bit#(16) totalWords = numWords + 1;
   Bit#(32) hdr = (fromInteger(portalNumber) << 24) | (fromInteger(i) << 16) | extend(numWords + 1);
   let sendHeader <- mkReg(True);
   Reg#(Bit#(16)) burstLenReg <- mkReg(0);
   return (interface PipeOut;
	      method Bit#(32) first() if (pipeOut.notEmpty());
	         if (sendHeader)
		    return hdr;
		 else
		    return pipeOut.first();
	      endmethod
	      method Action deq() if (pipeOut.notEmpty());
	         if (sendHeader) begin
		    sendHeader <= False;
		    burstLenReg <= numWords;
		 end
		 else begin
		    pipeOut.deq();
		    burstLenReg <= burstLenReg - 1;
		    if (burstLenReg == 1)
		       sendHeader <= True;
		 end
	      endmethod
	      method Bool notEmpty(); return pipeOut.notEmpty(); endmethod
	   endinterface);
endmodule

module mkSharedMemoryIndicationPortal#(PipePortal#(numRequests,numIndications,32) pipePortal,
				       function Bit#(16) messageSize(Bit#(16) methodNumber),
				       Vector#(2, MemReadEngineServer#(64)) readEngines, Vector#(2, MemWriteEngineServer#(64)) writeEngines)
   (SharedMemoryPortal#(64));

   Vector#(numIndications, PipeOut#(Bit#(32))) indicationPipes <- genWithM(mkFramedMessagePipe(0,pipePortal,messageSize));

   SharedMemoryPipeIn#(64) pipeIn <- mkSharedMemoryPipeIn(indicationPipes, readEngines, writeEngines);
   interface SharedMemoryPortalConfig cfg = pipeIn.cfg;
endmodule

interface SerialPortalDemux#(numeric type pipeCount);
   interface Vector#(pipeCount, PipeOut#(Bit#(32))) data;
   interface PipeIn#(Bit#(32))                      inputPipe;
endinterface
interface SerialPortalMux#(numeric type pipeCount);
   interface Vector#(pipeCount, PipeIn#(Bit#(32))) data;
   interface PipeOut#(Bit#(32))                    outputPipe;
endinterface

typedef enum {
   Idle,
   MessageHeader,
   MessageBody,
   Stop
   } SerialPortalState deriving (Bits,Eq);

typedef enum {
   Portal,
   Method
   } SerialPortalDemuxLevel deriving (Bits,Eq);

module mkSerialPortalDemux#(SerialPortalDemuxLevel portalDemux)(SerialPortalDemux#(pipeCount));
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(8))  selectorReg <- mkReg(0);
   Reg#(SerialPortalState) state <- mkReg(Idle);
   FIFOF#(Bit#(32)) inputFifo <- mkFIFOF();
   Vector#(pipeCount, FIFOF#(Bit#(32))) dataFifo <- replicateM(mkFIFOF);

   let verbose = False;

   rule idle if (state == Idle);
      state <= MessageHeader;
   endrule

   rule receiveMessageHeader if (state == MessageHeader);
      let hdr <- toGet(inputFifo).get();
      let selector = (portalDemux == Portal) ? hdr[31:24] : hdr[23:16];
      let messageWords = hdr[15:0];
      selectorReg <= selector;
      if (verbose)
         $display("receiveMessageHeader hdr=%x selector=%x messageWords=%d", hdr, selector, messageWords);
      if (portalDemux == Portal) // methodDemux need the header
         dataFifo[selector].enq(hdr);
      messageWordsReg <= messageWords - 1;
      if (messageWords == 1)
         state <= MessageHeader;
      else
         state <= MessageBody;
   endrule

   rule receiveMessage if (state == MessageBody);
      let data <- toGet(inputFifo).get();
      if (verbose)
         $display("receiveMessage data=%x messageWords=%d", data, messageWordsReg);
      if (selectorReg != 8'hFF)
         dataFifo[selectorReg].enq(data);
      messageWordsReg <= messageWordsReg - 1;
      if (messageWordsReg == 1)
         state <= MessageHeader;
   endrule

   interface data      = map(toPipeOut, dataFifo);
   interface inputPipe = toPipeIn(inputFifo);
endmodule

module mkSerialPortalMux(SerialPortalMux#(pipeCount));
   let clock <- exposeCurrentClock;
   let reset <- exposeCurrentReset;
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(16)) methodIdReg <- mkReg(0);
   Reg#(Bool) paddingReg <- mkReg(False);
   Reg#(SerialPortalState) state <- mkReg(Idle);
   Reg#(Bit#(32)) sglIdReg <- mkReg(0);

   function Bool fifoNotEmpty(FIFOF#(a) fifo); return fifo.notEmpty(); endfunction

   Vector#(pipeCount, FIFOF#(Bit#(32)))   inputFifos <- replicateM(mkFIFOF());
   Vector#(pipeCount, PipeOut#(Bit#(32))) pipes      = map(toPipeOut, inputFifos);
   Vector#(pipeCount, Bool)               readyBits  = map(fifoNotEmpty, inputFifos);
   Bool      interruptStatus = False;
   Bit#(16)  readyChannel = -1;
   FIFOF#(Bit#(32)) outputFifo <- mkFIFOF();

   let verbose = True;
   let stateProbe <- mkProbe();
   let messageWordsProbe <- mkProbe();
   let messageDataProbe <- mkProbe();

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

   rule idle if (state == Idle);
      state <= MessageHeader;
      stateProbe <= MessageHeader;
   endrule

   rule sendHeader if (state == MessageHeader && interruptStatus);
      Bit#(32) hdr <- toGet(inputFifos[readyChannel]).get();
      Bit#(16) totalWords = hdr[15:0];
      let messageWords = totalWords-1;
      messageWordsProbe <= messageWords;

      messageWordsReg <= messageWords;
      methodIdReg <= readyChannel;
      outputFifo.enq(hdr);
      state <= MessageBody;
      stateProbe <= MessageBody;
      messageDataProbe <= hdr;
   endrule

   rule sendMessage if (state == MessageBody);
      messageWordsReg <= messageWordsReg - 1;
      let v <- toGet(inputFifos[methodIdReg]).get();
      outputFifo.enq(v);
      $display("sendMessage v=%h messageWords=%d paddingReg=%d", v, messageWordsReg, paddingReg);
      if (messageWordsReg == 1) begin
         state <= MessageHeader;
	 stateProbe <= MessageHeader;
      end
      messageDataProbe <= v;
   endrule

   interface data       = map(toPipeIn, inputFifos);
   interface outputPipe = toPipeOut(outputFifo);
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 BuildVector::*;
import ClientServer::*;
import Connectable::*;
import DefaultValue::*;
import FIFOF::*;
import Gearbox::*;
import GetPut::*;
import MIMO::*;
import Probe::*;
import Vector::*;

import Pipe::*;
import Portal::*;
import MemTypes::*;
import ConnectalMemory::*;
import SharedMemoryFifo::*;

module mkSharedMemoryRequestPortal#(PipePortal#(numRequests, numIndications, 32) portal,
   function Bit#(16) messageSize(Bit#(16) methodNumber),
   Vector#(2, MemReadEngineServer#(64)) readEngine, Vector#(2, MemWriteEngineServer#(64)) writeEngine)(SharedMemoryPortal#(64));
   SharedMemoryPipeOut#(64,numRequests) pipeOut <- mkSharedMemoryPipeOut(readEngine, writeEngine);
   mapM_(uncurry(mkConnection), zip(pipeOut.data, portal.requests));

   interface SharedMemoryPortalConfig cfg = pipeOut.cfg;
endmodule

// adds a header word to each message out of an indication pipe
module mkFramedMessagePipe#(Integer portalNumber,
			    PipePortal#(numRequests,numIndications,32) pipePortal,
			    function Bit#(16) messageSize(Bit#(16) methodNumber),
			    Integer i)(PipeOut#(Bit#(32)));

   let pipeOut = pipePortal.indications[i];
   Bit#(16) messageBits = messageSize(fromInteger(i));
   Bit#(16) roundup = messageBits[4:0] == 0 ? 0 : 1;
   Bit#(16) numWords = (messageBits >> 5) + roundup;
   Bit#(16) totalWords = numWords + 1;
   Bit#(32) hdr = (fromInteger(portalNumber) << 24) | (fromInteger(i) << 16) | extend(numWords + 1);
   let sendHeader <- mkReg(True);
   Reg#(Bit#(16)) burstLenReg <- mkReg(0);
   return (interface PipeOut;
	      method Bit#(32) first() if (pipeOut.notEmpty());
	         if (sendHeader)
		    return hdr;
		 else
		    return pipeOut.first();
	      endmethod
	      method Action deq() if (pipeOut.notEmpty());
	         if (sendHeader) begin
		    sendHeader <= False;
		    burstLenReg <= numWords;
		 end
		 else begin
		    pipeOut.deq();
		    burstLenReg <= burstLenReg - 1;
		    if (burstLenReg == 1)
		       sendHeader <= True;
		 end
	      endmethod
	      method Bool notEmpty(); return pipeOut.notEmpty(); endmethod
	   endinterface);
endmodule

module mkSharedMemoryIndicationPortal#(PipePortal#(numRequests,numIndications,32) pipePortal,
				       function Bit#(16) messageSize(Bit#(16) methodNumber),
				       Vector#(2, MemReadEngineServer#(64)) readEngines, Vector#(2, MemWriteEngineServer#(64)) writeEngines)
   (SharedMemoryPortal#(64));

   Vector#(numIndications, PipeOut#(Bit#(32))) indicationPipes <- genWithM(mkFramedMessagePipe(0,pipePortal,messageSize));

   SharedMemoryPipeIn#(64) pipeIn <- mkSharedMemoryPipeIn(indicationPipes, readEngines, writeEngines);
   interface SharedMemoryPortalConfig cfg = pipeIn.cfg;
endmodule

interface SerialPortalDemux#(numeric type pipeCount);
   interface Vector#(pipeCount, PipeOut#(Bit#(32))) data;
   interface PipeIn#(Bit#(32))                      inputPipe;
endinterface

typedef enum {
   Idle,
   MessageHeader,
   MessageBody,
   Stop
   } SerialPortalState deriving (Bits,Eq);

typedef enum {
   Portal,
   Method
   } SerialPortalDemuxLevel deriving (Bits,Eq);

module mkSerialPortalDemux#(SerialPortalDemuxLevel portalDemux)(SerialPortalDemux#(pipeCount));
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(8))  selectorReg <- mkReg(0);
   Reg#(SerialPortalState) state <- mkReg(Idle);
   FIFOF#(Bit#(32)) inputFifo <- mkFIFOF();
   Vector#(pipeCount, FIFOF#(Bit#(32))) dataFifo <- replicateM(mkFIFOF);

   let verbose = False;

   rule idle if (state == Idle);
      state <= MessageHeader;
   endrule

   rule receiveMessageHeader if (state == MessageHeader);
      let hdr <- toGet(inputFifo).get();
      let selector = (portalDemux == Portal) ? hdr[31:24] : hdr[23:16];
      let messageWords = hdr[15:0];
      selectorReg <= selector;
      if (verbose)
         $display("receiveMessageHeader hdr=%x selector=%x messageWords=%d", hdr, selector, messageWords);
      if (portalDemux == Portal) // methodDemux need the header
         dataFifo[selector].enq(hdr);
      messageWordsReg <= messageWords - 1;
      if (messageWords == 1)
         state <= MessageHeader;
      else
         state <= MessageBody;
   endrule

   rule receiveMessage if (state == MessageBody);
      let data <- toGet(inputFifo).get();
      if (verbose)
         $display("receiveMessage data=%x messageWords=%d", data, messageWordsReg);
      if (selectorReg != 8'hFF)
         dataFifo[selectorReg].enq(data);
      messageWordsReg <= messageWordsReg - 1;
      if (messageWordsReg == 1)
         state <= MessageHeader;
   endrule

   interface data      = map(toPipeOut, dataFifo);
   interface inputPipe = toPipeIn(inputFifo);
endmodule

module mkSerialPortalMux#(Vector#(numIndications, PipeOut#(Bit#(32))) pipes)(PipeOut#(Bit#(32)));
   let clock <- exposeCurrentClock;
   let reset <- exposeCurrentReset;
   Reg#(Bit#(16)) messageWordsReg <- mkReg(0);
   Reg#(Bit#(16)) methodIdReg <- mkReg(0);
   Reg#(Bool) paddingReg <- mkReg(False);
   Reg#(SerialPortalState) state <- mkReg(Idle);
   Reg#(Bit#(32)) sglIdReg <- mkReg(0);
   function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
   Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, pipes);
   Bool      interruptStatus = False;
   Bit#(16)  readyChannel = -1;
   FIFOF#(Bit#(32)) outputFifo <- mkFIFOF();

   let verbose = True;
   let stateProbe <- mkProbe();
   let messageWordsProbe <- mkProbe();
   let messageDataProbe <- mkProbe();

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

   rule idle if (state == Idle);
      state <= MessageHeader;
      stateProbe <= MessageHeader;
   endrule

   rule sendHeader if (state == MessageHeader && interruptStatus);
      Bit#(32) hdr <- toGet(pipes[readyChannel]).get();
      Bit#(16) totalWords = hdr[15:0];
      let messageWords = totalWords-1;
      messageWordsProbe <= messageWords;

      messageWordsReg <= messageWords;
      methodIdReg <= readyChannel;
      outputFifo.enq(hdr);
      state <= MessageBody;
      stateProbe <= MessageBody;
      messageDataProbe <= hdr;
   endrule

   rule sendMessage if (state == MessageBody);
      messageWordsReg <= messageWordsReg - 1;
      let v <- toGet(pipes[methodIdReg]).get();
      outputFifo.enq(v);
      $display("sendMessage v=%h messageWords=%d paddingReg=%d", v, messageWordsReg, paddingReg);
      if (messageWordsReg == 1) begin
         state <= MessageHeader;
	 stateProbe <= MessageHeader;
      end
      messageDataProbe <= v;
   endrule

   return toPipeOut(outputFifo);
endmodule