actually use blocksync lock in DtpTx

actually use blocksync lock in DtpTx

// Copyright (c) 2014 Cornell University.

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

package DtpTx;

import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import FShow::*;
import Probe::*;
import Pipe::*;
import Ethernet::*;

typedef 8  CmdLen;
typedef 32 ParamLen;
typedef 4 GLOBAL_DELAY;
typedef 5 RXTX_DELAY;

typedef struct {
   Bit#(CmdLen)   cmd;
   Bit#(ParamLen) param;
} CmdTup deriving (Eq, Bits, FShow);

typedef struct {
   Bool     mux_sel;
   Bit#(53) c_local;
   Bit#(1)  parity;
} TxStageOneBuf deriving (Eq, Bits);

interface DtpTx;
   interface PipeIn#(Bit#(66))  dtpTxIn;
   interface PipeOut#(Bit#(66)) dtpTxOut;
   interface PipeOut#(Bit#(53)) dtpLocalOut;
   interface PipeIn#(Bit#(53))  dtpGlobalIn;
   interface DtpToPhyIfc api;

   interface PipeIn#(DtpEvent) dtpEventIn;
   interface PipeIn#(Bit#(32)) dtpErrCnt;

   (* always_ready, always_enabled *)
   method Action tx_ready(Bool v);
   (* always_ready, always_enabled *)
   method Action rx_ready(Bool v);
   (* always_ready, always_enabled *)
   method Action switch_mode(Bool v);
   (* always_ready, always_enabled *)
   method Action bsync_lock(Bool v);
endinterface

typedef 3'b100 LOG_TYPE;
typedef 2'b01 INIT_TYPE;
typedef 2'b10 ACK_TYPE;
typedef 2'b11 BEACON_TYPE;

typedef enum {INIT, SENT, SYNC} DtpState
deriving (Bits, Eq);

(* synthesize *)
module mkDtpTxTop(DtpTx);
   DtpTx _a <- mkDtpTx(0, 0);
   return _a;
endmodule

module mkDtpTx#(Integer id, Integer c_local_init)(DtpTx);

   let verbose = True;
   Wire#(Bool) tx_ready_wire <- mkDWire(False);
   Wire#(Bool) rx_ready_wire <- mkDWire(False);
   Wire#(Bool) switch_mode_wire <- mkDWire(False);
   Wire#(Bool) bsync_lock_wire <- mkDWire(False);

   FIFOF#(Bit#(66)) dtpTxInFifo <- mkFIFOF ();

   Reg#(Bool)   is_switch_mode <- mkReg(False); // by default, NIC mode
   Reg#(Bit#(32))  cycle   <- mkReg(0);
   Reg#(DtpState)  curr_state  <- mkReg(INIT);

   Reg#(Bit#(53))  c_local <- mkReg(fromInteger(c_local_init)); //mkReg(0); //fromInteger(c_local_init));
   Reg#(Bit#(53))  delay   <- mkReg(0);
   Reg#(Bit#(32))  timeout_count_init <- mkReg(0);
   Reg#(Bit#(32))  timeout_count_sync <- mkReg(0);
   Reg#(Bit#(64))  jumpCount <- mkReg(0);
   Reg#(Bit#(32))  interval_reg <- mkReg(1000);

   Wire#(Bool) init_rcvd    <- mkDWire(False);
   Wire#(Bool) ack_rcvd     <- mkDWire(False);
   Wire#(Bool) beacon_rcvd  <- mkDWire(False);

   Wire#(Bool) is_idle      <- mkDWire(False);

   Wire#(Bit#(53)) c_local_next <- mkDWire(0);
   FIFOF#(Bit#(2)) txMuxSelFifo <- mkSizedBypassFIFOF(3);

   // Tx Stage 1
   FIFOF#(Bit#(66)) dtpTxInPipelineFifo <- mkFIFOF;
   FIFOF#(Bit#(66)) dtpTxOutFifo <- mkFIFOF;
   FIFOF#(TxStageOneBuf) stageOneFifo <- mkFIFOF;

   FIFOF#(Bit#(53)) initTimestampFifo <- mkBypassFIFOF;
   FIFOF#(Bit#(1)) initParityFifo <- mkBypassFIFOF;
   FIFOF#(Bit#(53)) ackTimestampFifo <- mkBypassFIFOF;

   FIFOF#(Bit#(3))  dtpEventFifo   <- mkFIFOF;
   FIFOF#(Bit#(3))  dtpEventOutputFifo   <- mkFIFOF;
   FIFOF#(Bit#(66)) dtpRxOutFifo <- mkFIFOF;
   FIFOF#(DtpEvent) dtpEventInFifo <- mkFIFOF;
   FIFOF#(Bit#(32)) dtpErrCntFifo <- mkFIFOF;

   FIFOF#(Bit#(53)) dtpLocalOutFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) dtpGlobalInFifo <- mkFIFOF;

   FIFOF#(Bit#(53)) localCompareRemoteFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) localCompareGlobalFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) remoteCompareGlobalFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) remoteCompareLocalFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) globalCompareRemoteFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) globalCompareLocalFifo  <- mkFIFOF;

   FIFOF#(Bit#(53)) localOutputFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) globalOutputFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) remoteOutputFifo <- mkFIFOF;

   FIFOF#(Bool) localLtRemoteFifo  <- mkFIFOF;
   FIFOF#(Bool) localGeRemoteFifo  <- mkFIFOF;
   FIFOF#(Bool) globalLeRemoteFifo <- mkFIFOF;
   FIFOF#(Bool) globalGtRemoteFifo <- mkFIFOF;
   FIFOF#(Bool) globalLeLocalFifo  <- mkFIFOF;
   FIFOF#(Bool) globalGtLocalFifo  <- mkFIFOF;

   FIFOF#(void) cfFifo <- mkFIFOF;
   FIFOF#(void) dmFifo <- mkFIFOF;

   Reg#(Bool)       log_rcvd     <- mkReg(False);
   FIFOF#(Bit#(53)) fromHostFifo <- mkSizedBypassFIFOF(2);
   FIFOF#(Bit#(53)) toHostFifo   <- mkSizedBypassFIFOF(2);
   FIFOF#(Bit#(32)) delayFifo    <- mkSizedFIFOF(1);
   FIFOF#(Bit#(32)) stateFifo     <- mkSizedFIFOF(1);
   FIFOF#(Bit#(64)) jumpCountFifo <- mkSizedFIFOF(1);
   FIFOF#(Bit#(53)) cLocalFifo   <- mkSizedFIFOF(1);
   FIFOF#(Bit#(32)) intervalFifo <- mkSizedFIFOF(1);

   rule cyc;
      cycle <= cycle + 1;
   endrule

   rule set_switch_mode;
      is_switch_mode <= switch_mode_wire; // 0 for NIC, 1 for switch.
   endrule

   // Setup link first if neither Tx or Rx is ready, bypass DTP.
   rule tx_bypass(!tx_ready_wire || !rx_ready_wire || !bsync_lock_wire);
      let v <- toGet(dtpTxInFifo).get;
      dtpTxOutFifo.enq(v);
   endrule

   rule tx_stage1(tx_ready_wire && rx_ready_wire && bsync_lock_wire);
      let v <- toGet(dtpTxInFifo).get();
      Bit#(1) parity;
      Bool    mux_sel;
      Bit#(53) c_local_out;
      c_local_out = c_local+2; // forward compute parity.
      parity = ^c_local_out[52:0];

      if(v[9:2] == 8'h1e) begin
         mux_sel = True;
         is_idle <= True;
      end
      else begin
         mux_sel = False;
         is_idle <= False;
      end
      //if(verbose) $display("%d: %d dtpTxIn=%h, c_local=%h, is_idle=%h, curr_state=%d", cycle, id, v, c_local, mux_sel, curr_state);
      cfFifo.enq(?);
      dmFifo.enq(?);
      stageOneFifo.enq(TxStageOneBuf{mux_sel: mux_sel,
                                     parity: parity,
                                     c_local: c_local+1});
      dtpTxInPipelineFifo.enq(v);
   endrule

   Probe#(Bit#(53)) debug_from_host <- mkProbe();
   rule tx_stage2(tx_ready_wire && rx_ready_wire && bsync_lock_wire);
      let val <- toGet(stageOneFifo).get;
      let v <- toGet(dtpTxInPipelineFifo).get();
      let mux_sel = val.mux_sel;
      let c_local = val.c_local;
      let parity = val.parity;

      Bit#(10) block_type;
      Bit#(66) encodeOut;
      Bit#(3) log_type    = fromInteger(valueOf(LOG_TYPE));
      Bit#(2) init_type   = fromInteger(valueOf(INIT_TYPE));
      Bit#(2) ack_type    = fromInteger(valueOf(ACK_TYPE));
      Bit#(2) beacon_type = fromInteger(valueOf(BEACON_TYPE));

      block_type = v[9:0];

      if (mux_sel && fromHostFifo.notEmpty) begin
         let host_data = fromHostFifo.first;
         debug_from_host <= host_data;
         encodeOut = {host_data, log_type, block_type};
         fromHostFifo.deq;
      end
      else if (mux_sel && txMuxSelFifo.notEmpty) begin
         let sel = txMuxSelFifo.first;
         if (sel == init_type) begin
            encodeOut = {c_local+1, parity, init_type, block_type};
            if(verbose) $display("%d: %d, Enqueued outgoing init request %d", cycle, id, c_local+1);
         end
         else if (sel == ack_type) begin
            let init_timestamp <- toGet(initTimestampFifo).get;
            let init_parity <- toGet(initParityFifo).get;
            encodeOut = {init_timestamp, init_parity, ack_type, block_type};
            if(verbose) $display("%d: %d, Enqueued outgoing ack %d", cycle, id, init_timestamp);
         end
         else if (sel == beacon_type) begin
            encodeOut = {c_local+1, parity, beacon_type, block_type};
         end
         else begin
            encodeOut = v; //should never happen
         end
         txMuxSelFifo.deq;
      end
      else begin
         encodeOut = v;
      end
      //if(verbose) $display("%d: %d dtpTxOut=%h, c_local=%h, encodeOut=%h", cycle, id, encodeOut, c_local, encodeOut[12:10]);
      dtpTxOutFifo.enq(encodeOut);
   endrule

   // delay measurement
   rule delay_measurment(curr_state == INIT || curr_state == SENT);
      let init_timeout = interval_reg._read;
      let init_type = fromInteger(valueOf(INIT_TYPE));
      let ack_type = fromInteger(valueOf(ACK_TYPE));
      let rxtx_delay = fromInteger(valueOf(RXTX_DELAY));
      dmFifo.deq;
      // Timeout driven output
      if (init_rcvd) begin
         timeout_count_init <= timeout_count_init + 1;
         if (txMuxSelFifo.notFull) begin
            txMuxSelFifo.enq(ack_type);
         end
         if(verbose) $display("%d: %d, send ack, type %d", cycle, id, ack_type);
      end
      else if (timeout_count_init > init_timeout-1) begin
         if (is_idle) begin
            timeout_count_init <= 0;
            if (txMuxSelFifo.notFull) begin
               txMuxSelFifo.enq(init_type);
            end
            if(verbose) $display("%d: %d, init timed_out %d", cycle, id, timeout_count_init);
         end
         else begin
            timeout_count_init <= timeout_count_init + 1;
         end
      end
      else begin
         timeout_count_init <= timeout_count_init + 1;
      end

      // compute delay
      if (ack_rcvd) begin
         let temp <- toGet(ackTimestampFifo).get;
         delay <= (c_local - temp - (rxtx_delay << 1) - 1) >> 1;
         if(verbose) $display("%d: %d update delay=%d, %d, %d", cycle, id, c_local, temp, (c_local-temp-(rxtx_delay<<1)-1)>>1);
      end
   endrule

   // Beacon
   rule beacon(curr_state == SYNC);
      dmFifo.deq;
      let sync_timeout = interval_reg._read;
      let beacon_type = fromInteger(valueOf(BEACON_TYPE));
      let ack_type = fromInteger(valueOf(ACK_TYPE));
      let rxtx_delay = fromInteger(valueOf(RXTX_DELAY));
      if (timeout_count_sync >= sync_timeout-1) begin
         if (is_idle) begin
            timeout_count_sync <= 0;
         end
         else begin
            timeout_count_sync <= timeout_count_sync + 1;
         end
         if (txMuxSelFifo.notFull) begin
            txMuxSelFifo.enq(beacon_type);
         end
      end
      else if (init_rcvd) begin
         if (txMuxSelFifo.notFull) begin
            txMuxSelFifo.enq(ack_type);
         end
         timeout_count_sync <= timeout_count_sync + 1;
      end
      else begin
         timeout_count_sync <= timeout_count_sync + 1;
      end

      // compute delay
      if (ack_rcvd) begin
         let temp <- toGet(ackTimestampFifo).get;
         delay <= (c_local - temp - (rxtx_delay << 1) - 1) >> 1;
         if(verbose) $display("%d: %d update delay=%d, %d, %d", cycle, id, c_local, temp, (c_local-temp-(rxtx_delay<<1)-1)>>1);
      end
   endrule

   // DTP state machine
   rule state_init (curr_state == INIT);
      let init_type = fromInteger(valueOf(INIT_TYPE));
      cfFifo.deq;

      // update states
      if (txMuxSelFifo.notEmpty && is_idle) begin
         if (txMuxSelFifo.first == init_type) begin
            curr_state <= SENT;
         end
      end
      else if (init_rcvd) begin
         curr_state <= INIT;
      end
      else begin
         curr_state <= INIT;
      end
      //if(verbose) $display("%d: %d curr_state=%h", cycle, id, curr_state);
   endrule

   rule state_sent (curr_state == SENT);
      let init_type = fromInteger(valueOf(INIT_TYPE));
      cfFifo.deq;

      // update states
      if (init_rcvd) begin
         curr_state <= SENT;
      end
      else if (ack_rcvd) begin
         curr_state <= SYNC;
      end
      else if (txMuxSelFifo.notEmpty) begin
         if (txMuxSelFifo.first == init_type) begin
            curr_state <= INIT;
         end
      end
      else begin
         curr_state <= SENT;
      end
   endrule

   rule state_sync (curr_state == SYNC);
      cfFifo.deq;

      // update states
      if (init_rcvd) begin
         curr_state <= SYNC;
      end
      else begin
         curr_state <= SYNC;
      end
   endrule

   rule switch_in_c_local(is_switch_mode);
      //if(verbose) $display("%d: send c_local %h to L2", cycle, c_local);
      dtpLocalOutFifo.enq(c_local);
   endrule

   rule switch_out_c_global;
      let v <- toGet(dtpGlobalInFifo).get;
      if (is_switch_mode && beacon_rcvd) begin
         if (verbose) $display("%d: received global counter %d", cycle, v);
         globalCompareRemoteFifo.enq(v + 1);
         globalCompareLocalFifo.enq(v + 1);
      end
   endrule

   rule rx_stage1(tx_ready_wire && rx_ready_wire && bsync_lock_wire);
      let init_type   = fromInteger(valueOf(INIT_TYPE));
      let ack_type    = fromInteger(valueOf(ACK_TYPE));
      let beacon_type = fromInteger(valueOf(BEACON_TYPE));
      let log_type    = fromInteger(valueOf(LOG_TYPE));
      Bool init_rcvd_next   = False;
      Bool ack_rcvd_next    = False;
      Bool beacon_rcvd_next = False;
      Bool log_rcvd_next    = False;
      if (dtpEventInFifo.notEmpty) begin
         let v <- toGet(dtpEventInFifo).get;
         if ((v.e == init_type)) begin
            let parity = ^(v.t);
            if (initTimestampFifo.notFull && initParityFifo.notFull) begin
               initTimestampFifo.enq(v.t);
               initParityFifo.enq(parity);
            end
            if(verbose) $display("%d: %d DtpTx init_rcvd %h %d", cycle, id, v.e, v.t);
            init_rcvd_next = True;
         end
         else if (v.e == ack_type) begin
            ack_rcvd_next = True;
            // append received timestamp to fifo
            if (ackTimestampFifo.notFull)
               ackTimestampFifo.enq(v.t);
            if(verbose) $display("%d: %d DtpTx ack_rcvd %h %d", cycle, id, v.e, v.t);
         end
         else if (v.e == beacon_type) begin
            beacon_rcvd_next = True;
            if(verbose) $display("%d: %d DtpTx beacon_rcvd %h %d", cycle, id, v.e, v.t);
            localCompareRemoteFifo.enq(c_local+1);
            remoteCompareLocalFifo.enq(v.t+1);
            if (is_switch_mode) begin
               localCompareGlobalFifo.enq(c_local+1);
               remoteCompareGlobalFifo.enq(v.t+1);
            end
         end
         else if (v.e == log_type) begin
            log_rcvd_next = True;
            if(verbose) $display("%d: %d DtpTx log_rcvd %h %d", cycle, id, v.e, v.t);
            if (toHostFifo.notFull) begin
               toHostFifo.enq(v.t);
            end
         end
         dtpEventFifo.enq(v.e);
      end
      else begin
         dtpEventFifo.enq(3'b0);
      end
      init_rcvd   <= init_rcvd_next;
      ack_rcvd    <= ack_rcvd_next;
      beacon_rcvd <= beacon_rcvd_next;
      log_rcvd    <= log_rcvd_next;
   endrule

   rule rx_stage2;
      let v_local <- toGet(localCompareRemoteFifo).get();
      let v_remote <- toGet(remoteCompareLocalFifo).get();
      if(verbose) $display("%d: %d, v_local=%d, v_remote=%d, delay=%d", cycle, id, v_local, v_remote, delay);
      if (v_local + 1 < v_remote + delay) begin
         localLtRemoteFifo.enq(True);
         localGeRemoteFifo.enq(False);
      end
      else begin
         localLtRemoteFifo.enq(False);
         localGeRemoteFifo.enq(True);
      end
      localOutputFifo.enq(v_local + 1);
      remoteOutputFifo.enq(v_remote + 1);
   endrule

   rule compare_global_remote (is_switch_mode);
      let global_delay = fromInteger(valueOf(GLOBAL_DELAY));
      let v_global <- toGet(globalCompareRemoteFifo).get();
      let v_remote <- toGet(remoteCompareGlobalFifo).get();
      if (v_global + global_delay <= v_remote + delay) begin
         globalLeRemoteFifo.enq(True);
         globalGtRemoteFifo.enq(False);
      end
      else begin
         globalLeRemoteFifo.enq(False);
         globalGtRemoteFifo.enq(True);
      end
      if(verbose) $display("%d: %d, v_global=%d, v_remote=%d", cycle, id, v_global, v_remote);
      globalOutputFifo.enq(v_global + 1);
   endrule

   rule compare_global_local (is_switch_mode);
      let global_delay = fromInteger(valueOf(GLOBAL_DELAY));
      let v_global <- toGet(globalCompareLocalFifo).get();
      let v_local <- toGet(localCompareGlobalFifo).get();
      if (v_global + global_delay <= v_local + 1) begin
         globalLeLocalFifo.enq(True);
         globalGtLocalFifo.enq(False);
      end
      else begin
         globalLeLocalFifo.enq(False);
         globalGtLocalFifo.enq(True);
      end
      if(verbose) $display("%d: %d, v_global=%d, v_local=%d", cycle, id, v_global, v_local);
   endrule

   rule rx_stage2_bypass;
      let v_event <- toGet(dtpEventFifo).get();
      dtpEventOutputFifo.enq(v_event);
   endrule

   rule rx_stage3_nic_mode (!is_switch_mode);
      let vLocal <- toGet(localOutputFifo).get();
      let vRemote <- toGet(remoteOutputFifo).get();
      let useLocal <- toGet(localGeRemoteFifo).get();
      let incrJump <- toGet(localLtRemoteFifo).get();
      if(verbose) $display("%d: %d vLocal=%d", cycle, id, vLocal);
      if(verbose) $display("%d: %d vRemote=%d", cycle, id, vRemote);
      if(verbose) $display("%d: %d delay=%d", cycle, id, delay);
      if (useLocal) begin
         c_local_next <= vLocal + 1;
      end
      else begin
         c_local_next <= vRemote + delay + 1;
      end

      if (incrJump) begin
         jumpCount <= jumpCount + 1;
      end
   endrule

   rule rx_stage3_switch_mode (is_switch_mode);
      let v_local <- toGet(localOutputFifo).get();
      let v_remote <- toGet(remoteOutputFifo).get();
      let v_global <- toGet(globalOutputFifo).get();
      let isGR <- toGet(globalGtRemoteFifo).get();
      let isGL <- toGet(globalGtLocalFifo).get();
      let isLR <- toGet(localGeRemoteFifo).get();
      let isLG <- toGet(globalLeLocalFifo).get();
      let isRG <- toGet(globalLeRemoteFifo).get();
      let isRL <- toGet(localLtRemoteFifo).get();
      let global_delay = fromInteger(valueOf(GLOBAL_DELAY));
      let err = False;
      if(verbose) $display("%d: isGR=%d, isGL=%d, isLR=%d, isLG=%d, isRG=%d, isRL=%d", cycle, isGR, isGL, isLR, isLG, isRG, isRL);
      if (isGR && isGL) begin
         c_local_next <= v_global + global_delay;
         err = True;
      end
      else if (isLR && isLG) begin
         c_local_next <= v_local + 1;
      end
      else if (isRG && isRL) begin
         c_local_next <= v_remote + delay;
         err = True;
      end
      else begin
         c_local_next <= v_local + 1;
         err = True;
      end

      if (err) begin
         jumpCount <= jumpCount + 1;
      end

      if(verbose && id==1) $display("%d: jumpCount = %d", cycle, jumpCount);
   endrule

   rule rx_stage3;
      let v <- toGet(dtpEventOutputFifo).get();
      Bit#(2) beacon_type = fromInteger(valueOf(BEACON_TYPE));
      if (!rx_ready_wire) begin
         c_local <= 0;
      end
      else if (v == zeroExtend(beacon_type)) begin
         c_local <= c_local_next;
         if(verbose) $display("%d: %d c_local_next = %d", cycle, id, c_local_next);
      end
      else begin
         c_local <= c_local + 1;
      end
   endrule

   rule export_delay;
      delayFifo.enq(truncate(delay));
   endrule

   rule export_state;
      stateFifo.enq(zeroExtend(pack(curr_state)));
   endrule

   rule export_jumpCount;
      jumpCountFifo.enq(jumpCount);
   endrule

   rule export_c_local;
      cLocalFifo.enq(c_local);
   endrule

   rule import_interval(intervalFifo.notEmpty);
      let interval = intervalFifo.first;
      interval_reg <= interval;
      intervalFifo.deq;
   endrule

   method Action tx_ready(Bool v);
      tx_ready_wire <= v;
   endmethod

   method Action rx_ready(Bool v);
      rx_ready_wire <= v;
   endmethod

   method Action switch_mode(Bool v);
      switch_mode_wire <= v;
   endmethod

   method Action bsync_lock(Bool v);
      bsync_lock_wire <= v;
   endmethod

   interface api = (interface DtpToPhyIfc;
      interface delayOut = toPipeOut(delayFifo);
      interface stateOut = toPipeOut(stateFifo);
      interface jumpCount = toPipeOut(jumpCountFifo);
      interface cLocalOut = toPipeOut(cLocalFifo);
      interface toHost   = toPipeOut(toHostFifo);
      interface fromHost = toPipeIn(fromHostFifo);
      interface interval = toPipeIn(intervalFifo);
      interface dtpErrCnt = toPipeOut(dtpErrCntFifo);
   endinterface);
   interface dtpEventIn = toPipeIn(dtpEventInFifo);
   interface dtpErrCnt = toPipeIn(dtpErrCntFifo);
   interface dtpTxIn = toPipeIn(dtpTxInFifo);
   interface dtpTxOut = toPipeOut(dtpTxOutFifo);
   interface dtpLocalOut = toPipeOut(dtpLocalOutFifo);
   interface dtpGlobalIn = toPipeIn(dtpGlobalInFifo);
endmodule
endpackage

// Copyright (c) 2014 Cornell University.

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

package DtpTx;

import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import FShow::*;
import Probe::*;
import Pipe::*;
import Ethernet::*;

typedef 8  CmdLen;
typedef 32 ParamLen;
typedef 4 GLOBAL_DELAY;
typedef 5 RXTX_DELAY;

typedef struct {
   Bit#(CmdLen)   cmd;
   Bit#(ParamLen) param;
} CmdTup deriving (Eq, Bits, FShow);

typedef struct {
   Bool     mux_sel;
   Bit#(53) c_local;
   Bit#(1)  parity;
} TxStageOneBuf deriving (Eq, Bits);

interface DtpTx;
   interface PipeIn#(Bit#(66))  dtpTxIn;
   interface PipeOut#(Bit#(66)) dtpTxOut;
   interface PipeOut#(Bit#(53)) dtpLocalOut;
   interface PipeIn#(Bit#(53))  dtpGlobalIn;
   interface DtpToPhyIfc api;

   interface PipeIn#(DtpEvent) dtpEventIn;
   interface PipeIn#(Bit#(32)) dtpErrCnt;

   (* always_ready, always_enabled *)
   method Action tx_ready(Bool v);
   (* always_ready, always_enabled *)
   method Action rx_ready(Bool v);
   (* always_ready, always_enabled *)
   method Action switch_mode(Bool v);
   (* always_ready, always_enabled *)
   method Action bsync_lock(Bool v);
endinterface

typedef 3'b100 LOG_TYPE;
typedef 2'b01 INIT_TYPE;
typedef 2'b10 ACK_TYPE;
typedef 2'b11 BEACON_TYPE;

typedef enum {INIT, SENT, SYNC} DtpState
deriving (Bits, Eq);

(* synthesize *)
module mkDtpTxTop(DtpTx);
   DtpTx _a <- mkDtpTx(0, 0);
   return _a;
endmodule

module mkDtpTx#(Integer id, Integer c_local_init)(DtpTx);

   let verbose = True;
   Wire#(Bool) tx_ready_wire <- mkDWire(False);
   Wire#(Bool) rx_ready_wire <- mkDWire(False);
   Wire#(Bool) switch_mode_wire <- mkDWire(False);
   Wire#(Bool) bsync_lock_wire <- mkDWire(False);

   FIFOF#(Bit#(66)) dtpTxInFifo <- mkFIFOF ();

   Reg#(Bool)   is_switch_mode <- mkReg(False); // by default, NIC mode
   Reg#(Bit#(32))  cycle   <- mkReg(0);
   Reg#(DtpState)  curr_state  <- mkReg(INIT);

   Reg#(Bit#(53))  c_local <- mkReg(fromInteger(c_local_init)); //mkReg(0); //fromInteger(c_local_init));
   Reg#(Bit#(53))  delay   <- mkReg(0);
   Reg#(Bit#(32))  timeout_count_init <- mkReg(0);
   Reg#(Bit#(32))  timeout_count_sync <- mkReg(0);
   Reg#(Bit#(64))  jumpCount <- mkReg(0);
   Reg#(Bit#(32))  interval_reg <- mkReg(1000);

   Wire#(Bool) init_rcvd    <- mkDWire(False);
   Wire#(Bool) ack_rcvd     <- mkDWire(False);
   Wire#(Bool) beacon_rcvd  <- mkDWire(False);

   Wire#(Bool) is_idle      <- mkDWire(False);

   Wire#(Bit#(53)) c_local_next <- mkDWire(0);
   FIFOF#(Bit#(2)) txMuxSelFifo <- mkSizedBypassFIFOF(3);

   // Tx Stage 1
   FIFOF#(Bit#(66)) dtpTxInPipelineFifo <- mkFIFOF;
   FIFOF#(Bit#(66)) dtpTxOutFifo <- mkFIFOF;
   FIFOF#(TxStageOneBuf) stageOneFifo <- mkFIFOF;

   FIFOF#(Bit#(53)) initTimestampFifo <- mkBypassFIFOF;
   FIFOF#(Bit#(1)) initParityFifo <- mkBypassFIFOF;
   FIFOF#(Bit#(53)) ackTimestampFifo <- mkBypassFIFOF;

   FIFOF#(Bit#(3))  dtpEventFifo   <- mkFIFOF;
   FIFOF#(Bit#(3))  dtpEventOutputFifo   <- mkFIFOF;
   FIFOF#(Bit#(66)) dtpRxOutFifo <- mkFIFOF;
   FIFOF#(DtpEvent) dtpEventInFifo <- mkFIFOF;
   FIFOF#(Bit#(32)) dtpErrCntFifo <- mkFIFOF;

   FIFOF#(Bit#(53)) dtpLocalOutFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) dtpGlobalInFifo <- mkFIFOF;

   FIFOF#(Bit#(53)) localCompareRemoteFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) localCompareGlobalFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) remoteCompareGlobalFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) remoteCompareLocalFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) globalCompareRemoteFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) globalCompareLocalFifo  <- mkFIFOF;

   FIFOF#(Bit#(53)) localOutputFifo  <- mkFIFOF;
   FIFOF#(Bit#(53)) globalOutputFifo <- mkFIFOF;
   FIFOF#(Bit#(53)) remoteOutputFifo <- mkFIFOF;

   FIFOF#(Bool) localLtRemoteFifo  <- mkFIFOF;
   FIFOF#(Bool) localGeRemoteFifo  <- mkFIFOF;
   FIFOF#(Bool) globalLeRemoteFifo <- mkFIFOF;
   FIFOF#(Bool) globalGtRemoteFifo <- mkFIFOF;
   FIFOF#(Bool) globalLeLocalFifo  <- mkFIFOF;
   FIFOF#(Bool) globalGtLocalFifo  <- mkFIFOF;

   FIFOF#(void) cfFifo <- mkFIFOF;
   FIFOF#(void) dmFifo <- mkFIFOF;

   Reg#(Bool)       log_rcvd     <- mkReg(False);
   FIFOF#(Bit#(53)) fromHostFifo <- mkSizedBypassFIFOF(2);
   FIFOF#(Bit#(53)) toHostFifo   <- mkSizedBypassFIFOF(2);
   FIFOF#(Bit#(32)) delayFifo    <- mkSizedFIFOF(1);
   FIFOF#(Bit#(32)) stateFifo     <- mkSizedFIFOF(1);
   FIFOF#(Bit#(64)) jumpCountFifo <- mkSizedFIFOF(1);
   FIFOF#(Bit#(53)) cLocalFifo   <- mkSizedFIFOF(1);
   FIFOF#(Bit#(32)) intervalFifo <- mkSizedFIFOF(1);

   rule cyc;
      cycle <= cycle + 1;
   endrule

   rule set_switch_mode;
      is_switch_mode <= switch_mode_wire; // 0 for NIC, 1 for switch.
   endrule

   // Setup link first if neither Tx or Rx is ready, bypass DTP.
   rule tx_bypass(!tx_ready_wire || !rx_ready_wire);
      let v <- toGet(dtpTxInFifo).get;
      dtpTxOutFifo.enq(v);
   endrule

   rule tx_stage1(tx_ready_wire && rx_ready_wire);
      let v <- toGet(dtpTxInFifo).get();
      Bit#(1) parity;
      Bool    mux_sel;
      Bit#(53) c_local_out;
      c_local_out = c_local+2; // forward compute parity.
      parity = ^c_local_out[52:0];

      if(v[9:2] == 8'h1e) begin
         mux_sel = True;
         is_idle <= True;
      end
      else begin
         mux_sel = False;
         is_idle <= False;
      end
      //if(verbose) $display("%d: %d dtpTxIn=%h, c_local=%h, is_idle=%h, curr_state=%d", cycle, id, v, c_local, mux_sel, curr_state);
      cfFifo.enq(?);
      dmFifo.enq(?);
      stageOneFifo.enq(TxStageOneBuf{mux_sel: mux_sel,
                                     parity: parity,
                                     c_local: c_local+1});
      dtpTxInPipelineFifo.enq(v);
   endrule

   Probe#(Bit#(53)) debug_from_host <- mkProbe();
   rule tx_stage2(tx_ready_wire && rx_ready_wire);
      let val <- toGet(stageOneFifo).get;
      let v <- toGet(dtpTxInPipelineFifo).get();
      let mux_sel = val.mux_sel;
      let c_local = val.c_local;
      let parity = val.parity;

      Bit#(10) block_type;
      Bit#(66) encodeOut;
      Bit#(3) log_type    = fromInteger(valueOf(LOG_TYPE));
      Bit#(2) init_type   = fromInteger(valueOf(INIT_TYPE));
      Bit#(2) ack_type    = fromInteger(valueOf(ACK_TYPE));
      Bit#(2) beacon_type = fromInteger(valueOf(BEACON_TYPE));

      block_type = v[9:0];

      if (mux_sel && fromHostFifo.notEmpty) begin
         let host_data = fromHostFifo.first;
         debug_from_host <= host_data;
         encodeOut = {host_data, log_type, block_type};
         fromHostFifo.deq;
      end
      else if (mux_sel && txMuxSelFifo.notEmpty) begin
         let sel = txMuxSelFifo.first;
         if (sel == init_type) begin
            encodeOut = {c_local+1, parity, init_type, block_type};
            if(verbose) $display("%d: %d, Enqueued outgoing init request %d", cycle, id, c_local+1);
         end
         else if (sel == ack_type) begin
            let init_timestamp <- toGet(initTimestampFifo).get;
            let init_parity <- toGet(initParityFifo).get;
            encodeOut = {init_timestamp, init_parity, ack_type, block_type};
            if(verbose) $display("%d: %d, Enqueued outgoing ack %d", cycle, id, init_timestamp);
         end
         else if (sel == beacon_type) begin
            encodeOut = {c_local+1, parity, beacon_type, block_type};
         end
         else begin
            encodeOut = v; //should never happen
         end
         txMuxSelFifo.deq;
      end
      else begin
         encodeOut = v;
      end
      //if(verbose) $display("%d: %d dtpTxOut=%h, c_local=%h, encodeOut=%h", cycle, id, encodeOut, c_local, encodeOut[12:10]);
      dtpTxOutFifo.enq(encodeOut);
   endrule

   // delay measurement
   rule delay_measurment(curr_state == INIT || curr_state == SENT);
      let init_timeout = interval_reg._read;
      let init_type = fromInteger(valueOf(INIT_TYPE));
      let ack_type = fromInteger(valueOf(ACK_TYPE));
      let rxtx_delay = fromInteger(valueOf(RXTX_DELAY));
      dmFifo.deq;
      // Timeout driven output
      if (init_rcvd) begin
         timeout_count_init <= timeout_count_init + 1;
         if (txMuxSelFifo.notFull) begin
            txMuxSelFifo.enq(ack_type);
         end
         if(verbose) $display("%d: %d, send ack, type %d", cycle, id, ack_type);
      end
      else if (timeout_count_init > init_timeout-1) begin
         if (is_idle) begin
            timeout_count_init <= 0;
            if (txMuxSelFifo.notFull) begin
               txMuxSelFifo.enq(init_type);
            end
            if(verbose) $display("%d: %d, init timed_out %d", cycle, id, timeout_count_init);
         end
         else begin
            timeout_count_init <= timeout_count_init + 1;
         end
      end
      else begin
         timeout_count_init <= timeout_count_init + 1;
      end

      // compute delay
      if (ack_rcvd) begin
         let temp <- toGet(ackTimestampFifo).get;
         delay <= (c_local - temp - (rxtx_delay << 1) - 1) >> 1;
         if(verbose) $display("%d: %d update delay=%d, %d, %d", cycle, id, c_local, temp, (c_local-temp-(rxtx_delay<<1)-1)>>1);
      end
   endrule

   // Beacon
   rule beacon(curr_state == SYNC);
      dmFifo.deq;
      let sync_timeout = interval_reg._read;
      let beacon_type = fromInteger(valueOf(BEACON_TYPE));
      let ack_type = fromInteger(valueOf(ACK_TYPE));
      let rxtx_delay = fromInteger(valueOf(RXTX_DELAY));
      if (timeout_count_sync >= sync_timeout-1) begin
         if (is_idle) begin
            timeout_count_sync <= 0;
         end
         else begin
            timeout_count_sync <= timeout_count_sync + 1;
         end
         if (txMuxSelFifo.notFull) begin
            txMuxSelFifo.enq(beacon_type);
         end
      end
      else if (init_rcvd) begin
         if (txMuxSelFifo.notFull) begin
            txMuxSelFifo.enq(ack_type);
         end
         timeout_count_sync <= timeout_count_sync + 1;
      end
      else begin
         timeout_count_sync <= timeout_count_sync + 1;
      end

      // compute delay
      if (ack_rcvd) begin
         let temp <- toGet(ackTimestampFifo).get;
         delay <= (c_local - temp - (rxtx_delay << 1) - 1) >> 1;
         if(verbose) $display("%d: %d update delay=%d, %d, %d", cycle, id, c_local, temp, (c_local-temp-(rxtx_delay<<1)-1)>>1);
      end
   endrule

   // DTP state machine
   rule state_init (curr_state == INIT);
      let init_type = fromInteger(valueOf(INIT_TYPE));
      cfFifo.deq;

      // update states
      if (txMuxSelFifo.notEmpty && is_idle) begin
         if (txMuxSelFifo.first == init_type) begin
            curr_state <= SENT;
         end
      end
      else if (init_rcvd) begin
         curr_state <= INIT;
      end
      else begin
         curr_state <= INIT;
      end
      //if(verbose) $display("%d: %d curr_state=%h", cycle, id, curr_state);
   endrule

   rule state_sent (curr_state == SENT);
      let init_type = fromInteger(valueOf(INIT_TYPE));
      cfFifo.deq;

      // update states
      if (init_rcvd) begin
         curr_state <= SENT;
      end
      else if (ack_rcvd) begin
         curr_state <= SYNC;
      end
      else if (txMuxSelFifo.notEmpty) begin
         if (txMuxSelFifo.first == init_type) begin
            curr_state <= INIT;
         end
      end
      else begin
         curr_state <= SENT;
      end
   endrule

   rule state_sync (curr_state == SYNC);
      cfFifo.deq;

      // update states
      if (init_rcvd) begin
         curr_state <= SYNC;
      end
      else begin
         curr_state <= SYNC;
      end
   endrule

   rule switch_in_c_local(is_switch_mode);
      //if(verbose) $display("%d: send c_local %h to L2", cycle, c_local);
      dtpLocalOutFifo.enq(c_local);
   endrule

   rule switch_out_c_global;
      let v <- toGet(dtpGlobalInFifo).get;
      if (is_switch_mode && beacon_rcvd) begin
         if (verbose) $display("%d: received global counter %d", cycle, v);
         globalCompareRemoteFifo.enq(v + 1);
         globalCompareLocalFifo.enq(v + 1);
      end
   endrule

   rule rx_stage1(tx_ready_wire && rx_ready_wire);
      let init_type   = fromInteger(valueOf(INIT_TYPE));
      let ack_type    = fromInteger(valueOf(ACK_TYPE));
      let beacon_type = fromInteger(valueOf(BEACON_TYPE));
      let log_type    = fromInteger(valueOf(LOG_TYPE));
      Bool init_rcvd_next   = False;
      Bool ack_rcvd_next    = False;
      Bool beacon_rcvd_next = False;
      Bool log_rcvd_next    = False;
      if (dtpEventInFifo.notEmpty) begin
         let v <- toGet(dtpEventInFifo).get;
         if ((v.e == init_type)) begin
            let parity = ^(v.t);
            if (initTimestampFifo.notFull && initParityFifo.notFull) begin
               initTimestampFifo.enq(v.t);
               initParityFifo.enq(parity);
            end
            if(verbose) $display("%d: %d DtpTx init_rcvd %h %d", cycle, id, v.e, v.t);
            init_rcvd_next = True;
         end
         else if (v.e == ack_type) begin
            ack_rcvd_next = True;
            // append received timestamp to fifo
            if (ackTimestampFifo.notFull)
               ackTimestampFifo.enq(v.t);
            if(verbose) $display("%d: %d DtpTx ack_rcvd %h %d", cycle, id, v.e, v.t);
         end
         else if (v.e == beacon_type) begin
            beacon_rcvd_next = True;
            if(verbose) $display("%d: %d DtpTx beacon_rcvd %h %d", cycle, id, v.e, v.t);
            localCompareRemoteFifo.enq(c_local+1);
            remoteCompareLocalFifo.enq(v.t+1);
            if (is_switch_mode) begin
               localCompareGlobalFifo.enq(c_local+1);
               remoteCompareGlobalFifo.enq(v.t+1);
            end
         end
         else if (v.e == log_type) begin
            log_rcvd_next = True;
            if(verbose) $display("%d: %d DtpTx log_rcvd %h %d", cycle, id, v.e, v.t);
            if (toHostFifo.notFull) begin
               toHostFifo.enq(v.t);
            end
         end
         dtpEventFifo.enq(v.e);
      end
      else begin
         dtpEventFifo.enq(3'b0);
      end
      init_rcvd   <= init_rcvd_next;
      ack_rcvd    <= ack_rcvd_next;
      beacon_rcvd <= beacon_rcvd_next;
      log_rcvd    <= log_rcvd_next;
   endrule

   rule rx_stage2;
      let v_local <- toGet(localCompareRemoteFifo).get();
      let v_remote <- toGet(remoteCompareLocalFifo).get();
      if(verbose) $display("%d: %d, v_local=%d, v_remote=%d, delay=%d", cycle, id, v_local, v_remote, delay);
      if (v_local + 1 < v_remote + delay) begin
         localLtRemoteFifo.enq(True);
         localGeRemoteFifo.enq(False);
      end
      else begin
         localLtRemoteFifo.enq(False);
         localGeRemoteFifo.enq(True);
      end
      localOutputFifo.enq(v_local + 1);
      remoteOutputFifo.enq(v_remote + 1);
   endrule

   rule compare_global_remote (is_switch_mode);
      let global_delay = fromInteger(valueOf(GLOBAL_DELAY));
      let v_global <- toGet(globalCompareRemoteFifo).get();
      let v_remote <- toGet(remoteCompareGlobalFifo).get();
      if (v_global + global_delay <= v_remote + delay) begin
         globalLeRemoteFifo.enq(True);
         globalGtRemoteFifo.enq(False);
      end
      else begin
         globalLeRemoteFifo.enq(False);
         globalGtRemoteFifo.enq(True);
      end
      if(verbose) $display("%d: %d, v_global=%d, v_remote=%d", cycle, id, v_global, v_remote);
      globalOutputFifo.enq(v_global + 1);
   endrule

   rule compare_global_local (is_switch_mode);
      let global_delay = fromInteger(valueOf(GLOBAL_DELAY));
      let v_global <- toGet(globalCompareLocalFifo).get();
      let v_local <- toGet(localCompareGlobalFifo).get();
      if (v_global + global_delay <= v_local + 1) begin
         globalLeLocalFifo.enq(True);
         globalGtLocalFifo.enq(False);
      end
      else begin
         globalLeLocalFifo.enq(False);
         globalGtLocalFifo.enq(True);
      end
      if(verbose) $display("%d: %d, v_global=%d, v_local=%d", cycle, id, v_global, v_local);
   endrule

   rule rx_stage2_bypass;
      let v_event <- toGet(dtpEventFifo).get();
      dtpEventOutputFifo.enq(v_event);
   endrule

   rule rx_stage3_nic_mode (!is_switch_mode);
      let vLocal <- toGet(localOutputFifo).get();
      let vRemote <- toGet(remoteOutputFifo).get();
      let useLocal <- toGet(localGeRemoteFifo).get();
      let incrJump <- toGet(localLtRemoteFifo).get();
      if(verbose) $display("%d: %d vLocal=%d", cycle, id, vLocal);
      if(verbose) $display("%d: %d vRemote=%d", cycle, id, vRemote);
      if(verbose) $display("%d: %d delay=%d", cycle, id, delay);
      if (useLocal) begin
         c_local_next <= vLocal + 1;
      end
      else begin
         c_local_next <= vRemote + delay + 1;
      end

      if (incrJump) begin
         jumpCount <= jumpCount + 1;
      end
   endrule

   rule rx_stage3_switch_mode (is_switch_mode);
      let v_local <- toGet(localOutputFifo).get();
      let v_remote <- toGet(remoteOutputFifo).get();
      let v_global <- toGet(globalOutputFifo).get();
      let isGR <- toGet(globalGtRemoteFifo).get();
      let isGL <- toGet(globalGtLocalFifo).get();
      let isLR <- toGet(localGeRemoteFifo).get();
      let isLG <- toGet(globalLeLocalFifo).get();
      let isRG <- toGet(globalLeRemoteFifo).get();
      let isRL <- toGet(localLtRemoteFifo).get();
      let global_delay = fromInteger(valueOf(GLOBAL_DELAY));
      let err = False;
      if(verbose) $display("%d: isGR=%d, isGL=%d, isLR=%d, isLG=%d, isRG=%d, isRL=%d", cycle, isGR, isGL, isLR, isLG, isRG, isRL);
      if (isGR && isGL) begin
         c_local_next <= v_global + global_delay;
         err = True;
      end
      else if (isLR && isLG) begin
         c_local_next <= v_local + 1;
      end
      else if (isRG && isRL) begin
         c_local_next <= v_remote + delay;
         err = True;
      end
      else begin
         c_local_next <= v_local + 1;
         err = True;
      end

      if (err) begin
         jumpCount <= jumpCount + 1;
      end

      if(verbose && id==1) $display("%d: jumpCount = %d", cycle, jumpCount);
   endrule

   rule rx_stage3;
      let v <- toGet(dtpEventOutputFifo).get();
      Bit#(2) beacon_type = fromInteger(valueOf(BEACON_TYPE));
      if (!rx_ready_wire) begin
         c_local <= 0;
      end
      else if (v == zeroExtend(beacon_type)) begin
         c_local <= c_local_next;
         if(verbose) $display("%d: %d c_local_next = %d", cycle, id, c_local_next);
      end
      else begin
         c_local <= c_local + 1;
      end
   endrule

   rule export_delay;
      delayFifo.enq(truncate(delay));
   endrule

   rule export_state;
      stateFifo.enq(zeroExtend(pack(curr_state)));
   endrule

   rule export_jumpCount;
      jumpCountFifo.enq(jumpCount);
   endrule

   rule export_c_local;
      cLocalFifo.enq(c_local);
   endrule

   rule import_interval(intervalFifo.notEmpty);
      let interval = intervalFifo.first;
      interval_reg <= interval;
      intervalFifo.deq;
   endrule

   method Action tx_ready(Bool v);
      tx_ready_wire <= v;
   endmethod

   method Action rx_ready(Bool v);
      rx_ready_wire <= v;
   endmethod

   method Action switch_mode(Bool v);
      switch_mode_wire <= v;
   endmethod

   method Action bsync_lock(Bool v);
      bsync_lock_wire <= v;
   endmethod

   interface api = (interface DtpToPhyIfc;
      interface delayOut = toPipeOut(delayFifo);
      interface stateOut = toPipeOut(stateFifo);
      interface jumpCount = toPipeOut(jumpCountFifo);
      interface cLocalOut = toPipeOut(cLocalFifo);
      interface toHost   = toPipeOut(toHostFifo);
      interface fromHost = toPipeIn(fromHostFifo);
      interface interval = toPipeIn(intervalFifo);
      interface dtpErrCnt = toPipeOut(dtpErrCntFifo);
   endinterface);
   interface dtpEventIn = toPipeIn(dtpEventInFifo);
   interface dtpErrCnt = toPipeIn(dtpErrCntFifo);
   interface dtpTxIn = toPipeIn(dtpTxInFifo);
   interface dtpTxOut = toPipeOut(dtpTxOutFifo);
   interface dtpLocalOut = toPipeOut(dtpLocalOutFifo);
   interface dtpGlobalIn = toPipeIn(dtpGlobalInFifo);
endmodule
endpackage


added recursive mkReducePipe

added recursive mkReducePipe

// 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 FIFOF::*;
import GetPut::*;
import Connectable::*;
import Vector::*;
import MIMO::*;
import DefaultValue::*;

interface PipeOut#(type a);
   method a first();
   method Action deq();
   method Bool notEmpty();
endinterface

typeclass PipeInOut#(type a, type b);
   function PipeOut#(a) toPipeOut(b in);
endtypeclass

typeclass MkPipeInOut#(type a, type b);
   module mkPipeOut#(b in)(PipeOut#(a));
endtypeclass

instance PipeInOut#(a, FIFOF#(a));
   function PipeOut#(a) toPipeOut(FIFOF#(a) in);
      return (interface PipeOut#(a);
		 method first = in.first;
		 method deq = in.deq;
		 method notEmpty = in.notEmpty;
	      endinterface);
   endfunction
endinstance

instance MkPipeInOut#(a, Get#(a))
   provisos (Bits#(a, asz));
   module mkPipeOut#(Get#(a) in)(PipeOut#(a));
      FIFOF#(a) fifo <- mkFIFOF();
      rule connect;
	 let v <- in.get();
	 fifo.enq(v);
      endrule
      return toPipeOut(fifo);
   endmodule
endinstance

instance Connectable#(PipeOut#(a),Put#(a));
   module mkConnection#(PipeOut#(a) in, Put#(a) out)(Empty);
      rule connect;
	 let v = in.first;
	 in.deq();
	 out.put(v);
      endrule
   endmodule
endinstance

module mkUnfunnel#(PipeOut#(Vector#(m,a)) in)(PipeOut#(Vector#(mk, a)))
   provisos (Mul#(m, k, mk),
	     Bits#(a, asz),
	     Add#(1, b__, asz),
	     Add#(2, c__, mk),
	     Add#(d__, m, mk),
	     Add#(asz, m, e__),
	     Add#(asz, mk, f__));
   let m = fromInteger(valueOf(m));
   let mk = fromInteger(valueOf(mk));

   MIMOConfiguration cfg = defaultValue();
   MIMO#(m, mk, mk, a) mimo <- mkMIMO(cfg);
   rule consumer if (mimo.enqReadyN(m));
      Vector#(m, a) v = in.first();
      in.deq();
      mimo.enq(m, v);
   endrule

   method Vector#(mk, a) first() if (mimo.deqReadyN(mk));
      return mimo.first();
   endmethod
   method Action deq() if (mimo.deqReadyN(mk));
      mimo.deq(mk);
   endmethod
   method notEmpty();
      return mimo.deqReadyN(mk);
   endmethod
endmodule

module mkForkVector#(PipeOut#(a) inpipe)(Vector#(n, PipeOut#(a)))
   provisos (Bits#(a, asz));
   Vector#(n, FIFOF#(a)) fifos <- replicateM(mkFIFOF());
   rule forkelts;
      let v = inpipe.first();
      inpipe.deq;
      for (Integer i = 0; i < valueOf(n); i = i + 1) begin
	 fifos[i].enq(v);
      end
   endrule
   return map(toPipeOut, fifos);
endmodule

module mkJoin#(function c f(a av, b bv), PipeOut#(a) apipe, PipeOut#(b) bpipe)(PipeOut#(c));
   method c first();
      let av = apipe.first();
      let bv = bpipe.first();
      return f(av, bv);
   endmethod
   method Action deq();
      apipe.deq();
      bpipe.deq();
   endmethod
   method Bool notEmpty();
      return apipe.notEmpty() && bpipe.notEmpty();
   endmethod
endmodule

module mkMap#(function b f(a av), PipeOut#(a) apipe)(PipeOut#(b));
   method b first();
      let av = apipe.first();
      return f(av);
   endmethod
   method Action deq();
      apipe.deq();
   endmethod
   method Bool notEmpty();
      return apipe.notEmpty();
   endmethod
endmodule

typedef (function Module #(PipeOut #(tb)) mkPipeOut(PipeOut#(ta) ifc)) MkPipeOut#(type ta, type tb);

typeclass ReducePipe#( numeric type n, type a);
   module [Module] mkReducePipe (MkPipeOut#(Tuple2#(a,a), a) combinepipe,
				 PipeOut#(Vector#(n,a)) inpipe,
				 PipeOut#(a) ifc);
endtypeclass
instance ReducePipe#(1, a);
   module [Module] mkReducePipe (MkPipeOut#(Tuple2#(a,a), a) combinepipe,
				 PipeOut#(Vector#(1,a)) inpipe,
				 PipeOut#(a) ifc);
      let pipe <- mkMap(head, inpipe);
      return pipe;
   endmodule
endinstance
instance ReducePipe#(2, a);
   module [Module] mkReducePipe (MkPipeOut#(Tuple2#(a,a), a) combinepipe,
				 PipeOut#(Vector#(2,a)) inpipe,
				 PipeOut#(a) ifc);
      function Tuple2#(a,a) foo(Vector#(2,a) invec); return tuple2(invec[0], invec[1]); endfunction
      PipeOut#(Tuple2#(a,a)) zippipe <- mkMap(foo, inpipe);
      let pipe <- combinepipe(zippipe);
      return pipe;
   endmodule
endinstance

instance ReducePipe#(n, a)
   provisos (Add#(TDiv#(n,2), a__, n),
	     Bits#(Vector#(TDiv#(n,2), a), b__),
	     ReducePipe#(TDiv#(n,2),a));
   module [Module] mkReducePipe (MkPipeOut#(Tuple2#(a,a), a) combinepipe,
				 PipeOut#(Vector#(n,a)) inpipe,
				 PipeOut#(a) ifc);
      FIFOF#(Vector#(TDiv#(n,2),a)) infifo0 <- mkFIFOF;
      FIFOF#(Vector#(TDiv#(n,2),a)) infifo1 <- mkFIFOF;
      rule splitinput;
	 let v = inpipe.first();
	 inpipe.deq();
	 infifo0.enq(takeAt(0, v));
	 infifo1.enq(takeAt(valueOf(TDiv#(n,2)), v));
      endrule
      PipeOut#(Vector#(TDiv#(n,2),a)) inpipe0 = toPipeOut(infifo0);
      PipeOut#(Vector#(TDiv#(n,2),a)) inpipe1 = toPipeOut(infifo1);

      PipeOut#(a) p0 <- mkReducePipe(combinepipe, inpipe0);
      PipeOut#(a) p1 <- mkReducePipe(combinepipe, inpipe1);

      PipeOut#(Tuple2#(a,a)) tplpipe = (interface PipeOut;
					method Tuple2#(a,a) first(); return tuple2(p0.first, p1.first); endmethod
					method Action deq(); p0.deq(); p1.deq(); endmethod
					method notEmpty(); return p0.notEmpty() && p1.notEmpty(); endmethod
					endinterface);
      PipeOut#(a) outpipe <- combinepipe(tplpipe);
      return outpipe;
   endmodule
endinstance


missed a few

missed a few

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

// portz libraries
import Leds::*;
import CtrlMux::*;
import Portal::*;
import ConnectalMemory::*;
import MemTypes::*;
import MemServer::*;
import MemServerInternal::*;
import MMU::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import HostInterface::*;

// generated by tool
import MMURequest::*;
import StrstrRequest::*;
import MemServerIndication::*;
import MMUIndication::*;
import StrstrIndication::*;

import Strstr::*;

import AuroraCommon::*;
import FlashTop::*;
import FlashRequest::*;
import FlashIndication::*;

typedef enum {
   FlashIndication,
   FlashRequest,

   HostMemServerIndication,
   HostMemServerRequest,

   FlashMemServerIndication,
   FlashMemServerRequest,

   HostMMURequest,
   HostMMUIndication,

   FlashMMURequest,
   FlashMMUIndication,

   AlgoIndication,
   AlgoRequest

} IfcNames deriving (Eq,Bits);

interface Top_Pins;
	interface Aurora_Pins#(4) aurora_fmc1;
	interface Aurora_Clock_Pins aurora_clk_fmc1;
endinterface

typedef 128 WordSz;

module mkConnectalTop#(HostType host) (StdConnectalDmaTop#(PhysAddrWidth));

   Clock clk250 = host.doubleClock;
   Reset rst250 = host.doubleReset;

   Clock curClk <- exposeCurrentClock;
   Reset curRst <- exposeCurrentReset;

   // strstr algo
   StrstrIndicationProxy strstrIndicationProxy <- mkStrstrIndicationProxy(AlgoIndication);
   Strstr#(64) strstr <- mkStrstr(strstrIndicationProxy.ifc);
   StrstrRequestWrapper strstrRequestWrapper <- mkStrstrRequestWrapper(AlgoRequest,strstr.request);

   // host mmu
   MMUIndicationProxy hostMMUIndicationProxy <- mkMMUIndicationProxy(HostMMUIndication);
   MMU#(PhysAddrWidth) hostMMU <- mkMMU(1, True, hostMMUIndicationProxy.ifc);
   MMURequestWrapper hostMMURequestWrapper <- mkMMURequestWrapper(HostMMURequest, hostMMU.request);

   // flash mmu
   MMUIndicationProxy flashMMUIndicationProxy <- mkMMUIndicationProxy(FlashMMUIndication);
   MMU#(PhysAddrWidth) flashMMU <- mkMMU(0, False, flashMMUIndicationProxy.ifc);
   MMURequestWrapper flashMMURequestWrapper <- mkMMURequestWrapper(FlashMMURequest, flashMMU.request);

   // flash top
   FlashIndicationProxy flashIndicationProxy <- mkFlashIndicationProxy(FlashIndication);
   FlashTop flashtop <- mkFlashTop(flashIndicationProxy.ifc, clk250, rst250);
   FlashRequestWrapper flashRequestWrapper <- mkFlashRequestWrapper(FlashRequest,flashtop.request);

   // host memory server
   MemServerIndicationProxy hostMemServerIndicationProxy <- mkMemServerIndicationProxy(HostMemServerIndication);
   let rcs = cons(strstr.config_read_client,cons(flashtop.hostMemReadClient,nil));
   let wcs = cons(flashtop.hostMemWriteClient,nil);
   let mmus = cons(hostMMU,nil);
   MemServer#(PhysAddrWidth,64,1) hostMemServer <- mkMemServerRW(hostMemServerIndicationProxy.ifc, rcs, wcs, mmus);

   // flash memory read server
   MemServerIndicationProxy flashMemServerIndicationProxy <- mkMemServerIndicationProxy(FlashMemServerIndication);
   MemServer#(PhysAddrWidth,64,1) flashMemServer <- mkMemServerR(flashMemServerIndicationProxy.ifc, cons(strstr.haystack_read_client,nil),  cons(flashMMU,nil));
   mkConnection(flashMemServer.masters[0], flashtop.memSlave);

   Vector#(10,StdPortal) portals;

   portals[0] = strstrRequestWrapper.portalIfc;
   portals[1] = strstrIndicationProxy.portalIfc;

   portals[2] = hostMMURequestWrapper.portalIfc;
   portals[3] = hostMMUIndicationProxy.portalIfc;

   portals[4] = flashMMURequestWrapper.portalIfc;
   portals[5] = flashMMUIndicationProxy.portalIfc;

   portals[6] = flashRequestWrapper.portalIfc;
   portals[7] = flashIndicationProxy.portalIfc;

   portals[8] = hostMemServerIndicationProxy.portalIfc;
   portals[9] = nandsimMemServerIndicationProxy.portalIfc;

   let ctrl_mux <- mkSlaveMux(portals);

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

endmodule : mkConnectalTop

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

// portz libraries
import Leds::*;
import CtrlMux::*;
import Portal::*;
import ConnectalMemory::*;
import MemTypes::*;
import MemServer::*;
import MemServerInternal::*;
import MMU::*;
import MemreadEngine::*;
import MemwriteEngine::*;

// generated by tool
import MMURequest::*;
import StrstrRequest::*;
import MemServerIndication::*;
import MMUIndication::*;
import StrstrIndication::*;

import Strstr::*;

import AuroraCommon::*;
import FlashTop::*;
import FlashRequest::*;
import FlashIndication::*;

typedef enum {
   FlashIndication,
   FlashRequest,

   HostMemServerIndication,
   HostMemServerRequest,

   HostMMURequest,
   HostMMUIndication,

   FlashMMURequest,
   FlashMMUIndication,

   AlgoIndication,
   AlgoRequest

} IfcNames deriving (Eq,Bits);

interface Top_Pins;
	interface Aurora_Pins#(4) aurora_fmc1;
	interface Aurora_Clock_Pins aurora_clk_fmc1;
endinterface

typedef 128 WordSz;

module mkConnectalTop#(HostType host) (StdConnectalDmaTop#(PhysAddrWidth));

   Clock clk250 = host.doubleClock;
   Reset rst250 = host.doubleReset;

   Clock curClk <- exposeCurrentClock;
   Reset curRst <- exposeCurrentReset;

   // strstr algo
   StrstrIndicationProxy strstrIndicationProxy <- mkStrstrIndicationProxy(AlgoIndication);
   Strstr#(64) strstr <- mkStrstr(strstrIndicationProxy.ifc);
   StrstrRequestWrapper strstrRequestWrapper <- mkStrstrRequestWrapper(AlgoRequest,strstr.request);

   // host mmu
   MMUIndicationProxy hostMMUIndicationProxy <- mkMMUIndicationProxy(HostMMUIndication);
   MMU#(PhysAddrWidth) hostMMU <- mkMMU(1, True, hostMMUIndicationProxy.ifc);
   MMURequestWrapper hostMMURequestWrapper <- mkMMURequestWrapper(HostMMURequest, hostMMU.request);

   // flash mmu
   MMUIndicationProxy flashMMUIndicationProxy <- mkMMUIndicationProxy(FlashMMUIndication);
   MMU#(PhysAddrWidth) flashMMU <- mkMMU(0, False, flashMMUIndicationProxy.ifc);
   MMURequestWrapper flashMMURequestWrapper <- mkMMURequestWrapper(FlashMMURequest, flashMMU.request);

   // flash top
   FlashIndicationProxy flashIndicationProxy <- mkFlashIndicationProxy(FlashIndication);
   FlashTop flashtop <- mkFlashTop(flashIndicationProxy.ifc, clk250, rst250);
   FlashRequestWrapper flashRequestWrapper <- mkFlashRequestWrapper(FlashRequest,flashtop.request);

   // host memory server
   MemServerIndicationProxy hostMemServerIndicationProxy <- mkMemServerIndicationProxy(HostMemServerIndication);
   let rcs = cons(strstr.config_read_client,cons(flashtop.hostMemReadClient,nil));
   let wcs = cons(flashtop.hostMemWriteClient,nil);
   let mmus = cons(hostMMU,nil);
   MemServer#(PhysAddrWidth,64,1) hostMemServer <- mkMemServerRW(hostMemServerIndicationProxy.ifc, rcs, wcs, mmus);

   // flash memory read server
   MemServerIndicationProxy flashMemServerIndicationProxy <- mkMemServerIndicationProxy(FlashMemServerIndication);
   MemServer#(PhysAddrWidth,64,1) flashMemServer <- mkMemServerR(flashMemServerIndicationProxy.ifc, strstr.haystack_read_clients,  cons(flashMMU,nil));
   mkConnection(flashMemServer.masters[0], flashtop.memSlave);

   Vector#(10,StdPortal) portals;

   portals[0] = strstrRequestWrapper.portalIfc;
   portals[1] = strstrIndicationProxy.portalIfc;

   portals[2] = hostMMURequestWrapper.portalIfc;
   portals[3] = hostMMUIndicationProxy.portalIfc;

   portals[4] = flashMMURequestWrapper.portalIfc;
   portals[5] = flashMMUIndicationProxy.portalIfc;

   portals[6] = flashRequestWrapper.portalIfc;
   portals[7] = flashIndicationProxy.portalIfc;

   portals[8] = hostMemServerIndicationProxy.portalIfc;
   portals[9] = nandsimMemServerIndicationProxy.portalIfc;

   let ctrl_mux <- mkSlaveMux(portals);

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

endmodule : mkConnectalTop


use a vector of Regs for tracking commands in progress

// 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 ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import Connectable::*;

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

module mkMemwriteEngine(MemwriteEngine#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(TLog#(numServers)), MemTypes::MemengineCmd), TMin#(2,TLog#(numServers)))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(dataWidth),Bool),TMin#(2, TLog#(numServers)))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(1, d__, dataWidth)
	    ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, TLog#(numServers)))
	    ,FunnelPipesPipelined#(1, numServers,Tuple3#(Bit#(TLog#(numServers)), Bit#(dataWidth), Bool), TMin#(2,TLog#(numServers)))
	    ,Add#(e__, TLog#(numServers), 6)
	    );
   let rv <- mkMemwriteEngineBuff(valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

interface BurstFunnel#(numeric type k, numeric type w);
   method Action loadIdx(Bit#(TLog#(k)) i);
   interface Vector#(k, PipeIn#(Bit#(w))) dataIn;
   interface Vector#(k, Reg#(Bit#(BurstLenSize))) burstLen;
   interface PipeOut#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) dataOut;
endinterface

module mkBurstFunnel#(Integer maxBurstLen)(BurstFunnel#(k,w))
   provisos( Log#(k,logk)
	    ,Min#(2,logk,bpc)
	    ,FunnelPipesPipelined#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool), bpc)
	    );

   Reg#(Bit#(2)) nameGen <- mkReg(0);
   UGBramFifos#(4,16,Tuple2#(Bit#(w),Bool)) complBuff <- mkUGBramFifos;
   Vector#(4, ConfigCounter#(16)) compCnts <- replicateM(mkConfigCounter(0));
   Vector#(k,FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool))) data_in <- replicateM(mkFIFOF);
   Vector#(k,Reg#(Bit#(BurstLenSize))) burst_len <- replicateM(mkReg(0));
   Vector#(k,Reg#(Bit#(BurstLenSize))) drain_cnt <- replicateM(mkReg(0));
   Reg#(Bit#(BurstLenSize)) inj_ctrl <- mkReg(0);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) loadIdxs <- mkSizedBRAMFIFO(32);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) inFlight <- mkSizedBRAMFIFO(4);
   FunnelPipe#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool),bpc) data_in_funnel <- mkFunnelPipesPipelined(map(toPipeOut,data_in));
   Reg#(Bit#(BurstLenSize)) drainCnt <- mkReg(0);
   FIFOF#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) exit_data <- mkFIFOF;
   FIFO#(Bit#(logk)) drainRename <- mkFIFO;

   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(Bit#(32)) last_entry <- mkReg(0);

   rule cyc;
      cycle <= cycle+1;
   endrule

   function PipeIn#(Bit#(w)) enter_data(FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v) if (tpl_1(loadIdxs.first) == fromInteger(i));
	     last_entry <= cycle;
	     match {.old_name, .new_name} = loadIdxs.first;
	     let first = inj_ctrl == 0;
	     let cnt = first ? burst_len[i] : inj_ctrl;
	     let new_cnt = cnt-1;
	     let last = new_cnt == 0;
	     inj_ctrl <= new_cnt;
	     if (first)
		inFlight.enq(loadIdxs.first);
	     if (last)
		loadIdxs.deq;
	     f.enq(tuple3(new_name, v, last));
	     //$display("%d enq %d", cycle-last_entry, i);
	  endmethod
       endinterface);
   Vector#(k, PipeIn#(Bit#(w))) data_in_pipes = zipWith(enter_data, data_in, genVector);

   function Reg#(Bit#(BurstLenSize)) check(Reg#(Bit#(BurstLenSize)) r) =
      (interface Reg;
	  method Action _write(Bit#(BurstLenSize) v);
	     if(v > 16) begin
		$display("ERROR mkBurstFunnel: burstLen too large");
		$finish;
	     end
	     r <= v;
	  endmethod
	  method Bit#(BurstLenSize) _read = r._read;
       endinterface);

   rule drain_funnel;
      match{.new_name,.data,.last} = data_in_funnel[0].first;
      data_in_funnel[0].deq;
      complBuff.enq(new_name,tuple2(data,last));
      compCnts[new_name].increment(1);
   endrule

   rule drain_req (compCnts[tpl_2(inFlight.first)].read > 0);
      match {.old_name, .new_name} = inFlight.first;
      let new_drainCnt = drainCnt-1;
      if (drainCnt == 0) begin
	 new_drainCnt = burst_len[old_name]-1;
	 drainRename.enq(truncate(old_name));
      end
      if (new_drainCnt == 0) begin
	 inFlight.deq;
      end
      complBuff.first_req(new_name);
      drainCnt <= new_drainCnt;
      compCnts[new_name].decrement(1);
      complBuff.deq(new_name);
   endrule

   rule drain_resp;
      match {.data,.last} <- complBuff.first_resp;
      if (last)
	 drainRename.deq;
      exit_data.enq(tuple2(drainRename.first,data));
   endrule

   method Action loadIdx(Bit#(logk) idx);
      loadIdxs.enq(tuple2(extend(idx),nameGen));
      nameGen <= nameGen+1;
   endmethod
   interface burstLen = map(check,burst_len);
   interface dataIn = data_in_pipes;
   interface PipeOut dataOut = toPipeOut(exit_data);
endmodule

module mkMemwriteEngineBuff#(Integer bufferSizeBytes)(MemwriteEngine#(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)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(serverIdxSz),MemengineCmd),bpc)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(dataWidth),Bool),bpc)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, serverIdxSz))
	     ,Add#(1, d__, dataWidth)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(serverIdxSz),Bit#(dataWidth), Bool), TMin#(2, serverIdxSz))
	     ,Add#(f__, TLog#(numServers), TAdd#(1, serverIdxSz))
	     ,Add#(g__, serverIdxSz, 6)
	     );

   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(0));
   Vector#(numServers, Reg#(MemengineCmd))        cmdRegs <- replicateM(mkReg(unpack(0)));

   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#(MemTagSize),Bool))    workf <- mkSizedBRAMFIFO(32); // is this the right size?
   FIFO#(Tuple2#(Bit#(serverIdxSz),Bool))           donef <- mkSizedBRAMFIFO(32); // is this the right size?

   Vector#(numServers, FIFO#(Bool))              outfs <- replicateM(mkSizedFIFO(1));
   Vector#(numServers, FIFOF#(MemengineCmd))    cmds_in <- replicateM(mkSizedFIFOF(1));
   Vector#(numServers, FIFOF#(Bit#(dataWidth)))  write_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));
   Vector#(numServers, PipeOut#(Bit#(dataWidth))) foo = map(toPipeOut, write_data_buffs);
   BurstFunnel#(numServers,dataWidth) write_data_funnel <- mkBurstFunnel(bufferSizeBeats);
   zipWithM(mkConnection, foo, write_data_funnel.dataIn);

   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;
	 cmdRegs[idx] <= cmd;
      endrule

   rule load_ctxt_a if (!load_in_progress);
      if (outs1[loadIdx]) begin
	 load_in_progress <= True;
	 let cmd = cmdRegs[loadIdx];
	 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));
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b if (load_in_progress);
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 //$display("load_ctxt_b %h %d", cmd.base, idx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 write_data_funnel.loadIdx(truncate(loadIdx));
	 if (cond1) begin
	    outs1[loadIdx] <= False;
	 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};
	    cmdRegs[loadIdx] <= new_cmd;
	 end
      end
   endrule

   function PipeIn#(Bit#(w)) check_in(FIFOF#(Bit#(w)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v);
	     f.enq(v);
	     buffCap[i].increment(1);
	     //$display("check_in %d", i);
	     // if(i==2)
	     // 	for(Integer j = 0; j < valueOf(w); j=j+32) begin
	     // 	   Bit#(32) xx = v[j+31:j];
	     // 	   $display("%h", xx);
	     // 	end
	  endmethod
       endinterface);

   function MemwriteServer#(dataWidth) toMemwriteServer(Server#(MemengineCmd,Bool) cs, PipeIn#(Bit#(dataWidth)) p) =
      (interface MemwriteServer;
	  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 c) if (outs0[i] < cmd_q_depth);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			Bit#(32) bl = extend(c.burstLen);
			// this is because bsc lifts the divide operation (below)
			// and on startup the simulator gets a floating-point exception
	  		if (bl ==0)
			   bl = 1;
			let mdw0 = ((c.len)/bl)*bl != c.len;
			let mdw1 = ((c.len)/dw)*dw != c.len;
			let bbl = extend(c.burstLen) > bsb;
			if(bbl || mdw0 || mdw1 || c.len == 0) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported burstLen %d %d", bsb, c.burstLen);
			   if (mdw0 || mdw1 || c.len == 0)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported len %h mdw0=%d mdw1=%d", c.len, mdw0, mdw1);
			end
			else begin
`endif
			   outs0[i] <= outs0[i]+1;
			   cmds_in[i].enq(c);
			   write_data_funnel.burstLen[i] <= c.burstLen >> beat_shift;
			   //$display("(%d) %h %h %h", i, c.base, c.len, c.burstLen);
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			let rv <- toGet(outfs[i]).get;
	 		outs0[i] <= outs0[i]-1;
			return rv;
		     endmethod
		  endinterface
	       endinterface);
   interface writeServers = rs;
   interface MemWriteClient dmaClient;
      interface Get writeReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    Bool last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    let new_tag = (cmd.tag << valueOf(serverIdxSz)) | extend(idx);
	    workf.enq(tuple3(truncate(bl>>beat_shift), new_tag, last));
	    //$display("writeReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: new_tag};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get;
	    match {.rc, .new_tag, .last} = workf.first;
	    Bit#(serverIdxSz) idx = truncate(new_tag);
	    let new_respCnt = respCnt+1;
	    if (new_respCnt == rc) begin
	       respCnt <= 0;
	       workf.deq;
	       donef.enq(tuple2(idx,last));
	    end
	    else begin
	       respCnt <= new_respCnt;
	    end
	    match {._idx, .wd} <- toGet(write_data_funnel.dataOut).get;
	    if(_idx != idx) begin
	       $display("ERROR mkMemwriteEngineBuf: bursts oo %d %d", idx, _idx);
	       $finish;
	    end
	    // TODO: this last field should be set correctly to mark the end of bursts
	    return MemData{data:wd, tag:new_tag, last:False};
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(MemTagSize) tag);
	    match {.idx, .last} <- toGet(donef).get;
	    if (last)
	       outfs[idx].enq(True);
	    //$display("writeDone %d %d", idx, last);
	 endmethod
      endinterface
   endinterface
   interface dataPipes = zipWith(check_in, write_data_buffs, genVector);
   interface write_servers = zipWith(toMemwriteServer, rs, zipWith(check_in, write_data_buffs, genVector));
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 ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import Connectable::*;

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

module mkMemwriteEngine(MemwriteEngine#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(TLog#(numServers)), MemTypes::MemengineCmd), TMin#(2,TLog#(numServers)))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(dataWidth),Bool),TMin#(2, TLog#(numServers)))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(1, d__, dataWidth)
	    ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, TLog#(numServers)))
	    ,FunnelPipesPipelined#(1, numServers,Tuple3#(Bit#(TLog#(numServers)), Bit#(dataWidth), Bool), TMin#(2,TLog#(numServers)))
	    ,Add#(e__, TLog#(numServers), 6)
	    );
   let rv <- mkMemwriteEngineBuff(valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

interface BurstFunnel#(numeric type k, numeric type w);
   method Action loadIdx(Bit#(TLog#(k)) i);
   interface Vector#(k, PipeIn#(Bit#(w))) dataIn;
   interface Vector#(k, Reg#(Bit#(BurstLenSize))) burstLen;
   interface PipeOut#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) dataOut;
endinterface

module mkBurstFunnel#(Integer maxBurstLen)(BurstFunnel#(k,w))
   provisos( Log#(k,logk)
	    ,Min#(2,logk,bpc)
	    ,FunnelPipesPipelined#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool), bpc)
	    );

   Reg#(Bit#(2)) nameGen <- mkReg(0);
   UGBramFifos#(4,16,Tuple2#(Bit#(w),Bool)) complBuff <- mkUGBramFifos;
   Vector#(4, ConfigCounter#(16)) compCnts <- replicateM(mkConfigCounter(0));
   Vector#(k,FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool))) data_in <- replicateM(mkFIFOF);
   Vector#(k,Reg#(Bit#(BurstLenSize))) burst_len <- replicateM(mkReg(0));
   Vector#(k,Reg#(Bit#(BurstLenSize))) drain_cnt <- replicateM(mkReg(0));
   Reg#(Bit#(BurstLenSize)) inj_ctrl <- mkReg(0);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) loadIdxs <- mkSizedBRAMFIFO(32);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) inFlight <- mkSizedBRAMFIFO(4);
   FunnelPipe#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool),bpc) data_in_funnel <- mkFunnelPipesPipelined(map(toPipeOut,data_in));
   Reg#(Bit#(BurstLenSize)) drainCnt <- mkReg(0);
   FIFOF#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) exit_data <- mkFIFOF;
   FIFO#(Bit#(logk)) drainRename <- mkFIFO;

   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(Bit#(32)) last_entry <- mkReg(0);

   rule cyc;
      cycle <= cycle+1;
   endrule

   function PipeIn#(Bit#(w)) enter_data(FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v) if (tpl_1(loadIdxs.first) == fromInteger(i));
	     last_entry <= cycle;
	     match {.old_name, .new_name} = loadIdxs.first;
	     let first = inj_ctrl == 0;
	     let cnt = first ? burst_len[i] : inj_ctrl;
	     let new_cnt = cnt-1;
	     let last = new_cnt == 0;
	     inj_ctrl <= new_cnt;
	     if (first)
		inFlight.enq(loadIdxs.first);
	     if (last)
		loadIdxs.deq;
	     f.enq(tuple3(new_name, v, last));
	     //$display("%d enq %d", cycle-last_entry, i);
	  endmethod
       endinterface);
   Vector#(k, PipeIn#(Bit#(w))) data_in_pipes = zipWith(enter_data, data_in, genVector);

   function Reg#(Bit#(BurstLenSize)) check(Reg#(Bit#(BurstLenSize)) r) =
      (interface Reg;
	  method Action _write(Bit#(BurstLenSize) v);
	     if(v > 16) begin
		$display("ERROR mkBurstFunnel: burstLen too large");
		$finish;
	     end
	     r <= v;
	  endmethod
	  method Bit#(BurstLenSize) _read = r._read;
       endinterface);

   rule drain_funnel;
      match{.new_name,.data,.last} = data_in_funnel[0].first;
      data_in_funnel[0].deq;
      complBuff.enq(new_name,tuple2(data,last));
      compCnts[new_name].increment(1);
   endrule

   rule drain_req (compCnts[tpl_2(inFlight.first)].read > 0);
      match {.old_name, .new_name} = inFlight.first;
      let new_drainCnt = drainCnt-1;
      if (drainCnt == 0) begin
	 new_drainCnt = burst_len[old_name]-1;
	 drainRename.enq(truncate(old_name));
      end
      if (new_drainCnt == 0) begin
	 inFlight.deq;
      end
      complBuff.first_req(new_name);
      drainCnt <= new_drainCnt;
      compCnts[new_name].decrement(1);
      complBuff.deq(new_name);
   endrule

   rule drain_resp;
      match {.data,.last} <- complBuff.first_resp;
      if (last)
	 drainRename.deq;
      exit_data.enq(tuple2(drainRename.first,data));
   endrule

   method Action loadIdx(Bit#(logk) idx);
      loadIdxs.enq(tuple2(extend(idx),nameGen));
      nameGen <= nameGen+1;
   endmethod
   interface burstLen = map(check,burst_len);
   interface dataIn = data_in_pipes;
   interface PipeOut dataOut = toPipeOut(exit_data);
endmodule

module mkMemwriteEngineBuff#(Integer bufferSizeBytes)(MemwriteEngine#(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)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(serverIdxSz),MemengineCmd),bpc)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(dataWidth),Bool),bpc)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, serverIdxSz))
	     ,Add#(1, d__, dataWidth)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(serverIdxSz),Bit#(dataWidth), Bool), TMin#(2, serverIdxSz))
	     ,Add#(f__, TLog#(numServers), TAdd#(1, serverIdxSz))
	     ,Add#(g__, serverIdxSz, 6)
	     );

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(dataWidthBytes);
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs1 <- replicateM(mkReg(0));
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs0 <- replicateM(mkReg(0));
   Vector#(numServers, ConfigCounter#(16))           buffCap <- replicateM(mkConfigCounter(0));
   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#(MemTagSize),Bool))    workf <- mkSizedBRAMFIFO(32); // is this the right size?
   FIFO#(Tuple2#(Bit#(serverIdxSz),Bool))           donef <- mkSizedBRAMFIFO(32); // is this the right size?

   Vector#(numServers, FIFO#(Bool))              outfs <- replicateM(mkSizedFIFO(1));
   Vector#(numServers, FIFOF#(Tuple2#(Bit#(serverIdxSz), MemengineCmd))) cmds_in <- replicateM(mkSizedFIFOF(1));
   FunnelPipe#(1, numServers, Tuple2#(Bit#(serverIdxSz), MemengineCmd),bpc) cmds_in_funnel <- mkFunnelPipesPipelined(map(toPipeOut,cmds_in));
   Vector#(numServers, FIFOF#(Bit#(dataWidth)))  write_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));
   Vector#(numServers, PipeOut#(Bit#(dataWidth))) foo = map(toPipeOut, write_data_buffs);
   BurstFunnel#(numServers,dataWidth) write_data_funnel <- mkBurstFunnel(bufferSizeBeats);
   zipWithM(mkConnection, foo, write_data_funnel.dataIn);

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

   rule store_cmd;
      match {.idx, .cmd} <- toGet(cmds_in_funnel[0]).get;
      outs1[idx] <= outs1[idx]+1;
      cmdBuf.enq(idx,cmd);
      //$display("store_cmd %d", idx);
   endrule

   rule load_ctxt_a (!load_in_progress);
      if (outs1[loadIdx] > 0) begin
	 load_in_progress <= True;
	 cmdBuf.first_req(truncate(loadIdx));
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b;
      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));
   endrule

   rule load_ctxt_c;
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 //$display("load_ctxt_b %h %d", cmd.base, idx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 write_data_funnel.loadIdx(truncate(loadIdx));
	 if (cond1) begin
	    outs1[loadIdx] <= outs1[loadIdx]-1;
	    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

   function PipeIn#(Bit#(w)) check_in(FIFOF#(Bit#(w)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v);
	     f.enq(v);
	     buffCap[i].increment(1);
	     //$display("check_in %d", i);
	     // if(i==2)
	     // 	for(Integer j = 0; j < valueOf(w); j=j+32) begin
	     // 	   Bit#(32) xx = v[j+31:j];
	     // 	   $display("%h", xx);
	     // 	end
	  endmethod
       endinterface);

   function MemwriteServer#(dataWidth) toMemwriteServer(Server#(MemengineCmd,Bool) cs, PipeIn#(Bit#(dataWidth)) p) =
      (interface MemwriteServer;
	  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 c) if (outs0[i] < cmd_q_depth);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			Bit#(32) bl = extend(c.burstLen);
			// this is because bsc lifts the divide operation (below)
			// and on startup the simulator gets a floating-point exception
	  		if (bl ==0)
			   bl = 1;
			let mdw0 = ((c.len)/bl)*bl != c.len;
			let mdw1 = ((c.len)/dw)*dw != c.len;
			let bbl = extend(c.burstLen) > bsb;
			if(bbl || mdw0 || mdw1 || c.len == 0) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported burstLen %d %d", bsb, c.burstLen);
			   if (mdw0 || mdw1 || c.len == 0)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported len %h mdw0=%d mdw1=%d", c.len, mdw0, mdw1);
			end
			else begin
`endif
			   outs0[i] <= outs0[i]+1;
			   cmds_in[i].enq(tuple2(fromInteger(i),c));
			   write_data_funnel.burstLen[i] <= c.burstLen >> beat_shift;
			   //$display("(%d) %h %h %h", i, c.base, c.len, c.burstLen);
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			let rv <- toGet(outfs[i]).get;
	 		outs0[i] <= outs0[i]-1;
			return rv;
		     endmethod
		  endinterface
	       endinterface);
   interface writeServers = rs;
   interface MemWriteClient dmaClient;
      interface Get writeReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    Bool last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    let new_tag = (cmd.tag << valueOf(serverIdxSz)) | extend(idx);
	    workf.enq(tuple3(truncate(bl>>beat_shift), new_tag, last));
	    //$display("writeReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: new_tag};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get;
	    match {.rc, .new_tag, .last} = workf.first;
	    Bit#(serverIdxSz) idx = truncate(new_tag);
	    let new_respCnt = respCnt+1;
	    if (new_respCnt == rc) begin
	       respCnt <= 0;
	       workf.deq;
	       donef.enq(tuple2(idx,last));
	    end
	    else begin
	       respCnt <= new_respCnt;
	    end
	    match {._idx, .wd} <- toGet(write_data_funnel.dataOut).get;
	    if(_idx != idx) begin
	       $display("ERROR mkMemwriteEngineBuf: bursts oo %d %d", idx, _idx);
	       $finish;
	    end
	    // TODO: this last field should be set correctly to mark the end of bursts
	    return MemData{data:wd, tag:new_tag, last:False};
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(MemTagSize) tag);
	    match {.idx, .last} <- toGet(donef).get;
	    if (last)
	       outfs[idx].enq(True);
	    //$display("writeDone %d %d", idx, last);
	 endmethod
      endinterface
   endinterface
   interface dataPipes = zipWith(check_in, write_data_buffs, genVector);
   interface write_servers = zipWith(toMemwriteServer, rs, zipWith(check_in, write_data_buffs, genVector));
endmodule


instantiate fsms

instantiate fsms

// 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 FIFO::*;
import GetPut::*;
import ClientServer::*;
import PortalRMemory::*;
import RingTypes::*;

// The interface to the copyengine is a pair of fifos which supply and accept
// blocks of 8 64 bit words
// A Command:
//  word 0: COPY[63:56] TAG[31:0]
//  word 1: readAddress[39:0]
//  word 2: writeAddress[39:0]
//  word 3: bytecount[15:0'
//  word 4-7 unused
// Status
//  word0-6 all 0
//  word7  TAG[31:0]

module mkCopyEngine#(ReadChan#(Bit#(64)) copy_read_chan, WriteChan#(Bit#(64)) copy_write_chan) ( Server#(Bit#(64), Bit#(64)));
   FIFO#(Bit#(64)) f_in  <- mkSizedFIFO(16);    // to buffer incoming requests
   FIFO#(Bit#(64)) f_out <- mkSizedFIFO(16);    // to buffer outgoing responses
   Reg#(Bit#(16)) copyReadCount <- mkReg(0);
   Reg#(Bit#(16)) copyWriteCount <- mkReg(0);
   Reg#(Bit#(40)) copyReadAddr <- mkReg(0);
   Reg#(Bit#(40)) copyWriteAddr <- mkReg(0);
   Reg#(Bit#(32)) copyTag <- mkReg(0);
   Reg#(Bool) copyBusy <- mkReg(False);

   Stmt copyStart = while(True)
   seq
      while (copyBusy) noAction;
      copyTag <= f_in.deq[31:0];
      copyReadAddr <= f_in.deq[39:0];
      copyWriteAddr <= f_in.deq[39:0];
      action
	 let regv <-f_in.deq[15:0];
	 copyReadCount <= regv;
	 copyWriteCount <= regv;
      endaction
      for (ii <= 0; ii < 4; ii <= ii+1)
	 f_in.deq;
      copyBusy <= True;
   endseq

    rule copyReadRule (copyBusy && (copyReadCount != 0));
       $display("copyRead %h, count %h", copyReadAddr, copyReadCount);
       copy_read_chan.readReq.put(copyReadAddr);
       copyReadAddr <= copyReadAddr + 8;
       copyReadCount <= copyReadCount - 8;
    endrule

    rule copyReadWriteRule (copyBusy);
       let data <- copy_read_chan.readData.get;
       $display("copyReadWrite addr %h", copyWriteAddr);
       copy_write_chan.writeReq.put(copyWriteAddr);
       copy_write_chan.writeData.put(data);
       copyWriteAddr <= copyWriteAddr + 8;
    endrule

   Stmt copyFinish = while(True)
   seq
      while (!copyBusy) noAction;
      while (copyWriteCount > 0)
      action
	 let v <= copy_write_chan.writeDone.get;
	 copyWriteCount <= copyWriteCount - 8;
      endaction
      for (ii <= 0; ii < 7; ii += 1)
	 f_out.enq(0);
      f_out.enq(extend(copyTag));
      copyBusy <= False;
   endseq

   FSM start();
   mkAutoFSM#(copyStart)(start);
   FSM finish();
   mkAutoFSM#(copyFinish)(finish);

   interface Put request = toPut(f_in);
   interface Get response = toGet (f_out);

endmodule: mkCopyEngine

// 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 FIFO::*;
import GetPut::*;
import ClientServer::*;
import PortalRMemory::*;
import RingTypes::*;

// The interface to the copyengine is a pair of fifos which supply and accept
// blocks of 8 64 bit words

module mkCopyEngine#(ReadChan#(Bit#(64)) copy_read_chan, WriteChan#(Bit#(64)) copy_write_chan) ( Server#(Bit#(64), Bit#(64)));
   FIFO#(CommandStruct) f_in  <- mkFIFO;    // to buffer incoming requests
   FIFO#(Bit#(32)) f_out <- mkFIFO;    // to buffer outgoing responses
   Reg#(Bit#(16)) copyReadCount <- mkReg(0);
   Reg#(Bit#(16)) copyWriteCount <- mkReg(0);
   Reg#(Bit#(40)) copyReadAddr <- mkReg(0);
   Reg#(Bit#(40)) copyWriteAddr <- mkReg(0);
   Reg#(Bit#(32)) copyTag <- mkReg(0);
   Reg#(Bool) copyBusy <- mkReg(False);

   Stmt CopyStart = while(True)
   seq
      while (copyBusy) noAction;
      copyTag <= f_in.deq[31:0];
      copyReadAddr <= f_in.deq[39:0];
      copyWriteAddr <= f_in.deq[39:0];
      action
	 let regv <-f_in.deq[15:0];
	 copyReadCount <= regv;
	 copyWriteCount <= regv;
      endaction
      for (ii <= 0; ii < 4; ii <= ii+1)
	 f_in.deq;
      copyBusy <= True;
   endseq

    rule copyReadRule (copyBusy && (copyReadCount != 0));
       $display("copyRead %h, count %h", copyReadAddr, copyReadCount);
       copy_read_chan.readReq.put(copyReadAddr);
       copyReadAddr <= copyReadAddr + 8;
       copyReadCount <= copyReadCount - 8;
    endrule

    rule copyReadWriteRule (copyBusy);
       let data <- copy_read_chan.readData.get;
       $display("copyReadWrite addr %h", copyWriteAddr);
       copy_write_chan.writeReq.put(copyWriteAddr);
       copy_write_chan.writeData.put(data);
       copyWriteAddr <= copyWriteAddr + 8;
    endrule

   Stmt copyFinish = while(True)
   seq
      while (!copyBusy) noAction;
      while (copyWriteCount > 0)
      action
	 let v <= copy_write_chan.writeDone.get;
	 copyWriteCount <= copyWriteCount - 8;
      endaction
      for (ii <= 0; ii < 7; ii += 1)
	 f_out.enq(0);
      f_out.enq(extend(copyTag));
      copyBusy >= False;
   endseq

   interface Put request = toPut(f_in);
   interface Get response = toGet (f_out);

endmodule: mkCopyEngine