synthesize mkPcieHost separately

synthesize mkPcieHost separately

Change-Id: I27510140cf2a58977306124d2d1627e3c570caf3

// 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 Clocks            :: *;
import GetPut            :: *;
import FIFO              :: *;
import Connectable       :: *;
import ClientServer      :: *;
import Xilinx            :: *;
import DefaultValue      :: *;
import PcieSplitter      :: *;
import PcieTracer        :: *;
import PcieGearbox       :: *;
import XbsvXilinx7Pcie   :: *;
import PCIEWRAPPER       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import MemSlaveEngine    :: *;
import MemMasterEngine   :: *;
import PcieCsr           :: *;
import MemSlave          :: *;
import MemTypes          :: *;

`ifdef Artix7
typedef 4 PcieLanes;
typedef 4 NumLeds;
`else
typedef 8 PcieLanes;
typedef 8 NumLeds;
`endif
`ifndef DataBusWidth
`define DataBusWidth 64
`endif
`ifndef NumberOfMasters
`define NumberOfMasters 1
`endif
`ifndef PinType
`define PinType Empty
`endif
`ifdef USES_FCLK1
`define CLOCK_ARG  defaultClock
`else
`define CLOCK_ARG
`endif

typedef `DataBusWidth DataBusWidth;
typedef `NumberOfMasters NumberOfMasters;
typedef `PinType PinType;

interface PcieTop#(type ipins);
   (* prefix="PCIE" *)
   interface PciewrapPci_exp#(PcieLanes) pcie;
   (* always_ready *)
   method Bit#(NumLeds) leds();
   (* prefix="" *)
   interface ipins       pins;
endinterface

interface PcieHost#(numeric type dsz, numeric type nSlaves);
   interface Vector#(16,MSIX_Entry)              msixEntry;
   interface MemMaster#(32,32)                   master;
   interface Vector#(nSlaves,MemSlave#(40,dsz))  slave;
   interface Put#(Tuple2#(Bit#(64),Bit#(32)))    interruptRequest;
   interface Client#(TLPData#(16), TLPData#(16)) pci;
endinterface

(* synthesize *)
module [Module] mkPcieHost#(PciId my_pciId)(PcieHost#(DataBusWidth, NumberOfMasters));
// provisos(
//    Mul#(TDiv#(dsz, 8), 8, dsz),
//     Add#(a__, TDiv#(dsz, 32), 8),
//     Add#(b__, TMul#(32, TDiv#(dsz, 32)), 256),
//     Add#(c__, TMul#(8, TDiv#(dsz, 32)), 64),
//     Add#(d__, dsz, 256),
//     Add#(e__, 32, dsz),
//     Mul#(TDiv#(dsz, 32), 32, dsz));
   Clock epClock125 <- exposeCurrentClock();
   Reset epReset125 <- exposeCurrentReset();
   let dispatcher <- mkTLPDispatcher;
   let arbiter    <- mkTLPArbiter;
   Vector#(NumberOfMasters,MemSlaveEngine#(DataBusWidth)) sEngine <- replicateM(mkMemSlaveEngine(my_pciId));
   Vector#(NumberOfMasters,MemSlave#(40,DataBusWidth)) slavearr;
   MemInterrupt intr <- mkMemInterrupt(my_pciId);

   Vector#(PortMax, MemMasterEngine) mvec;
   for (Integer i = 0; i < valueOf(PortMax) - 1 + valueOf(NumberOfMasters); i=i+1) begin
       let tlp;
       if (i == portInterrupt)
           tlp = intr.tlp;
       else if (i >= portAxi) begin
           tlp = sEngine[i - portAxi].tlp;
           slavearr[i - portAxi] = sEngine[i - portAxi].slave;
       end
       else begin
           mvec[i] <- mkMemMasterEngine(my_pciId);
           tlp = mvec[i].tlp;
       end
       mkConnection((interface Server;
                        interface response = dispatcher.out[i];
                        interface request = arbiter.in[i];
                     endinterface), tlp);
   end

   PcieTracer  traceif <- mkPcieTracer();
   mkConnection(traceif.bus, (interface Client;
                                 interface request = arbiter.outToBus;
                                 interface response = dispatcher.inFromBus;
                              endinterface));

   PcieControlAndStatusRegs csr <- mkPcieControlAndStatusRegs(traceif.tlpdata);
   MemSlave#(32,32) my_slave <- mkMemSlave(csr.client);
   mkConnection(mvec[portConfig].master, my_slave);

   interface msixEntry = csr.msixEntry;
   interface master = mvec[portPortal].master;
   interface slave = slavearr;
   interface interruptRequest = intr.interruptRequest;
   interface pci = traceif.pci;
endmodule: mkPcieHost

(* synthesize *)
module mkSynthesizeablePortalTop(PortalTop#(40, DataBusWidth, PinType, NumberOfMasters));
   Clock defaultClock <- exposeCurrentClock();
   let top <- mkPortalTop(`CLOCK_ARG);
   interface masters = top.masters;
   interface slave = top.slave;
   interface interrupt = top.interrupt;
   interface leds = top.leds;
   interface pins = top.pins;
endmodule

(* no_default_clock, no_default_reset *)
module [Module] mkPcieTopFromPortal #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)
   (PcieTop#(PinType));

   Clock sys_clk_200mhz <- mkClockIBUFDS(sys_clk_p, sys_clk_n);
   Clock sys_clk_200mhz_buf <- mkClockBUFG(clocked_by sys_clk_200mhz);
   Clock pci_clk_100mhz_buf <- mkClockIBUFDS_GTE2(True, pci_sys_clk_p, pci_sys_clk_n);

   // Instantiate the PCIE endpoint
   PCIExpressX7#(PcieLanes) _ep <- mkPCIExpressEndpointX7( defaultValue
						  , clocked_by pci_clk_100mhz_buf
						  , reset_by pci_sys_reset_n
						  );
   // The PCIe endpoint exports full (250MHz) and half-speed (125MHz) clocks
   Clock epClock250 = _ep.user.clk_out;
   Reset user_reset_n <- mkResetInverter(_ep.user.reset_out);
   Reset epReset250 <- mkAsyncReset(4, user_reset_n, epClock250);

   ClockGenerator7Params     params = defaultValue;
   params.clkin1_period    = 4.000;
   params.clkin_buffer     = False;
   params.clkfbout_mult_f  = 4.000;
   params.clkout0_divide_f = 8.000;
   ClockGenerator7           clkgen <- mkClockGenerator7(params, clocked_by epClock250, reset_by user_reset_n);
   Clock epClock125 = clkgen.clkout0; /* half speed user_clk */
   Reset epReset125 <- mkAsyncReset(4, user_reset_n, epClock125);

   let portalTop <- mkSynthesizeablePortalTop(clocked_by epClock125, reset_by epReset125);
   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(
         PciId{ bus:  _ep.cfg.bus_number(), dev: _ep.cfg.device_number(), func: _ep.cfg.function_number()},
         clocked_by epClock125, reset_by epReset125);

   // The PCIE endpoint is processing TLPData#(8)s at 250MHz.  The
   // AXI bridge is accepting TLPData#(16)s at 125 MHz. The
   // connection between the endpoint and the AXI contains GearBox
   // instances for the TLPData#(8)@250 <--> TLPData#(16)@125
   // conversion.
   PcieGearbox gb <- mkPcieGearbox(epClock250, epReset250, epClock125, epReset125);
   mkConnection(_ep.tlp, gb.tlp, clocked_by epClock250, reset_by epReset250);
   mkConnection(gb.pci, pciehost.pci, clocked_by epClock125, reset_by epReset125);

   mkConnection(pciehost.master, portalTop.slave, clocked_by epClock125, reset_by epReset125);
   if (valueOf(NumberOfMasters) > 0) begin
      mapM(uncurry(mkConnection),zip(portalTop.masters, pciehost.slave));
   end

   // going from level to edge-triggered interrupt
   Vector#(15, Reg#(Bool)) interruptRequested <- replicateM(mkReg(False, clocked_by epClock125, reset_by epReset125));
   rule interrupt_rule;
     Integer intr_num = 0;
     for (Integer i = 0; i < 15; i = i + 1) begin
	 if (intr_num == 0 && portalTop.interrupt[i] && !interruptRequested[i])
             intr_num = i+1;
	 interruptRequested[i] <= portalTop.interrupt[i];
     end
     if (intr_num != 0) begin // i= 0 for the directory
        MSIX_Entry msixEntry = pciehost.msixEntry[intr_num];
        pciehost.interruptRequest.put(tuple2({msixEntry.addr_hi, msixEntry.addr_lo}, msixEntry.msg_data));
     end
   endrule

   interface pcie = _ep.pcie;
   method Bit#(NumLeds) leds();
      return extend({_ep.user.lnk_up(),3'd2});
   endmethod
   interface pins = portalTop.pins;
endmodule: mkPcieTopFromPortal

module mkPcieTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)
   (PcieTop#(PinType));
   let top <- mkPcieTopFromPortal(pci_sys_clk_p, pci_sys_clk_n, sys_clk_p, sys_clk_n, pci_sys_reset_n);
   return top;
endmodule: mkPcieTop

// 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 Clocks            :: *;
import GetPut            :: *;
import FIFO              :: *;
import Connectable       :: *;
import ClientServer      :: *;
import Xilinx            :: *;
import DefaultValue      :: *;
import PcieSplitter      :: *;
import PcieTracer        :: *;
import PcieGearbox       :: *;
import XbsvXilinx7Pcie   :: *;
import PCIEWRAPPER       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import MemSlaveEngine    :: *;
import MemMasterEngine   :: *;
import PcieCsr           :: *;
import MemSlave          :: *;
import MemTypes          :: *;

`ifdef Artix7
typedef 4 PcieLanes;
typedef 4 NumLeds;
`else
typedef 8 PcieLanes;
typedef 8 NumLeds;
`endif
`ifndef DataBusWidth
`define DataBusWidth 64
`endif
`ifndef NumberOfMasters
`define NumberOfMasters 1
`endif
`ifndef PinType
`define PinType Empty
`endif
`ifdef USES_FCLK1
`define CLOCK_ARG  defaultClock
`else
`define CLOCK_ARG
`endif

typedef `DataBusWidth DataBusWidth;
typedef `NumberOfMasters NumberOfMasters;
typedef `PinType PinType;

interface PcieTop#(type ipins);
   (* prefix="PCIE" *)
   interface PciewrapPci_exp#(PcieLanes) pcie;
   (* always_ready *)
   method Bit#(NumLeds) leds();
   (* prefix="" *)
   interface ipins       pins;
endinterface

interface PcieHost#(numeric type dsz, numeric type nSlaves);
   interface Vector#(16,MSIX_Entry)              msixEntry;
   interface MemMaster#(32,32)                   master;
   interface Vector#(nSlaves,MemSlave#(40,dsz))  slave;
   interface Put#(Tuple2#(Bit#(64),Bit#(32)))    interruptRequest;
   interface Client#(TLPData#(16), TLPData#(16)) pci;
endinterface

module [Module] mkPcieHost#(PciId my_pciId)(PcieHost#(dsz, nSlaves))
provisos(
   Mul#(TDiv#(dsz, 8), 8, dsz),
    Add#(a__, TDiv#(dsz, 32), 8),
    Add#(b__, TMul#(32, TDiv#(dsz, 32)), 256),
    Add#(c__, TMul#(8, TDiv#(dsz, 32)), 64),
    Add#(d__, dsz, 256),
    Add#(e__, 32, dsz),
    Mul#(TDiv#(dsz, 32), 32, dsz));
   Clock epClock125 <- exposeCurrentClock();
   Reset epReset125 <- exposeCurrentReset();
   let dispatcher <- mkTLPDispatcher;
   let arbiter    <- mkTLPArbiter;
   Vector#(nSlaves,MemSlaveEngine#(dsz)) sEngine <- replicateM(mkMemSlaveEngine(my_pciId));
   Vector#(nSlaves,MemSlave#(40,dsz)) slavearr;
   MemInterrupt intr <- mkMemInterrupt(my_pciId);

   Vector#(PortMax, MemMasterEngine) mvec;
   for (Integer i = 0; i < valueOf(PortMax) - 1 + valueOf(nSlaves); i=i+1) begin
       let tlp;
       if (i == portInterrupt)
           tlp = intr.tlp;
       else if (i >= portAxi) begin
           tlp = sEngine[i - portAxi].tlp;
           slavearr[i - portAxi] = sEngine[i - portAxi].slave;
       end
       else begin
           mvec[i] <- mkMemMasterEngine(my_pciId);
           tlp = mvec[i].tlp;
       end
       mkConnection((interface Server;
                        interface response = dispatcher.out[i];
                        interface request = arbiter.in[i];
                     endinterface), tlp);
   end

   PcieTracer  traceif <- mkPcieTracer();
   mkConnection(traceif.bus, (interface Client;
                                 interface request = arbiter.outToBus;
                                 interface response = dispatcher.inFromBus;
                              endinterface));

   PcieControlAndStatusRegs csr <- mkPcieControlAndStatusRegs(traceif.tlpdata);
   MemSlave#(32,32) my_slave <- mkMemSlave(csr.client);
   mkConnection(mvec[portConfig].master, my_slave);

   interface msixEntry = csr.msixEntry;
   interface master = mvec[portPortal].master;
   interface slave = slavearr;
   interface interruptRequest = intr.interruptRequest;
   interface pci = traceif.pci;
endmodule: mkPcieHost

(* synthesize *)
module mkSynthesizeablePortalTop(PortalTop#(40, DataBusWidth, PinType, NumberOfMasters));
   Clock defaultClock <- exposeCurrentClock();
   let top <- mkPortalTop(`CLOCK_ARG);
   interface masters = top.masters;
   interface slave = top.slave;
   interface interrupt = top.interrupt;
   interface leds = top.leds;
   interface pins = top.pins;
endmodule

(* no_default_clock, no_default_reset *)
module [Module] mkPcieTopFromPortal #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)
   (PcieTop#(PinType));

   Clock sys_clk_200mhz <- mkClockIBUFDS(sys_clk_p, sys_clk_n);
   Clock sys_clk_200mhz_buf <- mkClockBUFG(clocked_by sys_clk_200mhz);
   Clock pci_clk_100mhz_buf <- mkClockIBUFDS_GTE2(True, pci_sys_clk_p, pci_sys_clk_n);

   // Instantiate the PCIE endpoint
   PCIExpressX7#(PcieLanes) _ep <- mkPCIExpressEndpointX7( defaultValue
						  , clocked_by pci_clk_100mhz_buf
						  , reset_by pci_sys_reset_n
						  );
   // The PCIe endpoint exports full (250MHz) and half-speed (125MHz) clocks
   Clock epClock250 = _ep.user.clk_out;
   Reset user_reset_n <- mkResetInverter(_ep.user.reset_out);
   Reset epReset250 <- mkAsyncReset(4, user_reset_n, epClock250);

   ClockGenerator7Params     params = defaultValue;
   params.clkin1_period    = 4.000;
   params.clkin_buffer     = False;
   params.clkfbout_mult_f  = 4.000;
   params.clkout0_divide_f = 8.000;
   ClockGenerator7           clkgen <- mkClockGenerator7(params, clocked_by epClock250, reset_by user_reset_n);
   Clock epClock125 = clkgen.clkout0; /* half speed user_clk */
   Reset epReset125 <- mkAsyncReset(4, user_reset_n, epClock125);

   let portalTop <- mkSynthesizeablePortalTop(clocked_by epClock125, reset_by epReset125);
   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(
         PciId{ bus:  _ep.cfg.bus_number(), dev: _ep.cfg.device_number(), func: _ep.cfg.function_number()},
         clocked_by epClock125, reset_by epReset125);

   // The PCIE endpoint is processing TLPData#(8)s at 250MHz.  The
   // AXI bridge is accepting TLPData#(16)s at 125 MHz. The
   // connection between the endpoint and the AXI contains GearBox
   // instances for the TLPData#(8)@250 <--> TLPData#(16)@125
   // conversion.
   PcieGearbox gb <- mkPcieGearbox(epClock250, epReset250, epClock125, epReset125);
   mkConnection(_ep.tlp, gb.tlp, clocked_by epClock250, reset_by epReset250);
   mkConnection(gb.pci, pciehost.pci, clocked_by epClock125, reset_by epReset125);

   mkConnection(pciehost.master, portalTop.slave, clocked_by epClock125, reset_by epReset125);
   if (valueOf(NumberOfMasters) > 0) begin
      mapM(uncurry(mkConnection),zip(portalTop.masters, pciehost.slave));
   end

   // going from level to edge-triggered interrupt
   Vector#(15, Reg#(Bool)) interruptRequested <- replicateM(mkReg(False, clocked_by epClock125, reset_by epReset125));
   rule interrupt_rule;
     Integer intr_num = 0;
     for (Integer i = 0; i < 15; i = i + 1) begin
	 if (intr_num == 0 && portalTop.interrupt[i] && !interruptRequested[i])
             intr_num = i+1;
	 interruptRequested[i] <= portalTop.interrupt[i];
     end
     if (intr_num != 0) begin // i= 0 for the directory
        MSIX_Entry msixEntry = pciehost.msixEntry[intr_num];
        pciehost.interruptRequest.put(tuple2({msixEntry.addr_hi, msixEntry.addr_lo}, msixEntry.msg_data));
     end
   endrule

   interface pcie = _ep.pcie;
   method Bit#(NumLeds) leds();
      return extend({_ep.user.lnk_up(),3'd2});
   endmethod
   interface pins = portalTop.pins;
endmodule: mkPcieTopFromPortal

module mkPcieTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)
   (PcieTop#(PinType));
   let top <- mkPcieTopFromPortal(pci_sys_clk_p, pci_sys_clk_n, sys_clk_p, sys_clk_n, pci_sys_reset_n);
   return top;
endmodule: mkPcieTop


synthesis boundary for mkMemSlaveEngine

synthesis boundary for mkMemSlaveEngine

// 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 Clocks            :: *;
import GetPut            :: *;
import FIFO              :: *;
import Connectable       :: *;
import ClientServer      :: *;
import BRAM              :: *;
import DefaultValue      :: *;
import PcieSplitter      :: *;
import PcieTracer        :: *;
import Xilinx            :: *;
import Bscan             :: *;
import Portal            :: *;
import Leds              :: *;
import MemSlaveEngine    :: *;
import MemMasterEngine   :: *;
import PcieCsr           :: *;
import MemTypes          :: *;
`ifndef BSIM
`ifdef XILINX
import PcieEndpointX7    :: *;
import PCIEWRAPPER       :: *;
`elsif ALTERA
import PcieEndpointS5    :: *;
`endif
`endif
import HostInterface     :: *;

// implemented in TlpReplay.cxx
import "BDPI" function Action put_tlp(TLPData#(16) d);
import "BDPI" function ActionValue#(TLPData#(16)) get_tlp();
import "BDPI" function Bool can_put_tlp();
import "BDPI" function Bool can_get_tlp();

(* synthesize *)
module mkMemSlaveEngineSynth#(PciId my_id)(MemSlaveEngine#(DataBusWidth));
   let memSlaveEngine <- mkMemSlaveEngine(my_id);
   return memSlaveEngine;
endmodule

//(* synthesize *) commented out so that the guards in MemServer aren't destroyed (mdk)
module  mkPcieHost#(PciId my_pciId)(PcieHost#(DataBusWidth, NumberOfMasters));
   Clock epClock125 <- exposeCurrentClock();
   Reset epReset125 <- exposeCurrentReset();
   let dispatcher <- mkTLPDispatcher;
   let arbiter    <- mkTLPArbiter;
   Vector#(NumberOfMasters,MemSlaveEngine#(DataBusWidth)) sEngine <- replicateM(mkMemSlaveEngineSynth(my_pciId));
   Vector#(NumberOfMasters,PhysMemSlave#(PciePhysAddrWidth,DataBusWidth)) slavearr;
   MemInterrupt intr <- mkMemInterrupt(my_pciId);
`ifndef PCIE_NO_BSCAN
   BscanTop bscan <- mkBscanTop(3); // Use USER3  (JTAG IDCODE address 0x22)
   BscanLocal lbscan <- mkBscanLocal(bscan, clocked_by bscan.tck, reset_by bscan.rst);
`endif

   Vector#(PortMax, MemMasterEngine) mvec;
   for (Integer i = 0; i < valueOf(PortMax) - 1 + valueOf(NumberOfMasters); i=i+1) begin
       let tlp;
       if (i == portInterrupt)
           tlp = intr.tlp;
       else if (i >= portAxi) begin
           tlp = sEngine[i - portAxi].tlp;
           slavearr[i - portAxi] = sEngine[i - portAxi].slave;
       end
       else begin
           mvec[i] <- mkMemMasterEngine(my_pciId);
           tlp = mvec[i].tlp;
       end
       mkConnection((interface Server;
                        interface response = dispatcher.out[i];
                        interface request = arbiter.in[i];
                     endinterface), tlp);
   end

   PcieTracer  traceif <- mkPcieTracer();
   mkConnection(traceif.bus, (interface Client;
                                 interface request = arbiter.outToBus;
                                 interface response = dispatcher.inFromBus;
                              endinterface));
   if (valueOf(NumberOfMasters) > 0) begin
      mkConnection(sEngine[0].trace, traceif.trace);
   end

`ifndef BSIM
`ifndef PCIE_NO_BSCAN
   Reg#(Bit#(TAdd#(TlpTraceAddrSize,1))) bscanPcieTraceBramWrAddrReg <- mkReg(0);
   BscanBram#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) pcieBscanBram <- mkBscanBram(127, bscanPcieTraceBramWrAddrReg, lbscan.loc[1]);
   mkConnection(pcieBscanBram.bramClient, traceif.tlpdata.bscanBramServer);
   rule tdorule;
      lbscan.loc[1].tdo(pcieBscanBram.data_out());
   endrule
`endif
`endif

   PcieControlAndStatusRegs csr <- mkPcieControlAndStatusRegs(traceif.tlpdata);
   mkConnection(mvec[portConfig].master, csr.memSlave);

   interface msixEntry = csr.msixEntry;
   interface master = mvec[portPortal].master;
   interface slave = slavearr;
   interface interruptRequest = intr.interruptRequest;
   interface pci = traceif.pci;
`ifndef PCIE_NO_BSCAN
   interface BscanTop bscanif = lbscan.loc[0];
`else
   interface BRAMServer traceBramServer = traceif.tlpdata.bscanBramServer;
`endif
endmodule: mkPcieHost

interface PcieTop#(type ipins);
`ifndef BSIM
   (* prefix="PCIE" *)
   interface PciewrapPci_exp#(PcieLanes) pcie;
   (* always_ready *)
   method Bit#(NumLeds) leds();
   interface Clock deleteme_unused_clockLeds;
   (* prefix="" *)
   interface ipins       pins;
`endif
endinterface

`ifdef BSIM
module mkBsimPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);
   let dc <- exposeCurrentClock;
   let dr <- exposeCurrentReset;
   Clock epClock125 = dc;
   Reset epReset125 = dr;
   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(PciId{ bus:0, dev:0, func:0});
   // connect pciehost.pci to bdip functions here
   rule from_bdpi if (can_get_tlp);
      TLPData#(16) foo <- get_tlp;
      pciehost.pci.response.put(foo);
      //$display("from_bdpi: %h %d", foo, valueOf(SizeOf#(TLPData#(16))));
   endrule
   rule to_bdpi if (can_put_tlp);
      TLPData#(16) foo <- pciehost.pci.request.get;
      put_tlp(foo);
      //$display("to_bdpi");
   endrule
   interface Clock tepClock125 = epClock125;
   interface Reset tepReset125 = epReset125;
   interface PcieHost tpciehost = pciehost;
endmodule
`endif

`ifdef XILINX
(* no_default_clock, no_default_reset *)
module mkXilinxPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);

// Clock and PcieEndpoint for Xilinx
   Clock sys_clk_200mhz <- mkClockIBUFDS(
`ifdef ClockDefaultParam
       defaultValue,
`endif
       sys_clk_p, sys_clk_n);
   Clock sys_clk_200mhz_buf <- mkClockBUFG(clocked_by sys_clk_200mhz);
   Clock pci_clk_100mhz_buf <- mkClockIBUFDS_GTE2(
`ifdef ClockDefaultParam
       defaultValue,
`endif
       True, pci_sys_clk_p, pci_sys_clk_n);
   // Instantiate the PCIE endpoint
   PcieEndpointX7#(PcieLanes) ep7 <- mkPcieEndpointX7( clocked_by pci_clk_100mhz_buf, reset_by pci_sys_reset_n);

   Clock epClock125 = ep7.epClock125;
   Reset epReset125 = ep7.epReset125;
   Clock epClock250 = ep7.epClock250;
   Reset epReset250 = ep7.epReset250;
`ifdef PCIE_250MHZ
   Clock portalClock_ = epClock250;
   Reset portalReset_ = epReset250;
`else
   Clock portalClock_ = epClock125;
   Reset portalReset_ = epReset125;
`endif
   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(
         PciId{ bus:  ep7.cfg.bus_number(), dev: ep7.cfg.device_number(), func: ep7.cfg.function_number()},
         clocked_by portalClock_, reset_by portalReset_);
   mkConnection(ep7.tlp, pciehost.pci, clocked_by portalClock_, reset_by portalReset_);
   interface Clock tsys_clk_200mhz = sys_clk_200mhz;
   interface Clock tsys_clk_200mhz_buf = sys_clk_200mhz_buf;
   interface Clock tpci_clk_100mhz_buf = pci_clk_100mhz_buf;

   interface PcieEndpointX7 tep7 = ep7;
   interface Clock tepClock125 = epClock125;
   interface Reset tepReset125 = epReset125;
   interface PcieHost tpciehost = pciehost;

   //once the dot product server makes timing, replace epClock125 with ep7.epclock250
   interface portalClock = portalClock_;
   interface portalReset = portalReset_;
   interface derivedClock = ep7.epDerivedClock;
   interface derivedReset = ep7.epDerivedReset;
endmodule
`endif

`ifdef ALTERA
(* no_default_clock, no_default_reset *)
module mkAlteraPcieHostTop #(Clock pci_sys_clk_p, Reset pci_sys_reset_n)(PcieHostTop);

   PcieEndpointS5#(PcieLanes) ep7 <- mkPcieEndpointS5( clocked_by pci_sys_clk_p, reset_by pci_sys_reset_n);

   Clock epClock125 = ep7.epClock125;
   Reset epReset125 = ep7.epReset125;
   Clock epClock250 = ep7.epClock250;
   Reset epReset250 = ep7.epReset250;

`ifdef PCIE_250MHZ
   Clock portalClock_ = epClock250;
   Reset portalReset_ = epReset250;
`else
   Clock portalClock_ = epClock125;
   Reset portalReset_ = epReset125;
`endif

   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(
         PciId{ bus:  ep7.cfg.bus_number(), dev: ep7.cfg.device_number(), func: ep7.cfg.function_number()},
         clocked_by portalClock_, reset_by portalReset_);
   mkConnection(ep7.tlp, pciehost.pci, clocked_by portalClock_, reset_by portalReset_);

   interface PcieEndpointS5 tep7 = ep7;
   interface Clock tepClock125 = epClock125;
   interface Reset tepReset125 = epReset125;
   interface PcieHost tpciehost = pciehost;

   //once the dot product server makes timing, replace epClock125 with ep7.epclock250
   interface portalClock = portalClock_;
   interface portalReset = portalReset_;
   interface derivedClock = ep7.epDerivedClock;
   interface derivedReset = ep7.epDerivedReset;
endmodule
`endif

`ifdef BSIM
   module mkPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);
   PcieHostTop _a <- mkXilinxPcieHostTop(pci_sys_clk_p, pci_sys_clk_n, sys_clk_p, sys_clk_n, pci_sys_reset_n); return _a;
   endmodule
`elsif XILINX // XILINX
   //(* synthesize, no_default_clock, no_default_reset *)
   (* no_default_clock, no_default_reset *)
   module mkPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);
      PcieHostTop _a <- mkXilinxPcieHostTop(pci_sys_clk_p, pci_sys_clk_n, sys_clk_p, sys_clk_n, pci_sys_reset_n); return _a;
   endmodule
`elsif ALTERA
   //(* synthesize, no_default_clock, no_default_reset *)
   (* no_default_clock, no_default_reset *)
   module mkPcieHostTop #(Clock pci_sys_clk_p, Reset pci_sys_reset_n)(PcieHostTop);
      PcieHostTop _a <- mkAlteraPcieHostTop(pci_sys_clk_p, pci_sys_reset_n); return _a;
   endmodule
`endif // NOT ALTERA

// 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 Clocks            :: *;
import GetPut            :: *;
import FIFO              :: *;
import Connectable       :: *;
import ClientServer      :: *;
import BRAM              :: *;
import DefaultValue      :: *;
import PcieSplitter      :: *;
import PcieTracer        :: *;
import Xilinx            :: *;
import Bscan             :: *;
import Portal            :: *;
import Leds              :: *;
import MemSlaveEngine    :: *;
import MemMasterEngine   :: *;
import PcieCsr           :: *;
import MemTypes          :: *;
`ifndef BSIM
`ifdef XILINX
import PcieEndpointX7    :: *;
import PCIEWRAPPER       :: *;
`elsif ALTERA
import PcieEndpointS5    :: *;
`endif
`endif
import HostInterface     :: *;

// implemented in TlpReplay.cxx
import "BDPI" function Action put_tlp(TLPData#(16) d);
import "BDPI" function ActionValue#(TLPData#(16)) get_tlp();
import "BDPI" function Bool can_put_tlp();
import "BDPI" function Bool can_get_tlp();

//(* synthesize *) commented out so that the guards in MemServer aren't destroyed (mdk)
module  mkPcieHost#(PciId my_pciId)(PcieHost#(DataBusWidth, NumberOfMasters));
   Clock epClock125 <- exposeCurrentClock();
   Reset epReset125 <- exposeCurrentReset();
   let dispatcher <- mkTLPDispatcher;
   let arbiter    <- mkTLPArbiter;
   Vector#(NumberOfMasters,MemSlaveEngine#(DataBusWidth)) sEngine <- replicateM(mkMemSlaveEngine(my_pciId));
   Vector#(NumberOfMasters,PhysMemSlave#(PciePhysAddrWidth,DataBusWidth)) slavearr;
   MemInterrupt intr <- mkMemInterrupt(my_pciId);
`ifndef PCIE_NO_BSCAN
   BscanTop bscan <- mkBscanTop(3); // Use USER3  (JTAG IDCODE address 0x22)
   BscanLocal lbscan <- mkBscanLocal(bscan, clocked_by bscan.tck, reset_by bscan.rst);
`endif

   Vector#(PortMax, MemMasterEngine) mvec;
   for (Integer i = 0; i < valueOf(PortMax) - 1 + valueOf(NumberOfMasters); i=i+1) begin
       let tlp;
       if (i == portInterrupt)
           tlp = intr.tlp;
       else if (i >= portAxi) begin
           tlp = sEngine[i - portAxi].tlp;
           slavearr[i - portAxi] = sEngine[i - portAxi].slave;
       end
       else begin
           mvec[i] <- mkMemMasterEngine(my_pciId);
           tlp = mvec[i].tlp;
       end
       mkConnection((interface Server;
                        interface response = dispatcher.out[i];
                        interface request = arbiter.in[i];
                     endinterface), tlp);
   end

   PcieTracer  traceif <- mkPcieTracer();
   mkConnection(traceif.bus, (interface Client;
                                 interface request = arbiter.outToBus;
                                 interface response = dispatcher.inFromBus;
                              endinterface));
   if (valueOf(NumberOfMasters) > 0) begin
      mkConnection(sEngine[0].trace, traceif.trace);
   end

`ifndef BSIM
`ifndef PCIE_NO_BSCAN
   Reg#(Bit#(TAdd#(TlpTraceAddrSize,1))) bscanPcieTraceBramWrAddrReg <- mkReg(0);
   BscanBram#(Bit#(TAdd#(TlpTraceAddrSize,1)), TimestampedTlpData) pcieBscanBram <- mkBscanBram(127, bscanPcieTraceBramWrAddrReg, lbscan.loc[1]);
   mkConnection(pcieBscanBram.bramClient, traceif.tlpdata.bscanBramServer);
   rule tdorule;
      lbscan.loc[1].tdo(pcieBscanBram.data_out());
   endrule
`endif
`endif

   PcieControlAndStatusRegs csr <- mkPcieControlAndStatusRegs(traceif.tlpdata);
   mkConnection(mvec[portConfig].master, csr.memSlave);

   interface msixEntry = csr.msixEntry;
   interface master = mvec[portPortal].master;
   interface slave = slavearr;
   interface interruptRequest = intr.interruptRequest;
   interface pci = traceif.pci;
`ifndef PCIE_NO_BSCAN
   interface BscanTop bscanif = lbscan.loc[0];
`else
   interface BRAMServer traceBramServer = traceif.tlpdata.bscanBramServer;
`endif
endmodule: mkPcieHost

interface PcieTop#(type ipins);
`ifndef BSIM
   (* prefix="PCIE" *)
   interface PciewrapPci_exp#(PcieLanes) pcie;
   (* always_ready *)
   method Bit#(NumLeds) leds();
   interface Clock deleteme_unused_clockLeds;
   (* prefix="" *)
   interface ipins       pins;
`endif
endinterface

`ifdef BSIM
module mkBsimPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);
   let dc <- exposeCurrentClock;
   let dr <- exposeCurrentReset;
   Clock epClock125 = dc;
   Reset epReset125 = dr;
   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(PciId{ bus:0, dev:0, func:0});
   // connect pciehost.pci to bdip functions here
   rule from_bdpi if (can_get_tlp);
      TLPData#(16) foo <- get_tlp;
      pciehost.pci.response.put(foo);
      //$display("from_bdpi: %h %d", foo, valueOf(SizeOf#(TLPData#(16))));
   endrule
   rule to_bdpi if (can_put_tlp);
      TLPData#(16) foo <- pciehost.pci.request.get;
      put_tlp(foo);
      //$display("to_bdpi");
   endrule
   interface Clock tepClock125 = epClock125;
   interface Reset tepReset125 = epReset125;
   interface PcieHost tpciehost = pciehost;
endmodule
`endif

`ifdef XILINX
(* no_default_clock, no_default_reset *)
module mkXilinxPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);

// Clock and PcieEndpoint for Xilinx
   Clock sys_clk_200mhz <- mkClockIBUFDS(
`ifdef ClockDefaultParam
       defaultValue,
`endif
       sys_clk_p, sys_clk_n);
   Clock sys_clk_200mhz_buf <- mkClockBUFG(clocked_by sys_clk_200mhz);
   Clock pci_clk_100mhz_buf <- mkClockIBUFDS_GTE2(
`ifdef ClockDefaultParam
       defaultValue,
`endif
       True, pci_sys_clk_p, pci_sys_clk_n);
   // Instantiate the PCIE endpoint
   PcieEndpointX7#(PcieLanes) ep7 <- mkPcieEndpointX7( clocked_by pci_clk_100mhz_buf, reset_by pci_sys_reset_n);

   Clock epClock125 = ep7.epClock125;
   Reset epReset125 = ep7.epReset125;
   Clock epClock250 = ep7.epClock250;
   Reset epReset250 = ep7.epReset250;
`ifdef PCIE_250MHZ
   Clock portalClock_ = epClock250;
   Reset portalReset_ = epReset250;
`else
   Clock portalClock_ = epClock125;
   Reset portalReset_ = epReset125;
`endif
   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(
         PciId{ bus:  ep7.cfg.bus_number(), dev: ep7.cfg.device_number(), func: ep7.cfg.function_number()},
         clocked_by portalClock_, reset_by portalReset_);
   mkConnection(ep7.tlp, pciehost.pci, clocked_by portalClock_, reset_by portalReset_);
   interface Clock tsys_clk_200mhz = sys_clk_200mhz;
   interface Clock tsys_clk_200mhz_buf = sys_clk_200mhz_buf;
   interface Clock tpci_clk_100mhz_buf = pci_clk_100mhz_buf;

   interface PcieEndpointX7 tep7 = ep7;
   interface Clock tepClock125 = epClock125;
   interface Reset tepReset125 = epReset125;
   interface PcieHost tpciehost = pciehost;

   //once the dot product server makes timing, replace epClock125 with ep7.epclock250
   interface portalClock = portalClock_;
   interface portalReset = portalReset_;
   interface derivedClock = ep7.epDerivedClock;
   interface derivedReset = ep7.epDerivedReset;
endmodule
`endif

`ifdef ALTERA
(* no_default_clock, no_default_reset *)
module mkAlteraPcieHostTop #(Clock pci_sys_clk_p, Reset pci_sys_reset_n)(PcieHostTop);

   PcieEndpointS5#(PcieLanes) ep7 <- mkPcieEndpointS5( clocked_by pci_sys_clk_p, reset_by pci_sys_reset_n);

   Clock epClock125 = ep7.epClock125;
   Reset epReset125 = ep7.epReset125;
   Clock epClock250 = ep7.epClock250;
   Reset epReset250 = ep7.epReset250;

`ifdef PCIE_250MHZ
   Clock portalClock_ = epClock250;
   Reset portalReset_ = epReset250;
`else
   Clock portalClock_ = epClock125;
   Reset portalReset_ = epReset125;
`endif

   PcieHost#(DataBusWidth, NumberOfMasters) pciehost <- mkPcieHost(
         PciId{ bus:  ep7.cfg.bus_number(), dev: ep7.cfg.device_number(), func: ep7.cfg.function_number()},
         clocked_by portalClock_, reset_by portalReset_);
   mkConnection(ep7.tlp, pciehost.pci, clocked_by portalClock_, reset_by portalReset_);

   interface PcieEndpointS5 tep7 = ep7;
   interface Clock tepClock125 = epClock125;
   interface Reset tepReset125 = epReset125;
   interface PcieHost tpciehost = pciehost;

   //once the dot product server makes timing, replace epClock125 with ep7.epclock250
   interface portalClock = portalClock_;
   interface portalReset = portalReset_;
   interface derivedClock = ep7.epDerivedClock;
   interface derivedReset = ep7.epDerivedReset;
endmodule
`endif

`ifdef BSIM
   module mkPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);
   PcieHostTop _a <- mkXilinxPcieHostTop(pci_sys_clk_p, pci_sys_clk_n, sys_clk_p, sys_clk_n, pci_sys_reset_n); return _a;
   endmodule
`elsif XILINX // XILINX
   //(* synthesize, no_default_clock, no_default_reset *)
   (* no_default_clock, no_default_reset *)
   module mkPcieHostTop #(Clock pci_sys_clk_p, Clock pci_sys_clk_n, Clock sys_clk_p, Clock sys_clk_n, Reset pci_sys_reset_n)(PcieHostTop);
      PcieHostTop _a <- mkXilinxPcieHostTop(pci_sys_clk_p, pci_sys_clk_n, sys_clk_p, sys_clk_n, pci_sys_reset_n); return _a;
   endmodule
`elsif ALTERA
   //(* synthesize, no_default_clock, no_default_reset *)
   (* no_default_clock, no_default_reset *)
   module mkPcieHostTop #(Clock pci_sys_clk_p, Reset pci_sys_reset_n)(PcieHostTop);
      PcieHostTop _a <- mkAlteraPcieHostTop(pci_sys_clk_p, pci_sys_reset_n); return _a;
   endmodule
`endif // NOT ALTERA


disabling multiple outstanding writes in ctrlMux.  This was causing designs to hang on the pcie platform

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

import AxiMasterSlave::*;
import Portal::*;
import Directory::*;

module mkInterruptMux#(Vector#(numPortals,Portal#(aw,_a,_b,_c)) portals) (ReadOnly#(Bool))

   provisos(Add#(nz, TLog#(numPortals), 4),
	    Add#(1, a__, numPortals));

   Vector#(numPortals, ReadOnly#(Bool)) inputs = map(getInterrupt, portals);

   function Bool my_read(ReadOnly#(Bool) x);
      return x._read;
   endfunction

   function Bool my_or(Bool a, Bool b);
      return a || b;
   endfunction

   method Bool _read;
      return fold(my_or,map(my_read,inputs));
   endmethod

endmodule

module mkAxiSlaveMux#(Directory#(aw,_a,_b,_c) dir,
		      Vector#(numPortals,Portal#(aw,_a,_b,_c)) portals) (Axi3Slave#(_a,_b,_c))

   provisos(Add#(1,numPortals,numInputs),
	    Add#(1,numInputs,numIfcs),
	    Add#(nz, TLog#(numIfcs), 4));

   Axi3Slave#(_a,_b,_c) out_of_range <- mkAxi3SlaveOutOfRange;
   Vector#(numIfcs, Axi3Slave#(_a,_b,_c)) ifcs = append(cons(dir.portalIfc.ctrl,map(getCtrl, portals)),cons(out_of_range, nil));

`ifdef MULTIPLE_WRITES
   Vector#(numIfcs,FIFOF#(Bit#(_c))) req_aw_fifos <- replicateM(mkSizedFIFOF(1));
`else
   FIFO#(void) req_aw_fifo <- mkPipelineFIFO;
   Reg#(Bit#(TLog#(numIfcs))) ws <- mkReg(0);
`endif

   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TAdd#(3,aw));

   function Bit#(4) psel(Bit#(_a) a);
      return a[port_sel_high:port_sel_low];
   endfunction

   function Maybe#(Bit#(TLog#(numIfcs))) xxx(Integer x, GetF#(t) y);
      return (y.notEmpty) ? tagged Valid fromInteger(x) : tagged Invalid;
   endfunction

   function Maybe#(Bit#(TLog#(numIfcs))) yyy(Maybe#(Bit#(TLog#(numIfcs))) x, Maybe#(Bit#(TLog#(numIfcs))) y);
      return isValid(x) ? x : y;
   endfunction

   let next_resp_read_idx = fold(yyy, zipWith(xxx, genVector, map(get_resp_read,ifcs)));

`ifdef MULTIPLE_WRITES
   function Maybe#(Bit#(TLog#(numIfcs))) zzz(Bit#(_c) r, Integer x, FIFOF#(Bit#(_c)) y);
      return y.notEmpty ? (y.first == r ? tagged Valid fromInteger(x) : tagged Invalid) : tagged Invalid;
   endfunction

   let next_resp_b_idx    = fold(yyy, zipWith(xxx, genVector, map(get_resp_b,   ifcs)));

   function Bit#(TLog#(numIfcs)) write_idx(Bit#(_c) r);
      return fromMaybe(?, fold(yyy, zipWith(zzz(r), genVector, req_aw_fifos)));
   endfunction
`endif

   interface Put req_aw;
      method Action put(Axi3WriteRequest#(_a,_c) req);
	 Bit#(TLog#(numIfcs)) wsv = truncate(psel(req.address));
	 if (wsv > fromInteger(valueOf(numInputs)))
	    wsv = fromInteger(valueOf(numInputs));
	 ifcs[wsv].req_aw.put(req);
`ifdef MULTIPLE_WRITES
	 req_aw_fifos[wsv].enq(req.id);
`else
	 ws <= wsv;
	 req_aw_fifo.enq(?);
`endif
	 if (wsv > 0)
	    dir.writeEvent <= ?;
      endmethod
   endinterface
   interface Put resp_write;
      method Action put(Axi3WriteData#(_b,_c) wdata);
`ifdef MULTIPLE_WRITES
	 ifcs[write_idx(wdata.id)].resp_write.put(wdata);
`else
	 ifcs[ws].resp_write.put(wdata);
`endif
      endmethod
   endinterface
   interface GetF resp_b;
`ifdef MULTIPLE_WRITES
      method ActionValue#(Axi3WriteResponse#(_c)) get() if (next_resp_b_idx matches tagged Valid .idx);
	 let rv <- ifcs[idx].resp_b.get();
	 req_aw_fifos[idx].deq;
	 return rv;
      endmethod
`else
      method ActionValue#(Axi3WriteResponse#(_c)) get();
	 let rv <- ifcs[ws].resp_b.get();
	 req_aw_fifo.deq;
	 return rv;
      endmethod
`endif
`ifdef MULTIPLE_WRITES
      method Bool notEmpty();
	 return isValid(next_resp_b_idx);
      endmethod
`else
      method Bool notEmpty;
         return ifcs[ws].resp_b.notEmpty;
      endmethod
`endif
   endinterface
   interface Put req_ar;
      method Action put(Axi3ReadRequest#(_a,_c) req);
	 Bit#(TLog#(numIfcs)) rsv = truncate(psel(req.address));
	 if (rsv > fromInteger(valueOf(numInputs)))
	    rsv = fromInteger(valueOf(numInputs));
	 ifcs[rsv].req_ar.put(req);
	 if (rsv > 0)
	    dir.readEvent <= ?;
      endmethod
   endinterface
   interface GetF resp_read;
      method ActionValue#(Axi3ReadResponse#(_b,_c)) get() if (next_resp_read_idx matches tagged Valid .idx);
	 let rv <- ifcs[idx].resp_read.get();
	 return rv;
      endmethod
      method Bool notEmpty();
	 return isValid(next_resp_read_idx);
      endmethod
   endinterface

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 GetPutF::*;

import AxiMasterSlave::*;
import Portal::*;
import Directory::*;

module mkInterruptMux#(Vector#(numPortals,Portal#(aw,_a,_b,_c)) portals) (ReadOnly#(Bool))

   provisos(Add#(nz, TLog#(numPortals), 4),
	    Add#(1, a__, numPortals));

   Vector#(numPortals, ReadOnly#(Bool)) inputs = map(getInterrupt, portals);

   function Bool my_read(ReadOnly#(Bool) x);
      return x._read;
   endfunction

   function Bool my_or(Bool a, Bool b);
      return a || b;
   endfunction

   method Bool _read;
      return fold(my_or,map(my_read,inputs));
   endmethod

endmodule

module mkAxiSlaveMux#(Directory#(aw,_a,_b,_c) dir,
		      Vector#(numPortals,Portal#(aw,_a,_b,_c)) portals) (Axi3Slave#(_a,_b,_c))

   provisos(Add#(1,numPortals,numInputs),
	    Add#(1,numInputs,numIfcs),
	    Add#(nz, TLog#(numIfcs), 4));

   Axi3Slave#(_a,_b,_c) out_of_range <- mkAxi3SlaveOutOfRange;
   Vector#(numIfcs, Axi3Slave#(_a,_b,_c)) ifcs = append(cons(dir.portalIfc.ctrl,map(getCtrl, portals)),cons(out_of_range, nil));
   Vector#(numIfcs,FIFOF#(Bit#(_c))) req_aw_fifos <- replicateM(mkSizedFIFOF(1));

   let port_sel_low = valueOf(aw);
   let port_sel_high = valueOf(TAdd#(3,aw));

   function Bit#(4) psel(Bit#(_a) a);
      return a[port_sel_high:port_sel_low];
   endfunction

   function Maybe#(Bit#(TLog#(numIfcs))) xxx(Integer x, GetF#(t) y);
      return (y.notEmpty) ? tagged Valid fromInteger(x) : tagged Invalid;
   endfunction

   function Maybe#(Bit#(TLog#(numIfcs))) yyy(Maybe#(Bit#(TLog#(numIfcs))) x, Maybe#(Bit#(TLog#(numIfcs))) y);
      return isValid(x) ? x : y;
   endfunction

   function Maybe#(Bit#(TLog#(numIfcs))) zzz(Bit#(_c) r, Integer x, FIFOF#(Bit#(_c)) y);
      return y.notEmpty ? (y.first == r ? tagged Valid fromInteger(x) : tagged Invalid) : tagged Invalid;
   endfunction

   let next_resp_read_idx = fold(yyy, zipWith(xxx, genVector, map(get_resp_read,ifcs)));
   let next_resp_b_idx    = fold(yyy, zipWith(xxx, genVector, map(get_resp_b,   ifcs)));

   function Bit#(TLog#(numIfcs)) write_idx(Bit#(_c) r);
      return fromMaybe(?, fold(yyy, zipWith(zzz(r), genVector, req_aw_fifos)));
   endfunction

   interface Put req_aw;
      method Action put(Axi3WriteRequest#(_a,_c) req);
	 Bit#(TLog#(numIfcs)) wsv = truncate(psel(req.address));
	 if (wsv > fromInteger(valueOf(numInputs)))
	    wsv = fromInteger(valueOf(numInputs));
	 ifcs[wsv].req_aw.put(req);
	 if (wsv > 0)
	    dir.writeEvent <= ?;
	 req_aw_fifos[wsv].enq(req.id);
      endmethod
   endinterface
   interface Put resp_write;
      method Action put(Axi3WriteData#(_b,_c) wdata);
	 ifcs[write_idx(wdata.id)].resp_write.put(wdata);
      endmethod
   endinterface
   interface GetF resp_b;
      method ActionValue#(Axi3WriteResponse#(_c)) get() if (next_resp_b_idx matches tagged Valid .idx);
	 let rv <- ifcs[idx].resp_b.get();
	 req_aw_fifos[idx].deq;
	 return rv;
      endmethod
      method Bool notEmpty();
	 return isValid(next_resp_b_idx);
      endmethod
   endinterface
   interface Put req_ar;
      method Action put(Axi3ReadRequest#(_a,_c) req);
	 Bit#(TLog#(numIfcs)) rsv = truncate(psel(req.address));
	 if (rsv > fromInteger(valueOf(numInputs)))
	    rsv = fromInteger(valueOf(numInputs));
	 ifcs[rsv].req_ar.put(req);
	 if (rsv > 0)
	    dir.readEvent <= ?;
      endmethod
   endinterface
   interface GetF resp_read;
      method ActionValue#(Axi3ReadResponse#(_b,_c)) get() if (next_resp_read_idx matches tagged Valid .idx);
	 let rv <- ifcs[idx].resp_read.get();
	 return rv;
      endmethod
      method Bool notEmpty();
	 return isValid(next_resp_read_idx);
      endmethod
   endinterface

endmodule


typeclass AxiToMemReadClient

typeclass AxiToMemReadClient

// 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 Vector::*;
import Clocks::*;
import FIFOF::*;
import CFFIFO::*;
import GetPut::*;
import MemTypes::*;
import EHRM::*;
import Axi4MasterSlave::*;

interface AxiMasterBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Bit#(addrWidth)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(1)     aresetn();
    method Bit#(tagWidth)     arid();
    method Bit#(4)     arlen();
    method Bit#(2)     arlock();
    method Bit#(3)     arprot();
    method Bit#(4)     arqos();
    method Action      arready(Bit#(1) v);
    method Bit#(2)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(addrWidth)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(tagWidth)     awid();
    method Bit#(4)     awlen();
    method Bit#(2)     awlock();
    method Bit#(3)     awprot();
    method Bit#(4)     awqos();
    method Action      awready(Bit#(1) v);
    method Bit#(2)     awsize();
    method Bit#(1)     awvalid();
    method Action      bid(Bit#(tagWidth) v);
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(dataWidth) v);
    method Action      rid(Bit#(tagWidth) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(dataWidth)     wdata();
    method Bit#(tagWidth)     wid();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(TDiv#(dataWidth,8))     wstrb();
    method Bit#(1)     wvalid();
    interface extraType   extra;
endinterface

interface HPType;
    method Bit#(3)     racount();
    method Bit#(8)     rcount();
    method Action      rdissuecap1en(Bit#(1) v);
    method Bit#(6)     wacount();
    method Bit#(8)     wcount();
    method Action      wrissuecap1en(Bit#(1) v);
endinterface

interface ACPType;
    method Action      aruser(Bit#(5) v);
    method Action      awuser(Bit#(5) v);
endinterface

interface AxiSlaveBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Action      araddr(Bit#(addrWidth) v);
    method Action      arburst(Bit#(2) v);
    method Action      arcache(Bit#(4) v);
    method Bit#(1)     aresetn();
    method Action      arid(Bit#(tagWidth) v);
    method Action      arlen(Bit#(4) v);
    method Action      arlock(Bit#(2) v);
    method Action      arprot(Bit#(3) v);
    method Action      arqos(Bit#(4) v);
    method Bit#(1)     arready();
    method Action      arsize(Bit#(2) v);
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(addrWidth) v);
    method Action      awburst(Bit#(2) v);
    method Action      awcache(Bit#(4) v);
    method Action      awid(Bit#(tagWidth) v);
    method Action      awlen(Bit#(4) v);
    method Action      awlock(Bit#(2) v);
    method Action      awprot(Bit#(3) v);
    method Action      awqos(Bit#(4) v);
    method Bit#(1)     awready();
    method Action      awsize(Bit#(2) v);
    method Action      awvalid(Bit#(1) v);
    method Bit#(tagWidth)     bid();
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(dataWidth)     rdata();
    method Bit#(1)     rlast();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(dataWidth) v);
    method Action      wid(Bit#(tagWidth) v);
    method Action      wlast(Bit#(1) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(TDiv#(dataWidth,8)) v);
    method Action      wvalid(Bit#(1) v);
    method Bit#(tagWidth)     rid();
    interface extraType   extra;
endinterface

interface Axi4MasterBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Bit#(addrWidth)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(1)     aresetn();
    method Bit#(tagWidth)     arid();
    method Bit#(8)     arlen();
    method Bit#(2)     arlock();
    method Bit#(3)     arprot();
    method Bit#(4)     arqos();
    method Action      arready(Bit#(1) v);
    method Bit#(3)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(addrWidth)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(tagWidth)     awid();
    method Bit#(8)     awlen();
    method Bit#(2)     awlock();
    method Bit#(3)     awprot();
    method Bit#(4)     awqos();
    method Action      awready(Bit#(1) v);
    method Bit#(3)     awsize();
    method Bit#(1)     awvalid();
    method Action      bid(Bit#(tagWidth) v);
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(dataWidth) v);
    method Action      rid(Bit#(tagWidth) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(dataWidth)     wdata();
    method Bit#(tagWidth)     wid();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(TDiv#(dataWidth,8))     wstrb();
    method Bit#(1)     wvalid();
    interface extraType   extra;
endinterface

interface Axi4MasterUntaggedBits#(numeric type addrWidth, numeric type dataWidth);
    method Bit#(addrWidth)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(8)     arlen();
    //method Bit#(2)     arlock();
    method Bit#(3)     arprot();
    //method Bit#(4)     arqos();
    method Action      arready(Bit#(1) v);
    method Bit#(3)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(addrWidth)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(8)     awlen();
    //method Bit#(2)     awlock();
    method Bit#(3)     awprot();
    //method Bit#(4)     awqos();
    method Action      awready(Bit#(1) v);
    method Bit#(3)     awsize();
    method Bit#(1)     awvalid();
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(dataWidth) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(dataWidth)     wdata();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(TDiv#(dataWidth,8))     wstrb();
    method Bit#(1)     wvalid();
endinterface

typeclass ToAxi4MasterBits#(type atype, type btype);
   function atype toAxi4MasterBits(btype b);
endtypeclass

instance ToAxi4MasterBits#(Axi4MasterBits#(addrWidth,dataWidth,tagWidth,Empty), Axi4MasterUntaggedBits#(addrWidth,dataWidth));
function Axi4MasterBits#(addrWidth,dataWidth,tagWidth,Empty) toAxi4MasterBits(Axi4MasterUntaggedBits#(addrWidth,dataWidth) m);
   return (interface Axi4MasterBits#(addrWidth,dataWidth,tagWidth,Empty);
    method araddr = m.araddr;
      method arburst = m.arburst;
      method arcache = m.arcache;
      //method aresetn = no_reset;
      method Bit#(tagWidth)     arid(); return 0; endmethod
      method arlen = m.arlen;
      //method arlock = m.arlock;
      method arprot = m.arprot;
      //method arqos = m.arqos;
      method arready = m.arready;
      method arsize = m.arsize;
      method arvalid = m.arvalid;
      method awaddr = m.awaddr;
      method awburst = m.awburst;
      method awcache = m.awcache;
      method Bit#(tagWidth)     awid(); return 0; endmethod
      method awlen = m.awlen;
      //method awlock = m.awlock;
      method awprot = m.awprot;
      //method awqos = m.awqos;
      method awready = m.awready;
      method awsize = m.awsize;
      method awvalid = m.awvalid;
      method Action      bid(Bit#(tagWidth) v); endmethod
      method bready = m.bready;
      method bresp = m.bresp;
      method bvalid = m.bvalid;
      method rdata = m.rdata;
      method Action      rid(Bit#(tagWidth) v); endmethod
      method rlast = m.rlast;
      method rready = m.rready;
      method rresp = m.rresp;
      method rvalid = m.rvalid;
      method wdata = m.wdata;
      method Bit#(tagWidth)     wid(); return 0; endmethod
      method wlast = m.wlast;
      method wready = m.wready;
      method wstrb = m.wstrb;
      method wvalid = m.wvalid;
      interface extra = ?;
      endinterface);
endfunction
endinstance

interface Axi4SlaveBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Action      araddr(Bit#(addrWidth) v);
    method Action      arburst(Bit#(2) v);
    method Action      arcache(Bit#(4) v);
    method Bit#(1)     aresetn();
    method Action      arid(Bit#(tagWidth) v);
    method Action      arlen(Bit#(8) v);
    method Action      arlock(Bit#(2) v);
    method Action      arprot(Bit#(3) v);
    method Action      arqos(Bit#(4) v);
    method Bit#(1)     arready();
    method Action      arsize(Bit#(3) v);
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(addrWidth) v);
    method Action      awburst(Bit#(2) v);
    method Action      awcache(Bit#(4) v);
    method Action      awid(Bit#(tagWidth) v);
    method Action      awlen(Bit#(8) v);
    method Action      awlock(Bit#(2) v);
    method Action      awprot(Bit#(3) v);
    method Action      awqos(Bit#(4) v);
    method Bit#(1)     awready();
    method Action      awsize(Bit#(3) v);
    method Action      awvalid(Bit#(1) v);
    method Bit#(tagWidth)     bid();
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(dataWidth)     rdata();
    method Bit#(1)     rlast();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(dataWidth) v);
    method Action      wid(Bit#(tagWidth) v);
    method Action      wlast(Bit#(1) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(TDiv#(dataWidth,8)) v);
    method Action      wvalid(Bit#(1) v);
    method Bit#(tagWidth)     rid();
    interface extraType   extra;
endinterface

interface Axi4SlaveLiteBits#(numeric type addrWidth, numeric type dataWidth);
    method Action      araddr(Bit#(addrWidth) v);
    method Bit#(1)     arready();
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(addrWidth) v);
    method Bit#(1)     awready();
    method Action      awvalid(Bit#(1) v);
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(dataWidth)     rdata();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(dataWidth) v);
    method Bit#(1)     wready();
    method Action      wvalid(Bit#(1) v);
endinterface

typeclass ToAxi4SlaveBits#(type atype, type btype);
   function atype toAxi4SlaveBits(btype b);
endtypeclass

module mkAxiFifoF(FIFOF#(t)) provisos(Bits#(t, tSz));
  Ehr#(2, t) da <- mkEhr(?);
  Ehr#(2, Bool) va <- mkEhr(False);
  Ehr#(2, t) db <- mkEhr(?);
  Ehr#(2, Bool) vb <- mkEhr(False);

  rule canon if(vb[1] && !va[1]);
    da[1] <= db[1];
    va[1] <= True;
    vb[1] <= False;
  endrule

  method Bool notFull = !vb[0];

  method Action enq(t x) if(!vb[0]);
    db[0] <= x;
    vb[0] <= True;
  endmethod

  method Bool notEmpty = va[0];

  method Action deq if (va[0]);
    va[0] <= False;
  endmethod

  // no implicit guard, to simplify rule guards below
  method t first;
    return da[0];
  endmethod

  // conflicts with enq, deq, but we do not call it
  method Action clear;
    vb[0] <= False;
    va[0] <= False;
  endmethod
endmodule

typeclass MkPhysMemSlave#(type srctype, numeric type addrWidth, numeric type dataWidth);
   module mkPhysMemSlave#(srctype axiSlave)(PhysMemSlave#(addrWidth,dataWidth));
endtypeclass

instance MkPhysMemSlave#(Axi4SlaveLiteBits#(axiAddrWidth,dataWidth),addrWidth,dataWidth)
      provisos (Add#(axiAddrWidth,a__,addrWidth));
   module mkPhysMemSlave#(Axi4SlaveLiteBits#(axiAddrWidth,dataWidth) axiSlave)(PhysMemSlave#(addrWidth,dataWidth));
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) arfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) rfifo <- mkAxiFifoF();
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) awfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) wfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) bfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) rtagfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) wtagfifo <- mkAxiFifoF();

      rule rl_arvalid_araddr;
	 axiSlave.arvalid(pack(arfifo.notEmpty && rtagfifo.notFull));
	 let addr = 0;
	 if (arfifo.notEmpty)
	    addr = truncate(arfifo.first.addr);
	 axiSlave.araddr(addr);
      endrule
      rule rl_arfifo if (axiSlave.arready() == 1);
	 let req <- toGet(arfifo).get();
	 rtagfifo.enq(req.tag);
      endrule
      rule rl_rready;
	 axiSlave.rready(pack(rfifo.notFull && rtagfifo.notEmpty));
      endrule
      rule rl_rdata if (axiSlave.rvalid() == 1);
	 let rtag <- toGet(rtagfifo).get();
	 rfifo.enq(MemData { data: axiSlave.rdata(), tag: rtag } );
      endrule

      rule rl_awvalid_awaddr;
	 axiSlave.awvalid(pack(awfifo.notEmpty && wtagfifo.notFull));
	 let addr = 0;
	 if (awfifo.notEmpty)
	    addr = truncate(awfifo.first.addr);
	 axiSlave.awaddr(addr);
      endrule
      rule rl_awfifo if (axiSlave.awready() == 1);
	 let req <- toGet(awfifo).get();
	 wtagfifo.enq(req.tag);
      endrule
      rule rl_wvalid;
	 axiSlave.wvalid(pack(wfifo.notEmpty));
	 let wdata = 0;
	 if (wfifo.notEmpty)
	    wdata = wfifo.first.data;
	 axiSlave.wdata(wdata);
      endrule
      rule rl_wdata if (axiSlave.wready() == 1);
	 let md <- toGet(wfifo).get();
      endrule
      rule rl_bready;
	 axiSlave.bready(pack(wtagfifo.notEmpty && bfifo.notFull));
      endrule
      rule rl_done if (axiSlave.bvalid() == 1);
	 let tag <- toGet(wtagfifo).get();
	 bfifo.enq(tag);
      endrule

      interface PhysMemReadServer read_server;
	 interface Put readReq = toPut(arfifo);
	 interface Get readData = toGet(rfifo);
      endinterface
      interface PhysMemWriteServer write_server;
	 interface Put writeReq = toPut(awfifo);
	 interface Put writeData = toPut(wfifo);
	 interface Get writeDone = toGet(bfifo);
      endinterface
   endmodule
endinstance

//FIXME burst transfers
instance MkPhysMemSlave#(Axi4SlaveBits#(axiAddrWidth,dataWidth,tagWidth,Empty),addrWidth,dataWidth)
      provisos (Add#(axiAddrWidth,a__,addrWidth),
		Add#(b__, tagWidth, 6));
   module mkPhysMemSlave#(Axi4SlaveBits#(axiAddrWidth,dataWidth,tagWidth,Empty) axiSlave)(PhysMemSlave#(addrWidth,dataWidth));
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) arfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) rfifo <- mkAxiFifoF();
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) awfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) wfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) bfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) rtagfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) wtagfifo <- mkAxiFifoF();

      rule rl_arvalid_araddr;
	 axiSlave.arvalid(pack(arfifo.notEmpty && rtagfifo.notFull));
	 Bit#(axiAddrWidth) addr = 0;
	 Bit#(tagWidth) id = 0;
	 if (arfifo.notEmpty) begin
	    addr = truncate(arfifo.first.addr);
	    id = truncate(arfifo.first.tag);
	 end
	 axiSlave.araddr(addr);
	 axiSlave.arid(id);
	 axiSlave.arsize(axiBusSize(valueOf(dataWidth)));
	 axiSlave.arburst(2'b01);
	 axiSlave.arprot(3'b000);
	 axiSlave.arcache(4'b0011);
      endrule
      rule rl_arfifo if (axiSlave.arready() == 1);
	 let req <- toGet(arfifo).get();
	 rtagfifo.enq(req.tag);
      endrule
      rule rl_rready;
	 axiSlave.rready(pack(rfifo.notFull && rtagfifo.notEmpty));
      endrule
      rule rl_rdata if (axiSlave.rvalid() == 1);
	 let rtag <- toGet(rtagfifo).get();
	 rfifo.enq(MemData { data: axiSlave.rdata(), tag: rtag } );
      endrule

      rule rl_awvalid_awaddr;
	 axiSlave.awvalid(pack(awfifo.notEmpty && wtagfifo.notFull));
	 Bit#(axiAddrWidth) addr = 0;
	 Bit#(tagWidth) id = 0;
	 if (awfifo.notEmpty) begin
	    addr = truncate(awfifo.first.addr);
	    id = truncate(awfifo.first.tag);
	 end
	 axiSlave.awaddr(addr);
	 axiSlave.awid(id);
	 axiSlave.awsize(axiBusSize(valueOf(dataWidth)));
	 axiSlave.awburst(2'b01);
	 axiSlave.awprot(3'b000);
	 axiSlave.awcache(4'b0011);
      endrule
      rule rl_awfifo if (axiSlave.awready() == 1);
	 let req <- toGet(awfifo).get();
	 wtagfifo.enq(req.tag);
      endrule
      rule rl_wvalid;
	 axiSlave.wvalid(pack(wfifo.notEmpty));
	 let wdata = 0;
	 if (wfifo.notEmpty)
	    wdata = wfifo.first.data;
	 axiSlave.wdata(wdata);
      endrule
      rule rl_wdata if (axiSlave.wready() == 1);
	 let md <- toGet(wfifo).get();
      endrule
      rule rl_bready;
	 axiSlave.bready(pack(wtagfifo.notEmpty && bfifo.notFull));
      endrule
      rule rl_done if (axiSlave.bvalid() == 1);
	 let tag <- toGet(wtagfifo).get();
	 bfifo.enq(tag);
      endrule

      interface PhysMemReadServer read_server;
	 interface Put readReq = toPut(arfifo);
	 interface Get readData = toGet(rfifo);
      endinterface
      interface PhysMemWriteServer write_server;
	 interface Put writeReq = toPut(awfifo);
	 interface Put writeData = toPut(wfifo);
	 interface Get writeDone = toGet(bfifo);
      endinterface
   endmodule
endinstance

typeclass AxiToMemReadClient#(type objIdType, numeric type axiAddrWidth, numeric type dataWidth);
   module mkMemReadClient#(objIdType objId, Axi4MasterBits#(axiAddrWidth,dataWidth,MemTagSize,Empty) m)(MemReadClient#(dataWidth));
   module mkMemWriteClient#(objIdType objId, Axi4MasterBits#(axiAddrWidth,dataWidth,MemTagSize,Empty) m)(MemWriteClient#(dataWidth));
endtypeclass

instance AxiToMemReadClient#(Bit#(32),32,dataWidth);
   module mkMemReadClient#(Bit#(32) objId, Axi4MasterBits#(32,dataWidth,MemTagSize,Empty) m)(MemReadClient#(dataWidth));

      Wire#(Bit#(1)) arready <- mkDWire(0);
      Wire#(Bit#(1)) rvalid <- mkDWire(0);
      Wire#(Bit#(MemTagSize)) rid <- mkDWire(0);
      Wire#(Bit#(2)) rresp <- mkDWire(0);
      Wire#(Bit#(dataWidth)) rdata <- mkDWire(0);
      Wire#(Bit#(1)) rlast <- mkDWire(0);

      FIFOF#(MemRequest)          arfifo <- mkCFFIFOF();
      FIFOF#(MemData#(dataWidth))  rfifo <- mkCFFIFOF();

      rule rl_araddr if (m.arvalid() == 1);
	 let addr = m.araddr();
	 let burstLenBytes = (extend(m.arlen())+1)*fromInteger(valueOf(TDiv#(dataWidth,8)));
	 arfifo.enq(MemRequest { sglId: objId, offset: extend(addr), burstLen: burstLenBytes, tag: extend(m.arid()) });
      endrule
      rule handshake_ar;
	   m.arready(pack(arfifo.notFull()));
      endrule

      rule rl_rdata if (m.rready() == 1);
	 let md <- toGet(rfifo).get();
	 rdata <= md.data;
	 rlast <= pack(md.last);
	 rresp <= 0;
	 rid <= truncate(md.tag);
      endrule
      rule handshake_rdata;
	 m.rvalid(pack(rfifo.notEmpty()));
	 m.rid(rid);
	 m.rresp(rresp);
	 m.rdata(rdata);
	 m.rlast(rlast);
      endrule

      interface Get readReq = toGet(arfifo);
      interface Put readData = toPut(rfifo);
   endmodule

   module mkMemWriteClient#(Bit#(32) objId, Axi4MasterBits#(32,dataWidth,MemTagSize,Empty) m)(MemWriteClient#(dataWidth));

      Wire#(Bit#(1)) awready <- mkDWire(0);
      Wire#(Bit#(1)) wready <- mkDWire(0);
      Wire#(Bit#(1)) bvalid <- mkDWire(0);
      Wire#(Bit#(MemTagSize)) bid <- mkDWire(0);
      Wire#(Bit#(2)) bresp <- mkDWire(0);

      FIFOF#(MemRequest)       awfifo   <- mkCFFIFOF();
      FIFOF#(MemData#(dataWidth)) wfifo <- mkCFFIFOF();
      FIFOF#(Bit#(MemTagSize))    bfifo <- mkCFFIFOF();

      rule rl_awaddr if (m.awvalid() == 1);
	 let addr = m.awaddr();
	 let burstLenBytes = (extend(m.awlen())+1)*fromInteger(valueOf(TDiv#(dataWidth,8)));
	 awfifo.enq(MemRequest { sglId: objId, offset: extend(addr), burstLen: burstLenBytes, tag: extend(m.awid()) });
      endrule
      rule handshake_awaddr;
	 m.awready(pack(awfifo.notFull()));
      endrule

      rule rl_wdata if (m.wvalid() == 1);
	 wfifo.enq(MemData { data: m.wdata(), last: unpack(m.wlast()), tag: extend(m.wid()) });
      endrule
      rule handshake_wdata;
	 m.wready(pack(wfifo.notFull()));
      endrule

      rule rl_bresp if (m.bready() == 1);
	 let tag <- toGet(bfifo).get();
	 bresp <= 0;
	 bid <= truncate(tag);
      endrule
      rule handshake_b;
	   m.bvalid(pack(bfifo.notEmpty()));
	   m.bid(bid);
	   m.bresp(bresp);
      endrule

      interface Get writeReq = toGet(awfifo);
      interface Get writeData = toGet(wfifo);
      interface Put writeDone = toPut(bfifo);
   endmodule
endinstance

typedef AxiMasterBits#(32,32,12,Empty) Pps7Maxigp;
typedef AxiSlaveBits#(32,32,6,Empty) Pps7Saxigp;
typedef AxiSlaveBits#(32,64,6,HPType) Pps7Saxihp;
typedef AxiSlaveBits#(32,64,3,ACPType) Pps7Saxiacp;

// 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 Vector::*;
import Clocks::*;
import FIFOF::*;
import CFFIFO::*;
import GetPut::*;
import MemTypes::*;
import EHRM::*;
import Axi4MasterSlave::*;

interface AxiMasterBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Bit#(addrWidth)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(1)     aresetn();
    method Bit#(tagWidth)     arid();
    method Bit#(4)     arlen();
    method Bit#(2)     arlock();
    method Bit#(3)     arprot();
    method Bit#(4)     arqos();
    method Action      arready(Bit#(1) v);
    method Bit#(2)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(addrWidth)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(tagWidth)     awid();
    method Bit#(4)     awlen();
    method Bit#(2)     awlock();
    method Bit#(3)     awprot();
    method Bit#(4)     awqos();
    method Action      awready(Bit#(1) v);
    method Bit#(2)     awsize();
    method Bit#(1)     awvalid();
    method Action      bid(Bit#(tagWidth) v);
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(dataWidth) v);
    method Action      rid(Bit#(tagWidth) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(dataWidth)     wdata();
    method Bit#(tagWidth)     wid();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(TDiv#(dataWidth,8))     wstrb();
    method Bit#(1)     wvalid();
    interface extraType   extra;
endinterface

interface HPType;
    method Bit#(3)     racount();
    method Bit#(8)     rcount();
    method Action      rdissuecap1en(Bit#(1) v);
    method Bit#(6)     wacount();
    method Bit#(8)     wcount();
    method Action      wrissuecap1en(Bit#(1) v);
endinterface

interface ACPType;
    method Action      aruser(Bit#(5) v);
    method Action      awuser(Bit#(5) v);
endinterface

interface AxiSlaveBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Action      araddr(Bit#(addrWidth) v);
    method Action      arburst(Bit#(2) v);
    method Action      arcache(Bit#(4) v);
    method Bit#(1)     aresetn();
    method Action      arid(Bit#(tagWidth) v);
    method Action      arlen(Bit#(4) v);
    method Action      arlock(Bit#(2) v);
    method Action      arprot(Bit#(3) v);
    method Action      arqos(Bit#(4) v);
    method Bit#(1)     arready();
    method Action      arsize(Bit#(2) v);
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(addrWidth) v);
    method Action      awburst(Bit#(2) v);
    method Action      awcache(Bit#(4) v);
    method Action      awid(Bit#(tagWidth) v);
    method Action      awlen(Bit#(4) v);
    method Action      awlock(Bit#(2) v);
    method Action      awprot(Bit#(3) v);
    method Action      awqos(Bit#(4) v);
    method Bit#(1)     awready();
    method Action      awsize(Bit#(2) v);
    method Action      awvalid(Bit#(1) v);
    method Bit#(tagWidth)     bid();
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(dataWidth)     rdata();
    method Bit#(1)     rlast();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(dataWidth) v);
    method Action      wid(Bit#(tagWidth) v);
    method Action      wlast(Bit#(1) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(TDiv#(dataWidth,8)) v);
    method Action      wvalid(Bit#(1) v);
    method Bit#(tagWidth)     rid();
    interface extraType   extra;
endinterface

interface Axi4MasterBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Bit#(addrWidth)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(1)     aresetn();
    method Bit#(tagWidth)     arid();
    method Bit#(8)     arlen();
    method Bit#(2)     arlock();
    method Bit#(3)     arprot();
    method Bit#(4)     arqos();
    method Action      arready(Bit#(1) v);
    method Bit#(3)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(addrWidth)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(tagWidth)     awid();
    method Bit#(8)     awlen();
    method Bit#(2)     awlock();
    method Bit#(3)     awprot();
    method Bit#(4)     awqos();
    method Action      awready(Bit#(1) v);
    method Bit#(3)     awsize();
    method Bit#(1)     awvalid();
    method Action      bid(Bit#(tagWidth) v);
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(dataWidth) v);
    method Action      rid(Bit#(tagWidth) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(dataWidth)     wdata();
    method Bit#(tagWidth)     wid();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(TDiv#(dataWidth,8))     wstrb();
    method Bit#(1)     wvalid();
    interface extraType   extra;
endinterface

interface Axi4MasterUntaggedBits#(numeric type addrWidth, numeric type dataWidth);
    method Bit#(addrWidth)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(8)     arlen();
    //method Bit#(2)     arlock();
    method Bit#(3)     arprot();
    //method Bit#(4)     arqos();
    method Action      arready(Bit#(1) v);
    method Bit#(3)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(addrWidth)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(8)     awlen();
    //method Bit#(2)     awlock();
    method Bit#(3)     awprot();
    //method Bit#(4)     awqos();
    method Action      awready(Bit#(1) v);
    method Bit#(3)     awsize();
    method Bit#(1)     awvalid();
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(dataWidth) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(dataWidth)     wdata();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(TDiv#(dataWidth,8))     wstrb();
    method Bit#(1)     wvalid();
endinterface

typeclass ToAxi4MasterBits#(type atype, type btype);
   function atype toAxi4MasterBits(btype b);
endtypeclass

instance ToAxi4MasterBits#(Axi4MasterBits#(addrWidth,dataWidth,tagWidth,Empty), Axi4MasterUntaggedBits#(addrWidth,dataWidth));
function Axi4MasterBits#(addrWidth,dataWidth,tagWidth,Empty) toAxi4MasterBits(Axi4MasterUntaggedBits#(addrWidth,dataWidth) m);
   return (interface Axi4MasterBits#(addrWidth,dataWidth,tagWidth,Empty);
    method araddr = m.araddr;
      method arburst = m.arburst;
      method arcache = m.arcache;
      //method aresetn = no_reset;
      method Bit#(tagWidth)     arid(); return 0; endmethod
      method arlen = m.arlen;
      //method arlock = m.arlock;
      method arprot = m.arprot;
      //method arqos = m.arqos;
      method arready = m.arready;
      method arsize = m.arsize;
      method arvalid = m.arvalid;
      method awaddr = m.awaddr;
      method awburst = m.awburst;
      method awcache = m.awcache;
      method Bit#(tagWidth)     awid(); return 0; endmethod
      method awlen = m.awlen;
      //method awlock = m.awlock;
      method awprot = m.awprot;
      //method awqos = m.awqos;
      method awready = m.awready;
      method awsize = m.awsize;
      method awvalid = m.awvalid;
      method Action      bid(Bit#(tagWidth) v); endmethod
      method bready = m.bready;
      method bresp = m.bresp;
      method bvalid = m.bvalid;
      method rdata = m.rdata;
      method Action      rid(Bit#(tagWidth) v); endmethod
      method rlast = m.rlast;
      method rready = m.rready;
      method rresp = m.rresp;
      method rvalid = m.rvalid;
      method wdata = m.wdata;
      method Bit#(tagWidth)     wid(); return 0; endmethod
      method wlast = m.wlast;
      method wready = m.wready;
      method wstrb = m.wstrb;
      method wvalid = m.wvalid;
      interface extra = ?;
      endinterface);
endfunction
endinstance

interface Axi4SlaveBits#(numeric type addrWidth, numeric type dataWidth, numeric type tagWidth, type extraType);
    method Action      araddr(Bit#(addrWidth) v);
    method Action      arburst(Bit#(2) v);
    method Action      arcache(Bit#(4) v);
    method Bit#(1)     aresetn();
    method Action      arid(Bit#(tagWidth) v);
    method Action      arlen(Bit#(8) v);
    method Action      arlock(Bit#(2) v);
    method Action      arprot(Bit#(3) v);
    method Action      arqos(Bit#(4) v);
    method Bit#(1)     arready();
    method Action      arsize(Bit#(3) v);
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(addrWidth) v);
    method Action      awburst(Bit#(2) v);
    method Action      awcache(Bit#(4) v);
    method Action      awid(Bit#(tagWidth) v);
    method Action      awlen(Bit#(8) v);
    method Action      awlock(Bit#(2) v);
    method Action      awprot(Bit#(3) v);
    method Action      awqos(Bit#(4) v);
    method Bit#(1)     awready();
    method Action      awsize(Bit#(3) v);
    method Action      awvalid(Bit#(1) v);
    method Bit#(tagWidth)     bid();
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(dataWidth)     rdata();
    method Bit#(1)     rlast();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(dataWidth) v);
    method Action      wid(Bit#(tagWidth) v);
    method Action      wlast(Bit#(1) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(TDiv#(dataWidth,8)) v);
    method Action      wvalid(Bit#(1) v);
    method Bit#(tagWidth)     rid();
    interface extraType   extra;
endinterface

interface Axi4SlaveLiteBits#(numeric type addrWidth, numeric type dataWidth);
    method Action      araddr(Bit#(addrWidth) v);
    method Bit#(1)     arready();
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(addrWidth) v);
    method Bit#(1)     awready();
    method Action      awvalid(Bit#(1) v);
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(dataWidth)     rdata();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(dataWidth) v);
    method Bit#(1)     wready();
    method Action      wvalid(Bit#(1) v);
endinterface

typeclass ToAxi4SlaveBits#(type atype, type btype);
   function atype toAxi4SlaveBits(btype b);
endtypeclass

module mkAxiFifoF(FIFOF#(t)) provisos(Bits#(t, tSz));
  Ehr#(2, t) da <- mkEhr(?);
  Ehr#(2, Bool) va <- mkEhr(False);
  Ehr#(2, t) db <- mkEhr(?);
  Ehr#(2, Bool) vb <- mkEhr(False);

  rule canon if(vb[1] && !va[1]);
    da[1] <= db[1];
    va[1] <= True;
    vb[1] <= False;
  endrule

  method Bool notFull = !vb[0];

  method Action enq(t x) if(!vb[0]);
    db[0] <= x;
    vb[0] <= True;
  endmethod

  method Bool notEmpty = va[0];

  method Action deq if (va[0]);
    va[0] <= False;
  endmethod

  // no implicit guard, to simplify rule guards below
  method t first;
    return da[0];
  endmethod

  // conflicts with enq, deq, but we do not call it
  method Action clear;
    vb[0] <= False;
    va[0] <= False;
  endmethod
endmodule

typeclass MkPhysMemSlave#(type srctype, numeric type addrWidth, numeric type dataWidth);
   module mkPhysMemSlave#(srctype axiSlave)(PhysMemSlave#(addrWidth,dataWidth));
endtypeclass

instance MkPhysMemSlave#(Axi4SlaveLiteBits#(axiAddrWidth,dataWidth),addrWidth,dataWidth)
      provisos (Add#(axiAddrWidth,a__,addrWidth));
   module mkPhysMemSlave#(Axi4SlaveLiteBits#(axiAddrWidth,dataWidth) axiSlave)(PhysMemSlave#(addrWidth,dataWidth));
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) arfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) rfifo <- mkAxiFifoF();
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) awfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) wfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) bfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) rtagfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) wtagfifo <- mkAxiFifoF();

      rule rl_arvalid_araddr;
	 axiSlave.arvalid(pack(arfifo.notEmpty && rtagfifo.notFull));
	 let addr = 0;
	 if (arfifo.notEmpty)
	    addr = truncate(arfifo.first.addr);
	 axiSlave.araddr(addr);
      endrule
      rule rl_arfifo if (axiSlave.arready() == 1);
	 let req <- toGet(arfifo).get();
	 rtagfifo.enq(req.tag);
      endrule
      rule rl_rready;
	 axiSlave.rready(pack(rfifo.notFull && rtagfifo.notEmpty));
      endrule
      rule rl_rdata if (axiSlave.rvalid() == 1);
	 let rtag <- toGet(rtagfifo).get();
	 rfifo.enq(MemData { data: axiSlave.rdata(), tag: rtag } );
      endrule

      rule rl_awvalid_awaddr;
	 axiSlave.awvalid(pack(awfifo.notEmpty && wtagfifo.notFull));
	 let addr = 0;
	 if (awfifo.notEmpty)
	    addr = truncate(awfifo.first.addr);
	 axiSlave.awaddr(addr);
      endrule
      rule rl_awfifo if (axiSlave.awready() == 1);
	 let req <- toGet(awfifo).get();
	 wtagfifo.enq(req.tag);
      endrule
      rule rl_wvalid;
	 axiSlave.wvalid(pack(wfifo.notEmpty));
	 let wdata = 0;
	 if (wfifo.notEmpty)
	    wdata = wfifo.first.data;
	 axiSlave.wdata(wdata);
      endrule
      rule rl_wdata if (axiSlave.wready() == 1);
	 let md <- toGet(wfifo).get();
      endrule
      rule rl_bready;
	 axiSlave.bready(pack(wtagfifo.notEmpty && bfifo.notFull));
      endrule
      rule rl_done if (axiSlave.bvalid() == 1);
	 let tag <- toGet(wtagfifo).get();
	 bfifo.enq(tag);
      endrule

      interface PhysMemReadServer read_server;
	 interface Put readReq = toPut(arfifo);
	 interface Get readData = toGet(rfifo);
      endinterface
      interface PhysMemWriteServer write_server;
	 interface Put writeReq = toPut(awfifo);
	 interface Put writeData = toPut(wfifo);
	 interface Get writeDone = toGet(bfifo);
      endinterface
   endmodule
endinstance

//FIXME burst transfers
instance MkPhysMemSlave#(Axi4SlaveBits#(axiAddrWidth,dataWidth,tagWidth,Empty),addrWidth,dataWidth)
      provisos (Add#(axiAddrWidth,a__,addrWidth),
		Add#(b__, tagWidth, 6));
   module mkPhysMemSlave#(Axi4SlaveBits#(axiAddrWidth,dataWidth,tagWidth,Empty) axiSlave)(PhysMemSlave#(addrWidth,dataWidth));
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) arfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) rfifo <- mkAxiFifoF();
      FIFOF#(PhysMemRequest#(addrWidth,dataWidth)) awfifo <- mkAxiFifoF();
      FIFOF#(MemData#(dataWidth)) wfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) bfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) rtagfifo <- mkAxiFifoF();
      FIFOF#(Bit#(MemTagSize)) wtagfifo <- mkAxiFifoF();

      rule rl_arvalid_araddr;
	 axiSlave.arvalid(pack(arfifo.notEmpty && rtagfifo.notFull));
	 Bit#(axiAddrWidth) addr = 0;
	 Bit#(tagWidth) id = 0;
	 if (arfifo.notEmpty) begin
	    addr = truncate(arfifo.first.addr);
	    id = truncate(arfifo.first.tag);
	 end
	 axiSlave.araddr(addr);
	 axiSlave.arid(id);
	 axiSlave.arsize(axiBusSize(valueOf(dataWidth)));
	 axiSlave.arburst(2'b01);
	 axiSlave.arprot(3'b000);
	 axiSlave.arcache(4'b0011);
      endrule
      rule rl_arfifo if (axiSlave.arready() == 1);
	 let req <- toGet(arfifo).get();
	 rtagfifo.enq(req.tag);
      endrule
      rule rl_rready;
	 axiSlave.rready(pack(rfifo.notFull && rtagfifo.notEmpty));
      endrule
      rule rl_rdata if (axiSlave.rvalid() == 1);
	 let rtag <- toGet(rtagfifo).get();
	 rfifo.enq(MemData { data: axiSlave.rdata(), tag: rtag } );
      endrule

      rule rl_awvalid_awaddr;
	 axiSlave.awvalid(pack(awfifo.notEmpty && wtagfifo.notFull));
	 Bit#(axiAddrWidth) addr = 0;
	 Bit#(tagWidth) id = 0;
	 if (awfifo.notEmpty) begin
	    addr = truncate(awfifo.first.addr);
	    id = truncate(awfifo.first.tag);
	 end
	 axiSlave.awaddr(addr);
	 axiSlave.awid(id);
	 axiSlave.awsize(axiBusSize(valueOf(dataWidth)));
	 axiSlave.awburst(2'b01);
	 axiSlave.awprot(3'b000);
	 axiSlave.awcache(4'b0011);
      endrule
      rule rl_awfifo if (axiSlave.awready() == 1);
	 let req <- toGet(awfifo).get();
	 wtagfifo.enq(req.tag);
      endrule
      rule rl_wvalid;
	 axiSlave.wvalid(pack(wfifo.notEmpty));
	 let wdata = 0;
	 if (wfifo.notEmpty)
	    wdata = wfifo.first.data;
	 axiSlave.wdata(wdata);
      endrule
      rule rl_wdata if (axiSlave.wready() == 1);
	 let md <- toGet(wfifo).get();
      endrule
      rule rl_bready;
	 axiSlave.bready(pack(wtagfifo.notEmpty && bfifo.notFull));
      endrule
      rule rl_done if (axiSlave.bvalid() == 1);
	 let tag <- toGet(wtagfifo).get();
	 bfifo.enq(tag);
      endrule

      interface PhysMemReadServer read_server;
	 interface Put readReq = toPut(arfifo);
	 interface Get readData = toGet(rfifo);
      endinterface
      interface PhysMemWriteServer write_server;
	 interface Put writeReq = toPut(awfifo);
	 interface Put writeData = toPut(wfifo);
	 interface Get writeDone = toGet(bfifo);
      endinterface
   endmodule
endinstance

module mkMemReadClient#(Reg#(Bit#(32)) objId, Axi4MasterBits#(32,dataWidth,MemTagSize,Empty) m)(MemReadClient#(dataWidth));

   Wire#(Bit#(1)) arready <- mkDWire(0);
   Wire#(Bit#(1)) rvalid <- mkDWire(0);
   Wire#(Bit#(MemTagSize)) rid <- mkDWire(0);
   Wire#(Bit#(2)) rresp <- mkDWire(0);
   Wire#(Bit#(dataWidth)) rdata <- mkDWire(0);
   Wire#(Bit#(1)) rlast <- mkDWire(0);

   FIFOF#(MemRequest)          arfifo <- mkCFFIFOF();
   FIFOF#(MemData#(dataWidth))  rfifo <- mkCFFIFOF();

   rule rl_araddr if (m.arvalid() == 1);
      let addr = m.araddr();
      let burstLenBytes = (extend(m.arlen())+1)*fromInteger(valueOf(TDiv#(dataWidth,8)));
      arfifo.enq(MemRequest { sglId: objId, offset: extend(addr), burstLen: burstLenBytes, tag: extend(m.arid()) });
   endrule
   rule handshake_ar;
        m.arready(pack(arfifo.notFull()));
   endrule

   rule rl_rdata if (m.rready() == 1);
      let md <- toGet(rfifo).get();
      rdata <= md.data;
      rlast <= pack(md.last);
      rresp <= 0;
      rid <= truncate(md.tag);
   endrule
   rule handshake_rdata;
      m.rvalid(pack(rfifo.notEmpty()));
      m.rid(rid);
      m.rresp(rresp);
      m.rdata(rdata);
      m.rlast(rlast);
   endrule

   interface Get readReq = toGet(arfifo);
   interface Put readData = toPut(rfifo);
endmodule

module mkMemWriteClient#(Reg#(Bit#(32)) objId, Axi4MasterBits#(32,dataWidth,MemTagSize,Empty) m)(MemWriteClient#(dataWidth));

   Wire#(Bit#(1)) awready <- mkDWire(0);
   Wire#(Bit#(1)) wready <- mkDWire(0);
   Wire#(Bit#(1)) bvalid <- mkDWire(0);
   Wire#(Bit#(MemTagSize)) bid <- mkDWire(0);
   Wire#(Bit#(2)) bresp <- mkDWire(0);

   FIFOF#(MemRequest)       awfifo   <- mkCFFIFOF();
   FIFOF#(MemData#(dataWidth)) wfifo <- mkCFFIFOF();
   FIFOF#(Bit#(MemTagSize))    bfifo <- mkCFFIFOF();

   rule rl_awaddr if (m.awvalid() == 1);
      let addr = m.awaddr();
      let burstLenBytes = (extend(m.awlen())+1)*fromInteger(valueOf(TDiv#(dataWidth,8)));
      awfifo.enq(MemRequest { sglId: objId, offset: extend(addr), burstLen: burstLenBytes, tag: extend(m.awid()) });
   endrule
   rule handshake_awaddr;
      m.awready(pack(awfifo.notFull()));
   endrule

   rule rl_wdata if (m.wvalid() == 1);
      wfifo.enq(MemData { data: m.wdata(), last: unpack(m.wlast()), tag: extend(m.wid()) });
   endrule
   rule handshake_wdata;
      m.wready(pack(wfifo.notFull()));
   endrule

   rule rl_bresp if (m.bready() == 1);
      let tag <- toGet(bfifo).get();
      bresp <= 0;
      bid <= truncate(tag);
   endrule
   rule handshake_b;
        m.bvalid(pack(bfifo.notEmpty()));
        m.bid(bid);
        m.bresp(bresp);
   endrule

   interface Get writeReq = toGet(awfifo);
   interface Get writeData = toGet(wfifo);
   interface Put writeDone = toPut(bfifo);
endmodule

typedef AxiMasterBits#(32,32,12,Empty) Pps7Maxigp;
typedef AxiSlaveBits#(32,32,6,Empty) Pps7Saxigp;
typedef AxiSlaveBits#(32,64,6,HPType) Pps7Saxihp;
typedef AxiSlaveBits#(32,64,3,ACPType) Pps7Saxiacp;