propagate DmaErrorOffsetOutOfRange to address translation response

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

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

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

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

// BSV Libraries
import RegFile::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import Connectable::*;
import BRAMFIFO::*;
import BRAM::*;

import ConnectalBram::*;
import MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;
import SimDma::*;

`include "ConnectalProjectConfig.bsv"

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

typedef 8 IndexWidth;

typedef struct {
   SGListId             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;
   SGListId ptr;
   } Stage4Params deriving (Bits);

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

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

   // for simulators
   SimDma#(32) simDma <- mkSimDma();

   // 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) translationTable <- 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());
   for (Integer i = 0; i < 2; i = i + 1)
      mkConnection(toGet(dmaErrorFifos[i]), toPut(dmaErrorFifo));
   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 { error: DmaErrorNone, 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) });
	    off.error = DmaErrorOffsetOutOfRange;
	    p         = 0; // FIXME
	 end

	 if (verbose) $display("mkMMU::translationTable[%d].read %h", i, {ptr,p});
	 portsel(translationTable, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
							      address:{ptr,p}, datain:?});
	 offs1[i].enq(off);

      endrule
      rule stage5; // Concatenate page base address from sglist entry with LSB offset bits from request and return
	 Page0 page <- portsel(translationTable, 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: offset.error, physAddr: truncate(rv) });
      endrule
   end

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

   // given that the BRAM is faster than the connection from software, I see no need for a SizedBRAMFIFOF here. -Jamey
   FIFOF#(Bit#(32)) idReturnFifo <- mkFIFOF();
   rule idReturnRule;
      let sglId <- toGet(idReturnFifo).get;
      sglId_gen.returnTag(truncate(sglId));
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False, address: truncate(sglId), datain: tagged Invalid });
      $display("idReturn %d", sglId);
   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);
	     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
		return rv;
	     endmethod
	  endinterface
       endinterface);
   endfunction

   interface MMURequest request;
   method Action idRequest(SpecialTypeForSendingFd fd);
      let nextId <- sglId_gen.getTag;
      let resp = (fromInteger(iid) << 16) | extend(nextId);
      if (verbose) $display("mkMMU::idRequest %d", fd);
      let va <- simDma.initfd(resp, fd);
      mmuIndication.idResponse(resp);
   endmethod
   method Action idReturn(Bit#(32) sglId);
      idReturnFifo.enq(sglId);
      if (hostMapped)
	 simDma.idreturn(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
	 if(hostMapped)
	    let va <- simDma.init({0,pointer[31:16]}, {0,pointer[15:0]}, len);
         Bit#(IndexWidth) ind = truncate(pointerIndex);
	 portsel(translationTable, 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.

// BSV Libraries
import RegFile::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import Connectable::*;
import BRAMFIFO::*;
import BRAM::*;

import ConnectalBram::*;
import MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;
import SimDma::*;

`include "ConnectalProjectConfig.bsv"

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

typedef 8 IndexWidth;

typedef struct {
   SGListId             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 {
   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;
   SGListId ptr;
   } Stage4Params deriving (Bits);

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

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

   // for simulators
   SimDma#(32) simDma <- mkSimDma();

   // 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) translationTable <- 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());
   for (Integer i = 0; i < 2; i = i + 1)
      mkConnection(toGet(dmaErrorFifos[i]), toPut(dmaErrorFifo));
   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::translationTable[%d].read %h", i, {ptr,p});
	    portsel(translationTable, 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(translationTable, 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#(SGListId) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      let ptr <- toGet(configRespFifo).get();
      mmuIndication.configResp(extend(ptr));
   endrule

   // given that the BRAM is faster than the connection from software, I see no need for a SizedBRAMFIFOF here. -Jamey
   FIFOF#(Bit#(32)) idReturnFifo <- mkFIFOF();
   rule idReturnRule;
      let sglId <- toGet(idReturnFifo).get;
      sglId_gen.returnTag(truncate(sglId));
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False, address: truncate(sglId), datain: tagged Invalid });
      $display("idReturn %d", sglId);
   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);
	     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
		return rv;
	     endmethod
	  endinterface
       endinterface);
   endfunction

   interface MMURequest request;
   method Action idRequest(SpecialTypeForSendingFd fd);
      let nextId <- sglId_gen.getTag;
      let resp = (fromInteger(iid) << 16) | extend(nextId);
      if (verbose) $display("mkMMU::idRequest %d", fd);
      let va <- simDma.initfd(resp, fd);
      mmuIndication.idResponse(resp);
   endmethod
   method Action idReturn(Bit#(32) sglId);
      idReturnFifo.enq(sglId);
      if (hostMapped)
	 simDma.idreturn(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
	 if(hostMapped)
	    let va <- simDma.init({0,pointer[31:16]}, {0,pointer[15:0]}, len);
         Bit#(IndexWidth) ind = truncate(pointerIndex);
	 portsel(translationTable, 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


fix docall prototype in interface claue

fix docall prototype in interface claue

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

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

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

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

/*
 * Implementation of:
 *    simple stack for a type
 *
 * Single cycle push and pop, provided that consecutive pops aren't
 * too far apart
 */

import BRAM::*;

interface StackReg#(numeric type stackSize, type pctype, type argstype, type varstype);
   method Action doreturn();
   method Action docall(pctype jumpto, pctype returnto, argstype args, varstype vars);
   method Action nextpc(pctype jumpto);
   method Bit#(16) setjmp();
   method Action longjump(Bit#(16) where);
   interface Reg#(pctype) pc;
   interface Reg#(argstype) args;
   interface Reg#(varstype) vars;
endinterface

module mkStackReg#(int stackSize, pctype initialpc)(StackReg#(stackSize, pctype, argstype, varstype))
   provisos(Log#(stackSize, addressBits),
      Bits#(pctype, a__),
      Bits#(argstype, b__),
      Bits#(varstype, c__));

   BRAM1Port#(Bit#(addressBits), pctype) pcstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), argstype) argsstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), varstype) varsstack  <- mkBRAM1Server(defaultValue);
   Reg#(pctype) pctop <- mkReg(initialpc);
   Reg#(pctype) pcnext <- mkReg(?);
   Reg#(argstype) argstop <- mkReg(?);
   Reg#(argstype) argsnext <- mkReg(?);
   Reg#(varstype) varstop <- mkReg(?);
   Reg#(varstype) varsnext <- mkReg(?);
   Reg#(Bit#(addressBits)) fp <- mkReg(0);
   PulseWire calling <- mkPulseWire();
   PulseWire returning <- mkPulseWire();
   Reg#(Bool) returningd1 <- mkReg(False);

/* The "next" registers are there to compensate for the pipelined reads
 * of the BRAMs.
 * A return in isolation pops next to top, and then replaces the contents of
 * next the following cycle when the BRAM read completes.
 * In the event that there are multiple returns in consecutive cycles, the
 * first return values are fed from the next registers into top, and the
 * following values are bypassed from the BRAMs direct to TOP.
 * Once return cycles stop happening, the final pending BRAM read data is
 * loaded into next
 *
 * If a call happens in the cycle after a return, the BRAM read <into> next and
 * the bram write <from> next are both supressed, and next is pushed from top
 *
 * The wire calling says there is a call in the current cycle. The signal
 * returning says there is a return in the current cycle. The signal returningd1
 * says there was a return in the previous cycle.
 */

/*
   Reg#(Bit#(8)) cyc <- mkReg(0);

   rule cc;
      cyc <= cyc + 1;
   endrule
  */

   /* The pop rules fire exactly when returningd1 is true */
   rule poppc;
     let v = ?;
      //$display("%d poppc (c %d)", cyc, calling);
      v <- pcstack.portA.response.get();
      if (!calling) pcnext <= v;
      if (returning) pctop <= v;
   endrule

   rule popargs;
     let v = ?;
      v <- argsstack.portA.response.get();
      if (!calling) argsnext <= v;
      if (returning) argstop <= v;
   endrule

   rule popvars;
     let v = ?;
      v <- varsstack.portA.response.get();
      if (!calling) varsnext <= v;
      if (returning) varstop <= v;
   endrule

   (* fire_when_enabled, no_implicit_conditions *)
   rule returning_delay;
      returningd1 <= returning;
   endrule

   method Action docall(pctype jumpto, pctype returnto, argstype args, varstype vars);
      fp <= min(fp+1, maxBound);
      calling.send();
      //$display("%d docall jumpto %d returnto %d (d1 %d)", cyc, jumpto, returnto, returningd1);
      if (! returningd1)
	 begin
	    pcstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: pcnext});
	    argsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: argsnext});
	    varsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: varsnext});
	 end
      pcnext <= returnto;
      pctop <= jumpto;
      argsnext <= argstop;
      argstop <= args;
      varsnext <= varstop;
   endmethod

   method Action doreturn();
      fp <= max(fp-1, 0);
      returning.send();

      pcstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      argsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      varsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      //$display("%d doreturn pctop getting %d", cyc, pcnext);
     if (!returningd1)
	begin
	   pctop <= pcnext;
	   argstop <= argsnext;
	   varstop <= varsnext;
	end
   endmethod

   method Action nextpc(pctype jumpto);
      pctop <= jumpto;
   endmethod

   method Bit#(16) setjmp();
      return 0;
   endmethod

   method Action longjump(Bit#(16) where);
   // set fp <= where and pop? something like that
   endmethod

interface pc = pctop;
interface args = argstop;
interface vars = varstop;

endmodule

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

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

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

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

/*
 * Implementation of:
 *    simple stack for a type
 *
 * Single cycle push and pop, provided that consecutive pops aren't
 * too far apart
 */

import BRAM::*;

interface StackReg#(numeric type stackSize, type pctype, type argstype, type varstype);
   method Action doreturn();
   method Action docall(pctype jumpto, pctype returnto, argstype args);
   method Action nextpc(pctype jumpto);
   method Bit#(16) setjmp();
   method Action longjump(Bit#(16) where);
   interface Reg#(pctype) pc;
   interface Reg#(argstype) args;
   interface Reg#(varstype) vars;
endinterface

module mkStackReg#(int stackSize, pctype initialpc)(StackReg#(stackSize, pctype, argstype, varstype))
   provisos(Log#(stackSize, addressBits),
      Bits#(pctype, a__),
      Bits#(argstype, b__),
      Bits#(varstype, c__));

   BRAM1Port#(Bit#(addressBits), pctype) pcstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), argstype) argsstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), varstype) varsstack  <- mkBRAM1Server(defaultValue);
   Reg#(pctype) pctop <- mkReg(initialpc);
   Reg#(pctype) pcnext <- mkReg(?);
   Reg#(argstype) argstop <- mkReg(?);
   Reg#(argstype) argsnext <- mkReg(?);
   Reg#(varstype) varstop <- mkReg(?);
   Reg#(varstype) varsnext <- mkReg(?);
   Reg#(Bit#(addressBits)) fp <- mkReg(0);
   PulseWire calling <- mkPulseWire();
   PulseWire returning <- mkPulseWire();
   Reg#(Bool) returningd1 <- mkReg(False);

/* The "next" registers are there to compensate for the pipelined reads
 * of the BRAMs.
 * A return in isolation pops next to top, and then replaces the contents of
 * next the following cycle when the BRAM read completes.
 * In the event that there are multiple returns in consecutive cycles, the
 * first return values are fed from the next registers into top, and the
 * following values are bypassed from the BRAMs direct to TOP.
 * Once return cycles stop happening, the final pending BRAM read data is
 * loaded into next
 *
 * If a call happens in the cycle after a return, the BRAM read <into> next and
 * the bram write <from> next are both supressed, and next is pushed from top
 *
 * The wire calling says there is a call in the current cycle. The signal
 * returning says there is a return in the current cycle. The signal returningd1
 * says there was a return in the previous cycle.
 */

/*
   Reg#(Bit#(8)) cyc <- mkReg(0);

   rule cc;
      cyc <= cyc + 1;
   endrule
  */

   /* The pop rules fire exactly when returningd1 is true */
   rule poppc;
     let v = ?;
      //$display("%d poppc (c %d)", cyc, calling);
      v <- pcstack.portA.response.get();
      if (!calling) pcnext <= v;
      if (returning) pctop <= v;
   endrule

   rule popargs;
     let v = ?;
      v <- argsstack.portA.response.get();
      if (!calling) argsnext <= v;
      if (returning) argstop <= v;
   endrule

   rule popvars;
     let v = ?;
      v <- varsstack.portA.response.get();
      if (!calling) varsnext <= v;
      if (returning) varstop <= v;
   endrule

   (* fire_when_enabled, no_implicit_conditions *)
   rule returning_delay;
      returningd1 <= returning;
   endrule

   method Action docall(pctype jumpto, pctype returnto, argstype args, varstype vars);
      fp <= min(fp+1, maxBound);
      calling.send();
      //$display("%d docall jumpto %d returnto %d (d1 %d)", cyc, jumpto, returnto, returningd1);
      if (! returningd1)
	 begin
	    pcstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: pcnext});
	    argsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: argsnext});
	    varsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: varsnext});
	 end
      pcnext <= returnto;
      pctop <= jumpto;
      argsnext <= argstop;
      argstop <= args;
      varsnext <= varstop;
   endmethod

   method Action doreturn();
      fp <= max(fp-1, 0);
      returning.send();

      pcstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      argsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      varsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      //$display("%d doreturn pctop getting %d", cyc, pcnext);
     if (!returningd1)
	begin
	   pctop <= pcnext;
	   argstop <= argsnext;
	   varstop <= varsnext;
	end
   endmethod

   method Action nextpc(pctype jumpto);
      pctop <= jumpto;
   endmethod

   method Bit#(16) setjmp();
      return 0;
   endmethod

   method Action longjump(Bit#(16) where);
   // set fp <= where and pop? something like that
   endmethod

interface pc = pctop;
interface args = argstop;
interface vars = varstop;

endmodule


Paxos: don't drop payload in deparsing paxos

// Copyright (c) 2016 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 ClientServer::*;
import Connectable::*;
import DefaultValue::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import StmtFSM::*;
import SpecialFIFOs::*;
import Vector::*;
import Pipe::*;
import Ethernet::*;
import PaxosTypes::*;
import PacketBuffer::*;
import P4Types::*;

typedef enum {
   StateDeparseIdle,
   StateDeparseEthernet,
   StateDeparseArp,
   StateDeparseIpv4,
   StateDeparseIpv6,
   StateDeparseUdp,
   StateDeparsePaxos
} DeparserState deriving (Bits, Eq, FShow);

function Tuple2#(EthernetT, EthernetT) toEthernet(MetadataT meta);
   EthernetT data = defaultValue;
   EthernetT mask = defaultMask;
   data.dstAddr = fromMaybe(?, meta.dstAddr);
   mask.dstAddr = 0;
   data.etherType = fromMaybe(?, meta.etherType);
   mask.etherType = 0;
   return tuple2(data, mask);
endfunction

function Tuple2#(Ipv4T, Ipv4T) toIpv4(MetadataT meta);
   Ipv4T ipv4 = defaultValue;
   Ipv4T mask = defaultMask;
   ipv4.protocol = fromMaybe(?, meta.protocol);
   mask.protocol = 0;
   return tuple2(ipv4, mask);
endfunction

function Tuple2#(UdpT, UdpT) toUdp(MetadataT meta);
   UdpT udp = defaultValue;
   UdpT mask = defaultMask;
   udp.dstPort = fromMaybe(?, meta.dstPort);
   mask.dstPort = 0;
   return tuple2(udp, mask);
endfunction

function Tuple2#(Ipv6T, Ipv6T) toIpv6(MetadataT meta);
   Ipv6T ipv6 = defaultValue;
   Ipv6T mask = defaultMask;
   return tuple2(ipv6, mask);
endfunction

//
function Tuple2#(PaxosT, PaxosT) toPaxos(MetadataT meta);
   PaxosT paxos = defaultValue;
   PaxosT mask = defaultMask;
   return tuple2(paxos, mask);
endfunction

module mkStateDeparseIdle#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, Wire#(Bool) start_fsm)(Empty);

   rule load_packet if (state == StateDeparseIdle);
      let v = datain.first;
      if (v.sop) begin
         state <= StateDeparseEthernet;
         start_fsm <= True;
         $display("(%0d) Deparse Ethernet Start", $time);
      end
      else begin
         datain.deq;
         dataout.enq(v);
         $display("(%0d) payload ", $time, fshow(v));
         start_fsm <= False;
      end
   endrule
endmodule

interface DeparseEthernet;
   interface Get#(EtherData) deparse_arp;
   interface Get#(EtherData) deparse_ipv4;
   interface Get#(EtherData) deparse_ipv6;
   method Action start;
   method Action clear;
endinterface

module mkStateDeparseEthernet#(Reg#(DeparserState) state,
                               FIFOF#(EtherData) datain,
                               FIFOF#(EtherData) dataout,
                               FIFOF#(EthernetT) ethernet_meta,
                               FIFOF#(EthernetT) ethernet_mask)
                               (DeparseEthernet);
   let verbose = True;
   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFO#(EtherData) parse_arp_fifo <- mkFIFO;
   FIFO#(EtherData) parse_ipv4_fifo <- mkFIFO;
   FIFO#(EtherData) parse_ipv6_fifo <- mkFIFO;

   PulseWire start_wire <- mkPulseWire;
   PulseWire clear_wire <- mkPulseWire;

   function DeparserState compute_next_state(Bit#(16) etherType);
       DeparserState nextState = StateDeparseIdle;
       case (byteSwap(etherType)) matches
           'h806: begin
               nextState=StateDeparseArp;
           end
           'h800: begin
               nextState=StateDeparseIpv4;
           end
           'h86dd: begin
               nextState=StateDeparseIpv6;
           end
           default: begin
               nextState=StateDeparseIdle;
           end
       endcase
       return nextState;
   endfunction

   rule load_packet if (state == StateDeparseEthernet);
      let data_current <- toGet(datain).get;
      packet_in_wire <= data_current;
      $display("(%0d) Ether: ", $time, fshow(data_current));
   endrule

   Stmt deparse_ethernet =
   seq
   action
      let data_this_cycle = packet_in_wire;
      let metadata = ethernet_meta.first;
      let mask = ethernet_mask.first;
      Vector#(128, Bit#(1)) dataVec = unpack(data_this_cycle.data);
      Vector#(112, Bit#(1)) hdr = takeAt(0, dataVec);
      Vector#(16, Bit#(1)) unchanged = takeAt(112, dataVec);
      EthernetT ethernet = unpack(pack(hdr));
      let nextState = compute_next_state(metadata.etherType);
      if (verbose) $display("(%0d) Eth: %h", $time, metadata.etherType);
      if (verbose) $display("(%0d) Goto ", $time, fshow(nextState));
      data_this_cycle.data = {pack(unchanged), pack(hdr)};
      if (nextState == StateDeparseArp) begin
         parse_arp_fifo.enq(data_this_cycle);
      end
      else if (nextState == StateDeparseIpv4) begin
         parse_ipv4_fifo.enq(data_this_cycle);
      end
      else if (nextState == StateDeparseIpv6) begin
         parse_ipv6_fifo.enq(data_this_cycle);
      end
      state <= nextState;
      ethernet_meta.deq;
      ethernet_mask.deq;
   endaction
   endseq;

   FSM fsm_deparse_ethernet <- mkFSM(deparse_ethernet);
   rule start_fsm if (start_wire);
      fsm_deparse_ethernet.start;
   endrule
   rule clear_fsm if (clear_wire);
      fsm_deparse_ethernet.abort;
   endrule
   method Action start();
      start_wire.send();
   endmethod
   method Action clear();
      clear_wire.send();
   endmethod
   interface deparse_arp = toGet(parse_arp_fifo);
   interface deparse_ipv4 = toGet(parse_ipv4_fifo);
   interface deparse_ipv6 = toGet(parse_ipv6_fifo);
endmodule

interface DeparseIpv4;
   interface Put#(EtherData) deparse_ethernet;
   interface Get#(EtherData) deparse_udp;
   method Action start;
   method Action clear;
endinterface
module mkStateDeparseIpv4#(Reg#(DeparserState) state,
                           FIFOF#(EtherData) datain,
                           FIFOF#(EtherData) dataout,
                           FIFOF#(Ipv4T) ipv4_meta,
                           FIFOF#(Ipv4T) ipv4_mask)
                           (DeparseIpv4);

   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFOF#(EtherData) deparse_ethernet_fifo <- mkBypassFIFOF;
   FIFO#(EtherData) deparse_udp_fifo <- mkFIFO;
   PulseWire start_wire <- mkPulseWire();
   PulseWire clear_wire <- mkPulseWire();

   function DeparserState compute_next_state(Bit#(8) protocol);
       DeparserState nextState = StateDeparseIdle;
       case (byteSwap(protocol)) matches
           'h11: begin
               nextState=StateDeparseUdp;
           end
           default: begin
               nextState=StateDeparseIdle;
           end
       endcase
       return nextState;
   endfunction

   rule load_packet if (state == StateDeparseIpv4 && !deparse_ethernet_fifo.notEmpty());
       let data_current <- toGet(datain).get;
       packet_in_wire <= data_current;
       $display("(%0d) IPv4: ", $time, fshow(data_current));
   endrule

   Stmt deparse_ipv4 =
   seq
   action
      let data_this_cycle <- toGet(deparse_ethernet_fifo).get;
      Vector#(112, Bit#(1)) last_data = takeAt(0, unpack(data_this_cycle.data));
      Vector#(16, Bit#(1)) data = takeAt(112, unpack(data_this_cycle.data));
      Vector#(16, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(ipv4_meta.first))));
      Vector#(16, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(ipv4_mask.first))));
      let masked_data = pack(data) & pack(curr_mask);
      let out_data = masked_data | pack(curr_meta);
      $display("(%0d) IPv4: [1] ", $time, fshow(data_this_cycle), " meta=%h, mask=%h", curr_mask, curr_meta);
      data_this_cycle.data = {out_data, pack(last_data)};
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      Vector#(128, Bit#(1)) curr_meta = takeAt(16, unpack(byteSwap(pack(ipv4_meta.first))));
      Vector#(128, Bit#(1)) curr_mask = takeAt(16, unpack(byteSwap(pack(ipv4_mask.first))));
      let masked_data = data_this_cycle.data & pack(curr_mask);
      let curr_data = masked_data | pack(curr_meta);
      data_this_cycle.data = curr_data;
      dataout.enq(data_this_cycle);
      $display("(%0d) IPv4: [2] ", $time, fshow(data_this_cycle));
   endaction
   action
      let data_this_cycle = packet_in_wire;
      Vector#(16, Bit#(1)) buff_data = takeAt(0, unpack(data_this_cycle.data));
      Vector#(112, Bit#(1)) unchanged = takeAt(16, unpack(data_this_cycle.data));
      Vector#(16, Bit#(1)) curr_mask = takeAt(144, unpack(byteSwap(pack(ipv4_mask.first))));
      Vector#(16, Bit#(1)) curr_meta = takeAt(144, unpack(byteSwap(pack(ipv4_meta.first))));
      let masked_data = pack(buff_data) & pack(curr_mask);
      let curr_data = masked_data | pack(curr_meta);
      let nextState = compute_next_state(ipv4_meta.first.protocol);
      $display("(%0d) compute_next_state ", $time, fshow(ipv4_meta.first.protocol));
      $display("(%0d) Goto ", $time, fshow(nextState));
      if (nextState == StateDeparseUdp) begin
         data_this_cycle.data = {pack(unchanged), pack(curr_data)};
         deparse_udp_fifo.enq(data_this_cycle);
      end
      ipv4_meta.deq;
      ipv4_mask.deq;
      state <= nextState;
   endaction
   endseq;

   FSM fsm_deparse_ipv4 <- mkFSM(deparse_ipv4);
   rule start_fsm if (start_wire);
      fsm_deparse_ipv4.start;
   endrule
   rule clear_fsm if (clear_wire);
      fsm_deparse_ipv4.abort;
   endrule
   method Action start();
      start_wire.send();
   endmethod
   method Action clear();
      clear_wire.send();
   endmethod
   interface deparse_ethernet = toPut(deparse_ethernet_fifo);
   interface deparse_udp = toGet(deparse_udp_fifo);
endmodule

interface DeparseUdp;
   interface Put#(EtherData) deparse_ipv4;
   interface Get#(EtherData) deparse_paxos;
   method Action start;
   method Action clear;
endinterface
module mkStateDeparseUdp#(Reg#(DeparserState) state,
                          FIFOF#(EtherData) datain,
                          FIFOF#(EtherData) dataout,
                          FIFOF#(UdpT) udp_meta,
                          FIFOF#(UdpT) udp_mask)
                          (DeparseUdp);

   let verbose = True;
   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFOF#(EtherData) deparse_ipv4_fifo <- mkBypassFIFOF;
   FIFO#(EtherData) deparse_paxos_fifo <- mkFIFO;

   PulseWire start_wire <- mkPulseWire();
   PulseWire clear_wire <- mkPulseWire();

   function DeparserState compute_next_state(Bit#(16) dstPort);
       DeparserState nextState = StateDeparseIdle;
       case (byteSwap(dstPort)) matches
           'h8888: begin
               nextState=StateDeparsePaxos;
           end
           default: begin
               nextState=StateDeparseIdle;
           end
       endcase
       return nextState;
   endfunction

   Stmt deparse_udp =
   seq
   action
      let data_this_cycle <- toGet(deparse_ipv4_fifo).get;
      Vector#(80, Bit#(1)) unsent = takeAt(0, unpack(data_this_cycle.data));
      Vector#(48, Bit#(1)) unchanged = takeAt(80, unpack(data_this_cycle.data));
      let nextState = compute_next_state(udp_meta.first.dstPort);
      if (verbose) $display("(%0d) udp_meta.dstport=", $time, fshow(udp_meta.first.dstPort));
      if (verbose) $display("(%0d) Goto ", $time, fshow(nextState));
      if (nextState == StateDeparsePaxos) begin
         data_this_cycle.data = {pack(unchanged), pack(unsent)};
         deparse_paxos_fifo.enq(data_this_cycle);
      end
      udp_meta.deq;
      udp_mask.deq;
      state <= nextState;
   endaction
   endseq;

   FSM fsm_deparse_udp <- mkFSM(deparse_udp);
   rule start_fsm if (start_wire);
      fsm_deparse_udp.start();
   endrule
   rule clear_fsm if (clear_wire);
      fsm_deparse_udp.abort();
   endrule
   method Action start();
      start_wire.send();
   endmethod
   method Action clear();
      clear_wire.send();
   endmethod
   interface deparse_ipv4 = toPut(deparse_ipv4_fifo);
   interface deparse_paxos = toGet(deparse_paxos_fifo);
endmodule

interface DeparsePaxos;
   interface Put#(EtherData) deparse_udp;
   method Action start;
   method Action clear;
endinterface
module mkStateDeparsePaxos#(Reg#(DeparserState) state,
                            FIFOF#(EtherData) datain,
                            FIFOF#(EtherData) dataout,
                            FIFOF#(PaxosT) paxos_meta,
                            FIFOF#(PaxosT) paxos_mask)
                            (DeparsePaxos);

   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFOF#(EtherData) deparse_udp_fifo <- mkBypassFIFOF;

   PulseWire start_wire <- mkPulseWire();
   PulseWire clear_wire <- mkPulseWire();

   function Bit#(128) apply_metadata(Integer offset, Bit#(128) field, PaxosT data, PaxosT mask);
      Vector#(128, Bit#(1)) curr_meta = takeAt(offset, unpack(byteSwap(pack(data))));
      Vector#(128, Bit#(1)) curr_mask = takeAt(offset, unpack(byteSwap(pack(mask))));
      return (field & pack(curr_mask)) | pack(curr_meta);
   endfunction

   rule load_packet if (state == StateDeparsePaxos && !deparse_udp_fifo.notEmpty());
       let data_current <- toGet(datain).get;
       packet_in_wire <= data_current;
       $display("(%0d) Paxos: ", $time, fshow(data_current));
   endrule

   Stmt deparse_paxos =
   seq
   action
      let data_this_cycle <- toGet(deparse_udp_fifo).get;
      Vector#(80, Bit#(1)) prev_data = takeAt(0, unpack(pack(data_this_cycle.data)));
      Vector#(48, Bit#(1)) last_data = takeAt(80, unpack(pack(data_this_cycle.data)));
      Vector#(48, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(paxos_meta.first))));
      Vector#(48, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(paxos_mask.first))));
      let masked_data = pack(last_data) & pack(curr_mask);
      let curr_data = masked_data | pack(curr_meta);
      data_this_cycle.data = {curr_data, pack(prev_data)};
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      let out = apply_metadata(48, data_this_cycle.data, paxos_meta.first, paxos_mask.first);
      data_this_cycle.data = out;
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      let out = apply_metadata(176, data_this_cycle.data, paxos_meta.first, paxos_mask.first);
      data_this_cycle.data = out;
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      //let out = apply_metadata(304, data_this_cycle, paxos_meta.first, paxos_mask.first);
      Vector#(48, Bit#(1)) curr_meta = takeAt(304, unpack(byteSwap(pack(paxos_meta.first))));
      Vector#(48, Bit#(1)) curr_mask = takeAt(304, unpack(byteSwap(pack(paxos_mask.first))));
      Vector#(48, Bit#(1)) data = takeAt(0, unpack(pack(data_this_cycle.data)));
      Vector#(80, Bit#(1)) payload = takeAt(48, unpack(pack(data_this_cycle.data)));
      let out = (pack(data) & pack(curr_mask)) | pack(curr_meta);
      data_this_cycle.data = { pack(payload), out };
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
      paxos_meta.deq;
      paxos_mask.deq;
      state <= StateDeparseIdle;
   endaction
   endseq;
   FSM fsm_deparse_paxos <- mkFSM(deparse_paxos);
   rule start_fsm if (start_wire);
       fsm_deparse_paxos.start;
   endrule
   rule clear_fsm if (clear_wire);
       fsm_deparse_paxos.abort;
   endrule
   method Action start();
       start_wire.send();
   endmethod
   method Action clear();
       clear_wire.send();
   endmethod

   interface deparse_udp = toPut(deparse_udp_fifo);
endmodule

interface Deparser;
   interface PipeIn#(MetadataT) metadata;
   interface PktWriteServer writeServer;
   interface PktWriteClient writeClient;
endinterface

typedef 4 PortMax;
(* synthesize *)
module mkDeparser(Deparser);
   let verbose = True;
   FIFOF#(EtherData) data_in_fifo <- mkSizedFIFOF(4);
   FIFOF#(EtherData) data_out_fifo <- mkFIFOF;
   FIFOF#(MetadataT) metadata_in_fifo <- mkFIFOF;

   Reg#(Bool) started <- mkReg(False);
   Wire#(Bool) start_fsm <- mkDWire(False);
   Reg#(DeparserState) curr_state <- mkReg(StateDeparseIdle);

   Vector#(PortMax, FIFOF#(DeparserState)) deparse_state_in_fifo <- replicateM(mkGFIFOF(False, True));
   FIFOF#(DeparserState) deparse_state_out_fifo <- mkFIFOF;

   FIFOF#(EthernetT) ethernet_meta_fifo <- mkFIFOF;
   FIFOF#(Ipv4T) ipv4_meta_fifo <- mkFIFOF;
   FIFOF#(UdpT) udp_meta_fifo <- mkFIFOF;
   FIFOF#(PaxosT) paxos_meta_fifo <- mkFIFOF;
   FIFOF#(Ipv6T) ipv6_meta_fifo <- mkFIFOF;

   FIFOF#(EthernetT) ethernet_mask_fifo <- mkFIFOF;
   FIFOF#(Ipv4T) ipv4_mask_fifo <- mkFIFOF;
   FIFOF#(UdpT) udp_mask_fifo <- mkFIFOF;
   FIFOF#(PaxosT) paxos_mask_fifo <- mkFIFOF;
   FIFOF#(Ipv6T) ipv6_mask_fifo <- mkFIFOF;

   (* fire_when_enabled *)
   rule arbitrate_deparse_state;
      Bool sentOne = False;
      for (Integer port = 0; port < valueOf(PortMax); port = port+1) begin
         if (!sentOne && deparse_state_in_fifo[port].notEmpty()) begin
            DeparserState state <- toGet(deparse_state_in_fifo[port]).get();
            sentOne = True;
            $display("(%0d) xxx arbitrate %h", $time, port);
            deparse_state_out_fifo.enq(state);
         end
      end
   endrule

   rule get_metadata;
      let v <- toGet(metadata_in_fifo).get;
      let ethernet = toEthernet(v);
      match {.data, .mask} = ethernet;
      ethernet_meta_fifo.enq(data);
      ethernet_mask_fifo.enq(mask);

      let ipv4 = toIpv4(v);
      match {.ipv4_data, .ipv4_mask} = ipv4;
      if (verbose) $display("(%0d) ipv4 meta", $time, fshow(ipv4));
      ipv4_meta_fifo.enq(ipv4_data);
      ipv4_mask_fifo.enq(ipv4_mask);

      let udp = toUdp(v);
      match {.udp_data, .udp_mask} = udp;
      udp_meta_fifo.enq(udp_data);
      udp_mask_fifo.enq(udp_mask);

      //let ipv6 = toIpv6(v);
      //if (ipv6 matches tagged Valid {.data, .mask}) begin
      //   ipv6_meta_fifo.enq(data);
      //   ipv6_mask_fifo.enq(mask);
      //end

      let paxos = toPaxos(v);
      match {.paxos_data, .paxos_mask} = paxos;
      paxos_meta_fifo.enq(paxos_data);
      paxos_mask_fifo.enq(paxos_mask);
   endrule

   Empty init_state <- mkStateDeparseIdle(curr_state, data_in_fifo, data_out_fifo, start_fsm);
   DeparseEthernet deparse_ethernet <- mkStateDeparseEthernet(curr_state, data_in_fifo, data_out_fifo, ethernet_meta_fifo, ethernet_mask_fifo);
   //DeparseArp deparse_arp <- mkStateDeparseArp(curr_state, data_in_fifo);
   DeparseIpv4 deparse_ipv4 <- mkStateDeparseIpv4(curr_state, data_in_fifo, data_out_fifo, ipv4_meta_fifo, ipv4_mask_fifo);
   //DeparseIpv6 deparse_ipv6 <- mkStateDeparseIpv6(curr_state, data_in_fifo);
   DeparseUdp deparse_udp <- mkStateDeparseUdp(curr_state, data_in_fifo, data_out_fifo, udp_meta_fifo, udp_mask_fifo);
   DeparsePaxos deparse_paxos <- mkStateDeparsePaxos(curr_state, data_in_fifo, data_out_fifo, paxos_meta_fifo, paxos_mask_fifo);

   //mkConnection(deparse_arp.deparse_ethernet, deparse_ethernet.deparse_arp);
   mkConnection(deparse_ipv4.deparse_ethernet, deparse_ethernet.deparse_ipv4);
   //mkConnection(deparse_ipv6.deparse_ethernet, deparse_ethernet.deparse_ipv6);
   mkConnection(deparse_udp.deparse_ipv4, deparse_ipv4.deparse_udp);
   mkConnection(deparse_paxos.deparse_udp, deparse_udp.deparse_paxos);

   rule start if (start_fsm);
      if (!started) begin
         deparse_ethernet.start;
         //deparse_arp.start;
         deparse_ipv4.start;
         //deparse_ipv6.start;
         deparse_udp.start;
         deparse_paxos.start;
         started <= True;
      end
   endrule

   rule clear if (!start_fsm && curr_state == StateDeparseIdle);
      if (started) begin
         deparse_ethernet.clear;
         //deparse_arp.clear;
         deparse_ipv4.clear;
         //deparse_ipv6.clear;
         deparse_udp.clear;
         deparse_paxos.clear;
         started <= False;
      end
   endrule

   interface PktWriteServer writeServer;
      interface writeData = toPut(data_in_fifo);
   endinterface
   interface PktWriteClient writeClient;
      interface writeData = toGet(data_out_fifo);
   endinterface
   interface metadata = toPipeIn(metadata_in_fifo);
endmodule

// Copyright (c) 2016 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 ClientServer::*;
import Connectable::*;
import DefaultValue::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import StmtFSM::*;
import SpecialFIFOs::*;
import Vector::*;
import Pipe::*;
import Ethernet::*;
import PaxosTypes::*;
import PacketBuffer::*;
import P4Types::*;

typedef enum {
   StateDeparseIdle,
   StateDeparseEthernet,
   StateDeparseArp,
   StateDeparseIpv4,
   StateDeparseIpv6,
   StateDeparseUdp,
   StateDeparsePaxos
} DeparserState deriving (Bits, Eq, FShow);

function Tuple2#(EthernetT, EthernetT) toEthernet(MetadataT meta);
   EthernetT data = defaultValue;
   EthernetT mask = defaultMask;
   data.dstAddr = fromMaybe(?, meta.dstAddr);
   mask.dstAddr = 0;
   data.etherType = fromMaybe(?, meta.etherType);
   mask.etherType = 0;
   return tuple2(data, mask);
endfunction

function Tuple2#(Ipv4T, Ipv4T) toIpv4(MetadataT meta);
   Ipv4T ipv4 = defaultValue;
   Ipv4T mask = defaultMask;
   ipv4.protocol = fromMaybe(?, meta.protocol);
   mask.protocol = 0;
   return tuple2(ipv4, mask);
endfunction

function Tuple2#(UdpT, UdpT) toUdp(MetadataT meta);
   UdpT udp = defaultValue;
   UdpT mask = defaultMask;
   udp.dstPort = fromMaybe(?, meta.dstPort);
   mask.dstPort = 0;
   return tuple2(udp, mask);
endfunction

function Tuple2#(Ipv6T, Ipv6T) toIpv6(MetadataT meta);
   Ipv6T ipv6 = defaultValue;
   Ipv6T mask = defaultMask;
   return tuple2(ipv6, mask);
endfunction

//
function Tuple2#(PaxosT, PaxosT) toPaxos(MetadataT meta);
   PaxosT paxos = defaultValue;
   PaxosT mask = defaultMask;
   return tuple2(paxos, mask);
endfunction

module mkStateDeparseIdle#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, Wire#(Bool) start_fsm)(Empty);

   rule load_packet if (state == StateDeparseIdle);
      let v = datain.first;
      if (v.sop) begin
         state <= StateDeparseEthernet;
         start_fsm <= True;
         $display("(%0d) Deparse Ethernet Start", $time);
      end
      else begin
         datain.deq;
         dataout.enq(v);
         start_fsm <= False;
      end
   endrule
endmodule

interface DeparseEthernet;
   interface Get#(EtherData) deparse_arp;
   interface Get#(EtherData) deparse_ipv4;
   interface Get#(EtherData) deparse_ipv6;
   method Action start;
   method Action clear;
endinterface

module mkStateDeparseEthernet#(Reg#(DeparserState) state,
                               FIFOF#(EtherData) datain,
                               FIFOF#(EtherData) dataout,
                               FIFOF#(EthernetT) ethernet_meta,
                               FIFOF#(EthernetT) ethernet_mask)
                               (DeparseEthernet);
   let verbose = True;
   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFO#(EtherData) parse_arp_fifo <- mkFIFO;
   FIFO#(EtherData) parse_ipv4_fifo <- mkFIFO;
   FIFO#(EtherData) parse_ipv6_fifo <- mkFIFO;

   PulseWire start_wire <- mkPulseWire;
   PulseWire clear_wire <- mkPulseWire;

   function DeparserState compute_next_state(Bit#(16) etherType);
       DeparserState nextState = StateDeparseIdle;
       case (byteSwap(etherType)) matches
           'h806: begin
               nextState=StateDeparseArp;
           end
           'h800: begin
               nextState=StateDeparseIpv4;
           end
           'h86dd: begin
               nextState=StateDeparseIpv6;
           end
           default: begin
               nextState=StateDeparseIdle;
           end
       endcase
       return nextState;
   endfunction

   rule load_packet if (state == StateDeparseEthernet);
      let data_current <- toGet(datain).get;
      packet_in_wire <= data_current;
      $display("(%0d) Ether: ", $time, fshow(data_current));
   endrule

   Stmt deparse_ethernet =
   seq
   action
      let data_this_cycle = packet_in_wire;
      let metadata = ethernet_meta.first;
      let mask = ethernet_mask.first;
      Vector#(128, Bit#(1)) dataVec = unpack(data_this_cycle.data);
      Vector#(112, Bit#(1)) hdr = takeAt(0, dataVec);
      Vector#(16, Bit#(1)) unchanged = takeAt(112, dataVec);
      EthernetT ethernet = unpack(pack(hdr));
      let nextState = compute_next_state(metadata.etherType);
      if (verbose) $display("(%0d) Eth: %h", $time, metadata.etherType);
      if (verbose) $display("(%0d) Goto ", $time, fshow(nextState));
      data_this_cycle.data = {pack(unchanged), pack(hdr)};
      if (nextState == StateDeparseArp) begin
         parse_arp_fifo.enq(data_this_cycle);
      end
      else if (nextState == StateDeparseIpv4) begin
         parse_ipv4_fifo.enq(data_this_cycle);
      end
      else if (nextState == StateDeparseIpv6) begin
         parse_ipv6_fifo.enq(data_this_cycle);
      end
      state <= nextState;
      ethernet_meta.deq;
      ethernet_mask.deq;
   endaction
   endseq;

   FSM fsm_deparse_ethernet <- mkFSM(deparse_ethernet);
   rule start_fsm if (start_wire);
      fsm_deparse_ethernet.start;
   endrule
   rule clear_fsm if (clear_wire);
      fsm_deparse_ethernet.abort;
   endrule
   method Action start();
      start_wire.send();
   endmethod
   method Action clear();
      clear_wire.send();
   endmethod
   interface deparse_arp = toGet(parse_arp_fifo);
   interface deparse_ipv4 = toGet(parse_ipv4_fifo);
   interface deparse_ipv6 = toGet(parse_ipv6_fifo);
endmodule

interface DeparseIpv4;
   interface Put#(EtherData) deparse_ethernet;
   interface Get#(EtherData) deparse_udp;
   method Action start;
   method Action clear;
endinterface
module mkStateDeparseIpv4#(Reg#(DeparserState) state,
                           FIFOF#(EtherData) datain,
                           FIFOF#(EtherData) dataout,
                           FIFOF#(Ipv4T) ipv4_meta,
                           FIFOF#(Ipv4T) ipv4_mask)
                           (DeparseIpv4);

   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFOF#(EtherData) deparse_ethernet_fifo <- mkBypassFIFOF;
   FIFO#(EtherData) deparse_udp_fifo <- mkFIFO;
   PulseWire start_wire <- mkPulseWire();
   PulseWire clear_wire <- mkPulseWire();

   function DeparserState compute_next_state(Bit#(8) protocol);
       DeparserState nextState = StateDeparseIdle;
       case (byteSwap(protocol)) matches
           'h11: begin
               nextState=StateDeparseUdp;
           end
           default: begin
               nextState=StateDeparseIdle;
           end
       endcase
       return nextState;
   endfunction

   rule load_packet if (state == StateDeparseIpv4 && !deparse_ethernet_fifo.notEmpty());
       let data_current <- toGet(datain).get;
       packet_in_wire <= data_current;
       $display("(%0d) IPv4: ", $time, fshow(data_current));
   endrule

   Stmt deparse_ipv4 =
   seq
   action
      let data_this_cycle <- toGet(deparse_ethernet_fifo).get;
      Vector#(112, Bit#(1)) last_data = takeAt(0, unpack(data_this_cycle.data));
      Vector#(16, Bit#(1)) data = takeAt(112, unpack(data_this_cycle.data));
      Vector#(16, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(ipv4_meta.first))));
      Vector#(16, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(ipv4_mask.first))));
      let masked_data = pack(data) & pack(curr_mask);
      let out_data = masked_data | pack(curr_meta);
      $display("(%0d) IPv4: [1] ", $time, fshow(data_this_cycle), " meta=%h, mask=%h", curr_mask, curr_meta);
      data_this_cycle.data = {out_data, pack(last_data)};
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      Vector#(128, Bit#(1)) curr_meta = takeAt(16, unpack(byteSwap(pack(ipv4_meta.first))));
      Vector#(128, Bit#(1)) curr_mask = takeAt(16, unpack(byteSwap(pack(ipv4_mask.first))));
      let masked_data = data_this_cycle.data & pack(curr_mask);
      let curr_data = masked_data | pack(curr_meta);
      data_this_cycle.data = curr_data;
      dataout.enq(data_this_cycle);
      $display("(%0d) IPv4: [2] ", $time, fshow(data_this_cycle));
   endaction
   action
      let data_this_cycle = packet_in_wire;
      Vector#(16, Bit#(1)) buff_data = takeAt(0, unpack(data_this_cycle.data));
      Vector#(112, Bit#(1)) unchanged = takeAt(16, unpack(data_this_cycle.data));
      Vector#(16, Bit#(1)) curr_mask = takeAt(144, unpack(byteSwap(pack(ipv4_mask.first))));
      Vector#(16, Bit#(1)) curr_meta = takeAt(144, unpack(byteSwap(pack(ipv4_meta.first))));
      let masked_data = pack(buff_data) & pack(curr_mask);
      let curr_data = masked_data | pack(curr_meta);
      let nextState = compute_next_state(ipv4_meta.first.protocol);
      $display("(%0d) compute_next_state ", $time, fshow(ipv4_meta.first.protocol));
      $display("(%0d) Goto ", $time, fshow(nextState));
      if (nextState == StateDeparseUdp) begin
         data_this_cycle.data = {pack(unchanged), pack(curr_data)};
         deparse_udp_fifo.enq(data_this_cycle);
      end
      ipv4_meta.deq;
      ipv4_mask.deq;
      state <= nextState;
   endaction
   endseq;

   FSM fsm_deparse_ipv4 <- mkFSM(deparse_ipv4);
   rule start_fsm if (start_wire);
      fsm_deparse_ipv4.start;
   endrule
   rule clear_fsm if (clear_wire);
      fsm_deparse_ipv4.abort;
   endrule
   method Action start();
      start_wire.send();
   endmethod
   method Action clear();
      clear_wire.send();
   endmethod
   interface deparse_ethernet = toPut(deparse_ethernet_fifo);
   interface deparse_udp = toGet(deparse_udp_fifo);
endmodule

interface DeparseUdp;
   interface Put#(EtherData) deparse_ipv4;
   interface Get#(EtherData) deparse_paxos;
   method Action start;
   method Action clear;
endinterface
module mkStateDeparseUdp#(Reg#(DeparserState) state,
                          FIFOF#(EtherData) datain,
                          FIFOF#(EtherData) dataout,
                          FIFOF#(UdpT) udp_meta,
                          FIFOF#(UdpT) udp_mask)
                          (DeparseUdp);

   let verbose = True;
   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFOF#(EtherData) deparse_ipv4_fifo <- mkBypassFIFOF;
   FIFO#(EtherData) deparse_paxos_fifo <- mkFIFO;

   PulseWire start_wire <- mkPulseWire();
   PulseWire clear_wire <- mkPulseWire();

   function DeparserState compute_next_state(Bit#(16) dstPort);
       DeparserState nextState = StateDeparseIdle;
       case (byteSwap(dstPort)) matches
           'h8888: begin
               nextState=StateDeparsePaxos;
           end
           default: begin
               nextState=StateDeparseIdle;
           end
       endcase
       return nextState;
   endfunction

   Stmt deparse_udp =
   seq
   action
      let data_this_cycle <- toGet(deparse_ipv4_fifo).get;
      Vector#(80, Bit#(1)) unsent = takeAt(0, unpack(data_this_cycle.data));
      Vector#(48, Bit#(1)) unchanged = takeAt(80, unpack(data_this_cycle.data));
      let nextState = compute_next_state(udp_meta.first.dstPort);
      if (verbose) $display("(%0d) udp_meta.dstport=", $time, fshow(udp_meta.first.dstPort));
      if (verbose) $display("(%0d) Goto ", $time, fshow(nextState));
      if (nextState == StateDeparsePaxos) begin
         data_this_cycle.data = {pack(unchanged), pack(unsent)};
         deparse_paxos_fifo.enq(data_this_cycle);
      end
      udp_meta.deq;
      udp_mask.deq;
      state <= nextState;
   endaction
   endseq;

   FSM fsm_deparse_udp <- mkFSM(deparse_udp);
   rule start_fsm if (start_wire);
      fsm_deparse_udp.start();
   endrule
   rule clear_fsm if (clear_wire);
      fsm_deparse_udp.abort();
   endrule
   method Action start();
      start_wire.send();
   endmethod
   method Action clear();
      clear_wire.send();
   endmethod
   interface deparse_ipv4 = toPut(deparse_ipv4_fifo);
   interface deparse_paxos = toGet(deparse_paxos_fifo);
endmodule

interface DeparsePaxos;
   interface Put#(EtherData) deparse_udp;
   method Action start;
   method Action clear;
endinterface
module mkStateDeparsePaxos#(Reg#(DeparserState) state,
                            FIFOF#(EtherData) datain,
                            FIFOF#(EtherData) dataout,
                            FIFOF#(PaxosT) paxos_meta,
                            FIFOF#(PaxosT) paxos_mask)
                            (DeparsePaxos);

   Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
   FIFOF#(EtherData) deparse_udp_fifo <- mkBypassFIFOF;

   PulseWire start_wire <- mkPulseWire();
   PulseWire clear_wire <- mkPulseWire();

   function Bit#(128) apply_metadata(Integer offset, Bit#(128) field, PaxosT data, PaxosT mask);
      Vector#(128, Bit#(1)) curr_meta = takeAt(offset, unpack(byteSwap(pack(data))));
      Vector#(128, Bit#(1)) curr_mask = takeAt(offset, unpack(byteSwap(pack(mask))));
      return (field & pack(curr_mask)) | pack(curr_meta);
   endfunction

   rule load_packet if (state == StateDeparsePaxos && !deparse_udp_fifo.notEmpty());
       let data_current <- toGet(datain).get;
       packet_in_wire <= data_current;
       $display("(%0d) Paxos: ", $time, fshow(data_current));
   endrule

   Stmt deparse_paxos =
   seq
   action
      let data_this_cycle <- toGet(deparse_udp_fifo).get;
      Vector#(80, Bit#(1)) prev_data = takeAt(0, unpack(pack(data_this_cycle.data)));
      Vector#(48, Bit#(1)) last_data = takeAt(80, unpack(pack(data_this_cycle.data)));
      Vector#(48, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(paxos_meta.first))));
      Vector#(48, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(paxos_mask.first))));
      let masked_data = pack(last_data) & pack(curr_mask);
      let curr_data = masked_data | pack(curr_meta);
      data_this_cycle.data = {curr_data, pack(prev_data)};
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      let out = apply_metadata(48, data_this_cycle.data, paxos_meta.first, paxos_mask.first);
      data_this_cycle.data = out;
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      let out = apply_metadata(176, data_this_cycle.data, paxos_meta.first, paxos_mask.first);
      data_this_cycle.data = out;
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
   endaction
   action
      let data_this_cycle = packet_in_wire;
      //let out = apply_metadata(304, data_this_cycle, paxos_meta.first, paxos_mask.first);
      Vector#(48, Bit#(1)) curr_meta = takeAt(304, unpack(byteSwap(pack(paxos_meta.first))));
      Vector#(48, Bit#(1)) curr_mask = takeAt(304, unpack(byteSwap(pack(paxos_mask.first))));
      let out = (data_this_cycle.data[47:0] & pack(curr_mask)) | pack(curr_meta);
      data_this_cycle.data = zeroExtend(out);
      $display("(%0d) dataout ", $time, fshow(data_this_cycle));
      dataout.enq(data_this_cycle);
      paxos_meta.deq;
      paxos_mask.deq;
      state <= StateDeparseIdle;
   endaction
   endseq;
   FSM fsm_deparse_paxos <- mkFSM(deparse_paxos);
   rule start_fsm if (start_wire);
       fsm_deparse_paxos.start;
   endrule
   rule clear_fsm if (clear_wire);
       fsm_deparse_paxos.abort;
   endrule
   method Action start();
       start_wire.send();
   endmethod
   method Action clear();
       clear_wire.send();
   endmethod

   interface deparse_udp = toPut(deparse_udp_fifo);
endmodule

interface Deparser;
   interface PipeIn#(MetadataT) metadata;
   interface PktWriteServer writeServer;
   interface PktWriteClient writeClient;
endinterface

typedef 4 PortMax;
(* synthesize *)
module mkDeparser(Deparser);
   let verbose = True;
   FIFOF#(EtherData) data_in_fifo <- mkSizedFIFOF(4);
   FIFOF#(EtherData) data_out_fifo <- mkFIFOF;
   FIFOF#(MetadataT) metadata_in_fifo <- mkFIFOF;

   Reg#(Bool) started <- mkReg(False);
   Wire#(Bool) start_fsm <- mkDWire(False);
   Reg#(DeparserState) curr_state <- mkReg(StateDeparseIdle);

   Vector#(PortMax, FIFOF#(DeparserState)) deparse_state_in_fifo <- replicateM(mkGFIFOF(False, True));
   FIFOF#(DeparserState) deparse_state_out_fifo <- mkFIFOF;

   FIFOF#(EthernetT) ethernet_meta_fifo <- mkFIFOF;
   FIFOF#(Ipv4T) ipv4_meta_fifo <- mkFIFOF;
   FIFOF#(UdpT) udp_meta_fifo <- mkFIFOF;
   FIFOF#(PaxosT) paxos_meta_fifo <- mkFIFOF;
   FIFOF#(Ipv6T) ipv6_meta_fifo <- mkFIFOF;

   FIFOF#(EthernetT) ethernet_mask_fifo <- mkFIFOF;
   FIFOF#(Ipv4T) ipv4_mask_fifo <- mkFIFOF;
   FIFOF#(UdpT) udp_mask_fifo <- mkFIFOF;
   FIFOF#(PaxosT) paxos_mask_fifo <- mkFIFOF;
   FIFOF#(Ipv6T) ipv6_mask_fifo <- mkFIFOF;

   (* fire_when_enabled *)
   rule arbitrate_deparse_state;
      Bool sentOne = False;
      for (Integer port = 0; port < valueOf(PortMax); port = port+1) begin
         if (!sentOne && deparse_state_in_fifo[port].notEmpty()) begin
            DeparserState state <- toGet(deparse_state_in_fifo[port]).get();
            sentOne = True;
            $display("(%0d) xxx arbitrate %h", $time, port);
            deparse_state_out_fifo.enq(state);
         end
      end
   endrule

   rule get_metadata;
      let v <- toGet(metadata_in_fifo).get;
      let ethernet = toEthernet(v);
      match {.data, .mask} = ethernet;
      ethernet_meta_fifo.enq(data);
      ethernet_mask_fifo.enq(mask);

      let ipv4 = toIpv4(v);
      match {.ipv4_data, .ipv4_mask} = ipv4;
      if (verbose) $display("(%0d) ipv4 meta", $time, fshow(ipv4));
      ipv4_meta_fifo.enq(ipv4_data);
      ipv4_mask_fifo.enq(ipv4_mask);

      let udp = toUdp(v);
      match {.udp_data, .udp_mask} = udp;
      udp_meta_fifo.enq(udp_data);
      udp_mask_fifo.enq(udp_mask);

      //let ipv6 = toIpv6(v);
      //if (ipv6 matches tagged Valid {.data, .mask}) begin
      //   ipv6_meta_fifo.enq(data);
      //   ipv6_mask_fifo.enq(mask);
      //end

      let paxos = toPaxos(v);
      match {.paxos_data, .paxos_mask} = paxos;
      paxos_meta_fifo.enq(paxos_data);
      paxos_mask_fifo.enq(paxos_mask);
   endrule

   Empty init_state <- mkStateDeparseIdle(curr_state, data_in_fifo, data_out_fifo, start_fsm);
   DeparseEthernet deparse_ethernet <- mkStateDeparseEthernet(curr_state, data_in_fifo, data_out_fifo, ethernet_meta_fifo, ethernet_mask_fifo);
   //DeparseArp deparse_arp <- mkStateDeparseArp(curr_state, data_in_fifo);
   DeparseIpv4 deparse_ipv4 <- mkStateDeparseIpv4(curr_state, data_in_fifo, data_out_fifo, ipv4_meta_fifo, ipv4_mask_fifo);
   //DeparseIpv6 deparse_ipv6 <- mkStateDeparseIpv6(curr_state, data_in_fifo);
   DeparseUdp deparse_udp <- mkStateDeparseUdp(curr_state, data_in_fifo, data_out_fifo, udp_meta_fifo, udp_mask_fifo);
   DeparsePaxos deparse_paxos <- mkStateDeparsePaxos(curr_state, data_in_fifo, data_out_fifo, paxos_meta_fifo, paxos_mask_fifo);

   //mkConnection(deparse_arp.deparse_ethernet, deparse_ethernet.deparse_arp);
   mkConnection(deparse_ipv4.deparse_ethernet, deparse_ethernet.deparse_ipv4);
   //mkConnection(deparse_ipv6.deparse_ethernet, deparse_ethernet.deparse_ipv6);
   mkConnection(deparse_udp.deparse_ipv4, deparse_ipv4.deparse_udp);
   mkConnection(deparse_paxos.deparse_udp, deparse_udp.deparse_paxos);

   rule start if (start_fsm);
      if (!started) begin
         deparse_ethernet.start;
         //deparse_arp.start;
         deparse_ipv4.start;
         //deparse_ipv6.start;
         deparse_udp.start;
         deparse_paxos.start;
         started <= True;
      end
   endrule

   rule clear if (!start_fsm && curr_state == StateDeparseIdle);
      if (started) begin
         deparse_ethernet.clear;
         //deparse_arp.clear;
         deparse_ipv4.clear;
         //deparse_ipv6.clear;
         deparse_udp.clear;
         deparse_paxos.clear;
         started <= False;
      end
   endrule

   interface PktWriteServer writeServer;
      interface writeData = toPut(data_in_fifo);
   endinterface
   interface PktWriteClient writeClient;
      interface writeData = toGet(data_out_fifo);
   endinterface
   interface metadata = toPipeIn(metadata_in_fifo);
endmodule