Fixed a corner-case bug in AXI4_Fabric when num-masters = 1 (detail follows)

The fabric uses an index (0..num_masters-1) to identify a master so it
knows how to route responses. When num_masters=1, it's identification
is a Bit#(0), on which equality tests are dicey.  Fixed to assume a
range (0..num_masters), so at least Bit #(1).

// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved

package AXI4_Fabric;

// ================================================================
// This package defines a fabric connecting CPUs, Memories and DMAs
// and other IP blocks.

// ================================================================
// Bluespec library imports

import Vector       :: *;
import FIFOF        :: *;
import SpecialFIFOs :: *;
import ConfigReg    :: *;

// ----------------
// BSV additional libs

import Cur_Cycle  :: *;

// ================================================================
// Project imports

import Semi_FIFOF :: *;
import AXI4_Types :: *;

// ================================================================
// The interface for the fabric module

interface AXI4_Fabric_IFC #(numeric type tn_num_masters,
			    numeric type tn_num_slaves,
			    numeric type wd_id,
			    numeric type wd_addr,
			    numeric type wd_data,
			    numeric type wd_user);
   method Action reset;
   method Action set_verbosity (Bit #(4) verbosity);

   // From masters
   interface Vector #(tn_num_masters, AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user))  v_from_masters;

   // To slaves
   interface Vector #(tn_num_slaves,  AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_to_slaves;
endinterface

// ================================================================
// The Fabric module
// The function parameter is an address-decode function, which
// returns (True,  slave-port-num)  if address is mapped to slave-port-num
//         (False, ?)               if address is unmapped to any slave port

module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(tn_num_slaves)))
			        fn_addr_to_slave_num (Bit #(wd_addr) addr))
		     (AXI4_Fabric_IFC #(tn_num_masters, tn_num_slaves, wd_id, wd_addr, wd_data, wd_user))

   provisos (Log #(tn_num_masters, log_nm),
	     Log #(tn_num_slaves,  log_ns),
	     Log #(TAdd #(tn_num_slaves,  1),  log_ns_plus_1),
	     Log #(TAdd #(tn_num_masters,  1), log_nm_plus_1));

   Integer num_masters = valueOf (tn_num_masters);
   Integer num_slaves  = valueOf (tn_num_slaves);

   // 0: quiet; 1: show transactions
   Reg #(Bit #(4)) cfg_verbosity  <- mkConfigReg (0);

   Reg #(Bool) rg_reset <- mkReg (True);

   // Transactors facing masters
   Vector #(tn_num_masters, AXI4_Slave_Xactor_IFC  #(wd_id, wd_addr, wd_data, wd_user))
      xactors_from_masters <- replicateM (mkAXI4_Slave_Xactor);

   // Transactors facing slaves
   Vector #(tn_num_slaves,  AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user))
       xactors_to_slaves <- replicateM (mkAXI4_Master_Xactor);

   // ----------------------------------------------------------------
   // Book-keeping FIFOs and regs
   // - to keep track of which master originated a transaction, in order
   //       to route corresponding responses back to that master
   // - to manage wdata channel based on burst info in awaddr channel
   // - to manage requests that do not map to any of the slaves.
   // Legal slaves are 0..(num_slaves-1)
   //     The "illegal" value of 'num_slaves' is used for decode errors (no such slave).
   // num_masters could be 1 => Bit #(0) to identify a master, but
   //     equality on Bit #(0) is dicey, so we always use num_masters+1.
   // Size of SizedFIFOs is estimated: should cover round-trip latency to slave and back.

   // ----------------
   // Write-transaction book-keeping

   // On an mi->sj write-transaction, this fifo records sj for master mi
   Vector #(tn_num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_wr_sjs <- replicateM (mkSizedFIFOF (8));

   // On an mi->sj write-transaction, this fifo records mi for slave sj
   Vector #(tn_num_slaves,  FIFOF #(Bit #(log_nm_plus_1))) v_f_wr_mis  <- replicateM (mkSizedFIFOF (8));

   // On an mi->sj write-transaction, this fifo records a task (sj, awlen) for W channel
   Vector #(tn_num_masters,
	    FIFOF #(Tuple2 #(Bit #(log_ns_plus_1),
			     AXI4_Len)))     v_f_wd_tasks <- replicateM (mkFIFOF);
   // On an mi->sj write-transaction, this register is the W-channel burst beat_count
   // (0 => ready for next burst)
   Vector #(tn_num_masters, Reg #(AXI4_Len)) v_rg_wd_beat_count <- replicateM (mkReg (0));

   // On a write-transaction to non-existent slave, record id and user for error response
   Vector #(tn_num_masters,
	    FIFOF #(Tuple2 #(Bit #(wd_id),
			     Bit #(wd_user))))  v_f_wr_err_info <- replicateM (mkSizedFIFOF (8));

   // ----------------
   // Read-transaction book-keeping

   // On an mi->sj read-transaction, records sj for master mi
   Vector #(tn_num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_rd_sjs <- replicateM (mkSizedFIFOF (8));
   // On an mi->sj read-transaction, records (mi,arlen) for slave sj
   Vector #(tn_num_slaves,
	    FIFOF #(Tuple2 #(Bit #(log_nm_plus_1),
			     AXI4_Len)))            v_f_rd_mis <- replicateM (mkSizedFIFOF (8));
   // On an mi->sj read-transaction, this register is the R-channel burst beat_count
   // (0 => ready for next burst)
   Vector #(tn_num_slaves, Reg #(AXI4_Len)) v_rg_r_beat_count <- replicateM (mkReg (0));

   // On a read-transaction to non-exisitent slave, record id and user for error response
   Vector #(tn_num_masters,
	    FIFOF #(Tuple3 #(AXI4_Len,
			     Bit #(wd_id),
			     Bit #(wd_user)))) v_f_rd_err_info <- replicateM (mkSizedFIFOF (8));

   // On an mi->non-existent-slave read-transaction,
   // this register is the R-channel burst beat_count
   // (0 => ready for next burst)
   Vector #(tn_num_masters, Reg #(AXI4_Len)) v_rg_r_err_beat_count <- replicateM (mkReg (0));

   // ----------------------------------------------------------------
   // RESET

   rule rl_reset (rg_reset);
      if (cfg_verbosity > 0)
	 $display ("%0d: %m.rl_reset", cur_cycle);
      for (Integer mi = 0; mi < num_masters; mi = mi + 1) begin
	 xactors_from_masters [mi].reset;

	 v_f_wr_sjs [mi].clear;
	 v_f_wd_tasks [mi].clear;
	 v_rg_wd_beat_count [mi] <= 0;

	 v_f_wr_err_info [mi].clear;

	 v_f_rd_sjs [mi].clear;

	 v_f_rd_err_info [mi].clear;
      end

      for (Integer sj = 0; sj < num_slaves; sj = sj + 1) begin
	 xactors_to_slaves [sj].reset;
	 v_f_wr_mis [sj].clear;
	 v_f_rd_mis [sj].clear;
	 v_rg_r_beat_count [sj] <= 0;
      end
      rg_reset <= False;
   endrule

   // ----------------------------------------------------------------
   // BEHAVIOR

   // ----------------------------------------------------------------
   // Predicates to check if master I has transaction for slave J

   function Bool fv_mi_has_wr_for_sj (Integer mi, Integer sj);
      let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr;
      match { .legal, .slave_num } = fn_addr_to_slave_num (addr);
      return (legal
	      && (   (num_slaves == 1)
		  || (slave_num == fromInteger (sj))));
   endfunction

   function Bool fv_mi_has_wr_for_none (Integer mi);
      let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr;
      match { .legal, ._ } = fn_addr_to_slave_num (addr);
      return (! legal);
   endfunction

   function Bool fv_mi_has_rd_for_sj (Integer mi, Integer sj);
      let addr = xactors_from_masters [mi].o_rd_addr.first.araddr;
      match { .legal, .slave_num } = fn_addr_to_slave_num (addr);
      return (legal
	      && (   (num_slaves == 1)
		  || (slave_num == fromInteger (sj))));
   endfunction

   function Bool fv_mi_has_rd_for_none (Integer mi);
      let addr = xactors_from_masters [mi].o_rd_addr.first.araddr;
      match { .legal, ._ } = fn_addr_to_slave_num (addr);
      return (! legal);
   endfunction

   // ================================================================
   // Wr requests (AW, W and B channels)

   // Wr requests to legal slaves (AW channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_wr_xaction_master_to_slave (fv_mi_has_wr_for_sj (mi, sj));
	    // Move the AW transaction
	    AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);
	    xactors_to_slaves [sj].i_wr_addr.enq (a);

	    // Enqueue a task for the W channel
	    v_f_wd_tasks      [mi].enq (tuple2 (fromInteger (sj), a.awlen));

	    // Book-keeping
	    v_f_wr_mis        [sj].enq (fromInteger (mi));
	    v_f_wr_sjs        [mi].enq (fromInteger (sj));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_wr_xaction_master_to_slave: m%0d -> s%0d",
			 cur_cycle, mi, sj);
	       $display ("    ", fshow (a));
	    end
	 endrule

   // Wr requests to non-existent slave (AW channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
	 rule rl_wr_xaction_no_such_slave (fv_mi_has_wr_for_none (mi));
	    AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);

	    // Special value 'num_slaves' (not a legal sj) means "no such slave"
	    v_f_wr_sjs        [mi].enq (fromInteger (num_slaves));
	    v_f_wr_err_info   [mi].enq (tuple2 (a.awid, a.awuser));

	    // Enqueue a task for the W channel (must consume the write-data burst)
	    v_f_wd_tasks      [mi].enq (tuple2 (fromInteger (num_slaves), a.awlen));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_wr_xaction_no_such_slave: m%0d -> ?",
			 cur_cycle, mi);
	       $display ("        ", fshow (a));
	    end
	 endrule

   // Wr data (W channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)

      // Handle W channel burst
      // Invariant: v_rg_wd_beat_count == 0 between bursts
      // Note: awlen is encoded as 0..255 for burst lengths of 1..256
      rule rl_wr_xaction_master_to_slave_data (v_f_wd_tasks [mi].first matches {.sj, .awlen});
	 AXI4_Wr_Data #(wd_data, wd_user) d <- pop_o (xactors_from_masters [mi].o_wr_data);

	 // If sj is a legal slave, send it the data beat, else drop it.
	 if (sj < fromInteger (num_slaves))
	    xactors_to_slaves [sj].i_wr_data.enq (d);

	 if (cfg_verbosity > 0) begin
	    $display ("%0d: %m.rl_wr_xaction_master_to_slave_data: m%0d -> s%0d, beat %0d/%0d",
		      cur_cycle, mi, sj, v_rg_wd_beat_count [mi], awlen);
	    $display ("    ", fshow (d));
	 end

	 if (v_rg_wd_beat_count [mi] == awlen) begin
	    // End of burst
	    v_f_wd_tasks [mi].deq;
	    v_rg_wd_beat_count [mi] <= 0;

	    // Simulation-only assertion-check (no action, just display assertion failure)
	    // Final beat must have WLAST = 1
	    // Rely on slave (which should also see this error) to return error response
	    if (! (d.wlast)) begin
	       $display ("%0d: %m.rl_wr_xaction_master_to_slave_data: ERROR: m%0d -> s%0d",
			 cur_cycle, mi, sj);
	       $display ("    WLAST not set on final data beat (awlen = %0d)", awlen);
	       $display ("    ", fshow (d));
	    end
	 end
	 else
	    v_rg_wd_beat_count [mi] <= v_rg_wd_beat_count [mi] + 1;
      endrule

   // Wr responses from slaves to masters (B channel)

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_wr_resp_slave_to_master (   (v_f_wr_mis [sj].first == fromInteger (mi))
					  && (v_f_wr_sjs [mi].first == fromInteger (sj)));
	    v_f_wr_mis [sj].deq;
	    v_f_wr_sjs [mi].deq;
	    AXI4_Wr_Resp #(wd_id, wd_user) b <- pop_o (xactors_to_slaves [sj].o_wr_resp);

	    xactors_from_masters [mi].i_wr_resp.enq (b);

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_wr_resp_slave_to_master: m%0d <- s%0d",
			 cur_cycle, mi, sj);
	       $display ("        ", fshow (b));
	    end
	 endrule

   // Wr error responses to masters (B channel)
   // v_f_wr_sjs [mi].first has value num_slaves (illegal value)
   // v_f_wr_err_info [mi].first contains request fields 'awid' and 'awuser'

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)

      rule rl_wr_resp_err_to_master (v_f_wr_sjs [mi].first == fromInteger (num_slaves));
	 v_f_wr_sjs [mi].deq;
	 v_f_wr_err_info [mi].deq;

	 match { .awid, .awuser } = v_f_wr_err_info [mi].first;

	 let b = AXI4_Wr_Resp {bid:   awid,
			       bresp: axi4_resp_decerr,
			       buser: awuser};

	 xactors_from_masters [mi].i_wr_resp.enq (b);

	 if (cfg_verbosity > 0) begin
	    $display ("%0d: %m.rl_wr_resp_err_to_master: m%0d <- err", cur_cycle, mi);
	    $display ("        ", fshow (b));
	 end
      endrule

   // ================================================================
   // Rd requests (AR and R channels)

   // Rd requests to legal slaves (AR channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_rd_xaction_master_to_slave (fv_mi_has_rd_for_sj (mi, sj));
	    AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);

	    xactors_to_slaves [sj].i_rd_addr.enq (a);

	    v_f_rd_mis [sj].enq (tuple2 (fromInteger (mi), a.arlen));
	    v_f_rd_sjs [mi].enq (fromInteger (sj));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_rd_xaction_master_to_slave: m%0d -> s%0d",
			 cur_cycle, mi, sj);
	       $display ("        ", fshow (a));
	    end
	 endrule

   // Rd requests to non-existent slave (AR channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
	 rule rl_rd_xaction_no_such_slave (fv_mi_has_rd_for_none (mi));
	    AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);

	    v_f_rd_sjs      [mi].enq (fromInteger (num_slaves));
	    v_f_rd_err_info [mi].enq (tuple3 (a.arlen, a.arid, a.aruser));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_rd_xaction_no_such_slave: m%0d -> ?",
			 cur_cycle, mi);
	       $display ("        ", fshow (a));
	    end
	 endrule

   // Rd responses from slaves to masters (R channel)

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_rd_resp_slave_to_master (v_f_rd_mis [sj].first matches { .mi2, .arlen }
					  &&& (mi2 == fromInteger (mi))
					  &&& (v_f_rd_sjs [mi].first == fromInteger (sj)));

	    AXI4_Rd_Data #(wd_id, wd_data, wd_user) r <- pop_o (xactors_to_slaves [sj].o_rd_data);

	    if (v_rg_r_beat_count [sj] == arlen) begin
	       // Final beat of burst
	       v_f_rd_mis [sj].deq;
	       v_f_rd_sjs [mi].deq;
	       v_rg_r_beat_count [sj] <= 0;

	       // Assertion-check
	       // Final beat must have RLAST = 1
	       // If not, and if RRESP is OK, set RRESP to AXI4_RESP_SLVERR
	       if ((r.rresp == axi4_resp_okay) && (! (r.rlast))) begin
		  r.rresp = axi4_resp_slverr;
		  $display ("%0d: %m.rl_rd_resp_slave_to_master: ERROR: m%0d <- s%0d",
			    cur_cycle, mi, sj);
		  $display ("    RLAST not set on final data beat (arlen = %0d)", arlen);
		  $display ("    ", fshow (r));
	       end
	    end
	    else
	       v_rg_r_beat_count [sj] <= v_rg_r_beat_count [sj] + 1;

	    xactors_from_masters [mi].i_rd_data.enq (r);

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_rd_resp_slave_to_master: m%0d <- s%0d",
			 cur_cycle, mi, sj);
	       $display ("    r: ", fshow (r));
	    end
	 endrule

   // Rd error responses to masters (R channel)
   // v_f_rd_sjs [mi].first has value num_slaves (illegal value)
   // v_f_rd_err_info [mi].first contains request fields: 'arlen', 'arid', 'aruser'

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)

      rule rl_rd_resp_err_to_master (v_f_rd_sjs [mi].first == fromInteger (num_slaves));
	 match { .arlen, .arid, .aruser } = v_f_rd_err_info [mi].first;

	 Bit #(wd_data) data = 0;
	 let r = AXI4_Rd_Data {rid:    arid,
			       rdata:  data,
			       rresp:  axi4_resp_decerr,
			       rlast:  (v_rg_r_err_beat_count [mi] == arlen),
			       ruser:  aruser};

	 xactors_from_masters [mi].i_rd_data.enq (r);

	 if (v_rg_r_err_beat_count [mi] == arlen) begin
	    // Last beat of burst
	    v_f_rd_sjs [mi].deq;
	    v_f_rd_err_info [mi].deq;
	    v_rg_r_err_beat_count [mi] <= 0;
	 end
	 else
	    v_rg_r_err_beat_count [mi] <= v_rg_r_err_beat_count [mi] + 1;

	 if (cfg_verbosity > 0) begin
	    $display ("%0d: %m.rl_rd_resp_err_to_master: m%0d <- err",
		      cur_cycle, mi);
	    $display ("    r: ", fshow (r));
	 end
      endrule

   // ================================================================
   // INTERFACE

   function AXI4_Slave_IFC  #(wd_id, wd_addr, wd_data, wd_user) f1 (Integer j)
      = xactors_from_masters [j].axi_side;
   function AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user) f2 (Integer j)
      = xactors_to_slaves    [j].axi_side;

   method Action reset () if (! rg_reset);
      rg_reset <= True;
   endmethod

   method Action set_verbosity (Bit #(4) verbosity);
      cfg_verbosity <= verbosity;
   endmethod

   interface v_from_masters = genWith (f1);
   interface v_to_slaves    = genWith (f2);
endmodule

// ================================================================

endpackage: AXI4_Fabric

// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved

package AXI4_Fabric;

// ================================================================
// This package defines a fabric connecting CPUs, Memories and DMAs
// and other IP blocks.

// ================================================================
// Bluespec library imports

import Vector       :: *;
import FIFOF        :: *;
import SpecialFIFOs :: *;
import ConfigReg    :: *;

// ----------------
// BSV additional libs

import Cur_Cycle  :: *;

// ================================================================
// Project imports

import Semi_FIFOF :: *;
import AXI4_Types :: *;

// ================================================================
// The interface for the fabric module

interface AXI4_Fabric_IFC #(numeric type tn_num_masters,
			    numeric type tn_num_slaves,
			    numeric type wd_id,
			    numeric type wd_addr,
			    numeric type wd_data,
			    numeric type wd_user);
   method Action reset;
   method Action set_verbosity (Bit #(4) verbosity);

   // From masters
   interface Vector #(tn_num_masters, AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user))  v_from_masters;

   // To slaves
   interface Vector #(tn_num_slaves,  AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_to_slaves;
endinterface

// ================================================================
// The Fabric module
// The function parameter is an address-decode function, which
// returns (True,  slave-port-num)  if address is mapped to slave-port-num
//         (False, ?)               if address is unmapped to any slave port

module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(tn_num_slaves)))
			        fn_addr_to_slave_num (Bit #(wd_addr) addr))
		     (AXI4_Fabric_IFC #(tn_num_masters, tn_num_slaves, wd_id, wd_addr, wd_data, wd_user))

   provisos (Log #(tn_num_masters, log_nm),
	     Log #(tn_num_slaves,  log_ns),
	     Log #(TAdd #(tn_num_slaves,  1), log_ns_plus_1),
	     Add #(_dummy, TLog #(tn_num_slaves), log_ns_plus_1));

   Integer num_masters = valueOf (tn_num_masters);
   Integer num_slaves  = valueOf (tn_num_slaves);

   // 0: quiet; 1: show transactions
   Reg #(Bit #(4)) cfg_verbosity  <- mkConfigReg (0);

   Reg #(Bool) rg_reset <- mkReg (True);

   // Transactors facing masters
   Vector #(tn_num_masters, AXI4_Slave_Xactor_IFC  #(wd_id, wd_addr, wd_data, wd_user))
      xactors_from_masters <- replicateM (mkAXI4_Slave_Xactor);

   // Transactors facing slaves
   Vector #(tn_num_slaves,  AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user))
       xactors_to_slaves <- replicateM (mkAXI4_Master_Xactor);

   // ----------------------------------------------------------------
   // Book-keeping to keep track of which master originated a transaction, in
   // order to route corresponding responses back to that master, etc.
   // Legal slaves  are 0..(num_slaves-1)
   // The "illegal" value of 'num_slaves' is used for decode errors (no such slave)
   // Size of SizedFIFOs is estimated: should cover round-trip latency to slave and back.

   // ----------------
   // Write-transaction book-keeping

   // On an mi->sj write-transaction, this fifo records sj for master mi
   Vector #(tn_num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_wr_sjs <- replicateM (mkSizedFIFOF (8));

   // On an mi->sj write-transaction, this fifo records mi for slave sj
   Vector #(tn_num_slaves,  FIFOF #(Bit #(log_nm))) v_f_wr_mis  <- replicateM (mkSizedFIFOF (8));

   // On an mi->sj write-transaction, this fifo records a task (sj, awlen) for W channel
   Vector #(tn_num_masters,
	    FIFOF #(Tuple2 #(Bit #(log_ns_plus_1),
			     AXI4_Len)))     v_f_wd_tasks <- replicateM (mkFIFOF);
   // On an mi->sj write-transaction, this register is the W-channel burst beat_count
   // (0 => ready for next burst)
   Vector #(tn_num_masters, Reg #(AXI4_Len)) v_rg_wd_beat_count <- replicateM (mkReg (0));

   // On a write-transaction to non-exisitent slave, record id and user for error response
   Vector #(tn_num_masters,
	    FIFOF #(Tuple2 #(Bit #(wd_id),
			     Bit #(wd_user))))  v_f_wr_err_info <- replicateM (mkSizedFIFOF (8));

   // ----------------
   // Read-transaction book-keeping

   // On an mi->sj read-transaction, records sj for master mi
   Vector #(tn_num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_rd_sjs <- replicateM (mkSizedFIFOF (8));
   // On an mi->sj read-transaction, records (mi,arlen) for slave sj
   Vector #(tn_num_slaves,
	    FIFOF #(Tuple2 #(Bit #(log_nm),
			     AXI4_Len)))            v_f_rd_mis <- replicateM (mkSizedFIFOF (8));
   // On an mi->sj read-transaction, this register is the R-channel burst beat_count
   // (0 => ready for next burst)
   Vector #(tn_num_slaves, Reg #(AXI4_Len)) v_rg_r_beat_count <- replicateM (mkReg (0));

   // On a read-transaction to non-exisitent slave, record id and user for error response
   Vector #(tn_num_masters,
	    FIFOF #(Tuple3 #(AXI4_Len,
			     Bit #(wd_id),
			     Bit #(wd_user)))) v_f_rd_err_info <- replicateM (mkSizedFIFOF (8));

   // On an mi->non-existent-slave read-transaction,
   // this register is the R-channel burst beat_count
   // (0 => ready for next burst)
   Vector #(tn_num_masters, Reg #(AXI4_Len)) v_rg_r_err_beat_count <- replicateM (mkReg (0));

   // ----------------------------------------------------------------
   // RESET

   rule rl_reset (rg_reset);
      if (cfg_verbosity > 0)
	 $display ("%0d: %m.rl_reset", cur_cycle);
      for (Integer mi = 0; mi < num_masters; mi = mi + 1) begin
	 xactors_from_masters [mi].reset;

	 v_f_wr_sjs [mi].clear;
	 v_f_wd_tasks [mi].clear;
	 v_rg_wd_beat_count [mi] <= 0;

	 v_f_wr_err_info [mi].clear;

	 v_f_rd_sjs [mi].clear;

	 v_f_rd_err_info [mi].clear;
      end

      for (Integer sj = 0; sj < num_slaves; sj = sj + 1) begin
	 xactors_to_slaves [sj].reset;
	 v_f_wr_mis [sj].clear;
	 v_f_rd_mis [sj].clear;
	 v_rg_r_beat_count [sj] <= 0;
      end
      rg_reset <= False;
   endrule

   // ----------------------------------------------------------------
   // BEHAVIOR

   // ----------------------------------------------------------------
   // Predicates to check if master I has transaction for slave J

   function Bool fv_mi_has_wr_for_sj (Integer mi, Integer sj);
      let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr;
      match { .legal, .slave_num } = fn_addr_to_slave_num (addr);
      return (legal
	      && (   (num_slaves == 1)
		  || (slave_num == fromInteger (sj))));
   endfunction

   function Bool fv_mi_has_wr_for_none (Integer mi);
      let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr;
      match { .legal, ._ } = fn_addr_to_slave_num (addr);
      return (! legal);
   endfunction

   function Bool fv_mi_has_rd_for_sj (Integer mi, Integer sj);
      let addr = xactors_from_masters [mi].o_rd_addr.first.araddr;
      match { .legal, .slave_num } = fn_addr_to_slave_num (addr);
      return (legal
	      && (   (num_slaves == 1)
		  || (slave_num == fromInteger (sj))));
   endfunction

   function Bool fv_mi_has_rd_for_none (Integer mi);
      let addr = xactors_from_masters [mi].o_rd_addr.first.araddr;
      match { .legal, ._ } = fn_addr_to_slave_num (addr);
      return (! legal);
   endfunction

   // ================================================================
   // Wr requests (AW, W and B channels)

   // Wr requests to legal slaves (AW channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_wr_xaction_master_to_slave (fv_mi_has_wr_for_sj (mi, sj));
	    // Move the AW transaction
	    AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);
	    xactors_to_slaves [sj].i_wr_addr.enq (a);

	    // Enqueue a task for the W channel
	    v_f_wd_tasks      [mi].enq (tuple2 (fromInteger (sj), a.awlen));

	    // Book-keeping
	    v_f_wr_mis        [sj].enq (fromInteger (mi));
	    v_f_wr_sjs        [mi].enq (fromInteger (sj));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_wr_xaction_master_to_slave: m%0d -> s%0d",
			 cur_cycle, mi, sj);
	       $display ("    ", fshow (a));
	    end
	 endrule

   // Wr requests to non-existent slave (AW channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
	 rule rl_wr_xaction_no_such_slave (fv_mi_has_wr_for_none (mi));
	    AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);

	    // Special value 'num_slaves' (not a legal sj) means "no such slave"
	    v_f_wr_sjs        [mi].enq (fromInteger (num_slaves));
	    v_f_wr_err_info   [mi].enq (tuple2 (a.awid, a.awuser));

	    // Enqueue a task for the W channel (must consume the write-data burst)
	    v_f_wd_tasks      [mi].enq (tuple2 (fromInteger (num_slaves), a.awlen));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_wr_xaction_no_such_slave: m%0d -> ?",
			 cur_cycle, mi);
	       $display ("        ", fshow (a));
	    end
	 endrule

   // Wr data (W channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)

      // Handle W channel burst
      // Invariant: v_rg_wd_beat_count == 0 between bursts
      // Note: awlen is encoded as 0..255 for burst lengths of 1..256
      rule rl_wr_xaction_master_to_slave_data (v_f_wd_tasks [mi].first matches {.sj, .awlen});
	 AXI4_Wr_Data #(wd_data, wd_user) d <- pop_o (xactors_from_masters [mi].o_wr_data);

	 // If sj is a legal slave, send it the data beat, else drop it.
	 if (sj < fromInteger (num_slaves))
	    xactors_to_slaves [sj].i_wr_data.enq (d);

	 if (v_rg_wd_beat_count [mi] == awlen) begin
	    // End of burst
	    v_f_wd_tasks [mi].deq;
	    v_rg_wd_beat_count [mi] <= 0;

	    // Simulation-only assertion-check (no action, just display assertion failure)
	    // Final beat must have WLAST = 1
	    // Rely on slave (which should also see this error) to return error response
	    if (! (d.wlast)) begin
	       $display ("%0d: %m.rl_wr_xaction_master_to_slave_data: ERROR: m%0d -> s%0d",
			 cur_cycle, mi, sj);
	       $display ("    WLAST not set on final data beat (awlen = %0d)", awlen);
	       $display ("    ", fshow (d));
	    end
	 end
	 else
	    v_rg_wd_beat_count [mi] <= v_rg_wd_beat_count [mi] + 1;
      endrule

   // Wr responses from slaves to masters (B channel)

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_wr_resp_slave_to_master (   (v_f_wr_mis [sj].first == fromInteger (mi))
					  && (v_f_wr_sjs [mi].first == fromInteger (sj)));
	    v_f_wr_mis [sj].deq;
	    v_f_wr_sjs [mi].deq;
	    AXI4_Wr_Resp #(wd_id, wd_user) b <- pop_o (xactors_to_slaves [sj].o_wr_resp);

	    xactors_from_masters [mi].i_wr_resp.enq (b);

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_wr_resp_slave_to_master: m%0d <- s%0d",
			 cur_cycle, mi, sj);
	       $display ("        ", fshow (b));
	    end
	 endrule

   // Wr error responses to masters (B channel)
   // v_f_wr_sjs [mi].first has value num_slaves (illegal value)
   // v_f_wr_err_info [mi].first contains request fields 'awid' and 'awuser'

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)

      rule rl_wr_resp_err_to_master (v_f_wr_sjs [mi].first == fromInteger (num_slaves));
	 v_f_wr_sjs [mi].deq;
	 v_f_wr_err_info [mi].deq;

	 match { .awid, .awuser } = v_f_wr_err_info [mi].first;

	 let b = AXI4_Wr_Resp {bid:   awid,
			       bresp: axi4_resp_decerr,
			       buser: awuser};

	 xactors_from_masters [mi].i_wr_resp.enq (b);

	 if (cfg_verbosity > 0) begin
	    $display ("%0d: %m.rl_wr_resp_err_to_master: m%0d <- err", cur_cycle, mi);
	    $display ("        ", fshow (b));
	 end
      endrule

   // ================================================================
   // Rd requests (AR and R channels)

   // Rd requests to legal slaves (AR channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_rd_xaction_master_to_slave (fv_mi_has_rd_for_sj (mi, sj));
	    AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);

	    xactors_to_slaves [sj].i_rd_addr.enq (a);

	    v_f_rd_mis [sj].enq (tuple2 (fromInteger (mi), a.arlen));
	    v_f_rd_sjs [mi].enq (fromInteger (sj));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_rd_xaction_master_to_slave: m%0d -> s%0d",
			 cur_cycle, mi, sj);
	       $display ("        ", fshow (a));
	    end
	 endrule

   // Rd requests to non-existent slave (AR channel)
   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
	 rule rl_rd_xaction_no_such_slave (fv_mi_has_rd_for_none (mi));
	    AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);

	    v_f_rd_sjs      [mi].enq (fromInteger (num_slaves));
	    v_f_rd_err_info [mi].enq (tuple3 (a.arlen, a.arid, a.aruser));

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_rd_xaction_no_such_slave: m%0d -> ?",
			 cur_cycle, mi);
	       $display ("        ", fshow (a));
	    end
	 endrule

   // Rd responses from slaves to masters (R channel)

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)
      for (Integer sj = 0; sj < num_slaves; sj = sj + 1)

	 rule rl_rd_resp_slave_to_master (v_f_rd_mis [sj].first matches { .mi2, .arlen }
					  &&& (mi2 == fromInteger (mi))
					  &&& (v_f_rd_sjs [mi].first == fromInteger (sj)));

	    AXI4_Rd_Data #(wd_id, wd_data, wd_user) r <- pop_o (xactors_to_slaves [sj].o_rd_data);

	    if (v_rg_r_beat_count [sj] == arlen) begin
	       // Final beat of burst
	       v_f_rd_mis [sj].deq;
	       v_f_rd_sjs [mi].deq;
	       v_rg_r_beat_count [sj] <= 0;

	       // Assertion-check
	       // Final beat must have RLAST = 1
	       // If not, and if RRESP is OK, set RRESP to AXI4_RESP_SLVERR
	       if ((r.rresp == axi4_resp_okay) && (! (r.rlast))) begin
		  r.rresp = axi4_resp_slverr;
		  $display ("%0d: %m.rl_rd_resp_slave_to_master: ERROR: m%0d <- s%0d",
			    cur_cycle, mi, sj);
		  $display ("    RLAST not set on final data beat (arlen = %0d)", arlen);
		  $display ("    ", fshow (r));
	       end
	    end
	    else
	       v_rg_r_beat_count [sj] <= v_rg_r_beat_count [sj] + 1;

	    xactors_from_masters [mi].i_rd_data.enq (r);

	    if (cfg_verbosity > 0) begin
	       $display ("%0d: %m.rl_rd_resp_slave_to_master: m%0d <- s%0d",
			 cur_cycle, mi, sj);
	       $display ("    r: ", fshow (r));
	    end
	 endrule

   // Rd error responses to masters (R channel)
   // v_f_rd_sjs [mi].first has value num_slaves (illegal value)
   // v_f_rd_err_info [mi].first contains request fields: 'arlen', 'arid', 'aruser'

   for (Integer mi = 0; mi < num_masters; mi = mi + 1)

      rule rl_rd_resp_err_to_master (v_f_rd_sjs [mi].first == fromInteger (num_slaves));
	 match { .arlen, .arid, .aruser } = v_f_rd_err_info [mi].first;

	 Bit #(wd_data) data = 0;
	 let r = AXI4_Rd_Data {rid:    arid,
			       rdata:  data,
			       rresp:  axi4_resp_decerr,
			       rlast:  (v_rg_r_err_beat_count [mi] == arlen),
			       ruser:  aruser};

	 xactors_from_masters [mi].i_rd_data.enq (r);

	 if (v_rg_r_err_beat_count [mi] == arlen) begin
	    // Last beat of burst
	    v_f_rd_sjs [mi].deq;
	    v_f_rd_err_info [mi].deq;
	    v_rg_r_err_beat_count [mi] <= 0;
	 end
	 else
	    v_rg_r_err_beat_count [mi] <= v_rg_r_err_beat_count [mi] + 1;

	 if (cfg_verbosity > 0) begin
	    $display ("%0d: %m.rl_rd_resp_err_to_master: m%0d <- err",
		      cur_cycle, mi);
	    $display ("    r: ", fshow (r));
	 end
      endrule

   // ================================================================
   // INTERFACE

   function AXI4_Slave_IFC  #(wd_id, wd_addr, wd_data, wd_user) f1 (Integer j)
      = xactors_from_masters [j].axi_side;
   function AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user) f2 (Integer j)
      = xactors_to_slaves    [j].axi_side;

   method Action reset () if (! rg_reset);
      rg_reset <= True;
   endmethod

   method Action set_verbosity (Bit #(4) verbosity);
      cfg_verbosity <= verbosity;
   endmethod

   interface v_from_masters = genWith (f1);
   interface v_to_slaves    = genWith (f2);
endmodule

// ================================================================

endpackage: AXI4_Fabric


max payload size is only 256 for xilinx gen2

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

import ConnectalMimo :: *;
import MIFO         :: *;
import MemTypes     :: *;
import ConfigCounter ::*;
import ConnectalConfig::*;

`include "ConnectalProjectConfig.bsv"

typedef struct {
   TLPData#(16) tlp;
   TLPLength    dwCount;
   Bool         is3dw;
   Bool         isHeaderOnly;
   } TlpWriteHeaderInfo deriving (Bits);

interface MemToPcie#(numeric type buswidth);
    interface Client#(TLPData#(16), TLPData#(16)) tlp;
    interface PhysMemSlave#(40,buswidth) slave;
    method Bool tlpOutFifoNotEmpty();
    interface Reg#(Bool) use4dw;
endinterface: MemToPcie

`ifdef XILINX
   `define AXI
`elsif SIMULATION
   `define AXI
`elsif ALTERA
   `define AVALON
`endif

`ifdef PCIE3
typedef 1024 WriteDataBurstLen; // max payload size is 1024 bytes
`else
typedef 256 WriteDataBurstLen; // max payload size is 256 bytes for Xilinx gen2 core
`endif
typedef TDiv#(WriteDataBurstLen,4) WriteDataMimoSize; // number of words to hold in the MIMO
typedef BurstLenSize WriteDataBurstLenSize;
module mkMemToPcie#(PciId my_id)(MemToPcie#(buswidth))
   provisos (Div#(buswidth, 8, busWidthBytes),
	     Div#(buswidth, 32, busWidthWords),
	     Bits#(Vector#(busWidthWords, Bit#(32)), buswidth),
            Log#(busWidthBytes,beatShift),
	     Add#(aaa, 32, buswidth),
	     Add#(bbb, buswidth, 256),
	     Add#(ccc, TMul#(8, busWidthWords), 64),
	     Add#(ddd, TMul#(32, busWidthWords), 256),
	     Add#(eee, busWidthWords, 8),
	     Add#(1, a__, busWidthWords),
	     Add#(busWidthWords, b__, 4),
	     Add#(c__, busWidthWords, WriteDataMimoSize)
      );

    let beat_shift = fromInteger(valueOf(beatShift));

    FIFOF#(TLPData#(16)) tlpOutFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpInFifo <- mkFIFOF;
    FIFOF#(TlpWriteHeaderInfo) tlpWriteHeaderFifo <- mkFIFOF;

    Reg#(Bit#(7)) hitReg <- mkReg(0);
    Reg#(Bool) use4dwReg <- mkReg(True);

    // default configuration for MIMO is for guarded enq() and deq().
    // However, the implicit guard only checks for space for 1 element for enq(), and availability of 1 element for deq().
    MIMOConfiguration mimoCfg = defaultValue;
   MIFO#(4,busWidthWords,16,Bit#(32)) completionMimo <- mkMIFO();
   MIFO#(4,busWidthWords,16,Tuple2#(TLPTag,Bool)) completionTagMimo <- mkMIFO(); // tag, last beat of burst

   mimoCfg.bram_based = True;
   mimoCfg.unguarded = True;
    MIMO#(busWidthWords,4,WriteDataMimoSize,Bit#(32)) writeDataMimo <- ConnectalMimo::mkMIMOBram(mimoCfg);
    ConfigCounter#(8) writeDataCnt <- mkConfigCounter(0);
    Reg#(Bit#(WriteDataBurstLenSize)) writeBurstCount <- mkReg(0);
    FIFO#(Bit#(WriteDataBurstLenSize)) writeBurstCountFifo <- mkFIFO();
    Reg#(TLPLength)  writeDwCount <- mkReg(0); // how many 4 byte (double) words to send
    Reg#(LUInt#(4))    tlpDwCount <- mkReg(0); // how many to send in the next tlp (at most 4)
    Reg#(Bool)            lastTlp <- mkReg(False); // if the next tlp sent is the last one
    Reg#(Bool)    writeInProgress <- mkReg(False);
    FIFOF#(TLPTag) writeTag <- mkSizedFIFOF(16);
    FIFOF#(TLPTag) doneTag <- mkSizedFIFOF(16);

    Reg#(Bool) quadAlignedTlpHandled <- mkReg(True);

   Wire#(Bool) writeHeaderTlpWire <- mkDWire(False);
   Wire#(Bool) writeDataMimoEnqWire <- mkDWire(False);

   Reg#(Bool) writeDataCntAboveThreshold <- mkReg(False);
   Reg#(Bool) writeDataMimoHasRoom <- mkReg(False);
   rule updateWriteDataMimoHasRoom;
      writeDataMimoHasRoom <= (writeDataCnt.read <= fromInteger(valueOf(WriteDataMimoSize) - valueOf(busWidthWords)));
   endrule
   rule noheader if (!tlpWriteHeaderFifo.notEmpty());
      writeDataCntAboveThreshold <= False;
   endrule
   rule updateWriteDataCntAboveThreshold if (writeInProgress || !writeDataCntAboveThreshold);
      writeDataCntAboveThreshold <= (writeDataCnt.read >= truncate(unpack(tlpWriteHeaderFifo.first.dwCount)));
   endrule
   (* descending_urgency = "writeHeaderTlp,updateWriteDataCntAboveThreshold" *)
   rule writeHeaderTlp if (!writeInProgress && writeDataCntAboveThreshold);
      // update for next cycle
      writeDataCntAboveThreshold <= (writeDataCnt.read >= truncate(unpack(tlpWriteHeaderFifo.first.dwCount)));

      writeHeaderTlpWire <= True;
      let info <- toGet(tlpWriteHeaderFifo).get();
      let tlp     = info.tlp;
      let dwCount = info.dwCount;
      let is3dw   = info.is3dw;
      let isHeaderOnly = info.isHeaderOnly;

      TLPMemory4DWHeader hdr_4dw = unpack(tlp.data);

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      if (is3dw) begin
	 if (hdr_3dw.format != MEM_WRITE_3DW_DATA)
	    $display("MemToPcie: expecting MEM_WRITE_3DW_DATA, got %d", hdr_3dw.format);
	 Vector#(4, Bit#(32)) v = unpack(0);
         tlp.sof = True;
`ifdef AXI
         v = writeDataMimo.first();
	 writeDataMimo.deq(1);
	 hdr_3dw.data = byteSwap(v[0]);
         tlp.eof = (dwCount == 1) ? True : False;
         dwCount = dwCount - 1;
         writeDataCnt.decrement(1);
`elsif AVALON
         let quadWordAligned = isQuadWordAligned(getLowerAddr(hdr_3dw.addr, hdr_3dw.firstbe));
         // if quad-word aligned, insert bubble.
         if (!quadWordAligned) begin
            v = writeDataMimo.first();
            writeDataMimo.deq(1);
            hdr_3dw.data = v[0];
         end
         else begin
            hdr_3dw.data = unpack(0);
         end
         tlp.eof = (dwCount == 1 && !quadWordAligned) ? True : False;
         if (!quadWordAligned) begin
            dwCount = dwCount - 1;
            writeDataCnt.decrement(1);
         end
`endif
	 tlp.be = 16'hffff;
	 tlp.data = pack(hdr_3dw);
      end
      else begin
         quadAlignedTlpHandled <= isQuadWordAligned(getLowerAddr(truncate(unpack(hdr_4dw.addr)), hdr_4dw.firstbe));
	 tlp.be = 16'hffff;
      end

      tlpOutFifo.enq(tlp);
      $display("writeHeaderTlp dwCount=%d", dwCount);
      writeDwCount <= dwCount;
      tlpDwCount <= truncate(min(4,unpack(dwCount)));
      lastTlp <= (dwCount <= 4);
      writeInProgress <= (dwCount != 0);
      if (isHeaderOnly) begin
	 doneTag.enq(writeTag.first());
	 writeTag.deq();
      end

   endrule

   rule writeTlps if (writeInProgress); // already verified  writeDataMimo.deqReadyN(tlpDwCount)) for this transaction
      TLPData#(16) tlp = defaultValue;
      tlp.sof = False;
      Vector#(4, Bit#(32)) v = unpack(0);
      Integer currDwCount = 4;

`ifdef AVALON
      if (!quadAlignedTlpHandled) begin
         currDwCount = 3; // 3 Data Dwords in this cycle.
         quadAlignedTlpHandled <= True;
      end
`endif

      // The MIMO implicit guard only checks for availability of 1 element
      // so we explicitly check for the number of elements required
      writeDataMimo.deq(tlpDwCount);
      v = writeDataMimo.first();
      let dwCount = writeDwCount - extend(pack(tlpDwCount));
      writeDwCount <= dwCount;
      tlpDwCount <= truncate(min(fromInteger(currDwCount),unpack(dwCount)));
      writeDataCnt.decrement(unpack(extend(pack(tlpDwCount))));
      lastTlp <= (dwCount <= 4);
      if (tlpDwCount == 4)
	 tlp.be = 16'hffff;
      else if (tlpDwCount == 3)
	 tlp.be = 16'hfff0;
      else if (tlpDwCount == 2)
	 tlp.be = 16'hff00;
      else if (tlpDwCount == 1)
	 tlp.be = 16'hf000;
      tlp.eof = lastTlp;
      if (lastTlp) begin
	 writeInProgress <= False;
	 doneTag.enq(writeTag.first());
	 writeTag.deq();
	 $display("writeDwCount=%d will be zero", writeDwCount);
      end

      for (Integer i = 0; i < currDwCount; i = i + 1) begin
`ifdef AXI
`ifdef PCIE3
	 tlp.data[(i+1)*32-1:i*32] = v[i];
`else
	 tlp.data[(i+1)*32-1:i*32] = byteSwap(v[(currDwCount-1)-i]);
`endif
`elsif AVALON
	 tlp.data[(i+1)*32-1:i*32] = v[(currDwCount-1)-i];
`endif
      end

      tlpOutFifo.enq(tlp);
   endrule: writeTlps

   Reg#(TLPTag) lastTag <- mkReg(0);
   FIFOF#(TLPData#(16)) tlpDecodeFifo <- mkFIFOF();
   Reg#(TLPLength) wordCountReg <- mkReg(0);
   rule tlpInRule;
      let tlp <- toGet(tlpInFifo).get();
      tlpDecodeFifo.enq(tlp);
   endrule

   rule handleTlpRule;
      let tlp = tlpDecodeFifo.first;
      Bool handled = False;
      TLPMemoryIO3DWHeader h = unpack(tlp.data);
      hitReg <= tlp.hit;
      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      TLPCompletionHeader hdr_completion = unpack(tlp.data);
      Vector#(4, Bit#(32)) vec = unpack(0);
      Vector#(4, Bit#(32)) tlpvec = unpack(tlp.data);
      let wordCount = wordCountReg;
`ifdef AXI
      let dataInSecondTlp = False;
`elsif AVALON
      let quadWordAligned = isQuadWordAligned(getLowerAddr(hdr_3dw.addr, hdr_3dw.firstbe));
      let dataInSecondTlp = quadWordAligned;
`endif
      if (!tlp.sof) begin
`ifdef PCIE3
	 vec = tlpvec;
`else
	 vec = reverse(tlpvec);
`endif
	 // The MIMO implicit guard only checks for space to enqueue 1 element
	 // so we explicitly check for the number of elements required
	 // otherwise elements in the queue will be overwritten.
	 if (completionMimo.enqReady()
	    && completionTagMimo.enqReady())
	    begin
	       UInt#(3) count = 4;
	       if (tlp.eof) begin
		  count = truncate(unpack(wordCountReg));
	       end
	       wordCount = wordCountReg - extend(pack(count));
	       completionMimo.enq(count, vec);
	       function Tuple2#(TLPTag,Bool) taglast(Integer i); return tuple2(lastTag, (fromInteger(i) == (count-1)) ? tlp.eof : False); endfunction
	       Vector#(4, Tuple2#(TLPTag,Bool)) tagvec = genWith(taglast);
	       completionTagMimo.enq(count, tagvec);
	       handled = True;
	    end
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA
	       && hdr_3dw.pkttype == COMPLETION
	       && completionMimo.enqReady()
	       && completionTagMimo.enqReady()) begin
            TLPTag tag = hdr_completion.tag;
            lastTag <= tag;
            if (!dataInSecondTlp) begin
               vec[0] = hdr_3dw.data;
               wordCount = hdr_3dw.length - 1;
               completionMimo.enq(1, vec);
               completionTagMimo.enq(1, replicate(tuple2(tag,tlp.eof)));
            end
	    handled = True;
      end
      wordCountReg <= wordCount;
      $display("tlpIn handled=%d tlp=%h\n", handled, tlp);
      if (handled) begin
	 tlpDecodeFifo.deq();
      end
   endrule

   FIFO#(PhysMemRequest#(40,buswidth)) readReqFifo <- mkFIFO();
   rule readReqRule if (!writeInProgress); // && !writeDataMimo.deqReady());
      let req <- toGet(readReqFifo).get();
      let burstLen = req.burstLen >> beat_shift;
      let addr = req.addr;
      let arid = req.tag;

      TLPData#(16) tlp = defaultValue;
      tlp.sof = True;
      tlp.eof = True;
      tlp.hit = 7'h00;
      TLPLength tlplen = fromInteger(valueOf(busWidthWords))*truncate(burstLen);
      if (addr[39:32] != 0) begin
	 TLPMemory4DWHeader hdr_4dw = defaultValue;
	 hdr_4dw.format = MEM_READ_4DW_NO_DATA;
	 hdr_4dw.tag = extend(arid);
	 hdr_4dw.reqid = my_id;
	 hdr_4dw.nosnoop = SNOOPING_REQD;
	 hdr_4dw.addr = addr[40-1:2];
	 hdr_4dw.length = tlplen;
	 hdr_4dw.firstbe = reqFirstByteEnable(req)[3:0];
	 hdr_4dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	 tlp.data = pack(hdr_4dw);
	 tlp.be = 16'hffff;
      end
      else begin
	 TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
	 hdr_3dw.format = MEM_READ_3DW_NO_DATA;
	 hdr_3dw.tag = extend(arid);
	 hdr_3dw.reqid = my_id;
	 hdr_3dw.nosnoop = SNOOPING_REQD;
	 hdr_3dw.addr = addr[32-1:2];
	 hdr_3dw.length = tlplen;
	 hdr_3dw.firstbe = reqFirstByteEnable(req)[3:0];
	 hdr_3dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	 tlp.data = pack(hdr_3dw);
	 tlp.be = 16'hfff0;
      end
      tlpOutFifo.enq(tlp);
   endrule

    interface Client        tlp;
        interface request = toGet(tlpOutFifo);
        interface response = toPut(tlpInFifo);
    endinterface
    interface PhysMemSlave slave;
   interface PhysMemWriteServer write_server;
      interface Put writeReq;
         method Action put(PhysMemRequest#(40,buswidth) req); // if (writeBurstCount == 0);
	    Bit#(WriteDataBurstLenSize) burstLen = truncate(req.burstLen >> beat_shift);
	    let addr = req.addr;
	    let awid = req.tag;
	    let writeIs3dw = False;
	    let use3dw = True;
`ifdef PCIE3
	    awid = awid | (1 << (valueOf(MemTagSize)-1));
	    use3dw = False;
`endif
	    TLPLength tlplen = fromInteger(valueOf(busWidthWords))*truncate(burstLen);
	    TLPData#(16) tlp = defaultValue;
	    tlp.sof = True;
	    tlp.eof = False;
	    tlp.hit = 7'h00;
	    tlp.be = 16'hffff;

	    $display("slave.writeAddr tlplen=%d burstLen=%d", tlplen, burstLen);
	    if ((addr >> 32) != 0 || !use3dw) begin
	       TLPMemory4DWHeader hdr_4dw = defaultValue;
	       hdr_4dw.format = MEM_WRITE_4DW_DATA;
	       hdr_4dw.tag = extend(awid);
	       hdr_4dw.reqid = my_id;
	       hdr_4dw.nosnoop = SNOOPING_REQD;
	       hdr_4dw.addr = addr[40-1:2];
	       hdr_4dw.length = tlplen;
	       hdr_4dw.firstbe = reqFirstByteEnable(req)[3:0];
	       hdr_4dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	       tlp.data = pack(hdr_4dw);
	    end
	    else begin
	       writeIs3dw = True;
	       TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
	       hdr_3dw.format = MEM_WRITE_3DW_DATA;
	       hdr_3dw.tag = extend(awid);
	       hdr_3dw.reqid = my_id;
	       hdr_3dw.nosnoop = SNOOPING_REQD;
	       hdr_3dw.addr = addr[32-1:2];
	       hdr_3dw.length = tlplen;
	       hdr_3dw.firstbe = reqFirstByteEnable(req)[3:0];
	       hdr_3dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	       tlp.be = 16'hfff0; // no data word in this TLP

	       tlp.data = pack(hdr_3dw);
	    end
	    tlpWriteHeaderFifo.enq(TlpWriteHeaderInfo {tlp: tlp, dwCount: tlplen, is3dw: writeIs3dw, isHeaderOnly: (writeIs3dw && tlplen == 1) });
	    writeBurstCountFifo.enq(burstLen);
	    writeTag.enq(extend(awid));
         endmethod
	endinterface
      interface Put writeData;
         method Action put(MemData#(buswidth) wdata)
	    provisos (Bits#(Vector#(busWidthWords, Bit#(32)), busWidth)) if (writeDataMimoHasRoom); //.enqReadyN(fromInteger(valueOf(busWidthWords))));

	    let burstLen = writeBurstCount;
	    if (burstLen == 0) begin
	       burstLen <- toGet(writeBurstCountFifo).get();
	    end
	    writeBurstCount <= extend(burstLen-1);

	    Vector#(busWidthWords, Bit#(32)) v = unpack(wdata.data);
	    writeDataMimo.enq(fromInteger(valueOf(busWidthWords)), v);
            writeDataCnt.increment(fromInteger(valueOf(busWidthWords)));
	    writeDataMimoEnqWire <= True;
         endmethod
       endinterface
      interface Get writeDone;
         method ActionValue#(Bit#(MemTagSize)) get();
	      let tag = doneTag.first();
	      doneTag.deq();
	      return truncate(tag);
           endmethod
	endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
         method Action put(PhysMemRequest#(40,buswidth) req);
	    readReqFifo.enq(req);
         endmethod
       endinterface
      interface Get     readData;
         method ActionValue#(MemData#(buswidth)) get() if (completionMimo.deqReady()
							   && completionTagMimo.deqReady());
	      let data_v = completionMimo.first;
	      let tag_last_v = completionTagMimo.first;
	      match { .tag, .last } = tag_last_v[fromInteger(valueOf(busWidthWords))-1];
	      completionMimo.deq();
	      completionTagMimo.deq();
              Bit#(buswidth) v = 0;
              for (Integer i = 0; i < valueOf(busWidthWords); i = i+1) begin
`ifdef AXI
`ifdef PCIE3
		 v[(i+1)*32-1:i*32] = data_v[i];
`else
		 v[(i+1)*32-1:i*32] = byteSwap(data_v[i]);
`endif
`elsif AVALON
		 v[(i+1)*32-1:i*32] = data_v[i];
`endif
              end
	      return MemData { data: v, tag: truncate(tag), last: last}; // last beat of this response burst
           endmethod
	endinterface
   endinterface
    endinterface: slave
   method Bool tlpOutFifoNotEmpty() = tlpOutFifo.notEmpty;
   interface Reg use4dw = use4dwReg;
endmodule: mkMemToPcie

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

import ConnectalMimo :: *;
import MIFO         :: *;
import MemTypes     :: *;
import ConfigCounter ::*;
import ConnectalConfig::*;

`include "ConnectalProjectConfig.bsv"

typedef struct {
   TLPData#(16) tlp;
   TLPLength    dwCount;
   Bool         is3dw;
   Bool         isHeaderOnly;
   } TlpWriteHeaderInfo deriving (Bits);

interface MemToPcie#(numeric type buswidth);
    interface Client#(TLPData#(16), TLPData#(16)) tlp;
    interface PhysMemSlave#(40,buswidth) slave;
    method Bool tlpOutFifoNotEmpty();
    interface Reg#(Bool) use4dw;
endinterface: MemToPcie

`ifdef XILINX
   `define AXI
`elsif SIMULATION
   `define AXI
`elsif ALTERA
   `define AVALON
`endif

typedef 1024 WriteDataBurstLen; // max payload size is 1024 bytes
typedef TDiv#(WriteDataBurstLen,4) WriteDataMimoSize; // number of words to hold in the MIMO
typedef BurstLenSize WriteDataBurstLenSize;
module mkMemToPcie#(PciId my_id)(MemToPcie#(buswidth))
   provisos (Div#(buswidth, 8, busWidthBytes),
	     Div#(buswidth, 32, busWidthWords),
	     Bits#(Vector#(busWidthWords, Bit#(32)), buswidth),
            Log#(busWidthBytes,beatShift),
	     Add#(aaa, 32, buswidth),
	     Add#(bbb, buswidth, 256),
	     Add#(ccc, TMul#(8, busWidthWords), 64),
	     Add#(ddd, TMul#(32, busWidthWords), 256),
	     Add#(eee, busWidthWords, 8),
	     Add#(1, a__, busWidthWords),
	     Add#(busWidthWords, b__, 4),
	     Add#(c__, busWidthWords, WriteDataMimoSize)
      );

    let beat_shift = fromInteger(valueOf(beatShift));

    FIFOF#(TLPData#(16)) tlpOutFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpInFifo <- mkFIFOF;
    FIFOF#(TlpWriteHeaderInfo) tlpWriteHeaderFifo <- mkFIFOF;

    Reg#(Bit#(7)) hitReg <- mkReg(0);
    Reg#(Bool) use4dwReg <- mkReg(True);

    // default configuration for MIMO is for guarded enq() and deq().
    // However, the implicit guard only checks for space for 1 element for enq(), and availability of 1 element for deq().
    MIMOConfiguration mimoCfg = defaultValue;
   MIFO#(4,busWidthWords,16,Bit#(32)) completionMimo <- mkMIFO();
   MIFO#(4,busWidthWords,16,Tuple2#(TLPTag,Bool)) completionTagMimo <- mkMIFO(); // tag, last beat of burst

   mimoCfg.bram_based = True;
   mimoCfg.unguarded = True;
    MIMO#(busWidthWords,4,WriteDataMimoSize,Bit#(32)) writeDataMimo <- ConnectalMimo::mkMIMOBram(mimoCfg);
    ConfigCounter#(8) writeDataCnt <- mkConfigCounter(0);
    Reg#(Bit#(WriteDataBurstLenSize)) writeBurstCount <- mkReg(0);
    FIFO#(Bit#(WriteDataBurstLenSize)) writeBurstCountFifo <- mkFIFO();
    Reg#(TLPLength)  writeDwCount <- mkReg(0); // how many 4 byte (double) words to send
    Reg#(LUInt#(4))    tlpDwCount <- mkReg(0); // how many to send in the next tlp (at most 4)
    Reg#(Bool)            lastTlp <- mkReg(False); // if the next tlp sent is the last one
    Reg#(Bool)    writeInProgress <- mkReg(False);
    FIFOF#(TLPTag) writeTag <- mkSizedFIFOF(16);
    FIFOF#(TLPTag) doneTag <- mkSizedFIFOF(16);

    Reg#(Bool) quadAlignedTlpHandled <- mkReg(True);

   Wire#(Bool) writeHeaderTlpWire <- mkDWire(False);
   Wire#(Bool) writeDataMimoEnqWire <- mkDWire(False);

   Reg#(Bool) writeDataCntAboveThreshold <- mkReg(False);
   Reg#(Bool) writeDataMimoHasRoom <- mkReg(False);
   rule updateWriteDataMimoHasRoom;
      writeDataMimoHasRoom <= (writeDataCnt.read <= fromInteger(valueOf(WriteDataMimoSize) - valueOf(busWidthWords)));
   endrule
   rule noheader if (!tlpWriteHeaderFifo.notEmpty());
      writeDataCntAboveThreshold <= False;
   endrule
   rule updateWriteDataCntAboveThreshold if (writeInProgress || !writeDataCntAboveThreshold);
      writeDataCntAboveThreshold <= (writeDataCnt.read >= truncate(unpack(tlpWriteHeaderFifo.first.dwCount)));
   endrule
   (* descending_urgency = "writeHeaderTlp,updateWriteDataCntAboveThreshold" *)
   rule writeHeaderTlp if (!writeInProgress && writeDataCntAboveThreshold);
      // update for next cycle
      writeDataCntAboveThreshold <= (writeDataCnt.read >= truncate(unpack(tlpWriteHeaderFifo.first.dwCount)));

      writeHeaderTlpWire <= True;
      let info <- toGet(tlpWriteHeaderFifo).get();
      let tlp     = info.tlp;
      let dwCount = info.dwCount;
      let is3dw   = info.is3dw;
      let isHeaderOnly = info.isHeaderOnly;

      TLPMemory4DWHeader hdr_4dw = unpack(tlp.data);

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      if (is3dw) begin
	 if (hdr_3dw.format != MEM_WRITE_3DW_DATA)
	    $display("MemToPcie: expecting MEM_WRITE_3DW_DATA, got %d", hdr_3dw.format);
	 Vector#(4, Bit#(32)) v = unpack(0);
         tlp.sof = True;
`ifdef AXI
         v = writeDataMimo.first();
	 writeDataMimo.deq(1);
	 hdr_3dw.data = byteSwap(v[0]);
         tlp.eof = (dwCount == 1) ? True : False;
         dwCount = dwCount - 1;
         writeDataCnt.decrement(1);
`elsif AVALON
         let quadWordAligned = isQuadWordAligned(getLowerAddr(hdr_3dw.addr, hdr_3dw.firstbe));
         // if quad-word aligned, insert bubble.
         if (!quadWordAligned) begin
            v = writeDataMimo.first();
            writeDataMimo.deq(1);
            hdr_3dw.data = v[0];
         end
         else begin
            hdr_3dw.data = unpack(0);
         end
         tlp.eof = (dwCount == 1 && !quadWordAligned) ? True : False;
         if (!quadWordAligned) begin
            dwCount = dwCount - 1;
            writeDataCnt.decrement(1);
         end
`endif
	 tlp.be = 16'hffff;
	 tlp.data = pack(hdr_3dw);
      end
      else begin
         quadAlignedTlpHandled <= isQuadWordAligned(getLowerAddr(truncate(unpack(hdr_4dw.addr)), hdr_4dw.firstbe));
	 tlp.be = 16'hffff;
      end

      tlpOutFifo.enq(tlp);
      $display("writeHeaderTlp dwCount=%d", dwCount);
      writeDwCount <= dwCount;
      tlpDwCount <= truncate(min(4,unpack(dwCount)));
      lastTlp <= (dwCount <= 4);
      writeInProgress <= (dwCount != 0);
      if (isHeaderOnly) begin
	 doneTag.enq(writeTag.first());
	 writeTag.deq();
      end

   endrule

   rule writeTlps if (writeInProgress); // already verified  writeDataMimo.deqReadyN(tlpDwCount)) for this transaction
      TLPData#(16) tlp = defaultValue;
      tlp.sof = False;
      Vector#(4, Bit#(32)) v = unpack(0);
      Integer currDwCount = 4;

`ifdef AVALON
      if (!quadAlignedTlpHandled) begin
         currDwCount = 3; // 3 Data Dwords in this cycle.
         quadAlignedTlpHandled <= True;
      end
`endif

      // The MIMO implicit guard only checks for availability of 1 element
      // so we explicitly check for the number of elements required
      writeDataMimo.deq(tlpDwCount);
      v = writeDataMimo.first();
      let dwCount = writeDwCount - extend(pack(tlpDwCount));
      writeDwCount <= dwCount;
      tlpDwCount <= truncate(min(fromInteger(currDwCount),unpack(dwCount)));
      writeDataCnt.decrement(unpack(extend(pack(tlpDwCount))));
      lastTlp <= (dwCount <= 4);
      if (tlpDwCount == 4)
	 tlp.be = 16'hffff;
      else if (tlpDwCount == 3)
	 tlp.be = 16'hfff0;
      else if (tlpDwCount == 2)
	 tlp.be = 16'hff00;
      else if (tlpDwCount == 1)
	 tlp.be = 16'hf000;
      tlp.eof = lastTlp;
      if (lastTlp) begin
	 writeInProgress <= False;
	 doneTag.enq(writeTag.first());
	 writeTag.deq();
	 $display("writeDwCount=%d will be zero", writeDwCount);
      end

      for (Integer i = 0; i < currDwCount; i = i + 1) begin
`ifdef AXI
`ifdef PCIE3
	 tlp.data[(i+1)*32-1:i*32] = v[i];
`else
	 tlp.data[(i+1)*32-1:i*32] = byteSwap(v[(currDwCount-1)-i]);
`endif
`elsif AVALON
	 tlp.data[(i+1)*32-1:i*32] = v[(currDwCount-1)-i];
`endif
      end

      tlpOutFifo.enq(tlp);
   endrule: writeTlps

   Reg#(TLPTag) lastTag <- mkReg(0);
   FIFOF#(TLPData#(16)) tlpDecodeFifo <- mkFIFOF();
   Reg#(TLPLength) wordCountReg <- mkReg(0);
   rule tlpInRule;
      let tlp <- toGet(tlpInFifo).get();
      tlpDecodeFifo.enq(tlp);
   endrule

   rule handleTlpRule;
      let tlp = tlpDecodeFifo.first;
      Bool handled = False;
      TLPMemoryIO3DWHeader h = unpack(tlp.data);
      hitReg <= tlp.hit;
      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      TLPCompletionHeader hdr_completion = unpack(tlp.data);
      Vector#(4, Bit#(32)) vec = unpack(0);
      Vector#(4, Bit#(32)) tlpvec = unpack(tlp.data);
      let wordCount = wordCountReg;
`ifdef AXI
      let dataInSecondTlp = False;
`elsif AVALON
      let quadWordAligned = isQuadWordAligned(getLowerAddr(hdr_3dw.addr, hdr_3dw.firstbe));
      let dataInSecondTlp = quadWordAligned;
`endif
      if (!tlp.sof) begin
`ifdef PCIE3
	 vec = tlpvec;
`else
	 vec = reverse(tlpvec);
`endif
	 // The MIMO implicit guard only checks for space to enqueue 1 element
	 // so we explicitly check for the number of elements required
	 // otherwise elements in the queue will be overwritten.
	 if (completionMimo.enqReady()
	    && completionTagMimo.enqReady())
	    begin
	       UInt#(3) count = 4;
	       if (tlp.eof) begin
		  count = truncate(unpack(wordCountReg));
	       end
	       wordCount = wordCountReg - extend(pack(count));
	       completionMimo.enq(count, vec);
	       function Tuple2#(TLPTag,Bool) taglast(Integer i); return tuple2(lastTag, (fromInteger(i) == (count-1)) ? tlp.eof : False); endfunction
	       Vector#(4, Tuple2#(TLPTag,Bool)) tagvec = genWith(taglast);
	       completionTagMimo.enq(count, tagvec);
	       handled = True;
	    end
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA
	       && hdr_3dw.pkttype == COMPLETION
	       && completionMimo.enqReady()
	       && completionTagMimo.enqReady()) begin
            TLPTag tag = hdr_completion.tag;
            lastTag <= tag;
            if (!dataInSecondTlp) begin
               vec[0] = hdr_3dw.data;
               wordCount = hdr_3dw.length - 1;
               completionMimo.enq(1, vec);
               completionTagMimo.enq(1, replicate(tuple2(tag,tlp.eof)));
            end
	    handled = True;
      end
      wordCountReg <= wordCount;
      $display("tlpIn handled=%d tlp=%h\n", handled, tlp);
      if (handled) begin
	 tlpDecodeFifo.deq();
      end
   endrule

   FIFO#(PhysMemRequest#(40,buswidth)) readReqFifo <- mkFIFO();
   rule readReqRule if (!writeInProgress); // && !writeDataMimo.deqReady());
      let req <- toGet(readReqFifo).get();
      let burstLen = req.burstLen >> beat_shift;
      let addr = req.addr;
      let arid = req.tag;

      TLPData#(16) tlp = defaultValue;
      tlp.sof = True;
      tlp.eof = True;
      tlp.hit = 7'h00;
      TLPLength tlplen = fromInteger(valueOf(busWidthWords))*truncate(burstLen);
      if (addr[39:32] != 0) begin
	 TLPMemory4DWHeader hdr_4dw = defaultValue;
	 hdr_4dw.format = MEM_READ_4DW_NO_DATA;
	 hdr_4dw.tag = extend(arid);
	 hdr_4dw.reqid = my_id;
	 hdr_4dw.nosnoop = SNOOPING_REQD;
	 hdr_4dw.addr = addr[40-1:2];
	 hdr_4dw.length = tlplen;
	 hdr_4dw.firstbe = reqFirstByteEnable(req)[3:0];
	 hdr_4dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	 tlp.data = pack(hdr_4dw);
	 tlp.be = 16'hffff;
      end
      else begin
	 TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
	 hdr_3dw.format = MEM_READ_3DW_NO_DATA;
	 hdr_3dw.tag = extend(arid);
	 hdr_3dw.reqid = my_id;
	 hdr_3dw.nosnoop = SNOOPING_REQD;
	 hdr_3dw.addr = addr[32-1:2];
	 hdr_3dw.length = tlplen;
	 hdr_3dw.firstbe = reqFirstByteEnable(req)[3:0];
	 hdr_3dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	 tlp.data = pack(hdr_3dw);
	 tlp.be = 16'hfff0;
      end
      tlpOutFifo.enq(tlp);
   endrule

    interface Client        tlp;
        interface request = toGet(tlpOutFifo);
        interface response = toPut(tlpInFifo);
    endinterface
    interface PhysMemSlave slave;
   interface PhysMemWriteServer write_server;
      interface Put writeReq;
         method Action put(PhysMemRequest#(40,buswidth) req); // if (writeBurstCount == 0);
	    Bit#(WriteDataBurstLenSize) burstLen = truncate(req.burstLen >> beat_shift);
	    let addr = req.addr;
	    let awid = req.tag;
	    let writeIs3dw = False;
	    let use3dw = True;
`ifdef PCIE3
	    awid = awid | (1 << (valueOf(MemTagSize)-1));
	    use3dw = False;
`endif
	    TLPLength tlplen = fromInteger(valueOf(busWidthWords))*truncate(burstLen);
	    TLPData#(16) tlp = defaultValue;
	    tlp.sof = True;
	    tlp.eof = False;
	    tlp.hit = 7'h00;
	    tlp.be = 16'hffff;

	    $display("slave.writeAddr tlplen=%d burstLen=%d", tlplen, burstLen);
	    if ((addr >> 32) != 0 || !use3dw) begin
	       TLPMemory4DWHeader hdr_4dw = defaultValue;
	       hdr_4dw.format = MEM_WRITE_4DW_DATA;
	       hdr_4dw.tag = extend(awid);
	       hdr_4dw.reqid = my_id;
	       hdr_4dw.nosnoop = SNOOPING_REQD;
	       hdr_4dw.addr = addr[40-1:2];
	       hdr_4dw.length = tlplen;
	       hdr_4dw.firstbe = reqFirstByteEnable(req)[3:0];
	       hdr_4dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	       tlp.data = pack(hdr_4dw);
	    end
	    else begin
	       writeIs3dw = True;
	       TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
	       hdr_3dw.format = MEM_WRITE_3DW_DATA;
	       hdr_3dw.tag = extend(awid);
	       hdr_3dw.reqid = my_id;
	       hdr_3dw.nosnoop = SNOOPING_REQD;
	       hdr_3dw.addr = addr[32-1:2];
	       hdr_3dw.length = tlplen;
	       hdr_3dw.firstbe = reqFirstByteEnable(req)[3:0];
	       hdr_3dw.lastbe = (tlplen > 1) ? reqLastByteEnable(req)[valueOf(busWidthBytes)-1:valueOf(busWidthBytes)-4] : 0;
	       tlp.be = 16'hfff0; // no data word in this TLP

	       tlp.data = pack(hdr_3dw);
	    end
	    tlpWriteHeaderFifo.enq(TlpWriteHeaderInfo {tlp: tlp, dwCount: tlplen, is3dw: writeIs3dw, isHeaderOnly: (writeIs3dw && tlplen == 1) });
	    writeBurstCountFifo.enq(burstLen);
	    writeTag.enq(extend(awid));
         endmethod
	endinterface
      interface Put writeData;
         method Action put(MemData#(buswidth) wdata)
	    provisos (Bits#(Vector#(busWidthWords, Bit#(32)), busWidth)) if (writeDataMimoHasRoom); //.enqReadyN(fromInteger(valueOf(busWidthWords))));

	    let burstLen = writeBurstCount;
	    if (burstLen == 0) begin
	       burstLen <- toGet(writeBurstCountFifo).get();
	    end
	    writeBurstCount <= extend(burstLen-1);

	    Vector#(busWidthWords, Bit#(32)) v = unpack(wdata.data);
	    writeDataMimo.enq(fromInteger(valueOf(busWidthWords)), v);
            writeDataCnt.increment(fromInteger(valueOf(busWidthWords)));
	    writeDataMimoEnqWire <= True;
         endmethod
       endinterface
      interface Get writeDone;
         method ActionValue#(Bit#(MemTagSize)) get();
	      let tag = doneTag.first();
	      doneTag.deq();
	      return truncate(tag);
           endmethod
	endinterface
   endinterface
   interface PhysMemReadServer read_server;
      interface Put readReq;
         method Action put(PhysMemRequest#(40,buswidth) req);
	    readReqFifo.enq(req);
         endmethod
       endinterface
      interface Get     readData;
         method ActionValue#(MemData#(buswidth)) get() if (completionMimo.deqReady()
							   && completionTagMimo.deqReady());
	      let data_v = completionMimo.first;
	      let tag_last_v = completionTagMimo.first;
	      match { .tag, .last } = tag_last_v[fromInteger(valueOf(busWidthWords))-1];
	      completionMimo.deq();
	      completionTagMimo.deq();
              Bit#(buswidth) v = 0;
              for (Integer i = 0; i < valueOf(busWidthWords); i = i+1) begin
`ifdef AXI
`ifdef PCIE3
		 v[(i+1)*32-1:i*32] = data_v[i];
`else
		 v[(i+1)*32-1:i*32] = byteSwap(data_v[i]);
`endif
`elsif AVALON
		 v[(i+1)*32-1:i*32] = data_v[i];
`endif
              end
	      return MemData { data: v, tag: truncate(tag), last: last}; // last beat of this response burst
           endmethod
	endinterface
   endinterface
    endinterface: slave
   method Bool tlpOutFifoNotEmpty() = tlpOutFifo.notEmpty;
   interface Reg use4dw = use4dwReg;
endmodule: mkMemToPcie


removed parallel matchers for simplicity

// 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 FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import BuildVector::*;
import BRAM::*;
import Gearbox::*;
import Connectable::*;
import StmtFSM::*;
import MemTypes::*;
import MPEngine::*;
import MemReadEngine::*;
import Pipe::*;

interface StrstrRequest;
   method Action setup(Bit#(32) needleSGLId, Bit#(32) mpNextSGLId, Bit#(32) needle_len);
   method Action search(Bit#(32) haystackSGLId, Bit#(32) haystack_len);
endinterface

interface StrstrIndication;
   method Action searchResult(Int#(32) v);
endinterface

interface Strstr#(numeric type haystackBusWidth, numeric type configBusWidth);
   interface StrstrRequest request;
   interface Vector#(1, MemReadClient#(haystackBusWidth)) haystack_read_client;
   interface Vector#(1, MemReadClient#(configBusWidth)) config_read_client;
endinterface

// I can't belive we still have to do this shit
function Bool my_or(Bool a, Bool b) = a || b;

module mkStrstr#(StrstrIndication indication)(Strstr#(haystackBusWidth, configBusWidth))
   provisos (
   Mul#(TDiv#(configBusWidth, 8), 8, configBusWidth)
   ,Mul#(TDiv#(haystackBusWidth, 8), 8, haystackBusWidth)
   ,Add#(1, a__, TDiv#(haystackBusWidth, 8))
   ,Add#(b__, TLog#(TDiv#(haystackBusWidth, 8)), 32)
   ,Mul#(TDiv#(configBusWidth, 32), 32, configBusWidth)
   ,Add#(1, c__, TDiv#(configBusWidth, 32))
   ,Add#(d__, TLog#(TDiv#(configBusWidth, 32)), 32)
   ,Add#(e__, TLog#(TDiv#(configBusWidth, 8)), 32)
   ,Add#(1, f__, TDiv#(configBusWidth, 8))
   ,Add#(g__, TLog#(TDiv#(haystackBusWidth, 32)), 32)
   ,Mul#(TDiv#(haystackBusWidth, 32), 32, haystackBusWidth)
   ,Add#(1, h__, TDiv#(haystackBusWidth, 32))
   ,Add#(i__, 32, haystackBusWidth)
   ,Add#(j__, 8, haystackBusWidth)
	    );

   let verbose = True;

   Reg#(Bit#(32)) needleLen <- mkReg(0);
   MemReadEngine#(haystackBusWidth,haystackBusWidth,1,1) haystack_re <- mkMemReadEngineBuff(1024);
   MemReadEngine#(configBusWidth,configBusWidth,1,2) config_re <- mkMemReadEngineBuff(1024);

   Reg#(Bit#(32)) needleSGLId <- mkReg(0);
   Reg#(Bit#(32)) mpNextSGLId <- mkReg(0);
   Reg#(Bit#(32)) haystackSGLId <- mkReg(0);
   Reg#(Bit#(32)) haystackLen <- mkReg(0);
   Reg#(Bit#(32)) startCnt <- mkReg(0);
   Reg#(Bit#(32)) startBase <- mkReg(0);
   Reg#(Bit#(32)) setupCnt <- mkReg(0);
   Reg#(Bit#(32)) doneCnt <- mkReg(0);

   MPStreamEngine#(haystackBusWidth,configBusWidth) engine <- mkMPStreamEngine;
   mkConnection(config_re.readServers[0].data, engine.needle);
   mkConnection(config_re.readServers[1].data, engine.mpNext);
   mkConnection(haystack_re.readServers[0].data, engine.haystack);

   rule resr;
      let rv <- toGet(engine.locdone).get;
      // send results back to SW
      indication.searchResult(rv);
      if (verbose) $display("strstr search result %d", rv);
   endrule

   interface StrstrRequest request;
      method Action setup(Bit#(32) needle_sglId, Bit#(32) mpNext_sglId, Bit#(32) needle_len);
	 if (verbose) $display("mkStrstr::setup %d %d %d", needle_sglId, mpNext_sglId, needle_len);
	 needleLen <= needle_len;
	 needleSGLId <= needle_sglId;
	 mpNextSGLId <= mpNext_sglId;
	 let burstLen = fromInteger(valueOf(configBusWidth)/8);
	 let mask = burstLen - 1;
	 needle_len = (needle_len + mask) & ~mask;
	 $display("needle_len %d", needle_len);
	 config_re.readServers[0].request.put(MemengineCmd {sglId: needle_sglId, base: 0, burstLen: burstLen, len: needle_len, tag: 0});
	 config_re.readServers[1].request.put(MemengineCmd {sglId: mpNext_sglId, base: 0, burstLen: burstLen, len: needle_len*4, tag: 0});

	 engine.clear();
      endmethod

      method Action search(Bit#(32) haystack_sglId, Bit#(32) haystack_len);
	 if (verbose) $display("mkStrstr::search %d %d", haystack_sglId, haystack_len);
	 haystackLen <= haystack_len;
	 haystackSGLId <= haystack_sglId;
	 let burstLen = fromInteger(valueOf(haystackBusWidth)/8);
	 let mask = burstLen - 1;
	 haystack_len = (haystack_len + mask) & ~mask;
	 haystack_re.readServers[0].request.put(MemengineCmd {sglId: haystack_sglId, base: 0, burstLen: burstLen, len: haystack_len, tag: 0});

	 engine.start(needleLen);
      endmethod
   endinterface
   interface config_read_client = vec(config_re.dmaClient);
   interface haystack_read_client = vec(haystack_re.dmaClient);
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 FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import BuildVector::*;
import BRAM::*;
import Gearbox::*;
import Connectable::*;
import StmtFSM::*;
import MemTypes::*;
import MPEngine::*;
import MemReadEngine::*;
import Pipe::*;

interface StrstrRequest;
   method Action setup(Bit#(32) needleSGLId, Bit#(32) mpNextSGLId, Bit#(32) needle_len);
   method Action search(Bit#(32) haystackSGLId, Bit#(32) haystack_len);
endinterface

interface StrstrIndication;
   method Action searchResult(Int#(32) v);
endinterface

interface Strstr#(numeric type haystackBusWidth, numeric type configBusWidth);
   interface StrstrRequest request;
   interface Vector#(1, MemReadClient#(haystackBusWidth)) haystack_read_client;
   interface Vector#(1, MemReadClient#(configBusWidth)) config_read_client;
endinterface

// I can't belive we still have to do this shit
function Bool my_or(Bool a, Bool b) = a || b;

typedef `DEGPAR DegPar;

module mkStrstr#(StrstrIndication indication)(Strstr#(haystackBusWidth, configBusWidth))
   provisos( Log#(DegPar,logDegPar)

   ,Mul#(TDiv#(configBusWidth, 8), 8, configBusWidth)
   ,Mul#(TDiv#(haystackBusWidth, 8), 8, haystackBusWidth)
   ,Add#(1, a__, TDiv#(haystackBusWidth, 8))
   ,Add#(b__, TLog#(TDiv#(haystackBusWidth, 8)), 32)
   ,Mul#(TDiv#(configBusWidth, 32), 32, configBusWidth)
   ,Add#(1, c__, TDiv#(configBusWidth, 32))
   ,Add#(d__, TLog#(TDiv#(configBusWidth, 32)), 32)
   ,Add#(e__, TLog#(TDiv#(configBusWidth, 8)), 32)
   ,Add#(1, f__, TDiv#(configBusWidth, 8))
   ,Add#(g__, TLog#(TDiv#(haystackBusWidth, 32)), 32)
   ,Mul#(TDiv#(haystackBusWidth, 32), 32, haystackBusWidth)
   ,Add#(1, h__, TDiv#(haystackBusWidth, 32))
   ,Add#(i__, 32, haystackBusWidth)
   ,Add#(j__, 8, haystackBusWidth)
   ,FunnelPipesPipelined#(1, DegPar, MemData#(haystackBusWidth), MemReadFunnelBPC)
   ,FunnelPipesPipelined#(1, DegPar, MemData#(configBusWidth), MemReadFunnelBPC)
	    );

   let verbose = True;

   Reg#(Bit#(32)) needleLen <- mkReg(0);
   MemReadEngine#(haystackBusWidth,haystackBusWidth,1,DegPar) haystack_re <- mkMemReadEngineBuff(1024);
   MemReadEngine#(configBusWidth,configBusWidth,1,DegPar) config_re <- mkMemReadEngineBuff(1024);

   Reg#(Bit#(32)) needleSGLId <- mkReg(0);
   Reg#(Bit#(32)) mpNextSGLId <- mkReg(0);
   Reg#(Bit#(32)) haystackSGLId <- mkReg(0);
   Reg#(Bit#(32)) haystackLen <- mkReg(0);
   Reg#(Bit#(32)) startCnt <- mkReg(0);
   Reg#(Bit#(32)) startBase <- mkReg(0);
   Reg#(Bit#(32)) setupCnt <- mkReg(0);
   Reg#(Bit#(32)) doneCnt <- mkReg(0);

   let read_servers = zip(haystack_re.readServers,config_re.readServers);
   Vector#(DegPar, MPEngine#(haystackBusWidth,configBusWidth)) engines <- mapM(uncurry(mkMPEngine),read_servers);
   Vector#(DegPar, PipeOut#(Int#(32))) locdonePipes;

   FIFOF#(Triplet#(Bit#(32))) setsearchFIFO <- mkFIFOF;
   UnFunnelPipe#(1,DegPar,Triplet#(Bit#(32)),1) setsearchPipeUnFunnel <- mkUnFunnelPipesPipelinedRR(vec(toPipeOut(setsearchFIFO)), 1);

   for(Integer i = 0; i < valueOf(DegPar); i=i+1) begin
      locdonePipes[i] = engines[i].locdone;
      mkConnection(setsearchPipeUnFunnel[i],engines[i].setsearch);
   end

   FunnelPipe#(1,DegPar,Int#(32),1) locdonePipe <- mkFunnelPipesPipelined(locdonePipes);
   let lpv = fromInteger(valueOf(logDegPar));
   let pv = fromInteger(valueOf(DegPar));

   rule resr;
      let rv <- toGet(locdonePipe[0]).get;
      if (rv == -1) begin
	 // notify the SW when the search is finished
	 if (doneCnt+1 == pv) begin
	    doneCnt <= 0;
	    indication.searchResult(-1);
	 end
	 else begin
	    doneCnt <= doneCnt+1;
	 end
      end
      else begin
	 // send results back to SW
	 indication.searchResult(rv);
	 if (verbose) $display("strstr search result %d", rv);
      end
   endrule

   // setup the MPEngines when new configuration arrives
   Stmt setupStmt =
   seq
      action
	 if (verbose) $display("setupStmt (begin)");
	 setupCnt <= 0;
      endaction
      while(setupCnt < pv) action
	 setupCnt <= setupCnt+1;
	 let tup = tuple3(needleSGLId, mpNextSGLId, needleLen);
	 setsearchFIFO.enq(tup);
	 if (verbose) $display(fshow("setupStmt (mid) ")+fshow(tup));
      endaction
      if (verbose) $display("setupStmt (end)");
   endseq;
   FSM setupFSM <- mkFSM(setupStmt);

   // start the search;
   Stmt startStmt =
   seq
      action
	 if (verbose) $display("startStmt (begin)");
	 startCnt <= 0;
	 startBase <= 0;
      endaction
      while (startCnt < pv-1) action
	 let tup = tuple3(haystackSGLId, (haystackLen>>lpv)+needleLen, startBase);
	 setsearchFIFO.enq(tup);
	 startBase <= startBase + (haystackLen>>lpv);
	 startCnt <= startCnt+1;
	 if (verbose) $display(fshow("startStmt ")+fshow(tup)+fshow(" (mid)"));
      endaction
      action
	 let tup = tuple3(haystackSGLId, haystackLen>>lpv, startBase);
	 setsearchFIFO.enq(tup);
	 if (verbose) $display(fshow("startStmt ")+fshow(tup)+fshow(" (end)"));
      endaction
   endseq;
   FSM startFSM <- mkFSM(startStmt);

   interface StrstrRequest request;
      method Action setup(Bit#(32) needle_sglId, Bit#(32) mpNext_sglId, Bit#(32) needle_len);
	 if (verbose) $display("mkStrstr::setup %d %d %d", needle_sglId, mpNext_sglId, needle_len);
	 needleLen <= needle_len;
	 needleSGLId <= needle_sglId;
	 mpNextSGLId <= mpNext_sglId;
	 setupFSM.start();
      endmethod

      method Action search(Bit#(32) haystack_sglId, Bit#(32) haystack_len);
	 if (verbose) $display("mkStrstr::search %d %d", haystack_sglId, haystack_len);
	 haystackLen <= haystack_len;
	 haystackSGLId <= haystack_sglId;
	 startFSM.start();
      endmethod
   endinterface
   interface config_read_client = vec(config_re.dmaClient);
   interface haystack_read_client = vec(haystack_re.dmaClient);
endmodule