no need to pass mkSynthesizeablePortalTop as an input to mkPcieTopFromPortal, simplifying

Change-Id: Ib12e3fd6c09309055ab3af1c935d55aee048b426

// 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 AxiSlaveEngine    :: *;
import MemMasterEngine   :: *;
import AxiMasterSlave    :: *;
import AxiDma            :: *;
import PcieCsr           :: *;
import MemSlave          :: *;
import Dma               :: *;

`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

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();
   interface ipins       pins;
endinterface

interface PcieHost#(numeric type dsz);
   interface Vector#(16,MSIX_Entry) msixEntry;
   interface MemMaster#(32,32) master;
   interface Axi3Slave#(40,dsz,6)  slave;
   interface Put#(Tuple2#(Bit#(64),Bit#(32))) interruptRequest;
endinterface

module [Module] mkPcieHost #(Clock epClock250, Reset epReset250, PciId my_pciId,
Server#(TLPData#(8), TLPData#(8)) ep_tlp)(PcieHost#(dsz))
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;
   AxiSlaveEngine#(dsz) sEngine <- mkAxiSlaveEngine(my_pciId);

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

   // 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);

   PcieTracer  traceif <- mkPcieTracer();
   mkConnection(gb.pci, traceif.pci);
   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 = sEngine.slave;
   interface interruptRequest = mvec[portPortal].interruptRequest;
endmodule: mkPcieHost

(* synthesize *)
module mkSynthesizeablePortalTop(PortalTop#(40, DataBusWidth, Empty, NumberOfMasters));
   let top <- mkPortalTop();
   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 _ep.user.clk_out, 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) pciehost <- mkPcieHost(epClock250, epReset250,
         PciId{ bus:  _ep.cfg.bus_number(), dev: _ep.cfg.device_number(), func: _ep.cfg.function_number()},
         _ep.tlp, clocked_by epClock125, reset_by epReset125);

   mkConnection(pciehost.master, portalTop.slave, clocked_by epClock125, reset_by epReset125);
   if (valueOf(NumberOfMasters) > 0) begin
      Axi3Master#(40,DataBusWidth,6) m_axis <- mkAxiDmaMaster(portalTop.masters[0], clocked_by epClock125, reset_by epReset125);
      mkConnection(m_axis, pciehost.slave, clocked_by epClock125, reset_by epReset125);
   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 AxiSlaveEngine    :: *;
import MemMasterEngine   :: *;
import AxiMasterSlave    :: *;
import AxiDma            :: *;
import PcieCsr           :: *;
import MemSlave          :: *;
import Dma               :: *;

`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

typedef (function Module#(PortalTop#(40, dsz, ipins, `NumberOfMasters)) mkPortalTop()) MkPortalTop#(numeric type dsz, type ipins);

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

interface PcieHost#(numeric type dsz);
   interface Vector#(16,MSIX_Entry) msixEntry;
   interface MemMaster#(32,32) master;
   interface Axi3Slave#(40,dsz,6)  slave;
   interface Put#(Tuple2#(Bit#(64),Bit#(32))) interruptRequest;
endinterface

module [Module] mkPcieHost #(Clock epClock250, Reset epReset250, PciId my_pciId,
Server#(TLPData#(8), TLPData#(8)) ep_tlp)(PcieHost#(dsz))
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;
   AxiSlaveEngine#(dsz) sEngine <- mkAxiSlaveEngine(my_pciId);

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

   // 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);

   PcieTracer  traceif <- mkPcieTracer();
   mkConnection(gb.pci, traceif.pci);
   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 = sEngine.slave;
   interface interruptRequest = mvec[portPortal].interruptRequest;
endmodule: mkPcieHost

(* 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,
				      MkPortalTop#(dsz, ipins) mkPortalTop)
   (PcieTop#(ipins))
   provisos (Mul#(TDiv#(dsz, 32), 32, dsz),
	     Add#(b__, 32, dsz),
	     Add#(c__, dsz, 256),
	     Add#(d__, TMul#(8, TDiv#(dsz, 32)), 64),
	     Add#(e__, TMul#(32, TDiv#(dsz, 32)), 256),
	     Add#(f__, TDiv#(dsz, 32), 8),
	     Mul#(TDiv#(dsz, 8), 8, dsz)
      );

   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 _ep.user.clk_out, reset_by user_reset_n);
   Clock epClock125 = clkgen.clkout0; /* half speed user_clk */
   Reset epReset125 <- mkAsyncReset(4, user_reset_n, epClock125);

   let portalTop <- mkPortalTop(clocked_by epClock125, reset_by epReset125);
   PcieHost#(dsz) pciehost <- mkPcieHost(epClock250, epReset250,
         PciId{ bus:  _ep.cfg.bus_number(), dev: _ep.cfg.device_number(), func: _ep.cfg.function_number()},
         _ep.tlp, clocked_by epClock125, reset_by epReset125);

   mkConnection(pciehost.master, portalTop.slave, clocked_by epClock125, reset_by epReset125);
   if (`NumberOfMasters > 0) begin
      Vector#(`NumberOfMasters,Axi3Master#(40,dsz,6)) m_axis;
      m_axis[0] <- mkAxiDmaMaster(portalTop.masters[0], clocked_by epClock125, reset_by epReset125);
      mkConnection(m_axis[0], pciehost.slave, clocked_by epClock125, reset_by epReset125);
   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

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

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,mkSynthesizeablePortalTop);
   return top;
endmodule: mkPcieTop


fixed compilation problems with Jamey's help

// 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.

// The Jtag state machine is copied from the Bluespec Small Examples Tap.bsv

interface JtagReg#(type a);
   method Action update();
   method Action capture();
   method Action shift(bit d);
   interface Reg#(a) r;
   interface ReadOnly#(bit) tdo;
endinterface

// can I pass in an initial value for the register?

module mkJtagReg(JtagReg#(a))
   provisos(Bits#(a,asize), Add#(1,__a,asize));

   Reg#(a) dreg <- mkReg(?);
   Reg#(a) sreg <- mkReg(?);
   Reg#(bit) oreg <- mkReg(?);

   method Action update ();
      dreg <= sreg;
   endmethod

   method Action capture ();
      sreg <= dreg;
   endmethod

   method Action shift (bit d);
      Bit#(asize) svalue = pack(sreg);
      svalue = {d, svalue[(valueof(asize)-1):1]};
      sreg <= unpack(svalue);
      oreg <= svalue[0];
   endmethod

   interface r = dreg;
   interface tdo = regToReadOnly(oreg);

endmodule

module mkReadOnlyJtagReg#(a v)(JtagReg#(a))
   provisos(Bits#(a,asize), Add#(1,__a,asize));

   Reg#(a) dreg <- mkReg(v);
   Reg#(a) sreg <- mkReg(?);
   Reg#(bit) oreg <- mkReg(?);

   method Action update ();

   endmethod

   method Action capture ();
      sreg <= dreg;
   endmethod

   method Action shift (bit d);
      Bit#(asize) svalue = pack(sreg);
      svalue = {d, svalue[(valueof(asize)-1):1]};
      sreg <= unpack(svalue);
      oreg <= svalue[0];
   endmethod

   interface r = dreg;
   interface tdo = regToReadOnly(oreg);

endmodule

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

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

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

// The Jtag state machine is copied from the Bluespec Small Examples Tap.bsv

interface SerialReg#(type regtype);
   method Action update();
   method Action capture();
   method Action shift(bit d);
   interface Reg#(regtype) r;
   interface Readonly#(bit) tdo;
endinterface

// can I pass in an initial value for the register?

module mkSerialReg#(type regtype)(SerialReg);

   Reg#(regtype) dreg <- mkReg(?);
   Reg#(regtype) sreg <- mkReg(?);
   Reg#(bit) oreg <- mkReg(?);

   method Action update ();
      dreg <= sreg;
   endmethod

   method Action capture ();
      sreg <= dreg;
   endmethod

   method Action shift (bit d);
      sreg <= {d, (sreg >> 1)};
      oreg <= sreg[0];
   endmethod

   interface r = dreg;
   interface tdo = regToReadOnly(oreg);

endmodule

module mkReadOnlySerialReg#(type regtype, regtype v)(SerialReg);

   Reg#(regtype) dreg <- mkReg(v);
   Reg#(regtype) sreg <- mkReg(?);
   Reg#(bit) oreg <- mkReg(?);

   method Action update ();
      // dreg <= sreg;
   endmethod

   method Action capture ();
      sreg <= dreg;
   endmethod

   method Action shift (bit d);
      sreg <= {d, (sreg >> 1)};
      oreg <= sreg[0];
   endmethod

   interface r = dreg;
   interface tdo = regToReadOnly(oreg);

endmodule


PhysAddrWidth changes

PhysAddrWidth changes

// 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 GetPut::*;
import Connectable :: *;
import FIFO::*;
import Portal::*;
import HostInterface::*;
import CtrlMux::*;
import Leds::*;
import MemPortal::*;
import MemServer::*;

// defined by user
import DDSTestInterfaces::*;
import DDSTest::*;
import ChannelSelectTestInterfaces::*;
import ChannelSelectTest::*;

// generated by tool
import ChannelSelectTestRequest::*;
import ChannelSelectTestIndication::*;
import DDSTestRequest::*;
import DDSTestIndication::*;

typedef enum { ChannelSelectTestIndication, ChannelSelectTestRequest, DDSTestIndication, DDSTestRequest} IfcNames deriving (Eq,Bits);

module mkConnectalTop(StdConnectalTop#(PhysAddrWidth));

   ChannelSelectTestIndicationProxy channelSelectTestIndicationProxy <- mkChannelSelectTestIndicationProxy(ChannelSelectTestIndication);

   ChannelSelectTestRequest channelSelectTestRequest <- mkChannelSelectTestRequest(channelSelectTestIndicationProxy.ifc);

   ChannelSelectTestRequestWrapper channelSelectTestRequestWrapper <- mkChannelSelectTestRequestWrapper(ChannelSelectTestRequest, channelSelectTestRequest);

   DDSTestIndicationProxy ddsTestIndicationProxy <- mkDDSTestIndicationProxy(DDSTestIndication);
   DDSTestRequest ddsTestRequest <- mkDDSTestRequest(ddsTestIndicationProxy.ifc);
   DDSTestRequestWrapper ddsTestRequestWrapper <- mkDDSTestRequestWrapper(DDSTestRequest, ddsTestRequest);

   Vector#(4,StdPortal) portals;
   portals[0] = channelSelectTestRequestWrapper.portalIfc;
   portals[1] = channelSelectTestIndicationProxy.portalIfc;
   portals[2] = ddsTestRequestWrapper.portalIfc;
   portals[3] = ddsTestIndicationProxy.portalIfc;
   let ctrl_mux <- mkSlaveMux(portals);

   interface interrupt = getInterruptVector(portals);
   interface slave = ctrl_mux;
   interface masters = nil;
   interface leds = default_leds;

endmodule : mkConnectalTop

// 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.

// bsv libraries
import Vector::*;
import GetPut::*;
import Connectable :: *;
import FIFO::*;

// portz libraries
import Portal::*;
import CtrlMux::*;
import Portal::*;
import Leds::*;

import MemPortal::*;
import MemTypes::*;
import MemServer::*;

// defined by user
import DDSTestInterfaces::*;
import DDSTest::*;
import ChannelSelectTestInterfaces::*;
import ChannelSelectTest::*;

// generated by tool
import ChannelSelectTestRequest::*;
import ChannelSelectTestIndication::*;
import DDSTestRequest::*;
import DDSTestIndication::*;

typedef enum { ChannelSelectTestIndication, ChannelSelectTestRequest, DDSTestIndication, DDSTestRequest} IfcNames deriving (Eq,Bits);

module mkConnectalTop(StdConnectalTop#(PhysAddrWidth));

   ChannelSelectTestIndicationProxy channelSelectTestIndicationProxy <- mkChannelSelectTestIndicationProxy(ChannelSelectTestIndication);

   ChannelSelectTestRequest channelSelectTestRequest <- mkChannelSelectTestRequest(channelSelectTestIndicationProxy.ifc);

   ChannelSelectTestRequestWrapper channelSelectTestRequestWrapper <- mkChannelSelectTestRequestWrapper(ChannelSelectTestRequest, channelSelectTestRequest);

   DDSTestIndicationProxy ddsTestIndicationProxy <- mkDDSTestIndicationProxy(DDSTestIndication);
   DDSTestRequest ddsTestRequest <- mkDDSTestRequest(ddsTestIndicationProxy.ifc);
   DDSTestRequestWrapper ddsTestRequestWrapper <- mkDDSTestRequestWrapper(DDSTestRequest, ddsTestRequest);

   Vector#(4,StdPortal) portals;
   portals[0] = channelSelectTestRequestWrapper.portalIfc;
   portals[1] = channelSelectTestIndicationProxy.portalIfc;
   portals[2] = ddsTestRequestWrapper.portalIfc;
   portals[3] = ddsTestIndicationProxy.portalIfc;
   let ctrl_mux <- mkSlaveMux(portals);

   interface interrupt = getInterruptVector(portals);
   interface slave = ctrl_mux;
   interface masters = nil;
   interface leds = default_leds;

endmodule : mkConnectalTop


ordering for clarity of tutorial

ordering for clarity of tutorial

// 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 Leds::*;
import Vector::*;
import FIFOF::*;
import Arith::*;

interface HBridgeCtrlRequest;
   method Action ctrl(Vector#(2,Bit#(11)) power, Vector#(2,Bit#(1)) direction);
endinterface

interface HBridgeCtrlIndication;
   method Action hbc_event(Bit#(32) e);
endinterface

interface HBridge2;
   method Bit#(2) hbridge0();
   method Bit#(2) hbridge1();
endinterface

interface Controller;
   interface HBridgeCtrlRequest req;
   interface HBridge2 pins;
   interface LEDS leds;
endinterface

typedef enum {Stopped, Started} HBridgeCtrlEvent deriving (Eq,Bits);

module mkController#(HBridgeCtrlIndication ind)(Controller);

   Vector#(2, Reg#(Bit#(1))) direction <- replicateM(mkReg(0));
   Vector#(2, Reg#(Bit#(1)))   enabled <- replicateM(mkReg(0));
   Vector#(2, Reg#(Bit#(11)))    power <- replicateM(mkReg(0));
   Vector#(2, Reg#(Bool))           pz <- replicateM(mkReg(True));
   FIFOF#(Bit#(32))         event_fifo <- mkSizedFIFOF(4);
   Bit#(8) leds_val =  extend({direction[0],direction[1]});

   // frequency of design: 100 mHz
   // frequency of PWM System: 2 kHz
   // 2k design cycles == 1 PWM cycle
   Reg#(Bit#(11)) fcnt <- mkReg(0);

   rule detect_event;
      Vector#(2,Bool) npz;
      for(int i = 0; i < 2; i=i+1)
	 npz[i] = power[i]==0;
      Bool started =  fold(booland, readVReg(pz)) && !fold(booland, npz);
      Bool stopped = !fold(booland, readVReg(pz)) &&  fold(booland, npz);
      Bit#(32) e = 0;
      e = e | (extend(pack(stopped)) << pack(Stopped));
      e = e | (extend(pack(started)) << pack(Started));
      if (e != 0 && event_fifo.notFull)
	 event_fifo.enq(e);
      writeVReg(pz,npz);
   endrule

   rule report_event;
      ind.hbc_event(event_fifo.first);
      event_fifo.deq;
   endrule

   rule pwm;
      for(Integer i = 0; i < 2; i=i+1)
	 enabled[i] <= ((power[i] > 0) && (fcnt <= power[i])) ? 1 : 0;
      fcnt <= fcnt+1;
   endrule

   interface HBridgeCtrlRequest req;
      method Action ctrl(Vector#(2,Bit#(11)) p, Vector#(2,Bit#(1)) d);
	 for(Integer i = 0; i < 2; i=i+1) begin
	    direction[i] <= d[i];
	    power[i]     <= p[i];
	 end
      endmethod
   endinterface

   interface HBridge2 pins;
      method Bit#(2) hbridge0();
	 return {enabled[0],direction[0]};
      endmethod
      method Bit#(2) hbridge1();
	 return {enabled[1],direction[1]};
      endmethod
   endinterface

   interface LEDS leds;
      method Bit#(LedsWidth) leds() = leds_val;
   endinterface

endmodule

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

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

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

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

import Leds::*;
import Vector::*;
import FIFOF::*;
import Arith::*;

interface HBridge2;
   method Bit#(2) hbridge0();
   method Bit#(2) hbridge1();
endinterface

interface HBridgeCtrlRequest;
   method Action ctrl(Vector#(2,Bit#(11)) power, Vector#(2,Bit#(1)) direction);
endinterface

typedef enum {Stopped, Started} HBridgeCtrlEvent deriving (Eq,Bits);

interface HBridgeCtrlIndication;
   method Action hbc_event(Bit#(32) e);
endinterface

interface Controller;
   interface HBridgeCtrlRequest req;
   interface HBridge2 pins;
   interface LEDS leds;
endinterface

module mkController#(HBridgeCtrlIndication ind)(Controller);

   Vector#(2, Reg#(Bit#(1))) direction <- replicateM(mkReg(0));
   Vector#(2, Reg#(Bit#(1)))   enabled <- replicateM(mkReg(0));
   Vector#(2, Reg#(Bit#(11)))    power <- replicateM(mkReg(0));
   Vector#(2, Reg#(Bool))           pz <- replicateM(mkReg(True));
   FIFOF#(Bit#(32))         event_fifo <- mkSizedFIFOF(4);
   Bit#(8) leds_val =  extend({direction[0],direction[1]});

   // frequency of design: 100 mHz
   // frequency of PWM System: 2 kHz
   // 2k design cycles == 1 PWM cycle
   Reg#(Bit#(11)) fcnt <- mkReg(0);

   rule detect_event;
      Vector#(2,Bool) npz;
      for(int i = 0; i < 2; i=i+1)
	 npz[i] = power[i]==0;
      Bool started =  fold(booland, readVReg(pz)) && !fold(booland, npz);
      Bool stopped = !fold(booland, readVReg(pz)) &&  fold(booland, npz);
      Bit#(32) e = 0;
      e = e | (extend(pack(stopped)) << pack(Stopped));
      e = e | (extend(pack(started)) << pack(Started));
      if (e != 0 && event_fifo.notFull)
	 event_fifo.enq(e);
      writeVReg(pz,npz);
   endrule

   rule report_event;
      ind.hbc_event(event_fifo.first);
      event_fifo.deq;
   endrule

   rule pwm;
      for(Integer i = 0; i < 2; i=i+1)
	 enabled[i] <= ((power[i] > 0) && (fcnt <= power[i])) ? 1 : 0;
      fcnt <= fcnt+1;
   endrule

   interface HBridgeCtrlRequest req;
      method Action ctrl(Vector#(2,Bit#(11)) p, Vector#(2,Bit#(1)) d);
	 for(Integer i = 0; i < 2; i=i+1) begin
	    direction[i] <= d[i];
	    power[i]     <= p[i];
	 end
      endmethod
   endinterface

   interface HBridge2 pins;
      method Bit#(2) hbridge0();
	 return {enabled[0],direction[0]};
      endmethod
      method Bit#(2) hbridge1();
	 return {enabled[1],direction[1]};
      endmethod
   endinterface

   interface LEDS leds;
      method Bit#(LedsWidth) leds() = leds_val;
   endinterface

endmodule


removed dead code

removed dead code

// Copyright (c) 2008- 2009 Bluespec, Inc.  All rights reserved.
// $Revision$
// $Date$

package SceMiKintex7PCIEQrc;

// PCI-Express for Kintex 7
// FPGAs.

import Clocks       :: *;
import Connectable  :: *;
import TieOff       :: *;
import DefaultValue :: *;
import XilinxPCIE   :: *;
import XilinxCells  :: *;
import BlueNoC :: *;
import PCIEtoBNoCBridgeQrc::*;

// from SceMiDefines
typedef 4 BPB;

// Interface wrapper for PCIE
interface K7PCIEQrcIfc#(numeric type lanes);
   interface PCIE_EXP#(lanes) pcie;
   (* always_ready *)
   method Bool isLinkUp();
endinterface

// This module builds the transactor hierarchy, the clock
// generation logic and the PCIE-to-port logic.
(* no_default_clock, no_default_reset *)
module buildPCIEK7Qrc#( Clock pci_sys_clk_p
		       , Clock pci_sys_clk_n
		       , Reset pci_sys_reset
		       , Clock ref_clk
		       )
		       (K7PCIEQrcIfc#(lanes))
   provisos(Add#(1,_,lanes), SelectKintex7PCIE#(lanes));

   if (valueOf(lanes) != 8)
      errorM("Only 8-lane PCIe is supported on K7.");

   // Buffer clocks and reset before they are used
   Clock sys_clk_buf <- mkClockIBUFDS_GTE2(True, pci_sys_clk_p, pci_sys_clk_n);

   // Instantiate the PCIE endpoint
   PCIExpressK7#(lanes) _ep <- mkPCIExpressEndpointK7( defaultValue
                                                     , clocked_by sys_clk_buf
                                                     , reset_by pci_sys_reset
                                                     );
   mkTieOff(_ep.cfg);
   mkTieOff(_ep.cfg_interrupt);
   mkTieOff(_ep.cfg_err);
   mkTieOff(_ep.pl);

   // note our PCI ID
   PciId my_id = PciId { bus:  _ep.cfg.bus_number()
		       , dev:  _ep.cfg.device_number()
		       , func: _ep.cfg.function_number()
		       };

   // The PCIE endpoint is processing TLPWord#(8)s at 250MHz.  The
   // NoC bridge is accepting TLPWord#(16)s at 125 MHz. The
   // connection between the endpoint and the NoC contains GearBox
   // instances for the TLPWord#(8)@250 <--> TLPWord#(16)@125
   // conversion.

   // The PCIe endpoint exports full (250MHz) and half-speed (125MHz) clocks
   Clock epClock250 = _ep.trn.clk;
   Reset epReset250 <- mkAsyncReset(4, _ep.trn.reset_n, epClock250);
   Clock epClock125 = _ep.trn.clk2;
   Reset epReset125 <- mkAsyncReset(4, _ep.trn.reset_n, epClock125);

   // Extract some status info from the PCIE endpoint. These values are
   // all in the epClock250 domain, so we have to cross them into the
   // epClock125 domain.

   Bool link_is_up = _ep.trn.link_up();
   UInt#(13) max_read_req_bytes_250       = 128 << _ep.cfg.dcommand[14:12];
   UInt#(13) max_payload_bytes_250        = 128 << _ep.cfg.dcommand[7:5];
   UInt#(8)  read_completion_boundary_250 = 64 << _ep.cfg.lcommand[3];
   Bool      msix_enable_250              = (_ep.cfg_interrupt.msixenable() == 1);
   Bool      msix_masked_250              = (_ep.cfg_interrupt.msixfm()     == 1);

   CrossingReg#(UInt#(13)) max_rd_req_cr  <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(UInt#(13)) max_payload_cr <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(UInt#(8))  rcb_cr         <- mkNullCrossingReg(epClock125, 128,   clocked_by epClock250, reset_by epReset250);
   CrossingReg#(Bool)      msix_enable_cr <- mkNullCrossingReg(epClock125, False, clocked_by epClock250, reset_by epReset250);
   CrossingReg#(Bool)      msix_masked_cr <- mkNullCrossingReg(epClock125, True,  clocked_by epClock250, reset_by epReset250);

   Reg#(UInt#(13)) max_read_req_bytes <- mkReg(128,   clocked_by epClock125, reset_by epReset125);
   Reg#(UInt#(13)) max_payload_bytes  <- mkReg(128,   clocked_by epClock125, reset_by epReset125);
   Reg#(Bit#(7))   rcb_mask           <- mkReg(7'h3f, clocked_by epClock125, reset_by epReset125);
   Reg#(Bool)      msix_enable        <- mkReg(False, clocked_by epClock125, reset_by epReset125);
   Reg#(Bool)      msix_masked        <- mkReg(True,  clocked_by epClock125, reset_by epReset125);

   (* fire_when_enabled, no_implicit_conditions *)
   rule cross_config_values;
      max_rd_req_cr  <= max_read_req_bytes_250;
      max_payload_cr <= max_payload_bytes_250;
      rcb_cr         <= read_completion_boundary_250;
      msix_enable_cr <= msix_enable_250;
      msix_masked_cr <= msix_masked_250;
   endrule

   (* fire_when_enabled, no_implicit_conditions *)
   rule register_config_values;
      max_read_req_bytes <= max_rd_req_cr.crossed();
      max_payload_bytes  <= max_payload_cr.crossed();
      rcb_mask           <= (rcb_cr.crossed() == 64) ? 7'h3f : 7'h7f;
      msix_enable        <= msix_enable_cr.crossed();
      msix_masked        <= msix_masked_cr.crossed();
   endrule

   // setup PCIe interrupt for MSI-X
   // this rule executes in the epClock250 domain
   (* fire_when_enabled, no_implicit_conditions *)
   rule intr_ifc_ctl;
      _ep.cfg_interrupt.di('0);      // tied off for MSI-X
      _ep.cfg_interrupt.assrt('0);  // tied off for MSI-X
      _ep.cfg_interrupt.req(0);      // tied off for MSI-X
   endrule: intr_ifc_ctl

   // Build the PCIe-to-NoC bridge
   PCIEtoBNoCQrc#(BPB)  bridge <- mkPCIEtoBNoCQrc_4( 64'h05ce_0006_0008_2323
						   , my_id
						   , max_read_req_bytes
						   , max_payload_bytes
						   , rcb_mask
						   , msix_enable
						   , msix_masked
						   , False // no MSI, only MSI-X
						   , clocked_by epClock125, reset_by epReset125
						   );
   mkConnectionWithClocks(_ep.trn_rx, tpl_2(bridge.tlps), epClock250, epReset250, epClock125, epReset125);
   mkConnectionWithClocks(_ep.trn_tx, tpl_1(bridge.tlps), epClock250, epReset250, epClock125, epReset125);

   //mkConnection(_dut.noc_src,bridge.noc);
   mkTieOff(bridge.noc);

   interface pcie     = _ep.pcie;

   method Bool isLinkUp         = link_is_up;

endmodule: buildPCIEK7Qrc

endpackage: SceMiKintex7PCIEQrc


Add dec

Add dec

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

`include "ProcConfig.bsv"

import BrPred::*;
import DirPredictor::*;
import Btb::*;
import ClientServer::*;
import Connectable::*;
import Decode::*;
import Ehr::*;
import Fifo::*;
import GetPut::*;
import MemoryTypes::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import Ras::*;
import EpochManager::*;
import Performance::*;
import Vector::*;
import Assert::*;
import Cntrs::*;
import ConfigReg::*;
import TlbTypes::*;
import ITlb::*;
import CCTypes::*;
import L1CoCache::*;
import MMIOInst::*;

interface FetchStage;
    // pipeline
    interface Vector#(SupSize, SupFifoDeq#(FromFetchStage)) pipelines;

    // tlb and mem connections
    interface ITlb iTlbIfc;
    interface ICoCache iMemIfc;
    interface MMIOInstToCore mmioIfc;

    // starting and stopping
    method Action start(Addr pc);
    method Action stop();

    // redirection methods
    method Action setWaitRedirect;
    method Action redirect(Addr pc);
    method Action done_flushing();
    method Action train_predictors(
        Addr pc, Addr next_pc, IType iType, Bool taken,
        DirPredTrainInfo dpTrain, Bool mispred
    );

    // debug
    method FetchDebugState getFetchState;

    // performance
    interface Perf#(DecStagePerfType) perf;
endinterface

typedef struct {
    Addr pc;
    Epoch mainEp;
    Bool waitForRedirect;
    Bool waitForFlush;
} FetchDebugState deriving(Bits, Eq, FShow);

typedef struct {
    Addr pc;
    Addr pred_next_pc;
    Bool decode_epoch;
    Epoch main_epoch;
} Fetch1ToFetch2 deriving(Bits, Eq, FShow);

typedef struct {
    Addr pc;
    Addr phys_pc;
    Addr pred_next_pc;
    Maybe#(Exception) cause;
    Bool access_mmio; // inst fetch from MMIO
    Bool decode_epoch;
    Epoch main_epoch;
} Fetch2ToFetch3 deriving(Bits, Eq, FShow);

typedef struct {
  Addr pc;
  Addr ppc;
  Bool decode_epoch;
  Epoch main_epoch;
  Instruction inst;
  Maybe#(Exception) cause;
} Fetch3ToDecode deriving(Bits, Eq, FShow);

typedef struct {
  Addr pc;
  Addr ppc;
  Epoch main_epoch;
  DirPredTrainInfo dpTrain;
  Instruction inst;
  DecodedInst dInst;
  ArchRegs regs;
  Maybe#(Exception) cause;
} FromFetchStage deriving (Bits, Eq, FShow);

// train next addr pred (BTB)
typedef struct {
    Addr pc;
    Addr nextPc;
} TrainNAP deriving(Bits, Eq, FShow);

(* synthesize *)
module mkFetchStage(FetchStage);
    // rule ordering: Fetch1 (BTB+TLB) < Fetch3 (decode & dir pred) < redirect method
    // Fetch1 < Fetch3 to avoid bypassing path on PC and epochs

    let verbose = True;

    // Basic State Elements
    Reg#(Bool) started <- mkReg(False);

    // Stall fetch when trap happens or system inst is renamed
    // All inst younger than the trap/system inst will be killed
    // Since CSR may be modified, sending wrong path request to TLB may cause problem
    // So we stall until the next redirection happens
    // The next redirect is either by the trap/system inst or an older one
    Reg#(Bool) waitForRedirect <- mkReg(False);
    // We don't want setWaitForRedirect method and redirect method to happen together
    // make them conflict
    RWire#(void) setWaitRedirect_redirect_conflict <- mkRWire;

    // Stall fetch during the flush triggered by the procesing trap/system inst in commit stage
    // We stall until the flush is done
    Reg#(Bool) waitForFlush <- mkReg(False);

    Ehr#(3, Addr) pc_reg <- mkEhr(0);
    Integer pc_fetch1_port = 0;
    Integer pc_decode_port = 1;
    Integer pc_redirect_port = 2;

    // Epochs
    Reg#(Bool) decode_epoch <- mkReg(False);
    Reg#(Epoch) f_main_epoch <- mkReg(0); // fetch estimate of main epoch

    // Pipeline Stage FIFOs
    Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
    Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
    Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f32d <- mkCFFifo; // FIFO should match I$ latency
    SupFifo#(SupSize, 2, Fetch3ToDecode) f32decode <- mkSupFifo;
    SupFifo#(SupSize, 2, FromFetchStage) out_fifo <- mkSupFifo;
       // Can the fifo size be smaller?
    Fifo#(4,Vector#(SupSize,Maybe#(Instruction))) instdata <- mkPipelineFifo();

    // Branch Predictors
    NextAddrPred    nextAddrPred <- mkBtb;
    let             dirPred      <- mkDirPredictor;
    ReturnAddrStack ras          <- mkRas;
    // Wire to train next addr pred (NAP)
    Fifo#(2,TrainNAP) napTrainByDec <- mkCFFirainNAP) napTrainByExe <- mkRWire;

    // TLB and Cache connections
    ITlb iTlb <- mkITlb;
    ICoCache iMem <- mkICoCache;
    MMIOInst mmio <- mkMMIOInst;
    Server#(Addr, TlbResp) tlb_server = iTlb.to_proc;
    Server#(Addr, Vector#(SupSize, Maybe#(Instruction))) mem_server = iMem.to_proc;

    // performance counters
    Fifo#(1, DecStagePerfType) perfReqQ <- mkCFFifo; // perf req FIFO
`ifdef PERF_COUNT
    Reg#(Bool) doStats <- mkConfigReg(False);
    // decode stage redirect
    Count#(Data) decRedirectBrCnt <- mkCount(0);
    Count#(Data) decRedirectJmpCnt <- mkCount(0);
    Count#(Data) decRedirectJrCnt <- mkCount(0);
    Count#(Data) decRedirectOtherCnt <- mkCount(0);
    // perf resp FIFO
    Fifo#(1, PerfResp#(DecStagePerfType)) perfRespQ <- mkCFFifo;

    rule doPerfReq;
        let t <- toGet(perfReqQ).get;
        Data d = (case(t)
            DecRedirectBr: decRedirectBrCnt;
            DecRedirectJmp: decRedirectJmpCnt;
            DecRedirectJr: decRedirectJrCnt;
            DecRedirectOther: decRedirectOtherCnt;
            default: 0;
        endcase);
        perfRespQ.enq(PerfResp {
            pType: t,
            data: d
        });
    endrule
`endif

    // We don't send req to TLB when waiting for redirect or TLB flush. Since
    // there is no FIFO between doFetch1 and TLB, when OOO commit stage wait
    // TLB idle to change VM CSR / signal flush TLB, there is no wrong path
    // request afterwards to race with the system code that manage paget table.
    rule doFetch1(started && !waitForRedirect && !waitForFlush);
        let pc = pc_reg[pc_fetch1_port];

        // Chain of prediction for the next instructions
        // We need a BTB with a register file with enough ports!
        // Instead of cascading predictions, we can always feed pc+4*i into
        // predictor, because we will break superscaler fetch if nextpc != pc+4
        Vector#(SupSize, Addr) pred_future_pc;
        for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
            pred_future_pc[i] = nextAddrPred.predPc(pc + fromInteger(4 * i));
        end

        // Next pc is the first nextPc that breaks the chain of pc+4 or
        // that is at the end of a cacheline.
        Vector#(SupSize,Integer) indexes = genVector;
        function Bool findNextPc(Addr pc, Integer i);
            Bool notLastInst = getLineInstOffset(pc + fromInteger(4*i)) != maxBound;
            Bool noJump = pred_future_pc[i] == pc + fromInteger(4*(i+1));
            return (!(notLastInst && noJump));
        endfunction
        Integer posLastSup = fromMaybe(valueof(SupSize) - 1, find(findNextPc(pc), indexes));
        Addr pred_next_pc = pred_future_pc[posLastSup];
        pc_reg[pc_fetch1_port] <= pred_next_pc;

        // Send TLB request
        tlb_server.request.put(pc);

        let out = Fetch1ToFetch2 {
            pc: pc,
            pred_next_pc: pred_next_pc,
            decode_epoch: decode_epoch,
            main_epoch: f_main_epoch};
        f12f2.enq(tuple2(fromInteger(posLastSup),out));
        if (verbose) $display("Fetch1: ", fshow(out));
    endrule

    rule doFetch2;
        let {nbSup,in} = f12f2.first;
        f12f2.deq;

        // Get TLB response
        match {.phys_pc, .cause} <- tlb_server.response.get;

        // Access main mem or boot rom
        Bool access_mmio = False;
        if (!isValid(cause)) begin
            case(mmio.getFetchTarget(phys_pc))
                MainMem: begin
                    // Send ICache request
                    mem_server.request.put(phys_pc);
                end
                BootRom: begin
                    // Send MMIO req. Luckily boot rom is also aligned with
                    // cache line size, so all nbSup+1 insts can be fetched
                    // from boot rom. It won't happen that insts fetched from
                    // boot rom is less than requested.
                    mmio.bootRomReq(phys_pc, nbSup);
                    access_mmio = True;
                end
                default: begin
                    // Access fault
                    cause = Valid (InstAccessFault);
                end
            endcase
        end

        let out = Fetch2ToFetch3 {
            pc: in.pc,
            phys_pc: phys_pc,
            pred_next_pc: in.pred_next_pc,
            cause: cause,
            access_mmio: access_mmio,
            decode_epoch: in.decode_epoch,
            main_epoch: in.main_epoch };
        f22f3.enq(tuple2(nbSup,out));
        if (verbose) $display("Fetch2: ", fshow(out));
    endrule

// Break out of i$
    rule doFetch3;
        let {nbSup, fetch3In} = f22f3.first;
        f22f3.deq();
        if (verbose) $display("Fetch3 %d",fetch3In.pc);

        // Get ICache/MMIO response if no exception
        // In case of exception, we still need to process at least inst_data[0]
        // (it will be turned to an exception later), so inst_data[0] must be
        // valid.
        Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid (0));
        if(!isValid(fetch3In.cause)) begin
            if(fetch3In.access_mmio) begin
                if(verbose) $display("get answer from MMIO %d", fetch3In.pc);
                inst_d <- mmio.bootRomResp;
            end
            else begin
                if(verbose) $display("get answer from memory %d", fetch3In.pc);
                inst_d <- mem_server.response.get;
            end
        end
        if(verbose) $display("epoch instr: %d, epoch main : %d", fetch3In.main_epoch, f_main_epoch);
        instdata.enq(inst_d);
        f32d.enq(f22f3.first);
    endrule

    rule doDecode;
        let {nbSup, fetch3In} = f32d.first;
        f32d.deq();
	let inst_data = instdata.first();
	instdata.deq();
        // The main_epoch check is required to make sure this stage doesn't
        // redirect the PC if a later stage already redirected the PC.
        if (fetch3In.main_epoch == f_main_epoch) begin
            Bool decode_epoch_local = decode_epoch; // next value for decode epoch
            Maybe#(Addr) redirectPc = Invalid; // next pc redirect by branch predictor
            Maybe#(TrainNAP) trainNAP = Invalid; // training data sent to next addr pred
`ifdef PERF_COUNT
            // performance counter: inst being redirect by decode stage
            // Note that only 1 redirection may happen in a cycle
            Maybe#(IType) redirectInst = Invalid;
`endif

            for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
                if (inst_data[i] != tagged Invalid && fromInteger(i) <= nbSup) begin
                    // get the input to decode
                    let in = Fetch3ToDecode {
                        pc: fetch3In.pc+fromInteger(4*i),
                        // last inst, next pc may not be pc+4
                        ppc: fromInteger(i) == nbSup ? fetch3In.pred_next_pc :
                                                       fetch3In.pc+fromInteger(4*(i+1)),
                        decode_epoch: fetch3In.decode_epoch,
                        main_epoch: fetch3In.main_epoch,
                        inst: fromMaybe(?,inst_data[i]),
                        cause: fetch3In.cause
                    };
                    let cause = in.cause;
                    if (verbose) $display("Decode %d\n",i);

                    // do decode and branch prediction
                    // Drop here if does not match the decode_epoch.
                    if (in.decode_epoch == decode_epoch_local) begin
                        doAssert(in.main_epoch == f_main_epoch, "main epoch must match");

                        let decode_result = decode(in.inst);

                        // update cause if there was not an early detected exception
                        if (!isValid(cause)) begin
                            cause = decode_result.illegalInst ? tagged Valid IllegalInst : tagged Invalid;
                        end

                        let dInst = decode_result.dInst;
                        let regs = decode_result.regs;
                        DirPredTrainInfo dp_train = ?; // dir pred training bookkeeping

                        // update predicted next pc
                        if (!isValid(cause)) begin
                            // direction predict
                            Bool pred_taken = False;
                            if(dInst.iType == Br) begin
                                let pred_res <- dirPred.pred[i].pred(in.pc);
                                pred_taken = pred_res.taken;
                                dp_train = pred_res.train;
                            end
                            Maybe#(Addr) nextPc = decodeBrPred(in.pc, dInst, pred_taken);
                            function Bool linkedR(Maybe#(ArchRIndx) register);
                                    let res=False;
                                    if (register matches tagged Valid .r &&& (r == tagged Gpr 1 || r == tagged Gpr 5)) res=True;
				    return res;
                            endfunction

                            // return address stack only push  when rd = x1 or x5
                            if (dInst.iType == J && linkedR(regs.dst)) begin
                                // function call -- push return address to stack
                                ras.pushAddress[i](in.pc + 4);
                            end
                            else if (dInst.iType == Jr ) begin // jalr
                                // we push if rs1 != rd and both are x1 or x5
                                if( regs.src1 != regs.dst && linkedR(regs.src1) && linkedR(regs.dst)) begin
                                    ras.pushAddress[i](in.pc+4);
                                    let new_ppc = ras.firstAddress[i];
                                    ras.popAddress[i];
                                    nextPc = tagged Valid new_ppc;
                                end
                                // we push if rd = x1 or x5 and rs1 is not x1 && x5
                                if (!linkedR(regs.src1) && linkedR(regs.dst)) begin
                                    ras.pushAddress[i](in.pc+4);
                                end
                                // we push if rd = rs1 = x1 or x5
                                if (regs.src1 == regs.dst && linkedR(regs.src1)) begin
                                    ras.pushAddress[i](in.pc+4);
                                end
                                // we pop if rd != x1 or x5, and rs1 = x1 or x5
                                if (!linkedR(regs.dst) && linkedR(regs.src1)) begin
                                    let new_ppc = ras.firstAddress[i];
                                    ras.popAddress[i];
                                    nextPc = tagged Valid new_ppc;
                                end
                            end

                            if(verbose) begin
                                $display("Branch prediction: ", fshow(dInst.iType), " ; ", fshow(in.pc), " ; ",
                                         fshow(in.ppc), " ; ", fshow(pred_taken), " ; ", fshow(nextPc));
                            end

                            // check previous mispred
                            if (nextPc matches tagged Valid .decode_pred_next_pc &&& decode_pred_next_pc != in.ppc) begin
                                if (verbose) $display("ppc and decodeppc :  %h %h", in.ppc, decode_pred_next_pc);
                                decode_epoch_local = !decode_epoch_local;
                                redirectPc = Valid (decode_pred_next_pc); // record redirect next pc
                                in.ppc = decode_pred_next_pc;
                                // train next addr pred when mispredict
                                trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: decode_pred_next_pc});
`ifdef PERF_COUNT
                                // performance stats: record decode redirect
                                doAssert(redirectInst == Invalid, "at most 1 decode redirect per cycle");
                                redirectInst = Valid (dInst.iType);
`endif
                            end
                        end
                        let out = FromFetchStage{pc: in.pc,
                                                 ppc: in.ppc,
                                                 main_epoch: in.main_epoch,
                                                 dpTrain: dp_train,
                                                 inst: in.inst,
                                                 dInst: dInst,
                                                 regs: decode_result.regs,
                                                 cause: cause };
                        out_fifo.enqS[i].enq(out);
                        if (verbose) $display("Decode: ", fshow(out));
                    end
                    else begin
                        if (verbose) $display("Drop decoded within a superscalar");
                        // just drop wrong path instructions
                    end
                end
                else if (inst_data[i] == tagged Invalid && fromInteger(i) <= nbSup) begin
                    // inst num is less than expected; this should not happen
                    // because both I$ and boot rom are aligned to cache line
                    // size.
                    doAssert(False, "Fetched insts not enough");
                end
            end

            // update PC and epoch
            if(redirectPc matches tagged Valid .nextPc) begin
                pc_reg[pc_decode_port] <= nextPc;
            end
            decode_epoch <= decode_epoch_local;
            // send training data for next addr pred
            if (trainNAP matches tagged Valid .x) begin
                napTrainByDec.enq(x);
            end
`ifdef PERF_COUNT
            // performance counter: check whether redirect happens
            if(redirectInst matches tagged Valid .iType &&& doStats) begin
                case(iType)
                    Br: decRedirectBrCnt.incr(1);
                    J : decRedirectJmpCnt.incr(1);
                    Jr: decRedirectJrCnt.incr(1);
                    default: decRedirectOtherCnt.incr(1);
                endcase
            end
`endif
        end
        else begin
            if (verbose) $display("drop in fetch3decode");
        end
    endrule

    // train next addr pred
    (* fire_when_enabled*)
    rule doTrainNAP( isValid(napTrainByExe.wget));
        // give priority to train data from exe
        TrainNAP train = fromMaybe(napTrainByDec.first(), napTrainByExe.wget);
        napTrainByDec.deq();
        nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 4);
    endrule

    interface Vector pipelines = out_fifo.deqS;
    interface iTlbIfc = iTlb;
    interface iMemIfc = iMem;
    interface mmioIfc = mmio.toCore;

    method Action start(Addr start_pc);
        pc_reg[0] <= start_pc;
        started <= True;
        waitForRedirect <= False;
        waitForFlush <= False;
    endmethod
    method Action stop();
        started <= False;
    endmethod

    method Action setWaitRedirect;
        waitForRedirect <= True;
        setWaitRedirect_redirect_conflict.wset(?); // conflict with redirect
    endmethod
    method Action redirect(Addr new_pc);
        if (verbose) $display("Redirect: newpc %h, old f_main_epoch %d, new f_main_epoch %d",new_pc,f_main_epoch,f_main_epoch+1);
        pc_reg[pc_redirect_port] <= new_pc;
        f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
        // redirect comes, stop stalling for redirect
        waitForRedirect <= False;
        setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
        // this redirect may be caused by a trap/system inst in commit stage
        // we conservatively set wait for flush TODO make this an input parameter
        waitForFlush <= True;
    endmethod
    method Action done_flushing() if (waitForFlush);
        // signal that the pipeline can resume fetching
        waitForFlush <= False;
        // XXX The guard prevents the readyToFetch rule in Core.bsv from firing every cycle
        // The guard also makes this method sequence before (restricted) redirect method
        // So the effect of setting waitForFlush in redirect method will not be overwritten
        // Then we don't need to make two methods conflict
        // It's fine for the effect of this method to be overwritten, because it fires very often
    endmethod

    method Action train_predictors(
        Addr pc, Addr next_pc, IType iType, Bool taken,
        DirPredTrainInfo dpTrain, Bool mispred
    );
        //if (iType == J || (iType == Br && next_pc < pc)) begin
        //    // Only train the next address predictor for jumps and backward branches
        //    // next_pc != pc + 4 is a substitute for taken
        //    nextAddrPred.update(pc, next_pc, taken);
        //end
        if (iType == Br) begin
            // Train the direction predictor for all branches
            dirPred.update(pc, taken, dpTrain, mispred);
        end
        // train next addr pred when mispred
        if(mispred) begin
            napTrainByExe.wset(TrainNAP {pc: pc, nextPc: next_pc});
        end
    endmethod

    method FetchDebugState getFetchState;
        return FetchDebugState {
            pc: pc_reg[0],
            waitForRedirect: waitForRedirect,
            waitForFlush: waitForFlush,
            mainEp: f_main_epoch
        };
    endmethod

    interface Perf perf;
        method Action setStatus(Bool stats);
`ifdef PERF_COUNT
            doStats <= stats;
`else
            noAction;
`endif
        endmethod

        method Action req(DecStagePerfType r);
            perfReqQ.enq(r);
        endmethod

        method ActionValue#(PerfResp#(DecStagePerfType)) resp;
`ifdef PERF_COUNT
            perfRespQ.deq;
            return perfRespQ.first;
`else
            perfReqQ.deq;
            return PerfResp {
                pType: perfReqQ.first,
                data: 0
            };
`endif
        endmethod

`ifdef PERF_COUNT
        method Bool respValid = perfRespQ.notEmpty;
`else
        method Bool respValid = perfReqQ.notEmpty;
`endif
    endinterface
endmodule

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

`include "ProcConfig.bsv"

import BrPred::*;
import DirPredictor::*;
import Btb::*;
import ClientServer::*;
import Connectable::*;
import Decode::*;
import Ehr::*;
import Fifo::*;
import GetPut::*;
import MemoryTypes::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import Ras::*;
import EpochManager::*;
import Performance::*;
import Vector::*;
import Assert::*;
import Cntrs::*;
import ConfigReg::*;
import TlbTypes::*;
import ITlb::*;
import CCTypes::*;
import L1CoCache::*;
import MMIOInst::*;

interface FetchStage;
    // pipeline
    interface Vector#(SupSize, SupFifoDeq#(FromFetchStage)) pipelines;

    // tlb and mem connections
    interface ITlb iTlbIfc;
    interface ICoCache iMemIfc;
    interface MMIOInstToCore mmioIfc;

    // starting and stopping
    method Action start(Addr pc);
    method Action stop();

    // redirection methods
    method Action setWaitRedirect;
    method Action redirect(Addr pc);
    method Action done_flushing();
    method Action train_predictors(
        Addr pc, Addr next_pc, IType iType, Bool taken,
        DirPredTrainInfo dpTrain, Bool mispred
    );

    // debug
    method FetchDebugState getFetchState;

    // performance
    interface Perf#(DecStagePerfType) perf;
endinterface

typedef struct {
    Addr pc;
    Epoch mainEp;
    Bool waitForRedirect;
    Bool waitForFlush;
} FetchDebugState deriving(Bits, Eq, FShow);

typedef struct {
    Addr pc;
    Addr pred_next_pc;
    Bool decode_epoch;
    Epoch main_epoch;
} Fetch1ToFetch2 deriving(Bits, Eq, FShow);

typedef struct {
    Addr pc;
    Addr phys_pc;
    Addr pred_next_pc;
    Maybe#(Exception) cause;
    Bool access_mmio; // inst fetch from MMIO
    Bool decode_epoch;
    Epoch main_epoch;
} Fetch2ToFetch3 deriving(Bits, Eq, FShow);

typedef struct {
  Addr pc;
  Addr ppc;
  Bool decode_epoch;
  Epoch main_epoch;
  Instruction inst;
  Maybe#(Exception) cause;
} Fetch3ToDecode deriving(Bits, Eq, FShow);

typedef struct {
  Addr pc;
  Addr ppc;
  Epoch main_epoch;
  DirPredTrainInfo dpTrain;
  Instruction inst;
  DecodedInst dInst;
  ArchRegs regs;
  Maybe#(Exception) cause;
} FromFetchStage deriving (Bits, Eq, FShow);

// train next addr pred (BTB)
typedef struct {
    Addr pc;
    Addr nextPc;
} TrainNAP deriving(Bits, Eq, FShow);

(* synthesize *)
module mkFetchStage(FetchStage);
    // rule ordering: Fetch1 (BTB+TLB) < Fetch3 (decode & dir pred) < redirect method
    // Fetch1 < Fetch3 to avoid bypassing path on PC and epochs

    let verbose = True;

    // Basic State Elements
    Reg#(Bool) started <- mkReg(False);

    // Stall fetch when trap happens or system inst is renamed
    // All inst younger than the trap/system inst will be killed
    // Since CSR may be modified, sending wrong path request to TLB may cause problem
    // So we stall until the next redirection happens
    // The next redirect is either by the trap/system inst or an older one
    Reg#(Bool) waitForRedirect <- mkReg(False);
    // We don't want setWaitForRedirect method and redirect method to happen together
    // make them conflict
    RWire#(void) setWaitRedirect_redirect_conflict <- mkRWire;

    // Stall fetch during the flush triggered by the procesing trap/system inst in commit stage
    // We stall until the flush is done
    Reg#(Bool) waitForFlush <- mkReg(False);

    Ehr#(3, Addr) pc_reg <- mkEhr(0);
    Integer pc_fetch1_port = 0;
    Integer pc_decode_port = 1;
    Integer pc_redirect_port = 2;

    // Epochs
    Reg#(Bool) decode_epoch <- mkReg(False);
    Reg#(Epoch) f_main_epoch <- mkReg(0); // fetch estimate of main epoch

    // Pipeline Stage FIFOs
    Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
    Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
    Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f32d <- mkCFFifo; // FIFO should match I$ latency
    SupFifo#(SupSize, 2, Fetch3ToDecode) f32decode <- mkSupFifo;
    SupFifo#(SupSize, 2, FromFetchStage) out_fifo <- mkSupFifo;
       // Can the fifo size be smaller?
    Fifo#(4,Vector#(SupSize,Maybe#(Instruction))) instdata <- mkPipelineFifo();

    // Branch Predictors
    NextAddrPred    nextAddrPred <- mkBtb;
    let             dirPred      <- mkDirPredictor;
    ReturnAddrStack ras          <- mkRas;
    // Wire to train next addr pred (NAP)
    RWire#(TrainNAP) napTrainByDec <- mkRWire;
    RWire#(TrainNAP) napTrainByExe <- mkRWire;

    // TLB and Cache connections
    ITlb iTlb <- mkITlb;
    ICoCache iMem <- mkICoCache;
    MMIOInst mmio <- mkMMIOInst;
    Server#(Addr, TlbResp) tlb_server = iTlb.to_proc;
    Server#(Addr, Vector#(SupSize, Maybe#(Instruction))) mem_server = iMem.to_proc;

    // performance counters
    Fifo#(1, DecStagePerfType) perfReqQ <- mkCFFifo; // perf req FIFO
`ifdef PERF_COUNT
    Reg#(Bool) doStats <- mkConfigReg(False);
    // decode stage redirect
    Count#(Data) decRedirectBrCnt <- mkCount(0);
    Count#(Data) decRedirectJmpCnt <- mkCount(0);
    Count#(Data) decRedirectJrCnt <- mkCount(0);
    Count#(Data) decRedirectOtherCnt <- mkCount(0);
    // perf resp FIFO
    Fifo#(1, PerfResp#(DecStagePerfType)) perfRespQ <- mkCFFifo;

    rule doPerfReq;
        let t <- toGet(perfReqQ).get;
        Data d = (case(t)
            DecRedirectBr: decRedirectBrCnt;
            DecRedirectJmp: decRedirectJmpCnt;
            DecRedirectJr: decRedirectJrCnt;
            DecRedirectOther: decRedirectOtherCnt;
            default: 0;
        endcase);
        perfRespQ.enq(PerfResp {
            pType: t,
            data: d
        });
    endrule
`endif

    // We don't send req to TLB when waiting for redirect or TLB flush. Since
    // there is no FIFO between doFetch1 and TLB, when OOO commit stage wait
    // TLB idle to change VM CSR / signal flush TLB, there is no wrong path
    // request afterwards to race with the system code that manage paget table.
    rule doFetch1(started && !waitForRedirect && !waitForFlush);
        let pc = pc_reg[pc_fetch1_port];

        // Chain of prediction for the next instructions
        // We need a BTB with a register file with enough ports!
        // Instead of cascading predictions, we can always feed pc+4*i into
        // predictor, because we will break superscaler fetch if nextpc != pc+4
        Vector#(SupSize, Addr) pred_future_pc;
        for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
            pred_future_pc[i] = nextAddrPred.predPc(pc + fromInteger(4 * i));
        end

        // Next pc is the first nextPc that breaks the chain of pc+4 or
        // that is at the end of a cacheline.
        Vector#(SupSize,Integer) indexes = genVector;
        function Bool findNextPc(Addr pc, Integer i);
            Bool notLastInst = getLineInstOffset(pc + fromInteger(4*i)) != maxBound;
            Bool noJump = pred_future_pc[i] == pc + fromInteger(4*(i+1));
            return (!(notLastInst && noJump));
        endfunction
        Integer posLastSup = fromMaybe(valueof(SupSize) - 1, find(findNextPc(pc), indexes));
        Addr pred_next_pc = pred_future_pc[posLastSup];
        pc_reg[pc_fetch1_port] <= pred_next_pc;

        // Send TLB request
        tlb_server.request.put(pc);

        let out = Fetch1ToFetch2 {
            pc: pc,
            pred_next_pc: pred_next_pc,
            decode_epoch: decode_epoch,
            main_epoch: f_main_epoch};
        f12f2.enq(tuple2(fromInteger(posLastSup),out));
        if (verbose) $display("Fetch1: ", fshow(out));
    endrule

    rule doFetch2;
        let {nbSup,in} = f12f2.first;
        f12f2.deq;

        // Get TLB response
        match {.phys_pc, .cause} <- tlb_server.response.get;

        // Access main mem or boot rom
        Bool access_mmio = False;
        if (!isValid(cause)) begin
            case(mmio.getFetchTarget(phys_pc))
                MainMem: begin
                    // Send ICache request
                    mem_server.request.put(phys_pc);
                end
                BootRom: begin
                    // Send MMIO req. Luckily boot rom is also aligned with
                    // cache line size, so all nbSup+1 insts can be fetched
                    // from boot rom. It won't happen that insts fetched from
                    // boot rom is less than requested.
                    mmio.bootRomReq(phys_pc, nbSup);
                    access_mmio = True;
                end
                default: begin
                    // Access fault
                    cause = Valid (InstAccessFault);
                end
            endcase
        end

        let out = Fetch2ToFetch3 {
            pc: in.pc,
            phys_pc: phys_pc,
            pred_next_pc: in.pred_next_pc,
            cause: cause,
            access_mmio: access_mmio,
            decode_epoch: in.decode_epoch,
            main_epoch: in.main_epoch };
        f22f3.enq(tuple2(nbSup,out));
        if (verbose) $display("Fetch2: ", fshow(out));
    endrule

// Break out of i$
    rule doFetch3;
        let {nbSup, fetch3In} = f22f3.first;
        f22f3.deq();
        if (verbose) $display("Fetch3 %d",fetch3In.pc);

        // Get ICache/MMIO response if no exception
        // In case of exception, we still need to process at least inst_data[0]
        // (it will be turned to an exception later), so inst_data[0] must be
        // valid.
        Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid (0));
        if(!isValid(fetch3In.cause)) begin
            if(fetch3In.access_mmio) begin
                if(verbose) $display("get answer from MMIO %d", fetch3In.pc);
                inst_d <- mmio.bootRomResp;
            end
            else begin
                if(verbose) $display("get answer from memory %d", fetch3In.pc);
                inst_d <- mem_server.response.get;
            end
        end
        if(verbose) $display("epoch instr: %d, epoch main : %d", fetch3In.main_epoch, f_main_epoch);
        instdata.enq(inst_d);
        f32d.enq(f22f3.first);
    endrule

    rule doDecode;
        let {nbSup, fetch3In} = f32d.first;
        f32d.deq();
	let inst_data = instdata.first();
	instdata.deq();
        // The main_epoch check is required to make sure this stage doesn't
        // redirect the PC if a later stage already redirected the PC.
        if (fetch3In.main_epoch == f_main_epoch) begin
            Bool decode_epoch_local = decode_epoch; // next value for decode epoch
            Maybe#(Addr) redirectPc = Invalid; // next pc redirect by branch predictor
            Maybe#(TrainNAP) trainNAP = Invalid; // training data sent to next addr pred
`ifdef PERF_COUNT
            // performance counter: inst being redirect by decode stage
            // Note that only 1 redirection may happen in a cycle
            Maybe#(IType) redirectInst = Invalid;
`endif

            for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
                if (inst_data[i] != tagged Invalid && fromInteger(i) <= nbSup) begin
                    // get the input to decode
                    let in = Fetch3ToDecode {
                        pc: fetch3In.pc+fromInteger(4*i),
                        // last inst, next pc may not be pc+4
                        ppc: fromInteger(i) == nbSup ? fetch3In.pred_next_pc :
                                                       fetch3In.pc+fromInteger(4*(i+1)),
                        decode_epoch: fetch3In.decode_epoch,
                        main_epoch: fetch3In.main_epoch,
                        inst: fromMaybe(?,inst_data[i]),
                        cause: fetch3In.cause
                    };
                    let cause = in.cause;
                    if (verbose) $display("Decode %d\n",i);

                    // do decode and branch prediction
                    // Drop here if does not match the decode_epoch.
                    if (in.decode_epoch == decode_epoch_local) begin
                        doAssert(in.main_epoch == f_main_epoch, "main epoch must match");

                        let decode_result = decode(in.inst);

                        // update cause if there was not an early detected exception
                        if (!isValid(cause)) begin
                            cause = decode_result.illegalInst ? tagged Valid IllegalInst : tagged Invalid;
                        end

                        let dInst = decode_result.dInst;
                        let regs = decode_result.regs;
                        DirPredTrainInfo dp_train = ?; // dir pred training bookkeeping

                        // update predicted next pc
                        if (!isValid(cause)) begin
                            // direction predict
                            Bool pred_taken = False;
                            if(dInst.iType == Br) begin
                                let pred_res <- dirPred.pred[i].pred(in.pc);
                                pred_taken = pred_res.taken;
                                dp_train = pred_res.train;
                            end
                            Maybe#(Addr) nextPc = decodeBrPred(in.pc, dInst, pred_taken);

                            // return address stack
                            if (dInst.iType == J && isValid(regs.dst)) begin
                                // function call -- push return address to stack
                                ras.pushAddress[i](in.pc + 4);
                            end
                            else if (dInst.iType == Jr) begin
                                let new_ppc = ras.firstAddress[i];
                                ras.popAddress[i];
                                nextPc = tagged Valid new_ppc;
                            end

                            if(verbose) begin
                                $display("Branch prediction: ", fshow(dInst.iType), " ; ", fshow(in.pc), " ; ",
                                         fshow(in.ppc), " ; ", fshow(pred_taken), " ; ", fshow(nextPc));
                            end

                            // check previous mispred
                            if (nextPc matches tagged Valid .decode_pred_next_pc &&& decode_pred_next_pc != in.ppc) begin
                                if (verbose) $display("ppc and decodeppc :  %h %h", in.ppc, decode_pred_next_pc);
                                decode_epoch_local = !decode_epoch_local;
                                redirectPc = Valid (decode_pred_next_pc); // record redirect next pc
                                in.ppc = decode_pred_next_pc;
                                // train next addr pred when mispredict
                                trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: decode_pred_next_pc});
`ifdef PERF_COUNT
                                // performance stats: record decode redirect
                                doAssert(redirectInst == Invalid, "at most 1 decode redirect per cycle");
                                redirectInst = Valid (dInst.iType);
`endif
                            end
                        end
                        let out = FromFetchStage{pc: in.pc,
                                                 ppc: in.ppc,
                                                 main_epoch: in.main_epoch,
                                                 dpTrain: dp_train,
                                                 inst: in.inst,
                                                 dInst: dInst,
                                                 regs: decode_result.regs,
                                                 cause: cause };
                        out_fifo.enqS[i].enq(out);
                        if (verbose) $display("Decode: ", fshow(out));
                    end
                    else begin
                        if (verbose) $display("Drop decoded within a superscalar");
                        // just drop wrong path instructions
                    end
                end
                else if (inst_data[i] == tagged Invalid && fromInteger(i) <= nbSup) begin
                    // inst num is less than expected; this should not happen
                    // because both I$ and boot rom are aligned to cache line
                    // size.
                    doAssert(False, "Fetched insts not enough");
                end
            end

            // update PC and epoch
            if(redirectPc matches tagged Valid .nextPc) begin
                pc_reg[pc_decode_port] <= nextPc;
            end
            decode_epoch <= decode_epoch_local;
            // send training data for next addr pred
            if(trainNAP matches tagged Valid .x) begin
                napTrainByDec.wset(x);
            end
`ifdef PERF_COUNT
            // performance counter: check whether redirect happens
            if(redirectInst matches tagged Valid .iType &&& doStats) begin
                case(iType)
                    Br: decRedirectBrCnt.incr(1);
                    J : decRedirectJmpCnt.incr(1);
                    Jr: decRedirectJrCnt.incr(1);
                    default: decRedirectOtherCnt.incr(1);
                endcase
            end
`endif
        end
        else begin
            if (verbose) $display("drop in fetch3decode");
        end
    endrule

    // train next addr pred
    (* fire_when_enabled, no_implicit_conditions *)
    rule doTrainNAP(isValid(napTrainByDec.wget) || isValid(napTrainByExe.wget));
        // give priority to train data from exe
        TrainNAP train = fromMaybe(validValue(napTrainByDec.wget), napTrainByExe.wget);
        nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 4);
    endrule

    interface Vector pipelines = out_fifo.deqS;
    interface iTlbIfc = iTlb;
    interface iMemIfc = iMem;
    interface mmioIfc = mmio.toCore;

    method Action start(Addr start_pc);
        pc_reg[0] <= start_pc;
        started <= True;
        waitForRedirect <= False;
        waitForFlush <= False;
    endmethod
    method Action stop();
        started <= False;
    endmethod

    method Action setWaitRedirect;
        waitForRedirect <= True;
        setWaitRedirect_redirect_conflict.wset(?); // conflict with redirect
    endmethod
    method Action redirect(Addr new_pc);
        if (verbose) $display("Redirect: newpc %h, old f_main_epoch %d, new f_main_epoch %d",new_pc,f_main_epoch,f_main_epoch+1);
        pc_reg[pc_redirect_port] <= new_pc;
        f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
        // redirect comes, stop stalling for redirect
        waitForRedirect <= False;
        setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
        // this redirect may be caused by a trap/system inst in commit stage
        // we conservatively set wait for flush TODO make this an input parameter
        waitForFlush <= True;
    endmethod
    method Action done_flushing() if (waitForFlush);
        // signal that the pipeline can resume fetching
        waitForFlush <= False;
        // XXX The guard prevents the readyToFetch rule in Core.bsv from firing every cycle
        // The guard also makes this method sequence before (restricted) redirect method
        // So the effect of setting waitForFlush in redirect method will not be overwritten
        // Then we don't need to make two methods conflict
        // It's fine for the effect of this method to be overwritten, because it fires very often
    endmethod

    method Action train_predictors(
        Addr pc, Addr next_pc, IType iType, Bool taken,
        DirPredTrainInfo dpTrain, Bool mispred
    );
        //if (iType == J || (iType == Br && next_pc < pc)) begin
        //    // Only train the next address predictor for jumps and backward branches
        //    // next_pc != pc + 4 is a substitute for taken
        //    nextAddrPred.update(pc, next_pc, taken);
        //end
        if (iType == Br) begin
            // Train the direction predictor for all branches
            dirPred.update(pc, taken, dpTrain, mispred);
        end
        // train next addr pred when mispred
        if(mispred) begin
            napTrainByExe.wset(TrainNAP {pc: pc, nextPc: next_pc});
        end
    endmethod

    method FetchDebugState getFetchState;
        return FetchDebugState {
            pc: pc_reg[0],
            waitForRedirect: waitForRedirect,
            waitForFlush: waitForFlush,
            mainEp: f_main_epoch
        };
    endmethod

    interface Perf perf;
        method Action setStatus(Bool stats);
`ifdef PERF_COUNT
            doStats <= stats;
`else
            noAction;
`endif
        endmethod

        method Action req(DecStagePerfType r);
            perfReqQ.enq(r);
        endmethod

        method ActionValue#(PerfResp#(DecStagePerfType)) resp;
`ifdef PERF_COUNT
            perfRespQ.deq;
            return perfRespQ.first;
`else
            perfReqQ.deq;
            return PerfResp {
                pType: perfReqQ.first,
                data: 0
            };
`endif
        endmethod

`ifdef PERF_COUNT
        method Bool respValid = perfRespQ.notEmpty;
`else
        method Bool respValid = perfReqQ.notEmpty;
`endif
    endinterface
endmodule