updated Top.bsv for imageon

updated Top.bsv for imageon

// 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 Clocks :: *;
import Vector            :: *;
import Connectable       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import AxiMasterSlave    :: *;
import AxiDma            :: *;
import XilinxCells       :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import FIFOF::*;
import HDMI::*;
import Imageon::*;
import ConnectableWithTrace::*;
import CtrlMux::*;

//`define TRACE_AXI
//`define AXI_READ_TIMING

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
   (* prefix="GPIO" *)
   interface LEDS             leds;
   (* prefix="XADC" *)
   interface XADC             xadc;
   (* prefix="" *)
   interface pins             pins;
   interface Vector#(4, Clock) unused_clock;
   interface Vector#(4, Reset) unused_reset;
endinterface

typedef (function Module#(PortalTop#(32, 64, ipins)) mkpt(Clock clock200, Clock io_vita_clk)) MkPortalTop#(type ipins);

module [Module] mkZynqTopFromPortal#(Clock io_vita_clk_out_p, Clock io_vita_clk_out_n, MkPortalTop#(ipins) constructor)(ZynqTop#(ipins));
   Clock io_vita_clk <- XilinxCells::mkClockIBUFDS(io_vita_clk_out_p, io_vita_clk_out_n);
   PS7 ps7 <- mkPS7();
   Clock mainclock = ps7.fclkclk[0];
   Reset mainreset = ps7.fclkreset[0];

   // I have a feeling the fclkclk[3] is not what we want. Jamey
   let top <- constructor(ps7.fclkclk[3], io_vita_clk, clocked_by mainclock, reset_by mainreset);

   //mkConnection(ps7.m_axi_gp[0].client, top.ctrl);
   //mkConnectionWithTrace(top.m_axi, ps7.s_axi_hp[0].axi.server);
   Axi3Slave#(32,32,12) ctrl <- mkAxiDmaSlave(top.slave);
   mkConnection(ps7.m_axi_gp[0].client, ctrl, clocked_by mainclock, reset_by mainreset);
   Axi3Master#(32,64,6) m_axi <- mkAxiDmaMaster(top.master, clocked_by mainclock, reset_by mainreset);
   mkConnectionWithTrace(m_axi, ps7.s_axi_hp[0].axi.server, clocked_by mainclock, reset_by mainreset);

   let intr_mux <- mkInterruptMux(top.interrupt);
   rule send_int_rule;
      ps7.interrupt(pack(intr_mux));
   endrule

   interface zynq = ps7.pins;
   interface leds = top.leds;
   interface XADC xadc;
       method Bit#(4) gpio;
           return 0;
       endmethod
   endinterface
   interface pins = top.pins;

   // these are exported to make bsc happy, and then the ports are disconnected after synthesis
   interface unused_clock = ps7.fclkclk;
   interface unused_reset = ps7.fclkreset;
endmodule

module mkImageonZynqTop#(Clock io_vita_clk_out_p, Clock io_vita_clk_out_n)(ZynqTop#(ImageonVita));
   let top <- mkZynqTopFromPortal(io_vita_clk_out_p, io_vita_clk_out_n, mkPortalTop);
   return top;
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 Clocks :: *;
import Vector            :: *;
import Connectable       :: *;
import Portal            :: *;
import Leds              :: *;
import Top               :: *;
import AxiMasterSlave    :: *;
import XilinxCells       :: *;
import XbsvXilinxCells   :: *;
import PS7LIB::*;
import PPS7LIB::*;
import XADC::*;
import FIFOF::*;
import HDMI::*;
import Imageon::*;
import ConnectableWithTrace::*;
import CtrlMux::*;

//`define TRACE_AXI
//`define AXI_READ_TIMING

(* always_ready, always_enabled *)
interface ZynqTop#(type pins);
   (* prefix="" *)
   interface ZynqPins zynq;
   (* prefix="GPIO" *)
   interface LEDS             leds;
   (* prefix="XADC" *)
   interface XADC             xadc;
   (* prefix="" *)
   interface pins             pins;
   interface Vector#(4, Clock) unused_clock;
   interface Vector#(4, Reset) unused_reset;
endinterface

typedef (function Module#(PortalTop#(32, 64, ipins)) mkpt(Clock clock200, Clock io_vita_clk)) MkPortalTop#(type ipins);

module [Module] mkZynqTopFromPortal#(Clock io_vita_clk_out_p, Clock io_vita_clk_out_n, MkPortalTop#(ipins) constructor)(ZynqTop#(ipins));
   Clock io_vita_clk <- XilinxCells::mkClockIBUFDS(io_vita_clk_out_p, io_vita_clk_out_n);
   PS7 ps7 <- mkPS7();
   Clock mainclock = ps7.fclkclk[0];
   Reset mainreset = ps7.fclkreset[0];

   // I have a feeling the fclkclk[3] is not what we want. Jamey
   let top <- constructor(ps7.fclkclk[3], io_vita_clk, clocked_by mainclock, reset_by mainreset);

   mkConnection(ps7.m_axi_gp[0].client, top.ctrl);
   mkConnectionWithTrace(top.m_axi, ps7.s_axi_hp[0].axi.server);

   let intr_mux <- mkInterruptMux(top.interrupt);
   rule send_int_rule;
      ps7.interrupt(pack(intr_mux));
   endrule

   interface zynq = ps7.pins;
   interface leds = top.leds;
   interface XADC xadc;
       method Bit#(4) gpio;
           return 0;
       endmethod
   endinterface
   interface pins = top.pins;

   // these are exported to make bsc happy, and then the ports are disconnected after synthesis
   interface unused_clock = ps7.fclkclk;
   interface unused_reset = ps7.fclkreset;
endmodule

module mkImageonZynqTop#(Clock io_vita_clk_out_p, Clock io_vita_clk_out_n)(ZynqTop#(ImageonVita));
   let top <- mkZynqTopFromPortal(io_vita_clk_out_p, io_vita_clk_out_n, mkPortalTop);
   return top;
endmodule


silence "mkCtrl.writeData" message

silence "mkCtrl.writeData" message

// 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 FIFO::*;
import FIFOF::*;
import CtrlMux::*;

import Pipe::*;
import Portal::*;
import MemTypes::*;
import AddressGenerator::*;

typedef struct {
    Bit#(1) select;
    Bit#(6) tag;
} ReadReqInfo deriving (Bits);

interface PortalCtrlMemSlave#(numeric type addrWidth, numeric type dataWidth);
   interface MemSlave#(addrWidth, dataWidth) memSlave;
   interface ReadOnly#(Bool) interrupt;
endinterface

module mkPortalCtrlMemSlave#(Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indicationPipes)(PortalCtrlMemSlave#(addrWidth, dataWidth));
   AddressGenerator#(addrWidth) ctrlReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth) ctrlWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(ObjectTagSize))        ctrlWriteDoneFifo <- mkFIFO();

    // indication-specific state
    Reg#(Bit#(dataWidth)) underflowReadCountReg <- mkReg(0);
    Reg#(Bit#(dataWidth)) outOfRangeReadCountReg <- mkReg(0);
    Reg#(Bit#(dataWidth)) outOfRangeWriteCount <- mkReg(0);
    function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
    Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, indicationPipes);

    Reg#(Bool) interruptEnableReg <- mkReg(False);
    Bool      interruptStatus = False;

    Bit#(dataWidth)  readyChannel = -1;
    for (Integer i = 0; i < valueOf(numIndications); i = i + 1) begin
        if (readyBits[i]) begin
           interruptStatus = True;
           readyChannel = fromInteger(i);
        end
    end

   interface MemSlave memSlave;
      interface MemReadServer read_server;
	 interface Put readReq = ctrlReadAddrGenerator.request;
	 interface Get readData;
	    method ActionValue#(MemData#(dataWidth)) get();
	       let b <- ctrlReadAddrGenerator.addrBeat.get();
	       let addr = b.addr;
	       let v = 'h05a05a0;
	       if (addr == 'h000)
		  v = interruptStatus ? 1 : 0;
	       if (addr == 'h004)
		  v = interruptEnableReg ? 1 : 0;
	       if (addr == 'h008)
		  v = 7;
	       if (addr == 'h00C)
		  v = underflowReadCountReg;
	       if (addr == 'h010)
		  v = outOfRangeReadCountReg;
	       if (addr == 'h014)
		  v = outOfRangeWriteCount;
               if (addr == 'h018) begin
		  if (interruptStatus)
		     v = readyChannel+1;
		  else
		     v = 0;
               end
	       //$display("mkCtrl.readData addr=%h data=%h", b.addr, v);
	       return MemData { data: v, tag: b.tag, last: b.last };
	    endmethod
	 endinterface
      endinterface: read_server
      interface MemWriteServer write_server;
	 interface Put writeReq = ctrlWriteAddrGenerator.request;
	 interface Put writeData;
	    method Action put(MemData#(dataWidth) d);
	       let b <- ctrlWriteAddrGenerator.addrBeat.get();
	       //$display("mkCtrl.writeData addr=%h data=%h last=%d", b.addr, d.data, b.last);
	       let v = d.data;
	       let addr = b.addr;
	       if (addr == 'h000)
		  noAction;
	       if (addr == 'h004)
		  interruptEnableReg <= v[0] == 1'd1;
	       if (b.last)
		  ctrlWriteDoneFifo.enq(b.tag);
	    endmethod
	 endinterface
	 interface Get writeDone;
	    method ActionValue#(Bit#(ObjectTagSize)) get();
	       let tag <- toGet(ctrlWriteDoneFifo).get();
	       return tag;
	    endmethod
	 endinterface
      endinterface: write_server
   endinterface: memSlave
   interface ReadOnly interrupt;
      method Bool _read();
	 return interruptStatus && interruptEnableReg;
      endmethod
   endinterface
endmodule

module mkPipeInMemSlave#(PipeIn#(Bit#(dataWidth)) methodPipe)(MemSlave#(addrWidth, dataWidth));

   AddressGenerator#(addrWidth) fifoReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(ObjectTagSize))        fifoWriteDoneFifo <- mkFIFO();
   FIFO#(Bool)                           putFailedFifo <- mkFIFO();

   interface MemReadServer read_server;
      interface Put readReq = fifoReadAddrGenerator.request;
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let b <- fifoReadAddrGenerator.addrBeat.get();
	    return MemData { data: 0, tag: b.tag, last: b.last };
	 endmethod
      endinterface
   endinterface
   interface MemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeInMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	    if (methodPipe.notFull()) begin
	       // FIXME: handle putFailed
	       methodPipe.enq(d.data);
	    end
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkPipeOutMemSlave#(PipeOut#(Bit#(dataWidth)) methodPipe)(MemSlave#(addrWidth, dataWidth));
   AddressGenerator#(addrWidth) fifoReadAddrGenerator <- mkAddressGenerator();
   AddressGenerator#(addrWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(ObjectTagSize))                  fifoWriteDoneFifo <- mkFIFO();
   interface MemReadServer read_server;
      interface Put readReq;
	 method Action put(MemRequest#(addrWidth) req);
	    fifoReadAddrGenerator.request.put(req);
	    if (!methodPipe.notEmpty())
	       $display("***\n\n underflow! \n\n****");
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let b <- fifoReadAddrGenerator.addrBeat.get();
	    let data <- toGet(methodPipe).get();
	    //$display("mkPipeOutMemSlave.readData.get addr=%h data=%h", b.addr, data);
	    return MemData { data: data, tag: b.tag, last: b.last };
	 endmethod
      endinterface
   endinterface
   interface MemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeOutMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkMemPortal#(Portal#(numRequests, numIndications, slaveDataWidth) portal)(MemPortal#(slaveAddrWidth, slaveDataWidth))
   provisos (Add#(c__, 15, slaveAddrWidth),
	     Add#(d__, 1, c__),
	     Max#(numRequests,1,numRequestsToMux),
	     Max#(numIndications,1,numIndicationsToMux),

	     Add#(a__, TLog#(numRequestsToMux), 6),
	     Add#(b__, TLog#(numIndicationsToMux), 6),
	     Add#(numIndications, e__, numIndicationsToMux),
	     Add#(numRequests, f__, numRequestsToMux),
	     Add#(numIndicationsToMux, g__, TAdd#(numIndications, 1)),
	     Add#(numRequestsToMux, h__, TAdd#(numRequests, 1))
	     );

   PipeIn#(Bit#(slaveDataWidth)) guardRequestPipe =
      (interface PipeIn#(Bit#(slaveDataWidth));
	  method Action enq(Bit#(slaveDataWidth) v) if (False); endmethod
	  method Bool notFull(); return False; endmethod
       endinterface);
   FIFOF#(Bit#(slaveDataWidth)) putFailedIndicationFifo <- mkFIFOF();
   PipeOut#(Bit#(slaveDataWidth)) putFailedIndicationPipe = toPipeOut(putFailedIndicationFifo);

   Vector#(numRequestsToMux,    PipeIn#(Bit#(slaveDataWidth)))     requestPipes = take(append(portal.requests, cons(guardRequestPipe, nil)));
   Vector#(numIndicationsToMux, PipeOut#(Bit#(slaveDataWidth))) indicationPipes = take(append(portal.indications, cons(putFailedIndicationPipe, nil)));
   Vector#(numRequestsToMux,    MemSlave#(8, slaveDataWidth))    requestMemSlaves <- mapM(mkPipeInMemSlave, requestPipes);
   Vector#(numIndicationsToMux, MemSlave#(8, slaveDataWidth)) indicationMemSlaves <- mapM(mkPipeOutMemSlave, indicationPipes);

   MemSlave#(14,slaveDataWidth) requestFifoMemSlave    <- mkMemSlaveMux(requestMemSlaves);
   MemSlave#(14,slaveDataWidth) indicationFifoMemSlave <- mkMemSlaveMux(indicationMemSlaves);

   MemSlave#(14,slaveDataWidth)     requestCtrlMemSlave <- mkPipeInMemSlave(guardRequestPipe);
   PortalCtrlMemSlave#(14,slaveDataWidth) indicationCtrlPort <- mkPortalCtrlMemSlave(indicationPipes);

   MemSlave#(15,slaveDataWidth) requestMemSlave <- mkMemSlaveMux(cons(requestFifoMemSlave, cons(requestCtrlMemSlave, nil)));
   MemSlave#(15,slaveDataWidth) indicationMemSlave <- mkMemSlaveMux(cons(indicationFifoMemSlave, cons(indicationCtrlPort.memSlave, nil)));

   MemSlave#(slaveAddrWidth,slaveDataWidth) memslave  <- mkMemSlaveMux(cons(requestMemSlave, cons(indicationMemSlave, nil)));

   method ifcId   = portal.ifcId;
   method ifcType = portal.ifcType;

   interface MemSlave slave = memslave;
   interface ReadOnly interrupt = indicationCtrlPort.interrupt;
endmodule

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

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

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

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

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

import Pipe::*;
import Portal::*;
import MemTypes::*;
import AddressGenerator::*;

typedef struct {
    Bit#(1) select;
    Bit#(6) tag;
} ReadReqInfo deriving (Bits);

interface PortalCtrlMemSlave#(numeric type addrWidth, numeric type dataWidth);
   interface MemSlave#(addrWidth, dataWidth) memSlave;
   interface ReadOnly#(Bool) interrupt;
endinterface

module mkPortalCtrlMemSlave#(Vector#(numIndications, PipeOut#(Bit#(dataWidth))) indicationPipes)(PortalCtrlMemSlave#(addrWidth, dataWidth));
   AddressGenerator#(addrWidth) ctrlReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth) ctrlWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(ObjectTagSize))        ctrlWriteDoneFifo <- mkFIFO();

    // indication-specific state
    Reg#(Bit#(dataWidth)) underflowReadCountReg <- mkReg(0);
    Reg#(Bit#(dataWidth)) outOfRangeReadCountReg <- mkReg(0);
    Reg#(Bit#(dataWidth)) outOfRangeWriteCount <- mkReg(0);
    function Bool pipeOutNotEmpty(PipeOut#(a) po); return po.notEmpty(); endfunction
    Vector#(numIndications, Bool) readyBits = map(pipeOutNotEmpty, indicationPipes);

    Reg#(Bool) interruptEnableReg <- mkReg(False);
    Bool      interruptStatus = False;

    Bit#(dataWidth)  readyChannel = -1;
    for (Integer i = 0; i < valueOf(numIndications); i = i + 1) begin
        if (readyBits[i]) begin
           interruptStatus = True;
           readyChannel = fromInteger(i);
        end
    end

   interface MemSlave memSlave;
      interface MemReadServer read_server;
	 interface Put readReq = ctrlReadAddrGenerator.request;
	 interface Get readData;
	    method ActionValue#(MemData#(dataWidth)) get();
	       let b <- ctrlReadAddrGenerator.addrBeat.get();
	       let addr = b.addr;
	       let v = 'h05a05a0;
	       if (addr == 'h000)
		  v = interruptStatus ? 1 : 0;
	       if (addr == 'h004)
		  v = interruptEnableReg ? 1 : 0;
	       if (addr == 'h008)
		  v = 7;
	       if (addr == 'h00C)
		  v = underflowReadCountReg;
	       if (addr == 'h010)
		  v = outOfRangeReadCountReg;
	       if (addr == 'h014)
		  v = outOfRangeWriteCount;
               if (addr == 'h018) begin
		  if (interruptStatus)
		     v = readyChannel+1;
		  else
		     v = 0;
               end
	       //$display("mkCtrl.readData addr=%h data=%h", b.addr, v);
	       return MemData { data: v, tag: b.tag, last: b.last };
	    endmethod
	 endinterface
      endinterface: read_server
      interface MemWriteServer write_server;
	 interface Put writeReq = ctrlWriteAddrGenerator.request;
	 interface Put writeData;
	    method Action put(MemData#(dataWidth) d);
	       let b <- ctrlWriteAddrGenerator.addrBeat.get();
	       $display("mkCtrl.writeData addr=%h data=%h last=%d", b.addr, d.data, b.last);
	       let v = d.data;
	       let addr = b.addr;
	       if (addr == 'h000)
		  noAction;
	       if (addr == 'h004)
		  interruptEnableReg <= v[0] == 1'd1;
	       if (b.last)
		  ctrlWriteDoneFifo.enq(b.tag);
	    endmethod
	 endinterface
	 interface Get writeDone;
	    method ActionValue#(Bit#(ObjectTagSize)) get();
	       let tag <- toGet(ctrlWriteDoneFifo).get();
	       return tag;
	    endmethod
	 endinterface
      endinterface: write_server
   endinterface: memSlave
   interface ReadOnly interrupt;
      method Bool _read();
	 return interruptStatus && interruptEnableReg;
      endmethod
   endinterface
endmodule

module mkPipeInMemSlave#(PipeIn#(Bit#(dataWidth)) methodPipe)(MemSlave#(addrWidth, dataWidth));

   AddressGenerator#(addrWidth) fifoReadAddrGenerator  <- mkAddressGenerator();
   AddressGenerator#(addrWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(ObjectTagSize))        fifoWriteDoneFifo <- mkFIFO();
   FIFO#(Bool)                           putFailedFifo <- mkFIFO();

   interface MemReadServer read_server;
      interface Put readReq = fifoReadAddrGenerator.request;
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let b <- fifoReadAddrGenerator.addrBeat.get();
	    return MemData { data: 0, tag: b.tag, last: b.last };
	 endmethod
      endinterface
   endinterface
   interface MemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeInMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	    if (methodPipe.notFull()) begin
	       // FIXME: handle putFailed
	       methodPipe.enq(d.data);
	    end
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkPipeOutMemSlave#(PipeOut#(Bit#(dataWidth)) methodPipe)(MemSlave#(addrWidth, dataWidth));
   AddressGenerator#(addrWidth) fifoReadAddrGenerator <- mkAddressGenerator();
   AddressGenerator#(addrWidth) fifoWriteAddrGenerator <- mkAddressGenerator();
   FIFO#(Bit#(ObjectTagSize))                  fifoWriteDoneFifo <- mkFIFO();
   interface MemReadServer read_server;
      interface Put readReq;
	 method Action put(MemRequest#(addrWidth) req);
	    fifoReadAddrGenerator.request.put(req);
	    if (!methodPipe.notEmpty())
	       $display("***\n\n underflow! \n\n****");
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let b <- fifoReadAddrGenerator.addrBeat.get();
	    let data <- toGet(methodPipe).get();
	    //$display("mkPipeOutMemSlave.readData.get addr=%h data=%h", b.addr, data);
	    return MemData { data: data, tag: b.tag, last: b.last };
	 endmethod
      endinterface
   endinterface
   interface MemWriteServer write_server;
      interface Put writeReq = fifoWriteAddrGenerator.request;
      interface Put writeData;
	 method Action put((MemData#(dataWidth)) d);
	    let b <- fifoWriteAddrGenerator.addrBeat.get();
	    //$display("mkPipeOutMemSlave.writeData.put addr=%h data=%h", b.addr, d.data);
	    if (b.last)
	       fifoWriteDoneFifo.enq(b.tag);
	 endmethod
      endinterface
      interface Get writeDone = toGet(fifoWriteDoneFifo);
   endinterface
endmodule

module mkMemPortal#(Portal#(numRequests, numIndications, slaveDataWidth) portal)(MemPortal#(slaveAddrWidth, slaveDataWidth))
   provisos (Add#(c__, 15, slaveAddrWidth),
	     Add#(d__, 1, c__),
	     Max#(numRequests,1,numRequestsToMux),
	     Max#(numIndications,1,numIndicationsToMux),

	     Add#(a__, TLog#(numRequestsToMux), 6),
	     Add#(b__, TLog#(numIndicationsToMux), 6),
	     Add#(numIndications, e__, numIndicationsToMux),
	     Add#(numRequests, f__, numRequestsToMux),
	     Add#(numIndicationsToMux, g__, TAdd#(numIndications, 1)),
	     Add#(numRequestsToMux, h__, TAdd#(numRequests, 1))
	     );

   PipeIn#(Bit#(slaveDataWidth)) guardRequestPipe =
      (interface PipeIn#(Bit#(slaveDataWidth));
	  method Action enq(Bit#(slaveDataWidth) v) if (False); endmethod
	  method Bool notFull(); return False; endmethod
       endinterface);
   FIFOF#(Bit#(slaveDataWidth)) putFailedIndicationFifo <- mkFIFOF();
   PipeOut#(Bit#(slaveDataWidth)) putFailedIndicationPipe = toPipeOut(putFailedIndicationFifo);

   Vector#(numRequestsToMux,    PipeIn#(Bit#(slaveDataWidth)))     requestPipes = take(append(portal.requests, cons(guardRequestPipe, nil)));
   Vector#(numIndicationsToMux, PipeOut#(Bit#(slaveDataWidth))) indicationPipes = take(append(portal.indications, cons(putFailedIndicationPipe, nil)));
   Vector#(numRequestsToMux,    MemSlave#(8, slaveDataWidth))    requestMemSlaves <- mapM(mkPipeInMemSlave, requestPipes);
   Vector#(numIndicationsToMux, MemSlave#(8, slaveDataWidth)) indicationMemSlaves <- mapM(mkPipeOutMemSlave, indicationPipes);

   MemSlave#(14,slaveDataWidth) requestFifoMemSlave    <- mkMemSlaveMux(requestMemSlaves);
   MemSlave#(14,slaveDataWidth) indicationFifoMemSlave <- mkMemSlaveMux(indicationMemSlaves);

   MemSlave#(14,slaveDataWidth)     requestCtrlMemSlave <- mkPipeInMemSlave(guardRequestPipe);
   PortalCtrlMemSlave#(14,slaveDataWidth) indicationCtrlPort <- mkPortalCtrlMemSlave(indicationPipes);

   MemSlave#(15,slaveDataWidth) requestMemSlave <- mkMemSlaveMux(cons(requestFifoMemSlave, cons(requestCtrlMemSlave, nil)));
   MemSlave#(15,slaveDataWidth) indicationMemSlave <- mkMemSlaveMux(cons(indicationFifoMemSlave, cons(indicationCtrlPort.memSlave, nil)));

   MemSlave#(slaveAddrWidth,slaveDataWidth) memslave  <- mkMemSlaveMux(cons(requestMemSlave, cons(indicationMemSlave, nil)));

   method ifcId   = portal.ifcId;
   method ifcType = portal.ifcType;

   interface MemSlave slave = memslave;
   interface ReadOnly interrupt = indicationCtrlPort.interrupt;
endmodule


fix merge

fix merge

// 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 Vector::*;
import FIFO::*;
import GetPut::*;
import BuildVector::*;
import Cntrs::*;
import Types::*;
import ProcTypes::*;
import SynthParam::*;
import Exec::*;
import Performance::*;
import BrPred::*;
import DirPredictor::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReorderBuffer::*;
import SpecFifo::*;
import HasSpecBits::*;
import Bypass::*;

// ALU pipeline has 4 stages
// dispatch -> reg read -> exe -> finish (write reg)
// bypass is sent out from the end of exe stage
// and reg read stage will recv bypass

typedef struct {
    // inst info
    DecodedInst dInst;
    PhyRegs regs;
    InstTag tag;
    DirPredTrainInfo dpTrain;
    // specualtion
    Maybe#(SpecTag) spec_tag;
} AluDispatchToRegRead deriving(Bits, Eq, FShow);

typedef struct {
    // inst info
    DecodedInst dInst;
    Maybe#(PhyDst) dst;
    InstTag tag;
    DirPredTrainInfo dpTrain;
    // src reg vals & pc & ppc
    Data rVal1;
    Data rVal2;
    Addr pc;
    Addr ppc;
    // specualtion
    Maybe#(SpecTag) spec_tag;
} AluRegReadToExe deriving(Bits, Eq, FShow);

typedef struct {
    // inst info
    IType iType;
    Maybe#(PhyDst) dst;
    InstTag tag;
    DirPredTrainInfo dpTrain;
    // result
    Data data; // alu compute result
    Maybe#(Data) csrData; // data to write CSR file
    ControlFlow controlFlow;
    // speculation
    Maybe#(SpecTag) spec_tag;
} AluExeToFinish deriving(Bits, Eq, FShow);

// XXX currently ALU/Br should not have any exception, so we don't have cause feild above
// TODO FIXME In future, if branch target is unaligned to 4 bytes, we may have exception
// and probably JR/JAL should NOT write dst reg when exception happens
// However, currently JR/JAL renaming is included in the previous checkpoint
// so we need to explicitly check exception and don't write reg

// synthesized pipeline fifos
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluDispatchToRegRead) AluDispToRegFifo;
(* synthesize *)
module mkAluDispToRegFifo(AluDispToRegFifo);
    let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
    return m;
endmodule

// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluRegReadToExe) AluRegToExeFifo;
(* synthesize *)
module mkAluRegToExeFifo(AluRegToExeFifo);
    let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
    return m;
endmodule

// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_SB_deq_wrong_C_enq#(1, AluExeToFinish) AluExeToFinFifo;
(* synthesize *)
module mkAluExeToFinFifo(AluExeToFinFifo);
    let m <- mkSpecFifo_SB_deq_enq_SB_deq_wrong_C_enq(False);
    return m;
endmodule

typedef struct {
    Addr pc;
    Addr nextPc;
    IType iType;
    Bool taken;
    DirPredTrainInfo dpTrain;
    Bool mispred;
} FetchTrainBP deriving(Bits, Eq, FShow);

interface AluExeInput;
    // conservative scoreboard check in reg read stage
    method RegsReady sbCons_lazyLookup(PhyRegs r);
    // Phys reg file
    method Data rf_rd1(PhyRIndx rindx);
    method Data rf_rd2(PhyRIndx rindx);
    // CSR file
    method Data csrf_rd(CSR csr);
    // ROB
    method Addr rob_getPC(InstTag t);
    method Addr rob_getPredPC(InstTag t);
    method Action rob_setExecuted(InstTag t, Maybe#(Data) csrData, ControlFlow cf);
    // Fetch stage
    method Action fetch_train_predictors(FetchTrainBP train);

    // global broadcast methods
    // set aggressive sb & wake up inst in RS
    method Action setRegReadyAggr(PhyRIndx dst);
    // send bypass from exe and finish stage
    interface Vector#(2, SendBypass) sendBypass;
    // write reg file & set conservative sb
    method Action writeRegFile(PhyRIndx dst, Data data);
    // redirect
    method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
    // spec update
    method Action correctSpec(SpecTag t);

    // performance
    method Bool doStats;
endinterface

interface AluExePipeline;
    // recv bypass from exe and finish stages of each ALU pipeline
    interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
    interface ReservationStationAlu rsAluIfc;
    interface SpeculationUpdate specUpdate;
    method Data getPerf(ExeStagePerfType t);
endinterface

module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
    Bool verbose = True;

    // alu reservation station
    ReservationStationAlu rsAlu <- mkReservationStationAlu;
    // pipeline fifos
    let dispToRegQ <- mkAluDispToRegFifo;
    let regToExeQ <- mkAluRegToExeFifo;
    let exeToFinQ <- mkAluExeToFinFifo;
    // wire to recv bypass
    Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
    // index to send bypass, ordering doesn't matter
    Integer exeSendBypassPort = 0;
    Integer finishSendBypassPort = 1;

`ifdef PERF_COUNT
    // performance counters
    Count#(Data) exeRedirectBrCnt <- mkCount(0);
    Count#(Data) exeRedirectJrCnt <- mkCount(0);
    Count#(Data) exeRedirectOtherCnt <- mkCount(0);
`endif

    rule doDispatchAlu;
        rsAlu.doDispatch;
        let x = rsAlu.dispatchData;
        if(verbose) $display("[doDispatchAlu] ", fshow(x));

        // set reg ready aggressively
        if(x.regs.dst matches tagged Valid .dst) begin
            inIfc.setRegReadyAggr(dst.indx);
        end

        // go to next stage
        dispToRegQ.enq(ToSpecFifo {
            data: AluDispatchToRegRead {
                dInst: x.data.dInst,
                regs: x.regs,
                tag: x.tag,
                dpTrain: x.data.dpTrain,
                spec_tag: x.spec_tag
            },
            spec_bits: x.spec_bits
        });
    endrule

    rule doRegReadAlu;
        dispToRegQ.deq;
        let dispToReg = dispToRegQ.first;
        let x = dispToReg.data;
        if(verbose) $display("[doRegReadAlu] ", fshow(dispToReg));

        // check conservative scoreboard
        let regsReady = inIfc.sbCons_lazyLookup(x.regs);

        // get rVal1 (check bypass)
        Data rVal1 = ?;
        if(x.dInst.csr matches tagged Valid .csr) begin
            rVal1 = inIfc.csrf_rd(csr);
        end
        else if(x.regs.src1 matches tagged Valid .src1) begin
            rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
        end

        // get rVal2 (check bypass)
        Data rVal2 = ?;
        if(x.regs.src2 matches tagged Valid .src2) begin
            rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
        end

        // get PC and PPC
        let pc = inIfc.rob_getPC(x.tag);
        let ppc = inIfc.rob_getPredPC(x.tag);

        // go to next stage
        regToExeQ.enq(ToSpecFifo {
            data: AluRegReadToExe {
                dInst: x.dInst,
                dst: x.regs.dst,
                tag: x.tag,
                dpTrain: x.dpTrain,
                rVal1: rVal1,
                rVal2: rVal2,
                pc: pc,
                ppc: ppc,
                spec_tag: x.spec_tag
            },
            spec_bits: dispToReg.spec_bits
        });
    endrule

    rule doExeAlu;
        regToExeQ.deq;
        let regToExe = regToExeQ.first;
        let x = regToExe.data;
        if(verbose) $display("[doExeAlu] ", fshow(regToExe));

        // execution
        ExecResult exec_result = basicExec(x.dInst, x.rVal1, x.rVal2, x.pc, x.ppc);

        // when inst needs to store csrData in ROB, it must have iType = Csr, cannot mispredict
        if(isValid(x.dInst.csr)) begin
            doAssert(x.dInst.iType == Csr, "Only Csr inst needs to update csrData in ROB");
            doAssert(!exec_result.controlFlow.mispredict, "Csr inst cannot mispredict");
            doAssert(exec_result.controlFlow.nextPc == x.ppc && x.ppc == x.pc + 4, "Csr inst ppc = pc+4");
        end

        // send bypass
        if(x.dst matches tagged Valid .dst) begin
            inIfc.sendBypass[exeSendBypassPort].send(dst.indx, exec_result.data);
        end

        // go to next stage
        exeToFinQ.enq(ToSpecFifo {
            data: AluExeToFinish {
                iType: x.dInst.iType,
                dst: x.dst,
                tag: x.tag,
                dpTrain: x.dpTrain,
                data: exec_result.data,
                csrData: isValid(x.dInst.csr) ? Valid (exec_result.csrData) : Invalid,
                controlFlow: exec_result.controlFlow,
                spec_tag: x.spec_tag
            },
            spec_bits: regToExe.spec_bits
        });
    endrule

    rule doFinishAlu;
        exeToFinQ.deq;
        let exeToFin = exeToFinQ.first;
        let x = exeToFin.data;
        if(verbose) $display("[doFinishAlu] ", fshow(exeToFin));

        // send bypass & write reg file
        if(x.dst matches tagged Valid .dst) begin
            inIfc.sendBypass[finishSendBypassPort].send(dst.indx, x.data);
            inIfc.writeRegFile(dst.indx, x.data);
        end

        // update the instruction in the reorder buffer.
        inIfc.rob_setExecuted(
            x.tag,
            x.csrData,
            x.controlFlow
        );

        // handle spec tags for branch predictions
        (* split *)
        if (x.controlFlow.mispredict) (* nosplit *) begin
            // wrong branch predictin, we must have spec tag
            doAssert(isValid(x.spec_tag), "mispredicted branch must have spec tag");
            inIfc.redirect(x.controlFlow.nextPc, validValue(x.spec_tag), x.tag);
            // must be a branch, train branch predictor
            doAssert(x.iType == Jr || x.iType == Br, "only jr and br can mispredict");
            inIfc.fetch_train_predictors(FetchTrainBP {
                pc: x.controlFlow.pc,
                nextPc: x.controlFlow.nextPc,
                iType: x.iType,
                taken: x.controlFlow.taken,
                dpTrain: x.dpTrain,
                mispred: True
            });
`ifdef PERF_COUNT
            // performance counter
            if(inIfc.doStats) begin
                case(x.iType)
                    Br: exeRedirectBrCnt.incr(1);
                    Jr: exeRedirectJrCnt.incr(1);
                    default: exeRedirectOtherCnt.incr(1);
                endcase
            end
`endif
        end
        else (* nosplit *) begin
            // release spec tag
            if (x.spec_tag matches tagged Valid .valid_spec_tag) begin
                inIfc.correctSpec(valid_spec_tag);
            end
            // train branch predictor if needed
            // since we can only do 1 training in a cycle, split the rule
            // XXX not training JAL, reduce chance of conflicts
            if(x.iType == Jr || x.iType == Br) begin
                inIfc.fetch_train_predictors(FetchTrainBP {
                    pc: x.controlFlow.pc,
                    nextPc: x.controlFlow.nextPc,
                    iType: x.iType,
                    taken: x.controlFlow.taken,
                    dpTrain: x.dpTrain,
                    mispred: False
                });
            end
        end
    endrule

    interface recvBypass = map(getRecvBypassIfc, bypassWire);

    interface rsAluIfc = rsAlu;

    interface specUpdate = joinSpeculationUpdate(vec(
        rsAlu.specUpdate,
        dispToRegQ.specUpdate,
        regToExeQ.specUpdate,
        exeToFinQ.specUpdate
    ));

    method Data getPerf(ExeStagePerfType t);
        return (case(t)
`ifdef PERF_COUNT
            ExeRedirectBr: exeRedirectBrCnt;
            ExeRedirectJr: exeRedirectJrCnt;
            ExeRedirectOther: exeRedirectOtherCnt;
`endif
            default: 0;
        endcase);
    endmethod
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 Vector::*;
import FIFO::*;
import GetPut::*;
import BuildVector::*;
import Cntrs::*;
import Types::*;
import ProcTypes::*;
import SynthParam::*;
import Exec::*;
import Performance::*;
import BrPred::*;
import DirPredictor::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReorderBuffer::*;
import SpecFifo::*;
import HasSpecBits::*;
import Bypass::*;

// ALU pipeline has 4 stages
// dispatch -> reg read -> exe -> finish (write reg)
// bypass is sent out from the end of exe stage
// and reg read stage will recv bypass

typedef struct {
    // inst info
    DecodedInst dInst;
    PhyRegs regs;
    InstTag tag;
    DirPredTrainInfo dpTrain;
    // specualtion
    Maybe#(SpecTag) spec_tag;
} AluDispatchToRegRead deriving(Bits, Eq, FShow);

typedef struct {
    // inst info
    DecodedInst dInst;
    Maybe#(PhyDst) dst;
    InstTag tag;
    DirPredTrainInfo dpTrain;
    // src reg vals & pc & ppc
    Data rVal1;
    Data rVal2;
    Addr pc;
    Addr ppc;
    // specualtion
    Maybe#(SpecTag) spec_tag;
} AluRegReadToExe deriving(Bits, Eq, FShow);

typedef struct {
    // inst info
    IType iType;
    Maybe#(PhyDst) dst;
    InstTag tag;
    DirPredTrainInfo dpTrain;
    // result
    Data data; // alu compute result
    Maybe#(Data) csrData; // data to write CSR file
    ControlFlow controlFlow;
    // speculation
    Maybe#(SpecTag) spec_tag;
} AluExeToFinish deriving(Bits, Eq, FShow);

typedef struct {
    Addr pc;
    Addr nextPc;
    IType iType;
    Bool taken;
    DirPredTrainInfo dpTrain;
    Bool mispred;
} AluExeTrainBP deriving(Bits, Eq, FShow);

// XXX currently ALU/Br should not have any exception, so we don't have cause feild above
// TODO FIXME In future, if branch target is unaligned to 4 bytes, we may have exception
// and probably JR/JAL should NOT write dst reg when exception happens
// However, currently JR/JAL renaming is included in the previous checkpoint
// so we need to explicitly check exception and don't write reg

// synthesized pipeline fifos
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluDispatchToRegRead) AluDispToRegFifo;
(* synthesize *)
module mkAluDispToRegFifo(AluDispToRegFifo);
    let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
    return m;
endmodule

// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluRegReadToExe) AluRegToExeFifo;
(* synthesize *)
module mkAluRegToExeFifo(AluRegToExeFifo);
    let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
    return m;
endmodule

// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_SB_deq_wrong_C_enq#(1, AluExeToFinish) AluExeToFinFifo;
(* synthesize *)
module mkAluExeToFinFifo(AluExeToFinFifo);
    let m <- mkSpecFifo_SB_deq_enq_SB_deq_wrong_C_enq(False);
    return m;
endmodule

typedef struct {
    Addr pc;
    Addr nextPc;
    IType iType;
    Bool taken;
    DirPredTrainInfo dpTrain;
    Bool mispred;
} FetchTrainBP deriving(Bits, Eq, FShow);

interface AluExeInput;
    // conservative scoreboard check in reg read stage
    method RegsReady sbCons_lazyLookup(PhyRegs r);
    // Phys reg file
    method Data rf_rd1(PhyRIndx rindx);
    method Data rf_rd2(PhyRIndx rindx);
    // CSR file
    method Data csrf_rd(CSR csr);
    // ROB
    method Addr rob_getPC(InstTag t);
    method Addr rob_getPredPC(InstTag t);
    method Action rob_setExecuted(InstTag t, Maybe#(Data) csrData, ControlFlow cf);
    // Fetch stage
    method Action fetch_train_predictors(FetchTrainBP train);

    // global broadcast methods
    // set aggressive sb & wake up inst in RS
    method Action setRegReadyAggr(PhyRIndx dst);
    // send bypass from exe and finish stage
    interface Vector#(2, SendBypass) sendBypass;
    // write reg file & set conservative sb
    method Action writeRegFile(PhyRIndx dst, Data data);
    // redirect
    method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
    // spec update
    method Action correctSpec(SpecTag t);

    // performance
    method Bool doStats;
endinterface

interface AluExePipeline;
    // recv bypass from exe and finish stages of each ALU pipeline
    interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
    interface ReservationStationAlu rsAluIfc;
    interface SpeculationUpdate specUpdate;
    method Data getPerf(ExeStagePerfType t);
endinterface

module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
    Bool verbose = True;

    // alu reservation station
    ReservationStationAlu rsAlu <- mkReservationStationAlu;
    // pipeline fifos
    let dispToRegQ <- mkAluDispToRegFifo;
    let regToExeQ <- mkAluRegToExeFifo;
    let exeToFinQ <- mkAluExeToFinFifo;
    // wire to recv bypass
    Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
    // index to send bypass, ordering doesn't matter
    Integer exeSendBypassPort = 0;
    Integer finishSendBypassPort = 1;

    // train BP in a new cycle
    FIFO#(AluExeTrainBP) trainBPQ <- mkFIFO;

`ifdef PERF_COUNT
    // performance counters
    Count#(Data) exeRedirectBrCnt <- mkCount(0);
    Count#(Data) exeRedirectJrCnt <- mkCount(0);
    Count#(Data) exeRedirectOtherCnt <- mkCount(0);
`endif

    rule doDispatchAlu;
        rsAlu.doDispatch;
        let x = rsAlu.dispatchData;
        if(verbose) $display("[doDispatchAlu] ", fshow(x));

        // set reg ready aggressively
        if(x.regs.dst matches tagged Valid .dst) begin
            inIfc.setRegReadyAggr(dst.indx);
        end

        // go to next stage
        dispToRegQ.enq(ToSpecFifo {
            data: AluDispatchToRegRead {
                dInst: x.data.dInst,
                regs: x.regs,
                tag: x.tag,
                dpTrain: x.data.dpTrain,
                spec_tag: x.spec_tag
            },
            spec_bits: x.spec_bits
        });
    endrule

    rule doRegReadAlu;
        dispToRegQ.deq;
        let dispToReg = dispToRegQ.first;
        let x = dispToReg.data;
        if(verbose) $display("[doRegReadAlu] ", fshow(dispToReg));

        // check conservative scoreboard
        let regsReady = inIfc.sbCons_lazyLookup(x.regs);

        // get rVal1 (check bypass)
        Data rVal1 = ?;
        if(x.dInst.csr matches tagged Valid .csr) begin
            rVal1 = inIfc.csrf_rd(csr);
        end
        else if(x.regs.src1 matches tagged Valid .src1) begin
            rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
        end

        // get rVal2 (check bypass)
        Data rVal2 = ?;
        if(x.regs.src2 matches tagged Valid .src2) begin
            rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
        end

        // get PC and PPC
        let pc = inIfc.rob_getPC(x.tag);
        let ppc = inIfc.rob_getPredPC(x.tag);

        // go to next stage
        regToExeQ.enq(ToSpecFifo {
            data: AluRegReadToExe {
                dInst: x.dInst,
                dst: x.regs.dst,
                tag: x.tag,
                dpTrain: x.dpTrain,
                rVal1: rVal1,
                rVal2: rVal2,
                pc: pc,
                ppc: ppc,
                spec_tag: x.spec_tag
            },
            spec_bits: dispToReg.spec_bits
        });
    endrule

    rule doExeAlu;
        regToExeQ.deq;
        let regToExe = regToExeQ.first;
        let x = regToExe.data;
        if(verbose) $display("[doExeAlu] ", fshow(regToExe));

        // execution
        ExecResult exec_result = basicExec(x.dInst, x.rVal1, x.rVal2, x.pc, x.ppc);

        // when inst needs to store csrData in ROB, it must have iType = Csr, cannot mispredict
        if(isValid(x.dInst.csr)) begin
            doAssert(x.dInst.iType == Csr, "Only Csr inst needs to update csrData in ROB");
            doAssert(!exec_result.controlFlow.mispredict, "Csr inst cannot mispredict");
            doAssert(exec_result.controlFlow.nextPc == x.ppc && x.ppc == x.pc + 4, "Csr inst ppc = pc+4");
        end

        // send bypass
        if(x.dst matches tagged Valid .dst) begin
            inIfc.sendBypass[exeSendBypassPort].send(dst.indx, exec_result.data);
        end

        // go to next stage
        exeToFinQ.enq(ToSpecFifo {
            data: AluExeToFinish {
                iType: x.dInst.iType,
                dst: x.dst,
                tag: x.tag,
                dpTrain: x.dpTrain,
                data: exec_result.data,
                csrData: isValid(x.dInst.csr) ? Valid (exec_result.csrData) : Invalid,
                controlFlow: exec_result.controlFlow,
                spec_tag: x.spec_tag
            },
            spec_bits: regToExe.spec_bits
        });
    endrule

    rule doFinishAlu;
        exeToFinQ.deq;
        let exeToFin = exeToFinQ.first;
        let x = exeToFin.data;
        if(verbose) $display("[doFinishAlu] ", fshow(exeToFin));

        // send bypass & write reg file
        if(x.dst matches tagged Valid .dst) begin
            inIfc.sendBypass[finishSendBypassPort].send(dst.indx, x.data);
            inIfc.writeRegFile(dst.indx, x.data);
        end

        // update the instruction in the reorder buffer.
        inIfc.rob_setExecuted(
            x.tag,
            x.csrData,
            x.controlFlow
        );

        // handle spec tags for branch predictions
        (* split *)
        if (x.controlFlow.mispredict) (* nosplit *) begin
            // wrong branch predictin, we must have spec tag
            doAssert(isValid(x.spec_tag), "mispredicted branch must have spec tag");
            inIfc.redirect(x.controlFlow.nextPc, validValue(x.spec_tag), x.tag);
            // must be a branch, train branch predictor
            doAssert(x.iType == Jr || x.iType == Br, "only jr and br can mispredict");
            trainBPQ.enq(AluExeTrainBP {
                pc: x.controlFlow.pc,
                nextPc: x.controlFlow.nextPc,
                iType: x.iType,
                taken: x.controlFlow.taken,
                dpTrain: x.dpTrain,
                mispred: True
            });
`ifdef PERF_COUNT
            // performance counter
            if(inIfc.doStats) begin
                case(x.iType)
                    Br: exeRedirectBrCnt.incr(1);
                    Jr: exeRedirectJrCnt.incr(1);
                    default: exeRedirectOtherCnt.incr(1);
                endcase
            end
`endif
        end
        else (* nosplit *) begin
            // release spec tag
            if (x.spec_tag matches tagged Valid .valid_spec_tag) begin
                inIfc.correctSpec(valid_spec_tag);
            end
            // train branch predictor if needed
            // since we can only do 1 training in a cycle, split the rule
            // XXX not training JAL, reduce chance of conflicts
            if(x.iType == Jr || x.iType == Br) begin
                trainBPQ.enq(AluExeTrainBP {
                    pc: x.controlFlow.pc,
                    nextPc: x.controlFlow.nextPc,
                    iType: x.iType,
                    taken: x.controlFlow.taken,
                    dpTrain: x.dpTrain,
                    mispred: False
                });
            end
        end
    endrule

    rule doTrainBP;
        let x <- toGet(trainBPQ).get;
        inIfc.fetch_train_predictors(
            x.pc, x.nextPc, x.iType, x.taken, x.dpTrain, x.mispred
        );
    endrule

    interface recvBypass = map(getRecvBypassIfc, bypassWire);

    interface rsAluIfc = rsAlu;

    interface specUpdate = joinSpeculationUpdate(vec(
        rsAlu.specUpdate,
        dispToRegQ.specUpdate,
        regToExeQ.specUpdate,
        exeToFinQ.specUpdate
    ));

    method Data getPerf(ExeStagePerfType t);
        return (case(t)
`ifdef PERF_COUNT
            ExeRedirectBr: exeRedirectBrCnt;
            ExeRedirectJr: exeRedirectJrCnt;
            ExeRedirectOther: exeRedirectOtherCnt;
`endif
            default: 0;
        endcase);
    endmethod
endmodule