remove offending semicolon

remove offending semicolon

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

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

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

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

import Complex::*;
import FixedPoint::*;
import Pipe::*;

typedef FixedPoint#(2,16) Signal;
typedef FixedPoint#(2,23) Coeff;
typedef FixedPoint#(4,39) Product;

typedef struct {
		Complex#(Coeff) a;
		Bit#(1) filterPhase;
		} CoeffData;

typedef struct {
		Complex#(Product) y;
		Bit#(1) filterPhase;
		} ProductData;

interface FPCMult;
   interface PipeIn#(Complex#(Signal)) x;
   interface PipeIn#(CoeffData) a;
   interface PipeOut#(ProductData) y;
endinterface

module mkFPCMult(FPCMult);
   /* input registers */
   Reg#(Complex#(Signal)) xin <- mkReg(?);
   Reg#(CoeffData) ain <- mkReg(?);
   /* pipeline registers at output of multipliers */
   Reg#(Product) arxr <- mkReg(?);
   Reg#(Product) aixi <- mkReg(?);
   Reg#(Product) arxi <- mkReg(?);
   Reg#(Product) aixr <- mkReg(?);
   Reg#(Bit#(1)) multstage <- mkReg(?);
   /* result registers */
   Reg#(ProductData) yout <- mkReg(?);

   rule work;
      /* compute multiplies */
      arxr <= fxptMult(ain.a.r, xin.r);
      aixi <= fxptMult(ain.a.i, xin.i);
      arxi <= fxptMult(ain.a.r, xin.i);
      aixr <= fxptMult(ain.a.i, xin.r);
      multFilterPhase <= ain.filterPhase;
      /* combine into outputs */
      yout <= ProductData{y: Complex{r: arxr - aixi, i: arxi + aixr}, filterPhase: multstage};
   endrule

   interface PipeOut y = toPipeOut(yout);
   interface PipeIn x;
      method Action enq(Complex#(Signal) v);
         xin <= v;
      endmethod
      method Bool notFull();
         return (True);
      endmethod
   endinterface
   interface PipeIn a;
      method Action enq(CoeffData v);
         ain <= v;
      endmethod
      method Bool notFull();
         return (True);
      endmethod
   endinterface

endmodule;


remove FIBSTATE4, which should no longer be needed

// 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 StackReg::*;
import StmtFSM::*;

interface FibIndication;
    method Action fibresult(Bit#(32) v);
    method Action fibnote(Bit#(32) v);
endinterface

interface FibRequest;
   method Action fib(Bit#(32) v);
endinterface

typedef enum {FIBSTATEIDLE, FIBSTATE1, FIBSTATE2,
   FIBSTATE2A, FIBSTATE3, FIBSTATECOMPLETE} FSState
deriving (Bits,Eq);

module mkFibRequest#(FibIndication indication)(FibRequest);

   /* pc, args, vars */
   StackReg#(128, FSState, Bit#(16), Bit#(16)) frame <- mkStackReg(128, FIBSTATEIDLE);

   /* function variables that do not need to be saved or restored
    *    in the pseudocode below, this is tmp2
    */
   Reg#(Bit#(16)) fibretval <- mkReg(0);

   /* experiment: recursive fibonnaci
    * fib(n):
    *   int tmp1, tmp2;
    *   if n == 0 return 0  // fibstate1
    *   if n == 1 return 1
    *   tmp1 = fib(n-1)
    *   tmp2 = fib(n-2      // fibstate2
    *   return tmp1 + tmp2  // fibstate3
    */

   rule fib1 (frame.pc == FIBSTATE1);
      //$display("FIBSTATE1 %d", frame.args);
      if (frame.args == 0)
	 begin
	    fibretval <= 0;
	    frame.doreturn();
	 end
     else if (frame.args == 1)
	begin
	   fibretval <= 1;
	   frame.doreturn();
	end
     else
	frame.docall(FIBSTATE1, FIBSTATE2, frame.args - 1);
     endrule

   rule fib2 (frame.pc == FIBSTATE2);
      //$display("FIBSTATE2 (retval %d)", fibretval);
      frame.vars <= fibretval;
      frame.nextpc(FIBSTATE2A);
   endrule

   /* This state is here so the return value can get into frame.vars before
    * the next call
    */
   rule fib2a (frame.pc == FIBSTATE2A);
      //$display("FIBSTATE2A (retval %d)", fibretval);
      frame.docall(FIBSTATE1, FIBSTATE3, frame.args - 2);
   endrule

   rule fib3 (frame.pc == FIBSTATE3);
      //$display("FIBSTATE3 tmp1 %d fibretval %d return %d", frame.vars, fibretval, frame.vars + fibretval);
      fibretval <= frame.vars + fibretval;
      frame.doreturn();
   endrule

   rule fibcomplete (frame.pc == FIBSTATECOMPLETE);
      //$display("FIBSTATECOMPLETE %d %d", frame.args, fibretval);
      indication.fibresult(zeroExtend(fibretval));
      frame.nextpc(FIBSTATEIDLE);
   endrule

   method Action fib(Bit#(32) v);
      //$display("request fib %d", v);
      frame.docall(FIBSTATE1, FIBSTATECOMPLETE, truncate(v));
   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 StackReg::*;
import StmtFSM::*;

interface FibIndication;
    method Action fibresult(Bit#(32) v);
    method Action fibnote(Bit#(32) v);
endinterface

interface FibRequest;
   method Action fib(Bit#(32) v);
endinterface

typedef enum {FIBSTATEIDLE, FIBSTATE1, FIBSTATE2,
   FIBSTATE2A, FIBSTATE3, FIBSTATE4, FIBSTATECOMPLETE} FSState
deriving (Bits,Eq);

module mkFibRequest#(FibIndication indication)(FibRequest);

   /* pc, args, vars */
   StackReg#(128, FSState, Bit#(16), Bit#(16)) frame <- mkStackReg(128, FIBSTATEIDLE);

   /* function variables that do not need to be saved or restored
    *    in the pseudocode below, this is tmp2
    */
   Reg#(Bit#(16)) fibretval <- mkReg(0);

   /* experiment: recursive fibonnaci
    * fib(n):
    *   int tmp1, tmp2;
    *   if n == 0 return 0  // fibstate1
    *   if n == 1 return 1
    *   tmp1 = fib(n-1)
    *   tmp2 = fib(n-2      // fibstate2
    *   return tmp1 + tmp2  // fibstate3
    */

   rule fib1 (frame.pc == FIBSTATE1);
      //$display("FIBSTATE1 %d", frame.args);
      if (frame.args == 0)
	 begin
	    fibretval <= 0;
	    frame.doreturn();
	 end
     else if (frame.args == 1)
	begin
	   fibretval <= 1;
	   frame.doreturn();
	end
     else
	frame.docall(FIBSTATE1, FIBSTATE2, frame.args - 1);
     endrule

   rule fib2 (frame.pc == FIBSTATE2);
      //$display("FIBSTATE2 (retval %d)", fibretval);
      frame.vars <= fibretval;
      frame.nextpc(FIBSTATE2A);
   endrule

   /* This state is here so the return value can get into frame.vars before
    * the next call
    */
   rule fib2a (frame.pc == FIBSTATE2A);
      //$display("FIBSTATE2A (retval %d)", fibretval);
      frame.docall(FIBSTATE1, FIBSTATE3, frame.args - 2);
   endrule

   rule fib3 (frame.pc == FIBSTATE3);
      //$display("FIBSTATE3 tmp1 %d fibretval %d return %d", frame.vars, fibretval, frame.vars + fibretval);
      fibretval <= frame.vars + fibretval;
      frame.nextpc(FIBSTATE4);
   endrule

// stall cycle to avoid doreturns in adjacent cycles
   rule fib4 (frame.pc == FIBSTATE4);
      //$display("FIBSTATE4 ");
      frame.doreturn();
   endrule

   rule fibcomplete (frame.pc == FIBSTATECOMPLETE);
      //$display("FIBSTATECOMPLETE %d %d", frame.args, fibretval);
      indication.fibresult(zeroExtend(fibretval));
      frame.nextpc(FIBSTATEIDLE);
   endrule

   method Action fib(Bit#(32) v);
      //$display("request fib %d", v);
      frame.docall(FIBSTATE1, FIBSTATECOMPLETE, truncate(v));
   endmethod

endmodule


fix y connection stuff

fix y connection stuff

// 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 y)(Empty);
   module mkYLink#(Integer y, Integer x)(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(y), 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
   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


combine FIFOs passing params to stage4 of MMU

// 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 MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;
import SimDma::*;

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;
} ReqTup deriving (Eq,Bits,FShow);

interface MMU#(numeric type addrWidth);
   interface MMURequest request;
   interface Vector#(2,Server#(ReqTup,Bit#(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;
   ReqTup 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#(ReqTup)) incomingReqs <- replicateM(mkFIFO);

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

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

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

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

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

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   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
	 ReqTup 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
	 ReqTup 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:
	       dmaErrorFifo.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();
	 ReqTup 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);
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorOffsetOutOfRange, pref: extend(ptr), off:extend(off.value) });
	 end
	 else begin
	    if (verbose) $display("mkMMU::pages[%d].read %h", i, {ptr,p});
	    portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
						      address:{ptr,p}, datain:?});
	    offs1[i].enq(off);
	 end
      endrule
      rule stage5; // Concatenate page base address from sglist entry with LSB offset bits from request and return
	 Page0 page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs1[i]).get();
	 if (verbose) $display("mkMMU::p ages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(MemOffsetSize) rv = ?;
	 Page4 b4 = truncate(page);
	 Page8 b8 = truncate(page);
	 Page12 b12 = truncate(page);
	 case (offset.pageSize)
	    1: rv = {page,truncate(offset.value)};
	    2: rv = {b4,truncate(offset.value)};
	    3: rv = {b8,truncate(offset.value)};
	    4: rv = {b12,truncate(offset.value)};
	 endcase
	 pageResponseFifos[i].enq(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

   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();
`ifdef BSIM
		rv = rv | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
`ifdef XSIM
		rv = rv | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
		return rv;
	     endmethod
	  endinterface
       endinterface);

   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(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False,
             address:{truncate(pointer),ind}, datain:truncate(addr)});
         if (verbose) $display("mkMMU::sglist pointer=%d pointerIndex=%d addr=%d len=%d", pointer, pointerIndex, addr, len);
   endmethod
   method Action setInterface(Bit#(32) interfaceId, Bit#(32) sglId);
       /* this method is only implemented in s/w responders */
   endmethod
   endinterface
   interface addr = addrServers;

endmodule

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

module mkMMUAddrServer#(Server#(ReqTup,Bit#(addrWidth)) server) (MMUAddrServer#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(9);
   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 MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;
import SimDma::*;

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;
} ReqTup deriving (Eq,Bits,FShow);

interface MMU#(numeric type addrWidth);
   interface MMURequest request;
   interface Vector#(2,Server#(ReqTup,Bit#(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;
   ReqTup req;
   } Stage3Params 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#(ReqTup)) incomingReqs <- replicateM(mkFIFO);

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

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

   // stage 3 (latency == 1)
   Vector#(2,FIFOF#(Offset))           offs0 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(IndexWidth)))         pbases <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(IndexWidth)))    idxOffsets1 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(SGListId))         ptrs1 <- replicateM(mkFIFOF);

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

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

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   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
	 ReqTup 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
	 ReqTup 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:
	       dmaErrorFifo.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();
	 ReqTup 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
	 offs0[i].enq(o);
	 pbases[i].enq(pbase);
	 idxOffsets1[i].enq(idxOffset);
	 ptrs1[i].enq(req.id);
      endrule
      (* descending_urgency = "stage2, stage4" *)
      rule stage4; // Read relevant sglist entry
	 let off <- toGet(offs0[i]).get();
	 let pbase <- toGet(pbases[i]).get();
	 let idxOffset <- toGet(idxOffsets1[i]).get();
	 let ptr <- toGet(ptrs1[i]).get();
	 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);
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorOffsetOutOfRange, pref: extend(ptr), off:extend(off.value) });
	 end
	 else begin
	    if (verbose) $display("mkMMU::pages[%d].read %h", i, {ptr,p});
	    portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
						      address:{ptr,p}, datain:?});
	    offs1[i].enq(off);
	 end
      endrule
      rule stage5; // Concatenate page base address from sglist entry with LSB offset bits from request and return
	 Page0 page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs1[i]).get();
	 if (verbose) $display("mkMMU::p ages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(MemOffsetSize) rv = ?;
	 Page4 b4 = truncate(page);
	 Page8 b8 = truncate(page);
	 Page12 b12 = truncate(page);
	 case (offset.pageSize)
	    1: rv = {page,truncate(offset.value)};
	    2: rv = {b4,truncate(offset.value)};
	    3: rv = {b8,truncate(offset.value)};
	    4: rv = {b12,truncate(offset.value)};
	 endcase
	 pageResponseFifos[i].enq(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

   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();
`ifdef BSIM
		rv = rv | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
`ifdef XSIM
		rv = rv | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
		return rv;
	     endmethod
	  endinterface
       endinterface);

   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(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False,
             address:{truncate(pointer),ind}, datain:truncate(addr)});
         if (verbose) $display("mkMMU::sglist pointer=%d pointerIndex=%d addr=%d len=%d", pointer, pointerIndex, addr, len);
   endmethod
   method Action setInterface(Bit#(32) interfaceId, Bit#(32) sglId);
       /* this method is only implemented in s/w responders */
   endmethod
   endinterface
   interface addr = addrServers;

endmodule

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

module mkMMUAddrServer#(Server#(ReqTup,Bit#(addrWidth)) server) (MMUAddrServer#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(9);
   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