Fixed warning for rv32 compilation

Fixed warning for rv32 compilation

// Copyright (c) 2016 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 ClientServer::*;
import GetPut::*;
import Vector::*;

import CompareProvisos::*;
import Ehr::*;

import Abstraction::*;
import RVTypes::*;

interface MemoryMappedCSRs#(numeric type cores);
    // memory-mapped interface
    // 0x0000 = timer
    // 0x0008 = timecmp0
    // 0x0010 = timecmp1
    //  ...
    // 0x1000 = ipi0
    // 0x2000 = ipi1
    //  ...
    interface UncachedMemServer memifc;
    method Bit#(64) timerValue;
    method Vector#(cores, Bool) timerInterrupt;
    method Vector#(cores, Bool) ipi;
endinterface

module mkMemoryMappedCSRs#(PAddr baseaddr)(MemoryMappedCSRs#(cores)) provisos (LT#(cores, 16));
`ifdef rv32
    // TODO: needs to support single-word writes to support RV32
    warningM("mkMemoryMappedCSRs does not support RV32 yet");
`else
    // this doesn't work for 16 or more cores since it assumes a 16 bit address space
    Reg#(Maybe#(UncachedMemResp)) resp <- mkReg(tagged Invalid);

    Reg#(Bit#(10)) subTimer <- mkReg(0);
    Reg#(Bit#(64)) timer <- mkReg(0);
    Ehr#(2, Maybe#(Bit#(64))) newTimeEhr <- mkEhr(tagged Invalid);
    Vector#(cores, Reg#(Bit#(64))) timeCmp <- replicateM(mkReg('1));
    Vector#(cores, Reg#(Bool)) ipiReg <- replicateM(mkReg(False));

    rule incrementTimer;
        if (newTimeEhr[1] matches tagged Valid .validNewTime) begin
            subTimer <= 0;
            timer <= validNewTime;
        end else begin
            Bool overflow = subTimer == 999;
            subTimer <= overflow ? 0 : subTimer + 1;
            timer <= overflow ? timer + 1 : timer;
        end
        newTimeEhr[1] <= tagged Invalid;
    endrule

    interface UncachedMemServer memifc;
        interface Put request;
            method Action put(UncachedMemReq req) if (!isValid(resp));
                UncachedMemResp newResp = UncachedMemResp{write: req.write, data: 0};
                Bit#(16) addr = truncate(req.addr - baseaddr);
                if (addr < 16'h1000) begin
                    // RTC registers
                    if (((addr & 16'h0007) == 0) && (req.size == D)) begin
                        let index = addr >> 3;
                        if (index <= fromInteger(valueof(cores))) begin
                            if (index == 0) begin
                                if (req.write) begin
                                    newTimeEhr[0] <= tagged Valid req.data;
                                end else begin
                                    newResp.data = timer;
                                end
                            end else begin
                                if (req.write) begin
                                    timeCmp[index] <= req.data;
                                end else begin
                                    newResp.data = timeCmp[index];
                                end
                            end
                        end else begin
                            $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                        end
                    end else begin
                        $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                    end
                end else begin
                    // IPI registers
                    if ((addr & 16'h0FFF) == 0) begin
                        let index = addr >> 12;
                        if (index < fromInteger(valueof(cores))) begin
                            if (req.write) begin
                                ipiReg[index] <= unpack(req.data[0]);
                            end else begin
                                newResp.data = zeroExtend(pack(ipiReg[index]));
                            end
                        end else begin
                            $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                        end
                    end else begin
                        $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                    end
                end
                resp <= tagged Valid newResp;
            endmethod
        endinterface
        interface Get response;
            method ActionValue#(UncachedMemResp) get if (resp matches tagged Valid .validResp);
                resp <= tagged Invalid;
                return validResp;
            endmethod
        endinterface
    endinterface
    method Bit#(64) timerValue = timer;
    method Vector#(cores, Bool) timerInterrupt = zipWith( \>= , replicate(timer), readVReg(timeCmp));
    method Vector#(cores, Bool) ipi = readVReg(ipiReg);
`endif
endmodule

// Copyright (c) 2016 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 ClientServer::*;
import GetPut::*;
import Vector::*;

import CompareProvisos::*;
import Ehr::*;

import Abstraction::*;
import RVTypes::*;

interface MemoryMappedCSRs#(numeric type cores);
    // memory-mapped interface
    // 0x0000 = timer
    // 0x0008 = timecmp0
    // 0x0010 = timecmp1
    //  ...
    // 0x1000 = ipi0
    // 0x2000 = ipi1
    //  ...
    interface UncachedMemServer memifc;
    method Bit#(64) timerValue;
    method Vector#(cores, Bool) timerInterrupt;
    method Vector#(cores, Bool) ipi;
endinterface

module mkMemoryMappedCSRs#(PAddr baseaddr)(MemoryMappedCSRs#(cores)) provisos (LT#(cores, 16));
`ifdef rv32
    // TODO: needs to support single-word writes to support RV32
    warning("mkMemoryMappedCSRs does not support RV32 yet");
`else
    // this doesn't work for 16 or more cores since it assumes a 16 bit address space
    Reg#(Maybe#(UncachedMemResp)) resp <- mkReg(tagged Invalid);

    Reg#(Bit#(10)) subTimer <- mkReg(0);
    Reg#(Bit#(64)) timer <- mkReg(0);
    Ehr#(2, Maybe#(Bit#(64))) newTimeEhr <- mkEhr(tagged Invalid);
    Vector#(cores, Reg#(Bit#(64))) timeCmp <- replicateM(mkReg('1));
    Vector#(cores, Reg#(Bool)) ipiReg <- replicateM(mkReg(False));

    rule incrementTimer;
        if (newTimeEhr[1] matches tagged Valid .validNewTime) begin
            subTimer <= 0;
            timer <= validNewTime;
        end else begin
            Bool overflow = subTimer == 999;
            subTimer <= overflow ? 0 : subTimer + 1;
            timer <= overflow ? timer + 1 : timer;
        end
        newTimeEhr[1] <= tagged Invalid;
    endrule

    interface UncachedMemServer memifc;
        interface Put request;
            method Action put(UncachedMemReq req) if (!isValid(resp));
                UncachedMemResp newResp = UncachedMemResp{write: req.write, data: 0};
                Bit#(16) addr = truncate(req.addr - baseaddr);
                if (addr < 16'h1000) begin
                    // RTC registers
                    if (((addr & 16'h0007) == 0) && (req.size == D)) begin
                        let index = addr >> 3;
                        if (index <= fromInteger(valueof(cores))) begin
                            if (index == 0) begin
                                if (req.write) begin
                                    newTimeEhr[0] <= tagged Valid req.data;
                                end else begin
                                    newResp.data = timer;
                                end
                            end else begin
                                if (req.write) begin
                                    timeCmp[index] <= req.data;
                                end else begin
                                    newResp.data = timeCmp[index];
                                end
                            end
                        end else begin
                            $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                        end
                    end else begin
                        $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                    end
                end else begin
                    // IPI registers
                    if ((addr & 16'h0FFF) == 0) begin
                        let index = addr >> 12;
                        if (index < fromInteger(valueof(cores))) begin
                            if (req.write) begin
                                ipiReg[index] <= unpack(req.data[0]);
                            end else begin
                                newResp.data = zeroExtend(pack(ipiReg[index]));
                            end
                        end else begin
                            $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                        end
                    end else begin
                        $fdisplay(stderr, "[ERROR] MemoryMappedCSRs: unexpected mem request: ", fshow(req));
                    end
                end
                resp <= tagged Valid newResp;
            endmethod
        endinterface
        interface Get response;
            method ActionValue#(UncachedMemResp) get if (resp matches tagged Valid .validResp);
                resp <= tagged Invalid;
                return validResp;
            endmethod
        endinterface
    endinterface
    method Bit#(64) timerValue = timer;
    method Vector#(cores, Bool) timerInterrupt = zipWith( \>= , replicate(timer), readVReg(timeCmp));
    method Vector#(cores, Bool) ipi = readVReg(ipiReg);
`endif
endmodule


simplify mkDmaReadBuffers

simplify mkDmaReadBuffers

Change-Id: I651bff457ab0743c777f6de6c1406207dac9c516

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

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

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

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

import BRAM::*;
import FIFO::*;
import Vector::*;
import Gearbox::*;
import FIFOF::*;
import SpecialFIFOs::*;

import BRAMFIFOFLevel::*;
import GetPut::*;
import Dma::*;

function ObjectReadClient#(dataWidth) orc(DmaReadBuffer#(dataWidth,bufferDepth) rb) = rb.dmaClient;
function ObjectWriteClient#(dataWidth) owc(DmaWriteBuffer#(dataWidth,bufferDepth) wb) = wb.dmaClient;
function ObjectReadServer#(dataWidth) ors(DmaReadBuffer#(dataWidth,bufferDepth) rb) = rb.dmaServer;
function ObjectWriteServer#(dataWidth) ows(DmaWriteBuffer#(dataWidth,bufferDepth) wb) = wb.dmaServer;

//
// @brief A buffer for reading from a bus of width dataWidth.
//
// @param dataWidth The number of bits in the bus.
// @param bufferDepth The depth of the internal buffer
//
interface DmaReadBuffer#(numeric type dataWidth, numeric type bufferDepth);
   interface ObjectReadServer #(dataWidth) dmaServer;
   interface ObjectReadClient#(dataWidth) dmaClient;
endinterface

//
// @brief A buffer for writing to a bus of width dataWidth.
//
// @param dataWidth The number of bits in the bus.
// @param bufferDepth The depth of the internal buffer
//
interface DmaWriteBuffer#(numeric type dataWidth, numeric type bufferDepth);
   interface ObjectWriteServer#(dataWidth) dmaServer;
   interface ObjectWriteClient#(dataWidth) dmaClient;
endinterface

//
// @brief Makes a Dma buffer for reading wordSize words from memory.
//
// @param dataWidth The width of the bus in bits.
// @param bufferDepth The depth of the internal buffer
//
module mkDmaReadBuffer(DmaReadBuffer#(dataWidth, bufferDepth))
   provisos(Add#(b__, TAdd#(1,TLog#(bufferDepth)), 8),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift));

   FIFO#(ObjectData#(dataWidth))  readBuffer <- mkSizedFIFO(valueOf(bufferDepth));
   FIFOF#(ObjectRequest)        reqOutstanding <- mkFIFOF();
   FIFOF#(ObjectRequest)        reqReady       <- mkFIFOF();
   Ratchet#(TAdd#(1,TLog#(bufferDepth))) availableBuffers <- mkRatchet(fromInteger(valueOf(bufferDepth)));
   let beat_shift = fromInteger(valueOf(beatShift));

   Bit#(TAdd#(1,TLog#(bufferDepth))) requested = truncate(reqOutstanding.first.burstLen>>beat_shift);
   Bool ready = reqOutstanding.notEmpty && (unpack(requested) <= availableBuffers.read());
   // rule updateReady;
   //    Bit#(TAdd#(1,TLog#(bufferDepth))) requested = truncate(reqOutstanding.first.burstLen>>beat_shift);
   //    Bool ready = (unpack(requested) <= availableBuffers.read());
   //    if (ready) begin
   // 	 let req <- toGet(reqOutstanding).get();
   // 	 reqReady.enq(req);
   // 	 availableBuffers.decrement(unpack(requested));
   //    end
   // endrule

   // only issue the readRequest when sufficient buffering is available.  This includes the buffering we have already committed.
   interface ObjectReadServer dmaServer;
      interface Put readReq = toPut(reqOutstanding);
      interface Get readData;
	 method ActionValue#(ObjectData#(dataWidth)) get();
	    availableBuffers.increment(1);
	    let resp <- toGet(readBuffer).get();
	    return resp;
	 endmethod
      endinterface
   endinterface
   interface ObjectReadClient dmaClient;
      interface Get readReq;
	 method ActionValue#(ObjectRequest) get if (ready);
	    let req <- toGet(reqOutstanding).get();
	    availableBuffers.decrement(unpack(requested));
	    return req;
	 endmethod
      endinterface
      interface Put readData;
	 method Action put(ObjectData#(dataWidth) x);
	    readBuffer.enq(x);
	 endmethod
      endinterface
   endinterface
endmodule

//
// @brief Makes a Dma channel for writing wordSize words from memory.
//
// @param dataWidth The width of the bus in bits.
// @param bufferDepth The depth of the internal buffer
//
module mkDmaWriteBuffer(DmaWriteBuffer#(dataWidth, bufferDepth))
   provisos(Add#(b__, TAdd#(1, TLog#(bufferDepth)), 8),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift));

   FIFO#(ObjectData#(dataWidth)) writeBuffer <- mkSizedFIFO(valueOf(bufferDepth));
   FIFOF#(ObjectRequest)        reqOutstanding <- mkFIFOF();
   FIFOF#(Bit#(ObjectTagSize))  doneTags <- mkFIFOF();
   Ratchet#(TAdd#(1,TLog#(bufferDepth))) availableWords <- mkRatchet(fromInteger(valueOf(bufferDepth)));
   let beat_shift = fromInteger(valueOf(beatShift));

   FIFOF#(Bool) hasEnoughCapacity <- mkFIFOF();
   rule updateReady;
      Bit#(TAdd#(1,TLog#(bufferDepth))) requested = truncate(reqOutstanding.first.burstLen>>beat_shift);
      if (unpack(truncate(reqOutstanding.first.burstLen>>beat_shift)) <= availableWords.read())
	 hasEnoughCapacity.enq(True);
   endrule

   // only issue the writeRequest when sufficient data is available.  This includes the data we have already committed.
   interface ObjectWriteServer dmaServer;
      interface Put writeReq = toPut(reqOutstanding);
      interface Put writeData;
	 method Action put(ObjectData#(dataWidth) d);
	    writeBuffer.enq(d);
	    availableWords.increment(1);
	 endmethod
      endinterface
      interface Get writeDone = toGet(doneTags);
   endinterface
   interface ObjectWriteClient dmaClient;
      interface Get writeReq;
	 method ActionValue#(ObjectRequest) get if (hasEnoughCapacity.notEmpty());
	    hasEnoughCapacity.deq();
	    reqOutstanding.deq;
	    availableWords.decrement(unpack(truncate(reqOutstanding.first.burstLen>>beat_shift)));
	    return reqOutstanding.first;
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(ObjectData#(dataWidth)) get();
	    writeBuffer.deq;
	    return writeBuffer.first;
	 endmethod
      endinterface
      interface Put writeDone = toPut(doneTags);
   endinterface
endmodule

module mkDmaReadMux#(Vector#(numClients,ObjectReadClient#(dataWidth)) readClients)(ObjectReadClient#(dataWidth))
   provisos(Log#(numClients,tagsz),
	    Add#(tagsz,a__,ObjectTagSize));

   FIFO#(ObjectRequest)          readReqFifo  <- mkFIFO();
   FIFO#(ObjectData#(dataWidth)) readRespFifo <- mkFIFO();

   for (Integer i = 0; i < valueOf(numClients); i = i + 1) begin
      // assume fixed tag per client
      Reg#(Bit#(ObjectTagSize)) tagReg <- mkReg(0);
      rule getreq;
	 let req <- readClients[i].readReq.get();
	 tagReg <= req.tag;
	 req.tag = fromInteger(i);
	 readReqFifo.enq(req);
      endrule
      rule sendresp if (readRespFifo.first.tag == fromInteger(i));
	 let resp <- toGet(readRespFifo).get();
	 resp.tag = tagReg;
	 readClients[i].readData.put(resp);
      endrule
   end

   interface Get readReq = toGet(readReqFifo);
   interface Put readData = toPut(readRespFifo);
endmodule

module mkDmaWriteMux#(Vector#(numClients,ObjectWriteClient#(dataWidth)) writeClients)(ObjectWriteClient#(dataWidth))
   provisos(Log#(numClients,tagsz),
	    Add#(tagsz,a__,ObjectTagSize));

   FIFO#(ObjectRequest)          writeReqFifo  <- mkFIFO();
   FIFO#(ObjectData#(dataWidth)) writeDataFifo <- mkFIFO();
   FIFO#(Bit#(ObjectTagSize))    writeDoneFifo <- mkFIFO();
   FIFO#(Bit#(ObjectTagSize))    arbFifo       <- mkFIFO();

   for (Integer i = 0; i < valueOf(numClients); i = i + 1) begin
      // assume fixed tag per client
      Reg#(Bit#(ObjectTagSize)) tagReg <- mkReg(0);
      rule getreq;
	 let req <- writeClients[i].writeReq.get();
	 tagReg <= req.tag;
	 req.tag = fromInteger(i);
	 writeReqFifo.enq(req);
	 arbFifo.enq(req.tag);
      endrule
      rule senddata if (fromInteger(i) == arbFifo.first);
	 let data <- writeClients[i].writeData.get();
	 data.tag = tagReg;
	 writeDataFifo.enq(data);
      endrule
      rule senddone if (writeDoneFifo.first == fromInteger(i));
	 arbFifo.deq();
	 let done <- toGet(writeDoneFifo).get();
	 writeClients[i].writeDone.put(tagReg);
      endrule
   end

   interface Get writeReq = toGet(writeReqFifo);
   interface Get writeData = toGet(writeDataFifo);
   interface Put writeDone = toPut(writeDoneFifo);
endmodule

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

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

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

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

import BRAM::*;
import FIFO::*;
import Vector::*;
import Gearbox::*;
import FIFOF::*;
import SpecialFIFOs::*;

import BRAMFIFOFLevel::*;
import GetPut::*;
import Dma::*;

function ObjectReadClient#(dataWidth) orc(DmaReadBuffer#(dataWidth,bufferDepth) rb) = rb.dmaClient;
function ObjectWriteClient#(dataWidth) owc(DmaWriteBuffer#(dataWidth,bufferDepth) wb) = wb.dmaClient;
function ObjectReadServer#(dataWidth) ors(DmaReadBuffer#(dataWidth,bufferDepth) rb) = rb.dmaServer;
function ObjectWriteServer#(dataWidth) ows(DmaWriteBuffer#(dataWidth,bufferDepth) wb) = wb.dmaServer;

//
// @brief A buffer for reading from a bus of width dataWidth.
//
// @param dataWidth The number of bits in the bus.
// @param bufferDepth The depth of the internal buffer
//
interface DmaReadBuffer#(numeric type dataWidth, numeric type bufferDepth);
   interface ObjectReadServer #(dataWidth) dmaServer;
   interface ObjectReadClient#(dataWidth) dmaClient;
endinterface

//
// @brief A buffer for writing to a bus of width dataWidth.
//
// @param dataWidth The number of bits in the bus.
// @param bufferDepth The depth of the internal buffer
//
interface DmaWriteBuffer#(numeric type dataWidth, numeric type bufferDepth);
   interface ObjectWriteServer#(dataWidth) dmaServer;
   interface ObjectWriteClient#(dataWidth) dmaClient;
endinterface

//
// @brief Makes a Dma buffer for reading wordSize words from memory.
//
// @param dataWidth The width of the bus in bits.
// @param bufferDepth The depth of the internal buffer
//
module mkDmaReadBuffer(DmaReadBuffer#(dataWidth, bufferDepth))
   provisos(Add#(b__, TAdd#(1,TLog#(bufferDepth)), 8),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift));

   FIFO#(ObjectData#(dataWidth))  readBuffer <- mkSizedFIFO(valueOf(bufferDepth));
   FIFOF#(ObjectRequest)        reqOutstanding <- mkFIFOF();
   FIFOF#(ObjectRequest)        reqReady       <- mkFIFOF();
   Ratchet#(TAdd#(1,TLog#(bufferDepth))) availableBuffers <- mkRatchet(fromInteger(valueOf(bufferDepth)));
   let beat_shift = fromInteger(valueOf(beatShift));

   rule updateReady;
      Bit#(TAdd#(1,TLog#(bufferDepth))) requested = truncate(reqOutstanding.first.burstLen>>beat_shift);
      Bool ready = (unpack(requested) <= availableBuffers.read());
      if (ready) begin
	 let req <- toGet(reqOutstanding).get();
	 reqReady.enq(req);
	 availableBuffers.decrement(unpack(requested));
      end
   endrule

   // only issue the readRequest when sufficient buffering is available.  This includes the buffering we have already committed.
   interface ObjectReadServer dmaServer;
      interface Put readReq = toPut(reqOutstanding);
      interface Get readData;
	 method ActionValue#(ObjectData#(dataWidth)) get();
	    availableBuffers.increment(1);
	    let resp <- toGet(readBuffer).get();
	    return resp;
	 endmethod
      endinterface
   endinterface
   interface ObjectReadClient dmaClient;
      interface Get readReq;
	 method ActionValue#(ObjectRequest) get;
	    let req <- toGet(reqReady).get();
	    return req;
	 endmethod
      endinterface
      interface Put readData;
	 method Action put(ObjectData#(dataWidth) x);
	    readBuffer.enq(x);
	 endmethod
      endinterface
   endinterface
endmodule

//
// @brief Makes a Dma channel for writing wordSize words from memory.
//
// @param dataWidth The width of the bus in bits.
// @param bufferDepth The depth of the internal buffer
//
module mkDmaWriteBuffer(DmaWriteBuffer#(dataWidth, bufferDepth))
   provisos(Add#(b__, TAdd#(1, TLog#(bufferDepth)), 8),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift));

   FIFO#(ObjectData#(dataWidth)) writeBuffer <- mkSizedFIFO(valueOf(bufferDepth));
   FIFOF#(ObjectRequest)        reqOutstanding <- mkFIFOF();
   FIFOF#(Bit#(ObjectTagSize))  doneTags <- mkFIFOF();
   Ratchet#(TAdd#(1,TLog#(bufferDepth))) availableWords <- mkRatchet(fromInteger(valueOf(bufferDepth)));
   let beat_shift = fromInteger(valueOf(beatShift));

   FIFOF#(Bool) hasEnoughCapacity <- mkFIFOF();
   rule updateReady;
      Bit#(TAdd#(1,TLog#(bufferDepth))) requested = truncate(reqOutstanding.first.burstLen>>beat_shift);
      if (unpack(truncate(reqOutstanding.first.burstLen>>beat_shift)) <= availableWords.read())
	 hasEnoughCapacity.enq(True);
   endrule

   // only issue the writeRequest when sufficient data is available.  This includes the data we have already committed.
   interface ObjectWriteServer dmaServer;
      interface Put writeReq = toPut(reqOutstanding);
      interface Put writeData;
	 method Action put(ObjectData#(dataWidth) d);
	    writeBuffer.enq(d);
	    availableWords.increment(1);
	 endmethod
      endinterface
      interface Get writeDone = toGet(doneTags);
   endinterface
   interface ObjectWriteClient dmaClient;
      interface Get writeReq;
	 method ActionValue#(ObjectRequest) get if (hasEnoughCapacity.notEmpty());
	    hasEnoughCapacity.deq();
	    reqOutstanding.deq;
	    availableWords.decrement(unpack(truncate(reqOutstanding.first.burstLen>>beat_shift)));
	    return reqOutstanding.first;
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(ObjectData#(dataWidth)) get();
	    writeBuffer.deq;
	    return writeBuffer.first;
	 endmethod
      endinterface
      interface Put writeDone = toPut(doneTags);
   endinterface
endmodule

module mkDmaReadMux#(Vector#(numClients,ObjectReadClient#(dataWidth)) readClients)(ObjectReadClient#(dataWidth))
   provisos(Log#(numClients,tagsz),
	    Add#(tagsz,a__,ObjectTagSize));

   FIFO#(ObjectRequest)          readReqFifo  <- mkFIFO();
   FIFO#(ObjectData#(dataWidth)) readRespFifo <- mkFIFO();

   for (Integer i = 0; i < valueOf(numClients); i = i + 1) begin
      // assume fixed tag per client
      Reg#(Bit#(ObjectTagSize)) tagReg <- mkReg(0);
      rule getreq;
	 let req <- readClients[i].readReq.get();
	 tagReg <= req.tag;
	 req.tag = fromInteger(i);
	 readReqFifo.enq(req);
      endrule
      rule sendresp if (readRespFifo.first.tag == fromInteger(i));
	 let resp <- toGet(readRespFifo).get();
	 resp.tag = tagReg;
	 readClients[i].readData.put(resp);
      endrule
   end

   interface Get readReq = toGet(readReqFifo);
   interface Put readData = toPut(readRespFifo);
endmodule

module mkDmaWriteMux#(Vector#(numClients,ObjectWriteClient#(dataWidth)) writeClients)(ObjectWriteClient#(dataWidth))
   provisos(Log#(numClients,tagsz),
	    Add#(tagsz,a__,ObjectTagSize));

   FIFO#(ObjectRequest)          writeReqFifo  <- mkFIFO();
   FIFO#(ObjectData#(dataWidth)) writeDataFifo <- mkFIFO();
   FIFO#(Bit#(ObjectTagSize))    writeDoneFifo <- mkFIFO();
   FIFO#(Bit#(ObjectTagSize))    arbFifo       <- mkFIFO();

   for (Integer i = 0; i < valueOf(numClients); i = i + 1) begin
      // assume fixed tag per client
      Reg#(Bit#(ObjectTagSize)) tagReg <- mkReg(0);
      rule getreq;
	 let req <- writeClients[i].writeReq.get();
	 tagReg <= req.tag;
	 req.tag = fromInteger(i);
	 writeReqFifo.enq(req);
	 arbFifo.enq(req.tag);
      endrule
      rule senddata if (fromInteger(i) == arbFifo.first);
	 let data <- writeClients[i].writeData.get();
	 data.tag = tagReg;
	 writeDataFifo.enq(data);
      endrule
      rule senddone if (writeDoneFifo.first == fromInteger(i));
	 arbFifo.deq();
	 let done <- toGet(writeDoneFifo).get();
	 writeClients[i].writeDone.put(tagReg);
      endrule
   end

   interface Get writeReq = toGet(writeReqFifo);
   interface Get writeData = toGet(writeDataFifo);
   interface Put writeDone = toPut(writeDoneFifo);
endmodule


remember to save vars!

remember to save vars!

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

/*
 * Implementation of:
 *    simple stack for a type
 *
 * Single cycle push and pop, provided that consecutive pops aren't
 * too far apart
 */

import BRAM::*;

interface StackReg#(numeric type stackSize, type pctype, type argstype, type varstype);
   method Action doreturn();
   method Action docall(pctype jumpto, pctype returnto, argstype args, varstype vars);
   method Action nextpc(pctype jumpto);
   method Bit#(16) setjmp();
   method Action longjump(Bit#(16) where);
   interface Reg#(pctype) pc;
   interface Reg#(argstype) args;
   interface Reg#(varstype) vars;
endinterface

module mkStackReg#(int stackSize, pctype initialpc)(StackReg#(stackSize, pctype, argstype, varstype))
   provisos(Log#(stackSize, addressBits),
      Bits#(pctype, a__),
      Bits#(argstype, b__),
      Bits#(varstype, c__));

   BRAM1Port#(Bit#(addressBits), pctype) pcstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), argstype) argsstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), varstype) varsstack  <- mkBRAM1Server(defaultValue);
   Reg#(pctype) pctop <- mkReg(initialpc);
   Reg#(pctype) pcnext <- mkReg(?);
   Reg#(argstype) argstop <- mkReg(?);
   Reg#(argstype) argsnext <- mkReg(?);
   Reg#(varstype) varstop <- mkReg(?);
   Reg#(varstype) varsnext <- mkReg(?);
   Reg#(Bit#(addressBits)) fp <- mkReg(0);
   PulseWire calling <- mkPulseWire();
   PulseWire returning <- mkPulseWire();
   Reg#(Bool) returningd1 <- mkReg(False);

/* The "next" registers are there to compensate for the pipelined reads
 * of the BRAMs.
 * A return in isolation pops next to top, and then replaces the contents of
 * next the following cycle when the BRAM read completes.
 * In the event that there are multiple returns in consecutive cycles, the
 * first return values are fed from the next registers into top, and the
 * following values are bypassed from the BRAMs direct to TOP.
 * Once return cycles stop happening, the final pending BRAM read data is
 * loaded into next
 *
 * If a call happens in the cycle after a return, the BRAM read <into> next and
 * the bram write <from> next are both supressed, and next is pushed from top
 *
 * The wire calling says there is a call in the current cycle. The signal
 * returning says there is a return in the current cycle. The signal returningd1
 * says there was a return in the previous cycle.
 */

/*
   Reg#(Bit#(8)) cyc <- mkReg(0);

   rule cc;
      cyc <= cyc + 1;
   endrule
  */

   /* The pop rules fire exactly when returningd1 is true */
   rule poppc;
     let v = ?;
      //$display("%d poppc (c %d)", cyc, calling);
      v <- pcstack.portA.response.get();
      if (!calling) pcnext <= v;
      if (returning) pctop <= v;
   endrule

   rule popargs;
     let v = ?;
      v <- argsstack.portA.response.get();
      if (!calling) argsnext <= v;
      if (returning) argstop <= v;
   endrule

   rule popvars;
     let v = ?;
      v <- varsstack.portA.response.get();
      if (!calling) varsnext <= v;
      if (returning) varstop <= v;
   endrule

   (* fire_when_enabled, no_implicit_conditions *)
   rule returning_delay;
      returningd1 <= returning;
   endrule

   method Action docall(pctype jumpto, pctype returnto, argstype args, varstype vars);
      fp <= min(fp+1, maxBound);
      calling.send();
      //$display("%d docall jumpto %d returnto %d (d1 %d)", cyc, jumpto, returnto, returningd1);
      if (! returningd1)
	 begin
	    pcstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: pcnext});
	    argsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: argsnext});
	    varsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: varsnext});
	 end
      pcnext <= returnto;
      pctop <= jumpto;
      argsnext <= argstop;
      argstop <= args;
      varsnext <= varstop;
      varstp[ <= vars;
   endmethod

   method Action doreturn();
      fp <= max(fp-1, 0);
      returning.send();

      pcstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      argsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      varsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      //$display("%d doreturn pctop getting %d", cyc, pcnext);
     if (!returningd1)
	begin
	   pctop <= pcnext;
	   argstop <= argsnext;
	   varstop <= varsnext;
	end
   endmethod

   method Action nextpc(pctype jumpto);
      pctop <= jumpto;
   endmethod

   method Bit#(16) setjmp();
      return 0;
   endmethod

   method Action longjump(Bit#(16) where);
   // set fp <= where and pop? something like that
   endmethod

interface pc = pctop;
interface args = argstop;
interface vars = varstop;

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.

/*
 * Implementation of:
 *    simple stack for a type
 *
 * Single cycle push and pop, provided that consecutive pops aren't
 * too far apart
 */

import BRAM::*;

interface StackReg#(numeric type stackSize, type pctype, type argstype, type varstype);
   method Action doreturn();
   method Action docall(pctype jumpto, pctype returnto, argstype args, varstype vars);
   method Action nextpc(pctype jumpto);
   method Bit#(16) setjmp();
   method Action longjump(Bit#(16) where);
   interface Reg#(pctype) pc;
   interface Reg#(argstype) args;
   interface Reg#(varstype) vars;
endinterface

module mkStackReg#(int stackSize, pctype initialpc)(StackReg#(stackSize, pctype, argstype, varstype))
   provisos(Log#(stackSize, addressBits),
      Bits#(pctype, a__),
      Bits#(argstype, b__),
      Bits#(varstype, c__));

   BRAM1Port#(Bit#(addressBits), pctype) pcstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), argstype) argsstack  <- mkBRAM1Server(defaultValue);
   BRAM1Port#(Bit#(addressBits), varstype) varsstack  <- mkBRAM1Server(defaultValue);
   Reg#(pctype) pctop <- mkReg(initialpc);
   Reg#(pctype) pcnext <- mkReg(?);
   Reg#(argstype) argstop <- mkReg(?);
   Reg#(argstype) argsnext <- mkReg(?);
   Reg#(varstype) varstop <- mkReg(?);
   Reg#(varstype) varsnext <- mkReg(?);
   Reg#(Bit#(addressBits)) fp <- mkReg(0);
   PulseWire calling <- mkPulseWire();
   PulseWire returning <- mkPulseWire();
   Reg#(Bool) returningd1 <- mkReg(False);

/* The "next" registers are there to compensate for the pipelined reads
 * of the BRAMs.
 * A return in isolation pops next to top, and then replaces the contents of
 * next the following cycle when the BRAM read completes.
 * In the event that there are multiple returns in consecutive cycles, the
 * first return values are fed from the next registers into top, and the
 * following values are bypassed from the BRAMs direct to TOP.
 * Once return cycles stop happening, the final pending BRAM read data is
 * loaded into next
 *
 * If a call happens in the cycle after a return, the BRAM read <into> next and
 * the bram write <from> next are both supressed, and next is pushed from top
 *
 * The wire calling says there is a call in the current cycle. The signal
 * returning says there is a return in the current cycle. The signal returningd1
 * says there was a return in the previous cycle.
 */

/*
   Reg#(Bit#(8)) cyc <- mkReg(0);

   rule cc;
      cyc <= cyc + 1;
   endrule
  */

   /* The pop rules fire exactly when returningd1 is true */
   rule poppc;
     let v = ?;
      //$display("%d poppc (c %d)", cyc, calling);
      v <- pcstack.portA.response.get();
      if (!calling) pcnext <= v;
      if (returning) pctop <= v;
   endrule

   rule popargs;
     let v = ?;
      v <- argsstack.portA.response.get();
      if (!calling) argsnext <= v;
      if (returning) argstop <= v;
   endrule

   rule popvars;
     let v = ?;
      v <- varsstack.portA.response.get();
      if (!calling) varsnext <= v;
      if (returning) varstop <= v;
   endrule

   (* fire_when_enabled, no_implicit_conditions *)
   rule returning_delay;
      returningd1 <= returning;
   endrule

   method Action docall(pctype jumpto, pctype returnto, argstype args, varstype vars);
      fp <= min(fp+1, maxBound);
      calling.send();
      //$display("%d docall jumpto %d returnto %d (d1 %d)", cyc, jumpto, returnto, returningd1);
      if (! returningd1)
	 begin
	    pcstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: pcnext});
	    argsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: argsnext});
	    varsstack.portA.request.put(BRAMRequest{write: True,
	       responseOnWrite: False,
	       address: fp, datain: varsnext});
	 end
      pcnext <= returnto;
      pctop <= jumpto;
      argsnext <= argstop;
      argstop <= args;
      varsnext <= varstop;
   endmethod

   method Action doreturn();
      fp <= max(fp-1, 0);
      returning.send();

      pcstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      argsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      varsstack.portA.request.put(BRAMRequest{write: False,
	 responseOnWrite: False,
	 address: fp-1, datain: ?});
      //$display("%d doreturn pctop getting %d", cyc, pcnext);
     if (!returningd1)
	begin
	   pctop <= pcnext;
	   argstop <= argsnext;
	   varstop <= varsnext;
	end
   endmethod

   method Action nextpc(pctype jumpto);
      pctop <= jumpto;
   endmethod

   method Bit#(16) setjmp();
      return 0;
   endmethod

   method Action longjump(Bit#(16) where);
   // set fp <= where and pop? something like that
   endmethod

interface pc = pctop;
interface args = argstop;
interface vars = varstop;

endmodule


cleanup

cleanup

// Copyright (c) 2015 The Connectal Project

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

interface Clai;
    method Action      deq();
    method Action      enq(Bit#(32) v);
    method Bit#(32)    first();
endinterface

import "BVI" l_class_OC_Fifo1 =
module mkClai(Clai);
    default_reset rst(nRST);
    default_clock clk(CLK);
    method deq() enable(deq__ENA) ready(deq__RDY);
    method enq(enq_v) enable(enq__ENA) ready(enq__RDY);
    method first first() ready(first__RDY);
    schedule(first, deq, enq) CF(first, deq, enq);
endmodule

// Copyright (c) 2015 The Connectal Project

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

interface Clai;
    method Action      deq();
    method Action      enq(Bit#(32) v);
    method Bit#(32)    first();
endinterface

import "BVI" l_class_OC_Fifo1 =
module mkClai(Clai);
    default_reset rst(nRST);
    default_clock clk(CLK);
    method deq() enable(deq__ENA) ready(deq__RDY);
    method enq(enq_v) enable(enq__ENA) ready(enq__RDY);
    method first first() ready(first__RDY);
    schedule(first, deq, enq) CF(first, deq, enq);
endmodule


reduce conflict on train dir BP

reduce conflict on train dir BP

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

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, RobInstState new_state);
    // Fetch stage
    method Action fetch_train_predictors(
        Addr pc, Addr next_pc, IType iType, Bool taken,
        DirPredTrainInfo dpTrain, Bool mispred
    );

    // 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_action(Addr trap_pc, Maybe#(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,
            Executed
        );

        // 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_action(x.controlFlow.nextPc, 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

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

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, RobInstState new_state);
    // Fetch stage
    method Action fetch_train_predictors(
        Addr pc, Addr next_pc, IType iType, Bool taken,
        DirPredTrainInfo dpTrain, Bool mispred
    );

    // 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_action(Addr trap_pc, Maybe#(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,
            Executed
        );

        // 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_action(x.controlFlow.nextPc, 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(
                x.controlFlow.pc, x.controlFlow.nextPc, x.iType,
                x.controlFlow.taken, x.dpTrain, 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
            (* split *)
            if(x.iType == Jr || x.iType == Br) (* nosplit *) begin
                inIfc.fetch_train_predictors(
                    x.controlFlow.pc, x.controlFlow.nextPc, x.iType,
                    x.controlFlow.taken, x.dpTrain, 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


size of cmd queue is derived from cmdQDepth

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

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

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

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

import Vector::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;

import ConnectalMemory::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;

module mkMemreadEngine(MemreadEngine#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(d__, TLog#(numServers), 6)
	    );
   let rv <- mkMemreadEngineBuff(valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

module mkMemreadEngineBuff#(Integer bufferSizeBytes) (MemreadEngine#(dataWidth, cmdQDepth, numServers))
   provisos (Div#(dataWidth,8,dataWidthBytes),
	     Mul#(dataWidthBytes,8,dataWidth),
	     Log#(dataWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Mul#(cmdQDepth,numServers,cmdBuffSz),
	     Log#(cmdBuffSz, cmdBuffAddrSz),
	     Log#(numServers, serverIdxSz),
	     Add#(1,logCmdQDepth, outCntSz),
	     Add#(1, c__, numServers),
	     Add#(b__, TLog#(numServers), cmdBuffAddrSz),
	     Add#(e__, TLog#(numServers), TAdd#(1, cmdBuffAddrSz)),
	     Add#(a__, serverIdxSz, cmdBuffAddrSz),
	     Min#(2,TLog#(numServers),bpc),
	     Add#(d__, TLog#(numServers), TAdd#(1, serverIdxSz)),
	     Add#(f__, serverIdxSz, 6));

   let verbose = False;

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(dataWidthBytes);
   Vector#(numServers, Reg#(Bool))               outs1 <- replicateM(mkReg(False));
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs0 <- replicateM(mkReg(0));
   Vector#(numServers, ConfigCounter#(16))     buffCap <- replicateM(mkConfigCounter(fromInteger(bufferSizeBeats)));
   UGBramFifos#(numServers,cmdQDepth,MemengineCmd) cmdBuf <- mkUGBramFifos;

   Reg#(Bool) load_in_progress <- mkReg(False);
   FIFO#(Tuple3#(MemengineCmd,Bool,Bool))         loadf_b <- mkSizedFIFO(1);
   FIFO#(Tuple2#(Bit#(serverIdxSz),MemengineCmd)) loadf_c <- mkSizedFIFO(1);
   FIFO#(Tuple3#(Bit#(8),Bit#(serverIdxSz),Bool))   workf <- mkSizedBRAMFIFO(32); // isthis the right size?

   Vector#(numServers, FIFO#(void))              outfs <- replicateM(mkSizedFIFO(valueOf(cmdQDepth)));
   Vector#(numServers, FIFOF#(MemengineCmd))   cmds_in <- replicateM(mkSizedFIFOF(valueOf(cmdQDepth)));

   FIFOF#(MemData#(dataWidth))                             read_data <- mkFIFOF;
   Vector#(numServers, FIFOF#(MemData#(dataWidth)))  read_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));
   function PipeOut#(Bit#(dataWidth)) check_out(PipeOut#(MemData#(dataWidth)) inpipe, Integer i) =
      (interface PipeOut;
	  method Bit#(dataWidth) first;
	     return inpipe.first.data;
	  endmethod
	  method Action deq;
	     inpipe.deq;
	     buffCap[i].increment(1);
	     if (inpipe.first.last)
		outfs[i].enq(?);
	  endmethod
	  method Bool notEmpty = inpipe.notEmpty;
       endinterface);
   Vector#(numServers, PipeOut#(Bit#(dataWidth))) read_data_pipes = zipWith(check_out, map(toPipeOut,read_data_buffs), genVector);

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(TAdd#(1,serverIdxSz))) loadIdx <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   let cmd_q_depth = fromInteger(valueOf(cmdQDepth));

   function Action incr_loadIdx =
      (action
       if(loadIdx+1 >= fromInteger(valueOf(numServers)))
	  loadIdx <= 0;
       else
	  loadIdx <= loadIdx+1;
       endaction);

   for (Integer idx = 0; idx < valueOf(numServers); idx = idx + 1)
      rule store_cmd if (!outs1[idx]);
	 let cmd <- toGet(cmds_in[idx]).get();
	 outs1[idx] <= True;
	 cmdBuf.enq(fromInteger(idx),cmd);
	 if (verbose) $display("mkMemreadEngineBuff::store_cmd %d %d", idx, buffCap[idx].read);
      endrule

   rule load_ctxt_a (!load_in_progress);
      if (outs1[loadIdx]) begin
	 load_in_progress <= True;
	 cmdBuf.first_req(truncate(loadIdx));
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_a %d", loadIdx);
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b if (load_in_progress);
      let cmd <- cmdBuf.first_resp;
      let cond0 <- buffCap[loadIdx].maybeDecrement(unpack(extend(cmd.burstLen>>beat_shift)));
      let cond1 = cmd.len <= extend(cmd.burstLen);
      loadf_b.enq(tuple3(cmd,cond0,cond1));
      if (verbose) $display("mkMemreadEngineBuff::load_ctxt_b %d %d", buffCap[loadIdx].read(), cmd.burstLen>>beat_shift);
   endrule

   rule load_ctxt_c if (load_in_progress);
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_c %d %d", cmd.len, loadIdx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 if (cond1) begin
	    //$display("load_ctxt_b cond1");
	    outs1[loadIdx] <= False;
	    cmdBuf.deq(truncate(loadIdx));
	 end
	 else begin
	    let new_cmd = MemengineCmd{sglId:cmd.sglId, base:cmd.base+extend(cmd.burstLen),
				       burstLen:cmd.burstLen, len:cmd.len-extend(cmd.burstLen), tag:cmd.tag};
	    cmdBuf.upd_head(truncate(loadIdx),new_cmd);
	 end
      end
   endrule

   rule read_data_rule;
      let d <- toGet(read_data).get();
      match {.rc, .idx, .last} = workf.first;
      let new_respCnt = respCnt+1;
      let l = False;
      //$display("%h %d", d.data, idx);
      if (new_respCnt == rc) begin
	 respCnt <= 0;
	 workf.deq;
	 //$display("eob %d", idx);
	 l = last;
      end
      else begin
	 respCnt <= new_respCnt;
      end
      read_data_buffs[idx].enq(d);
   endrule

   function MemreadServer#(dataWidth) toMemreadServer(Server#(MemengineCmd,Bool) cs, PipeOut#(Bit#(dataWidth)) p) =
      (interface MemreadServer;
	  interface cmdServer = cs;
	  interface dataPipe  = p;
       endinterface);

   Vector#(numServers, Server#(MemengineCmd,Bool)) rs;
   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      rs[i] = (interface Server#(MemengineCmd,Bool);
		  interface Put request;
		     method Action put(MemengineCmd cmd) if (outs0[i] < cmd_q_depth);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			let mdw = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported len %d", cmd.len);
			end
			else begin
`endif
	 		   outs0[i] <= outs0[i]+1;
			   cmds_in[i].enq(cmd);
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			outfs[i].deq;
	 		outs0[i] <= outs0[i]-1;
			return True;
		     endmethod
		  endinterface
	       endinterface);
   interface readServers = rs;
   interface MemReadClient dmaClient;
      interface Get readReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    let last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    workf.enq(tuple3(truncate(bl>>beat_shift), idx, last));
	    //$display("readReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: (cmd.tag << valueOf(serverIdxSz)) | extend(idx)};
	 endmethod
      endinterface
      interface Put readData = toPut(read_data);
   endinterface
   interface dataPipes = read_data_pipes;
   interface read_servers = zipWith(toMemreadServer, rs, read_data_pipes);
endmodule

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

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

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

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

import Vector::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;

import ConnectalMemory::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;

module mkMemreadEngine(MemreadEngine#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(d__, TLog#(numServers), 6)
	    );
   let rv <- mkMemreadEngineBuff(valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

module mkMemreadEngineBuff#(Integer bufferSizeBytes) (MemreadEngine#(dataWidth, cmdQDepth, numServers))
   provisos (Div#(dataWidth,8,dataWidthBytes),
	     Mul#(dataWidthBytes,8,dataWidth),
	     Log#(dataWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Mul#(cmdQDepth,numServers,cmdBuffSz),
	     Log#(cmdBuffSz, cmdBuffAddrSz),
	     Log#(numServers, serverIdxSz),
	     Add#(1,logCmdQDepth, outCntSz),
	     Add#(1, c__, numServers),
	     Add#(b__, TLog#(numServers), cmdBuffAddrSz),
	     Add#(e__, TLog#(numServers), TAdd#(1, cmdBuffAddrSz)),
	     Add#(a__, serverIdxSz, cmdBuffAddrSz),
	     Min#(2,TLog#(numServers),bpc),
	     Add#(d__, TLog#(numServers), TAdd#(1, serverIdxSz)),
	     Add#(f__, serverIdxSz, 6));

   let verbose = False;

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(dataWidthBytes);
   Vector#(numServers, Reg#(Bool))               outs1 <- replicateM(mkReg(False));
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs0 <- replicateM(mkReg(0));
   Vector#(numServers, ConfigCounter#(16))     buffCap <- replicateM(mkConfigCounter(fromInteger(bufferSizeBeats)));
   UGBramFifos#(numServers,cmdQDepth,MemengineCmd) cmdBuf <- mkUGBramFifos;

   Reg#(Bool) load_in_progress <- mkReg(False);
   FIFO#(Tuple3#(MemengineCmd,Bool,Bool))         loadf_b <- mkSizedFIFO(1);
   FIFO#(Tuple2#(Bit#(serverIdxSz),MemengineCmd)) loadf_c <- mkSizedFIFO(1);
   FIFO#(Tuple3#(Bit#(8),Bit#(serverIdxSz),Bool))   workf <- mkSizedBRAMFIFO(32); // isthis the right size?

   Vector#(numServers, FIFO#(void))              outfs <- replicateM(mkSizedFIFO(1));
   Vector#(numServers, FIFOF#(MemengineCmd))   cmds_in <- replicateM(mkSizedFIFOF(1));

   FIFOF#(MemData#(dataWidth))                             read_data <- mkFIFOF;
   Vector#(numServers, FIFOF#(MemData#(dataWidth)))  read_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));
   function PipeOut#(Bit#(dataWidth)) check_out(PipeOut#(MemData#(dataWidth)) inpipe, Integer i) =
      (interface PipeOut;
	  method Bit#(dataWidth) first;
	     return inpipe.first.data;
	  endmethod
	  method Action deq;
	     inpipe.deq;
	     buffCap[i].increment(1);
	     if (inpipe.first.last)
		outfs[i].enq(?);
	  endmethod
	  method Bool notEmpty = inpipe.notEmpty;
       endinterface);
   Vector#(numServers, PipeOut#(Bit#(dataWidth))) read_data_pipes = zipWith(check_out, map(toPipeOut,read_data_buffs), genVector);

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(TAdd#(1,serverIdxSz))) loadIdx <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   let cmd_q_depth = fromInteger(valueOf(cmdQDepth));

   function Action incr_loadIdx =
      (action
       if(loadIdx+1 >= fromInteger(valueOf(numServers)))
	  loadIdx <= 0;
       else
	  loadIdx <= loadIdx+1;
       endaction);

   for (Integer idx = 0; idx < valueOf(numServers); idx = idx + 1)
      rule store_cmd if (!outs1[idx]);
	 let cmd <- toGet(cmds_in[idx]).get();
	 outs1[idx] <= True;
	 cmdBuf.enq(fromInteger(idx),cmd);
	 if (verbose) $display("mkMemreadEngineBuff::store_cmd %d %d", idx, buffCap[idx].read);
      endrule

   rule load_ctxt_a (!load_in_progress);
      if (outs1[loadIdx]) begin
	 load_in_progress <= True;
	 cmdBuf.first_req(truncate(loadIdx));
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_a %d", loadIdx);
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b if (load_in_progress);
      let cmd <- cmdBuf.first_resp;
      let cond0 <- buffCap[loadIdx].maybeDecrement(unpack(extend(cmd.burstLen>>beat_shift)));
      let cond1 = cmd.len <= extend(cmd.burstLen);
      loadf_b.enq(tuple3(cmd,cond0,cond1));
      if (verbose) $display("mkMemreadEngineBuff::load_ctxt_b %d %d", buffCap[loadIdx].read(), cmd.burstLen>>beat_shift);
   endrule

   rule load_ctxt_c if (load_in_progress);
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_c %d %d", cmd.len, loadIdx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 if (cond1) begin
	    //$display("load_ctxt_b cond1");
	    outs1[loadIdx] <= False;
	    cmdBuf.deq(truncate(loadIdx));
	 end
	 else begin
	    let new_cmd = MemengineCmd{sglId:cmd.sglId, base:cmd.base+extend(cmd.burstLen),
				       burstLen:cmd.burstLen, len:cmd.len-extend(cmd.burstLen), tag:cmd.tag};
	    cmdBuf.upd_head(truncate(loadIdx),new_cmd);
	 end
      end
   endrule

   rule read_data_rule;
      let d <- toGet(read_data).get();
      match {.rc, .idx, .last} = workf.first;
      let new_respCnt = respCnt+1;
      let l = False;
      //$display("%h %d", d.data, idx);
      if (new_respCnt == rc) begin
	 respCnt <= 0;
	 workf.deq;
	 //$display("eob %d", idx);
	 l = last;
      end
      else begin
	 respCnt <= new_respCnt;
      end
      read_data_buffs[idx].enq(d);
   endrule

   function MemreadServer#(dataWidth) toMemreadServer(Server#(MemengineCmd,Bool) cs, PipeOut#(Bit#(dataWidth)) p) =
      (interface MemreadServer;
	  interface cmdServer = cs;
	  interface dataPipe  = p;
       endinterface);

   Vector#(numServers, Server#(MemengineCmd,Bool)) rs;
   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      rs[i] = (interface Server#(MemengineCmd,Bool);
		  interface Put request;
		     method Action put(MemengineCmd cmd) if (outs0[i] < cmd_q_depth);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			let mdw = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported len %d", cmd.len);
			end
			else begin
`endif
	 		   outs0[i] <= outs0[i]+1;
			   cmds_in[i].enq(cmd);
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			outfs[i].deq;
	 		outs0[i] <= outs0[i]-1;
			return True;
		     endmethod
		  endinterface
	       endinterface);
   interface readServers = rs;
   interface MemReadClient dmaClient;
      interface Get readReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    let last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    workf.enq(tuple3(truncate(bl>>beat_shift), idx, last));
	    //$display("readReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: (cmd.tag << valueOf(serverIdxSz)) | extend(idx)};
	 endmethod
      endinterface
      interface Put readData = toPut(read_data);
   endinterface
   interface dataPipes = read_data_pipes;
   interface read_servers = zipWith(toMemreadServer, rs, read_data_pipes);
endmodule