Using Cache_Decls instead of nested ifdefs for imports

// Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved.

package MMU_Cache_AXI4_Adapter;

// ================================================================
// Adapter converting generic 64b-wide read/write requests into an
// AXI4 bus master.  Two 'clients' upstream:
// - a cache: requests/responses are for full cache lines
// - an MMIO: requests/responses are for 64b word or sub-word,
//            and where lane-alignment is already done.

// The AXI4 bus master can be used with 32b or 64b buses, and manages
// byte-lane alignment, number of beats in a burst, write-strobes,
// etc. accordingly.

// TODO: Wrapping bursts (when cache can handle that)

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

export  MMU_Cache_AXI4_Adapter_IFC (..);
export  mkMMU_Cache_AXI4_Adapter;

// ================================================================
// BSV lib imports

import Vector       :: *;
import BRAMCore     :: *;
import ConfigReg    :: *;
import FIFOF        :: *;
import GetPut       :: *;
import ClientServer :: *;
import Assert       :: *;

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

import Cur_Cycle     :: *;
import GetPut_Aux    :: *;
import Semi_FIFOF    :: *;

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

import Cache_Decls      :: *;
import MMU_Cache_Common :: *;

import AXI4_Types  :: *;
import Fabric_Defs :: *;

// ================================================================
// This module collects and discards write-responses.
// There is no write-data response to either client.
// Errors on writes are reported on a separate method that can be used
// to trigger an interrupt.

// This module avoids interleaving read and write requests,
// i.e., it launches a read request only when no write-responses from
// the AXI4 fabric are pending.

// ================================================================
// MODULE INTERFACE

interface MMU_Cache_AXI4_Adapter_IFC;
   // ----------------
   // interface for cache-line read/write client

   interface Put #(Line_Req)  p_mem_line_req;
   interface Put #(Bit #(64)) p_mem_line_write_data;
   interface Get #(Read_Data) g_mem_line_read_data;

   // ----------------
   // interface for word/sub-word read/write client

   interface Put #(Single_Req) p_mem_single_req;
   interface Put #(Bit #(64))  p_mem_single_write_data;
   interface Get #(Read_Data)  g_mem_single_read_data;

   // ----------------
   // Write-error from memory
   (* always_ready *)
   method Bool mv_write_error;

   // ----------------
   // Fabric master interface
   interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mem_master;

   // ----------------------------------------------------------------
   // Misc. control and status

   // Signal that DDR4 has been initialized and is ready to accept requests
   method Action ma_ddr4_ready;

   // Misc. status; 0 = running, no error
   (* always_ready *)
   method Bit #(8) mv_status;

endinterface

// ================================================================
// Misc. help functions

// ----------------------------------------------------------------
// Byte-width of the fabric

Integer bytes_per_fabric_data = ((valueOf (Wd_Data) == 32) ? 4 : 8);

// ----------------------------------------------------------------
// Convert size code into AXI4_Size code (number of bytes in a beat).
// It just so happens that our coding coincides with AXI4's coding.

function AXI4_Size  fv_size_code_to_AXI4_Size (Bit #(2) size_code);
   return { 1'b0, size_code };
endfunction

// ----------------------------------------------------------------
// Check if addr is cache-line-aligned

Bit #(64) byte_in_line_mask = fromInteger (bytes_per_cline - 1);

function Bool fv_is_line_aligned (Bit #(64) addr);
   return ((addr & byte_in_line_mask) == 0);
endfunction

// ----------------------------------------------------------------
// Align an address to beginning of cache line containing the addr

function Bit #(64) fv_align_to_line (Bit #(64) addr);
   return (addr & (~ byte_in_line_mask));
endfunction

// ----------------------------------------------------------------
// Convert a 64-bit Address to an AXI4 Fabric Address
// For FABRIC64 this does nothing.
// For FABRIC32 it discards the upper 32 bits.

function Fabric_Addr fv_Addr_to_Fabric_Addr (Bit #(64) addr);
   return truncate (addr);
endfunction

// ================================================================
// MODULE IMPLEMENTATION

(* synthesize *)
module mkMMU_Cache_AXI4_Adapter #(parameter Bit #(3) verbosity)
                                (MMU_Cache_AXI4_Adapter_IFC);

   // Verbosity: 0=quiet, 1 = rule firings
   // Integer verbosity = 1;

   // FIFOs for line-client
   FIFOF #(Line_Req)   f_line_reqs       <- mkFIFOF;
   FIFOF #(Bit #(64))  f_line_write_data <- mkFIFOF;
   FIFOF #(Read_Data)  f_line_read_data  <- mkFIFOF;

   // FIFOs for single-word client
   FIFOF #(Single_Req) f_single_reqs       <- mkFIFOF;
   FIFOF #(Bit #(64))  f_single_write_data <- mkFIFOF;
   FIFOF #(Read_Data)  f_single_read_data  <- mkFIFOF;

   // Identifiers for requestor client
   Bit #(1) client_id_line   = 0;
   Bit #(1) client_id_single = 1;

   // Limit the number of reads/writes outstanding to 15
   // TODO: change these to concurrent up/down counters?
   Reg #(Bit #(4)) rg_rd_rsps_pending <- mkReg (0);
   Reg #(Bit #(4)) rg_wr_rsps_pending <- mkReg (0);

   Reg #(Bool) rg_ddr4_ready <- mkReg (False);

   // Record errors on write-responses from mem
   Reg #(Bool) rg_write_error <- mkReg (False);

   // AXI4 fabric request/response
   AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;

   // ****************************************************************
   // BEHAVIOR: READ RESPONSES (for line and single clients)
   // The following registers identify the client, the beat, etc.

   Reg #(Bit #(1)) rg_rd_client_id <- mkRegU;

   // TODO: change mkFIFOF to mkSizedFIFOF (allowing multiple outstanding reads)
   // may reads can be outstanding
   FIFOF #(Tuple3 #(Bit #(2),     // size_code
		    Bit #(1),     // addr bit [2]
		    Bit #(8)))    // Num beats read-data
         f_rd_rsp_control <- mkFIFOF;
   match { .rd_req_size_code, .rd_req_addr_bit_2, .rd_req_beats } = f_rd_rsp_control.first;

   // Count beats in a single transaction.
   // Note: beat-count is for fabric, not for client-side data.
   // Former is 2 x latter for Fabric32 for 64b line data and 64b single data.

   Reg #(Bit #(8)) rg_rd_beat <- mkRegU;

   // The following are needed for FABRIC32 during each burst response
   // to assemble lower and upper 32b into a 64b response to client
   Reg #(Bool)       rg_rd_data_lower32_ok <- mkRegU;
   Reg #(Bit #(32))  rg_rd_data_lower32    <- mkRegU;

   rule rl_read_data (rg_rd_beat < rd_req_beats);
      rg_rd_beat <= rg_rd_beat + 1;
      Bool last_beat = (rg_rd_beat == (rd_req_beats - 1));
      if (last_beat) begin
	 f_rd_rsp_control.deq;
	 rg_rd_rsps_pending <= rg_rd_rsps_pending - 1;
      end

      let rd_data <- pop_o (master_xactor.o_rd_data);

      Bool      ok   = (rd_data.rresp == axi4_resp_okay);
      Bit #(64) data = zeroExtend (rd_data.rdata);

      Bool do_enq    = True;
      Bool even_beat = (rg_rd_beat [0] == 1'b0);

      // FABRIC32 adjustments
      if (valueOf (Wd_Data) == 32) begin
	 if (rd_req_size_code != 2'b11) begin
	    // B, H, W: only 1 beat
	    Bool in_upper32 = (rd_req_addr_bit_2 == 1'b1);
	    if (in_upper32)
	       data = { data [31:0], 32'b0 };
	    do_enq = True;
	 end
	 else if (even_beat) begin // D, even beat
	    // Just save lower 32b; enq response only after next beat
	    rg_rd_data_lower32_ok <= ok;
	    rg_rd_data_lower32    <= data [31:0];
	    do_enq = False;
	 end
	 else begin // D, odd beat
	    ok   = (rg_rd_data_lower32_ok && ok);
	    data = { data [31:0], rg_rd_data_lower32 };
	    do_enq = True;
	 end
      end

      if (do_enq) begin
	 let rsp = Read_Data {ok: ok, data: data};
	 if (rg_rd_client_id == client_id_line)
	    f_line_read_data.enq (rsp);
	 else
	    f_single_read_data.enq (rsp);
      end

      if (verbosity >= 1) begin
	 $display ("%0d: %m.rl_read_data: ", cur_cycle);
	 if (rg_rd_client_id == client_id_line) $write ("    line (beat %0d)", rg_rd_beat);
	 else                                   $write ("     single");
	 $write (" ok %0d data %0h", pack (ok), data);
	 if ((valueOf (Wd_Data) == 32) && even_beat) $write (" (lower 32b of 64b)");
	 $display ("");
      end
   endrule: rl_read_data

   // ****************************************************************
   // BEHAVIOR: LINE READ REQUESTS

   rule rl_line_read_req (f_line_reqs.first.is_read
			  && rg_ddr4_ready
			  && (rg_rd_rsps_pending < '1)
			  && (rg_wr_rsps_pending == 0));
      let req <- pop (f_line_reqs);

      dynamicAssert (fv_is_line_aligned (req.addr), "rl_line_read_req addr misaligned");

      let addr = fv_align_to_line (req.addr);

      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (addr);
      AXI4_Size   fabric_size = ((valueOf (Wd_Data) == 32) ? axsize_4 : axsize_8);

      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      Integer   num_beats  = bytes_per_cline / bytes_per_fabric_data;
      AXI4_Len  fabric_len = fromInteger (num_beats - 1);

      let mem_req_rd_addr = AXI4_Rd_Addr {arid:     fabric_default_id,
					  araddr:   fabric_addr,
					  arlen:    fabric_len,
					  arsize:   fabric_size,
					  arburst:  axburst_incr,
					  arlock:   fabric_default_lock,
					  arcache:  fabric_default_arcache,
					  arprot:   fabric_default_prot,
					  arqos:    fabric_default_qos,
					  arregion: fabric_default_region,
					  aruser:   fabric_default_user};
      master_xactor.i_rd_addr.enq (mem_req_rd_addr);

      f_rd_rsp_control.enq (tuple3 (2'b11, 1'b0, fromInteger (num_beats)));
      rg_rd_client_id    <= client_id_line;
      rg_rd_beat         <= 0;
      rg_rd_rsps_pending <= rg_rd_rsps_pending + 1;

      // Debugging
      if (verbosity >= 1) begin
	 $display ("%0d: %m.rl_line_read_req: ", cur_cycle);
	 $display ("    AXI4_Rd_Addr{araddr %0h arlen %0d (burst len %0d) ",
		   fabric_addr,
		   fabric_len, num_beats,
		   fshow_AXI4_Size (fabric_size),
		   " incr}");
      end
   endrule

   // ****************************************************************
   // BEHAVIOR: Single read requests (not a burst)

   rule rl_single_read_req (f_single_reqs.first.is_read
			    && rg_ddr4_ready
			    && (rg_rd_rsps_pending < '1)
			    && (rg_wr_rsps_pending == 0));
      let         req        <- pop (f_single_reqs);
      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (req.addr);
      AXI4_Size   fabric_size = fv_size_code_to_AXI4_Size (req.size_code);

      if (verbosity >= 1)
	 $display ("%0d: %m.rl_single_read_req:\n    AXI4_Rd_Addr{araddr %0h arlen 0 (burst length 1) ",
		   cur_cycle, fabric_addr,  fshow_AXI4_Size (fabric_size), "}");

      Bit #(8)    num_beats   = 1;

      // FABRIC32 adjustments
      if ((valueOf (Wd_Data) == 32) && (req.size_code == 2'b11)) begin
	 fabric_size = axsize_4;
	 num_beats   = 2;
      end
      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      AXI4_Len fabric_len = num_beats - 1;

      let mem_req_rd_addr = AXI4_Rd_Addr {arid:     fabric_default_id,
					  araddr:   fabric_addr,
					  arlen:    0,           // burst len = arlen+1
					  arsize:   fabric_size,
					  arburst:  fabric_default_burst,
					  arlock:   fabric_default_lock,
					  arcache:  fabric_default_arcache,
					  arprot:   fabric_default_prot,
					  arqos:    fabric_default_qos,
					  arregion: fabric_default_region,
					  aruser:   fabric_default_user};
      master_xactor.i_rd_addr.enq (mem_req_rd_addr);

      f_rd_rsp_control.enq (tuple3 (req.size_code, req.addr [2], num_beats));
      rg_rd_client_id    <= client_id_single;
      rg_rd_beat         <= 0;
      rg_rd_rsps_pending <= rg_rd_rsps_pending + 1;
   endrule

   // ****************************************************************
   // BEHAVIOR: WRITE RESPONSES (for line and single clients)
   // The following register identifies the client

   rule rl_write_rsp;
      let wr_resp <- pop_o (master_xactor.o_wr_resp);

      Bool err = False;
      if (rg_wr_rsps_pending == 0) begin
	 rg_write_error <= True;

	 $display ("%0d: %m.rl_write_rsp: ERROR not expecting any write-response:", cur_cycle);
	 $display ("    ", fshow (wr_resp));
      end
      else begin
	 rg_wr_rsps_pending <= rg_wr_rsps_pending - 1;
	 if (wr_resp.bresp != axi4_resp_okay) begin
	    rg_write_error <= True;
	    if (verbosity >= 1) begin
	       $display ("%0d: %m.rl_write_rsp: FABRIC RESPONSE ERROR", cur_cycle);
	       $display ("    ", fshow (wr_resp));
	    end
	 end
	 else if (verbosity >= 1) begin
	    $display ("%0d: %m.rl_write_rsp: pending=%0d, ",
		      cur_cycle, rg_wr_rsps_pending, fshow (wr_resp));
	 end
      end
   endrule

   // ****************************************************************
   // BEHAVIOR: Write data (for both line and single)
   // Assume that data is already lane-aligned for 64b data width.

   FIFOF #(Tuple4 #(Bit #(1),     // client_id
		    Bit #(2),     // size_code
		    Bit #(3),     // addr lsbs
		    Bit #(8)))    // Num beats in write-data
         f_wr_data_control <- mkFIFOF;
   match {.wr_client_id,
	  .wr_req_size_code,
	  .wr_req_addr_lsbs,
	  .wr_req_beats } = f_wr_data_control.first;

   // Count beats in a single transaction
   // Note: beat-count is for fabric, not for client-side data.
   // Former is 2 x latter for Fabric32 for 64b line data and 64b single data.
   Reg #(Bit #(8)) rg_wr_beat <- mkReg (0);

   rule rl_write_data (rg_ddr4_ready && (rg_wr_beat < wr_req_beats));
      Bool last = (rg_wr_beat == (wr_req_beats - 1));
      if (last) begin
	 f_wr_data_control.deq;
	 rg_wr_beat <= 0;
      end
      else
	 rg_wr_beat <= rg_wr_beat + 1;

      Bit #(64) data = ((wr_client_id == client_id_line)
			? f_line_write_data.first
			: f_single_write_data.first);

      // Compute strobe from size and address
      Bit #(8)  strb = case (wr_req_size_code)
			  2'b00: 8'h_01;
			  2'b01: 8'h_03;
			  2'b10: 8'h_0F;
			  2'b11: 8'h_FF;
		       endcase;
      strb = (strb << wr_req_addr_lsbs);

      Bool do_deq    = True;
      Bool even_beat = (rg_wr_beat [0] == 1'b0);

      // FABRIC32 adjustments
      if (valueOf (Wd_Data) == 32) begin
	 if (wr_req_size_code != 2'b11) begin
	    // B, H, W: only 1 beat
	    Bool in_upper32 = (wr_req_addr_lsbs [2] == 1'b1);
	    if (in_upper32) begin
	       data = { 32'b0, data [63:32] };
	       strb = {  4'b0, strb [7:4] };
	    end
	    do_deq = True;
	 end
	 else if (even_beat) begin  // D, even beat
	    do_deq = False;
	 end
	 else begin // D, odd beat
	    data   = { 32'b0, data [63:32] };
	    strb   = '1;
	    do_deq = True;
	 end
      end

      let mem_req_wr_data = AXI4_Wr_Data {wdata:  truncate (data),
					  wstrb:  truncate (strb),
					  wlast:  last,
					  wuser:  fabric_default_user};
      master_xactor.i_wr_data.enq (mem_req_wr_data);

      if (do_deq) begin
	 if (wr_client_id == client_id_line)
	    f_line_write_data.deq;
	 else
	    f_single_write_data.deq;
      end

      if (verbosity >= 1) begin
	 $display ("%0d: %m.rl_write_data: beat %0d/%0d", cur_cycle, rg_wr_beat, wr_req_beats);
	 $display ("    AXI4_Wr_Data{%0h strb %0h last %0d}", data, strb, pack (last));
      end
   endrule

   // ****************************************************************
   // BEHAVIOR: Line-write requests

   rule rl_line_write_req ((! f_line_reqs.first.is_read)
			   && rg_ddr4_ready
			   && (rg_rd_rsps_pending == 0)
			   && (rg_wr_rsps_pending < '1));
      let req <- pop (f_line_reqs);

      dynamicAssert (fv_is_line_aligned (req.addr), "rl_line_write_req addr misaligned");

      let addr = fv_align_to_line (req.addr);

      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (addr);
      AXI4_Size   fabric_size = ((valueOf (Wd_Data) == 32) ? axsize_4 : axsize_8);

      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      Integer   num_beats  = bytes_per_cline / bytes_per_fabric_data;
      AXI4_Len  fabric_len = fromInteger (num_beats - 1);

      let mem_req_wr_addr = AXI4_Wr_Addr {awid:     fabric_default_id,
					  awaddr:   fabric_addr,
					  awlen:    fabric_len,
					  awsize:   fabric_size,
					  awburst:  axburst_incr,
					  awlock:   fabric_default_lock,
					  awcache:  fabric_default_awcache,
					  awprot:   fabric_default_prot,
					  awqos:    fabric_default_qos,
					  awregion: fabric_default_region,
					  awuser:   fabric_default_user};
      master_xactor.i_wr_addr.enq (mem_req_wr_addr);

      f_wr_data_control.enq (tuple4 (client_id_line,
				     2'b11,        // size_code D
				     0,            // req.addr lsbs
				     fromInteger (num_beats)));
      rg_wr_rsps_pending <= rg_wr_rsps_pending + 1;

      // Debugging
      if (verbosity >= 1)
	 $display ("%0d: %m.rl_line_write_req: AXI4_Wr_Addr{awaddr %0h awlen %0d burst-length %0d ",
		   cur_cycle,
		   fabric_addr,
		   fabric_len, num_beats,
		   fshow_AXI4_Size (fabric_size),
		   " incr}");
   endrule

   // ****************************************************************
   // Scheduling

   (* descending_urgency = "rl_read_data,       rl_write_rsp" *)
   (* descending_urgency = "rl_write_rsp,       rl_line_read_req" *)
   (* descending_urgency = "rl_line_read_req,   rl_line_write_req" *)
   (* descending_urgency = "rl_line_write_req,  rl_write_data" *)
   (* descending_urgency = "rl_write_data,      rl_single_read_req" *)
   (* descending_urgency = "rl_single_read_req, rl_single_write_req" *)

   // The following should be implied by transitivity, but bsc still cites them
   (* descending_urgency = "rl_read_data,       rl_line_read_req" *)
   (* descending_urgency = "rl_line_read_req,   rl_single_read_req" *)
   (* descending_urgency = "rl_line_write_req,  rl_single_read_req" *)
   (* descending_urgency = "rl_write_rsp,       rl_line_write_req" *)
   (* descending_urgency = "rl_line_write_req,  rl_single_write_req" *)
   (* descending_urgency = "rl_line_read_req,   rl_single_write_req" *)

   // ****************************************************************
   // BEHAVIOR: Single write requests (not a burst)

   rule rl_single_write_req ((! f_single_reqs.first.is_read)
			     && rg_ddr4_ready
			     && (rg_rd_rsps_pending == 0)
			     && (rg_wr_rsps_pending < '1));
      let req <- pop (f_single_reqs);

      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (req.addr);
      AXI4_Size   fabric_size = fv_size_code_to_AXI4_Size (req.size_code);
      Bit #(8)    num_beats   = 1;

      // FABRIC32 adjustments
      if ((valueOf (Wd_Data) == 32) && (req.size_code == 2'b11)) begin
	 fabric_size = axsize_4;
	 num_beats   = 2;
      end
      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      AXI4_Len fabric_len = num_beats - 1;

      // AXI4 Write-Address channel
      let mem_req_wr_addr = AXI4_Wr_Addr {awid:     fabric_default_id,
					  awaddr:   fabric_addr,
					  awlen:    fabric_len,
					  awsize:   fabric_size,
					  awburst:  fabric_default_burst,
					  awlock:   fabric_default_lock,
					  awcache:  fabric_default_awcache,
					  awprot:   fabric_default_prot,
					  awqos:    fabric_default_qos,
					  awregion: fabric_default_region,
					  awuser:   fabric_default_user};
      master_xactor.i_wr_addr.enq (mem_req_wr_addr);

      f_wr_data_control.enq (tuple4 (client_id_single,
				     req.size_code,
				     req.addr [2:0],
				     num_beats));
      rg_wr_rsps_pending <= rg_wr_rsps_pending + 1;

      // Debugging
      if (verbosity >= 1)
	 $display ("%0d: %m.rl_single_write_req: AXI4_Wr_Addr{awaddr %0h awlen %0d burst-length %0d ",
		   cur_cycle,
		   fabric_addr,
		   fabric_len, num_beats,
		   fshow_AXI4_Size (fabric_size),
		   " incr}");
   endrule

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

   // ----------------
   // interface for cache-line read/write client

   interface Put p_mem_line_req        = toPut (f_line_reqs);
   interface Put p_mem_line_write_data = toPut (f_line_write_data);
   interface Get g_mem_line_read_data  = toGet (f_line_read_data);

   // ----------------
   // interface for word/sub-word read/write client

   interface Put p_mem_single_req        = toPut (f_single_reqs);
   interface Put p_mem_single_write_data = toPut (f_single_write_data);
   interface Get g_mem_single_read_data  = toGet (f_single_read_data);

   // ----------------
   // Write-error from memory

   method Bool mv_write_error = rg_write_error;

   // ----------------
   // Fabric master interface
   interface mem_master = master_xactor.axi_side;

   // ----------------------------------------------------------------
   // Misc. control and status

   // Signal that DDR4 has been initialized and is ready to accept requests
   method Action ma_ddr4_ready;
      rg_ddr4_ready <= True;
      $display ("%0d: %0m: Enabling memory accesses", cur_cycle);
   endmethod

   // Misc. status; 0 = running, no error
   method Bit #(8) mv_status;
      return (rg_write_error ? 1 : 0);
   endmethod

endmodule

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

endpackage

// Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved.

package MMU_Cache_AXI4_Adapter;

// ================================================================
// Adapter converting generic 64b-wide read/write requests into an
// AXI4 bus master.  Two 'clients' upstream:
// - a cache: requests/responses are for full cache lines
// - an MMIO: requests/responses are for 64b word or sub-word,
//            and where lane-alignment is already done.

// The AXI4 bus master can be used with 32b or 64b buses, and manages
// byte-lane alignment, number of beats in a burst, write-strobes,
// etc. accordingly.

// TODO: Wrapping bursts (when cache can handle that)

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

export  MMU_Cache_AXI4_Adapter_IFC (..);
export  mkMMU_Cache_AXI4_Adapter;

// ================================================================
// BSV lib imports

import Vector       :: *;
import BRAMCore     :: *;
import ConfigReg    :: *;
import FIFOF        :: *;
import GetPut       :: *;
import ClientServer :: *;
import Assert       :: *;

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

import Cur_Cycle     :: *;
import GetPut_Aux    :: *;
import Semi_FIFOF    :: *;

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

`ifdef RV32
`ifdef SV32
import Cache_Decls_RV32_Sv32_8KB_2way :: *;
`else
import Cache_Decls_RV32_8KB_2way :: *;
`endif
`endif

`ifdef RV64
`ifdef SV39
import Cache_Decls_RV64_Sv39_8KB_2way :: *;
`else
import Cache_Decls_RV64_8KB_2way :: *;
`endif
`endif

import MMU_Cache_Common :: *;

import AXI4_Types  :: *;
import Fabric_Defs :: *;

// ================================================================
// This module collects and discards write-responses.
// There is no write-data response to either client.
// Errors on writes are reported on a separate method that can be used
// to trigger an interrupt.

// This module avoids interleaving read and write requests,
// i.e., it launches a read request only when no write-responses from
// the AXI4 fabric are pending.

// ================================================================
// MODULE INTERFACE

interface MMU_Cache_AXI4_Adapter_IFC;
   // ----------------
   // interface for cache-line read/write client

   interface Put #(Line_Req)  p_mem_line_req;
   interface Put #(Bit #(64)) p_mem_line_write_data;
   interface Get #(Read_Data) g_mem_line_read_data;

   // ----------------
   // interface for word/sub-word read/write client

   interface Put #(Single_Req) p_mem_single_req;
   interface Put #(Bit #(64))  p_mem_single_write_data;
   interface Get #(Read_Data)  g_mem_single_read_data;

   // ----------------
   // Write-error from memory
   (* always_ready *)
   method Bool mv_write_error;

   // ----------------
   // Fabric master interface
   interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mem_master;

   // ----------------------------------------------------------------
   // Misc. control and status

   // Signal that DDR4 has been initialized and is ready to accept requests
   method Action ma_ddr4_ready;

   // Misc. status; 0 = running, no error
   (* always_ready *)
   method Bit #(8) mv_status;

endinterface

// ================================================================
// Misc. help functions

// ----------------------------------------------------------------
// Byte-width of the fabric

Integer bytes_per_fabric_data = ((valueOf (Wd_Data) == 32) ? 4 : 8);

// ----------------------------------------------------------------
// Convert size code into AXI4_Size code (number of bytes in a beat).
// It just so happens that our coding coincides with AXI4's coding.

function AXI4_Size  fv_size_code_to_AXI4_Size (Bit #(2) size_code);
   return { 1'b0, size_code };
endfunction

// ----------------------------------------------------------------
// Check if addr is cache-line-aligned

Bit #(64) byte_in_line_mask = fromInteger (bytes_per_cline - 1);

function Bool fv_is_line_aligned (Bit #(64) addr);
   return ((addr & byte_in_line_mask) == 0);
endfunction

// ----------------------------------------------------------------
// Align an address to beginning of cache line containing the addr

function Bit #(64) fv_align_to_line (Bit #(64) addr);
   return (addr & (~ byte_in_line_mask));
endfunction

// ----------------------------------------------------------------
// Convert a 64-bit Address to an AXI4 Fabric Address
// For FABRIC64 this does nothing.
// For FABRIC32 it discards the upper 32 bits.

function Fabric_Addr fv_Addr_to_Fabric_Addr (Bit #(64) addr);
   return truncate (addr);
endfunction

// ================================================================
// MODULE IMPLEMENTATION

(* synthesize *)
module mkMMU_Cache_AXI4_Adapter #(parameter Bit #(3) verbosity)
                                (MMU_Cache_AXI4_Adapter_IFC);

   // Verbosity: 0=quiet, 1 = rule firings
   // Integer verbosity = 1;

   // FIFOs for line-client
   FIFOF #(Line_Req)   f_line_reqs       <- mkFIFOF;
   FIFOF #(Bit #(64))  f_line_write_data <- mkFIFOF;
   FIFOF #(Read_Data)  f_line_read_data  <- mkFIFOF;

   // FIFOs for single-word client
   FIFOF #(Single_Req) f_single_reqs       <- mkFIFOF;
   FIFOF #(Bit #(64))  f_single_write_data <- mkFIFOF;
   FIFOF #(Read_Data)  f_single_read_data  <- mkFIFOF;

   // Identifiers for requestor client
   Bit #(1) client_id_line   = 0;
   Bit #(1) client_id_single = 1;

   // Limit the number of reads/writes outstanding to 15
   // TODO: change these to concurrent up/down counters?
   Reg #(Bit #(4)) rg_rd_rsps_pending <- mkReg (0);
   Reg #(Bit #(4)) rg_wr_rsps_pending <- mkReg (0);

   Reg #(Bool) rg_ddr4_ready <- mkReg (False);

   // Record errors on write-responses from mem
   Reg #(Bool) rg_write_error <- mkReg (False);

   // AXI4 fabric request/response
   AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;

   // ****************************************************************
   // BEHAVIOR: READ RESPONSES (for line and single clients)
   // The following registers identify the client, the beat, etc.

   Reg #(Bit #(1)) rg_rd_client_id <- mkRegU;

   // TODO: change mkFIFOF to mkSizedFIFOF (allowing multiple outstanding reads)
   // may reads can be outstanding
   FIFOF #(Tuple3 #(Bit #(2),     // size_code
		    Bit #(1),     // addr bit [2]
		    Bit #(8)))    // Num beats read-data
         f_rd_rsp_control <- mkFIFOF;
   match { .rd_req_size_code, .rd_req_addr_bit_2, .rd_req_beats } = f_rd_rsp_control.first;

   // Count beats in a single transaction.
   // Note: beat-count is for fabric, not for client-side data.
   // Former is 2 x latter for Fabric32 for 64b line data and 64b single data.

   Reg #(Bit #(8)) rg_rd_beat <- mkRegU;

   // The following are needed for FABRIC32 during each burst response
   // to assemble lower and upper 32b into a 64b response to client
   Reg #(Bool)       rg_rd_data_lower32_ok <- mkRegU;
   Reg #(Bit #(32))  rg_rd_data_lower32    <- mkRegU;

   rule rl_read_data (rg_rd_beat < rd_req_beats);
      rg_rd_beat <= rg_rd_beat + 1;
      Bool last_beat = (rg_rd_beat == (rd_req_beats - 1));
      if (last_beat) begin
	 f_rd_rsp_control.deq;
	 rg_rd_rsps_pending <= rg_rd_rsps_pending - 1;
      end

      let rd_data <- pop_o (master_xactor.o_rd_data);

      Bool      ok   = (rd_data.rresp == axi4_resp_okay);
      Bit #(64) data = zeroExtend (rd_data.rdata);

      Bool do_enq    = True;
      Bool even_beat = (rg_rd_beat [0] == 1'b0);

      // FABRIC32 adjustments
      if (valueOf (Wd_Data) == 32) begin
	 if (rd_req_size_code != 2'b11) begin
	    // B, H, W: only 1 beat
	    Bool in_upper32 = (rd_req_addr_bit_2 == 1'b1);
	    if (in_upper32)
	       data = { data [31:0], 32'b0 };
	    do_enq = True;
	 end
	 else if (even_beat) begin // D, even beat
	    // Just save lower 32b; enq response only after next beat
	    rg_rd_data_lower32_ok <= ok;
	    rg_rd_data_lower32    <= data [31:0];
	    do_enq = False;
	 end
	 else begin // D, odd beat
	    ok   = (rg_rd_data_lower32_ok && ok);
	    data = { data [31:0], rg_rd_data_lower32 };
	    do_enq = True;
	 end
      end

      if (do_enq) begin
	 let rsp = Read_Data {ok: ok, data: data};
	 if (rg_rd_client_id == client_id_line)
	    f_line_read_data.enq (rsp);
	 else
	    f_single_read_data.enq (rsp);
      end

      if (verbosity >= 1) begin
	 $display ("%0d: %m.rl_read_data: ", cur_cycle);
	 if (rg_rd_client_id == client_id_line) $write ("    line (beat %0d)", rg_rd_beat);
	 else                                   $write ("     single");
	 $write (" ok %0d data %0h", pack (ok), data);
	 if ((valueOf (Wd_Data) == 32) && even_beat) $write (" (lower 32b of 64b)");
	 $display ("");
      end
   endrule: rl_read_data

   // ****************************************************************
   // BEHAVIOR: LINE READ REQUESTS

   rule rl_line_read_req (f_line_reqs.first.is_read
			  && rg_ddr4_ready
			  && (rg_rd_rsps_pending < '1)
			  && (rg_wr_rsps_pending == 0));
      let req <- pop (f_line_reqs);

      dynamicAssert (fv_is_line_aligned (req.addr), "rl_line_read_req addr misaligned");

      let addr = fv_align_to_line (req.addr);

      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (addr);
      AXI4_Size   fabric_size = ((valueOf (Wd_Data) == 32) ? axsize_4 : axsize_8);

      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      Integer   num_beats  = bytes_per_cline / bytes_per_fabric_data;
      AXI4_Len  fabric_len = fromInteger (num_beats - 1);

      let mem_req_rd_addr = AXI4_Rd_Addr {arid:     fabric_default_id,
					  araddr:   fabric_addr,
					  arlen:    fabric_len,
					  arsize:   fabric_size,
					  arburst:  axburst_incr,
					  arlock:   fabric_default_lock,
					  arcache:  fabric_default_arcache,
					  arprot:   fabric_default_prot,
					  arqos:    fabric_default_qos,
					  arregion: fabric_default_region,
					  aruser:   fabric_default_user};
      master_xactor.i_rd_addr.enq (mem_req_rd_addr);

      f_rd_rsp_control.enq (tuple3 (2'b11, 1'b0, fromInteger (num_beats)));
      rg_rd_client_id    <= client_id_line;
      rg_rd_beat         <= 0;
      rg_rd_rsps_pending <= rg_rd_rsps_pending + 1;

      // Debugging
      if (verbosity >= 1) begin
	 $display ("%0d: %m.rl_line_read_req: ", cur_cycle);
	 $display ("    AXI4_Rd_Addr{araddr %0h arlen %0d (burst len %0d) ",
		   fabric_addr,
		   fabric_len, num_beats,
		   fshow_AXI4_Size (fabric_size),
		   " incr}");
      end
   endrule

   // ****************************************************************
   // BEHAVIOR: Single read requests (not a burst)

   rule rl_single_read_req (f_single_reqs.first.is_read
			    && rg_ddr4_ready
			    && (rg_rd_rsps_pending < '1)
			    && (rg_wr_rsps_pending == 0));
      let         req        <- pop (f_single_reqs);
      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (req.addr);
      AXI4_Size   fabric_size = fv_size_code_to_AXI4_Size (req.size_code);

      if (verbosity >= 1)
	 $display ("%0d: %m.rl_single_read_req:\n    AXI4_Rd_Addr{araddr %0h arlen 0 (burst length 1) ",
		   cur_cycle, fabric_addr,  fshow_AXI4_Size (fabric_size), "}");

      Bit #(8)    num_beats   = 1;

      // FABRIC32 adjustments
      if ((valueOf (Wd_Data) == 32) && (req.size_code == 2'b11)) begin
	 fabric_size = axsize_4;
	 num_beats   = 2;
      end
      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      AXI4_Len fabric_len = num_beats - 1;

      let mem_req_rd_addr = AXI4_Rd_Addr {arid:     fabric_default_id,
					  araddr:   fabric_addr,
					  arlen:    0,           // burst len = arlen+1
					  arsize:   fabric_size,
					  arburst:  fabric_default_burst,
					  arlock:   fabric_default_lock,
					  arcache:  fabric_default_arcache,
					  arprot:   fabric_default_prot,
					  arqos:    fabric_default_qos,
					  arregion: fabric_default_region,
					  aruser:   fabric_default_user};
      master_xactor.i_rd_addr.enq (mem_req_rd_addr);

      f_rd_rsp_control.enq (tuple3 (req.size_code, req.addr [2], num_beats));
      rg_rd_client_id    <= client_id_single;
      rg_rd_beat         <= 0;
      rg_rd_rsps_pending <= rg_rd_rsps_pending + 1;
   endrule

   // ****************************************************************
   // BEHAVIOR: WRITE RESPONSES (for line and single clients)
   // The following register identifies the client

   rule rl_write_rsp;
      let wr_resp <- pop_o (master_xactor.o_wr_resp);

      Bool err = False;
      if (rg_wr_rsps_pending == 0) begin
	 rg_write_error <= True;

	 $display ("%0d: %m.rl_write_rsp: ERROR not expecting any write-response:", cur_cycle);
	 $display ("    ", fshow (wr_resp));
      end
      else begin
	 rg_wr_rsps_pending <= rg_wr_rsps_pending - 1;
	 if (wr_resp.bresp != axi4_resp_okay) begin
	    rg_write_error <= True;
	    if (verbosity >= 1) begin
	       $display ("%0d: %m.rl_write_rsp: FABRIC RESPONSE ERROR", cur_cycle);
	       $display ("    ", fshow (wr_resp));
	    end
	 end
	 else if (verbosity >= 1) begin
	    $display ("%0d: %m.rl_write_rsp: pending=%0d, ",
		      cur_cycle, rg_wr_rsps_pending, fshow (wr_resp));
	 end
      end
   endrule

   // ****************************************************************
   // BEHAVIOR: Write data (for both line and single)
   // Assume that data is already lane-aligned for 64b data width.

   FIFOF #(Tuple4 #(Bit #(1),     // client_id
		    Bit #(2),     // size_code
		    Bit #(3),     // addr lsbs
		    Bit #(8)))    // Num beats in write-data
         f_wr_data_control <- mkFIFOF;
   match {.wr_client_id,
	  .wr_req_size_code,
	  .wr_req_addr_lsbs,
	  .wr_req_beats } = f_wr_data_control.first;

   // Count beats in a single transaction
   // Note: beat-count is for fabric, not for client-side data.
   // Former is 2 x latter for Fabric32 for 64b line data and 64b single data.
   Reg #(Bit #(8)) rg_wr_beat <- mkReg (0);

   rule rl_write_data (rg_ddr4_ready && (rg_wr_beat < wr_req_beats));
      Bool last = (rg_wr_beat == (wr_req_beats - 1));
      if (last) begin
	 f_wr_data_control.deq;
	 rg_wr_beat <= 0;
      end
      else
	 rg_wr_beat <= rg_wr_beat + 1;

      Bit #(64) data = ((wr_client_id == client_id_line)
			? f_line_write_data.first
			: f_single_write_data.first);

      // Compute strobe from size and address
      Bit #(8)  strb = case (wr_req_size_code)
			  2'b00: 8'h_01;
			  2'b01: 8'h_03;
			  2'b10: 8'h_0F;
			  2'b11: 8'h_FF;
		       endcase;
      strb = (strb << wr_req_addr_lsbs);

      Bool do_deq    = True;
      Bool even_beat = (rg_wr_beat [0] == 1'b0);

      // FABRIC32 adjustments
      if (valueOf (Wd_Data) == 32) begin
	 if (wr_req_size_code != 2'b11) begin
	    // B, H, W: only 1 beat
	    Bool in_upper32 = (wr_req_addr_lsbs [2] == 1'b1);
	    if (in_upper32) begin
	       data = { 32'b0, data [63:32] };
	       strb = {  4'b0, strb [7:4] };
	    end
	    do_deq = True;
	 end
	 else if (even_beat) begin  // D, even beat
	    do_deq = False;
	 end
	 else begin // D, odd beat
	    data   = { 32'b0, data [63:32] };
	    strb   = '1;
	    do_deq = True;
	 end
      end

      let mem_req_wr_data = AXI4_Wr_Data {wdata:  truncate (data),
					  wstrb:  truncate (strb),
					  wlast:  last,
					  wuser:  fabric_default_user};
      master_xactor.i_wr_data.enq (mem_req_wr_data);

      if (do_deq) begin
	 if (wr_client_id == client_id_line)
	    f_line_write_data.deq;
	 else
	    f_single_write_data.deq;
      end

      if (verbosity >= 1) begin
	 $display ("%0d: %m.rl_write_data: beat %0d/%0d", cur_cycle, rg_wr_beat, wr_req_beats);
	 $display ("    AXI4_Wr_Data{%0h strb %0h last %0d}", data, strb, pack (last));
      end
   endrule

   // ****************************************************************
   // BEHAVIOR: Line-write requests

   rule rl_line_write_req ((! f_line_reqs.first.is_read)
			   && rg_ddr4_ready
			   && (rg_rd_rsps_pending == 0)
			   && (rg_wr_rsps_pending < '1));
      let req <- pop (f_line_reqs);

      dynamicAssert (fv_is_line_aligned (req.addr), "rl_line_write_req addr misaligned");

      let addr = fv_align_to_line (req.addr);

      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (addr);
      AXI4_Size   fabric_size = ((valueOf (Wd_Data) == 32) ? axsize_4 : axsize_8);

      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      Integer   num_beats  = bytes_per_cline / bytes_per_fabric_data;
      AXI4_Len  fabric_len = fromInteger (num_beats - 1);

      let mem_req_wr_addr = AXI4_Wr_Addr {awid:     fabric_default_id,
					  awaddr:   fabric_addr,
					  awlen:    fabric_len,
					  awsize:   fabric_size,
					  awburst:  axburst_incr,
					  awlock:   fabric_default_lock,
					  awcache:  fabric_default_awcache,
					  awprot:   fabric_default_prot,
					  awqos:    fabric_default_qos,
					  awregion: fabric_default_region,
					  awuser:   fabric_default_user};
      master_xactor.i_wr_addr.enq (mem_req_wr_addr);

      f_wr_data_control.enq (tuple4 (client_id_line,
				     2'b11,        // size_code D
				     0,            // req.addr lsbs
				     fromInteger (num_beats)));
      rg_wr_rsps_pending <= rg_wr_rsps_pending + 1;

      // Debugging
      if (verbosity >= 1)
	 $display ("%0d: %m.rl_line_write_req: AXI4_Wr_Addr{awaddr %0h awlen %0d burst-length %0d ",
		   cur_cycle,
		   fabric_addr,
		   fabric_len, num_beats,
		   fshow_AXI4_Size (fabric_size),
		   " incr}");
   endrule

   // ****************************************************************
   // Scheduling

   (* descending_urgency = "rl_read_data,       rl_write_rsp" *)
   (* descending_urgency = "rl_write_rsp,       rl_line_read_req" *)
   (* descending_urgency = "rl_line_read_req,   rl_line_write_req" *)
   (* descending_urgency = "rl_line_write_req,  rl_write_data" *)
   (* descending_urgency = "rl_write_data,      rl_single_read_req" *)
   (* descending_urgency = "rl_single_read_req, rl_single_write_req" *)

   // The following should be implied by transitivity, but bsc still cites them
   (* descending_urgency = "rl_read_data,       rl_line_read_req" *)
   (* descending_urgency = "rl_line_read_req,   rl_single_read_req" *)
   (* descending_urgency = "rl_line_write_req,  rl_single_read_req" *)
   (* descending_urgency = "rl_write_rsp,       rl_line_write_req" *)
   (* descending_urgency = "rl_line_write_req,  rl_single_write_req" *)
   (* descending_urgency = "rl_line_read_req,   rl_single_write_req" *)

   // ****************************************************************
   // BEHAVIOR: Single write requests (not a burst)

   rule rl_single_write_req ((! f_single_reqs.first.is_read)
			     && rg_ddr4_ready
			     && (rg_rd_rsps_pending == 0)
			     && (rg_wr_rsps_pending < '1));
      let req <- pop (f_single_reqs);

      Fabric_Addr fabric_addr = fv_Addr_to_Fabric_Addr (req.addr);
      AXI4_Size   fabric_size = fv_size_code_to_AXI4_Size (req.size_code);
      Bit #(8)    num_beats   = 1;

      // FABRIC32 adjustments
      if ((valueOf (Wd_Data) == 32) && (req.size_code == 2'b11)) begin
	 fabric_size = axsize_4;
	 num_beats   = 2;
      end
      // Note: AXI4 codes a burst length of 'n' as 'n-1'
      AXI4_Len fabric_len = num_beats - 1;

      // AXI4 Write-Address channel
      let mem_req_wr_addr = AXI4_Wr_Addr {awid:     fabric_default_id,
					  awaddr:   fabric_addr,
					  awlen:    fabric_len,
					  awsize:   fabric_size,
					  awburst:  fabric_default_burst,
					  awlock:   fabric_default_lock,
					  awcache:  fabric_default_awcache,
					  awprot:   fabric_default_prot,
					  awqos:    fabric_default_qos,
					  awregion: fabric_default_region,
					  awuser:   fabric_default_user};
      master_xactor.i_wr_addr.enq (mem_req_wr_addr);

      f_wr_data_control.enq (tuple4 (client_id_single,
				     req.size_code,
				     req.addr [2:0],
				     num_beats));
      rg_wr_rsps_pending <= rg_wr_rsps_pending + 1;

      // Debugging
      if (verbosity >= 1)
	 $display ("%0d: %m.rl_single_write_req: AXI4_Wr_Addr{awaddr %0h awlen %0d burst-length %0d ",
		   cur_cycle,
		   fabric_addr,
		   fabric_len, num_beats,
		   fshow_AXI4_Size (fabric_size),
		   " incr}");
   endrule

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

   // ----------------
   // interface for cache-line read/write client

   interface Put p_mem_line_req        = toPut (f_line_reqs);
   interface Put p_mem_line_write_data = toPut (f_line_write_data);
   interface Get g_mem_line_read_data  = toGet (f_line_read_data);

   // ----------------
   // interface for word/sub-word read/write client

   interface Put p_mem_single_req        = toPut (f_single_reqs);
   interface Put p_mem_single_write_data = toPut (f_single_write_data);
   interface Get g_mem_single_read_data  = toGet (f_single_read_data);

   // ----------------
   // Write-error from memory

   method Bool mv_write_error = rg_write_error;

   // ----------------
   // Fabric master interface
   interface mem_master = master_xactor.axi_side;

   // ----------------------------------------------------------------
   // Misc. control and status

   // Signal that DDR4 has been initialized and is ready to accept requests
   method Action ma_ddr4_ready;
      rg_ddr4_ready <= True;
      $display ("%0d: %0m: Enabling memory accesses", cur_cycle);
   endmethod

   // Misc. status; 0 = running, no error
   method Bit #(8) mv_status;
      return (rg_write_error ? 1 : 0);
   endmethod

endmodule

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

endpackage


changed ip to index mapping function

changed ip to index mapping function

import Vector::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import BRAM::*;
import GetPut::*;
import DefaultValue::*;
import Clocks::*;

import SchedulerTypes::*;
import RingBufferTypes::*;
import RingBuffer::*;
import MachineToPortMapping::*;
import GlobalClock::*;
import Addresses::*;

typedef struct {
	ReadResType data;
	PortIndex tx_port;
} DataToPutInTx deriving(Bits, Eq);

instance DefaultValue#(DataToPutInTx);
	defaultValue = DataToPutInTx {
					data : defaultValue,
					tx_port : fromInteger(valueof(NUM_OF_PORTS))
				};
endinstance

interface Scheduler#(type readReqType, type readResType,
                     type writeReqType, type writeResType);

    /* MAC interfaces */
    interface Put#(readReqType) mac_read_request_port_1;
    interface Put#(writeReqType) mac_write_request_port_1;
    interface Get#(readResType) mac_read_response_port_1;
    interface Get#(writeResType) mac_write_response_port_1;

    interface Put#(readReqType) mac_read_request_port_2;
    interface Put#(writeReqType) mac_write_request_port_2;
    interface Get#(readResType) mac_read_response_port_2;
    interface Get#(writeResType) mac_write_response_port_2;

    interface Put#(readReqType) mac_read_request_port_3;
    interface Put#(writeReqType) mac_write_request_port_3;
    interface Get#(readResType) mac_read_response_port_3;
    interface Get#(writeResType) mac_write_response_port_3;

    interface Put#(readReqType) mac_read_request_port_4;
    interface Put#(writeReqType) mac_write_request_port_4;
    interface Get#(readResType) mac_read_response_port_4;
    interface Get#(writeResType) mac_write_response_port_4;

    /* DMA simulator interface */
    interface Put#(readReqType) dma_read_request;
    interface Put#(writeReqType) dma_write_request;
    interface Get#(readResType) dma_read_response;
    interface Get#(writeResType) dma_write_response;

	interface Get#(Bit#(64)) time_slots_response;
	interface Get#(Bit#(64)) host_pkt_response;
	interface Get#(Bit#(64)) non_host_pkt_response;
	interface Get#(Bit#(64)) received_pkt_response;
	interface Get#(Bit#(64)) unknown_pkt_response;
//	interface Get#(RingBufferDataT) debug_consuming_pkt;

	method Action start(ServerIndex serverIdx);
	method Action stop();
	method Action insertToSchedTable(ServerIndex index, IP ip_addr, MAC mac_addr);
    method Action timeSlotsCount();
	method Action hostPktCount();
	method Action nonHostPktCount();
	method Action receivedPktCount();
	method Action unknownPktCount();
endinterface

(* synthesize *)
module mkScheduler#(Clock pcieClock, Reset pcieReset,
                    Clock txClock, Reset txReset,
                    Clock rxClock, Reset rxReset)
				(Scheduler#(ReadReqType, ReadResType, WriteReqType, WriteResType));

    Reg#(Bool) verbose <- mkReg(False);

    GlobalClock clk <- mkGlobalClock;

	Reg#(ServerIndex) host_index <- mkReg(0);

    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();

    Vector#(NUM_OF_PORTS, FIFO#(ReadReqType)) mac_read_request_fifo
            <- replicateM(mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN))));
    Vector#(NUM_OF_PORTS, FIFO#(ReadResType)) mac_read_response_fifo
            <- replicateM(mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN))));
    Vector#(NUM_OF_PORTS, SyncFIFOIfc#(WriteReqType)) mac_write_request_fifo
            <- replicateM(mkSyncFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)),
			                               rxClock, rxReset, defaultClock));
    Vector#(NUM_OF_PORTS, SyncFIFOIfc#(WriteResType)) mac_write_response_fifo
            <- replicateM(mkSyncFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)),
			                               rxClock, rxReset, defaultClock));

    FIFO#(ReadReqType) dma_read_request_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));
    FIFO#(ReadResType) dma_read_response_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));
    FIFO#(WriteReqType) dma_write_request_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));
    FIFO#(WriteResType) dma_write_response_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));

    Reg#(State) curr_state <- mkReg(CONFIG);

	Vector#(NUM_OF_SERVERS, Reg#(TableData)) sched_table
	                                    <- replicateM(mkReg(defaultValue));
/*------------------------------------------------------------------------------*/
    Vector#(NUM_OF_PORTS,
            RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType))
    rx_ring_buffer <- replicateM
                      (mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE))));

    Vector#(NUM_OF_PORTS,
            RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType))
    tx_ring_buffer <- replicateM
                      (mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE))));

    Vector#(NUM_OF_SERVERS,
            RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType))
    ring_buffer <- replicateM
                   (mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE))));

    RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType)
        src_rx_ring_buffer <- mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE)));

    Vector#(NUM_OF_SERVERS, Reg#(ServerIndex)) schedule_list <- replicateM(mkReg(0));

    /* Flags */
    Reg#(Bit#(1)) configure <- mkReg(0);
    Reg#(Bit#(1)) start_scheduling_flag <- mkReg(0);
    Reg#(Bit#(1)) start_polling_rx_buffer <- mkReg(0);
	Reg#(Bit#(1)) start_tx_scheduling <- mkReg(0);

    /* Stats */
	SyncFIFOIfc#(Bit#(64)) time_slots_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) host_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) non_host_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) received_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) unknown_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	Reg#(Bit#(64)) num_of_time_slots_used_reg <- mkReg(0);
	Reg#(Bit#(64)) host_pkt_transmitted_reg <- mkReg(0);
	Reg#(Bit#(64)) non_host_pkt_transmitted_reg <- mkReg(0);
	Vector#(NUM_OF_PORTS, Reg#(Bit#(64))) num_of_pkt_received_reg
	                                     <- replicateM(mkReg(0));
	Vector#(NUM_OF_PORTS, Reg#(Bit#(64))) num_of_unknown_pkt_reg
	                                     <- replicateM(mkReg(0));

//	SyncFIFOIfc#(RingBufferDataT) debug_consuming_pkt_fifo
//	        <- mkSyncFIFO(16, defaultClock, defaultReset, pcieClock);

    /*
    * Here the assumption is that since it is our own
    * network, so we assign the IP addresses to the
    * machines in such a way that the mapping IP -> index
    * is trivial. For eg. currently I am assuming that the
    * IP addresses have been assigned such as the mapping
    * is index = least significant byte of IP address - 1.
    * So the IP addrs may be like 192.168.0.1, 192.168.0.2,
    * 192.168.0.3 etc. Note that this is just to make processing
    * faster and saving memory resources. If this approach is not
    * viable in a particular setting, then we can rely back on
    * storing the mapping in a table in BRAM memory, and traversing
    * the table every time we need to know the index of a ring
    * buffer.
    */
    function ServerIndex ipToIndexMapping (IP ip_addr);
		ServerIndex index = 0;
		Bit#(24) msb = ip_addr[31:8];

        if (ip_addr == ip_address(host_index))
            index = 0;

		else if (msb == 'hc0a800 && ip_addr != 'hc0a80000)
		begin
			index = truncate(ip_addr[7:0]) - 1;
			if (index < truncate((ip_address(host_index))[7:0]))
				index = index + 1;
		end

        else
			index = fromInteger(valueof(NUM_OF_SERVERS));

        return index;
    endfunction

    /*
    * 0th entry in the schedule table will always contain the info of the
    * host server. Entries 1 to NUM_OF_SERVERS-1 will contain info of the
    * remaining servers. As there will be a total of NUM_OF_SERVERS-1 time
    * slots, so the size of schedule_list is NUM_OF_SERVERS-1.
    *
    * schedule_list[i] = j means in time slot i, send pkt to server at index
    * j in the schedule table.
    */
    rule configure_scheduling (curr_state == RUN && configure == 1);
        configure <= 0;
        for (Integer i = 0; i < fromInteger(valueof(NUM_OF_SERVERS))-1; i = i + 1)
        begin
            schedule_list[i] <= fromInteger(i) + 1;
            if (verbose)
            $display("[SCHED (%d)] schedule_list[%d] = %d", host_index, i, i+1);
        end
        start_scheduling_flag <= 1;
    endrule

    rule get_host_mac_addr (curr_state == RUN && start_scheduling_flag == 1);
        start_scheduling_flag <= 0;
        start_polling_rx_buffer <= 1;
		start_tx_scheduling <= 1;
    endrule

/*------------------------------------------------------------------------------*/

	// should be atleast as large as buffer_depth + max num of data blocks
    Vector#(NUM_OF_PORTS, FIFOF#(RingBufferDataT)) buffer_fifo
	        <- replicateM(mkSizedFIFOF(fromInteger(valueof(DEFAULT_FIFO_LEN))));
    Vector#(NUM_OF_PORTS, FIFOF#(ServerIndex)) ring_buffer_index_fifo
              <- replicateM(mkSizedFIFOF(fromInteger(valueof(NUM_OF_PORTS))+1));
    Vector#(NUM_OF_PORTS, Reg#(Bit#(1)))
                 ready_to_deq_from_index_buffer <- replicateM(mkReg(1));
    Vector#(NUM_OF_PORTS, Reg#(Bit#(1)))
                  ready_to_deq_from_ring_buffer <- replicateM(mkReg(0));

    /* Stores the number of data blocks to buffer */
    Vector#(NUM_OF_PORTS, Reg#(int)) buffer_depth <- replicateM(mkReg(3));

    /* Have to buffer first 3 data blocks to get to dst IP addr
    *
    * Assumption here is that the MAC Frame structure is as shown
    *  ------------------------------------------------------
    * | dst MAC | src MAC | ether type | IP header | Payload |
    *  ------------------------------------------------------
    * So, no VLAN tags etc.
    */
    Vector#(NUM_OF_PORTS, Reg#(Bit#(384))) buffered_data <- replicateM(mkReg(0));

    Vector#(NUM_OF_PORTS, Reg#(Bit#(1))) stop_polling <- replicateM(mkReg(0));

    Vector#(NUM_OF_PORTS, Reg#(ServerIndex)) curr_ring_buffer_index
                        <- replicateM(mkReg(fromInteger(valueof(NUM_OF_SERVERS))));

    Vector#(NUM_OF_SERVERS, FIFOF#(PortIndex))
    token_queue <- replicateM(mkSizedFIFOF(fromInteger(valueof(NUM_OF_SERVERS))));

    for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
    begin
        rule start_polling_rx (curr_state == RUN && start_polling_rx_buffer == 1
                               && stop_polling[i] == 0);
            let ring_buf_empty <- rx_ring_buffer[i].empty;

            if (!ring_buf_empty)
            begin
				num_of_pkt_received_reg[i] <= num_of_pkt_received_reg[i] + 1;
                rx_ring_buffer[i].read_request.put(makeReadReq(READ));
				stop_polling[i] <= 1;
            end
        endrule

        rule buffer_and_parse_incoming_data (curr_state == RUN);
            let d <- rx_ring_buffer[i].read_response.get;

            if (d.data.sop == 1 && d.data.eop == 0)
            begin
                Bit#(384) pload = zeroExtend(d.data.payload);
				buffered_data[i] <= buffered_data[i] |
				(pload << (fromInteger(valueof(BUS_WIDTH)) * (buffer_depth[i]-1)));
                buffer_depth[i] <= buffer_depth[i] - 1;
                buffer_fifo[i].enq(d.data);
            end

            else
            begin
                if (buffer_depth[i] > 0)
                begin
                    buffer_depth[i] <= buffer_depth[i] - 1;
                    Bit#(384) pload = zeroExtend(d.data.payload);
                    buffered_data[i] <= buffered_data[i] | (pload
					<< (fromInteger(valueof(BUS_WIDTH)) * (buffer_depth[i]-1)));
                end

                else if (buffer_depth[i] == 0) /* already buffered 3 data blocks */
                begin
                    if (d.data.sop == 0 && d.data.eop == 1)
                    begin
                        /* reset state */
                        buffer_depth[i] <= 3;  //no of data blocks to buffer
                        buffered_data[i] <= 0;
                    end
                    else
                        buffer_depth[i] <= buffer_depth[i] - 1;

                    /* Find the index of the ring buffer to insert to. */
                    Bit#(32) dst_ip = (buffered_data[i])[143:112]; /* dst IP */

                    if (dst_ip == ip_address(host_index))
                        ring_buffer_index_fifo[i].enq(0);
                    else
                    begin
                        ServerIndex index = ipToIndexMapping(dst_ip);
						if (index >= fromInteger(valueof(NUM_OF_SERVERS)))
                        begin
                            ring_buffer_index_fifo[i].enq(0);
							num_of_unknown_pkt_reg[i] <=
							               num_of_unknown_pkt_reg[i] + 1;
                        end
                        else
                            ring_buffer_index_fifo[i].enq(index);

                        if (verbose)
                            $display("[SCHED (%d)] Adding idx = %d to idx fifo %d",
                                     host_index, index, i);
                    end
                end

                else if (d.data.sop == 0 && d.data.eop == 1)
                begin
                    /* reset state */
                    buffer_depth[i] <= 3;  //no of data blocks to buffer
                    buffered_data[i] <= 0;
                end

                buffer_fifo[i].enq(d.data);
            end
        endrule

        rule extract_ring_buffer_index_to_add_to
                    (curr_state == RUN && ready_to_deq_from_index_buffer[i] == 1);
            let index <- toGet(ring_buffer_index_fifo[i]).get;
            curr_ring_buffer_index[i] <= index;
            ready_to_deq_from_index_buffer[i] <= 0;
            ready_to_deq_from_ring_buffer[i] <= 1;
        endrule

        for (Integer j = 0; j < fromInteger(valueof(NUM_OF_SERVERS)); j = j + 1)
        begin
            rule enq_to_token_queue (curr_state == RUN
                                  && curr_ring_buffer_index[i] == fromInteger(j));
                token_queue[j].enq(fromInteger(i));
                if (verbose)
                $display("[SCHED (%d)] token_queue[%d].enq(%d)", host_index, j, i);
                curr_ring_buffer_index[i] <= fromInteger(valueof(NUM_OF_SERVERS));
            endrule
        end
    end

    Vector#(NUM_OF_SERVERS, Reg#(PortIndex))
         port_idx <- replicateM(mkReg(fromInteger(valueof(NUM_OF_PORTS))));

	Vector#(NUM_OF_SERVERS, Reg#(Bit#(1))) wait_for_completion
	                                            <- replicateM(mkReg(0));

    for (Integer j = 0; j < fromInteger(valueof(NUM_OF_SERVERS)); j = j + 1)
    begin
        rule deq_from_token_queue (curr_state == RUN
			                       && wait_for_completion[j] == 0);
            let port_index <- toGet(token_queue[j]).get;
            port_idx[j] <= port_index;
            if (verbose)
                $display("[SCHED (%d)] port_idx[%d] = %d",host_index,j,port_index);
            stop_polling[port_index] <= 0;
			wait_for_completion[j] <= 1;
        endrule

        for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
        begin
            rule add_to_correct_ring_buffer (curr_state == RUN
                                        && port_idx[j] == fromInteger(i)
                                        && ready_to_deq_from_ring_buffer[i] == 1);
                let d <- toGet(buffer_fifo[i]).get;

                if (d.sop == 0 && d.eop == 1)
                begin
                    ready_to_deq_from_index_buffer[i] <= 1;
                    ready_to_deq_from_ring_buffer[i] <= 0;
					wait_for_completion[j] <= 0;
                    port_idx[j] <= fromInteger(valueof(NUM_OF_PORTS));
                end

                if (j != 0)
                    ring_buffer[j].write_request.put
                                    (makeWriteReq(d.sop, d.eop, d.payload));
                else
                    src_rx_ring_buffer.write_request.put
                                    (makeWriteReq(d.sop, d.eop, d.payload));
                if (verbose)
                    $display("[SCHED (%d)] CLK = %d buffer index to put data = %d data = %d %d %x", host_index, clk.currTime(), j, d.sop, d.eop, d.payload);
            endrule
        end
    end

/*-------------------------------------------------------------------------------*/
    Reg#(MAC) dst_mac_addr <- mkReg(0);

    rule get_dst_addr (curr_state == RUN && start_tx_scheduling == 1);
		Bit#(64) curr_time = clk.currTime();

		Bit#(3) clock_lsb_four_bits = curr_time[2:0];
		if (clock_lsb_four_bits == 0)
		begin
			ServerIndex slot = 0;
            if (fromInteger(valueof(NUM_OF_SERVERS)) == 1)
                slot = fromInteger(valueof(NUM_OF_SERVERS))-1;
            else if (fromInteger(valueof(NUM_OF_SERVERS)) == 2)
                slot = 0;
            else
			    slot = curr_time[6:3] %
			                   (fromInteger(valueof(NUM_OF_SERVERS))-1);

			if (verbose)
            $display("[SCHED (%d)] CLK = %d  schedule_list[%d] = %d", host_index,
                            curr_time, slot, schedule_list[slot]);

            num_of_time_slots_used_reg <= num_of_time_slots_used_reg + 1;

			/* Get the dst mac and ip addr */
			ServerIndex table_index = schedule_list[slot];
			dst_mac_addr <= sched_table[table_index].server_mac;
			IP dst_ip_addr = sched_table[table_index].server_ip;

			if (verbose)
				$display("[SCHED (%d)] CLK = %d MAC = %x IP = %x", host_index,
				clk.currTime(), sched_table[table_index].server_mac,
			    dst_ip_addr);

			/* Get the index of the ring buffer to extract from */
			ServerIndex index = ipToIndexMapping(dst_ip_addr);

			if (verbose)
			$display("[SCHED (%d)] CLK = %d buffer index to extract from = %d %d",
				host_index, clk.currTime(), index, ring_buffer[index].elements);

			Bool is_empty <- ring_buffer[index].empty;

			/*
			 * Only if the forwarding ring buffer is empty, extract packet from
			 * the host tx buffer.
			 */
			if (!is_empty)
				ring_buffer[index].read_request.put(makeReadReq(READ));
			else
			begin
				if (verbose)
				begin
				$display("[SCHED (%d)] CLK = %d Empty ring; extract from host tx",
													  host_index, clk.currTime());
				$display("[SCHED (%d)] Host tx buffer size = %d", host_index,
													  ring_buffer[0].elements);
				end
				ring_buffer[0].read_request.put(makeReadReq(READ));
			end
		end
    endrule

    Vector#(NUM_OF_SERVERS, FIFO#(DataToPutInTx)) data_to_put
	            <- replicateM(mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN))));
	Vector#(NUM_OF_SERVERS, Reg#(PortIndex)) tx_port_index <- replicateM(mkReg(0));

    Vector#(NUM_OF_SERVERS, FIFO#(ReadResType)) data_fifo
	                                           <- replicateM(mkBypassFIFO);
	Vector#(NUM_OF_SERVERS, Wire#(PortIndex)) correct_tx_index
	                <- replicateM(mkDWire(fromInteger(valueof(NUM_OF_PORTS))));

    for (Integer i = 0; i < fromInteger(valueof(NUM_OF_SERVERS)); i = i + 1)
    begin
        rule modify_mac_headers_and_put_to_tx (curr_state == RUN);
            let d <- ring_buffer[i].read_response.get;

            if (d.data.sop == 1 && d.data.eop == 0)
            begin
                /* Update MAC header */
                Bit#(96) new_addr = {dst_mac_addr, mac_address(host_index)};
                Bit#(96) zero = 0;
                Bit#(BUS_WIDTH) temp = {zero, '1};
				Bit#(BUS_WIDTH) new_addr_temp = {new_addr, '0};
                d.data.payload = (d.data.payload & temp) | new_addr_temp;
                PortIndex index = machineToPortMapping(host_index, dst_mac_addr);
				tx_port_index[i] <= index;
                if (i == 0)
                    host_pkt_transmitted_reg <= host_pkt_transmitted_reg + 1;
                else
                    non_host_pkt_transmitted_reg <=
					                        non_host_pkt_transmitted_reg + 1;

				DataToPutInTx x = DataToPutInTx {
									data : d,
									tx_port : index
								};
				data_to_put[i].enq(x);
            end

			else
			begin
				DataToPutInTx x = DataToPutInTx {
									data : d,
									tx_port : tx_port_index[i]
								};
				data_to_put[i].enq(x);
			end

            if (verbose)
                $display("[SCHED (%d)] CLK = %d", host_index, clk.currTime());
        endrule

		rule set_correct_tx_index (curr_state == RUN);
			let res <- toGet(data_to_put[i]).get;
			ReadResType d = res.data;
			PortIndex idx = res.tx_port;
			data_fifo[i].enq(d);
			correct_tx_index[i] <= idx;
		if (verbose)
		$display("[SCHED (%d)] CLK = %d data written to tx %d data %d %d %x",
		host_index, clk.currTime(), idx, d.data.sop, d.data.eop, d.data.payload);
		endrule

		for (Integer j = 0; j < fromInteger(valueof(NUM_OF_PORTS)); j = j + 1)
		begin
			rule add_to_correct_tx (correct_tx_index[i] == fromInteger(j));
				let d <- toGet(data_fifo[i]).get;
				tx_ring_buffer[j].write_request.put
					(makeWriteReq(d.data.sop, d.data.eop, d.data.payload));
			endrule
		end
    end

/*-------------------------------------------------------------------------------*/
    for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
    begin
        rule handle_rx_buffer_write_req_from_mac;
            let req <- toGet(mac_write_request_fifo[i]).get;

            if (verbose)
                $display("[SCHED (%d)] CLK = %d Putting data into rx port buffer %d %d %x i = %d",
                host_index, clk.currTime(), req.data.sop, req.data.eop, req.data.payload, i);

            rx_ring_buffer[i].write_request.put
                  (makeWriteReq(req.data.sop, req.data.eop, req.data.payload));
        endrule

        rule handle_tx_buffer_read_req_from_mac;
            let req <- toGet(mac_read_request_fifo[i]).get;
            tx_ring_buffer[i].read_request.put(makeReadReq(req.op));
        endrule

        rule send_tx_buffer_read_res_to_mac;
            let d <- tx_ring_buffer[i].read_response.get;
            mac_read_response_fifo[i].enq(makeReadRes(d.data));
        endrule
    end

    rule handle_dma_simulator_write_req;
        let req <- toGet(dma_write_request_fifo).get;

        if (verbose)
            $display("[SCHED (%d)] Putting data into host tx buffer %d %d %x",
                        host_index, req.data.sop, req.data.eop, req.data.payload);

        ring_buffer[0].write_request.put
            (makeWriteReq(req.data.sop, req.data.eop, req.data.payload));
    endrule

    rule handle_dma_simulator_read_req;
        let req <- toGet(dma_read_request_fifo).get;
        src_rx_ring_buffer.read_request.put(makeReadReq(READ));
    endrule

    rule consume_pkt_in_response_to_dma_simulator_read_req;
        let d <- src_rx_ring_buffer.read_response.get;
		//debug_consuming_pkt_fifo.enq(d.data);
    endrule

/*-----------------------------------------------------------------------------*/
	method Action insertToSchedTable(ServerIndex index, IP ip_addr, MAC mac_addr);
		TableData d = TableData {
						server_ip : ip_addr,
						server_mac : mac_addr
		              };
		sched_table[index] <= d;
	endmethod

    method Action timeSlotsCount();
		time_slots_fifo.enq(num_of_time_slots_used_reg);
    endmethod

	method Action hostPktCount();
		host_pkt_fifo.enq(host_pkt_transmitted_reg);
	endmethod

	method Action nonHostPktCount();
		non_host_pkt_fifo.enq(non_host_pkt_transmitted_reg);
	endmethod

	method Action receivedPktCount();
		Bit#(64) pkt_received = 0;
		for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
			pkt_received = pkt_received + num_of_pkt_received_reg[i];
		received_pkt_fifo.enq(pkt_received);
	endmethod

	method Action unknownPktCount();
		Bit#(64) unknown_pkt = 0;
		for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
			unknown_pkt = unknown_pkt + num_of_unknown_pkt_reg[i];
		unknown_pkt_fifo.enq(unknown_pkt);
	endmethod

	method Action start(ServerIndex serverIdx);
		curr_state <= RUN;
	    configure <= 1;
        start_scheduling_flag <= 0;
        start_polling_rx_buffer <= 0;
        start_tx_scheduling <= 0;
		host_index <= serverIdx;
	endmethod

	method Action stop();
		curr_state <= CONFIG;
	endmethod

    /* MAC interfaces */
    interface Put mac_read_request_port_1 = toPut(mac_read_request_fifo[0]);
    interface Put mac_write_request_port_1 = toPut(mac_write_request_fifo[0]);
    interface Get mac_read_response_port_1 = toGet(mac_read_response_fifo[0]);
    interface Get mac_write_response_port_1 = toGet(mac_write_response_fifo[0]);

    interface Put mac_read_request_port_2 = toPut(mac_read_request_fifo[1]);
    interface Put mac_write_request_port_2 = toPut(mac_write_request_fifo[1]);
    interface Get mac_read_response_port_2 = toGet(mac_read_response_fifo[1]);
    interface Get mac_write_response_port_2 = toGet(mac_write_response_fifo[1]);

    interface Put mac_read_request_port_3 = toPut(mac_read_request_fifo[2]);
    interface Put mac_write_request_port_3 = toPut(mac_write_request_fifo[2]);
    interface Get mac_read_response_port_3 = toGet(mac_read_response_fifo[2]);
    interface Get mac_write_response_port_3 = toGet(mac_write_response_fifo[2]);

    interface Put mac_read_request_port_4 = toPut(mac_read_request_fifo[3]);
    interface Put mac_write_request_port_4 = toPut(mac_write_request_fifo[3]);
    interface Get mac_read_response_port_4 = toGet(mac_read_response_fifo[3]);
    interface Get mac_write_response_port_4 = toGet(mac_write_response_fifo[3]);

    /* DMA simulator interface */
    interface Put dma_read_request = toPut(dma_read_request_fifo);
    interface Put dma_write_request = toPut(dma_write_request_fifo);
    interface Get dma_read_response = toGet(dma_read_response_fifo);
    interface Get dma_write_response = toGet(dma_write_response_fifo);

	interface Get time_slots_response = toGet(time_slots_fifo);
	interface Get host_pkt_response = toGet(host_pkt_fifo);
	interface Get non_host_pkt_response = toGet(non_host_pkt_fifo);
	interface Get received_pkt_response = toGet(received_pkt_fifo);
	interface Get unknown_pkt_response = toGet(unknown_pkt_fifo);
//	interface Get debug_consuming_pkt = toGet(debug_consuming_pkt_fifo);

endmodule

import Vector::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import BRAM::*;
import GetPut::*;
import DefaultValue::*;
import Clocks::*;

import SchedulerTypes::*;
import RingBufferTypes::*;
import RingBuffer::*;
import MachineToPortMapping::*;
import GlobalClock::*;
import Addresses::*;

typedef struct {
	ReadResType data;
	PortIndex tx_port;
} DataToPutInTx deriving(Bits, Eq);

instance DefaultValue#(DataToPutInTx);
	defaultValue = DataToPutInTx {
					data : defaultValue,
					tx_port : fromInteger(valueof(NUM_OF_PORTS))
				};
endinstance

interface Scheduler#(type readReqType, type readResType,
                     type writeReqType, type writeResType);

    /* MAC interfaces */
    interface Put#(readReqType) mac_read_request_port_1;
    interface Put#(writeReqType) mac_write_request_port_1;
    interface Get#(readResType) mac_read_response_port_1;
    interface Get#(writeResType) mac_write_response_port_1;

    interface Put#(readReqType) mac_read_request_port_2;
    interface Put#(writeReqType) mac_write_request_port_2;
    interface Get#(readResType) mac_read_response_port_2;
    interface Get#(writeResType) mac_write_response_port_2;

    interface Put#(readReqType) mac_read_request_port_3;
    interface Put#(writeReqType) mac_write_request_port_3;
    interface Get#(readResType) mac_read_response_port_3;
    interface Get#(writeResType) mac_write_response_port_3;

    interface Put#(readReqType) mac_read_request_port_4;
    interface Put#(writeReqType) mac_write_request_port_4;
    interface Get#(readResType) mac_read_response_port_4;
    interface Get#(writeResType) mac_write_response_port_4;

    /* DMA simulator interface */
    interface Put#(readReqType) dma_read_request;
    interface Put#(writeReqType) dma_write_request;
    interface Get#(readResType) dma_read_response;
    interface Get#(writeResType) dma_write_response;

	interface Get#(Bit#(64)) time_slots_response;
	interface Get#(Bit#(64)) host_pkt_response;
	interface Get#(Bit#(64)) non_host_pkt_response;
	interface Get#(Bit#(64)) received_pkt_response;
	interface Get#(Bit#(64)) unknown_pkt_response;
//	interface Get#(RingBufferDataT) debug_consuming_pkt;

	method Action start(ServerIndex serverIdx);
	method Action stop();
	method Action insertToSchedTable(ServerIndex index, IP ip_addr, MAC mac_addr);
    method Action timeSlotsCount();
	method Action hostPktCount();
	method Action nonHostPktCount();
	method Action receivedPktCount();
	method Action unknownPktCount();
endinterface

(* synthesize *)
module mkScheduler#(Clock pcieClock, Reset pcieReset,
                    Clock txClock, Reset txReset,
                    Clock rxClock, Reset rxReset)
				(Scheduler#(ReadReqType, ReadResType, WriteReqType, WriteResType));

    Reg#(Bool) verbose <- mkReg(False);

    GlobalClock clk <- mkGlobalClock;

	Reg#(ServerIndex) host_index <- mkReg(0);

    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();

    Vector#(NUM_OF_PORTS, FIFO#(ReadReqType)) mac_read_request_fifo
            <- replicateM(mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN))));
    Vector#(NUM_OF_PORTS, FIFO#(ReadResType)) mac_read_response_fifo
            <- replicateM(mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN))));
    Vector#(NUM_OF_PORTS, SyncFIFOIfc#(WriteReqType)) mac_write_request_fifo
            <- replicateM(mkSyncFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)),
			                               rxClock, rxReset, defaultClock));
    Vector#(NUM_OF_PORTS, SyncFIFOIfc#(WriteResType)) mac_write_response_fifo
            <- replicateM(mkSyncFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)),
			                               rxClock, rxReset, defaultClock));

    FIFO#(ReadReqType) dma_read_request_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));
    FIFO#(ReadResType) dma_read_response_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));
    FIFO#(WriteReqType) dma_write_request_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));
    FIFO#(WriteResType) dma_write_response_fifo
	                 <- mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN)));

    Reg#(State) curr_state <- mkReg(CONFIG);

	Vector#(NUM_OF_SERVERS, Reg#(TableData)) sched_table
	                                    <- replicateM(mkReg(defaultValue));
/*------------------------------------------------------------------------------*/
    Vector#(NUM_OF_PORTS,
            RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType))
    rx_ring_buffer <- replicateM
                      (mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE))));

    Vector#(NUM_OF_PORTS,
            RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType))
    tx_ring_buffer <- replicateM
                      (mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE))));

    Vector#(NUM_OF_SERVERS,
            RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType))
    ring_buffer <- replicateM
                   (mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE))));

    RingBuffer#(ReadReqType, ReadResType, WriteReqType, WriteResType)
        src_rx_ring_buffer <- mkRingBuffer(fromInteger(valueof(RING_BUFFER_SIZE)));

    Vector#(NUM_OF_SERVERS, Reg#(ServerIndex)) schedule_list <- replicateM(mkReg(0));

    /* Flags */
    Reg#(Bit#(1)) configure <- mkReg(0);
    Reg#(Bit#(1)) start_scheduling_flag <- mkReg(0);
    Reg#(Bit#(1)) start_polling_rx_buffer <- mkReg(0);
	Reg#(Bit#(1)) start_tx_scheduling <- mkReg(0);

    /* Stats */
	SyncFIFOIfc#(Bit#(64)) time_slots_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) host_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) non_host_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) received_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	SyncFIFOIfc#(Bit#(64)) unknown_pkt_fifo
	        <- mkSyncFIFO(1, defaultClock, defaultReset, pcieClock);
	Reg#(Bit#(64)) num_of_time_slots_used_reg <- mkReg(0);
	Reg#(Bit#(64)) host_pkt_transmitted_reg <- mkReg(0);
	Reg#(Bit#(64)) non_host_pkt_transmitted_reg <- mkReg(0);
	Vector#(NUM_OF_PORTS, Reg#(Bit#(64))) num_of_pkt_received_reg
	                                     <- replicateM(mkReg(0));
	Vector#(NUM_OF_PORTS, Reg#(Bit#(64))) num_of_unknown_pkt_reg
	                                     <- replicateM(mkReg(0));

//	SyncFIFOIfc#(RingBufferDataT) debug_consuming_pkt_fifo
//	        <- mkSyncFIFO(16, defaultClock, defaultReset, pcieClock);

    /*
    * Here the assumption is that since it is our own
    * network, so we assign the IP addresses to the
    * machines in such a way that the mapping IP -> index
    * is trivial. For eg. currently I am assuming that the
    * IP addresses have been assigned such as the mapping
    * is index = least significant byte of IP address - 1.
    * So the IP addrs may be like 192.168.0.1, 192.168.0.2,
    * 192.168.0.3 etc. Note that this is just to make processing
    * faster and saving memory resources. If this approach is not
    * viable in a particular setting, then we can rely back on
    * storing the mapping in a table in BRAM memory, and traversing
    * the table every time we need to know the index of a ring
    * buffer.
    */
    function ServerIndex ipToIndexMapping (IP ip_addr);
		ServerIndex index = 0;
		Bit#(24) msb = ip_addr[31:8];
		if (msb == 'hc0a800)
		begin
			index = truncate(ip_addr[7:0]) - 1;
			if (index < truncate((ip_address(host_index))[7:0]))
				index = index + 1;
		end
		if (index >= fromInteger(valueof(NUM_OF_SERVERS)))
			index = 0;
        return index;
    endfunction

    /*
    * 0th entry in the schedule table will always contain the info of the
    * host server. Entries 1 to NUM_OF_SERVERS-1 will contain info of the
    * remaining servers. As there will be a total of NUM_OF_SERVERS-1 time
    * slots, so the size of schedule_list is NUM_OF_SERVERS-1.
    *
    * schedule_list[i] = j means in time slot i, send pkt to server at index
    * j in the schedule table.
    */
    rule configure_scheduling (curr_state == RUN && configure == 1);
        configure <= 0;
        for (Integer i = 0; i < fromInteger(valueof(NUM_OF_SERVERS))-1; i = i + 1)
        begin
            schedule_list[i] <= fromInteger(i) + 1;
            if (verbose)
            $display("[SCHED (%d)] schedule_list[%d] = %d", host_index, i, i+1);
        end
        start_scheduling_flag <= 1;
    endrule

    rule get_host_mac_addr (curr_state == RUN && start_scheduling_flag == 1);
        start_scheduling_flag <= 0;
        start_polling_rx_buffer <= 1;
		start_tx_scheduling <= 1;
    endrule

/*------------------------------------------------------------------------------*/

	// should be atleast as large as buffer_depth + max num of data blocks
    Vector#(NUM_OF_PORTS, FIFOF#(RingBufferDataT)) buffer_fifo
	        <- replicateM(mkSizedFIFOF(fromInteger(valueof(DEFAULT_FIFO_LEN))));
    Vector#(NUM_OF_PORTS, FIFOF#(ServerIndex)) ring_buffer_index_fifo
              <- replicateM(mkSizedFIFOF(fromInteger(valueof(NUM_OF_PORTS))+1));
    Vector#(NUM_OF_PORTS, Reg#(Bit#(1)))
                 ready_to_deq_from_index_buffer <- replicateM(mkReg(1));
    Vector#(NUM_OF_PORTS, Reg#(Bit#(1)))
                  ready_to_deq_from_ring_buffer <- replicateM(mkReg(0));

    /* Stores the number of data blocks to buffer */
    Vector#(NUM_OF_PORTS, Reg#(int)) buffer_depth <- replicateM(mkReg(3));

    /* Have to buffer first 3 data blocks to get to dst IP addr
    *
    * Assumption here is that the MAC Frame structure is as shown
    *  ------------------------------------------------------
    * | dst MAC | src MAC | ether type | IP header | Payload |
    *  ------------------------------------------------------
    * So, no VLAN tags etc.
    */
    Vector#(NUM_OF_PORTS, Reg#(Bit#(384))) buffered_data <- replicateM(mkReg(0));

    Vector#(NUM_OF_PORTS, Reg#(Bit#(1))) stop_polling <- replicateM(mkReg(0));

    Vector#(NUM_OF_PORTS, Reg#(ServerIndex)) curr_ring_buffer_index
                        <- replicateM(mkReg(fromInteger(valueof(NUM_OF_SERVERS))));

    Vector#(NUM_OF_SERVERS, FIFOF#(PortIndex))
    token_queue <- replicateM(mkSizedFIFOF(fromInteger(valueof(NUM_OF_SERVERS))));

    for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
    begin
        rule start_polling_rx (curr_state == RUN && start_polling_rx_buffer == 1
                               && stop_polling[i] == 0);
            let ring_buf_empty <- rx_ring_buffer[i].empty;

            if (!ring_buf_empty)
            begin
				num_of_pkt_received_reg[i] <= num_of_pkt_received_reg[i] + 1;
                rx_ring_buffer[i].read_request.put(makeReadReq(READ));
				stop_polling[i] <= 1;
            end
        endrule

        rule buffer_and_parse_incoming_data (curr_state == RUN);
            let d <- rx_ring_buffer[i].read_response.get;

            if (d.data.sop == 1 && d.data.eop == 0)
            begin
                Bit#(384) pload = zeroExtend(d.data.payload);
				buffered_data[i] <= buffered_data[i] |
				(pload << (fromInteger(valueof(BUS_WIDTH)) * (buffer_depth[i]-1)));
                buffer_depth[i] <= buffer_depth[i] - 1;
                buffer_fifo[i].enq(d.data);
            end

            else
            begin
                if (buffer_depth[i] > 0)
                begin
                    buffer_depth[i] <= buffer_depth[i] - 1;
                    Bit#(384) pload = zeroExtend(d.data.payload);
                    buffered_data[i] <= buffered_data[i] | (pload
					<< (fromInteger(valueof(BUS_WIDTH)) * (buffer_depth[i]-1)));
                end

                else if (buffer_depth[i] == 0) /* already buffered 3 data blocks */
                begin
                    if (d.data.sop == 0 && d.data.eop == 1)
                    begin
                        /* reset state */
                        buffer_depth[i] <= 3;  //no of data blocks to buffer
                        buffered_data[i] <= 0;
                    end
                    else
                        buffer_depth[i] <= buffer_depth[i] - 1;

                    /* Find the index of the ring buffer to insert to. */
                    Bit#(32) dst_ip = (buffered_data[i])[143:112]; /* dst IP */
                    if (dst_ip == ip_address(host_index))
                        ring_buffer_index_fifo[i].enq(0);
                    else
                    begin
                        ServerIndex index = ipToIndexMapping(dst_ip);
                        ring_buffer_index_fifo[i].enq(index);
						if (index == 0)
							num_of_unknown_pkt_reg[i] <=
							               num_of_unknown_pkt_reg[i] + 1;
                        if (verbose)
                            $display("[SCHED (%d)] Adding idx = %d to idx fifo %d",
                                     host_index, index, i);
                    end
                end

                else if (d.data.sop == 0 && d.data.eop == 1)
                begin
                    /* reset state */
                    buffer_depth[i] <= 3;  //no of data blocks to buffer
                    buffered_data[i] <= 0;
                end

                buffer_fifo[i].enq(d.data);
            end
        endrule

        rule extract_ring_buffer_index_to_add_to
                    (curr_state == RUN && ready_to_deq_from_index_buffer[i] == 1);
            let index <- toGet(ring_buffer_index_fifo[i]).get;
            curr_ring_buffer_index[i] <= index;
            ready_to_deq_from_index_buffer[i] <= 0;
            ready_to_deq_from_ring_buffer[i] <= 1;
        endrule

        for (Integer j = 0; j < fromInteger(valueof(NUM_OF_SERVERS)); j = j + 1)
        begin
            rule enq_to_token_queue (curr_state == RUN
                                  && curr_ring_buffer_index[i] == fromInteger(j));
                token_queue[j].enq(fromInteger(i));
                if (verbose)
                $display("[SCHED (%d)] token_queue[%d].enq(%d)", host_index, j, i);
                curr_ring_buffer_index[i] <= fromInteger(valueof(NUM_OF_SERVERS));
            endrule
        end
    end

    Vector#(NUM_OF_SERVERS, Reg#(PortIndex))
         port_idx <- replicateM(mkReg(fromInteger(valueof(NUM_OF_PORTS))));

	Vector#(NUM_OF_SERVERS, Reg#(Bit#(1))) wait_for_completion
	                                            <- replicateM(mkReg(0));

    for (Integer j = 0; j < fromInteger(valueof(NUM_OF_SERVERS)); j = j + 1)
    begin
        rule deq_from_token_queue (curr_state == RUN
			                       && wait_for_completion[j] == 0);
            let port_index <- toGet(token_queue[j]).get;
            port_idx[j] <= port_index;
            if (verbose)
                $display("[SCHED (%d)] port_idx[%d] = %d",host_index,j,port_index);
            stop_polling[port_index] <= 0;
			wait_for_completion[j] <= 1;
        endrule

        for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
        begin
            rule add_to_correct_ring_buffer (curr_state == RUN
                                        && port_idx[j] == fromInteger(i)
                                        && ready_to_deq_from_ring_buffer[i] == 1);
                let d <- toGet(buffer_fifo[i]).get;

                if (d.sop == 0 && d.eop == 1)
                begin
                    ready_to_deq_from_index_buffer[i] <= 1;
                    ready_to_deq_from_ring_buffer[i] <= 0;
					wait_for_completion[j] <= 0;
                    port_idx[j] <= fromInteger(valueof(NUM_OF_PORTS));
                end

                if (j != 0)
                    ring_buffer[j].write_request.put
                                    (makeWriteReq(d.sop, d.eop, d.payload));
                else
                    src_rx_ring_buffer.write_request.put
                                    (makeWriteReq(d.sop, d.eop, d.payload));
                if (verbose)
                    $display("[SCHED (%d)] CLK = %d buffer index to put data = %d data = %d %d %x", host_index, clk.currTime(), j, d.sop, d.eop, d.payload);
            endrule
        end
    end

/*-------------------------------------------------------------------------------*/
    Reg#(MAC) dst_mac_addr <- mkReg(0);

    rule get_dst_addr (curr_state == RUN && start_tx_scheduling == 1);
		Bit#(64) curr_time = clk.currTime();

		Bit#(3) clock_lsb_four_bits = curr_time[2:0];
		if (clock_lsb_four_bits == 0)
		begin
			ServerIndex slot = 0;
            if (fromInteger(valueof(NUM_OF_SERVERS)) == 1)
                slot = fromInteger(valueof(NUM_OF_SERVERS))-1;
            else if (fromInteger(valueof(NUM_OF_SERVERS)) == 2)
                slot = 0;
            else
			    slot = curr_time[6:3] %
			                   (fromInteger(valueof(NUM_OF_SERVERS))-1);

			if (verbose)
            $display("[SCHED (%d)] CLK = %d  schedule_list[%d] = %d", host_index,
                            curr_time, slot, schedule_list[slot]);

            num_of_time_slots_used_reg <= num_of_time_slots_used_reg + 1;

			/* Get the dst mac and ip addr */
			ServerIndex table_index = schedule_list[slot];
			dst_mac_addr <= sched_table[table_index].server_mac;
			IP dst_ip_addr = sched_table[table_index].server_ip;

			if (verbose)
				$display("[SCHED (%d)] CLK = %d MAC = %x IP = %x", host_index,
				clk.currTime(), sched_table[table_index].server_mac,
			    dst_ip_addr);

			/* Get the index of the ring buffer to extract from */
			ServerIndex index = ipToIndexMapping(dst_ip_addr);

			if (verbose)
			$display("[SCHED (%d)] CLK = %d buffer index to extract from = %d %d",
				host_index, clk.currTime(), index, ring_buffer[index].elements);

			Bool is_empty <- ring_buffer[index].empty;

			/*
			 * Only if the forwarding ring buffer is empty, extract packet from
			 * the host tx buffer.
			 */
			if (!is_empty)
				ring_buffer[index].read_request.put(makeReadReq(READ));
			else
			begin
				if (verbose)
				begin
				$display("[SCHED (%d)] CLK = %d Empty ring; extract from host tx",
													  host_index, clk.currTime());
				$display("[SCHED (%d)] Host tx buffer size = %d", host_index,
													  ring_buffer[0].elements);
				end
				ring_buffer[0].read_request.put(makeReadReq(READ));
			end
		end
    endrule

    Vector#(NUM_OF_SERVERS, FIFO#(DataToPutInTx)) data_to_put
	            <- replicateM(mkSizedFIFO(fromInteger(valueof(DEFAULT_FIFO_LEN))));
	Vector#(NUM_OF_SERVERS, Reg#(PortIndex)) tx_port_index <- replicateM(mkReg(0));

    Vector#(NUM_OF_SERVERS, FIFO#(ReadResType)) data_fifo
	                                           <- replicateM(mkBypassFIFO);
	Vector#(NUM_OF_SERVERS, Wire#(PortIndex)) correct_tx_index
	                <- replicateM(mkDWire(fromInteger(valueof(NUM_OF_PORTS))));

    for (Integer i = 0; i < fromInteger(valueof(NUM_OF_SERVERS)); i = i + 1)
    begin
        rule modify_mac_headers_and_put_to_tx (curr_state == RUN);
            let d <- ring_buffer[i].read_response.get;

            if (d.data.sop == 1 && d.data.eop == 0)
            begin
                /* Update MAC header */
                Bit#(96) new_addr = {dst_mac_addr, mac_address(host_index)};
                Bit#(96) zero = 0;
                Bit#(BUS_WIDTH) temp = {zero, '1};
				Bit#(BUS_WIDTH) new_addr_temp = {new_addr, '0};
                d.data.payload = (d.data.payload & temp) | new_addr_temp;
                PortIndex index = machineToPortMapping(host_index, dst_mac_addr);
				tx_port_index[i] <= index;
                if (i == 0)
                    host_pkt_transmitted_reg <= host_pkt_transmitted_reg + 1;
                else
                    non_host_pkt_transmitted_reg <=
					                        non_host_pkt_transmitted_reg + 1;

				DataToPutInTx x = DataToPutInTx {
									data : d,
									tx_port : index
								};
				data_to_put[i].enq(x);
            end

			else
			begin
				DataToPutInTx x = DataToPutInTx {
									data : d,
									tx_port : tx_port_index[i]
								};
				data_to_put[i].enq(x);
			end

            if (verbose)
                $display("[SCHED (%d)] CLK = %d", host_index, clk.currTime());
        endrule

		rule set_correct_tx_index (curr_state == RUN);
			let res <- toGet(data_to_put[i]).get;
			ReadResType d = res.data;
			PortIndex idx = res.tx_port;
			data_fifo[i].enq(d);
			correct_tx_index[i] <= idx;
		if (verbose)
		$display("[SCHED (%d)] CLK = %d data written to tx %d data %d %d %x",
		host_index, clk.currTime(), idx, d.data.sop, d.data.eop, d.data.payload);
		endrule

		for (Integer j = 0; j < fromInteger(valueof(NUM_OF_PORTS)); j = j + 1)
		begin
			rule add_to_correct_tx (correct_tx_index[i] == fromInteger(j));
				let d <- toGet(data_fifo[i]).get;
				tx_ring_buffer[j].write_request.put
					(makeWriteReq(d.data.sop, d.data.eop, d.data.payload));
			endrule
		end
    end

/*-------------------------------------------------------------------------------*/
    for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
    begin
        rule handle_rx_buffer_write_req_from_mac;
            let req <- toGet(mac_write_request_fifo[i]).get;

            if (verbose)
                $display("[SCHED (%d)] CLK = %d Putting data into rx port buffer %d %d %x i = %d",
                host_index, clk.currTime(), req.data.sop, req.data.eop, req.data.payload, i);

            rx_ring_buffer[i].write_request.put
                  (makeWriteReq(req.data.sop, req.data.eop, req.data.payload));
        endrule

        rule handle_tx_buffer_read_req_from_mac;
            let req <- toGet(mac_read_request_fifo[i]).get;
            tx_ring_buffer[i].read_request.put(makeReadReq(req.op));
        endrule

        rule send_tx_buffer_read_res_to_mac;
            let d <- tx_ring_buffer[i].read_response.get;
            mac_read_response_fifo[i].enq(makeReadRes(d.data));
        endrule
    end

    rule handle_dma_simulator_write_req;
        let req <- toGet(dma_write_request_fifo).get;

        if (verbose)
            $display("[SCHED (%d)] Putting data into host tx buffer %d %d %x",
                        host_index, req.data.sop, req.data.eop, req.data.payload);

        ring_buffer[0].write_request.put
            (makeWriteReq(req.data.sop, req.data.eop, req.data.payload));
    endrule

    rule handle_dma_simulator_read_req;
        let req <- toGet(dma_read_request_fifo).get;
        src_rx_ring_buffer.read_request.put(makeReadReq(READ));
    endrule

    rule consume_pkt_in_response_to_dma_simulator_read_req;
        let d <- src_rx_ring_buffer.read_response.get;
		//debug_consuming_pkt_fifo.enq(d.data);
    endrule

/*-----------------------------------------------------------------------------*/
	method Action insertToSchedTable(ServerIndex index, IP ip_addr, MAC mac_addr);
		TableData d = TableData {
						server_ip : ip_addr,
						server_mac : mac_addr
		              };
		sched_table[index] <= d;
	endmethod

    method Action timeSlotsCount();
		time_slots_fifo.enq(num_of_time_slots_used_reg);
    endmethod

	method Action hostPktCount();
		host_pkt_fifo.enq(host_pkt_transmitted_reg);
	endmethod

	method Action nonHostPktCount();
		non_host_pkt_fifo.enq(non_host_pkt_transmitted_reg);
	endmethod

	method Action receivedPktCount();
		Bit#(64) pkt_received = 0;
		for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
			pkt_received = pkt_received + num_of_pkt_received_reg[i];
		received_pkt_fifo.enq(pkt_received);
	endmethod

	method Action unknownPktCount();
		Bit#(64) unknown_pkt = 0;
		for (Integer i = 0; i < fromInteger(valueof(NUM_OF_PORTS)); i = i + 1)
			unknown_pkt = unknown_pkt + num_of_unknown_pkt_reg[i];
		unknown_pkt_fifo.enq(unknown_pkt);
	endmethod

	method Action start(ServerIndex serverIdx);
		curr_state <= RUN;
	    configure <= 1;
        start_scheduling_flag <= 0;
        start_polling_rx_buffer <= 0;
        start_tx_scheduling <= 0;
		host_index <= serverIdx;
	endmethod

	method Action stop();
		curr_state <= CONFIG;
	endmethod

    /* MAC interfaces */
    interface Put mac_read_request_port_1 = toPut(mac_read_request_fifo[0]);
    interface Put mac_write_request_port_1 = toPut(mac_write_request_fifo[0]);
    interface Get mac_read_response_port_1 = toGet(mac_read_response_fifo[0]);
    interface Get mac_write_response_port_1 = toGet(mac_write_response_fifo[0]);

    interface Put mac_read_request_port_2 = toPut(mac_read_request_fifo[1]);
    interface Put mac_write_request_port_2 = toPut(mac_write_request_fifo[1]);
    interface Get mac_read_response_port_2 = toGet(mac_read_response_fifo[1]);
    interface Get mac_write_response_port_2 = toGet(mac_write_response_fifo[1]);

    interface Put mac_read_request_port_3 = toPut(mac_read_request_fifo[2]);
    interface Put mac_write_request_port_3 = toPut(mac_write_request_fifo[2]);
    interface Get mac_read_response_port_3 = toGet(mac_read_response_fifo[2]);
    interface Get mac_write_response_port_3 = toGet(mac_write_response_fifo[2]);

    interface Put mac_read_request_port_4 = toPut(mac_read_request_fifo[3]);
    interface Put mac_write_request_port_4 = toPut(mac_write_request_fifo[3]);
    interface Get mac_read_response_port_4 = toGet(mac_read_response_fifo[3]);
    interface Get mac_write_response_port_4 = toGet(mac_write_response_fifo[3]);

    /* DMA simulator interface */
    interface Put dma_read_request = toPut(dma_read_request_fifo);
    interface Put dma_write_request = toPut(dma_write_request_fifo);
    interface Get dma_read_response = toGet(dma_read_response_fifo);
    interface Get dma_write_response = toGet(dma_write_response_fifo);

	interface Get time_slots_response = toGet(time_slots_fifo);
	interface Get host_pkt_response = toGet(host_pkt_fifo);
	interface Get non_host_pkt_response = toGet(non_host_pkt_fifo);
	interface Get received_pkt_response = toGet(received_pkt_fifo);
	interface Get unknown_pkt_response = toGet(unknown_pkt_fifo);
//	interface Get debug_consuming_pkt = toGet(debug_consuming_pkt_fifo);

endmodule