added SimpleMMU to sharedbuffMMU

added SimpleMMU to sharedbuffMMU

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

// Modified Connectal MMU with smaller pages
// BSV Libraries
import RegFile::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import Connectable::*;
import BRAMFIFO::*;
import BRAM::*;
import Probe::*;

import ConnectalBram::*;
import MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;
import Ethernet::*;
import PrintTrace::*;
`include "ConnectalProjectConfig.bsv"

typedef 8 SGListPageShift0;
typedef 12 SGListPageShift4;
typedef 16 SGListPageShift8;
typedef 20 SGListPageShift12;
typedef Bit#(TLog#(MaxNumSGLists)) RegionsIdx;

typedef 8 IndexWidth;

typedef struct {
   RegionsIdx           id;
   Bit#(MemOffsetSize) off;
} AddrTransRequest deriving (Eq,Bits,FShow);

typedef struct {
   DmaErrorType    error;
   Bit#(addrWidth) physAddr;
} AddrTransResponse#(numeric type addrWidth) deriving (Eq,Bits,FShow);

interface MMU#(numeric type addrWidth);
   interface MMURequest request;
   interface Vector#(2,Server#(AddrTransRequest,AddrTransResponse#(addrWidth))) addr;
endinterface

module mkSimpleMMU#(Bit#(4) mmuid, Bool hostMapped, MMUIndication mmuIndication)(MMU#(addrWidth))
   provisos (Add#(b__,8,addrWidth)
	     ,Add#(c__,24,addrWidth)
	     ,Bits#(AddrTransResponse#(addrWidth),d__)
	     );
   Vector#(2,FIFOF#(AddrTransRequest)) reqFifos <- replicateM(mkFIFOF());
   Vector#(2,FIFOF#(AddrTransResponse#(addrWidth))) respFifos <- replicateM(mkFIFOF());

   let probePhysAddr <- mkProbe();

   for (Integer i = 0; i < 2; i = i + 1) begin
      rule rl_process_req;
	 let req <- toGet(reqFifos[i]).get();
	 Bit#(addrWidth) physAddr = extend(req.off[23:0]);
	 physAddr[31:24] = extend(req.id);
	 probePhysAddr <= physAddr;
	 respFifos[i].enq(AddrTransResponse { error: DmaErrorNone, physAddr: physAddr });
      endrule
   end

   function Server#(AddrTransRequest,AddrTransResponse#(addrWidth)) genServer(Integer i);
      return (interface Server#(AddrTransRequest,AddrTransResponse#(addrWidth));
		 interface request = toPut(reqFifos[i]);
	 interface response = toGet(respFifos[i]);
	 endinterface);
   endfunction

   interface MMURequest request;
      method Action idRequest(SpecialTypeForSendingFd fd);
	 mmuIndication.idResponse(0);
      endmethod
      method Action idReturn(Bit#(32) sglId);
	 mmuIndication.idResponse(sglId);
      endmethod
      method Action region(Bit#(32) pointer, Bit#(64) barr12, Bit#(32) index12, Bit#(64) barr8, Bit#(32) index8, Bit#(64) barr4, Bit#(32) index4, Bit#(64) barr0, Bit#(32) index0);
	 mmuIndication.configResp(extend(pointer));
      endmethod
      method Action sglist(Bit#(32) pointer, Bit#(32) pointerIndex, Bit#(64) addr,  Bit#(32) len);
	 // no response
      endmethod
      method Action setInterface(Bit#(32) interfaceId, Bit#(32) sglId);
	 /* this method is only implemented in s/w responders */
      endmethod
   endinterface
   interface Vector addr = genWith(genServer);
endmodule

typedef struct {
   DmaErrorType error;
   Bit#(3) pageSize;
   Bit#(SGListPageShift12) value;
} Offset deriving (Eq,Bits,FShow);

typedef Bit#(TSub#(MemOffsetSize,SGListPageShift0)) Page0;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift4)) Page4;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift8)) Page8;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift12)) Page12;

typedef struct {
   Bit#(TSub#(MemOffsetSize,SGListPageShift0)) barrier;
   Bit#(IndexWidth) idxOffset;
   } SingleRegion deriving (Eq,Bits,FShow);

typedef struct {
   SingleRegion reg12;
   SingleRegion reg8;
   SingleRegion reg4;
   SingleRegion reg0;
   } Region deriving (Eq,Bits,FShow);

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

typedef struct {
   Bool cond12;
   Bool cond8;
   Bool cond4;
   Bool cond0;
   Bit#(IndexWidth) idxOffset12;
   Bit#(IndexWidth) idxOffset8;
   Bit#(IndexWidth) idxOffset4;
   Bit#(IndexWidth) idxOffset0;
   AddrTransRequest req;
   } Stage3Params deriving (Bits);

typedef struct {
   Offset off;
   Bit#(IndexWidth) pbase;
   Bit#(IndexWidth) idxOffset;
   PktId ptr;
   } Stage4Params deriving (Bits);

// the address translation servers (addr[0], addr[1]) have a latency of 8 and are fully pipelined
module mkSharedBuffMMU#(Integer iid, MMUIndication mmuIndication)(MMU#(addrWidth))
   provisos(Log#(MaxNumSGLists, listIdxSize)
	    ,Add#(listIdxSize,8, entryIdxSize)
	    ,Add#(a__,addrWidth,MemOffsetSize)
     );

   let verbose = True;
   TagGen#(MaxNumSGLists) sglId_gen <- mkTagGen();
   rule complete_sglId_gen;
      let __x <- sglId_gen.complete;
   endrule

   Reg#(Bit#(32)) cycle <- mkReg(0);
   rule every1 if (verbose);
      cycle <= cycle + 1;
   endrule

   // stage 0 (latency == 1)
   Vector#(2, FIFO#(AddrTransRequest)) incomingReqs <- replicateM(mkFIFO);

   // stage 1 (latency == 2)
   BRAM_Configure bramConfig = defaultValue;
   bramConfig.latency        = 2;
   BRAM2Port#(RegionsIdx, Maybe#(Region)) regall <- ConnectalBram::mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(AddrTransRequest))          reqs0 <- replicateM(mkSizedFIFOF(3));

   // stage 2 (latency == 1)
   Vector#(2, FIFOF#(Stage3Params)) stage3Params <- replicateM(mkFIFOF);

   // stage 3 (latency == 1)
   Vector#(2, FIFOF#(Stage4Params)) stage4Params <- replicateM(mkFIFOF);

   // stage 4 (latency == 2)
   BRAM2Port#(Bit#(entryIdxSize),Page0) pages <- ConnectalBram::mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(Offset))           offs1 <- replicateM(mkSizedFIFOF(3));

   // stage 4 (latnecy == 1)
   Vector#(2,FIFOF#(AddrTransResponse#(addrWidth))) pageResponseFifos <- replicateM(mkFIFOF);

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   Vector#(2,FIFO#(DmaError)) dmaErrorFifos <- replicateM(mkFIFO());
   Rules rs = emptyRules;
   for (Integer i = 0; i < 2; i = i + 1) begin
      Rules r =
         rules
            rule connectDmaError;
               let v <- toGet(dmaErrorFifos[i]).get;
               dmaErrorFifo.enq(v);
            endrule
         endrules;
      rs = rJoinDescendingUrgency(rs, r);
   end

   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      mmuIndication.error(extend(pack(error.errorType)), error.pref, extend(error.off), fromInteger(iid));
   endrule

   let page_shift0 = fromInteger(valueOf(SGListPageShift0));
   let page_shift4 = fromInteger(valueOf(SGListPageShift4));
   let page_shift8 = fromInteger(valueOf(SGListPageShift8));
   let page_shift12 = fromInteger(valueOf(SGListPageShift12));

   function BRAMServer#(a,b) portsel(BRAM2Port#(a,b) x, Integer i);
      if(i==0) return x.portA;
      else return x.portB;
   endfunction

   for (Integer i = 0; i < 2; i=i+1) begin
      rule stage1;  // first read in the address cutoff values between regions
	 AddrTransRequest req <- toGet(incomingReqs[i]).get();
	 portsel(regall, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
            address:truncate(req.id), datain:?});
	 reqs0[i].enq(req);
      endrule

      // pipeline the address lookup
      rule stage2; // Now compare address cutoffs with requested offset
	 AddrTransRequest req <- toGet(reqs0[i]).get;
	 Maybe#(Region) m_regionall <- portsel(regall,i).response.get;

	 case (m_regionall) matches
	    tagged Valid .regionall: begin
               Page0 off0 = truncate(req.off >> valueOf(SGListPageShift0));
               Page4 off4 = truncate(req.off >> valueOf(SGListPageShift4));
               Page8 off8 = truncate(req.off >> valueOf(SGListPageShift8));
               Page12 off12 = truncate(req.off >> valueOf(SGListPageShift12));
	       let cond12 = off12 < truncate(regionall.reg12.barrier);
	       let cond8 = off8 < truncate(regionall.reg8.barrier);
	       let cond4 = off4 < truncate(regionall.reg4.barrier);
	       let cond0 = off0 < regionall.reg0.barrier;

	       if (verbose) $display("mkMMU::stage2: id=%d off=%h (%h %h %h) (%h %h %h)", req.id, req.off,
				     regionall.reg8.barrier, regionall.reg4.barrier, regionall.reg0.barrier,
				     off8, off4, off0);

	       stage3Params[i].enq(Stage3Params {cond12: cond12, cond8: cond8, cond4: cond4, cond0: cond0,
						 idxOffset12: regionall.reg12.idxOffset,idxOffset8: regionall.reg8.idxOffset,
						 idxOffset4: regionall.reg4.idxOffset, idxOffset0: regionall.reg0.idxOffset,
						 req: req });
	    end
	    tagged Invalid:
	       dmaErrorFifos[0].enq(DmaError { errorType: DmaErrorSGLIdInvalid, pref: extend(req.id), off:req.off });
	 endcase
      endrule
      rule stage3; // Based on results of comparision, select a region, putting it into 'o.pageSize'.  idxOffset holds offset in sglist table of relevant entry
	 let params <- toGet(stage3Params[i]).get();
	 AddrTransRequest req = params.req;
	 Offset o = Offset{pageSize: 0, value: truncate(req.off)};
	 Bit#(IndexWidth) pbase = 0;
	 Bit#(IndexWidth) idxOffset = 0;

	 if (params.cond12) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 4;
	    pbase = truncate(req.off>>page_shift12);
	    idxOffset = params.idxOffset12;
	 end
	 else if (params.cond8) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 3;
	    pbase = truncate(req.off>>page_shift8);
	    idxOffset = params.idxOffset8;
	 end
	 else if (params.cond4) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 2;
	    pbase = truncate(req.off>>page_shift4);
	    idxOffset = params.idxOffset4;
	 end
	 else if (params.cond0) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 1;
	    pbase = truncate(req.off>>page_shift0);
	    idxOffset = params.idxOffset0;
	 end
	 stage4Params[i].enq(Stage4Params { off: o, pbase: pbase, idxOffset: idxOffset, ptr: req.id });
      endrule
      (* descending_urgency = "stage2, stage4" *)
      rule stage4; // Read relevant sglist entry
	 let params <- toGet(stage4Params[i]).get();
	 let off = params.off;
	 let pbase = params.pbase;
	 let idxOffset = params.idxOffset;
	 let ptr = params.ptr;
	 Bit#(IndexWidth) p = pbase + idxOffset;
	 if (off.pageSize == 0) begin
	    if (verbose) $display("mkMMU::addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaErrorFifos[1].enq(DmaError { errorType: DmaErrorOffsetOutOfRange, pref: extend(ptr), off:extend(off.value) });
	 end
	 else begin
	    if (verbose) $display("mkMMU::pages[%d].read %h", i, {ptr,p});
	    portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
						      address:{ptr,p}, datain:?});
	    offs1[i].enq(off);
	 end
      endrule
      rule stage5; // Concatenate page base address from sglist entry with LSB offset bits from request and return
	 Page0 page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs1[i]).get();
	 if (verbose) $display("mkMMU::p ages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(MemOffsetSize) rv = ?;
	 Page4 b4 = truncate(page);
	 Page8 b8 = truncate(page);
	 Page12 b12 = truncate(page);
	 case (offset.pageSize)
	    1: rv = {page,truncate(offset.value)};
	    2: rv = {b4,truncate(offset.value)};
	    3: rv = {b8,truncate(offset.value)};
	    4: rv = {b12,truncate(offset.value)};
	 endcase
	 pageResponseFifos[i].enq(AddrTransResponse { error: DmaErrorNone, physAddr: truncate(rv) });
      endrule
   end

   FIFO#(PktId) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      let ptr <- toGet(configRespFifo).get();
      mmuIndication.configResp(extend(ptr));
   endrule

   function Server#(AddrTransRequest,AddrTransResponse#(addrWidth)) addrServer(Integer i);
   return
      (interface Server#(AddrTransRequest,Bit#(addrWidth));
	  interface Put request;
	     method Action put(AddrTransRequest req);
		incomingReqs[i].enq(req);
                if (verbose) $display("SharedBuffMMU:: %d: incomingReq ", cycle, fshow(req));
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(AddrTransResponse#(addrWidth)) get();
		let rv <- toGet(pageResponseFifos[i]).get();
`ifdef SIMULATION
		rv.physAddr = rv.physAddr | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
                if (verbose) $display("SharedBuffMMU:: %d: pageResponse ", cycle, fshow(rv));
		return rv;
	     endmethod
	  endinterface
       endinterface);
   endfunction

   interface MMURequest request;
   method Action idRequest(Bit#(32) fd);
      let nextId <- sglId_gen.getTag;
      let resp = (fromInteger(iid) << 16) | extend(nextId);
      if (verbose) $display("mkMMU::idRequest %d", fd);
      mmuIndication.idResponse(resp);
   endmethod
   method Action idReturn(Bit#(32) sglId);
      sglId_gen.returnTag(truncate(sglId));
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False, address: truncate(sglId), datain: tagged Invalid });
      if (verbose) $display("idReturn %d", sglId);
   endmethod
   method Action region(Bit#(32) pointer, Bit#(64) barr12, Bit#(32) index12, Bit#(64) barr8, Bit#(32) index8, Bit#(64) barr4, Bit#(32) index4, Bit#(64) barr0, Bit#(32) index0);
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False,
          address: truncate(pointer), datain: tagged Valid Region{
             reg12: SingleRegion{barrier: truncate(barr12), idxOffset: truncate(index12)},
             reg8: SingleRegion{barrier: truncate(barr8), idxOffset: truncate(index8)},
             reg4: SingleRegion{barrier: truncate(barr4), idxOffset: truncate(index4)},
             reg0: SingleRegion{barrier: truncate(barr0), idxOffset: truncate(index0)}} });
      if (verbose) $display("mkMMU::region pointer=%d barr12=%h barr8=%h barr4=%h barr0=%h", pointer, barr12, barr8, barr4, barr0);
      configRespFifo.enq(truncate(pointer));
   endmethod

   method Action sglist(Bit#(32) pointer, Bit#(32) pointerIndex, Bit#(64) addr,  Bit#(32) len);
         if (fromInteger(iid) != pointer[31:16]) begin
	    $display("mkMMU::sglist ERROR");
	    $finish();
	 end
         Bit#(IndexWidth) ind = truncate(pointerIndex);
	 portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False,
             address:{truncate(pointer),ind}, datain:truncate(addr)});
         if (verbose) $display("mkMMU::sglist pointer=%d pointerIndex=%d addr=%d len=%d", pointer, pointerIndex, addr, len);
   endmethod
   method Action setInterface(Bit#(32) interfaceId, Bit#(32) sglId);
       /* this method is only implemented in s/w responders */
   endmethod
   endinterface
   interface addr = genWith(addrServer);

endmodule

interface ArbitratedMMU#(numeric type addrWidth, numeric type numServers);
   interface Vector#(numServers,Server#(AddrTransRequest,AddrTransResponse#(addrWidth))) servers;
endinterface

module mkArbitratedMMU#(Server#(AddrTransRequest,AddrTransResponse#(addrWidth)) server) (ArbitratedMMU#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(9);
   Reg#(Bit#(TLog#(numServers))) arb <- mkReg(0);

   // this is a very crude arbiter.  something more sophisticated may be required (mdk)
   rule inc_arb;
      arb <= arb+1;
   endrule

   function Server#(AddrTransRequest,AddrTransResponse#(addrWidth)) arbitratedServer(Integer i);
   return
      (interface Server#(AddrTransRequest,AddrTransResponse#(addrWidth));
	  interface Put request;
	     method Action put(AddrTransRequest req) if (arb == fromInteger(i));
		tokFifo.enq(fromInteger(i));
		server.request.put(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(AddrTransResponse#(addrWidth)) get() if (tokFifo.first == fromInteger(i));
		tokFifo.deq;
		let rv <- server.response.get;
		return rv;
	     endmethod
	  endinterface
       endinterface);
   endfunction

   interface servers = genWith(arbitratedServer);

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.

// Modified Connectal MMU with smaller pages
// BSV Libraries
import RegFile::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import Connectable::*;
import BRAMFIFO::*;
import BRAM::*;
import Probe::*;

import ConnectalBram::*;
import MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;
import Ethernet::*;
import PrintTrace::*;
`include "ConnectalProjectConfig.bsv"

typedef 8 SGListPageShift0;
typedef 12 SGListPageShift4;
typedef 16 SGListPageShift8;
typedef 20 SGListPageShift12;
typedef Bit#(TLog#(MaxNumSGLists)) RegionsIdx;

typedef 8 IndexWidth;

typedef struct {
   RegionsIdx           id;
   Bit#(MemOffsetSize) off;
} AddrTransRequest deriving (Eq,Bits,FShow);

typedef struct {
   DmaErrorType    error;
   Bit#(addrWidth) physAddr;
} AddrTransResponse#(numeric type addrWidth) deriving (Eq,Bits,FShow);

interface MMU#(numeric type addrWidth);
   interface MMURequest request;
   interface Vector#(2,Server#(AddrTransRequest,AddrTransResponse#(addrWidth))) addr;
endinterface

typedef struct {
   DmaErrorType error;
   Bit#(3) pageSize;
   Bit#(SGListPageShift12) value;
} Offset deriving (Eq,Bits,FShow);

typedef Bit#(TSub#(MemOffsetSize,SGListPageShift0)) Page0;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift4)) Page4;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift8)) Page8;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift12)) Page12;

typedef struct {
   Bit#(TSub#(MemOffsetSize,SGListPageShift0)) barrier;
   Bit#(IndexWidth) idxOffset;
   } SingleRegion deriving (Eq,Bits,FShow);

typedef struct {
   SingleRegion reg12;
   SingleRegion reg8;
   SingleRegion reg4;
   SingleRegion reg0;
   } Region deriving (Eq,Bits,FShow);

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

typedef struct {
   Bool cond12;
   Bool cond8;
   Bool cond4;
   Bool cond0;
   Bit#(IndexWidth) idxOffset12;
   Bit#(IndexWidth) idxOffset8;
   Bit#(IndexWidth) idxOffset4;
   Bit#(IndexWidth) idxOffset0;
   AddrTransRequest req;
   } Stage3Params deriving (Bits);

typedef struct {
   Offset off;
   Bit#(IndexWidth) pbase;
   Bit#(IndexWidth) idxOffset;
   PktId ptr;
   } Stage4Params deriving (Bits);

// the address translation servers (addr[0], addr[1]) have a latency of 8 and are fully pipelined
module mkSharedBuffMMU#(Integer iid, MMUIndication mmuIndication)(MMU#(addrWidth))
   provisos(Log#(MaxNumSGLists, listIdxSize)
	    ,Add#(listIdxSize,8, entryIdxSize)
	    ,Add#(a__,addrWidth,MemOffsetSize)
     );

   let verbose = True;
   TagGen#(MaxNumSGLists) sglId_gen <- mkTagGen();
   rule complete_sglId_gen;
      let __x <- sglId_gen.complete;
   endrule

   Reg#(Bit#(32)) cycle <- mkReg(0);
   rule every1 if (verbose);
      cycle <= cycle + 1;
   endrule

   // stage 0 (latency == 1)
   Vector#(2, FIFO#(AddrTransRequest)) incomingReqs <- replicateM(mkFIFO);

   // stage 1 (latency == 2)
   BRAM_Configure bramConfig = defaultValue;
   bramConfig.latency        = 2;
   BRAM2Port#(RegionsIdx, Maybe#(Region)) regall <- ConnectalBram::mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(AddrTransRequest))          reqs0 <- replicateM(mkSizedFIFOF(3));

   // stage 2 (latency == 1)
   Vector#(2, FIFOF#(Stage3Params)) stage3Params <- replicateM(mkFIFOF);

   // stage 3 (latency == 1)
   Vector#(2, FIFOF#(Stage4Params)) stage4Params <- replicateM(mkFIFOF);

   // stage 4 (latency == 2)
   BRAM2Port#(Bit#(entryIdxSize),Page0) pages <- ConnectalBram::mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(Offset))           offs1 <- replicateM(mkSizedFIFOF(3));

   // stage 4 (latnecy == 1)
   Vector#(2,FIFOF#(AddrTransResponse#(addrWidth))) pageResponseFifos <- replicateM(mkFIFOF);

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   Vector#(2,FIFO#(DmaError)) dmaErrorFifos <- replicateM(mkFIFO());
   Rules rs = emptyRules;
   for (Integer i = 0; i < 2; i = i + 1) begin
      Rules r =
         rules
            rule connectDmaError;
               let v <- toGet(dmaErrorFifos[i]).get;
               dmaErrorFifo.enq(v);
            endrule
         endrules;
      rs = rJoinDescendingUrgency(rs, r);
   end

   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      mmuIndication.error(extend(pack(error.errorType)), error.pref, extend(error.off), fromInteger(iid));
   endrule

   let page_shift0 = fromInteger(valueOf(SGListPageShift0));
   let page_shift4 = fromInteger(valueOf(SGListPageShift4));
   let page_shift8 = fromInteger(valueOf(SGListPageShift8));
   let page_shift12 = fromInteger(valueOf(SGListPageShift12));

   function BRAMServer#(a,b) portsel(BRAM2Port#(a,b) x, Integer i);
      if(i==0) return x.portA;
      else return x.portB;
   endfunction

   for (Integer i = 0; i < 2; i=i+1) begin
      rule stage1;  // first read in the address cutoff values between regions
	 AddrTransRequest req <- toGet(incomingReqs[i]).get();
	 portsel(regall, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
            address:truncate(req.id), datain:?});
	 reqs0[i].enq(req);
      endrule

      // pipeline the address lookup
      rule stage2; // Now compare address cutoffs with requested offset
	 AddrTransRequest req <- toGet(reqs0[i]).get;
	 Maybe#(Region) m_regionall <- portsel(regall,i).response.get;

	 case (m_regionall) matches
	    tagged Valid .regionall: begin
               Page0 off0 = truncate(req.off >> valueOf(SGListPageShift0));
               Page4 off4 = truncate(req.off >> valueOf(SGListPageShift4));
               Page8 off8 = truncate(req.off >> valueOf(SGListPageShift8));
               Page12 off12 = truncate(req.off >> valueOf(SGListPageShift12));
	       let cond12 = off12 < truncate(regionall.reg12.barrier);
	       let cond8 = off8 < truncate(regionall.reg8.barrier);
	       let cond4 = off4 < truncate(regionall.reg4.barrier);
	       let cond0 = off0 < regionall.reg0.barrier;

	       if (verbose) $display("mkMMU::stage2: id=%d off=%h (%h %h %h) (%h %h %h)", req.id, req.off,
				     regionall.reg8.barrier, regionall.reg4.barrier, regionall.reg0.barrier,
				     off8, off4, off0);

	       stage3Params[i].enq(Stage3Params {cond12: cond12, cond8: cond8, cond4: cond4, cond0: cond0,
						 idxOffset12: regionall.reg12.idxOffset,idxOffset8: regionall.reg8.idxOffset,
						 idxOffset4: regionall.reg4.idxOffset, idxOffset0: regionall.reg0.idxOffset,
						 req: req });
	    end
	    tagged Invalid:
	       dmaErrorFifos[0].enq(DmaError { errorType: DmaErrorSGLIdInvalid, pref: extend(req.id), off:req.off });
	 endcase
      endrule
      rule stage3; // Based on results of comparision, select a region, putting it into 'o.pageSize'.  idxOffset holds offset in sglist table of relevant entry
	 let params <- toGet(stage3Params[i]).get();
	 AddrTransRequest req = params.req;
	 Offset o = Offset{pageSize: 0, value: truncate(req.off)};
	 Bit#(IndexWidth) pbase = 0;
	 Bit#(IndexWidth) idxOffset = 0;

	 if (params.cond12) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 4;
	    pbase = truncate(req.off>>page_shift12);
	    idxOffset = params.idxOffset12;
	 end
	 else if (params.cond8) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 3;
	    pbase = truncate(req.off>>page_shift8);
	    idxOffset = params.idxOffset8;
	 end
	 else if (params.cond4) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 2;
	    pbase = truncate(req.off>>page_shift4);
	    idxOffset = params.idxOffset4;
	 end
	 else if (params.cond0) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 1;
	    pbase = truncate(req.off>>page_shift0);
	    idxOffset = params.idxOffset0;
	 end
	 stage4Params[i].enq(Stage4Params { off: o, pbase: pbase, idxOffset: idxOffset, ptr: req.id });
      endrule
      (* descending_urgency = "stage2, stage4" *)
      rule stage4; // Read relevant sglist entry
	 let params <- toGet(stage4Params[i]).get();
	 let off = params.off;
	 let pbase = params.pbase;
	 let idxOffset = params.idxOffset;
	 let ptr = params.ptr;
	 Bit#(IndexWidth) p = pbase + idxOffset;
	 if (off.pageSize == 0) begin
	    if (verbose) $display("mkMMU::addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaErrorFifos[1].enq(DmaError { errorType: DmaErrorOffsetOutOfRange, pref: extend(ptr), off:extend(off.value) });
	 end
	 else begin
	    if (verbose) $display("mkMMU::pages[%d].read %h", i, {ptr,p});
	    portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
						      address:{ptr,p}, datain:?});
	    offs1[i].enq(off);
	 end
      endrule
      rule stage5; // Concatenate page base address from sglist entry with LSB offset bits from request and return
	 Page0 page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs1[i]).get();
	 if (verbose) $display("mkMMU::p ages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(MemOffsetSize) rv = ?;
	 Page4 b4 = truncate(page);
	 Page8 b8 = truncate(page);
	 Page12 b12 = truncate(page);
	 case (offset.pageSize)
	    1: rv = {page,truncate(offset.value)};
	    2: rv = {b4,truncate(offset.value)};
	    3: rv = {b8,truncate(offset.value)};
	    4: rv = {b12,truncate(offset.value)};
	 endcase
	 pageResponseFifos[i].enq(AddrTransResponse { error: DmaErrorNone, physAddr: truncate(rv) });
      endrule
   end

   FIFO#(PktId) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      let ptr <- toGet(configRespFifo).get();
      mmuIndication.configResp(extend(ptr));
   endrule

   function Server#(AddrTransRequest,AddrTransResponse#(addrWidth)) addrServer(Integer i);
   return
      (interface Server#(AddrTransRequest,Bit#(addrWidth));
	  interface Put request;
	     method Action put(AddrTransRequest req);
		incomingReqs[i].enq(req);
                if (verbose) $display("SharedBuffMMU:: %d: incomingReq ", cycle, fshow(req));
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(AddrTransResponse#(addrWidth)) get();
		let rv <- toGet(pageResponseFifos[i]).get();
`ifdef SIMULATION
		rv.physAddr = rv.physAddr | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
                if (verbose) $display("SharedBuffMMU:: %d: pageResponse ", cycle, fshow(rv));
		return rv;
	     endmethod
	  endinterface
       endinterface);
   endfunction

   interface MMURequest request;
   method Action idRequest(Bit#(32) fd);
      let nextId <- sglId_gen.getTag;
      let resp = (fromInteger(iid) << 16) | extend(nextId);
      if (verbose) $display("mkMMU::idRequest %d", fd);
      mmuIndication.idResponse(resp);
   endmethod
   method Action idReturn(Bit#(32) sglId);
      sglId_gen.returnTag(truncate(sglId));
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False, address: truncate(sglId), datain: tagged Invalid });
      if (verbose) $display("idReturn %d", sglId);
   endmethod
   method Action region(Bit#(32) pointer, Bit#(64) barr12, Bit#(32) index12, Bit#(64) barr8, Bit#(32) index8, Bit#(64) barr4, Bit#(32) index4, Bit#(64) barr0, Bit#(32) index0);
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False,
          address: truncate(pointer), datain: tagged Valid Region{
             reg12: SingleRegion{barrier: truncate(barr12), idxOffset: truncate(index12)},
             reg8: SingleRegion{barrier: truncate(barr8), idxOffset: truncate(index8)},
             reg4: SingleRegion{barrier: truncate(barr4), idxOffset: truncate(index4)},
             reg0: SingleRegion{barrier: truncate(barr0), idxOffset: truncate(index0)}} });
      if (verbose) $display("mkMMU::region pointer=%d barr12=%h barr8=%h barr4=%h barr0=%h", pointer, barr12, barr8, barr4, barr0);
      configRespFifo.enq(truncate(pointer));
   endmethod

   method Action sglist(Bit#(32) pointer, Bit#(32) pointerIndex, Bit#(64) addr,  Bit#(32) len);
         if (fromInteger(iid) != pointer[31:16]) begin
	    $display("mkMMU::sglist ERROR");
	    $finish();
	 end
         Bit#(IndexWidth) ind = truncate(pointerIndex);
	 portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False,
             address:{truncate(pointer),ind}, datain:truncate(addr)});
         if (verbose) $display("mkMMU::sglist pointer=%d pointerIndex=%d addr=%d len=%d", pointer, pointerIndex, addr, len);
   endmethod
   method Action setInterface(Bit#(32) interfaceId, Bit#(32) sglId);
       /* this method is only implemented in s/w responders */
   endmethod
   endinterface
   interface addr = genWith(addrServer);

endmodule

interface ArbitratedMMU#(numeric type addrWidth, numeric type numServers);
   interface Vector#(numServers,Server#(AddrTransRequest,AddrTransResponse#(addrWidth))) servers;
endinterface

module mkArbitratedMMU#(Server#(AddrTransRequest,AddrTransResponse#(addrWidth)) server) (ArbitratedMMU#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(9);
   Reg#(Bit#(TLog#(numServers))) arb <- mkReg(0);

   // this is a very crude arbiter.  something more sophisticated may be required (mdk)
   rule inc_arb;
      arb <= arb+1;
   endrule

   function Server#(AddrTransRequest,AddrTransResponse#(addrWidth)) arbitratedServer(Integer i);
   return
      (interface Server#(AddrTransRequest,AddrTransResponse#(addrWidth));
	  interface Put request;
	     method Action put(AddrTransRequest req) if (arb == fromInteger(i));
		tokFifo.enq(fromInteger(i));
		server.request.put(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(AddrTransResponse#(addrWidth)) get() if (tokFifo.first == fromInteger(i));
		tokFifo.deq;
		let rv <- server.response.get;
		return rv;
	     endmethod
	  endinterface
       endinterface);
   endfunction

   interface servers = genWith(arbitratedServer);

endmodule


removed fifo full check ifor lread_data

removed fifo full check ifor lread_data

// Copyright (c) 2015 Cornell University.

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

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

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

import Clocks::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import Pipe::*;
import GetPut::*;
import Probe::*;

typedef struct {
   Bit#(8) port_no;
   Bit#(64) data;
} BufData deriving (Bits, Eq);

interface DtpIfc;
   interface PipeIn#(Bit#(128)) timestamp; // streaming time counter from NetTop.
   interface Vector#(4, PipeOut#(Bit#(53))) fromHost;
   interface Vector#(4, PipeIn#(Bit#(53)))  toHost;
   interface Vector#(4, PipeIn#(Bit#(32)))  delay;
   interface Vector#(4, PipeIn#(Bit#(32)))  state;
   interface Vector#(4, PipeIn#(Bit#(64)))  jumpCount;
   interface Vector#(4, PipeIn#(Bit#(53)))  cLocal;
   interface PipeIn#(Bit#(53)) globalOut;
   interface Vector#(4, PipeOut#(Bit#(32))) interval;
   interface Reset rst;
   interface PipeIn#(Bit#(1)) isSwitch;
endinterface

interface SonicUserRequest;
   method Action dtp_read_version();
   method Action dtp_reset(Bit#(32) len);
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
   method Action dtp_read_delay(Bit#(8) port_no);
   method Action dtp_read_state(Bit#(8) port_no);
   method Action dtp_read_error(Bit#(8) port_no);
   method Action dtp_read_cnt(Bit#(8) cmd);
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) local_cnt);
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
   method Action dtp_read_local_cnt(Bit#(8) port_no);
   method Action dtp_read_global_cnt();
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
   method Action dtp_debug_rcvd_msg(Bit#(8) port_no);
   method Action dtp_debug_sent_msg(Bit#(8) port_no);
/* NOT IMPLEMENTED YET.
   method Action dtp_ctrl_disable();
   method Action dtp_ctrl_enable();
*/
endinterface

interface SonicUserIndication;
   method Action dtp_read_version_resp(Bit#(32) version);
   method Action dtp_read_delay_resp(Bit#(8) port_no, Bit#(32) delay);
   method Action dtp_read_state_resp(Bit#(8) port_no, Bit#(32) state);
   method Action dtp_read_error_resp(Bit#(8) port_no, Bit#(64) jumpc);
   method Action dtp_read_cnt_resp(Bit#(64) val);
   method Action dtp_logger_read_cnt_resp(Bit#(8) port_no, Bit#(64) localc, Bit#(64) msg1, Bit#(64) msg2);
   method Action dtp_read_local_cnt_resp(Bit#(8) port_no, Bit#(64) val);
   method Action dtp_read_global_cnt_resp(Bit#(64) val);
   method Action dtp_read_beacon_interval_resp(Bit#(8) port_no, Bit#(32) interval);
   method Action dtp_debug_rcvd_msg_resp(Bit#(8) port_no, Bit#(32) lread_cnt_enq1, Bit#(32) lread_cnt_enq2, Bit#(32) lread_cnt_deq);
   method Action dtp_debug_sent_msg_resp(Bit#(8) port_no, Bit#(32) lwrite_cnt_enq, Bit#(32) lwrite_cnt_deq1, Bit#(32) lwrite_cnt_deq2);
endinterface

interface SonicUser;
   interface SonicUserRequest request;
   interface DtpIfc           dtp;
endinterface

module mkSonicUser#(SonicUserIndication indication)(SonicUser);
   let verbose = False;
   Clock defaultClock <- exposeCurrentClock();

   Reg#(Bit#(64))  cycle_count <- mkReg(0);
   Reg#(Bit#(64))  last_count  <- mkReg(0);

   FIFOF#(Bit#(128)) cntFifo <- mkFIFOF();
   Reg#(Bit#(128))   cycle_reg <- mkReg(0);
   Reg#(Bit#(1))     isSwitch_reg <- mkReg(0);
   Vector#(4, Reg#(Bit#(32))) beacon_interval <- replicateM(mkReg(1000)); //default beacon interval is 1000.

   Vector#(4, FIFOF#(Bit#(53))) fromHostFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) toHostFifo   <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) delayFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) stateFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(64))) jumpCountFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) cLocalFifo   <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(53)) cGlobalFifo <- mkSizedFIFOF(4);
   Vector#(4, FIFOF#(Bit#(32))) intervalFifo <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(1)) isSwitchFifo <- mkFIFOF();

   Reg#(Bit#(8))  lwrite_port <- mkReg(0);
   FIFOF#(BufData) lwrite_data_cycle1 <- mkSizedFIFOF(2);
   FIFOF#(BufData) lwrite_data_cycle2 <- mkSizedFIFOF(2);
   FIFOF#(void) log_write_cf <- mkFIFOF;
   Vector#(4, Reg#(Bit#(32))) lwrite_cnt_enq <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32))) lwrite_cnt_deq1 <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32))) lwrite_cnt_deq2 <- replicateM(mkReg(0));

   Vector#(4, FIFOF#(BufData)) lread_data_cycle1 <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(BufData)) lread_data_cycle2 <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) lread_data_timestamp <- replicateM(mkSizedFIFOF(4));
   Vector#(4, Reg#(Bit#(32)))  lread_cnt_enq1 <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32)))  lread_cnt_enq2 <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32)))  lread_cnt_deq <- replicateM(mkReg(0));

   Reg#(Bit#(28)) dtp_rst_cntr <- mkReg(0);
   MakeResetIfc dtpResetOut <- mkResetSync(0, False, defaultClock);

   rule count;
      cycle_count <= cycle_count + 1;
   endrule

   // clear all fifo on reset
   rule clearOnReset(dtpResetOut.isAsserted);
      for (Integer i=0; i<4; i=i+1) begin
         fromHostFifo[i].clear();
         toHostFifo[i].clear();
         delayFifo[i].clear();
         stateFifo[i].clear();
         jumpCountFifo[i].clear();
         cLocalFifo[i].clear();
         intervalFifo[i].clear();
         lread_data_cycle1[i].clear();
         lread_data_cycle2[i].clear();
         lread_data_timestamp[i].clear();
      end
      cGlobalFifo.clear();
      cntFifo.clear();
      lwrite_data_cycle1.clear();
      lwrite_data_cycle2.clear();
      log_write_cf.clear();
   endrule

   // dtp_read_cnt
   rule snapshot_dtp_timestamp;
      let v <- toGet(cntFifo).get;
      cycle_reg <= v;
   endrule

   // dtp_read_delay
   Vector#(4, Reg#(Bit#(32))) delay_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_delay;
         let v <- toGet(delayFifo[i]).get;
         delay_reg[i] <= v;
      endrule
   end

   // dtp_read_state
   Vector#(4, Reg#(Bit#(32))) state_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_state;
         let v <- toGet(stateFifo[i]).get;
         state_reg[i] <= v;
      endrule
   end

   // dtp_read_error
   Vector#(4, Reg#(Bit#(64))) jumpc_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_jumpc;
         let v <- toGet(jumpCountFifo[i]).get;
         jumpc_reg[i] <= v;
      endrule
   end

   // dtp_read_local_cnt
   Vector#(4, Reg#(Bit#(53))) clocal_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_clocal;
         let v <- toGet(cLocalFifo[i]).get;
         clocal_reg[i] <= v;
      endrule
   end

   // dtp_read_global_cnt
   Reg#(Bit#(53)) cglobal_reg <- mkReg(0);
   rule snapshot_cglobal;
      let v <- toGet(cGlobalFifo).get;
      cglobal_reg <= v;
   endrule

   // dtp_set_interval
   for (Integer i=0; i<4; i=i+1) begin
      rule set_interval;
         let v = beacon_interval[i];
         intervalFifo[i].enq(v);
      endrule
   end

   Integer chunk_a = 0;
   Integer chunk_b = 1;
   function Bit#(2) toState(Integer st);
      return (1<<st);
   endfunction

   Reg#(Bit#(2)) lwstate <- mkReg(toState(chunk_a));
   // dtp_logger_write_cnt
   rule log_from_host_cycle;
      if ((lwstate[chunk_a] == 1) && lwrite_data_cycle1.notEmpty) begin
         let v1 = lwrite_data_cycle1.first;
         if (fromHostFifo[v1.port_no].notFull) begin
            fromHostFifo[v1.port_no].enq({1'b0, truncate(v1.data)});
            lwrite_data_cycle1.deq;
            lwstate <= toState(chunk_b);
            lwrite_cnt_deq1[v1.port_no] <= lwrite_cnt_deq1[v1.port_no] + 1;
         end
      end
      else if((lwstate[chunk_b] == 1) && lwrite_data_cycle2.notEmpty) begin
         let v2 = lwrite_data_cycle2.first;
         if (fromHostFifo[v2.port_no].notFull) begin
            fromHostFifo[v2.port_no].enq({1'b1, truncate(v2.data)});
            lwrite_data_cycle2.deq;
            lwstate <= toState(chunk_a);
            lwrite_cnt_deq2[v2.port_no] <= lwrite_cnt_deq2[v2.port_no] + 1;
         end
      end
   endrule

   for (Integer i=0; i<4; i=i+1) begin
      rule save_host_data (toHostFifo[i].notEmpty);
         Bit#(53) v = toHostFifo[i].first;
         case (v[52]) matches
            0: begin
               lread_data_cycle1[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
               lread_cnt_enq1[i] <= lread_cnt_enq1[i] + 1;
            end
            1: begin
               lread_data_cycle2[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
               Bit#(53) timestamp = 0;
               if (isSwitch_reg == 0) begin // 0 for NIC, 1 for Switch
                  timestamp = clocal_reg[i];
               end
               else begin
                  timestamp = cglobal_reg;
               end
               lread_data_timestamp[i].enq(timestamp);
               lread_cnt_enq2[i] <= lread_cnt_enq2[i] + 1;
            end
         endcase
         toHostFifo[i].deq;
      endrule
   end

   rule assert_reset (dtp_rst_cntr > 0);
      dtpResetOut.assertReset;
      dtp_rst_cntr <= dtp_rst_cntr - 1;
   endrule

   rule switch_mode;
      let v <- toGet(isSwitchFifo).get;
      isSwitch_reg <= v;
   endrule

   // Interface to external modules.
   interface dtp = (interface DtpIfc;
      interface timestamp = toPipeIn(cntFifo);
      interface delay     = map(toPipeIn, delayFifo);
      interface state     = map(toPipeIn, stateFifo);
      interface jumpCount = map(toPipeIn, jumpCountFifo);
      interface toHost    = map(toPipeIn, toHostFifo);
      interface fromHost  = map(toPipeOut, fromHostFifo);
      interface cLocal    = map(toPipeIn, cLocalFifo);
      interface globalOut = toPipeIn(cGlobalFifo);
      interface interval  = map(toPipeOut, intervalFifo);
      interface rst       = dtpResetOut.new_rst;
      interface isSwitch  = toPipeIn(isSwitchFifo);
   endinterface);

   // API implementation
   interface SonicUserRequest request;
   method Action dtp_read_version();
      let v = `DtpVersion; //Defined in Makefile as time of compilation.
      indication.dtp_read_version_resp(v);
   endmethod
   method Action dtp_reset(Bit#(32) len) if (dtp_rst_cntr == 0);
      Bit#(28) limit = truncate(len) & 28'hFFFFFFF;
      if (limit < 32) begin
         limit = 32;
      end
      dtp_rst_cntr <= limit;
   endmethod
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
      //
   endmethod
   method Action dtp_read_delay(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_delay_resp(port_no, truncate(delay_reg[port_no]));
      end
   endmethod
   method Action dtp_read_state(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_state_resp(port_no, state_reg[port_no]);
      end
   endmethod
   method Action dtp_read_error(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_error_resp(port_no, jumpc_reg[port_no]);
      end
   endmethod
   method Action dtp_read_cnt(Bit#(8) port_no);
      indication.dtp_read_cnt_resp(truncate(cycle_reg));
   endmethod
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) host_timestamp);
      // Check valid port No.
      if (port_no < 4) begin
         lwrite_data_cycle1.enq(BufData{port_no: port_no, data: host_timestamp});
         lwrite_data_cycle2.enq(BufData{port_no: port_no, data: zeroExtend(clocal_reg[port_no])});
         lwrite_cnt_enq[port_no] <= lwrite_cnt_enq[port_no] + 1;
      end
   endmethod
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         if (lread_data_cycle1[port_no].notEmpty &&
             lread_data_cycle2[port_no].notEmpty &&
             lread_data_timestamp[port_no].notEmpty) begin
            Bit#(64) remote_message1 = lread_data_cycle1[port_no].first.data;
            Bit#(64) remote_message2 = lread_data_cycle2[port_no].first.data;
            Bit#(53) timestamp = lread_data_timestamp[port_no].first;
            indication.dtp_logger_read_cnt_resp(port_no,
                                                zeroExtend(timestamp),
                                                zeroExtend(remote_message1),
                                                zeroExtend(remote_message2));
            lread_data_cycle1[port_no].deq;
            lread_data_cycle2[port_no].deq;
            lread_data_timestamp[port_no].deq;
            lread_cnt_deq[port_no] <= lread_cnt_deq[port_no] + 1;
         end
      end
   endmethod
   method Action dtp_read_local_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_local_cnt_resp(port_no, zeroExtend(clocal_reg[port_no]));
      end
   endmethod
   method Action dtp_read_global_cnt();
      indication.dtp_read_global_cnt_resp(zeroExtend(cglobal_reg));
   endmethod
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
      if (port_no < 4) begin
         beacon_interval[port_no] <= interval;
      end
   endmethod
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_beacon_interval_resp(port_no, beacon_interval[port_no]);
      end
   endmethod
   method Action dtp_debug_rcvd_msg (Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_debug_rcvd_msg_resp(port_no, lread_cnt_enq1[port_no], lread_cnt_enq2[port_no], lread_cnt_deq[port_no]);
      end
   endmethod
   method Action dtp_debug_sent_msg (Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_debug_sent_msg_resp(port_no, lwrite_cnt_enq[port_no], lwrite_cnt_deq1[port_no], lwrite_cnt_deq2[port_no]);
      end
   endmethod
   endinterface
endmodule

// Copyright (c) 2015 Cornell University.

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

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

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

import Clocks::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import Pipe::*;
import GetPut::*;
import Probe::*;

typedef struct {
   Bit#(8) port_no;
   Bit#(64) data;
} BufData deriving (Bits, Eq);

interface DtpIfc;
   interface PipeIn#(Bit#(128)) timestamp; // streaming time counter from NetTop.
   interface Vector#(4, PipeOut#(Bit#(53))) fromHost;
   interface Vector#(4, PipeIn#(Bit#(53)))  toHost;
   interface Vector#(4, PipeIn#(Bit#(32)))  delay;
   interface Vector#(4, PipeIn#(Bit#(32)))  state;
   interface Vector#(4, PipeIn#(Bit#(64)))  jumpCount;
   interface Vector#(4, PipeIn#(Bit#(53)))  cLocal;
   interface PipeIn#(Bit#(53)) globalOut;
   interface Vector#(4, PipeOut#(Bit#(32))) interval;
   interface Reset rst;
   interface PipeIn#(Bit#(1)) isSwitch;
endinterface

interface SonicUserRequest;
   method Action dtp_read_version();
   method Action dtp_reset(Bit#(32) len);
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
   method Action dtp_read_delay(Bit#(8) port_no);
   method Action dtp_read_state(Bit#(8) port_no);
   method Action dtp_read_error(Bit#(8) port_no);
   method Action dtp_read_cnt(Bit#(8) cmd);
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) local_cnt);
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
   method Action dtp_read_local_cnt(Bit#(8) port_no);
   method Action dtp_read_global_cnt();
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
   method Action dtp_debug_rcvd_msg(Bit#(8) port_no);
   method Action dtp_debug_sent_msg(Bit#(8) port_no);
/* NOT IMPLEMENTED YET.
   method Action dtp_ctrl_disable();
   method Action dtp_ctrl_enable();
*/
endinterface

interface SonicUserIndication;
   method Action dtp_read_version_resp(Bit#(32) version);
   method Action dtp_read_delay_resp(Bit#(8) port_no, Bit#(32) delay);
   method Action dtp_read_state_resp(Bit#(8) port_no, Bit#(32) state);
   method Action dtp_read_error_resp(Bit#(8) port_no, Bit#(64) jumpc);
   method Action dtp_read_cnt_resp(Bit#(64) val);
   method Action dtp_logger_read_cnt_resp(Bit#(8) port_no, Bit#(64) localc, Bit#(64) msg1, Bit#(64) msg2);
   method Action dtp_read_local_cnt_resp(Bit#(8) port_no, Bit#(64) val);
   method Action dtp_read_global_cnt_resp(Bit#(64) val);
   method Action dtp_read_beacon_interval_resp(Bit#(8) port_no, Bit#(32) interval);
   method Action dtp_debug_rcvd_msg_resp(Bit#(8) port_no, Bit#(32) lread_cnt_enq1, Bit#(32) lread_cnt_enq2, Bit#(32) lread_cnt_deq);
   method Action dtp_debug_sent_msg_resp(Bit#(8) port_no, Bit#(32) lwrite_cnt_enq, Bit#(32) lwrite_cnt_deq1, Bit#(32) lwrite_cnt_deq2);
endinterface

interface SonicUser;
   interface SonicUserRequest request;
   interface DtpIfc           dtp;
endinterface

module mkSonicUser#(SonicUserIndication indication)(SonicUser);
   let verbose = False;
   Clock defaultClock <- exposeCurrentClock();

   Reg#(Bit#(64))  cycle_count <- mkReg(0);
   Reg#(Bit#(64))  last_count  <- mkReg(0);

   FIFOF#(Bit#(128)) cntFifo <- mkFIFOF();
   Reg#(Bit#(128))   cycle_reg <- mkReg(0);
   Reg#(Bit#(1))     isSwitch_reg <- mkReg(0);
   Vector#(4, Reg#(Bit#(32))) beacon_interval <- replicateM(mkReg(1000)); //default beacon interval is 1000.

   Vector#(4, FIFOF#(Bit#(53))) fromHostFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) toHostFifo   <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) delayFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) stateFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(64))) jumpCountFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) cLocalFifo   <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(53)) cGlobalFifo <- mkSizedFIFOF(4);
   Vector#(4, FIFOF#(Bit#(32))) intervalFifo <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(1)) isSwitchFifo <- mkFIFOF();

   Reg#(Bit#(8))  lwrite_port <- mkReg(0);
   FIFOF#(BufData) lwrite_data_cycle1 <- mkSizedFIFOF(2);
   FIFOF#(BufData) lwrite_data_cycle2 <- mkSizedFIFOF(2);
   FIFOF#(void) log_write_cf <- mkFIFOF;
   Vector#(4, Reg#(Bit#(32))) lwrite_cnt_enq <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32))) lwrite_cnt_deq1 <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32))) lwrite_cnt_deq2 <- replicateM(mkReg(0));

   Vector#(4, FIFOF#(BufData)) lread_data_cycle1 <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(BufData)) lread_data_cycle2 <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) lread_data_timestamp <- replicateM(mkSizedFIFOF(4));
   Vector#(4, Reg#(Bit#(32)))  lread_cnt_enq1 <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32)))  lread_cnt_enq2 <- replicateM(mkReg(0));
   Vector#(4, Reg#(Bit#(32)))  lread_cnt_deq <- replicateM(mkReg(0));

   Reg#(Bit#(28)) dtp_rst_cntr <- mkReg(0);
   MakeResetIfc dtpResetOut <- mkResetSync(0, False, defaultClock);

   rule count;
      cycle_count <= cycle_count + 1;
   endrule

   // clear all fifo on reset
   rule clearOnReset(dtpResetOut.isAsserted);
      for (Integer i=0; i<4; i=i+1) begin
         fromHostFifo[i].clear();
         toHostFifo[i].clear();
         delayFifo[i].clear();
         stateFifo[i].clear();
         jumpCountFifo[i].clear();
         cLocalFifo[i].clear();
         intervalFifo[i].clear();
         lread_data_cycle1[i].clear();
         lread_data_cycle2[i].clear();
         lread_data_timestamp[i].clear();
      end
      cGlobalFifo.clear();
      cntFifo.clear();
      lwrite_data_cycle1.clear();
      lwrite_data_cycle2.clear();
      log_write_cf.clear();
   endrule

   // dtp_read_cnt
   rule snapshot_dtp_timestamp;
      let v <- toGet(cntFifo).get;
      cycle_reg <= v;
   endrule

   // dtp_read_delay
   Vector#(4, Reg#(Bit#(32))) delay_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_delay;
         let v <- toGet(delayFifo[i]).get;
         delay_reg[i] <= v;
      endrule
   end

   // dtp_read_state
   Vector#(4, Reg#(Bit#(32))) state_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_state;
         let v <- toGet(stateFifo[i]).get;
         state_reg[i] <= v;
      endrule
   end

   // dtp_read_error
   Vector#(4, Reg#(Bit#(64))) jumpc_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_jumpc;
         let v <- toGet(jumpCountFifo[i]).get;
         jumpc_reg[i] <= v;
      endrule
   end

   // dtp_read_local_cnt
   Vector#(4, Reg#(Bit#(53))) clocal_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_clocal;
         let v <- toGet(cLocalFifo[i]).get;
         clocal_reg[i] <= v;
      endrule
   end

   // dtp_read_global_cnt
   Reg#(Bit#(53)) cglobal_reg <- mkReg(0);
   rule snapshot_cglobal;
      let v <- toGet(cGlobalFifo).get;
      cglobal_reg <= v;
   endrule

   // dtp_set_interval
   for (Integer i=0; i<4; i=i+1) begin
      rule set_interval;
         let v = beacon_interval[i];
         intervalFifo[i].enq(v);
      endrule
   end

   Integer chunk_a = 0;
   Integer chunk_b = 1;
   function Bit#(2) toState(Integer st);
      return (1<<st);
   endfunction

   Reg#(Bit#(2)) lwstate <- mkReg(toState(chunk_a));
   // dtp_logger_write_cnt
   rule log_from_host_cycle;
      if ((lwstate[chunk_a] == 1) && lwrite_data_cycle1.notEmpty) begin
         let v1 = lwrite_data_cycle1.first;
         if (fromHostFifo[v1.port_no].notFull) begin
            fromHostFifo[v1.port_no].enq({1'b0, truncate(v1.data)});
            lwrite_data_cycle1.deq;
            lwstate <= toState(chunk_b);
            lwrite_cnt_deq1[v1.port_no] <= lwrite_cnt_deq1[v1.port_no] + 1;
         end
      end
      else if((lwstate[chunk_b] == 1) && lwrite_data_cycle2.notEmpty) begin
         let v2 = lwrite_data_cycle2.first;
         if (fromHostFifo[v2.port_no].notFull) begin
            fromHostFifo[v2.port_no].enq({1'b1, truncate(v2.data)});
            lwrite_data_cycle2.deq;
            lwstate <= toState(chunk_a);
            lwrite_cnt_deq2[v2.port_no] <= lwrite_cnt_deq2[v2.port_no] + 1;
         end
      end
   endrule

   for (Integer i=0; i<4; i=i+1) begin
      rule save_host_data (toHostFifo[i].notEmpty);
         Bit#(53) v = toHostFifo[i].first;
         case (v[52]) matches
            0: begin
               if (lread_data_cycle1[i].notFull) begin
                  lread_data_cycle1[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
               lread_cnt_enq1[i] <= lread_cnt_enq1[i] + 1;
               end
            end
            1: begin
               if (lread_data_cycle2[i].notFull) begin
                  lread_data_cycle2[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
               end
               Bit#(53) timestamp = 0;
               if (isSwitch_reg == 0) begin // 0 for NIC, 1 for Switch
                  timestamp = clocal_reg[i];
               end
               else begin
                  timestamp = cglobal_reg;
               end

               if (lread_data_timestamp[i].notFull) begin
                  lread_data_timestamp[i].enq(timestamp);
               end
               lread_cnt_enq2[i] <= lread_cnt_enq2[i] + 1;
            end
         endcase
         toHostFifo[i].deq;
      endrule
   end

   rule assert_reset (dtp_rst_cntr > 0);
      dtpResetOut.assertReset;
      dtp_rst_cntr <= dtp_rst_cntr - 1;
   endrule

   rule switch_mode;
      let v <- toGet(isSwitchFifo).get;
      isSwitch_reg <= v;
   endrule

   // Interface to external modules.
   interface dtp = (interface DtpIfc;
      interface timestamp = toPipeIn(cntFifo);
      interface delay     = map(toPipeIn, delayFifo);
      interface state     = map(toPipeIn, stateFifo);
      interface jumpCount = map(toPipeIn, jumpCountFifo);
      interface toHost    = map(toPipeIn, toHostFifo);
      interface fromHost  = map(toPipeOut, fromHostFifo);
      interface cLocal    = map(toPipeIn, cLocalFifo);
      interface globalOut = toPipeIn(cGlobalFifo);
      interface interval  = map(toPipeOut, intervalFifo);
      interface rst       = dtpResetOut.new_rst;
      interface isSwitch  = toPipeIn(isSwitchFifo);
   endinterface);

   // API implementation
   interface SonicUserRequest request;
   method Action dtp_read_version();
      let v = `DtpVersion; //Defined in Makefile as time of compilation.
      indication.dtp_read_version_resp(v);
   endmethod
   method Action dtp_reset(Bit#(32) len) if (dtp_rst_cntr == 0);
      Bit#(28) limit = truncate(len) & 28'hFFFFFFF;
      if (limit < 32) begin
         limit = 32;
      end
      dtp_rst_cntr <= limit;
   endmethod
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
      //
   endmethod
   method Action dtp_read_delay(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_delay_resp(port_no, truncate(delay_reg[port_no]));
      end
   endmethod
   method Action dtp_read_state(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_state_resp(port_no, state_reg[port_no]);
      end
   endmethod
   method Action dtp_read_error(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_error_resp(port_no, jumpc_reg[port_no]);
      end
   endmethod
   method Action dtp_read_cnt(Bit#(8) port_no);
      indication.dtp_read_cnt_resp(truncate(cycle_reg));
   endmethod
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) host_timestamp);
      // Check valid port No.
      if (port_no < 4) begin
         lwrite_data_cycle1.enq(BufData{port_no: port_no, data: host_timestamp});
         lwrite_data_cycle2.enq(BufData{port_no: port_no, data: zeroExtend(clocal_reg[port_no])});
         lwrite_cnt_enq[port_no] <= lwrite_cnt_enq[port_no] + 1;
      end
   endmethod
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         if (lread_data_cycle1[port_no].notEmpty &&
             lread_data_cycle2[port_no].notEmpty &&
             lread_data_timestamp[port_no].notEmpty) begin
            Bit#(64) remote_message1 = lread_data_cycle1[port_no].first.data;
            Bit#(64) remote_message2 = lread_data_cycle2[port_no].first.data;
            Bit#(53) timestamp = lread_data_timestamp[port_no].first;
            indication.dtp_logger_read_cnt_resp(port_no,
                                                zeroExtend(timestamp),
                                                zeroExtend(remote_message1),
                                                zeroExtend(remote_message2));
            lread_data_cycle1[port_no].deq;
            lread_data_cycle2[port_no].deq;
            lread_data_timestamp[port_no].deq;
            lread_cnt_deq[port_no] <= lread_cnt_deq[port_no] + 1;
         end
      end
   endmethod
   method Action dtp_read_local_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_local_cnt_resp(port_no, zeroExtend(clocal_reg[port_no]));
      end
   endmethod
   method Action dtp_read_global_cnt();
      indication.dtp_read_global_cnt_resp(zeroExtend(cglobal_reg));
   endmethod
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
      if (port_no < 4) begin
         beacon_interval[port_no] <= interval;
      end
   endmethod
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_beacon_interval_resp(port_no, beacon_interval[port_no]);
      end
   endmethod
   method Action dtp_debug_rcvd_msg (Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_debug_rcvd_msg_resp(port_no, lread_cnt_enq1[port_no], lread_cnt_enq2[port_no], lread_cnt_deq[port_no]);
      end
   endmethod
   method Action dtp_debug_sent_msg (Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_debug_sent_msg_resp(port_no, lwrite_cnt_enq[port_no], lwrite_cnt_deq1[port_no], lwrite_cnt_deq2[port_no]);
      end
   endmethod
   endinterface
endmodule


use MAC tx width to compute ipg, not datapath width

// Copyright (c) 2014 Cornell University.

// 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 DefaultValue::*;
import Vector::*;
import BuildVector::*;
import GetPut::*;
import ClientServer::*;
import Connectable::*;
import Clocks::*;
import Gearbox::*;
import Pipe::*;
import Ethernet::*;
import PacketBuffer::*;

typedef 1 MinimumIPG; // 1 beat == 16 bytes.

interface PktGen;
    interface PktWriteServer writeServer;
    interface PktWriteClient writeClient;
    method Action start(Bit#(32) iter, Bit#(32) ipg);
    method Action stop();
endinterface

module mkPktGen(PktGen)
   provisos (Div#(64, 8, bytesPerBeat)
            ,Log#(bytesPerBeat, beatShift));

   let verbose = False;

   Reg#(Bit#(32)) traceLen <- mkReg(0);
   Reg#(Bit#(32)) pktCount <- mkReg(0);
   Reg#(Bit#(32)) ipgCount <- mkReg(0);
   Reg#(Bool) idle <- mkReg(False);
   Reg#(Bit#(32)) currIPG <- mkReg(0);
   Reg#(Bool) started <- mkReg(False);
   Reg#(Bool) infiniteLoop <- mkReg(False);

   FIFO#(EtherData) outgoing_fifo <- mkFIFO();
   PacketBuffer buff <- mkPacketBuffer();

   rule prepare_packet if (pktCount>0 && !idle);
      let pktLen <- buff.readServer.readLen.get;
      buff.readServer.readReq.put(EtherReq{len:pktLen});
      if (verbose) $display("Pktgen:: fetch_packet pktlen=%h", pktLen);
   endrule

   rule enqueue_packet if (pktCount>0 && !idle);
      let data <- buff.readServer.readData.get;
      buff.writeServer.writeData.put(data);
      outgoing_fifo.enq(data);

      if (data.eop) begin
         if (!infiniteLoop)
            pktCount <= pktCount - 1;
         idle <= True;
         currIPG <= 0;
         if (verbose) $display("Pktgen:: eop %h %h %h %h", idle, started, currIPG, ipgCount);
      end
   endrule

   rule compute_idle if (pktCount>0 && idle);
      if (currIPG < ipgCount + fromInteger(valueOf(MinimumIPG))) begin
         currIPG <= currIPG + fromInteger(valueOf(bytesPerBeat));
         if (verbose) $display("Pktgen:: ipg = %d", currIPG);
      end
      else begin
         currIPG <= 0;
         idle <= False;
      end
   endrule

   // has to drain buffer at the end of packet generation
   rule cleanup if (pktCount==0 && traceLen>0 && started);
      let pktLen <- buff.readServer.readLen.get;
      buff.readServer.readReq.put(EtherReq{len: pktLen});
      if (verbose) $display("Pktgen:: drain buffer");
   endrule

   rule drainBufferPayload if (pktCount==0 && traceLen>0 && started);
      let data <- buff.readServer.readData.get;
      // do nothing
      if (verbose) $display("Pktgen:: drain buffer payload");
      if (data.eop) begin
         traceLen <= traceLen - 1;
      end
   endrule

   rule drainFinished if (pktCount==0 && traceLen==0 && started);
      started <= False;
   endrule

   interface PktWriteServer writeServer;
      interface Put writeData;
         method Action put (EtherData d);
            buff.writeServer.writeData.put(d);
            if (verbose) $display("Pktgen:: write data", fshow(d));
            if (d.eop) begin
               traceLen <= traceLen + 1;
            end
         endmethod
      endinterface
   endinterface
   interface PktWriteClient writeClient;
       interface Get writeData = toGet(outgoing_fifo);
   endinterface
   method Action start(Bit#(32) pc, Bit#(32) ipg) if (pktCount==0 && traceLen!=0);
      started <= True;
      ipgCount <= ipg;
      if (pc != 0) begin
         pktCount <= pc;
      end
      else begin
         pktCount <= 1;
         infiniteLoop <= True;
      end
      if (verbose) $display("Pktgen:: start %h %h", pc, ipg);
   endmethod
   method Action stop();
      infiniteLoop <= False;
   endmethod
endmodule

// Copyright (c) 2014 Cornell University.

// 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 DefaultValue::*;
import Vector::*;
import BuildVector::*;
import GetPut::*;
import ClientServer::*;
import Connectable::*;
import Clocks::*;
import Gearbox::*;
import Pipe::*;
import Ethernet::*;
import PacketBuffer::*;

typedef 1 MinimumIPG; // 1 beat == 16 bytes.

interface PktGen;
    interface PktWriteServer writeServer;
    interface PktWriteClient writeClient;
    method Action start(Bit#(32) iter, Bit#(32) ipg);
    method Action stop();
endinterface

module mkPktGen(PktGen)
   provisos (Div#(`DataBusWidth, 8, bytesPerBeat)
            ,Log#(bytesPerBeat, beatShift));

   let verbose = False;

   Reg#(Bit#(32)) traceLen <- mkReg(0);
   Reg#(Bit#(32)) pktCount <- mkReg(0);
   Reg#(Bit#(32)) ipgCount <- mkReg(0);
   Reg#(Bool) idle <- mkReg(False);
   Reg#(Bit#(32)) currIPG <- mkReg(0);
   Reg#(Bool) started <- mkReg(False);
   Reg#(Bool) infiniteLoop <- mkReg(False);

   FIFO#(EtherData) outgoing_fifo <- mkFIFO();
   PacketBuffer buff <- mkPacketBuffer();

   rule prepare_packet if (pktCount>0 && !idle);
      let pktLen <- buff.readServer.readLen.get;
      buff.readServer.readReq.put(EtherReq{len:pktLen});
      if (verbose) $display("Pktgen:: fetch_packet pktlen=%h", pktLen);
   endrule

   rule enqueue_packet if (pktCount>0 && !idle);
      let data <- buff.readServer.readData.get;
      buff.writeServer.writeData.put(data);
      outgoing_fifo.enq(data);

      if (data.eop) begin
         if (!infiniteLoop)
            pktCount <= pktCount - 1;
         idle <= True;
         currIPG <= 0;
         if (verbose) $display("Pktgen:: eop %h %h %h %h", idle, started, currIPG, ipgCount);
      end
   endrule

   rule compute_idle if (pktCount>0 && idle);
      if (currIPG < ipgCount + fromInteger(valueOf(MinimumIPG))) begin
         currIPG <= currIPG + fromInteger(valueOf(bytesPerBeat));
         if (verbose) $display("Pktgen:: ipg = %d", currIPG);
      end
      else begin
         currIPG <= 0;
         idle <= False;
      end
   endrule

   // has to drain buffer at the end of packet generation
   rule cleanup if (pktCount==0 && traceLen>0 && started);
      let pktLen <- buff.readServer.readLen.get;
      buff.readServer.readReq.put(EtherReq{len: pktLen});
      if (verbose) $display("Pktgen:: drain buffer");
   endrule

   rule drainBufferPayload if (pktCount==0 && traceLen>0 && started);
      let data <- buff.readServer.readData.get;
      // do nothing
      if (verbose) $display("Pktgen:: drain buffer payload");
      if (data.eop) begin
         traceLen <= traceLen - 1;
      end
   endrule

   rule drainFinished if (pktCount==0 && traceLen==0 && started);
      started <= False;
   endrule

   interface PktWriteServer writeServer;
      interface Put writeData;
         method Action put (EtherData d);
            buff.writeServer.writeData.put(d);
            if (verbose) $display("Pktgen:: write data", fshow(d));
            if (d.eop) begin
               traceLen <= traceLen + 1;
            end
         endmethod
      endinterface
   endinterface
   interface PktWriteClient writeClient;
       interface Get writeData = toGet(outgoing_fifo);
   endinterface
   method Action start(Bit#(32) pc, Bit#(32) ipg) if (pktCount==0 && traceLen!=0);
      started <= True;
      ipgCount <= ipg;
      if (pc != 0) begin
         pktCount <= pc;
      end
      else begin
         pktCount <= 1;
         infiniteLoop <= True;
      end
      if (verbose) $display("Pktgen:: start %h %h", pc, ipg);
   endmethod
   method Action stop();
      infiniteLoop <= False;
   endmethod
endmodule


Add proper check for Mmode with epar and par csrs, currently deactivated

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// 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.

`include "ProcConfig.bsv"
import ClientServer::*;
import DefaultValue::*;
import GetPut::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
import Performance::*;
import FullAssocTlb::*;
import ConfigReg::*;
import Fifo::*;
import Cntrs::*;
import SafeCounter::*;
import CacheUtils::*;
import LatencyTimer::*;

// currently blocking
typedef `L1_TLB_SIZE ITlbSize;

typedef struct {
    Vpn vpn;
} ITlbRqToP deriving(Bits, Eq, FShow);

typedef struct {
    // may get page fault: i.e. hit invalid page or
    // get non-leaf page at last-level page table
    Maybe#(TlbEntry) entry;
} ITlbRsFromP deriving(Bits, Eq, FShow);

interface ITlbToParent;
    interface FifoDeq#(ITlbRqToP) rqToP;
    interface FifoEnq#(ITlbRsFromP) rsFromP;
    // after ITLB flush itself, it notifies L2, and wait L2 to flush
    interface Client#(void, void) flush;
endinterface

interface ITlb;
    // system consistency related
    method Bool flush_done;
    method Action flush;
    method Action updateVMInfo(VMInfo vm);
    method Bool noPendingReq;

    // req/resp with core
    interface Server#(Addr, TlbResp) to_proc;

    // req/resp with L2 TLB
    interface ITlbToParent toParent;

    // performance
    interface Perf#(L1TlbPerfType) perf;
endinterface

typedef FullAssocTlb#(ITlbSize) ITlbArray;
module mkITlbArray(ITlbArray);
    let m <- mkFullAssocTlb(True); // randomness in replacement
    return m;
endmodule

(* synthesize *)
module mkITlb(ITlb::ITlb);
    Bool verbose = True;

    // TLB array
    ITlbArray tlb <- mkITlbArray;

    // processor init flushing by setting this flag
    Reg#(Bool) needFlush <- mkReg(False);
    // after flushing ITLB itself, we want parent TLB to flush
    Reg#(Bool) waitFlushP <- mkReg(False);

    // resp FIFO to proc
    Fifo#(2, TlbResp) hitQ <- mkCFFifo;

    // current processor VM information
    Reg#(VMInfo) vm_info <- mkReg(defaultValue);

    // blocking miss
    Reg#(Maybe#(Addr)) miss <- mkReg(Invalid);

    // req & resp with parent TLB
    Fifo#(2, ITlbRqToP) rqToPQ <- mkCFFifo;
    Fifo#(2, ITlbRsFromP) rsFromPQ <- mkCFFifo;
    // flush req/resp with parent TLB
    Fifo#(1, void) flushRqToPQ <- mkCFFifo;
    Fifo#(1, void) flushRsFromPQ <- mkCFFifo;

    // perf counters
    Fifo#(1, L1TlbPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
    Fifo#(1, PerfResp#(L1TlbPerfType)) perfRespQ <- mkCFFifo;
    Reg#(Bool) doStats <- mkConfigReg(False);
    Count#(Data) accessCnt <- mkCount(0);
    Count#(Data) missCnt <- mkCount(0);
    Count#(Data) missLat <- mkCount(0);

    LatencyTimer#(2, 12) latTimer <- mkLatencyTimer; // max latency: 4K cycles

    rule doPerf;
        let t <- toGet(perfReqQ).get;
        Data d = (case(t)
            L1TlbAccessCnt: (accessCnt);
            L1TlbMissParentCnt: (missCnt);
            L1TlbMissParentLat: (missLat);
            default: (0);
        endcase);
        perfRespQ.enq(PerfResp {
            pType: t,
            data: d
        });
    endrule
`endif

    // do flush: only start when all misses resolve
    rule doStartFlush(needFlush && !waitFlushP && !isValid(miss));
        tlb.flush;
        // request parent TLB to flush
        flushRqToPQ.enq(?);
        waitFlushP <= True;
        if(verbose) $display("ITLB %m: flush begin");
    endrule

    rule doFinishFlush(needFlush && waitFlushP && !isValid(miss));
        flushRsFromPQ.deq;
        needFlush <= False;
        waitFlushP <= False;
        if(verbose) $display("ITLB %m: flush done");
    endrule

    rule doRsFromP(miss matches tagged Valid .vaddr);
        rsFromPQ.deq;
        let pRs = rsFromPQ.first;

        if(pRs.entry matches tagged Valid .en) begin
            // TODO when we have multiple misses in future.  We first need to
            // search TLB to check whether the PTE is already in TLB; this may
            // happen for mega/giga pages.  We don't want same PTE to occupy >1
            // TLB entires.

            // check permission
            if(hasVMPermission(vm_info,
                               en.pteType,
                               en.ppn,
                               en.level,
                               InstFetch)) begin
                // fill TLB and resp to proc
                tlb.addEntry(en);
                let trans_addr = translate(vaddr, en.ppn, en.level);
                hitQ.enq(tuple2(trans_addr, Invalid));
                if(verbose) begin
                    $display("ITLB %m refill: ", fshow(vaddr),
                             " ; ", fshow(trans_addr));
                end
            end
            else begin
                // page fault
                hitQ.enq(tuple2(?, Valid (InstPageFault)));
                if(verbose) begin
                    $display("ITLB %m refill no permission: ", fshow(vaddr));
                end
            end
        end
        else begin
            // page fault
            hitQ.enq(tuple2(?, Valid (InstPageFault)));
            if(verbose) $display("ITLB %m refill page fault: ", fshow(vaddr));
        end
        // miss resolved
        miss <= Invalid;

`ifdef PERF_COUNT
        let lat <- latTimer.done(0);
        if(doStats) begin
            missLat.incr(zeroExtend(lat));
        end
`endif
    endrule

    // we check no pending req only at Commit when Fetch1 stage has been
    // stalled (setWaitRedirect has been called in previous cycle). Note that
    // Commit calls redirect method which is ordered after Fetch1, so checking
    // no pending req cannot sequence before Fetch1 which calls iTlb.req.
    // Therefore, we use a wire to catch the pending req a the beginning of the
    // cycle. This does not matter because Fetch1 stage has been stalled.
    Wire#(Bool) no_pending_wire <- mkBypassWire;
    (* fire_when_enabled, no_implicit_conditions *)
    rule set_no_pending;
        no_pending_wire <= !isValid(miss);
    endrule

    method Action flush if(!needFlush);
        needFlush <= True;
        waitFlushP <= False;
        // this won't interrupt current processing, since
        // (1) miss process will continue even if needFlush=True
        // (2) flush truly starts when there is no pending req
    endmethod

    method Bool flush_done = !needFlush;

    method Action updateVMInfo(VMInfo vm);
        vm_info <= vm;
    endmethod

    method Bool noPendingReq = no_pending_wire._read;

    interface Server to_proc;
        interface Put request;
            // We do not accept new req when flushing flag is set.  We also
            // make the guard more restrictive to reduce the time of computing
            // guard i.e. guard does not depend on whether TLB hit or miss
            method Action put(Addr vaddr) if(
                !needFlush && !isValid(miss) && hitQ.notFull && rqToPQ.notFull
            );
`ifdef SECURITY
                // Security Check
                // For non M-mode forbid translating any virtual pc outside of
                // the protection domain. Be careful, in M mode we don't take
                // into account the same vm_info: changing the evbase and
                // evmask in M in en security monitor would lead to failure
                // before we have a chance to go back to U: while in M mode
                // sanctum_evbase is morally from an other set of CSR, which is
                // constant and hardcoded in our implementation.

                // TODO TODO TODO TODO WARNING DANGER NO SECURITY HERE we need
                // to replace sanctum_evbase and evmask with the actual fixed
                // SM region
                VMInfo parvm_info = vm_info;
                VMInfo eparvm_info = vm_info;
                parvm_info.sanctum_evbase = maxBound;
                parvm_info.sanctum_evmask = 0;
                eparvm_info.sanctum_evbase = maxBound;
                eparvm_info.sanctum_evmask = 0;
                if ((vm_info.prv == prvM ? (outOfProtectionDomain(parvm_info,vaddr) && outOfProtectionDomain(eparvm_info,vaddr)) : outOfProtectionDomain(vm_info, vaddr))) begin
                    hitQ.enq(tuple2(?, Valid (InstAccessFault)));
                end
`else
                // No security check
                if (False) begin
                    noAction;
                end
`endif
                else if (vm_info.sv39) begin
                    let vpn = getVpn(vaddr);
                    let trans_result = tlb.translate(vpn, vm_info.asid);
                    if (trans_result.hit) begin
                        // TLB hit
                        let entry = trans_result.entry;
                        // check permission
                        if(hasVMPermission(vm_info,
                                           entry.pteType,
                                           entry.ppn,
                                           entry.level,
                                           InstFetch)) begin
                            // update replacement info
                            tlb.updateRepByHit(trans_result.index);
                            // translate addr
                            Addr trans_addr = translate(
                                vaddr, entry.ppn, entry.level
                            );
                            hitQ.enq(tuple2(trans_addr, Invalid));
                            if(verbose) begin
                                $display("ITLB %m req (hit): ", fshow(vaddr),
                                         " ; ", fshow(trans_result));
                            end
                        end
                        else begin
                            // page fault
                            hitQ.enq(tuple2(?, Valid (InstPageFault)));
                            if(verbose) begin
                                $display("ITLB %m req no permission: ",
                                         fshow(vaddr));
                            end
                        end
                    end
                    else begin
                        // TLB miss, req to parent TLB
                        miss <= Valid (vaddr);
                        rqToPQ.enq(ITlbRqToP {vpn: vpn});
                        if(verbose) begin
                            $display("ITLB %m req (miss): ", fshow(vaddr));
                        end
`ifdef PERF_COUNT
                        latTimer.start(0);
                        if(doStats) begin
                            missCnt.incr(1);
                        end
`endif
                    end
                end
                else begin
                    // bare mode, no translation
                    hitQ.enq(tuple2(vaddr, Invalid));
                    if (verbose) $display("ITLB %m req (bare): ", fshow(vaddr));
                end

`ifdef PERF_COUNT
                if(doStats) begin
                    accessCnt.incr(1);
                end
`endif
            endmethod
        endinterface
        interface Get response = toGet(hitQ);
    endinterface

    interface ITlbToParent toParent;
        interface rqToP = toFifoDeq(rqToPQ);
        interface rsFromP = toFifoEnq(rsFromPQ);
        interface Client flush;
            interface request = toGet(flushRqToPQ);
            interface response = toPut(flushRsFromPQ);
        endinterface
    endinterface

    interface Perf perf;
        method Action setStatus(Bool stats);
`ifdef PERF_COUNT
            doStats <= stats;
`else
            noAction;
`endif
        endmethod

        method Action req(L1TlbPerfType r);
            perfReqQ.enq(r);
        endmethod

        method ActionValue#(PerfResp#(L1TlbPerfType)) resp;
`ifdef PERF_COUNT
            perfRespQ.deq;
            return perfRespQ.first;
`else
            perfReqQ.deq;
            return PerfResp {
                pType: perfReqQ.first,
                data: 0
            };
`endif
        endmethod

        method Bool respValid;
`ifdef PERF_COUNT
            return perfRespQ.notEmpty;
`else
            return perfReqQ.notEmpty;
`endif
        endmethod
    endinterface
endmodule

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// 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.

`include "ProcConfig.bsv"
import ClientServer::*;
import DefaultValue::*;
import GetPut::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
import Performance::*;
import FullAssocTlb::*;
import ConfigReg::*;
import Fifo::*;
import Cntrs::*;
import SafeCounter::*;
import CacheUtils::*;
import LatencyTimer::*;

// currently blocking
typedef `L1_TLB_SIZE ITlbSize;

typedef struct {
    Vpn vpn;
} ITlbRqToP deriving(Bits, Eq, FShow);

typedef struct {
    // may get page fault: i.e. hit invalid page or
    // get non-leaf page at last-level page table
    Maybe#(TlbEntry) entry;
} ITlbRsFromP deriving(Bits, Eq, FShow);

interface ITlbToParent;
    interface FifoDeq#(ITlbRqToP) rqToP;
    interface FifoEnq#(ITlbRsFromP) rsFromP;
    // after ITLB flush itself, it notifies L2, and wait L2 to flush
    interface Client#(void, void) flush;
endinterface

interface ITlb;
    // system consistency related
    method Bool flush_done;
    method Action flush;
    method Action updateVMInfo(VMInfo vm);
    method Bool noPendingReq;

    // req/resp with core
    interface Server#(Addr, TlbResp) to_proc;

    // req/resp with L2 TLB
    interface ITlbToParent toParent;

    // performance
    interface Perf#(L1TlbPerfType) perf;
endinterface

typedef FullAssocTlb#(ITlbSize) ITlbArray;
module mkITlbArray(ITlbArray);
    let m <- mkFullAssocTlb(True); // randomness in replacement
    return m;
endmodule

(* synthesize *)
module mkITlb(ITlb::ITlb);
    Bool verbose = True;

    // TLB array
    ITlbArray tlb <- mkITlbArray;

    // processor init flushing by setting this flag
    Reg#(Bool) needFlush <- mkReg(False);
    // after flushing ITLB itself, we want parent TLB to flush
    Reg#(Bool) waitFlushP <- mkReg(False);

    // resp FIFO to proc
    Fifo#(2, TlbResp) hitQ <- mkCFFifo;

    // current processor VM information
    Reg#(VMInfo) vm_info <- mkReg(defaultValue);

    // blocking miss
    Reg#(Maybe#(Addr)) miss <- mkReg(Invalid);

    // req & resp with parent TLB
    Fifo#(2, ITlbRqToP) rqToPQ <- mkCFFifo;
    Fifo#(2, ITlbRsFromP) rsFromPQ <- mkCFFifo;
    // flush req/resp with parent TLB
    Fifo#(1, void) flushRqToPQ <- mkCFFifo;
    Fifo#(1, void) flushRsFromPQ <- mkCFFifo;

    // perf counters
    Fifo#(1, L1TlbPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
    Fifo#(1, PerfResp#(L1TlbPerfType)) perfRespQ <- mkCFFifo;
    Reg#(Bool) doStats <- mkConfigReg(False);
    Count#(Data) accessCnt <- mkCount(0);
    Count#(Data) missCnt <- mkCount(0);
    Count#(Data) missLat <- mkCount(0);

    LatencyTimer#(2, 12) latTimer <- mkLatencyTimer; // max latency: 4K cycles

    rule doPerf;
        let t <- toGet(perfReqQ).get;
        Data d = (case(t)
            L1TlbAccessCnt: (accessCnt);
            L1TlbMissParentCnt: (missCnt);
            L1TlbMissParentLat: (missLat);
            default: (0);
        endcase);
        perfRespQ.enq(PerfResp {
            pType: t,
            data: d
        });
    endrule
`endif

    // do flush: only start when all misses resolve
    rule doStartFlush(needFlush && !waitFlushP && !isValid(miss));
        tlb.flush;
        // request parent TLB to flush
        flushRqToPQ.enq(?);
        waitFlushP <= True;
        if(verbose) $display("ITLB %m: flush begin");
    endrule

    rule doFinishFlush(needFlush && waitFlushP && !isValid(miss));
        flushRsFromPQ.deq;
        needFlush <= False;
        waitFlushP <= False;
        if(verbose) $display("ITLB %m: flush done");
    endrule

    rule doRsFromP(miss matches tagged Valid .vaddr);
        rsFromPQ.deq;
        let pRs = rsFromPQ.first;

        if(pRs.entry matches tagged Valid .en) begin
            // TODO when we have multiple misses in future.  We first need to
            // search TLB to check whether the PTE is already in TLB; this may
            // happen for mega/giga pages.  We don't want same PTE to occupy >1
            // TLB entires.

            // check permission
            if(hasVMPermission(vm_info,
                               en.pteType,
                               en.ppn,
                               en.level,
                               InstFetch)) begin
                // fill TLB and resp to proc
                tlb.addEntry(en);
                let trans_addr = translate(vaddr, en.ppn, en.level);
                hitQ.enq(tuple2(trans_addr, Invalid));
                if(verbose) begin
                    $display("ITLB %m refill: ", fshow(vaddr),
                             " ; ", fshow(trans_addr));
                end
            end
            else begin
                // page fault
                hitQ.enq(tuple2(?, Valid (InstPageFault)));
                if(verbose) begin
                    $display("ITLB %m refill no permission: ", fshow(vaddr));
                end
            end
        end
        else begin
            // page fault
            hitQ.enq(tuple2(?, Valid (InstPageFault)));
            if(verbose) $display("ITLB %m refill page fault: ", fshow(vaddr));
        end
        // miss resolved
        miss <= Invalid;

`ifdef PERF_COUNT
        let lat <- latTimer.done(0);
        if(doStats) begin
            missLat.incr(zeroExtend(lat));
        end
`endif
    endrule

    // we check no pending req only at Commit when Fetch1 stage has been
    // stalled (setWaitRedirect has been called in previous cycle). Note that
    // Commit calls redirect method which is ordered after Fetch1, so checking
    // no pending req cannot sequence before Fetch1 which calls iTlb.req.
    // Therefore, we use a wire to catch the pending req a the beginning of the
    // cycle. This does not matter because Fetch1 stage has been stalled.
    Wire#(Bool) no_pending_wire <- mkBypassWire;
    (* fire_when_enabled, no_implicit_conditions *)
    rule set_no_pending;
        no_pending_wire <= !isValid(miss);
    endrule

    method Action flush if(!needFlush);
        needFlush <= True;
        waitFlushP <= False;
        // this won't interrupt current processing, since
        // (1) miss process will continue even if needFlush=True
        // (2) flush truly starts when there is no pending req
    endmethod

    method Bool flush_done = !needFlush;

    method Action updateVMInfo(VMInfo vm);
        vm_info <= vm;
    endmethod

    method Bool noPendingReq = no_pending_wire._read;

    interface Server to_proc;
        interface Put request;
            // We do not accept new req when flushing flag is set.  We also
            // make the guard more restrictive to reduce the time of computing
            // guard i.e. guard does not depend on whether TLB hit or miss
            method Action put(Addr vaddr) if(
                !needFlush && !isValid(miss) && hitQ.notFull && rqToPQ.notFull
            );
`ifdef SECURITY
                // Security Check
                // For non M-mode forbid translating any virtual pc outside of
                // the protection domain. Be careful, in M mode we don't take
                // into account the same vm_info: changing the evbase and
                // evmask in M in en security monitor would lead to failure
                // before we have a chance to go back to U: while in M mode
                // sanctum_evbase is morally from an other set of CSR, which is
                // constant and hardcoded in our implementation.

                // TODO TODO TODO TODO WARNING DANGER NO SECURITY HERE we need
                // to replace sanctum_evbase and evmask with the actual fixed
                // SM region
                VMInfo mvm_info = vm_info;
                mvm_info.sanctum_evbase = maxBound;
                mvm_info.sanctum_evmask = 0;
                if (outOfProtectionDomain(vm_info.prv == prvM ? mvm_info : vm_info, vaddr)) begin
                    hitQ.enq(tuple2(?, Valid (InstAccessFault)));
                end
`else
                // No security check
                if (False) begin
                    noAction;
                end
`endif
                else if (vm_info.sv39) begin
                    let vpn = getVpn(vaddr);
                    let trans_result = tlb.translate(vpn, vm_info.asid);
                    if (trans_result.hit) begin
                        // TLB hit
                        let entry = trans_result.entry;
                        // check permission
                        if(hasVMPermission(vm_info,
                                           entry.pteType,
                                           entry.ppn,
                                           entry.level,
                                           InstFetch)) begin
                            // update replacement info
                            tlb.updateRepByHit(trans_result.index);
                            // translate addr
                            Addr trans_addr = translate(
                                vaddr, entry.ppn, entry.level
                            );
                            hitQ.enq(tuple2(trans_addr, Invalid));
                            if(verbose) begin
                                $display("ITLB %m req (hit): ", fshow(vaddr),
                                         " ; ", fshow(trans_result));
                            end
                        end
                        else begin
                            // page fault
                            hitQ.enq(tuple2(?, Valid (InstPageFault)));
                            if(verbose) begin
                                $display("ITLB %m req no permission: ",
                                         fshow(vaddr));
                            end
                        end
                    end
                    else begin
                        // TLB miss, req to parent TLB
                        miss <= Valid (vaddr);
                        rqToPQ.enq(ITlbRqToP {vpn: vpn});
                        if(verbose) begin
                            $display("ITLB %m req (miss): ", fshow(vaddr));
                        end
`ifdef PERF_COUNT
                        latTimer.start(0);
                        if(doStats) begin
                            missCnt.incr(1);
                        end
`endif
                    end
                end
                else begin
                    // bare mode, no translation
                    hitQ.enq(tuple2(vaddr, Invalid));
                    if (verbose) $display("ITLB %m req (bare): ", fshow(vaddr));
                end

`ifdef PERF_COUNT
                if(doStats) begin
                    accessCnt.incr(1);
                end
`endif
            endmethod
        endinterface
        interface Get response = toGet(hitQ);
    endinterface

    interface ITlbToParent toParent;
        interface rqToP = toFifoDeq(rqToPQ);
        interface rsFromP = toFifoEnq(rsFromPQ);
        interface Client flush;
            interface request = toGet(flushRqToPQ);
            interface response = toPut(flushRsFromPQ);
        endinterface
    endinterface

    interface Perf perf;
        method Action setStatus(Bool stats);
`ifdef PERF_COUNT
            doStats <= stats;
`else
            noAction;
`endif
        endmethod

        method Action req(L1TlbPerfType r);
            perfReqQ.enq(r);
        endmethod

        method ActionValue#(PerfResp#(L1TlbPerfType)) resp;
`ifdef PERF_COUNT
            perfRespQ.deq;
            return perfRespQ.first;
`else
            perfReqQ.deq;
            return PerfResp {
                pType: perfReqQ.first,
                data: 0
            };
`endif
        endmethod

        method Bool respValid;
`ifdef PERF_COUNT
            return perfRespQ.notEmpty;
`else
            return perfReqQ.notEmpty;
`endif
        endmethod
    endinterface
endmodule