pipeline sglist method

pipeline sglist method

Change-Id: I7c0e030f304c1268e415a26ba9b642c3a8642d78

// 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 BRAMFIFO::*;
import BRAM::*;
import Dma::*;
import StmtFSM::*;
import ClientServer::*;
import PortalMemory::*;

typedef 32 MaxNumSGLists;
typedef Bit#(TLog#(MaxNumSGLists)) SGListId;
typedef 12 SGListPageShift0;
typedef 16 SGListPageShift4;
typedef 20 SGListPageShift8;
typedef Bit#(TLog#(MaxNumSGLists)) RegionsIdx;
typedef Tuple2#(SGListId,Bit#(ObjectOffsetSize)) ReqTup;

interface SGListMMU#(numeric type addrWidth);
   method Action sglist(Bit#(32) pointer, Bit#(40) paddr, Bit#(32) len);
   method Action region(Bit#(32) ptr, Bit#(40) barr8, Bit#(8) off8, Bit#(40) barr4, Bit#(8) off4, Bit#(40) barr0, Bit#(8) off0);
   interface Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addr;
endinterface

typedef union tagged{
   Bit#(SGListPageShift0) OOrd0;
   Bit#(SGListPageShift4) OOrd4;
   Bit#(SGListPageShift8) OOrd8;
} Offset deriving (Eq,Bits,FShow);

typedef union tagged{
   Bit#(TSub#(ObjectOffsetSize,SGListPageShift0)) POrd0;
   Bit#(TSub#(ObjectOffsetSize,SGListPageShift4)) POrd4;
   Bit#(TSub#(ObjectOffsetSize,SGListPageShift8)) POrd8;
} Page deriving (Eq,Bits,FShow);

typedef struct {
   Bit#(ObjectOffsetSize) barrier;
   Bit#(8) idxOffset;
   } Region deriving (Eq,Bits,FShow);

module mkSGListMMU#(DmaIndication dmaIndication)(SGListMMU#(addrWidth))

   provisos(Log#(MaxNumSGLists, listIdxSize),
	    Add#(listIdxSize,8, entryIdxSize),
	    Add#(c__, addrWidth, ObjectOffsetSize));

   BRAM_Configure bramConfig = defaultValue;
   bramConfig.latency        = 2;
   BRAM2Port#(Bit#(entryIdxSize),Page) pages <- mkBRAM2Server(bramConfig);
   BRAM2Port#(RegionsIdx, Region)       reg8 <- mkBRAM2Server(bramConfig);
   BRAM2Port#(RegionsIdx, Region)       reg4 <- mkBRAM2Server(bramConfig);
   BRAM2Port#(RegionsIdx, Region)       reg0 <- mkBRAM2Server(bramConfig);

   Vector#(2,FIFOF#(SGListId))          ptrs <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(8)))         pbases <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(8)))     idxOffsets <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(entryIdxSize)))  rp <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Offset))            offs <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(ReqTup))            reqs <- replicateM(mkFIFOF);
   Vector#(2,FIFO#(Bit#(addrWidth))) pageResponseFifos <- replicateM(mkFIFO);
   Reg#(Bit#(8))                      idxReg <- mkReg(0);

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

   let ord0 = 40'd1 << page_shift0;
   let ord4 = 40'd1 << page_shift4;
   let ord8 = 40'd1 << page_shift8;

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

   // pipeline the address lookup
   for(Integer i = 0; i < 2; i=i+1) begin
      rule req0;
	 reqs[i].deq;
	 let ptr = tpl_1(reqs[i].first);
	 let off = tpl_2(reqs[i].first);
	 Offset o = tagged OOrd0 0;
	 Bit#(8) pbase = 0;
	 Bit#(8) idxOffset = 0;
	 Region region8 <- portsel(reg8,i).response.get;
	 Region region4 <- portsel(reg4,i).response.get;
	 Region region0 <- portsel(reg0,i).response.get;
	 Bit#(40) barrier8 = region8.barrier;
	 Bit#(40) barrier4 = region4.barrier;
	 Bit#(40) barrier0 = region0.barrier;

	 if (off < barrier8) begin
	    //$display("request: ptr=%h off=%h barrier8=%h", ptr, off, barrier8);
	    o = tagged OOrd8 truncate(off);
	    pbase = truncate(off>>page_shift8);
	    idxOffset = region8.idxOffset;
	 end
	 else if (off < barrier4) begin
	    //$display("request: ptr=%h off=%h barrier4=%h", ptr, off, barrier4);
	    o = tagged OOrd4 truncate(off);
	    pbase = truncate(off>>page_shift4);
	    idxOffset = region4.idxOffset;
	 end
	 else if (off < barrier0) begin
	    //$display("request: ptr=%h off=%h barrier0=%h", ptr, off, barrier0);
	    o = tagged OOrd0 truncate(off);
	    pbase = truncate(off>>page_shift0);
	    idxOffset = region0.idxOffset;
	 end
	 else begin
	    $display("mkSGListMMU.addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaIndication.badAddrTrans(extend(ptr), extend(off), barrier0);
	 end
	 offs[i].enq(o);
	 pbases[i].enq(pbase);
	 idxOffsets[i].enq(idxOffset);
	 ptrs[i].enq(ptr);
      endrule
      rule req1;
	 let ptr <- toGet(ptrs[i]).get();
	 let pbase <- toGet(pbases[i]).get();
	 let idxOffset <- toGet(idxOffsets[i]).get();
	 Bit#(8) p = pbase + idxOffset;
	 rp[i].enq({ptr-1,p});
      endrule
      rule req2;
	 rp[i].deq;
	 //$display("pages[%d].read %h", i, rp[i].first());
	 portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:rp[i].first, datain:?});
      endrule
      rule pageResponse;
	 let page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs[i]).get();
	 //$display("pages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(ObjectOffsetSize) rv = 0;
	 case (offset) matches
	    tagged OOrd0 .o:
	       begin
		  case (page) matches
		     tagged POrd4 .p:
			$display("OOrd0 vs POrd4");
		     tagged POrd8 .p:
			$display("OOrd0 vs POrd8");
		  endcase
		  rv = {page.POrd0,o};
	       end
	    tagged OOrd4 .o:
	       begin
		  case (page) matches
		     tagged POrd0 .p:
			$display("OOrd4 vs POrd0");
		     tagged POrd8 .p:
			$display("OOrd4 vs POrd8");
		  endcase
		  rv = {page.POrd4,o};
	       end
	    tagged OOrd8 .o:
	       begin
		  case (page) matches
		     tagged POrd0 .p:
			$display("OOrd8 vs POrd0");
		     tagged POrd4 .p:
			$display("OOrd8 vs POrd4");
		  endcase
		  rv = {page.POrd8,o};
	       end
	 endcase
	 pageResponseFifos[i].enq(truncate(rv));
      endrule
   end

   Vector#(2, FIFO#(ReqTup)) incomingReqs <- replicateM(mkFIFO);
   for (Integer i = 0; i < 2; i=i+1)
      rule incomingReqRule;
	 let req <- toGet(incomingReqs[i]).get();
	 match { .ptr, .off } = req;
	 portsel(reg8, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:truncate(ptr-1), datain:?});
	 portsel(reg4, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:truncate(ptr-1), datain:?});
	 portsel(reg0, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:truncate(ptr-1), datain:?});
	 reqs[i].enq(req);
      endrule

   Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addrServers;
   for(Integer i = 0; i < 2; i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req);
		incomingReqs[i].enq(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get();
		let rv <- toGet(pageResponseFifos[i]).get();
		return rv;
	     endmethod
	  endinterface
       endinterface);

   FIFO#(Tuple2#(Bit#(32),Bit#(40))) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      match { .ptr, .barr0 } <- toGet(configRespFifo).get();
      dmaIndication.configResp(ptr, barr0);
   endrule

   FIFO#(Tuple2#(Bit#(entryIdxSize),Page)) sglistPageReadFifo <- mkFIFO();
   rule sglistPageRead;
      match { .addr, .page } <- toGet(sglistPageReadFifo).get();
      portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:addr, datain:page});
   endrule

   method Action region(Bit#(32) ptr, Bit#(40) barr8, Bit#(8) off8, Bit#(40) barr4, Bit#(8) off4, Bit#(40) barr0, Bit#(8) off0);
      Region region8 = Region { barrier: barr8, idxOffset: off8 };
      Region region4 = Region { barrier: barr4, idxOffset: off4 };
      Region region0 = Region { barrier: barr0, idxOffset: off0 };
      portsel(reg8, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(ptr-1), datain: region8});
      portsel(reg4, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(ptr-1), datain: region4});
      portsel(reg0, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(ptr-1), datain: region0});
      //$display("region ptr=%d off8=%h off4=%h off0=%h", ptr, off8, off4, off0);
      configRespFifo.enq(tuple2(ptr, barr0));
   endmethod

   method Action sglist(Bit#(32) ptr, Bit#(40) paddr, Bit#(32) len);
      // $display("sglist(ptr=%d, paddr=%h, len=%h", ptr, paddr,len);
      if (idxReg+1 == 0) begin
	 $display("sglist: exceeded maximun length of sglist");
	 dmaIndication.badNumberEntries(ptr,len);
      end
      else begin
	 Page page = tagged POrd0 0;
	 if(len == 0) begin
	    idxReg <= 0;
	 end
	 else begin
	    idxReg <= idxReg+1;
	    if (extend(len) == ord0) begin
	       page = tagged POrd0 truncate(paddr>>page_shift0);
	    end
	    else if (extend(len) == ord4) begin
	       page = tagged POrd4 truncate(paddr>>page_shift4);
	    end
	    else if (extend(len) == ord8) begin
	       page = tagged POrd8 truncate(paddr>>page_shift8);
	    end
	    if (extend(len) > ord8) begin
	       $display("mkSGListMMU::sglist unsupported length %h", len);
	       dmaIndication.badPageSize(ptr, len);
	    end
	 end
	 sglistPageReadFifo.enq(tuple2({truncate(ptr-1),idxReg}, page));
	 configRespFifo.enq(tuple2(ptr, 40'haaaaaaaa));
      end
   endmethod

   interface addr = addrServers;

endmodule

interface SglAddrServer#(numeric type addrWidth, numeric type numServers);
   interface Vector#(numServers,Server#(ReqTup,Bit#(addrWidth))) servers;
endinterface

module mkSglAddrServer#(Server#(ReqTup,Bit#(addrWidth)) server) (SglAddrServer#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(1);
   Vector#(numServers, Server#(ReqTup,Bit#(addrWidth))) addrServers;
   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

   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req) if (arb == fromInteger(i));
		tokFifo.enq(fromInteger(i));
		server.request.put(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get() if (tokFifo.first == fromInteger(i));
		tokFifo.deq;
		let rv <- server.response.get;
		return rv;
	     endmethod
	  endinterface
       endinterface);

   interface servers = addrServers;

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 BRAMFIFO::*;
import BRAM::*;
import Dma::*;
import StmtFSM::*;
import ClientServer::*;
import PortalMemory::*;

typedef 32 MaxNumSGLists;
typedef Bit#(TLog#(MaxNumSGLists)) SGListId;
typedef 12 SGListPageShift0;
typedef 16 SGListPageShift4;
typedef 20 SGListPageShift8;
typedef Bit#(TLog#(MaxNumSGLists)) RegionsIdx;
typedef Tuple2#(SGListId,Bit#(ObjectOffsetSize)) ReqTup;

interface SGListMMU#(numeric type addrWidth);
   method Action sglist(Bit#(32) pointer, Bit#(40) paddr, Bit#(32) len);
   method Action region(Bit#(32) ptr, Bit#(40) barr8, Bit#(8) off8, Bit#(40) barr4, Bit#(8) off4, Bit#(40) barr0, Bit#(8) off0);
   interface Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addr;
endinterface

typedef union tagged{
   Bit#(SGListPageShift0) OOrd0;
   Bit#(SGListPageShift4) OOrd4;
   Bit#(SGListPageShift8) OOrd8;
} Offset deriving (Eq,Bits,FShow);

typedef union tagged{
   Bit#(TSub#(ObjectOffsetSize,SGListPageShift0)) POrd0;
   Bit#(TSub#(ObjectOffsetSize,SGListPageShift4)) POrd4;
   Bit#(TSub#(ObjectOffsetSize,SGListPageShift8)) POrd8;
} Page deriving (Eq,Bits,FShow);

typedef struct {
   Bit#(ObjectOffsetSize) barrier;
   Bit#(8) idxOffset;
   } Region deriving (Eq,Bits,FShow);

module mkSGListMMU#(DmaIndication dmaIndication)(SGListMMU#(addrWidth))

   provisos(Log#(MaxNumSGLists, listIdxSize),
	    Add#(listIdxSize,8, entryIdxSize),
	    Add#(c__, addrWidth, ObjectOffsetSize));

   BRAM_Configure bramConfig = defaultValue;
   bramConfig.latency        = 2;
   BRAM2Port#(Bit#(entryIdxSize),Page) pages <- mkBRAM2Server(bramConfig);
   BRAM2Port#(RegionsIdx, Region)       reg8 <- mkBRAM2Server(bramConfig);
   BRAM2Port#(RegionsIdx, Region)       reg4 <- mkBRAM2Server(bramConfig);
   BRAM2Port#(RegionsIdx, Region)       reg0 <- mkBRAM2Server(bramConfig);

   Vector#(2,FIFOF#(SGListId))          ptrs <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(8)))         pbases <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(8)))     idxOffsets <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(entryIdxSize)))  rp <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Offset))            offs <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(ReqTup))            reqs <- replicateM(mkFIFOF);
   Vector#(2,FIFO#(Bit#(addrWidth))) pageResponseFifos <- replicateM(mkFIFO);
   Reg#(Bit#(8))                      idxReg <- mkReg(0);

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

   let ord0 = 40'd1 << page_shift0;
   let ord4 = 40'd1 << page_shift4;
   let ord8 = 40'd1 << page_shift8;

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

   // pipeline the address lookup
   for(Integer i = 0; i < 2; i=i+1) begin
      rule req0;
	 reqs[i].deq;
	 let ptr = tpl_1(reqs[i].first);
	 let off = tpl_2(reqs[i].first);
	 Offset o = tagged OOrd0 0;
	 Bit#(8) pbase = 0;
	 Bit#(8) idxOffset = 0;
	 Region region8 <- portsel(reg8,i).response.get;
	 Region region4 <- portsel(reg4,i).response.get;
	 Region region0 <- portsel(reg0,i).response.get;
	 Bit#(40) barrier8 = region8.barrier;
	 Bit#(40) barrier4 = region4.barrier;
	 Bit#(40) barrier0 = region0.barrier;

	 if (off < barrier8) begin
	    //$display("request: ptr=%h off=%h barrier8=%h", ptr, off, barrier8);
	    o = tagged OOrd8 truncate(off);
	    pbase = truncate(off>>page_shift8);
	    idxOffset = region8.idxOffset;
	 end
	 else if (off < barrier4) begin
	    //$display("request: ptr=%h off=%h barrier4=%h", ptr, off, barrier4);
	    o = tagged OOrd4 truncate(off);
	    pbase = truncate(off>>page_shift4);
	    idxOffset = region4.idxOffset;
	 end
	 else if (off < barrier0) begin
	    //$display("request: ptr=%h off=%h barrier0=%h", ptr, off, barrier0);
	    o = tagged OOrd0 truncate(off);
	    pbase = truncate(off>>page_shift0);
	    idxOffset = region0.idxOffset;
	 end
	 else begin
	    $display("mkSGListMMU.addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaIndication.badAddrTrans(extend(ptr), extend(off), barrier0);
	 end
	 offs[i].enq(o);
	 pbases[i].enq(pbase);
	 idxOffsets[i].enq(idxOffset);
	 ptrs[i].enq(ptr);
      endrule
      rule req1;
	 let ptr <- toGet(ptrs[i]).get();
	 let pbase <- toGet(pbases[i]).get();
	 let idxOffset <- toGet(idxOffsets[i]).get();
	 Bit#(8) p = pbase + idxOffset;
	 rp[i].enq({ptr-1,p});
      endrule
      rule req2;
	 rp[i].deq;
	 //$display("pages[%d].read %h", i, rp[i].first());
	 portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:rp[i].first, datain:?});
      endrule
      rule pageResponse;
	 let page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs[i]).get();
	 //$display("pages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(ObjectOffsetSize) rv = 0;
	 case (offset) matches
	    tagged OOrd0 .o:
	       begin
		  case (page) matches
		     tagged POrd4 .p:
			$display("OOrd0 vs POrd4");
		     tagged POrd8 .p:
			$display("OOrd0 vs POrd8");
		  endcase
		  rv = {page.POrd0,o};
	       end
	    tagged OOrd4 .o:
	       begin
		  case (page) matches
		     tagged POrd0 .p:
			$display("OOrd4 vs POrd0");
		     tagged POrd8 .p:
			$display("OOrd4 vs POrd8");
		  endcase
		  rv = {page.POrd4,o};
	       end
	    tagged OOrd8 .o:
	       begin
		  case (page) matches
		     tagged POrd0 .p:
			$display("OOrd8 vs POrd0");
		     tagged POrd4 .p:
			$display("OOrd8 vs POrd4");
		  endcase
		  rv = {page.POrd8,o};
	       end
	 endcase
	 pageResponseFifos[i].enq(truncate(rv));
      endrule
   end

   Vector#(2, FIFO#(ReqTup)) incomingReqs <- replicateM(mkFIFO);
   for (Integer i = 0; i < 2; i=i+1)
      rule incomingReqRule;
	 let req <- toGet(incomingReqs[i]).get();
	 match { .ptr, .off } = req;
	 portsel(reg8, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:truncate(ptr-1), datain:?});
	 portsel(reg4, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:truncate(ptr-1), datain:?});
	 portsel(reg0, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:truncate(ptr-1), datain:?});
	 reqs[i].enq(req);
      endrule

   Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addrServers;
   for(Integer i = 0; i < 2; i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req);
		incomingReqs[i].enq(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get();
		let rv <- toGet(pageResponseFifos[i]).get();
		return rv;
	     endmethod
	  endinterface
       endinterface);

   FIFO#(Tuple2#(Bit#(32),Bit#(40))) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      match { .ptr, .barr0 } <- toGet(configRespFifo).get();
      dmaIndication.configResp(ptr, barr0);
   endrule

   method Action region(Bit#(32) ptr, Bit#(40) barr8, Bit#(8) off8, Bit#(40) barr4, Bit#(8) off4, Bit#(40) barr0, Bit#(8) off0);
      Region region8 = Region { barrier: barr8, idxOffset: off8 };
      Region region4 = Region { barrier: barr4, idxOffset: off4 };
      Region region0 = Region { barrier: barr0, idxOffset: off0 };
      portsel(reg8, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(ptr-1), datain: region8});
      portsel(reg4, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(ptr-1), datain: region4});
      portsel(reg0, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:truncate(ptr-1), datain: region0});
      //$display("region ptr=%d off8=%h off4=%h off0=%h", ptr, off8, off4, off0);
      configRespFifo.enq(tuple2(ptr, barr0));
   endmethod

   method Action sglist(Bit#(32) ptr, Bit#(40) paddr, Bit#(32) len);
      // $display("sglist(ptr=%d, paddr=%h, len=%h", ptr, paddr,len);
      if (idxReg+1 == 0) begin
	 $display("sglist: exceeded maximun length of sglist");
	 dmaIndication.badNumberEntries(ptr,len);
      end
      else begin
	 Page page = tagged POrd0 0;
	 if(len == 0) begin
	    idxReg <= 0;
	 end
	 else begin
	    idxReg <= idxReg+1;
	    if (extend(len) == ord0) begin
	       page = tagged POrd0 truncate(paddr>>page_shift0);
	    end
	    else if (extend(len) == ord4) begin
	       page = tagged POrd4 truncate(paddr>>page_shift4);
	    end
	    else if (extend(len) == ord8) begin
	       page = tagged POrd8 truncate(paddr>>page_shift8);
	    end
	    if (extend(len) > ord8) begin
	       $display("mkSGListMMU::sglist unsupported length %h", len);
	       dmaIndication.badPageSize(ptr, len);
	    end
	 end
	 portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:{truncate(ptr-1),idxReg}, datain:page});
	 configRespFifo.enq(tuple2(ptr, 40'haaaaaaaa));
      end
   endmethod

   interface addr = addrServers;

endmodule

interface SglAddrServer#(numeric type addrWidth, numeric type numServers);
   interface Vector#(numServers,Server#(ReqTup,Bit#(addrWidth))) servers;
endinterface

module mkSglAddrServer#(Server#(ReqTup,Bit#(addrWidth)) server) (SglAddrServer#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(1);
   Vector#(numServers, Server#(ReqTup,Bit#(addrWidth))) addrServers;
   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

   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req) if (arb == fromInteger(i));
		tokFifo.enq(fromInteger(i));
		server.request.put(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get() if (tokFifo.first == fromInteger(i));
		tokFifo.deq;
		let rv <- server.response.get;
		return rv;
	     endmethod
	  endinterface
       endinterface);

   interface servers = addrServers;

endmodule


Changes to CSRFile towards 32-bit ISA support

// Copyright (c) 2016 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 RVTypes::*;
import ConcatReg::*;
import ConfigReg::*;
import DefaultValue::*;
import RegUtil::*;
import Vector::*;

typedef union tagged {
    struct {
        TrapCause exception;
        Addr      trapHandlerPC;
    } Exception;     // exception/interrupt redirection
    Addr RedirectPC; // non-trap redirection (xRET)
    Data CsrData;    // CSR read operations
    void None;       // all other operations
} CsrReturn deriving (Bits, Eq, FShow);

interface RVCsrFile;
    // Read and Write ports
    // method Data rd(CSR csr);
    method ActionValue#(CsrReturn)
        wr( Addr pc,
            Maybe#(SystemInst) sysInst,
            CSR csr,
            Data data, // zimm or rval
            Addr addr, // badaddr
            Maybe#(TrapCause) trap, // exception or interrupt
            // indirect updates
            Bit#(5) fflags,
            Bool fpuDirty,
            Bool xDirty);

    // Outputs for CSRs that the rest of the processor needs to know about
    method VMInfo vmI;
    method VMInfo vmD;
    method CsrState csrState; // prv, frm, f_enabled, x_enabled
    method Maybe#(InterruptCause) readyInterrupt; // TODO: fix this data type
endinterface

module mkRVCsrFile#(
            Data hartid,                // Compile-time constant
            Bit#(64) mtime, Bool mtip,  // From RTC
            Bool msip,                  // From IPI
            Bool meip                   // From interrupt controller
        )(RVCsrFile);

    let verbose = False;
    File fout = stdout;

    RiscVISASubset isa = defaultValue;
    Data mvendorid     = 0; // non-commercial
    Data marchid       = 0; // not implemented
    Data mimpid        = 0; // not implemented
    Addr default_mtvec = 'h0000_1000;
    Addr default_stvec = 'h0000_8000;

    Reg#(Bit#(2)) prv <- mkReg(prvM); // resets to machine mode

    // Counters
    Reg#(Bit#(64)) cycle_counter <- mkReg(0);
    Reg#(Bit#(64)) instret_counter <- mkReg(0);

    rule incCycleCounter;
        cycle_counter <= cycle_counter + 1;
    endrule

    // Counter enables
    Reg#(Bit#(1)) u_ir_field <- mkReg(0);
    Reg#(Bit#(1)) u_tm_field <- mkReg(0);
    Reg#(Bit#(1)) u_cy_field <- mkReg(0);
    Reg#(Bit#(1)) s_ir_field <- mkReg(0);
    Reg#(Bit#(1)) s_tm_field <- mkReg(0);
    Reg#(Bit#(1)) s_cy_field <- mkReg(0);

    // FPU Fields
    Reg#(Bit#(5)) fflags_field  <- mkReg(0);
    Reg#(Bit#(3)) frm_field     <- mkReg(0);

    // vm fields
    // XLEN = 64
    Reg#(Bit#(26)) asid_field      <- mkReg(0);
    Reg#(Bit#(38)) sptbr_ppn_field <- mkReg(0);
    // XLEN = 32
    // XXX: Reg#(Bit#(10)) asid_field      <- mkReg(0);
    // XXX: Reg#(Bit#(22)) sptbr_ppn_field <- mkReg(0);

    // trap delegation fields
    Reg#(Bit#(12)) sedeleg_field <- mkReg(0);
    Reg#(Bit#(12)) sideleg_field <- mkReg(0);
    Reg#(Bit#(12)) medeleg_field <- mkReg(0);
    Reg#(Bit#(12)) mideleg_field <- mkReg(0);

    // trap vector fields (same as CSR without bottom 2 bits)
    Reg#(Bit#(TSub#(XLEN,2))) mtvec_field <- mkReg(truncateLSB(default_mtvec));
    Reg#(Bit#(TSub#(XLEN,2))) stvec_field <- mkReg(truncateLSB(default_stvec));

    // mstatus fields
    Reg#(Bit#(5)) vm_field   <- mkReg(0); // WARL
    Reg#(Bit#(1)) mxr_field  <- mkReg(0);
    Reg#(Bit#(1)) pum_field  <- mkReg(0);
    Reg#(Bit#(1)) mprv_field <- mkReg(0);
    Reg#(Bit#(2)) xs_field   <- mkReg(0);
    Reg#(Bit#(2)) fs_field   <- mkReg(0);
    Reg#(Bit#(2)) mpp_field  <- mkReg(0);
    Reg#(Bit#(2)) hpp_field  =  readOnlyReg(0);
    Reg#(Bit#(1)) spp_field  <- mkReg(0);
    Reg#(Bit#(1)) mpie_field <- mkReg(0);
    Reg#(Bit#(1)) hpie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) spie_field <- mkReg(0);
    Reg#(Bit#(1)) upie_field <- mkReg(0);
    Reg#(Bit#(1)) mie_field  <- mkReg(0);
    Reg#(Bit#(1)) hie_field  =  readOnlyReg(0);
    Reg#(Bit#(1)) sie_field  <- mkReg(0);
    Reg#(Bit#(1)) uie_field  <- mkReg(0);
    Reg#(Bit#(1)) sd_field   =  readOnlyReg(pack((xs_field == 2'b11) || (fs_field == 2'b11)));

    // mie fields
    Reg#(Bit#(1)) meie_field <- mkReg(0);
    Reg#(Bit#(1)) heie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) seie_field <- mkReg(0);
    Reg#(Bit#(1)) ueie_field <- mkReg(0);
    Reg#(Bit#(1)) mtie_field <- mkReg(0);
    Reg#(Bit#(1)) htie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) stie_field <- mkReg(0);
    Reg#(Bit#(1)) utie_field <- mkReg(0);
    Reg#(Bit#(1)) msie_field <- mkReg(0);
    Reg#(Bit#(1)) hsie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) ssie_field <- mkReg(0);
    Reg#(Bit#(1)) usie_field <- mkReg(0);

    // mip fields
    Reg#(Bit#(1)) meip_field =  readOnlyReg(pack(meip));
    Reg#(Bit#(1)) heip_field =  readOnlyReg(0);
    Reg#(Bit#(1)) seip_field <- mkReg(0);
    Reg#(Bit#(1)) ueip_field <- mkReg(0);
    Reg#(Bit#(1)) mtip_field =  readOnlyReg(pack(mtip));
    Reg#(Bit#(1)) htip_field =  readOnlyReg(0);
    Reg#(Bit#(1)) stip_field <- mkReg(0);
    Reg#(Bit#(1)) utip_field <- mkReg(0);
    Reg#(Bit#(1)) msip_field =  readOnlyReg(pack(msip));
    Reg#(Bit#(1)) hsip_field =  readOnlyReg(0);
    Reg#(Bit#(1)) ssip_field <- mkReg(0);
    Reg#(Bit#(1)) usip_field <- mkReg(0);

    // Priv 1.9 CSRs

    // Machine Timers and Counters
    // TODO: support 32-bit ISA
    Reg#(Data) mcycle_csr   = readOnlyReg(truncate(cycle_counter));
    Reg#(Data) mtime_csr    = readOnlyReg(truncate(mtime));
    Reg#(Data) minstret_csr = readOnlyReg(truncate(instret_counter));

    // Machine Counter-Delta Registers
    // TODO: support 32-bit ISA
    Reg#(Data) mucycle_delta_csr   <- mkReg(0);
    Reg#(Data) mutime_delta_csr    <- mkReg(0);
    Reg#(Data) muinstret_delta_csr <- mkReg(0);
    Reg#(Data) mscycle_delta_csr   <- mkReg(0);
    Reg#(Data) mstime_delta_csr    <- mkReg(0);
    Reg#(Data) msinstret_delta_csr <- mkReg(0);

    // User FPU
    Reg#(Data) fflags_csr = addWriteSideEffect(zeroExtendReg(fflags_field), fs_field._write(2'b11));
    Reg#(Data) frm_csr    = addWriteSideEffect(zeroExtendReg(frm_field), fs_field._write(2'b11));
    Reg#(Data) fcsr_csr   = addWriteSideEffect(zeroExtendReg(concatReg2(frm_field, fflags_field)), fs_field._write(2'b11));

    // User Timers and Counters
    // TODO: support 32-bit ISA
    Reg#(Data) cycle_csr   = readOnlyReg(mcycle_csr + mucycle_delta_csr);
    Reg#(Data) time_csr    = readOnlyReg(mtime_csr + mutime_delta_csr);
    Reg#(Data) instret_csr = readOnlyReg(minstret_csr + muinstret_delta_csr);

    // Supervisor
    Reg#(Data) sstatus_csr =  concatReg20(
            sd_field,
            readOnlyReg(0), // flexible width to support XLEN = 32 or 64
            vm_field,
            readOnlyReg(4'b0),
            readOnlyReg(1'b0), pum_field, readOnlyReg(1'b0), // memory privilege
            xs_field, fs_field, // coprocessor states
            readOnlyReg(2'b0), readOnlyReg(2'b0), spp_field, // previous privileges
            readOnlyReg(1'b0), readOnlyReg(1'b0), spie_field, upie_field, // previous interrupt enables
            readOnlyReg(1'b0), readOnlyReg(1'b0), sie_field, uie_field); // interrupt enables
    Reg#(Data) sedeleg_csr  =  concatReg2(readOnlyReg(0), sedeleg_field); // WARL - 12 legal exceptions
    Reg#(Data) sideleg_csr  =  concatReg2(readOnlyReg(0), sideleg_field); // WARL - 12 legal interrupts
    Reg#(Data) sie_csr      =  concatReg13(
            readOnlyReg(0),
            readOnlyReg(1'b0), readOnlyReg(1'b0), seie_field, ueie_field,
            readOnlyReg(1'b0), readOnlyReg(1'b0), stie_field, utie_field,
            readOnlyReg(1'b0), readOnlyReg(1'b0), ssie_field, usie_field);
    Reg#(Data) stvec_csr    =  concatReg2(stvec_field, readOnlyReg(2'd0));
    Reg#(Data) sscratch_csr <- mkReg(0);
    Reg#(Data) sepc_csr     <- mkReg(0);
    Reg#(Data) scause_csr   <- mkReg(0);
    Reg#(Data) sbadaddr_csr <- mkReg(0);
    Reg#(Data) sip_csr      =  concatReg13(
            readOnlyReg(0),
            readOnlyReg(1'b0), readOnlyReg(1'b0), readOnlyReg(seip_field), readOnlyReg(ueip_field),
            readOnlyReg(1'b0), readOnlyReg(1'b0), readOnlyReg(stip_field), readOnlyReg(utip_field),
            readOnlyReg(1'b0), readOnlyReg(1'b0), ssip_field, usip_field);

    Reg#(Data) sptbr_csr    = concatReg2(asid_field, sptbr_ppn_field);

    // TODO: Support 32-bit ISA
    Reg#(Data) scycle_csr   = readOnlyReg(mcycle_csr + mscycle_delta_csr);
    Reg#(Data) stime_csr    = readOnlyReg(mtime_csr + mstime_delta_csr);
    Reg#(Data) sinstret_csr = readOnlyReg(minstret_csr + msinstret_delta_csr);

    // Machine Information Registers
    Reg#(Data) misa_csr      = readOnlyReg(getMISA(isa));
    Reg#(Data) mvendorid_csr = readOnlyReg(mvendorid);
    Reg#(Data) marchid_csr   = readOnlyReg(marchid);
    Reg#(Data) mimpid_csr    = readOnlyReg(mimpid);
    Reg#(Data) mhartid_csr   = readOnlyReg(hartid);

    // Machine Trap Setup
    Reg#(Data) mstatus_csr =  concatReg20(
            sd_field,
            readOnlyReg(0),
            vm_field,
            readOnlyReg(4'b0),
            mxr_field, pum_field, mprv_field, // memory privilege
            xs_field, fs_field, // coprocessor states
            mpp_field, hpp_field, spp_field, // previous privileges
            mpie_field, hpie_field, spie_field, upie_field, // previous interrupt enables
            mie_field, hie_field, sie_field, uie_field); // interrupt enables
    Reg#(Data) medeleg_csr =  concatReg2(readOnlyReg(0), medeleg_field); // WARL - 12 legal exceptions
    Reg#(Data) mideleg_csr =  concatReg2(readOnlyReg(0), mideleg_field); // WARL - 12 legal interrupts
    Reg#(Data) mie_csr     =  concatReg13(
            readOnlyReg(0),
            meie_field, heie_field, seie_field, ueie_field,
            mtie_field, htie_field, stie_field, utie_field,
            msie_field, hsie_field, ssie_field, usie_field);
    Reg#(Data) mtvec_csr   =  concatReg2(mtvec_field, readOnlyReg(2'd0));

    // Machine Trap Handling
    Reg#(Data) mscratch_csr <- mkReg(0);
    Reg#(Data) mepc_csr     <- mkReg(0);
    Reg#(Data) mcause_csr   <- mkReg(0);
    Reg#(Data) mbadaddr_csr <- mkReg(0);
    Reg#(Data) mip_csr      =  concatReg13(
            readOnlyReg(0),
            readOnlyReg(meip_field), readOnlyReg(heip_field), readOnlyReg(seip_field), readOnlyReg(ueip_field),
            readOnlyReg(mtip_field), htip_field, stip_field, utip_field,
            readOnlyReg(msip_field), hsip_field, ssip_field, usip_field);

    // Machine Protection and Translation
    Reg#(Data) mbase_csr   <- mkReg(0);
    Reg#(Data) mbound_csr  <- mkReg(0);
    Reg#(Data) mibase_csr  <- mkReg(0);
    Reg#(Data) mibound_csr <- mkReg(0);
    Reg#(Data) mdbase_csr  <- mkReg(0);
    Reg#(Data) mdbound_csr <- mkReg(0);

    // Machine Counter Setup
    Reg#(Data) mucounteren_csr = concatReg4(readOnlyReg(0), u_ir_field, u_tm_field, u_cy_field);
    Reg#(Data) mscounteren_csr = concatReg4(readOnlyReg(0), s_ir_field, s_tm_field, s_cy_field);

    function Reg#(Data) getCSR(CSR csr);
        return (case (csr)
                CSRfflags:              fflags_csr;
                CSRfrm:                 frm_csr;
                CSRfcsr:                fcsr_csr;
                CSRcycle:               cycle_csr;
                CSRtime:                time_csr;
                CSRinstret:             instret_csr;
                CSRsstatus:             sstatus_csr;
                CSRsedeleg:             sedeleg_csr;
                CSRsideleg:             sideleg_csr;
                CSRsie:                 sie_csr;
                CSRstvec:               stvec_csr;
                CSRsscratch:            sscratch_csr;
                CSRsepc:                sepc_csr;
                CSRscause:              scause_csr;
                CSRsbadaddr:            sbadaddr_csr;
                CSRsip:                 sip_csr;
                CSRsptbr:               sptbr_csr;
                CSRscycle:              scycle_csr;
                CSRstime:               stime_csr;
                CSRsinstret:            sinstret_csr;
                CSRmisa:                misa_csr;
                CSRmvendorid:           mvendorid_csr;
                CSRmarchid:             marchid_csr;
                CSRmimpid:              mimpid_csr;
                CSRmhartid:             mhartid_csr;
                CSRmstatus:             mstatus_csr;
                CSRmedeleg:             medeleg_csr;
                CSRmideleg:             mideleg_csr;
                CSRmie:                 mie_csr;
                CSRmtvec:               mtvec_csr;
                CSRmscratch:            mscratch_csr;
                CSRmepc:                mepc_csr;
                CSRmcause:              mcause_csr;
                CSRmbadaddr:            mbadaddr_csr;
                CSRmip:                 mip_csr;
                CSRmbase:               mbase_csr;
                CSRmbound:              mbound_csr;
                CSRmibase:              mibase_csr;
                CSRmibound:             mibound_csr;
                CSRmdbase:              mdbase_csr;
                CSRmdbound:             mdbound_csr;
                CSRmcycle:              mcycle_csr;
                CSRmtime:               mtime_csr;
                CSRminstret:            minstret_csr;
                CSRmucounteren:         mucounteren_csr;
                CSRmscounteren:         mscounteren_csr;
                CSRmucycle_delta:       mucycle_delta_csr;
                CSRmutime_delta:        mutime_delta_csr;
                CSRmuinstret_delta:     muinstret_delta_csr;
                CSRmscycle_delta:       mscycle_delta_csr;
                CSRmstime_delta:        mstime_delta_csr;
                CSRmsinstret_delta:     msinstret_delta_csr;
                default:                (readOnlyReg(64'h0));
            endcase);
    endfunction

    function Bool isLegalCSR(CSR csr);
        return (case (csr)
                CSRfflags:              (fs_field != 0);
                CSRfrm:                 (fs_field != 0);
                CSRfcsr:                (fs_field != 0);
                CSRcycle:               (u_cy_field == 1);
                CSRtime:                (u_tm_field == 1);
                CSRinstret:             (u_ir_field == 1);
                CSRsstatus:             True;
                CSRsedeleg:             True;
                CSRsideleg:             True;
                CSRsie:                 True;
                CSRstvec:               True;
                CSRsscratch:            True;
                CSRsepc:                True;
                CSRscause:              True;
                CSRsbadaddr:            True;
                CSRsip:                 True;
                CSRsptbr:               True;
                CSRscycle:              (s_cy_field == 1);
                CSRstime:               (s_tm_field == 1);
                CSRsinstret:            (s_ir_field == 1);
                CSRmisa:                True;
                CSRmvendorid:           True;
                CSRmarchid:             True;
                CSRmimpid:              True;
                CSRmhartid:             True;
                CSRmstatus:             True;
                CSRmedeleg:             True;
                CSRmideleg:             True;
                CSRmie:                 True;
                CSRmtvec:               True;
                CSRmscratch:            True;
                CSRmepc:                True;
                CSRmcause:              True;
                CSRmbadaddr:            True;
                CSRmip:                 True;
                CSRmbase:               True;
                CSRmbound:              True;
                CSRmibase:              True;
                CSRmibound:             True;
                CSRmdbase:              True;
                CSRmdbound:             True;
                CSRmcycle:              True;
                CSRmtime:               True;
                CSRminstret:            True;
                CSRmucounteren:         True;
                CSRmscounteren:         True;
                CSRmucycle_delta:       True;
                CSRmutime_delta:        True;
                CSRmuinstret_delta:     True;
                CSRmscycle_delta:       True;
                CSRmstime_delta:        True;
                CSRmsinstret_delta:     True;
                default:                False;
            endcase);
    endfunction

    // RULES
    ////////////////////////////////////////////////////////

    rule incrementCycle;
        cycle_counter <= cycle_counter + 1;
    endrule

    // METHODS
    ////////////////////////////////////////////////////////

    method VMInfo vmI;
        Bit#(5) vm = (prv == prvM) ? vmMbare : vm_field;
        Addr base = (case (vm)
                        vmMbare: 0;
                        vmMbb: mbase_csr;
                        vmMbbid: mibase_csr;
                        // all paged virtual memory modes
                        default: {0, sptbr_ppn_field, 12'd0};
                    endcase);
        Addr bound = (case (vm)
                        vmMbb: mbound_csr;
                        vmMbbid: mibound_csr;
                        default: -1;
                    endcase);
        return VMInfo{ prv: prv, asid: asid_field, vm: vm, mxr: unpack(mxr_field), pum: unpack(pum_field), base: base, bound: bound };
    endmethod

    method VMInfo vmD;
        Bit#(2) vm_prv = (mprv_field == 1) ? mpp_field : prv;
        Bit#(5) vm = (vm_prv == prvM) ? vmMbare : vm_field;
        Addr base = (case (vm)
                        vmMbare: 0;
                        vmMbb: mbase_csr;
                        vmMbbid: mdbase_csr;
                        // all paged virtual memory modes
                        default: {0, sptbr_ppn_field, 12'd0};
                    endcase);
        Addr bound = (case (vm)
                        vmMbb: mbound_csr;
                        vmMbbid: mdbound_csr;
                        default: -1;
                    endcase);
        return VMInfo{ prv: vm_prv, asid: asid_field, vm: vm, mxr: unpack(mxr_field), pum: unpack(pum_field), base: base, bound: bound };
    endmethod

    method CsrState csrState = CsrState {prv: prv, frm: frm_field, f_enabled: (fs_field != 0), x_enabled: (xs_field != 0)};

    method Maybe#(InterruptCause) readyInterrupt;
        Bit#(12) ready_interrupts = truncate(mip_csr) & truncate(mie_csr);
        // machine mode
        let ready_machine_interrupts = ready_interrupts & ~truncate(mideleg_csr);
        Bool machine_interrupts_enabled = (mie_field == 1) || (prv < prvM);
        // supervisor mode
        let ready_supervisor_interrupts = ready_interrupts & truncate(mideleg_csr) & ~truncate(sideleg_csr);
        Bool supervisor_interrupts_enabled = ((sie_field == 1) && (prv == prvS)) || (prv < prvS);
        // user mode
        let ready_user_interrupts = ready_interrupts & truncate(mideleg_csr) & truncate(sideleg_csr);
        let user_interrupts_enabled = (uie_field == 1) && (prv == prvU);
        // combined
        ready_interrupts = (machine_interrupts_enabled ? ready_machine_interrupts : 0)
                            | (supervisor_interrupts_enabled ? ready_supervisor_interrupts : 0)
                            | (user_interrupts_enabled ? ready_user_interrupts : 0);
        // format pendingInterrupt value to return
        Maybe#(InterruptCause) ret = tagged Invalid;
        if (ready_interrupts != 0) begin
            // pack/unpack type conversion:
            // UInt#(TLog#(TAdd#(12,1))) == UInt#(4) -> Bit#(4) -> InterruptCause
            ret = tagged Valid unpack(pack(countZerosLSB(ready_interrupts)));
        end
        return ret;
    endmethod

    method ActionValue#(CsrReturn) wr(
            Addr pc, // pc of current exception
            Maybe#(SystemInst) sysInst,
            CSR csr,
            Data data, // zimm or rval
            Addr addr, // badaddr
            Maybe#(TrapCause) trap, // exception or interrupt
            // indirect updates
            Bit#(5) fflags,
            Bool fpuDirty,
            Bool xDirty);

        Maybe#(TrapCause) trapToTake = trap;
        if (!isValid(trapToTake) &&& sysInst matches tagged Valid .validSysInst) begin
            case (validSysInst)
                ECall:  trapToTake = tagged Valid (case (prv)
                                                    prvU: (tagged Exception EnvCallU);
                                                    prvS: (tagged Exception EnvCallS);
                                                    prvH: (tagged Exception EnvCallH);
                                                    prvM: (tagged Exception EnvCallM);
                                                endcase);
                // URet and HRet are not supported
                URet: trapToTake = tagged Valid (tagged Exception IllegalInst);
                SRet:
                    begin
                        if (prv < prvS) begin
                            trapToTake = tagged Valid (tagged Exception IllegalInst);
                        end
                    end
                HRet: trapToTake = tagged Valid (tagged Exception IllegalInst);
                MRet:
                    begin
                        if (prv < prvM) begin
                            trapToTake = tagged Valid (tagged Exception IllegalInst);
                        end
                    end
                EBreak: trapToTake = tagged Valid (tagged Exception Breakpoint);
                CSRRW, CSRRS, CSRRC, CSRR, CSRW:
                    begin
                        Bool read = (validSysInst != CSRW);
                        Bool write = (validSysInst != CSRR);
                        if (!isLegalCSR(csr) || !hasCSRPermission(csr, prv, write)) begin
                            trapToTake = tagged Valid (tagged Exception IllegalInst);
                        end
                    end
            endcase
        end

        // Three case from this point onward:
        // 1) Trap (exception or interrupt)
        // 2) Non-trapping system instruction
        //      increment instret
        // 3) Normal instruction
        //      increment instret
        //      update fs and fflags if necessary
        if (trapToTake matches tagged Valid .validTrap) begin
            // Traps
            // delegate to S if the prv <= S and the corresponding deleg bit is set
            Bool delegToS = prv <= prvS && (case (validTrap) matches
                    tagged Exception .exceptionCause: (((medeleg_csr >> pack(exceptionCause)) & 1) != 0);
                    tagged Interrupt .interruptCause: (((mideleg_csr >> pack(interruptCause)) & 1) != 0);
                endcase);
            Addr newPC = delegToS ? stvec_csr : mtvec_csr;
            if (delegToS) begin
                // trap to prvS
                sepc_csr <= pc;
                scause_csr <= toCauseCSR(validTrap);
                case (validTrap)
                    tagged Exception InstAddrMisaligned,
                    tagged Exception InstAccessFault,
                    tagged Exception LoadAddrMisaligned,
                    tagged Exception LoadAccessFault,
                    tagged Exception StoreAddrMisaligned,
                    tagged Exception StoreAccessFault:
                        sbadaddr_csr <= addr;
                endcase
                // update mstatus fields
                spp_field <= (prv == prvU) ? 0 : 1;
                spie_field <= (prv == prvU) ? uie_field : sie_field;
                sie_field <= 0;
                // update privilege
                prv <= prvS;
            end else begin
                // trap to prvM
                mepc_csr <= pc;
                mcause_csr <= toCauseCSR(validTrap);
                case (validTrap)
                    tagged Exception InstAddrMisaligned,
                    tagged Exception InstAccessFault,
                    tagged Exception LoadAddrMisaligned,
                    tagged Exception LoadAccessFault,
                    tagged Exception StoreAddrMisaligned,
                    tagged Exception StoreAccessFault:
                        mbadaddr_csr <= addr;
                endcase
                // update mstatus fields
                mpp_field <= prv;
                mpie_field <= (case (prv)
                                prvU: uie_field;
                                prvS: sie_field;
                                prvH: hie_field;
                                prvM: mie_field;
                            endcase);
                mie_field <= 0;
                // update privilege
                prv <= prvM;
            end
            return tagged Exception {exception: validTrap, trapHandlerPC: newPC};
        end else if (sysInst matches tagged Valid .validSysInst) begin
            // Non-trapping system instructions
            instret_counter <= instret_counter + 1;
            case (validSysInst)
                SRet:
                    begin
                        let next_prv = spp_field == 1 ? prvS : prvU;
                        case (next_prv)
                            prvU: uie_field <= spie_field;
                            prvS: sie_field <= spie_field;
                        endcase
                        spie_field <= 0;
                        spp_field <= 0;
                        prv <= next_prv;
                        Addr newPC = sepc_csr;
                        return tagged RedirectPC newPC;
                    end
                MRet:
                    begin
                        let next_prv = mpp_field;
                        case (next_prv)
                            prvU: uie_field <= mpie_field;
                            prvS: sie_field <= mpie_field;
                            prvM: mie_field <= mpie_field;
                        endcase
                        mpie_field <= 0;
                        mpp_field <= prvU;
                        prv <= next_prv;
                        Addr newPC = mepc_csr;
                        return tagged RedirectPC newPC;
                    end
                CSRRW, CSRRS, CSRRC, CSRR, CSRW:
                    begin
                        // Not used at the moment, just a place holder in case
                        // any read side-effects are needed in the CSRs.
                        Bool read = (validSysInst != CSRW);
                        Bool write = (validSysInst != CSRR);
                        // CSR read/write operation
                        let oldVal = getCSR(csr)._read;
                        let newVal = (case(validSysInst)
                                CSRW, CSRR, CSRRW: data;
                                CSRRS: (oldVal | data);
                                CSRRC: (oldVal & (~data));
                            endcase);
                        if (write) begin
                            getCSR(csr)._write(newVal);
                        end
                        return tagged CsrData oldVal;
                    end
                default:
                    begin
                        return tagged None;
                    end
            endcase
        end else begin
            // Normal instruction
            // update fflags, fs, and xs if necessary
            if ((fflags | fflags_field) != fflags_field) begin
                fflags_field <= fflags_field | fflags;
                fpuDirty = True;
            end
            if (fpuDirty) begin
                if (fs_field == 0) begin
                    // fs_field shouldn't be 00
                    $fdisplay(stderr, "[ERROR] CSRFile: fpuDirty is true, but fs_field is set to 0");
                end
                fs_field <= 2'b11;
            end
            if (xDirty) begin
                xs_field <= 2'b11;
            end
            instret_counter <= instret_counter + 1;
            return tagged None;
        end
    endmethod
endmodule

// Copyright (c) 2016 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 RVTypes::*;
import ConcatReg::*;
import ConfigReg::*;
import DefaultValue::*;
import RegUtil::*;
import Vector::*;

typedef union tagged {
    struct {
        TrapCause exception;
        Addr      trapHandlerPC;
    } Exception;     // exception/interrupt redirection
    Addr RedirectPC; // non-trap redirection (xRET)
    Data CsrData;    // CSR read operations
    void None;       // all other operations
} CsrReturn deriving (Bits, Eq, FShow);

interface RVCsrFile;
    // Read and Write ports
    // method Data rd(CSR csr);
    method ActionValue#(CsrReturn)
        wr( Addr pc,
            Maybe#(SystemInst) sysInst,
            CSR csr,
            Data data, // zimm or rval
            Addr addr, // badaddr
            Maybe#(TrapCause) trap, // exception or interrupt
            // indirect updates
            Bit#(5) fflags,
            Bool fpuDirty,
            Bool xDirty);

    // Outputs for CSRs that the rest of the processor needs to know about
    method VMInfo vmI;
    method VMInfo vmD;
    method CsrState csrState; // prv, frm, f_enabled, x_enabled
    method Maybe#(InterruptCause) readyInterrupt; // TODO: fix this data type
endinterface

module mkRVCsrFile#(
            Data hartid,            // Compile-time constant
            Data mtime, Bool mtip,  // From RTC
            Bool msip,              // From IPI
            Bool meip               // From interrupt controller
        )(RVCsrFile);

    let verbose = False;
    File fout = stdout;

    RiscVISASubset isa = defaultValue;
    Data mvendorid     = 64'h0; // non-commercial
    Data marchid       = 64'h0; // not implemented
    Data mimpid        = 64'h0; // not implemented
    Addr default_mtvec = 64'h0000_1000;
    Addr default_stvec = 64'h0000_8000;

    Reg#(Bit#(2)) prv <- mkReg(prvM); // resets to machine mode

    // Counters
    Reg#(Bit#(64)) cycle_counter <- mkReg(0);
    Reg#(Bit#(64)) instret_counter <- mkReg(0);

    rule incCycleCounter;
        cycle_counter <= cycle_counter + 1;
    endrule

    // Counter enables
    Reg#(Bit#(1)) u_ir_field <- mkReg(0);
    Reg#(Bit#(1)) u_tm_field <- mkReg(0);
    Reg#(Bit#(1)) u_cy_field <- mkReg(0);
    Reg#(Bit#(1)) s_ir_field <- mkReg(0);
    Reg#(Bit#(1)) s_tm_field <- mkReg(0);
    Reg#(Bit#(1)) s_cy_field <- mkReg(0);

    // FPU Fields
    Reg#(Bit#(5)) fflags_field  <- mkReg(0);
    Reg#(Bit#(3)) frm_field     <- mkReg(0);

    // vm fields
    Reg#(Bit#(26)) asid_field      <- mkReg(0);
    Reg#(Bit#(38)) sptbr_ppn_field <- mkReg(0);

    // trap delegation fields
    Reg#(Bit#(12)) sedeleg_field <- mkReg(0);
    Reg#(Bit#(12)) sideleg_field <- mkReg(0);
    Reg#(Bit#(12)) medeleg_field <- mkReg(0);
    Reg#(Bit#(12)) mideleg_field <- mkReg(0);

    // trap vector fields (same as CSR without bottom 2 bits
    Reg#(Bit#(62)) mtvec_field <- mkReg(default_mtvec[63:2]);
    Reg#(Bit#(62)) stvec_field <- mkReg(default_stvec[63:2]);

    // mstatus fields
    Reg#(Bit#(5)) vm_field   <- mkReg(0); // WARL
    Reg#(Bit#(1)) mxr_field  <- mkReg(0);
    Reg#(Bit#(1)) pum_field  <- mkReg(0);
    Reg#(Bit#(1)) mprv_field <- mkReg(0);
    Reg#(Bit#(2)) xs_field   <- mkReg(0);
    Reg#(Bit#(2)) fs_field   <- mkReg(0);
    Reg#(Bit#(2)) mpp_field  <- mkReg(0);
    Reg#(Bit#(2)) hpp_field  =  readOnlyReg(0);
    Reg#(Bit#(1)) spp_field  <- mkReg(0);
    Reg#(Bit#(1)) mpie_field <- mkReg(0);
    Reg#(Bit#(1)) hpie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) spie_field <- mkReg(0);
    Reg#(Bit#(1)) upie_field <- mkReg(0);
    Reg#(Bit#(1)) mie_field  <- mkReg(0);
    Reg#(Bit#(1)) hie_field  =  readOnlyReg(0);
    Reg#(Bit#(1)) sie_field  <- mkReg(0);
    Reg#(Bit#(1)) uie_field  <- mkReg(0);
    Reg#(Bit#(1)) sd_field   =  readOnlyReg(pack((xs_field == 2'b11) || (fs_field == 2'b11)));

    // mie fields
    Reg#(Bit#(1)) meie_field <- mkReg(0);
    Reg#(Bit#(1)) heie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) seie_field <- mkReg(0);
    Reg#(Bit#(1)) ueie_field <- mkReg(0);
    Reg#(Bit#(1)) mtie_field <- mkReg(0);
    Reg#(Bit#(1)) htie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) stie_field <- mkReg(0);
    Reg#(Bit#(1)) utie_field <- mkReg(0);
    Reg#(Bit#(1)) msie_field <- mkReg(0);
    Reg#(Bit#(1)) hsie_field =  readOnlyReg(0);
    Reg#(Bit#(1)) ssie_field <- mkReg(0);
    Reg#(Bit#(1)) usie_field <- mkReg(0);

    // mip fields
    Reg#(Bit#(1)) meip_field =  readOnlyReg(pack(meip));
    Reg#(Bit#(1)) heip_field =  readOnlyReg(0);
    Reg#(Bit#(1)) seip_field <- mkReg(0);
    Reg#(Bit#(1)) ueip_field <- mkReg(0);
    Reg#(Bit#(1)) mtip_field =  readOnlyReg(pack(mtip));
    Reg#(Bit#(1)) htip_field =  readOnlyReg(0);
    Reg#(Bit#(1)) stip_field <- mkReg(0);
    Reg#(Bit#(1)) utip_field <- mkReg(0);
    Reg#(Bit#(1)) msip_field =  readOnlyReg(pack(msip));
    Reg#(Bit#(1)) hsip_field =  readOnlyReg(0);
    Reg#(Bit#(1)) ssip_field <- mkReg(0);
    Reg#(Bit#(1)) usip_field <- mkReg(0);

    // Priv 1.9 CSRs

    // Machine Timers and Counters
    Reg#(Data) mcycle_csr   = readOnlyReg(cycle_counter);
    Reg#(Data) mtime_csr    = readOnlyReg(mtime);
    Reg#(Data) minstret_csr = readOnlyReg(instret_counter);

    // Machine Counter-Delta Registers
    Reg#(Data) mucycle_delta_csr   <- mkReg(0);
    Reg#(Data) mutime_delta_csr    <- mkReg(0);
    Reg#(Data) muinstret_delta_csr <- mkReg(0);
    Reg#(Data) mscycle_delta_csr   <- mkReg(0);
    Reg#(Data) mstime_delta_csr    <- mkReg(0);
    Reg#(Data) msinstret_delta_csr <- mkReg(0);

    // User FPU
    Reg#(Data) fflags_csr = addWriteSideEffect(zeroExtendReg(fflags_field), fs_field._write(2'b11));
    Reg#(Data) frm_csr    = addWriteSideEffect(zeroExtendReg(frm_field), fs_field._write(2'b11));
    Reg#(Data) fcsr_csr   = addWriteSideEffect(zeroExtendReg(concatReg2(frm_field, fflags_field)), fs_field._write(2'b11));

    // User Timers and Counters
    Reg#(Data) cycle_csr   = readOnlyReg(mcycle_csr + mucycle_delta_csr);
    Reg#(Data) time_csr    = readOnlyReg(mtime_csr + mutime_delta_csr);
    Reg#(Data) instret_csr = readOnlyReg(minstret_csr + muinstret_delta_csr);

    // Supervisor
    Reg#(Data) sstatus_csr =  concatReg20(
            sd_field,
            readOnlyReg(0),
            vm_field,
            readOnlyReg(4'b0),
            readOnlyReg(1'b0), pum_field, readOnlyReg(1'b0), // memory privilege
            xs_field, fs_field, // coprocessor states
            readOnlyReg(2'b0), readOnlyReg(2'b0), spp_field, // previous privileges
            readOnlyReg(1'b0), readOnlyReg(1'b0), spie_field, upie_field, // previous interrupt enables
            readOnlyReg(1'b0), readOnlyReg(1'b0), sie_field, uie_field); // interrupt enables
    Reg#(Data) sedeleg_csr  =  concatReg2(readOnlyReg(0), sedeleg_field); // WARL - 12 legal exceptions
    Reg#(Data) sideleg_csr  =  concatReg2(readOnlyReg(0), sideleg_field); // WARL - 12 legal interrupts
    Reg#(Data) sie_csr      =  concatReg13(
            readOnlyReg(0),
            readOnlyReg(1'b0), readOnlyReg(1'b0), seie_field, ueie_field,
            readOnlyReg(1'b0), readOnlyReg(1'b0), stie_field, utie_field,
            readOnlyReg(1'b0), readOnlyReg(1'b0), ssie_field, usie_field);
    Reg#(Data) stvec_csr    =  concatReg2(stvec_field, readOnlyReg(2'd0));
    Reg#(Data) sscratch_csr <- mkReg(0);
    Reg#(Data) sepc_csr     <- mkReg(0);
    Reg#(Data) scause_csr   <- mkReg(0);
    Reg#(Data) sbadaddr_csr <- mkReg(0);
    Reg#(Data) sip_csr      =  concatReg13(
            readOnlyReg(0),
            readOnlyReg(1'b0), readOnlyReg(1'b0), readOnlyReg(seip_field), readOnlyReg(ueip_field),
            readOnlyReg(1'b0), readOnlyReg(1'b0), readOnlyReg(stip_field), readOnlyReg(utip_field),
            readOnlyReg(1'b0), readOnlyReg(1'b0), ssip_field, usip_field);

    Reg#(Data) sptbr_csr    = concatReg2(asid_field, sptbr_ppn_field);

    Reg#(Data) scycle_csr   = readOnlyReg(mcycle_csr + mscycle_delta_csr);
    Reg#(Data) stime_csr    = readOnlyReg(mtime_csr + mstime_delta_csr);
    Reg#(Data) sinstret_csr = readOnlyReg(minstret_csr + msinstret_delta_csr);

    // Machine Information Registers
    Reg#(Data) misa_csr      = readOnlyReg(getMISA(isa));
    Reg#(Data) mvendorid_csr = readOnlyReg(mvendorid);
    Reg#(Data) marchid_csr   = readOnlyReg(marchid);
    Reg#(Data) mimpid_csr    = readOnlyReg(mimpid);
    Reg#(Data) mhartid_csr   = readOnlyReg(hartid);

    // Machine Trap Setup
    Reg#(Data) mstatus_csr =  concatReg20(
            sd_field,
            readOnlyReg(0),
            vm_field,
            readOnlyReg(4'b0),
            mxr_field, pum_field, mprv_field, // memory privilege
            xs_field, fs_field, // coprocessor states
            mpp_field, hpp_field, spp_field, // previous privileges
            mpie_field, hpie_field, spie_field, upie_field, // previous interrupt enables
            mie_field, hie_field, sie_field, uie_field); // interrupt enables
    Reg#(Data) medeleg_csr =  concatReg2(readOnlyReg(0), medeleg_field); // WARL - 12 legal exceptions
    Reg#(Data) mideleg_csr =  concatReg2(readOnlyReg(0), mideleg_field); // WARL - 12 legal interrupts
    Reg#(Data) mie_csr     =  concatReg13(
            readOnlyReg(0),
            meie_field, heie_field, seie_field, ueie_field,
            mtie_field, htie_field, stie_field, utie_field,
            msie_field, hsie_field, ssie_field, usie_field);
    Reg#(Data) mtvec_csr   =  concatReg2(mtvec_field, readOnlyReg(2'd0));

    // Machine Trap Handling
    Reg#(Data) mscratch_csr <- mkReg(0);
    Reg#(Data) mepc_csr     <- mkReg(0);
    Reg#(Data) mcause_csr   <- mkReg(0);
    Reg#(Data) mbadaddr_csr <- mkReg(0);
    Reg#(Data) mip_csr      =  concatReg13(
            readOnlyReg(0),
            readOnlyReg(meip_field), readOnlyReg(heip_field), readOnlyReg(seip_field), readOnlyReg(ueip_field),
            readOnlyReg(mtip_field), htip_field, stip_field, utip_field,
            readOnlyReg(msip_field), hsip_field, ssip_field, usip_field);

    // Machine Protection and Translation
    Reg#(Data) mbase_csr   <- mkReg(0);
    Reg#(Data) mbound_csr  <- mkReg(0);
    Reg#(Data) mibase_csr  <- mkReg(0);
    Reg#(Data) mibound_csr <- mkReg(0);
    Reg#(Data) mdbase_csr  <- mkReg(0);
    Reg#(Data) mdbound_csr <- mkReg(0);

    // Machine Counter Setup
    Reg#(Data) mucounteren_csr = concatReg4(readOnlyReg(0), u_ir_field, u_tm_field, u_cy_field);
    Reg#(Data) mscounteren_csr = concatReg4(readOnlyReg(0), s_ir_field, s_tm_field, s_cy_field);

    function Reg#(Data) getCSR(CSR csr);
        return (case (csr)
                CSRfflags:              fflags_csr;
                CSRfrm:                 frm_csr;
                CSRfcsr:                fcsr_csr;
                CSRcycle:               cycle_csr;
                CSRtime:                time_csr;
                CSRinstret:             instret_csr;
                CSRsstatus:             sstatus_csr;
                CSRsedeleg:             sedeleg_csr;
                CSRsideleg:             sideleg_csr;
                CSRsie:                 sie_csr;
                CSRstvec:               stvec_csr;
                CSRsscratch:            sscratch_csr;
                CSRsepc:                sepc_csr;
                CSRscause:              scause_csr;
                CSRsbadaddr:            sbadaddr_csr;
                CSRsip:                 sip_csr;
                CSRsptbr:               sptbr_csr;
                CSRscycle:              scycle_csr;
                CSRstime:               stime_csr;
                CSRsinstret:            sinstret_csr;
                CSRmisa:                misa_csr;
                CSRmvendorid:           mvendorid_csr;
                CSRmarchid:             marchid_csr;
                CSRmimpid:              mimpid_csr;
                CSRmhartid:             mhartid_csr;
                CSRmstatus:             mstatus_csr;
                CSRmedeleg:             medeleg_csr;
                CSRmideleg:             mideleg_csr;
                CSRmie:                 mie_csr;
                CSRmtvec:               mtvec_csr;
                CSRmscratch:            mscratch_csr;
                CSRmepc:                mepc_csr;
                CSRmcause:              mcause_csr;
                CSRmbadaddr:            mbadaddr_csr;
                CSRmip:                 mip_csr;
                CSRmbase:               mbase_csr;
                CSRmbound:              mbound_csr;
                CSRmibase:              mibase_csr;
                CSRmibound:             mibound_csr;
                CSRmdbase:              mdbase_csr;
                CSRmdbound:             mdbound_csr;
                CSRmcycle:              mcycle_csr;
                CSRmtime:               mtime_csr;
                CSRminstret:            minstret_csr;
                CSRmucounteren:         mucounteren_csr;
                CSRmscounteren:         mscounteren_csr;
                CSRmucycle_delta:       mucycle_delta_csr;
                CSRmutime_delta:        mutime_delta_csr;
                CSRmuinstret_delta:     muinstret_delta_csr;
                CSRmscycle_delta:       mscycle_delta_csr;
                CSRmstime_delta:        mstime_delta_csr;
                CSRmsinstret_delta:     msinstret_delta_csr;
                default:                (readOnlyReg(64'h0));
            endcase);
    endfunction

    function Bool isLegalCSR(CSR csr);
        return (case (csr)
                CSRfflags:              (fs_field != 0);
                CSRfrm:                 (fs_field != 0);
                CSRfcsr:                (fs_field != 0);
                CSRcycle:               (u_cy_field == 1);
                CSRtime:                (u_tm_field == 1);
                CSRinstret:             (u_ir_field == 1);
                CSRsstatus:             True;
                CSRsedeleg:             True;
                CSRsideleg:             True;
                CSRsie:                 True;
                CSRstvec:               True;
                CSRsscratch:            True;
                CSRsepc:                True;
                CSRscause:              True;
                CSRsbadaddr:            True;
                CSRsip:                 True;
                CSRsptbr:               True;
                CSRscycle:              (s_cy_field == 1);
                CSRstime:               (s_tm_field == 1);
                CSRsinstret:            (s_ir_field == 1);
                CSRmisa:                True;
                CSRmvendorid:           True;
                CSRmarchid:             True;
                CSRmimpid:              True;
                CSRmhartid:             True;
                CSRmstatus:             True;
                CSRmedeleg:             True;
                CSRmideleg:             True;
                CSRmie:                 True;
                CSRmtvec:               True;
                CSRmscratch:            True;
                CSRmepc:                True;
                CSRmcause:              True;
                CSRmbadaddr:            True;
                CSRmip:                 True;
                CSRmbase:               True;
                CSRmbound:              True;
                CSRmibase:              True;
                CSRmibound:             True;
                CSRmdbase:              True;
                CSRmdbound:             True;
                CSRmcycle:              True;
                CSRmtime:               True;
                CSRminstret:            True;
                CSRmucounteren:         True;
                CSRmscounteren:         True;
                CSRmucycle_delta:       True;
                CSRmutime_delta:        True;
                CSRmuinstret_delta:     True;
                CSRmscycle_delta:       True;
                CSRmstime_delta:        True;
                CSRmsinstret_delta:     True;
                default:                False;
            endcase);
    endfunction

    // RULES
    ////////////////////////////////////////////////////////

    rule incrementCycle;
        cycle_counter <= cycle_counter + 1;
    endrule

    // METHODS
    ////////////////////////////////////////////////////////

    method VMInfo vmI;
        Bit#(5) vm = (prv == prvM) ? vmMbare : vm_field;
        Addr base = (case (vm)
                        vmMbare: 0;
                        vmMbb: mbase_csr;
                        vmMbbid: mibase_csr;
                        // all paged virtual memory modes
                        default: {0, sptbr_ppn_field, 12'd0};
                    endcase);
        Addr bound = (case (vm)
                        vmMbb: mbound_csr;
                        vmMbbid: mibound_csr;
                        default: -1;
                    endcase);
        return VMInfo{ prv: prv, asid: asid_field, vm: vm, mxr: unpack(mxr_field), pum: unpack(pum_field), base: base, bound: bound };
    endmethod

    method VMInfo vmD;
        Bit#(2) vm_prv = (mprv_field == 1) ? mpp_field : prv;
        Bit#(5) vm = (vm_prv == prvM) ? vmMbare : vm_field;
        Addr base = (case (vm)
                        vmMbare: 0;
                        vmMbb: mbase_csr;
                        vmMbbid: mdbase_csr;
                        // all paged virtual memory modes
                        default: {0, sptbr_ppn_field, 12'd0};
                    endcase);
        Addr bound = (case (vm)
                        vmMbb: mbound_csr;
                        vmMbbid: mdbound_csr;
                        default: -1;
                    endcase);
        return VMInfo{ prv: vm_prv, asid: asid_field, vm: vm, mxr: unpack(mxr_field), pum: unpack(pum_field), base: base, bound: bound };
    endmethod

    method CsrState csrState = CsrState {prv: prv, frm: frm_field, f_enabled: (fs_field != 0), x_enabled: (xs_field != 0)};

    method Maybe#(InterruptCause) readyInterrupt;
        Bit#(12) ready_interrupts = truncate(mip_csr) & truncate(mie_csr);
        // machine mode
        let ready_machine_interrupts = ready_interrupts & ~truncate(mideleg_csr);
        Bool machine_interrupts_enabled = (mie_field == 1) || (prv < prvM);
        // supervisor mode
        let ready_supervisor_interrupts = ready_interrupts & truncate(mideleg_csr) & ~truncate(sideleg_csr);
        Bool supervisor_interrupts_enabled = ((sie_field == 1) && (prv == prvS)) || (prv < prvS);
        // user mode
        let ready_user_interrupts = ready_interrupts & truncate(mideleg_csr) & truncate(sideleg_csr);
        let user_interrupts_enabled = (uie_field == 1) && (prv == prvU);
        // combined
        ready_interrupts = (machine_interrupts_enabled ? ready_machine_interrupts : 0)
                            | (supervisor_interrupts_enabled ? ready_supervisor_interrupts : 0)
                            | (user_interrupts_enabled ? ready_user_interrupts : 0);
        // format pendingInterrupt value to return
        Maybe#(InterruptCause) ret = tagged Invalid;
        if (ready_interrupts != 0) begin
            // pack/unpack type conversion:
            // UInt#(TLog#(TAdd#(12,1))) == UInt#(4) -> Bit#(4) -> InterruptCause
            ret = tagged Valid unpack(pack(countZerosLSB(ready_interrupts)));
        end
        return ret;
    endmethod

    method ActionValue#(CsrReturn) wr(
            Addr pc, // pc of current exception
            Maybe#(SystemInst) sysInst,
            CSR csr,
            Data data, // zimm or rval
            Addr addr, // badaddr
            Maybe#(TrapCause) trap, // exception or interrupt
            // indirect updates
            Bit#(5) fflags,
            Bool fpuDirty,
            Bool xDirty);

        Maybe#(TrapCause) trapToTake = trap;
        if (!isValid(trapToTake) &&& sysInst matches tagged Valid .validSysInst) begin
            case (validSysInst)
                ECall:  trapToTake = tagged Valid (case (prv)
                                                    prvU: (tagged Exception EnvCallU);
                                                    prvS: (tagged Exception EnvCallS);
                                                    prvH: (tagged Exception EnvCallH);
                                                    prvM: (tagged Exception EnvCallM);
                                                endcase);
                // URet and HRet are not supported
                URet: trapToTake = tagged Valid (tagged Exception IllegalInst);
                SRet:
                    begin
                        if (prv < prvS) begin
                            trapToTake = tagged Valid (tagged Exception IllegalInst);
                        end
                    end
                HRet: trapToTake = tagged Valid (tagged Exception IllegalInst);
                MRet:
                    begin
                        if (prv < prvM) begin
                            trapToTake = tagged Valid (tagged Exception IllegalInst);
                        end
                    end
                EBreak: trapToTake = tagged Valid (tagged Exception Breakpoint);
                CSRRW, CSRRS, CSRRC, CSRR, CSRW:
                    begin
                        Bool read = (validSysInst != CSRW);
                        Bool write = (validSysInst != CSRR);
                        if (!isLegalCSR(csr) || !hasCSRPermission(csr, prv, write)) begin
                            trapToTake = tagged Valid (tagged Exception IllegalInst);
                        end
                    end
            endcase
        end

        // Three case from this point onward:
        // 1) Trap (exception or interrupt)
        // 2) Non-trapping system instruction
        //      increment instret
        // 3) Normal instruction
        //      increment instret
        //      update fs and fflags if necessary
        if (trapToTake matches tagged Valid .validTrap) begin
            // Traps
            // delegate to S if the prv <= S and the corresponding deleg bit is set
            Bool delegToS = prv <= prvS && (case (validTrap) matches
                    tagged Exception .exceptionCause: (((medeleg_csr >> pack(exceptionCause)) & 1) != 0);
                    tagged Interrupt .interruptCause: (((mideleg_csr >> pack(interruptCause)) & 1) != 0);
                endcase);
            Addr newPC = delegToS ? stvec_csr : mtvec_csr;
            if (delegToS) begin
                // trap to prvS
                sepc_csr <= pc;
                scause_csr <= toCauseCSR(validTrap);
                case (validTrap)
                    tagged Exception InstAddrMisaligned,
                    tagged Exception InstAccessFault,
                    tagged Exception LoadAddrMisaligned,
                    tagged Exception LoadAccessFault,
                    tagged Exception StoreAddrMisaligned,
                    tagged Exception StoreAccessFault:
                        sbadaddr_csr <= addr;
                endcase
                // update mstatus fields
                spp_field <= (prv == prvU) ? 0 : 1;
                spie_field <= (prv == prvU) ? uie_field : sie_field;
                sie_field <= 0;
                // update privilege
                prv <= prvS;
            end else begin
                // trap to prvM
                mepc_csr <= pc;
                mcause_csr <= toCauseCSR(validTrap);
                case (validTrap)
                    tagged Exception InstAddrMisaligned,
                    tagged Exception InstAccessFault,
                    tagged Exception LoadAddrMisaligned,
                    tagged Exception LoadAccessFault,
                    tagged Exception StoreAddrMisaligned,
                    tagged Exception StoreAccessFault:
                        mbadaddr_csr <= addr;
                endcase
                // update mstatus fields
                mpp_field <= prv;
                mpie_field <= (case (prv)
                                prvU: uie_field;
                                prvS: sie_field;
                                prvH: hie_field;
                                prvM: mie_field;
                            endcase);
                mie_field <= 0;
                // update privilege
                prv <= prvM;
            end
            return tagged Exception {exception: validTrap, trapHandlerPC: newPC};
        end else if (sysInst matches tagged Valid .validSysInst) begin
            // Non-trapping system instructions
            instret_counter <= instret_counter + 1;
            case (validSysInst)
                SRet:
                    begin
                        let next_prv = spp_field == 1 ? prvS : prvU;
                        case (next_prv)
                            prvU: uie_field <= spie_field;
                            prvS: sie_field <= spie_field;
                        endcase
                        spie_field <= 0;
                        spp_field <= 0;
                        prv <= next_prv;
                        Addr newPC = sepc_csr;
                        return tagged RedirectPC newPC;
                    end
                MRet:
                    begin
                        let next_prv = mpp_field;
                        case (next_prv)
                            prvU: uie_field <= mpie_field;
                            prvS: sie_field <= mpie_field;
                            prvM: mie_field <= mpie_field;
                        endcase
                        mpie_field <= 0;
                        mpp_field <= prvU;
                        prv <= next_prv;
                        Addr newPC = mepc_csr;
                        return tagged RedirectPC newPC;
                    end
                CSRRW, CSRRS, CSRRC, CSRR, CSRW:
                    begin
                        // Not used at the moment, just a place holder in case
                        // any read side-effects are needed in the CSRs.
                        Bool read = (validSysInst != CSRW);
                        Bool write = (validSysInst != CSRR);
                        // CSR read/write operation
                        let oldVal = getCSR(csr)._read;
                        let newVal = (case(validSysInst)
                                CSRW, CSRR, CSRRW: data;
                                CSRRS: (oldVal | data);
                                CSRRC: (oldVal & (~data));
                            endcase);
                        if (write) begin
                            getCSR(csr)._write(newVal);
                        end
                        return tagged CsrData oldVal;
                    end
                default:
                    begin
                        return tagged None;
                    end
            endcase
        end else begin
            // Normal instruction
            // update fflags, fs, and xs if necessary
            if ((fflags | fflags_field) != fflags_field) begin
                fflags_field <= fflags_field | fflags;
                fpuDirty = True;
            end
            if (fpuDirty) begin
                if (fs_field == 0) begin
                    // fs_field shouldn't be 00
                    $fdisplay(stderr, "[ERROR] CSRFile: fpuDirty is true, but fs_field is set to 0");
                end
                fs_field <= 2'b11;
            end
            if (xDirty) begin
                xs_field <= 2'b11;
            end
            instret_counter <= instret_counter + 1;
            return tagged None;
        end
    endmethod
endmodule


Use Jamey's mapping magic to make the connections

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

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

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

import SerialFIFO::*;
import NocNode::*;
import Connectable::*;
import StmtFSM::*;
import Vector::*;
import FIFOF::*;
import Pipe::*;

interface NocIndication;
   method Action ack(Bit#(8) recvnode, Bit#(8) to, Bit#(32) message);
endinterface

interface NocRequest;
   method Action send(Bit#(8) sendnode, Bit#(8) to, Bit#(32) message);
endinterface

module mkPipeOutDiscard#(PipeOut#(Bit#(1)) x)(Empty);
   rule toss;
      x.deq();
   endrule
endmodule

module mkNocRequest#(NocIndication indication)(NocRequest);

   Vector#(4, Vector#(4, NocNode#(2))) node = replicate(newVector);

   for (Bit#(4) x = 0; x < 4; x = x + 1)
      for (Bit#(4) y = 0; y < 4; y = y + 1)
	 begin
	    Vector#(2, Bit#(4)) id = newVector;
	    id[0] = x;
	    id[1] = y;
	    node[x][y] <- mkNocNode(id);
	 end

   // wire up the links in the x direction
   Vector#(3, Integer) indexes3 = genVector();
   Vector#(4, Integer) indexes4 = genVector();
   module mkXLinks#(Integer x)(Empty);
      module mkXLink#(Integer x, Integer y)(Empty);
	 mkConnection(node[x][y].linkupout[0], node[x+1][y].linkupin[0]);
	 mkConnection(node[x+1][y].linkdownout[0], node[x][y].linkupin[0]);
      endmodule
      mapM_(mkXLink(x), indexes4);
   endmodule

   mapM_(mkXLinks, indexes3);
   /*
   for (Bit#(4) x = 0; x < 3; x = x + 1)
      for (Bit#(4) y = 0; y < 4; y = y + 1)
	 begin
	    mkConnection(node[x][y].linkupout[0], node[x+1][y].linkupin[0]);
	    mkConnection(node[x+1][y].linkdownout[0], node[x][y].linkupin[0]);
	 end
*/
   // wire up the links in the y direction

   module mkYLinks#(Integer x)(Empty);
   module mkYLink#(Integer x, Integer y)(Empty);
      mkConnection(node[x][y].linkupout[1], node[x][y+1].linkupin[1]);
      mkConnection(node[x][y+1].linkdownout[1], node[x][y].linkupin[1]);
   endmodule
      mapM_(mkYLink(x), indexes4);
   endmodule
   mapM_(mkYLinks, indexes3);

   /*
   for (Bit#(4) x = 0; x < 4; x = x + 1)
      for (Bit#(4) y = 0; y < 3; y = y + 1)
	 begin
	    mkConnection(node[x][y].linkupout[1], node[x][y+1].linkupin[1]);
	    mkConnection(node[x][y+1].linkdownout[1], node[x][y].linkupin[1]);
	 end
*/
   // discard traffic from loose ends in x direction
   for (Bit#(4) x = 0; x < 4; x = x + 1)
      begin
	 mkPipeOutDiscard(node[x][0].linkdownout[1]);
	 mkPipeOutDiscard(node[x][3].linkupout[1]);
      end
   // discard traffic from loose ends in y direction
   for (Bit#(4) y = 0; y < 4; y = y + 1)
      begin
	 mkPipeOutDiscard(node[0][y].linkdownout[0]);
	 mkPipeOutDiscard(node[3][y].linkupout[0]);
      end

  // fsm to read from host ports and generate indications

  Reg#(Bit#(4)) idx <- mkReg(0);
  Reg#(Bit#(4)) idy <- mkReg(0);

  Stmt readindications =
    seq
    while(True) seq
      for(idx <= 0; idx < 4; idx <= idy + 1)
	 for(idy <= 0; idy < 4; idy <= idy + 1)
            if (node[idx][idy].nodetohost.notEmpty())
	      seq
		 $display("recv at [%d,%d] to [%d,%d] m %x",
		    idx,idy,
		    node[idx][idy].nodetohost.first.address[0],
		    node[idx][idy].nodetohost.first.address[1],
	            node[idx][idy].nodetohost.first.payload);
		 indication.ack({idx,idy},
		    unpack(pack(node[idx][idy].nodetohost.first.address)),
	            node[idx][idy].nodetohost.first.payload);
		 node[idx][idy].nodetohost.deq();
              endseq
      endseq
    endseq;

    mkAutoFSM(readindications);

   method Action send(Bit#(8) sendnode, Bit#(8) to, Bit#(32) message);
      Vector#(2,Bit#(4)) id = newVector;
      Vector#(2,Bit#(4)) dest = newVector;
      id[0] = sendnode[7:4];
      id[1] = sendnode[3:0];
      dest[0] = to[7:4];
      dest[1] = to[3:0];
      $display("send f %x t %x m %x", sendnode, to, message);
      node[id[0]][id[1]].hosttonode.enq(DataMessage{address: dest, payload: message});
   endmethod

endmodule

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

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

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

import SerialFIFO::*;
import NocNode::*;
import Connectable::*;
import StmtFSM::*;
import Vector::*;
import FIFOF::*;
import Pipe::*;

interface NocIndication;
   method Action ack(Bit#(8) recvnode, Bit#(8) to, Bit#(32) message);
endinterface

interface NocRequest;
   method Action send(Bit#(8) sendnode, Bit#(8) to, Bit#(32) message);
endinterface

module mkPipeOutDiscard#(PipeOut#(Bit#(1)) x)(Empty);
   rule toss;
      x.deq();
   endrule
endmodule

module mkNocRequest#(NocIndication indication)(NocRequest);

   Vector#(4, Vector#(4, NocNode#(2))) node = replicate(newVector);

   for (Bit#(4) x = 0; x < 4; x = x + 1)
      for (Bit#(4) y = 0; y < 4; y = y + 1)
	 begin
	    Vector#(2, Bit#(4)) id = newVector;
	    id[0] = x;
	    id[1] = y;
	    node[x][y] <- mkNocNode(id);
	 end

   // wire up the links in the x direction
   for (Bit#(4) x = 0; x < 3; x = x + 1)
      for (Bit#(4) y = 0; y < 4; y = x + 1)
	 begin
	    mkConnection(node[x][y].linkupout[0], node[x+1][y].linkupin[0]);
	    mkConnection(node[x+1][y].linkdownout[0], node[x][y].linkupin[0]);
	 end
   // wire up the links in the y direction
   for (Bit#(4) x = 0; x < 4; x = x + 1)
      for (Bit#(4) y = 0; y < 3; y = x + 1)
	 begin
	    mkConnection(node[x][y].linkupout[1], node[x][y+1].linkupin[1]);
	    mkConnection(node[x][y+1].linkdownout[1], node[x][y].linkupin[1]);
	 end

   // discard traffic from loose ends in x direction
   for (Bit#(4) x = 0; x < 4; x = x + 1)
      begin
	 mkPipeOutDiscard(node[x][0].linkdownout[1]);
	 mkPipeOutDiscard(node[x][3].linkupout[1]);
      end
   // discard traffic from loose ends in y direction
   for (Bit#(4) y = 0; y < 4; y = y + 1)
      begin
	 mkPipeOutDiscard(node[0][y].linkdownout[0]);
	 mkPipeOutDiscard(node[3][y].linkupout[0]);
      end

  // fsm to read from host ports and generate indications

  Reg#(Bit#(4)) idx <- mkReg(0);
  Reg#(Bit#(4)) idy <- mkReg(0);

  Stmt readindications =
    seq
    while(True) seq
      for(idx <= 0; idx < 4; idx <= idy + 1)
	 for(idy <= 0; idy < 4; idy <= idy + 1)
            if (node[idx][idy].nodetohost.notEmpty())
	      seq
		 $display("recv at [%d,%d] to [%d,%d] m %x",
		    idx,idy,
		    node[idx][idy].nodetohost.first.address[0],
		    node[idx][idy].nodetohost.first.address[1],
	            node[idx][idy].nodetohost.first.payload);
		 indication.ack({idx,idy},
		    unpack(pack(node[idx][idy].nodetohost.first.address)),
	            node[idx][idy].nodetohost.first.payload);
		 node[idx][idy].nodetohost.deq();
              endseq
      endseq
    endseq;

    mkAutoFSM(readindications);

   method Action send(Bit#(8) sendnode, Bit#(8) to, Bit#(32) message);
      Vector#(2,Bit#(4)) id = newVector;
      Vector#(2,Bit#(4)) dest = newVector;
      id[0] = sendnode[7:4];
      id[1] = sendnode[3:0];
      dest[0] = to[7:4];
      dest[1] = to[3:0];
      $display("send f %x t %x m %x", sendnode, to, message);
      node[id[0]][id[1]].hosttonode.enq(DataMessage{address: dest, payload: message});
   endmethod

endmodule