updated AddressGenerator in PcieCsr

updated AddressGenerator in PcieCsr

// 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 Vector         :: *;
import BRAM           :: *;
import FIFOF          :: *;
import GetPut         :: *;
import Connectable    :: *;
import PCIE           :: *;
import Clocks         :: *;
import PcieTracer     :: *;
import MemTypes       :: *;
import AddressGenerator::*;

`define msix_base 1024

// An MSIX table entry, as defined in the PCIe spec
interface MSIX_Entry;
   interface Reg#(Bit#(32)) addr_lo;
   interface Reg#(Bit#(32)) addr_hi;
   interface Reg#(Bit#(32)) msg_data;
   interface Reg#(Bool)     masked;
endinterface

interface ReadOnly_MSIX_Entry;
   interface ReadOnly#(Bit#(32)) addr_lo;
   interface ReadOnly#(Bit#(32)) addr_hi;
   interface ReadOnly#(Bit#(32)) msg_data;
   interface ReadOnly#(Bool)     masked;
endinterface

function ReadOnly_MSIX_Entry toReadOnlyMsixEntry(MSIX_Entry msix);
   return (interface ReadOnly_MSIX_Entry;
	   interface ReadOnly addr_lo = regToReadOnly(msix.addr_lo);
	   interface ReadOnly addr_hi = regToReadOnly(msix.addr_hi);
	   interface ReadOnly msg_data = regToReadOnly(msix.msg_data);
	   interface ReadOnly masked = regToReadOnly(msix.masked);
	   endinterface);
endfunction

// control and status registers accessed from PCIe
interface PcieControlAndStatusRegs;
   interface MemSlave#(32,32) memSlave;
   interface Vector#(16,ReadOnly_MSIX_Entry) msixEntry;
endinterface: PcieControlAndStatusRegs

// This module encapsulates all of the logic for instantiating and
// accessing the control and status registers. It defines the
// registers, the address map, and how the registers respond to reads
// and writes.
module mkPcieControlAndStatusRegs#(TlpTraceData tlpdata)(PcieControlAndStatusRegs);

   // Utility for module creating all of the storage for a single MSIX
   // table entry
   module mkMSIXEntry(MSIX_Entry);
      Reg#(Bit#(32)) _addr_lo  <- mkReg(0);
      Reg#(Bit#(32)) _addr_hi  <- mkReg(0);
      Reg#(Bit#(32)) _msg_data <- mkReg(0);
      Reg#(Bool)     _masked   <- mkReg(True);

      interface addr_lo  = _addr_lo;
      interface addr_hi  = _addr_hi;
      interface msg_data = _msg_data;
      interface masked   = _masked;
   endmodule: mkMSIXEntry

   // Registers and their default values
   Vector#(16,MSIX_Entry) msix_entry              <- replicateM(mkMSIXEntry);
   Reg#(TimestampedTlpData) pcieTraceBramResponse <- mkReg(unpack(0));
   Reg#(Bit#(TAdd#(TlpTraceAddrSize,1))) bramMuxRdAddrReg <- mkReg(0);

   // Function to return a one-word slice of the tlpTraceBramResponse
   function Bit#(32) tlpTraceBramResponseSlice(Reg#(TimestampedTlpData) data, Bit#(3) i);
       Bit#(8) i8 = zeroExtend(i);
       begin
           Bit#(192) v = extend(pack(data));
           return v[31 + (i8*32) : 0 + (i8*32)];
       end
   endfunction

   // State used to actually service read and write requests
   rule brmMuxResponse;
       let v <- tlpdata.bramServer.response.get();
       pcieTraceBramResponse <= v;
   endrule

   AddressGenerator#(16,32)           csrRag <- mkAddressGenerator;
   AddressGenerator#(16,32)           csrWag <- mkAddressGenerator;
   FIFOF#(MemData#(32))     readResponseFifo <- mkFIFOF();
   FIFOF#(MemData#(32))        writeDataFifo <- mkFIFOF();
   FIFOF#(Bit#(ObjectTagSize)) writeDoneFifo <- mkFIFOF();

   FIFOF#(AddrBeat#(16)) csrRagBeatFifo <- mkFIFOF();
   FIFOF#(Bool)       csrIsMsixAddrFifo <- mkFIFOF();
   FIFOF#(Vector#(1024,Bool)) csrOneHotFifo <- mkFIFOF();
   rule readDataRule;
      let beat <- csrRag.addrBeat.get();
      let addr = beat.addr >> 2; // word address
      Bit#(32) data = 0;
      let modaddr = (addr % 8192);
      let msixaddr = modaddr - `msix_base;

      csrRagBeatFifo.enq(beat);
      csrIsMsixAddrFifo.enq(msixaddr >= 0 && msixaddr <= 63);
      function Bool addrDecode(Integer i); return addr == fromInteger(i); endfunction
      csrOneHotFifo.enq(genWith(addrDecode));
   endrule
   rule readDataRule2;
      let beat       <- toGet(csrRagBeatFifo).get();
      let isMsixAddr <- toGet(csrIsMsixAddrFifo).get();
      let addr = beat.addr >> 2; // word address
      Bit#(32) data = 32'hbad0add0;
      let modaddr = (addr % 8192);
      let msix_base = `msix_base;
      let msixaddr = modaddr - msix_base;
      let oneHotDecode <- toGet(csrOneHotFifo).get();

      if (isMsixAddr) begin
         begin
            let groupaddr = (msixaddr / 4);
            //******************************** msix_base has to match CONFIG.MXIx_Table_Offset in scripts/xbsv-synth-pcie.tcl
            case (msixaddr % 4)
               0: data = msix_entry[groupaddr].addr_lo;
               1: data = msix_entry[groupaddr].addr_hi;
               2: data = msix_entry[groupaddr].msg_data;
               3: data = {'0, pack(msix_entry[groupaddr].masked)}; // vector control
               default: data = 32'hbad0add0;
	  //******************************** end of MSIX Table
            endcase
         end
      end
      else begin
	  // board identification
	  if (oneHotDecode[0]) data = 32'h65756c42; // Blue
	  if (oneHotDecode[1]) data = 32'h63657073; // spec

	  if (oneHotDecode[768]) data = extend(bramMuxRdAddrReg);
	  if (oneHotDecode[774]) data = fromInteger(2**valueOf(TAdd#(TlpTraceAddrSize,1)));
	  if (oneHotDecode[775]) data = (tlpdata.tlpTracing ? 1 : 0);
	  if (oneHotDecode[776]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 0);
	  if (oneHotDecode[777]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 1);
	  if (oneHotDecode[778]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 2);
	  if (oneHotDecode[779]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 3);
	  if (oneHotDecode[780]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 4);
	  if (oneHotDecode[781]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 5);
	  if (oneHotDecode[792]) data = extend(tlpdata.pcieTraceBramWrAddr);
	  if (oneHotDecode[794]) data = extend(tlpdata.tlpTraceLimit);

         //******************************** msix_base has to match CONFIG.MXIx_PBA_Offset in scripts/xbsv-synth-pcie.tcl
	  // 4-bit MSIx pending bit field
	  if (oneHotDecode[992]) data = '0;                               // PBA structure (low)
	  if (oneHotDecode[993]) data = '0;                               // PBA structure (high)
	  //******************************** end of PBA Table
      end
      readResponseFifo.enq(MemData { data: data, tag: beat.tag, last: beat.last });
   endrule

   FIFOF#(BRAMRequest#(Bit#(TAdd#(TlpTraceAddrSize,1)),TimestampedTlpData)) bramRequestFifo <- mkFIFOF();
   mkConnection(toGet(bramRequestFifo), tlpdata.bramServer.request);

   rule writeDataRule;
      let beat <- csrWag.addrBeat.get();
      let addr = beat.addr >> 2; // word address
      let memData <- toGet(writeDataFifo).get();
      let dword = memData.data;

      let modaddr = (addr % 8192);
      let msixaddr = modaddr - `msix_base;
      if (msixaddr >= 0 && msixaddr <= 63)
         begin
            let groupaddr = (msixaddr / 4);
            //******************************** area referenced from xilinx_x7_pcie_wrapper.v
            case (msixaddr % 4)
               0: msix_entry[groupaddr].addr_lo  <= (dword & 32'hfffffffc);
               1: msix_entry[groupaddr].addr_hi  <= dword;
               2: msix_entry[groupaddr].msg_data <= dword;
               3: msix_entry[groupaddr].masked <= unpack(dword[0]);
            endcase
         end
      else
         case (modaddr)
	    775: tlpdata.tlpTracing <= (dword != 0) ? True : False;

	    768: begin
		    bramRequestFifo.enq(BRAMRequest{ write: False, responseOnWrite: False, address: bramMuxRdAddrReg, datain: ?});
		    bramMuxRdAddrReg <= bramMuxRdAddrReg + 1;
		 end
	    792: tlpdata.pcieTraceBramWrAddr <= truncate(dword);
	    794: tlpdata.tlpTraceLimit <= truncate(dword);
         endcase
      if (beat.last)
	 writeDoneFifo.enq(beat.tag);
   endrule

   interface MemSlave memSlave;
      interface MemReadServer read_server;
	 interface Put readReq;
	    method Action put(MemRequest#(32) req);
	       csrRag.request.put(MemRequest { addr: truncate(req.addr), burstLen: req.burstLen, tag: req.tag});
	    endmethod
	 endinterface
	 interface Get readData = toGet(readResponseFifo);
   endinterface: read_server

  interface MemWriteServer write_server;
	 interface Put writeReq;
	    method Action put(MemRequest#(32) req);
	       csrWag.request.put(MemRequest { addr: truncate(req.addr), burstLen: req.burstLen, tag: req.tag});
	    endmethod
	 endinterface
     interface Put writeData = toPut(writeDataFifo);
     interface Get writeDone = toGet(writeDoneFifo);
   endinterface: write_server
   endinterface
   interface Vector msixEntry = map(toReadOnlyMsixEntry, msix_entry);
endmodule: mkPcieControlAndStatusRegs

// 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 Vector         :: *;
import BRAM           :: *;
import FIFOF          :: *;
import GetPut         :: *;
import Connectable    :: *;
import PCIE           :: *;
import Clocks         :: *;
import PcieTracer     :: *;
import MemTypes       :: *;
import AddressGenerator::*;

`define msix_base 1024

// An MSIX table entry, as defined in the PCIe spec
interface MSIX_Entry;
   interface Reg#(Bit#(32)) addr_lo;
   interface Reg#(Bit#(32)) addr_hi;
   interface Reg#(Bit#(32)) msg_data;
   interface Reg#(Bool)     masked;
endinterface

interface ReadOnly_MSIX_Entry;
   interface ReadOnly#(Bit#(32)) addr_lo;
   interface ReadOnly#(Bit#(32)) addr_hi;
   interface ReadOnly#(Bit#(32)) msg_data;
   interface ReadOnly#(Bool)     masked;
endinterface

function ReadOnly_MSIX_Entry toReadOnlyMsixEntry(MSIX_Entry msix);
   return (interface ReadOnly_MSIX_Entry;
	   interface ReadOnly addr_lo = regToReadOnly(msix.addr_lo);
	   interface ReadOnly addr_hi = regToReadOnly(msix.addr_hi);
	   interface ReadOnly msg_data = regToReadOnly(msix.msg_data);
	   interface ReadOnly masked = regToReadOnly(msix.masked);
	   endinterface);
endfunction

// control and status registers accessed from PCIe
interface PcieControlAndStatusRegs;
   interface MemSlave#(32,32) memSlave;
   interface Vector#(16,ReadOnly_MSIX_Entry) msixEntry;
endinterface: PcieControlAndStatusRegs

// This module encapsulates all of the logic for instantiating and
// accessing the control and status registers. It defines the
// registers, the address map, and how the registers respond to reads
// and writes.
module mkPcieControlAndStatusRegs#(TlpTraceData tlpdata)(PcieControlAndStatusRegs);

   // Utility for module creating all of the storage for a single MSIX
   // table entry
   module mkMSIXEntry(MSIX_Entry);
      Reg#(Bit#(32)) _addr_lo  <- mkReg(0);
      Reg#(Bit#(32)) _addr_hi  <- mkReg(0);
      Reg#(Bit#(32)) _msg_data <- mkReg(0);
      Reg#(Bool)     _masked   <- mkReg(True);

      interface addr_lo  = _addr_lo;
      interface addr_hi  = _addr_hi;
      interface msg_data = _msg_data;
      interface masked   = _masked;
   endmodule: mkMSIXEntry

   // Registers and their default values
   Vector#(16,MSIX_Entry) msix_entry              <- replicateM(mkMSIXEntry);
   Reg#(TimestampedTlpData) pcieTraceBramResponse <- mkReg(unpack(0));
   Reg#(Bit#(TAdd#(TlpTraceAddrSize,1))) bramMuxRdAddrReg <- mkReg(0);

   // Function to return a one-word slice of the tlpTraceBramResponse
   function Bit#(32) tlpTraceBramResponseSlice(Reg#(TimestampedTlpData) data, Bit#(3) i);
       Bit#(8) i8 = zeroExtend(i);
       begin
           Bit#(192) v = extend(pack(data));
           return v[31 + (i8*32) : 0 + (i8*32)];
       end
   endfunction

   // State used to actually service read and write requests
   rule brmMuxResponse;
       let v <- tlpdata.bramServer.response.get();
       pcieTraceBramResponse <= v;
   endrule

   AddressGenerator#(16)              csrRag <- mkAddressGenerator;
   AddressGenerator#(16)              csrWag <- mkAddressGenerator;
   FIFOF#(MemData#(32))     readResponseFifo <- mkFIFOF();
   FIFOF#(MemData#(32))        writeDataFifo <- mkFIFOF();
   FIFOF#(Bit#(ObjectTagSize)) writeDoneFifo <- mkFIFOF();

   FIFOF#(AddrBeat#(16)) csrRagBeatFifo <- mkFIFOF();
   FIFOF#(Bool)       csrIsMsixAddrFifo <- mkFIFOF();
   FIFOF#(Vector#(1024,Bool)) csrOneHotFifo <- mkFIFOF();
   rule readDataRule;
      let beat <- csrRag.addrBeat.get();
      let addr = beat.addr >> 2; // word address
      Bit#(32) data = 0;
      let modaddr = (addr % 8192);
      let msixaddr = modaddr - `msix_base;

      csrRagBeatFifo.enq(beat);
      csrIsMsixAddrFifo.enq(msixaddr >= 0 && msixaddr <= 63);
      function Bool addrDecode(Integer i); return addr == fromInteger(i); endfunction
      csrOneHotFifo.enq(genWith(addrDecode));
   endrule
   rule readDataRule2;
      let beat       <- toGet(csrRagBeatFifo).get();
      let isMsixAddr <- toGet(csrIsMsixAddrFifo).get();
      let addr = beat.addr >> 2; // word address
      Bit#(32) data = 32'hbad0add0;
      let modaddr = (addr % 8192);
      let msix_base = `msix_base;
      let msixaddr = modaddr - msix_base;
      let oneHotDecode <- toGet(csrOneHotFifo).get();

      if (isMsixAddr) begin
         begin
            let groupaddr = (msixaddr / 4);
            //******************************** msix_base has to match CONFIG.MXIx_Table_Offset in scripts/xbsv-synth-pcie.tcl
            case (msixaddr % 4)
               0: data = msix_entry[groupaddr].addr_lo;
               1: data = msix_entry[groupaddr].addr_hi;
               2: data = msix_entry[groupaddr].msg_data;
               3: data = {'0, pack(msix_entry[groupaddr].masked)}; // vector control
               default: data = 32'hbad0add0;
	  //******************************** end of MSIX Table
            endcase
         end
      end
      else begin
	  // board identification
	  if (oneHotDecode[0]) data = 32'h65756c42; // Blue
	  if (oneHotDecode[1]) data = 32'h63657073; // spec

	  if (oneHotDecode[768]) data = extend(bramMuxRdAddrReg);
	  if (oneHotDecode[774]) data = fromInteger(2**valueOf(TAdd#(TlpTraceAddrSize,1)));
	  if (oneHotDecode[775]) data = (tlpdata.tlpTracing ? 1 : 0);
	  if (oneHotDecode[776]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 0);
	  if (oneHotDecode[777]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 1);
	  if (oneHotDecode[778]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 2);
	  if (oneHotDecode[779]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 3);
	  if (oneHotDecode[780]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 4);
	  if (oneHotDecode[781]) data = tlpTraceBramResponseSlice(pcieTraceBramResponse, 5);
	  if (oneHotDecode[792]) data = extend(tlpdata.pcieTraceBramWrAddr);
	  if (oneHotDecode[794]) data = extend(tlpdata.tlpTraceLimit);

         //******************************** msix_base has to match CONFIG.MXIx_PBA_Offset in scripts/xbsv-synth-pcie.tcl
	  // 4-bit MSIx pending bit field
	  if (oneHotDecode[992]) data = '0;                               // PBA structure (low)
	  if (oneHotDecode[993]) data = '0;                               // PBA structure (high)
	  //******************************** end of PBA Table
      end
      readResponseFifo.enq(MemData { data: data, tag: beat.tag, last: beat.last });
   endrule

   FIFOF#(BRAMRequest#(Bit#(TAdd#(TlpTraceAddrSize,1)),TimestampedTlpData)) bramRequestFifo <- mkFIFOF();
   mkConnection(toGet(bramRequestFifo), tlpdata.bramServer.request);

   rule writeDataRule;
      let beat <- csrWag.addrBeat.get();
      let addr = beat.addr >> 2; // word address
      let memData <- toGet(writeDataFifo).get();
      let dword = memData.data;

      let modaddr = (addr % 8192);
      let msixaddr = modaddr - `msix_base;
      if (msixaddr >= 0 && msixaddr <= 63)
         begin
            let groupaddr = (msixaddr / 4);
            //******************************** area referenced from xilinx_x7_pcie_wrapper.v
            case (msixaddr % 4)
               0: msix_entry[groupaddr].addr_lo  <= (dword & 32'hfffffffc);
               1: msix_entry[groupaddr].addr_hi  <= dword;
               2: msix_entry[groupaddr].msg_data <= dword;
               3: msix_entry[groupaddr].masked <= unpack(dword[0]);
            endcase
         end
      else
         case (modaddr)
	    775: tlpdata.tlpTracing <= (dword != 0) ? True : False;

	    768: begin
		    bramRequestFifo.enq(BRAMRequest{ write: False, responseOnWrite: False, address: bramMuxRdAddrReg, datain: ?});
		    bramMuxRdAddrReg <= bramMuxRdAddrReg + 1;
		 end
	    792: tlpdata.pcieTraceBramWrAddr <= truncate(dword);
	    794: tlpdata.tlpTraceLimit <= truncate(dword);
         endcase
      if (beat.last)
	 writeDoneFifo.enq(beat.tag);
   endrule

   interface MemSlave memSlave;
      interface MemReadServer read_server;
	 interface Put readReq;
	    method Action put(MemRequest#(32) req);
	       csrRag.request.put(MemRequest { addr: truncate(req.addr), burstLen: req.burstLen, tag: req.tag});
	    endmethod
	 endinterface
	 interface Get readData = toGet(readResponseFifo);
   endinterface: read_server

  interface MemWriteServer write_server;
	 interface Put writeReq;
	    method Action put(MemRequest#(32) req);
	       csrWag.request.put(MemRequest { addr: truncate(req.addr), burstLen: req.burstLen, tag: req.tag});
	    endmethod
	 endinterface
     interface Put writeData = toPut(writeDataFifo);
     interface Get writeDone = toGet(writeDoneFifo);
   endinterface: write_server
   endinterface
   interface Vector msixEntry = map(toReadOnlyMsixEntry, msix_entry);
endmodule: mkPcieControlAndStatusRegs


mkPhysMemSlaveMux no longer forwards too much write data

This fixes a bug that shows up when using a portal clock frequency that
is not the same as the pcie clock frequency. This bug is tracked on
github as issue #133.

// Copyright (c) 2012 Nokia, Inc.
// 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.

import Vector::*;
import GetPut::*;
import FIFOF::*;
import SpecialFIFOs::*;
import FIFO::*;

`include "ConnectalProjectConfig.bsv"
import ConnectalConfig::*;
import HostInterface::*;
import Connectable::*;
import AddressGenerator::*;

import Portal::*;
import MemTypes::*;
import Arith::*;
import Pipe::*;

interface PortalCtrl#(numeric type addrWidth, numeric type dataWidth);
   method ActionValue#(Bit#(dataWidth)) read(Bit#(addrWidth) addr);
   method Action write(Bit#(addrWidth) addr, Bit#(dataWidth) v);
endinterface

interface PortalCtrlMemSlave#(numeric type addrWidth, numeric type dataWidth);
   interface PortalCtrl#(addrWidth, dataWidth)  memSlave;
   interface ReadOnly#(Bool)                    interrupt;
   interface WriteOnly#(Bit#(dataWidth))        num_portals;
endinterface

module mkInterruptMux#(Vector#(numPortals,ReadOnly#(Bool)) inputs) (ReadOnly#(Bool))
   provisos(Add#(nz, TLog#(numPortals), 4),
	    Add#(1, a__, numPortals));
   function Bool my_read(ReadOnly#(Bool) x);
      return x._read;
   endfunction
   method Bool _read;
      return fold(boolor,map(my_read,inputs));
   endmethod
endmodule

module mkPortalInterrupt#(Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indicationPipes)
   (PortalInterrupt#(dataWidth));
   Bool      interruptStatus = False;
   function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
   Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, indicationPipes);

   Bit#(dataWidth)  readyChannel = -1;
   for (Integer i = valueOf(numIndications) - 1; i >= 0; i = i - 1) begin
      if (readyBits[i]) begin
         interruptStatus = True;
         readyChannel = fromInteger(i);
      end
   end
   method Bool status();
      return interruptStatus;
   endmethod
   method Bit#(dataWidth) channel();
      return readyChannel;
   endmethod
endmodule

module mkPortalCtrlMemSlave#(Bit#(dataWidth) ifcId, PortalInterrupt#(dataWidth) intr)
   (PortalCtrlMemSlave#(addrWidth, dataWidth))
   provisos(Add#(d__, dataWidth, TMul#(dataWidth, 2)));
   Reg#(Bit#(dataWidth)) num_portals_reg <- mkReg(0);
   Reg#(Bool) interruptEnableReg <- mkReg(False);
   Reg#(Bit#(TMul#(dataWidth,2))) cycle_count <- mkReg(0);
   Reg#(Bit#(dataWidth))    snapshot <- mkReg(0);
   let verbose = False;

   rule count;
      cycle_count <= cycle_count+1;
   endrule

   interface PortalCtrl memSlave;
   method Action write(Bit#(addrWidth) addr, Bit#(dataWidth) v);
      if (addr == 4)
	 interruptEnableReg <= v[0] == 1'd1;
   endmethod

   method ActionValue#(Bit#(dataWidth)) read(Bit#(addrWidth) addr);
	       let v = 'h05a05a0;
	       if (addr == 0)
		  v = intr.status() ? 1 : 0;
	       if (addr == 4)
		  v = interruptEnableReg ? 1 : 0;
	       if (addr == 8)
		  v = fromInteger(valueOf(NumberOfTiles));
               if (addr == 'h00C) begin
		  if (intr.status())
		     v = intr.channel()+1;
		  else
		     v = 0;
               end
	       if (addr == 'h010)
		  v = ifcId;
	       if (addr == 'h014)
		  v = num_portals_reg;
	       if (addr == 'h018) begin
		  snapshot <= truncate(cycle_count);
		  v = truncate(cycle_count>>valueOf(dataWidth));
	       end
	       if (addr == 'h01C)
		  v = snapshot;
	       return v;
   endmethod
   endinterface
   interface ReadOnly interrupt;
      method Bool _read();
	 return intr.status() && interruptEnableReg;
      endmethod
   endinterface
   interface WriteOnly num_portals;
      method Action _write(Bit#(dataWidth) v);
	 num_portals_reg <= v;
      endmethod
   endinterface
endmodule

module mkMemMethodMuxIn#(PortalCtrl#(aw,dataWidth) ctrl, Vector#(numRequests, PipeIn#(Bit#(dataWidth))) requests
        )(PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    , Add#(a__, TLog#(numRequests), selWidth)
            , Add#(1, b__, dataWidth)
      );
   AddressGenerator#(aw,dataWidth) fifoReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(aw,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))                fifoWriteDoneFifo <- mkFIFO();
   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TSub#(addrWidth,1));
   function Bit#(selWidth) psel(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v - 1;
   endfunction
   function Bool pselCtrl(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v == 0;
   endfunction
   function Bit#(aw) asel(Bit#(addrWidth) a);
      return a[(port_sel_low-1):0];
   endfunction

   FIFO#(Bit#(MemTagSize)) doneFifo          <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_ars <- mkFIFO1();
   FIFO#(Bit#(TLog#(numRequests))) rs <- mkFIFO1();
   FIFO#(Bool)                   rsCtrl <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_aws <- mkFIFO1();
   FIFO#(Bit#(TLog#(numRequests))) ws <- mkFIFO1();
   FIFO#(Bool)                   wsCtrl <- mkFIFO1();

   rule write_done;
      let rv <- toGet(fifoWriteDoneFifo).get();
      ws.deq();
      wsCtrl.deq();
      doneFifo.enq(rv);
   endrule

   rule req_aw;
      let req <- toGet(req_aws).get;
      fifoWriteAddrGenerator.request.put(req);
   endrule

   rule req_ar;
      let req <- toGet(req_ars).get;
      fifoReadAddrGenerator.request.put(req);
   endrule

   FIFO#(MemData#(dataWidth)) writeDataFifo <- mkFIFO();
   rule writeDataRule;
      let wdata <- toGet(writeDataFifo).get();
      //$display("mkMemMethodMux.writeData aw=%d ws=%d data=%h", valueOf(aw), ws.first, wdata.data);
      let b <- fifoWriteAddrGenerator.addrBeat.get();
      //$display("mkPipeInMemSlave.writeData.put addr=%h data=%h", b.addr, wdata.data);
      if (b.last)
	 fifoWriteDoneFifo.enq(b.tag);
      if (wsCtrl.first)
	 ctrl.write(b.addr, wdata.data);
      else begin
	 requests[ws.first].enq(wdata.data);
	 // this used to be where we triggered putFailed
      end
   endrule

   FIFO#(MemData#(dataWidth)) rvFifo <- mkFIFO;
   rule rvrule;
	 let v = 0;
      let b <- fifoReadAddrGenerator.addrBeat.get();
      if (rsCtrl.first) begin
	 let vr <- ctrl.read(b.addr);
         v = vr;
      end
      else begin
	 if (b.addr == 4)
	    v = extend(pack(requests[rs.first].notFull()));
      end
      rvFifo.enq(MemData { data: v, tag: b.tag, last: b.last });
      //$display("mkMemMethodMux.readData aw=%d rs=%d data=%h", valueOf(aw), rs.first, rv.data);
      if (b.last) begin
	 rs.deq();
	 rsCtrl.deq();
      end
   endrule

   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_aws.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
				       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
				       });
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.writeReq len=%d \n\n ****", req.burstLen);
	    //$display("mkMemMethodMux.writeReq addr=%h selWidth=%d aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(selWidth), valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    ws.enq(truncate(psel(req.addr)));
            wsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(dataWidth) wdata);
	    writeDataFifo.enq(wdata);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get();
	    let rv <- toGet(doneFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_ars.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
				       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    //$display("mkMemMethodMux.readReq addr=%h aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.readReq len=%d \n\n ****", req.burstLen);
	    rs.enq(truncate(psel(req.addr)));
            rsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let rv <- toGet(rvFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
endmodule

module mkMemMethodMuxOut#(PortalCtrl#(aw,dataWidth) ctrl, Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indications)(PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    , Add#(a__, TLog#(numIndications), selWidth)
            , Add#(1, b__, dataWidth)
      );
   AddressGenerator#(aw,dataWidth) fifoReadAddrGenerator <- mkAddressGenerator();
   AddressGenerator#(aw,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))                fifoWriteDoneFifo <- mkFIFO();
   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TSub#(addrWidth,1));
   function Bit#(selWidth) psel(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v - 1;
   endfunction
   function Bool pselCtrl(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v == 0;
   endfunction
   function Bit#(aw) asel(Bit#(addrWidth) a);
      return a[(port_sel_low-1):0];
   endfunction

   FIFO#(Bit#(MemTagSize)) doneFifo          <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_ars <- mkFIFO1();
   FIFO#(Bit#(TLog#(numIndications))) rs <- mkFIFO1();
   FIFO#(Bool)                   rsCtrl <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_aws <- mkFIFO1();
   FIFO#(Bit#(TLog#(numIndications))) ws <- mkFIFO1();
   FIFO#(Bool)                   wsCtrl <- mkFIFO1();

   rule write_done;
      let rv <- toGet(fifoWriteDoneFifo).get();
      ws.deq();
      wsCtrl.deq();
      doneFifo.enq(rv);
   endrule

   rule req_aw;
      let req <- toGet(req_aws).get;
      fifoWriteAddrGenerator.request.put(req);
   endrule

   rule req_ar;
      let req <- toGet(req_ars).get;
      fifoReadAddrGenerator.request.put(req);
   endrule

   FIFO#(MemData#(dataWidth)) rvFifo <- mkFIFO;
   rule rvrule;
      let b <- fifoReadAddrGenerator.addrBeat.get();
      let rv = MemData { data: 0, tag: b.tag, last: b.last };
      if (rsCtrl.first) begin
	 let vr <- ctrl.read(b.addr);
         rv.data = vr;
      end
      else begin
	 if (b.addr == 0)
	    rv.data <- toGet(indications[rs.first]).get();
	 else if (b.addr == 4)
	    rv.data = extend(pack(indications[rs.first].notEmpty()));
      end
      rvFifo.enq(rv);
      //$display("mkMemMethodMux.readData aw=%d rs=%d data=%h", valueOf(aw), rs.first, rv.data);
      rs.deq();
      rsCtrl.deq();
   endrule

   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_aws.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
	       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.writeReq len=%d \n\n ****", req.burstLen);
	    //$display("mkMemMethodMux.writeReq addr=%h selWidth=%d aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(selWidth), valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    ws.enq(truncate(psel(req.addr)));
            wsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(dataWidth) wdata);
	    //$display("mkMemMethodMux.writeData aw=%d ws=%d data=%h", valueOf(aw), ws.first, wdata.data);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
            if (wsCtrl.first)
	       ctrl.write(b.addr, wdata.data);
	       //$display("mkPipeOutMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	       if (b.last)
	          fifoWriteDoneFifo.enq(b.tag);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get();
	    let rv <- toGet(doneFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_ars.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
	       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    //$display("mkMemMethodMux.readReq addr=%h aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.readReq len=%d \n\n ****", req.burstLen);
	    rs.enq(truncate(psel(req.addr)));
            rsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let rv <- toGet(rvFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
endmodule

module mkPhysMemSlaveMux#(Vector#(numSlaves,PhysMemSlave#(aw,dataWidth)) slaves) (PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    ,Add#(a__, TLog#(numSlaves), selWidth)
	    ,Min#(4,TLog#(numSlaves),bpc)
	    ,Pipe::FunnelPipesPipelined#(1, numSlaves, MemTypes::MemData#(dataWidth),bpc)
	    );
   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TSub#(addrWidth,1));
   function Bit#(selWidth) psel(Bit#(addrWidth) a);
      return a[port_sel_high:port_sel_low];
   endfunction
   function Bit#(aw) asel(Bit#(addrWidth) a);
      return a[(port_sel_low-1):0];
   endfunction
   function Get#(MemData#(dataWidth)) getMemPortalReadData(PhysMemSlave#(aw,dataWidth) x) = x.read_server.readData;
   function Put#(MemData#(dataWidth)) getMemPortalWriteData(PhysMemSlave#(aw,dataWidth) x) = x.write_server.writeData;

   Reg#(Bool) lastWriteDataSeen <- mkReg(False);
   FIFO#(Bit#(MemTagSize))        doneFifo <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_ars <- mkSizedFIFO(1);
   FIFO#(Bit#(TLog#(numSlaves))) rs <- mkFIFO1();
   Vector#(numSlaves, PipeOut#(MemData#(dataWidth))) readDataPipes <- mapM(mkPipeOut, map(getMemPortalReadData,slaves));
   FunnelPipe#(1, numSlaves, MemData#(dataWidth), bpc) read_data_funnel <- mkFunnelPipesPipelined(readDataPipes);

   FIFO#(PhysMemRequest#(aw,dataWidth)) req_aws <- mkFIFO1();
   FIFO#(Bit#(TLog#(numSlaves))) ws <- mkFIFO1();
   FIFOF#(Tuple2#(Bit#(TLog#(numSlaves)), MemData#(dataWidth))) write_data <- mkFIFOF;
   UnFunnelPipe#(1, numSlaves, MemData#(dataWidth), bpc) write_data_unfunnel <- mkUnFunnelPipesPipelined(cons(toPipeOut(write_data),nil));
   Vector#(numSlaves, PipeIn#(MemData#(dataWidth))) writeDataPipes <- mapM(mkPipeIn, map(getMemPortalWriteData,slaves));
   zipWithM_(mkConnection, write_data_unfunnel, writeDataPipes);
   let verbose = False;

   rule req_aw;
      let req <- toGet(req_aws).get;
      slaves[ws.first].write_server.writeReq.put(req);
   endrule

   rule req_ar;
      let req <- toGet(req_ars).get;
      slaves[rs.first].read_server.readReq.put(req);
   endrule

   rule write_done_rule;
      let rv <- slaves[ws.first].write_server.writeDone.get();
      ws.deq();
      doneFifo.enq(rv);
   endrule

   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_aws.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
	       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    if (req.burstLen > 4) $display("**** \n\n mkPhysMemSlaveMux.writeReq len=%d \n\n ****", req.burstLen);
	    ws.enq(truncate(psel(req.addr)));
	    lastWriteDataSeen <= False;
	    if(verbose) $display("mkPhysMemSlaveMux.writeReq addr=%h aw=%d psel=%h", req.addr, valueOf(aw), psel(req.addr));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(dataWidth) wdata) if (!lastWriteDataSeen);
	    write_data.enq(tuple2(ws.first,wdata));
	    if (wdata.last) begin
	       lastWriteDataSeen <= True;
	    end
	    if(verbose) $display("mkPhysMemSlaveMux.writeData dst=%h wdata=%h", ws.first,wdata);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get();
	    let rv <- toGet(doneFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_ars.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
				       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    rs.enq(truncate(psel(req.addr)));
	    if (req.burstLen > 4) $display("**** \n\n mkPhysMemSlaveMux.readReq len=%d \n\n ****", req.burstLen);
	    if(verbose) $display("mkPhysMemSlaveMux.readReq addr=%h aw=%d psel=%h", req.addr, valueOf(aw), psel(req.addr));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let rv <- toGet(read_data_funnel[0]).get;
	    rs.deq();
	    if(verbose) $display("mkPhysMemSlaveMux.readData rs=%d data=%h", rs.first, rv.data);
	    return rv;
	 endmethod
      endinterface
   endinterface
endmodule

module mkSlaveMux#(Vector#(numPortals,MemPortal#(aw,dataWidth)) portals) (PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    ,Add#(a__, TLog#(numPortals), selWidth)
	    ,FunnelPipesPipelined#(1, numPortals, MemData#(dataWidth),TMin#(4, TLog#(numPortals)))
	    );
   function PhysMemSlave#(_a,_d) getSlave(MemPortal#(_a,_d) p);
      return p.slave;
   endfunction
   Vector#(numPortals, PhysMemSlave#(aw,dataWidth)) slaves = map(getSlave, portals);
   for(Integer i = 0; i < valueOf(numPortals); i=i+1)
      rule writeTop;
	 portals[i].num_portals <= fromInteger(valueOf(numPortals));
      endrule
   let rv <- mkPhysMemSlaveMux(slaves);
   return rv;
endmodule

// Copyright (c) 2012 Nokia, Inc.
// 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.

import Vector::*;
import GetPut::*;
import FIFOF::*;
import SpecialFIFOs::*;
import FIFO::*;

`include "ConnectalProjectConfig.bsv"
import ConnectalConfig::*;
import HostInterface::*;
import Connectable::*;
import AddressGenerator::*;

import Portal::*;
import MemTypes::*;
import Arith::*;
import Pipe::*;

interface PortalCtrl#(numeric type addrWidth, numeric type dataWidth);
   method ActionValue#(Bit#(dataWidth)) read(Bit#(addrWidth) addr);
   method Action write(Bit#(addrWidth) addr, Bit#(dataWidth) v);
endinterface

interface PortalCtrlMemSlave#(numeric type addrWidth, numeric type dataWidth);
   interface PortalCtrl#(addrWidth, dataWidth)  memSlave;
   interface ReadOnly#(Bool)                    interrupt;
   interface WriteOnly#(Bit#(dataWidth))        num_portals;
endinterface

module mkInterruptMux#(Vector#(numPortals,ReadOnly#(Bool)) inputs) (ReadOnly#(Bool))
   provisos(Add#(nz, TLog#(numPortals), 4),
	    Add#(1, a__, numPortals));
   function Bool my_read(ReadOnly#(Bool) x);
      return x._read;
   endfunction
   method Bool _read;
      return fold(boolor,map(my_read,inputs));
   endmethod
endmodule

module mkPortalInterrupt#(Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indicationPipes)
   (PortalInterrupt#(dataWidth));
   Bool      interruptStatus = False;
   function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
   Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, indicationPipes);

   Bit#(dataWidth)  readyChannel = -1;
   for (Integer i = valueOf(numIndications) - 1; i >= 0; i = i - 1) begin
      if (readyBits[i]) begin
         interruptStatus = True;
         readyChannel = fromInteger(i);
      end
   end
   method Bool status();
      return interruptStatus;
   endmethod
   method Bit#(dataWidth) channel();
      return readyChannel;
   endmethod
endmodule

module mkPortalCtrlMemSlave#(Bit#(dataWidth) ifcId, PortalInterrupt#(dataWidth) intr)
   (PortalCtrlMemSlave#(addrWidth, dataWidth))
   provisos(Add#(d__, dataWidth, TMul#(dataWidth, 2)));
   Reg#(Bit#(dataWidth)) num_portals_reg <- mkReg(0);
   Reg#(Bool) interruptEnableReg <- mkReg(False);
   Reg#(Bit#(TMul#(dataWidth,2))) cycle_count <- mkReg(0);
   Reg#(Bit#(dataWidth))    snapshot <- mkReg(0);
   let verbose = False;

   rule count;
      cycle_count <= cycle_count+1;
   endrule

   interface PortalCtrl memSlave;
   method Action write(Bit#(addrWidth) addr, Bit#(dataWidth) v);
      if (addr == 4)
	 interruptEnableReg <= v[0] == 1'd1;
   endmethod

   method ActionValue#(Bit#(dataWidth)) read(Bit#(addrWidth) addr);
	       let v = 'h05a05a0;
	       if (addr == 0)
		  v = intr.status() ? 1 : 0;
	       if (addr == 4)
		  v = interruptEnableReg ? 1 : 0;
	       if (addr == 8)
		  v = fromInteger(valueOf(NumberOfTiles));
               if (addr == 'h00C) begin
		  if (intr.status())
		     v = intr.channel()+1;
		  else
		     v = 0;
               end
	       if (addr == 'h010)
		  v = ifcId;
	       if (addr == 'h014)
		  v = num_portals_reg;
	       if (addr == 'h018) begin
		  snapshot <= truncate(cycle_count);
		  v = truncate(cycle_count>>valueOf(dataWidth));
	       end
	       if (addr == 'h01C)
		  v = snapshot;
	       return v;
   endmethod
   endinterface
   interface ReadOnly interrupt;
      method Bool _read();
	 return intr.status() && interruptEnableReg;
      endmethod
   endinterface
   interface WriteOnly num_portals;
      method Action _write(Bit#(dataWidth) v);
	 num_portals_reg <= v;
      endmethod
   endinterface
endmodule

module mkMemMethodMuxIn#(PortalCtrl#(aw,dataWidth) ctrl, Vector#(numRequests, PipeIn#(Bit#(dataWidth))) requests
        )(PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    , Add#(a__, TLog#(numRequests), selWidth)
            , Add#(1, b__, dataWidth)
      );
   AddressGenerator#(aw,dataWidth) fifoReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(aw,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))                fifoWriteDoneFifo <- mkFIFO();
   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TSub#(addrWidth,1));
   function Bit#(selWidth) psel(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v - 1;
   endfunction
   function Bool pselCtrl(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v == 0;
   endfunction
   function Bit#(aw) asel(Bit#(addrWidth) a);
      return a[(port_sel_low-1):0];
   endfunction

   FIFO#(Bit#(MemTagSize)) doneFifo          <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_ars <- mkFIFO1();
   FIFO#(Bit#(TLog#(numRequests))) rs <- mkFIFO1();
   FIFO#(Bool)                   rsCtrl <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_aws <- mkFIFO1();
   FIFO#(Bit#(TLog#(numRequests))) ws <- mkFIFO1();
   FIFO#(Bool)                   wsCtrl <- mkFIFO1();

   rule write_done;
      let rv <- toGet(fifoWriteDoneFifo).get();
      ws.deq();
      wsCtrl.deq();
      doneFifo.enq(rv);
   endrule

   rule req_aw;
      let req <- toGet(req_aws).get;
      fifoWriteAddrGenerator.request.put(req);
   endrule

   rule req_ar;
      let req <- toGet(req_ars).get;
      fifoReadAddrGenerator.request.put(req);
   endrule

   FIFO#(MemData#(dataWidth)) writeDataFifo <- mkFIFO();
   rule writeDataRule;
      let wdata <- toGet(writeDataFifo).get();
      //$display("mkMemMethodMux.writeData aw=%d ws=%d data=%h", valueOf(aw), ws.first, wdata.data);
      let b <- fifoWriteAddrGenerator.addrBeat.get();
      //$display("mkPipeInMemSlave.writeData.put addr=%h data=%h", b.addr, wdata.data);
      if (b.last)
	 fifoWriteDoneFifo.enq(b.tag);
      if (wsCtrl.first)
	 ctrl.write(b.addr, wdata.data);
      else begin
	 requests[ws.first].enq(wdata.data);
	 // this used to be where we triggered putFailed
      end
   endrule

   FIFO#(MemData#(dataWidth)) rvFifo <- mkFIFO;
   rule rvrule;
	 let v = 0;
      let b <- fifoReadAddrGenerator.addrBeat.get();
      if (rsCtrl.first) begin
	 let vr <- ctrl.read(b.addr);
         v = vr;
      end
      else begin
	 if (b.addr == 4)
	    v = extend(pack(requests[rs.first].notFull()));
      end
      rvFifo.enq(MemData { data: v, tag: b.tag, last: b.last });
      //$display("mkMemMethodMux.readData aw=%d rs=%d data=%h", valueOf(aw), rs.first, rv.data);
      if (b.last) begin
	 rs.deq();
	 rsCtrl.deq();
      end
   endrule

   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_aws.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
				       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
				       });
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.writeReq len=%d \n\n ****", req.burstLen);
	    //$display("mkMemMethodMux.writeReq addr=%h selWidth=%d aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(selWidth), valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    ws.enq(truncate(psel(req.addr)));
            wsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(dataWidth) wdata);
	    writeDataFifo.enq(wdata);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get();
	    let rv <- toGet(doneFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_ars.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
				       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    //$display("mkMemMethodMux.readReq addr=%h aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.readReq len=%d \n\n ****", req.burstLen);
	    rs.enq(truncate(psel(req.addr)));
            rsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let rv <- toGet(rvFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
endmodule

module mkMemMethodMuxOut#(PortalCtrl#(aw,dataWidth) ctrl, Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indications)(PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    , Add#(a__, TLog#(numIndications), selWidth)
            , Add#(1, b__, dataWidth)
      );
   AddressGenerator#(aw,dataWidth) fifoReadAddrGenerator <- mkAddressGenerator();
   AddressGenerator#(aw,dataWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(MemTagSize))                fifoWriteDoneFifo <- mkFIFO();
   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TSub#(addrWidth,1));
   function Bit#(selWidth) psel(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v - 1;
   endfunction
   function Bool pselCtrl(Bit#(addrWidth) a);
      Bit#(selWidth) v = a[port_sel_high:port_sel_low];
      return v == 0;
   endfunction
   function Bit#(aw) asel(Bit#(addrWidth) a);
      return a[(port_sel_low-1):0];
   endfunction

   FIFO#(Bit#(MemTagSize)) doneFifo          <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_ars <- mkFIFO1();
   FIFO#(Bit#(TLog#(numIndications))) rs <- mkFIFO1();
   FIFO#(Bool)                   rsCtrl <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_aws <- mkFIFO1();
   FIFO#(Bit#(TLog#(numIndications))) ws <- mkFIFO1();
   FIFO#(Bool)                   wsCtrl <- mkFIFO1();

   rule write_done;
      let rv <- toGet(fifoWriteDoneFifo).get();
      ws.deq();
      wsCtrl.deq();
      doneFifo.enq(rv);
   endrule

   rule req_aw;
      let req <- toGet(req_aws).get;
      fifoWriteAddrGenerator.request.put(req);
   endrule

   rule req_ar;
      let req <- toGet(req_ars).get;
      fifoReadAddrGenerator.request.put(req);
   endrule

   FIFO#(MemData#(dataWidth)) rvFifo <- mkFIFO;
   rule rvrule;
      let b <- fifoReadAddrGenerator.addrBeat.get();
      let rv = MemData { data: 0, tag: b.tag, last: b.last };
      if (rsCtrl.first) begin
	 let vr <- ctrl.read(b.addr);
         rv.data = vr;
      end
      else begin
	 if (b.addr == 0)
	    rv.data <- toGet(indications[rs.first]).get();
	 else if (b.addr == 4)
	    rv.data = extend(pack(indications[rs.first].notEmpty()));
      end
      rvFifo.enq(rv);
      //$display("mkMemMethodMux.readData aw=%d rs=%d data=%h", valueOf(aw), rs.first, rv.data);
      rs.deq();
      rsCtrl.deq();
   endrule

   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_aws.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
	       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.writeReq len=%d \n\n ****", req.burstLen);
	    //$display("mkMemMethodMux.writeReq addr=%h selWidth=%d aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(selWidth), valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    ws.enq(truncate(psel(req.addr)));
            wsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(dataWidth) wdata);
	    //$display("mkMemMethodMux.writeData aw=%d ws=%d data=%h", valueOf(aw), ws.first, wdata.data);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
            if (wsCtrl.first)
	       ctrl.write(b.addr, wdata.data);
	       //$display("mkPipeOutMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	       if (b.last)
	          fifoWriteDoneFifo.enq(b.tag);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get();
	    let rv <- toGet(doneFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_ars.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
	       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    //$display("mkMemMethodMux.readReq addr=%h aw=%d psel=%h pselCtrl=%x", req.addr, valueOf(aw), psel(req.addr), pselCtrl(req.addr));
	    if (req.burstLen > 4) $display("**** \n\n mkMemMethodMux.readReq len=%d \n\n ****", req.burstLen);
	    rs.enq(truncate(psel(req.addr)));
            rsCtrl.enq(pselCtrl(req.addr));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let rv <- toGet(rvFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
endmodule

module mkPhysMemSlaveMux#(Vector#(numSlaves,PhysMemSlave#(aw,dataWidth)) slaves) (PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    ,Add#(a__, TLog#(numSlaves), selWidth)
	    ,Min#(4,TLog#(numSlaves),bpc)
	    ,Pipe::FunnelPipesPipelined#(1, numSlaves, MemTypes::MemData#(dataWidth),bpc)
	    );
   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TSub#(addrWidth,1));
   function Bit#(selWidth) psel(Bit#(addrWidth) a);
      return a[port_sel_high:port_sel_low];
   endfunction
   function Bit#(aw) asel(Bit#(addrWidth) a);
      return a[(port_sel_low-1):0];
   endfunction
   function Get#(MemData#(dataWidth)) getMemPortalReadData(PhysMemSlave#(aw,dataWidth) x) = x.read_server.readData;
   function Put#(MemData#(dataWidth)) getMemPortalWriteData(PhysMemSlave#(aw,dataWidth) x) = x.write_server.writeData;

   FIFO#(Bit#(MemTagSize))        doneFifo <- mkFIFO1();
   FIFO#(PhysMemRequest#(aw,dataWidth)) req_ars <- mkSizedFIFO(1);
   FIFO#(Bit#(TLog#(numSlaves))) rs <- mkFIFO1();
   Vector#(numSlaves, PipeOut#(MemData#(dataWidth))) readDataPipes <- mapM(mkPipeOut, map(getMemPortalReadData,slaves));
   FunnelPipe#(1, numSlaves, MemData#(dataWidth), bpc) read_data_funnel <- mkFunnelPipesPipelined(readDataPipes);

   FIFO#(PhysMemRequest#(aw,dataWidth)) req_aws <- mkFIFO1();
   FIFO#(Bit#(TLog#(numSlaves))) ws <- mkFIFO1();
   FIFOF#(Tuple2#(Bit#(TLog#(numSlaves)), MemData#(dataWidth))) write_data <- mkFIFOF;
   UnFunnelPipe#(1, numSlaves, MemData#(dataWidth), bpc) write_data_unfunnel <- mkUnFunnelPipesPipelined(cons(toPipeOut(write_data),nil));
   Vector#(numSlaves, PipeIn#(MemData#(dataWidth))) writeDataPipes <- mapM(mkPipeIn, map(getMemPortalWriteData,slaves));
   zipWithM_(mkConnection, write_data_unfunnel, writeDataPipes);
   let verbose = False;

   rule req_aw;
      let req <- toGet(req_aws).get;
      slaves[ws.first].write_server.writeReq.put(req);
   endrule

   rule req_ar;
      let req <- toGet(req_ars).get;
      slaves[rs.first].read_server.readReq.put(req);
   endrule

   rule write_done_rule;
      let rv <- slaves[ws.first].write_server.writeDone.get();
      ws.deq();
      doneFifo.enq(rv);
   endrule

   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_aws.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
	       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    if (req.burstLen > 4) $display("**** \n\n mkPhysMemSlaveMux.writeReq len=%d \n\n ****", req.burstLen);
	    ws.enq(truncate(psel(req.addr)));
	    if(verbose) $display("mkPhysMemSlaveMux.writeReq addr=%h aw=%d psel=%h", req.addr, valueOf(aw), psel(req.addr));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(dataWidth) wdata);
	    write_data.enq(tuple2(ws.first,wdata));
	    if(verbose) $display("mkPhysMemSlaveMux.writeData dst=%h wdata=%h", ws.first,wdata);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get();
	    let rv <- toGet(doneFifo).get();
	    return rv;
	 endmethod
      endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(addrWidth,dataWidth) req);
	    req_ars.enq(PhysMemRequest{addr:asel(req.addr), burstLen:req.burstLen, tag:req.tag
`ifdef BYTE_ENABLES
				       , firstbe: req.firstbe, lastbe: req.lastbe
`endif
	       });
	    rs.enq(truncate(psel(req.addr)));
	    if (req.burstLen > 4) $display("**** \n\n mkPhysMemSlaveMux.readReq len=%d \n\n ****", req.burstLen);
	    if(verbose) $display("mkPhysMemSlaveMux.readReq addr=%h aw=%d psel=%h", req.addr, valueOf(aw), psel(req.addr));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let rv <- toGet(read_data_funnel[0]).get;
	    rs.deq();
	    if(verbose) $display("mkPhysMemSlaveMux.readData rs=%d data=%h", rs.first, rv.data);
	    return rv;
	 endmethod
      endinterface
   endinterface
endmodule

module mkSlaveMux#(Vector#(numPortals,MemPortal#(aw,dataWidth)) portals) (PhysMemSlave#(addrWidth,dataWidth))
   provisos(Add#(selWidth,aw,addrWidth)
	    ,Add#(a__, TLog#(numPortals), selWidth)
	    ,FunnelPipesPipelined#(1, numPortals, MemData#(dataWidth),TMin#(4, TLog#(numPortals)))
	    );
   function PhysMemSlave#(_a,_d) getSlave(MemPortal#(_a,_d) p);
      return p.slave;
   endfunction
   Vector#(numPortals, PhysMemSlave#(aw,dataWidth)) slaves = map(getSlave, portals);
   for(Integer i = 0; i < valueOf(numPortals); i=i+1)
      rule writeTop;
	 portals[i].num_portals <= fromInteger(valueOf(numPortals));
      endrule
   let rv <- mkPhysMemSlaveMux(slaves);
   return rv;
endmodule


adding missing rule to consume resp_b in BsimTop

// 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 Vector            :: *;
import FIFO              :: *;
import SpecialFIFOs      :: *;
import GetPut            :: *;
import Connectable       :: *;
import StmtFSM           :: *;
import Portal            :: *;
import AxiClientServer   :: *;
import Leds              :: *;
import Top               :: *;

import "BDPI" function Action      initPortal(Bit#(32) d);
import "BDPI" function Bool                    writeReq();
import "BDPI" function ActionValue#(Bit#(32)) writeAddr();
import "BDPI" function ActionValue#(Bit#(32)) writeData();
import "BDPI" function Bool                     readReq();
import "BDPI" function ActionValue#(Bit#(32))  readAddr();
import "BDPI" function Action        readData(Bit#(32) d);

module mkBsimTop();
   let top <- mkPortalTop();
   let wf <- mkPipelineFIFO;
   let init_seq = (action
		      initPortal(0);
		      initPortal(1);
		      initPortal(2);
		      initPortal(3);
		      initPortal(4);
		      initPortal(5);
		      initPortal(6);
		      initPortal(7);
                   endaction);
   let init_fsm <- mkOnce(init_seq);

   (* descending_urgency = "rdResp, rdReq" *)
   rule init_rule;
      init_fsm.start;
   endrule
   rule wrReq (writeReq());
      let wa <- writeAddr;
      let wd <- writeData;
      top.ctrl.req_aw.put(Axi3WriteRequest { address: wa, len: 0, size: axiBusSize(32), id: 0, prot: 0, burst: 1, cache: 'b11, qos: 0, lock: 0 });
      wf.enq(wd);
   endrule
   rule wrData;
      wf.deq;
      top.ctrl.resp_write.put(Axi3WriteData { data: wf.first, id: 0, last: 1 });
   endrule
   rule wrB;
      let resp <- top.ctrl.resp_b.get();
   endrule
   rule rdReq (readReq());
      let ra <- readAddr;
	 top.ctrl.req_ar.put(Axi3ReadRequest { address: ra, len: 0, size: axiBusSize(32), id: 0, prot: 0, burst: 1, cache: 'b11, qos: 0, lock: 0 });
   endrule
   rule rdResp;
      let rd <- top.ctrl.resp_read.get();
      readData(rd.data);
   endrule
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 Vector            :: *;
import FIFO              :: *;
import SpecialFIFOs      :: *;
import GetPut            :: *;
import Connectable       :: *;
import StmtFSM           :: *;
import Portal            :: *;
import AxiClientServer   :: *;
import Leds              :: *;
import Top               :: *;

import "BDPI" function Action      initPortal(Bit#(32) d);
import "BDPI" function Bool                    writeReq();
import "BDPI" function ActionValue#(Bit#(32)) writeAddr();
import "BDPI" function ActionValue#(Bit#(32)) writeData();
import "BDPI" function Bool                     readReq();
import "BDPI" function ActionValue#(Bit#(32))  readAddr();
import "BDPI" function Action        readData(Bit#(32) d);

module mkBsimTop();
   let top <- mkPortalTop();
   let wf <- mkPipelineFIFO;
   let init_seq = (action
		      initPortal(0);
		      initPortal(1);
		      initPortal(2);
		      initPortal(3);
		      initPortal(4);
		      initPortal(5);
		      initPortal(6);
		      initPortal(7);
                   endaction);
   let init_fsm <- mkOnce(init_seq);

   (* descending_urgency = "rdResp, rdReq" *)
   rule init_rule;
      init_fsm.start;
   endrule
   rule wrReq (writeReq());
      let wa <- writeAddr;
      let wd <- writeData;
      top.ctrl.req_aw.put(Axi3WriteRequest { address: wa, len: 0, size: axiBusSize(32), id: 0, prot: 0, burst: 1, cache: 'b11, qos: 0, lock: 0 });
      wf.enq(wd);
   endrule
   rule wrData;
      wf.deq;
      top.ctrl.resp_write.put(Axi3WriteData { data: wf.first, id: 0, last: 1 });
   endrule
   rule rdReq (readReq());
      let ra <- readAddr;
	 top.ctrl.req_ar.put(Axi3ReadRequest { address: ra, len: 0, size: axiBusSize(32), id: 0, prot: 0, burst: 1, cache: 'b11, qos: 0, lock: 0 });
   endrule
   rule rdResp;
      let rd <- top.ctrl.resp_read.get();
      readData(rd.data);
   endrule
endmodule


removed obsolete Connectable instances

removed obsolete Connectable instances

// Copyright (c) 2015 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 GetPut            :: *;
import Connectable       :: *;
import FIFOF             :: *;
import Pipe              :: *;
import Portal            :: *;
import MsgFormat         :: *;
import CnocPortal        :: *;

interface SimLink#(numeric type dataWidth);
   method Action start(Bool listening);
   interface PipeOut#(Bit#(dataWidth)) rx;
   interface PipeIn#(Bit#(dataWidth)) tx;
endinterface

import "BDPI" function Action                 bsimLinkOpen(String name, Bool listening);
import "BDPI" function Bool                   bsimLinkCanReceive(String name, Bool listening);
import "BDPI" function Bool                   bsimLinkCanTransmit(String name, Bool listening);
import "BDPI" function ActionValue#(Bit#(32)) bsimLinkReceive32(String name, Bool listening);
import "BDPI" function Action                 bsimLinkTransmit32(String name, Bool listening, Bit#(32) value);
import "BDPI" function ActionValue#(Bit#(64)) bsimLinkReceive64(String name, Bool listening);
import "BDPI" function Action                 bsimLinkTransmit64(String name, Bool listening, Bit#(64) value);

typeclass SelectLinkWidth#(numeric type dsz);
   function ActionValue#(Bit#(dsz)) bsimLinkReceive(String name, Bool listening);
   function Action bsimLinkTransmit(String name, Bool listening, Bit#(dsz) value);
endtypeclass

instance SelectLinkWidth#(32);
   function ActionValue#(Bit#(32)) bsimLinkReceive(String name, Bool listening);
   actionvalue
      let v <- bsimLinkReceive32(name, listening);
      return v;
   endactionvalue
   endfunction
   function Action bsimLinkTransmit(String name, Bool listening, Bit#(32) value);
   action
      bsimLinkTransmit32(name, listening, value);
   endaction
   endfunction
endinstance
instance SelectLinkWidth#(64);
   function ActionValue#(Bit#(64)) bsimLinkReceive(String name, Bool listening);
   actionvalue
      let v <- bsimLinkReceive64(name, listening);
      return v;
   endactionvalue
   endfunction
   function Action bsimLinkTransmit(String name, Bool listening, Bit#(64) value);
   action
      bsimLinkTransmit64(name, listening, value);
   endaction
   endfunction
endinstance

module mkSimLink#(String name)(SimLink#(dataWidth)) provisos (SelectLinkWidth#(dataWidth));
   FIFOF#(Bit#(dataWidth)) rxFifo <- mkFIFOF();
   FIFOF#(Bit#(dataWidth)) txFifo <- mkFIFOF();
   Reg#(Bool) opened    <- mkReg(False);
   Reg#(Bool) listening <- mkReg(False);
   Reg#(Bool) started   <- mkReg(False);

   rule open if (!opened && started);
      bsimLinkOpen(name, listening);
      opened <= True;
   endrule

   rule receive if (bsimLinkCanReceive(name, listening));
      let v <- bsimLinkReceive(name, listening);
      rxFifo.enq(v);
   endrule

   rule transmit if (bsimLinkCanTransmit(name, listening));
      let v <- toGet(txFifo).get();
      bsimLinkTransmit(name, listening, v);
   endrule

   interface rx = toPipeOut(rxFifo);
   interface tx = toPipeIn(txFifo);
   method Action start(Bool l);
      started <= True;
      listening <= l;
   endmethod
endmodule

instance Connectable#(PipePortal#(0,numIndications,32),SimLink#(32));
   module mkConnection#(PipePortal#(0,numIndications,32) pp, SimLink#(32) link)(Empty);
      MsgSource#(4) msgSource <- mkPortalMsgSource(pp);
      (* fire_when_enabled *)
      rule tx;
	 msgSource.dst_rdy(link.tx.notFull());
	 if (link.tx.notFull() && msgSource.src_rdy()) begin
	    let v = msgSource.beat();
	    link.tx.enq(v);
	 end
      endrule
   endmodule
endinstance

instance Connectable#(SimLink#(32),PipePortal#(numRequests,0,32));
   module mkConnection#(SimLink#(32) link,PipePortal#(numRequests,0,32) pp)(Empty);
      MsgSink#(4) msgSink <- mkPortalMsgSink(pp);
      (* fire_when_enabled *)
      rule rx;
	 msgSink.src_rdy(link.rx.notEmpty());
	 if (link.rx.notEmpty() && msgSink.dst_rdy()) begin
	    let v <- toGet(link.rx).get();
	    msgSink.beat(v);
	 end
      endrule

   endmodule
endinstance


only call $dumpfile once

only call $dumpfile once

// Copyright (c) 2015 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 Portal            :: *;
import Top               :: *;
import HostInterface     :: *;
import BlueNoC::*;
import BnocPortal::*;

`ifndef PinType
`define PinType Empty
`endif

typedef `PinType PinType;
typedef `NumberOfMasters NumberOfMasters;

module  mkXsimHost#(Clock derivedClock, Reset derivedReset)(XsimHost);
   interface derivedClock = derivedClock;
   interface derivedReset = derivedReset;
endmodule

interface XsimTop;
   interface MsgSource#(4) msgSource;
   interface MsgSink#(4) msgSink;
   interface Clock singleClock;
   interface Reset singleReset;
endinterface

module mkXsimTop(XsimTop);
   Clock derivedClock <- exposeCurrentClock;
   Reset derivedReset <- exposeCurrentReset;
   Clock single_clock = derivedClock;
   Reset single_reset = derivedReset;

   Reg#(Bool) dumpstarted <- mkReg(False);
   rule startdump if (!dumpstarted);
      $dumpfile("dump.vcd");
      $dumpvars;
      dumpstarted <= True;
   endrule
   XsimHost host <- mkXsimHost(derivedClock, derivedReset);
   BluenocTop#(1,1) top <- mkBluenocTop(
`ifdef IMPORT_HOSTIF
       host
`endif
       );

   interface msgSink = top.requests[0];
   interface msgSource = top.indications[0];
   interface Clock singleClock = single_clock;
   interface Reset singleReset = single_reset;
endmodule

// Copyright (c) 2015 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 Portal            :: *;
import Top               :: *;
import HostInterface     :: *;
import BlueNoC::*;
import BnocPortal::*;

`ifndef PinType
`define PinType Empty
`endif

typedef `PinType PinType;
typedef `NumberOfMasters NumberOfMasters;

module  mkXsimHost#(Clock derivedClock, Reset derivedReset)(XsimHost);
   interface derivedClock = derivedClock;
   interface derivedReset = derivedReset;
endmodule

interface XsimTop;
   interface MsgSource#(4) msgSource;
   interface MsgSink#(4) msgSink;
   interface Clock singleClock;
   interface Reset singleReset;
endinterface

module mkXsimTop(XsimTop);
   Clock derivedClock <- exposeCurrentClock;
   Reset derivedReset <- exposeCurrentReset;
   Clock single_clock = derivedClock;
   Reset single_reset = derivedReset;

   Reg#(Bool) dumpstarted <- mkReg(False);
   rule startdump if (!dumpstarted);
      $dumpfile("dump.vcd");
      $dumpvars;
   endrule
   XsimHost host <- mkXsimHost(derivedClock, derivedReset);
   BluenocTop#(1,1) top <- mkBluenocTop(
`ifdef IMPORT_HOSTIF
       host
`endif
       );

   interface msgSink = top.requests[0];
   interface msgSource = top.indications[0];
   interface Clock singleClock = single_clock;
   interface Reset singleReset = single_reset;
endmodule


pipeline the region() method

pipeline the region() method

Change-Id: Ifdae47396ac3225096b4fda574ffd91ce4630819

// 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,FIFOF#(Bit#(3)))      pageSizes <- 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

   FIFO#(Tuple4#(RegionsIdx,Region,Region,Region)) regionFifo <- mkFIFO();
   FIFO#(Region) region8Fifo <- mkFIFO();
   FIFO#(Region) region4Fifo <- mkFIFO();
   FIFO#(Region) region0Fifo <- mkFIFO();
   // pipeline the address lookup
   for(Integer i = 0; i < 2; i=i+1) begin
      rule req0;
	 Region region8 <- portsel(reg8,i).response.get;
	 Region region4 <- portsel(reg4,i).response.get;
	 Region region0 <- portsel(reg0,i).response.get;

	 region8Fifo.enq(region8);
	 region4Fifo.enq(region4);
	 region0Fifo.enq(region0);
      endrule
      rule req1;
	 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 <- toGet(region8Fifo).get();
	 Region region4 <- toGet(region4Fifo).get();
	 Region region0 <- toGet(region0Fifo).get();
	 Bit#(40) barrier8 = region8.barrier;
	 Bit#(40) barrier4 = region4.barrier;
	 Bit#(40) barrier0 = region0.barrier;

	 Bit#(3) pageSize = 0;
	 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);
	    pageSize = 3;
	    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);
	    pageSize = 2;
	    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);
	    pageSize = 1;
	    idxOffset = region0.idxOffset;
	 end
	 else begin
	    pageSize = 0;
	    //dmaIndication.badAddrTrans(extend(ptr), extend(off), barrier0);
	 end
	 offs[i].enq(o);
	 pbases[i].enq(pbase);
	 idxOffsets[i].enq(idxOffset);
	 ptrs[i].enq(ptr);
	 pageSizes[i].enq(pageSize);
      endrule
      rule req2;
	 let ptr <- toGet(ptrs[i]).get();
	 let pbase <- toGet(pbases[i]).get();
	 let idxOffset <- toGet(idxOffsets[i]).get();
	 let pageSize <- toGet(pageSizes[i]).get();
	 Bit#(8) p = pbase + idxOffset;
	 if (pageSize == 3) begin
	    //$display("request: ptr=%h off=%h barrier8=%h", ptr, off, barrier8);
	 end
	 else if (pageSize == 2) begin
	 end
	 else if (pageSize == 1) begin
	 end
	 else if (pageSize == 0) begin
	    //FIXME offset
	    //$display("mkSGListMMU.addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaIndication.badAddrTrans(extend(ptr), -1, 0);
	 end
	 let address = {ptr-1,p};
	 //$display("pages[%d].read %h", i, rp[i].first());
	 portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:address, 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#(SGListId,Bit#(40))) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      match { .ptr, .barr0 } <- toGet(configRespFifo).get();
      dmaIndication.configResp(extend(ptr), barr0);
   endrule

   FIFO#(Tuple3#(SGListId,Bit#(40),Bit#(32))) sglistFifo <- mkFIFO();
   rule sglistRule;
      match { .ptr, .paddr, .len } <- toGet(sglistFifo).get();

      // $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(extend(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(extend(ptr), len);
	    end
	 end
	 configRespFifo.enq(tuple2(truncate(ptr), 40'haaaaaaaa));
	 portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:{truncate(ptr-1),idxReg}, datain:page});
      end
   endrule

   rule regionRule;
      match { .ptr, .region8, .region4, .region0 } <- toGet(regionFifo).get();
      let idx = ptr-1;
      portsel(reg8, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address: idx, datain: region8});
      portsel(reg4, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address: idx, datain: region4});
      portsel(reg0, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address: idx, datain: region0});
      //$display("region ptr=%d off8=%h off4=%h off0=%h", ptr, off8, off4, off0);
      configRespFifo.enq(tuple2(ptr, region0.barrier));
   endrule

   // FIXME: split this into three methods?
   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 };
      regionFifo.enq(tuple4(truncate(ptr),region8,region4,region0));
   endmethod

   method Action sglist(Bit#(32) ptr, Bit#(40) paddr, Bit#(32) len);
      sglistFifo.enq(tuple3(truncate(ptr), paddr, len));
   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,FIFOF#(Bit#(3)))      pageSizes <- 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

   FIFO#(Region) region8Fifo <- mkFIFO();
   FIFO#(Region) region4Fifo <- mkFIFO();
   FIFO#(Region) region0Fifo <- mkFIFO();
   // pipeline the address lookup
   for(Integer i = 0; i < 2; i=i+1) begin
      rule req0;
	 Region region8 <- portsel(reg8,i).response.get;
	 Region region4 <- portsel(reg4,i).response.get;
	 Region region0 <- portsel(reg0,i).response.get;

	 region8Fifo.enq(region8);
	 region4Fifo.enq(region4);
	 region0Fifo.enq(region0);
      endrule
      rule req1;
	 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 <- toGet(region8Fifo).get();
	 Region region4 <- toGet(region4Fifo).get();
	 Region region0 <- toGet(region0Fifo).get();
	 Bit#(40) barrier8 = region8.barrier;
	 Bit#(40) barrier4 = region4.barrier;
	 Bit#(40) barrier0 = region0.barrier;

	 Bit#(3) pageSize = 0;
	 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);
	    pageSize = 3;
	    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);
	    pageSize = 2;
	    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);
	    pageSize = 1;
	    idxOffset = region0.idxOffset;
	 end
	 else begin
	    pageSize = 0;
	    //dmaIndication.badAddrTrans(extend(ptr), extend(off), barrier0);
	 end
	 offs[i].enq(o);
	 pbases[i].enq(pbase);
	 idxOffsets[i].enq(idxOffset);
	 ptrs[i].enq(ptr);
	 pageSizes[i].enq(pageSize);
      endrule
      rule req2;
	 let ptr <- toGet(ptrs[i]).get();
	 let pbase <- toGet(pbases[i]).get();
	 let idxOffset <- toGet(idxOffsets[i]).get();
	 let pageSize <- toGet(pageSizes[i]).get();
	 Bit#(8) p = pbase + idxOffset;
	 if (pageSize == 3) begin
	    //$display("request: ptr=%h off=%h barrier8=%h", ptr, off, barrier8);
	 end
	 else if (pageSize == 2) begin
	 end
	 else if (pageSize == 1) begin
	 end
	 else if (pageSize == 0) begin
	    //FIXME offset
	    //$display("mkSGListMMU.addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaIndication.badAddrTrans(extend(ptr), -1, 0);
	 end
	 let address = {ptr-1,p};
	 //$display("pages[%d].read %h", i, rp[i].first());
	 portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False, address:address, 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#(Tuple3#(Bit#(32),Bit#(40),Bit#(32))) sglistFifo <- mkFIFO();
   rule sglistRule;
      match { .ptr, .paddr, .len } <- toGet(sglistFifo).get();

      // $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
	 configRespFifo.enq(tuple2(ptr, 40'haaaaaaaa));
	 portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False, address:{truncate(ptr-1),idxReg}, datain:page});
      end
   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);
      sglistFifo.enq(tuple3(ptr, paddr, len));
   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