AXI4_Deburster.bsv: Make implementation reasonably synthesisable

Using the modulus operator does not synthesise efficiently as
wrap_boundary is not statically known to be a power of 2 without
external knowledge of the AXI specification, putting it on a critical
path with over -30ns WNS in the Connectal-based SSITH AWS SoC, and
likely also AWSteria once FPGA builds are attempted.

Instead, exploit the fact that the burst length is required to be a
power of two, and use a bitmask to wrap the address within the aligned
range.

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

package AXI4_Deburster;

// ================================================================
// This package defines a AXI4-slave-to-AXI4-slave conversion module.
// The parameter interface is an AXI4-slave that carries no burst transactions.
// The output interface is an AXI4-slave that carries burst transactions.

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

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

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

import Cur_Cycle  :: *;

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

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

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

interface AXI4_Deburster_IFC #(numeric type wd_id,
			       numeric type wd_addr,
			       numeric type wd_data,
			       numeric type wd_user);
   method Action reset;

   // From master
   interface AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user) from_master;

   // To slave
   interface AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user) to_slave;
endinterface

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

module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user))
   provisos (Add #(a__, 8, wd_addr));

   // 0 quiet; 1: display start of burst; 2: display all traffic
   Integer cfg_verbosity = 0;

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

   // Transactor facing master
   AXI4_Slave_Xactor_IFC  #(wd_id, wd_addr, wd_data, wd_user)
      xactor_from_master <- mkAXI4_Slave_Xactor;

   // Transactor facing slave
   AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)
       xactor_to_slave <- mkAXI4_Master_Xactor;

   // On a write-transaction, this register is the W-channel burst beat count
   // (0 => start of burst)
   Reg #(AXI4_Len) rg_w_beat_count <- mkReg (0);

   // On a write-transaction, records awlen for slave
   // Size of FIFO should cover slave latency
   FIFOF #(AXI4_Len)  f_w_awlen <- mkSizedFIFOF (4);

   // On a write-transaction, this register is the B-channel burst beat count
   // which is the number of individual (non-burst) responses from the
   // slave to be combined into a single burst response to the master.
   // (0 => ready for next burst)
   Reg #(AXI4_Len) rg_b_beat_count <- mkReg (0);

   // On a burst write-transaction, all the individual slave responses
   // may not have the same 'resp' on the B channel. This register
   // remembers the first 'non-okay' resp (if any), to be returned to
   // the master in the burst response.
   Reg #(AXI4_Resp) rg_b_resp <- mkReg (axi4_resp_okay);

   // On a read-transaction, records arlen for slave
   // Size of FIFO should cover slave latency
   FIFOF #(AXI4_Len)  f_r_arlen <- mkSizedFIFOF (4);

   // On a read-transaction, this register is the AR-channel burst beat count
   // (0 => start of next burst)
   Reg #(AXI4_Len) rg_ar_beat_count <- mkReg (0);

   // On a read-transaction, this register is the R-channel burst beat count
   // (0 => ready for next burst)
   Reg #(AXI4_Len) rg_r_beat_count <- mkReg (0);

   // ----------------------------------------------------------------
   // Compute address for beat

// function ActionValue#(Bit #(wd_addr)) fv_addr_for_beat (Bit #(wd_addr) start_addr,
//      				     AXI4_Size      axsize,
//      				     AXI4_Burst     axburst,
//                                           AXI4_Len       axlen,
//      				     AXI4_Len       beat_count);
//
//    actionvalue
//    // For incrementing bursts this address is the next address
//    Bit #(wd_addr) addr = start_addr;
//    addr = start_addr + (1 << pack (axsize));
//
//    // The actual length of the burst is one more than indicated by axlen
//    Bit #(wd_addr) burst_len = zeroExtend (axlen) + 1;
//
//    // find the wrap boundary bit - this becomes the mask - will only work
//    // for burst lengths which are a power of two
//    Bit #(wd_addr) wrap_boundary = (burst_len << pack (axsize));
//
//    // For wrapping bursts the wrap_mask needs to be applied to check if the
//    // wrapping boundary has been reached
//    if (axburst == axburst_wrap) begin
//       $display ("%0d: %m::AXI4_Deburster: wrapping burst. boundary: (%0x). addr: (%0x)", cur_cycle, wrap_boundary, addr);
//       // The wrapping condition
//       if ((addr % wrap_boundary) == 0) begin
//          // wrap the address - retain all bits except the wrap boundary bit
//          addr = addr & (~wrap_boundary);
//          $display ("%0d: %m::AXI4_Deburster: wrapping burst. Wrapping: addr: (%0x)", cur_cycle, addr);
//       end
//    end
//    return addr;
//    endactionvalue
// endfunction

   function Bit #(wd_addr) fv_addr_for_beat (Bit #(wd_addr) start_addr,
					     AXI4_Size      axsize,
					     AXI4_Burst     axburst,
                                             AXI4_Len       axlen,
					     AXI4_Len       beat_count);

      // For incrementing bursts this address is the next address
      Bit #(wd_addr) addr = start_addr;
      addr = start_addr + (1 << pack (axsize));

      // The actual length of the burst is one more than indicated by axlen
      Bit #(wd_addr) burst_len = zeroExtend (axlen) + 1;

      // Compute the mask used to wrap the address, given that burst lenths are
      // always powers of two
      Bit #(wd_addr) wrap_mask = (burst_len << pack (axsize)) - 1;

      // For wrapping bursts the wrap_mask needs to be applied to wrap the
      // address round when it reaaches the boundary
      if (axburst == axburst_wrap) begin
         addr = (start_addr & (~ wrap_mask)) | (addr & wrap_mask);
      end
      return addr;
   endfunction

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

   rule rl_reset (rg_reset);
      $display ("%0d: %m::AXI4_Deburster.rl_reset", cur_cycle);
      xactor_from_master.reset;
      xactor_to_slave.reset;

      f_w_awlen.clear;
      rg_w_beat_count <= 0;
      rg_b_beat_count <= 0;
      rg_b_resp       <= axi4_resp_okay;

      f_r_arlen.clear;
      rg_ar_beat_count <= 0;
      rg_r_beat_count  <= 0;

      rg_reset <= False;
   endrule

   // ----------------------------------------------------------------
   // BEHAVIOR
   Reg #(Bit #(wd_addr)) rg_last_beat_waddr <- mkRegU;

   // ----------------
   // Wr requests (AW and W channels)

   rule rl_wr_xaction_master_to_slave;
      AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a_in = xactor_from_master.o_wr_addr.first;
      AXI4_Wr_Data #(wd_data, wd_user)        d_in = xactor_from_master.o_wr_data.first;

      // Construct output AW item
      let a_out = a_in;
      // For the first beat the address is unchanged from the address in the
      // input request, for the remaining beats we have the update the address
      // based on the previous address used
      if (rg_w_beat_count != 0) begin
         a_out.awaddr = fv_addr_for_beat (rg_last_beat_waddr, a_in.awsize, a_in.awburst, a_in.awlen, rg_w_beat_count);
      end

      a_out.awlen   = 0;
      a_out.awburst = axburst_fixed; // Not necessary when awlen=1, but slave may be finicky

      // Set WLAST to true since this is always last beat of outgoing xaction (awlen=1)
      let d_out   = d_in;
      d_out.wlast = True;

      // Send to slave
      xactor_to_slave.i_wr_addr.enq (a_out);
      xactor_to_slave.i_wr_data.enq (d_out);

      xactor_from_master.o_wr_data.deq;

      // Remember burst length so that individual responses from slave
     // can be combined into a single burst response to the master.
      if (rg_w_beat_count == 0)
	 f_w_awlen.enq (a_in.awlen);

      if (rg_w_beat_count < a_in.awlen) begin
	 rg_w_beat_count <= rg_w_beat_count + 1;
      end
      else begin
	 // Last beat of incoming burst; done with AW item
	 xactor_from_master.o_wr_addr.deq;
	 rg_w_beat_count <= 0;

	 // Simulation-only assertion-check (no action, just display assertion failure)
	 // Last incoming beat must have WLAST = 1
	 if (! d_in.wlast) begin
	    $display ("%0d: ERROR: %m::AXI4_Deburster.rl_wr_xaction_master_to_slave: m -> s", cur_cycle);
	    $display ("    WLAST not set on last data beat (awlen = %0d)", a_in.awlen);
	    $display ("    ", fshow (d_in));
	 end
      end

      // Remember this beat's address for calculating the next beat address.
      // This is necessary to support wrapping bursts
      rg_last_beat_waddr <= a_out.awaddr;

      // Debugging
      if (cfg_verbosity > 0) begin
	 $display ("%0d: %m::AXI4_Deburster.rl_wr_xaction_master_to_slave: m -> s, beat %0d",
		   cur_cycle, rg_w_beat_count);
	 if (rg_w_beat_count == 0)
	    $display ("    a_in : ", fshow (a_in));
	 if ((rg_w_beat_count == 0) || (cfg_verbosity > 1)) begin
	    $display ("    d_in : ", fshow (d_in));
	    $display ("    a_out: ", fshow (a_out));
	    $display ("    d_out: ", fshow (d_out));
	 end
      end
   endrule: rl_wr_xaction_master_to_slave

   // ----------------
   // Wr responses (B channel): consume responses from slave until the
   // last response for a burst, then respond to master.  Remember if
   // any of them was not an 'okay' response.

   rule rl_wr_resp_slave_to_master;
      AXI4_Wr_Resp #(wd_id, wd_user) b_in <- pop_o (xactor_to_slave.o_wr_resp);

      if (rg_b_beat_count < f_w_awlen.first) begin
	 // Remember first non-okay response (if any) of a burst in rg_b_resp
	 if ((rg_b_resp == axi4_resp_okay) && (b_in.bresp != axi4_resp_okay))
	    rg_b_resp <= b_in.bresp;

	 // not last beat of burst
	 rg_b_beat_count <= rg_b_beat_count + 1;

	 if (cfg_verbosity > 1) begin
	    $display ("%0d: %m::AXI4_Deburster.rl_wr_resp_slave_to_master: m <- s, beat %0d",
		      cur_cycle, rg_b_beat_count);
	    $display ("    Consuming and discarding beat %0d", rg_b_beat_count);
	    $display ("    ", fshow (b_in));
	 end
      end
      else begin
	 // Last beat of burst
	 let b_out = b_in;
	 if (rg_b_resp != axi4_resp_okay)
	    b_out.bresp = rg_b_resp;
	 xactor_from_master.i_wr_resp.enq (b_out);

	 f_w_awlen.deq;

	 // Get ready for next burst
	 rg_b_beat_count <= 0;
	 rg_b_resp       <= axi4_resp_okay;

	 if (cfg_verbosity > 1) begin
	    $display ("%0d: %m::AXI4_Deburster.rl_wr_resp_slave_to_master: m <- s, beat %0d",
		      cur_cycle, rg_b_beat_count);
	    $display ("    b_in: ",  fshow (b_in));
	    $display ("    b_out: ", fshow (b_out));
	 end
      end
   endrule

  // ----------------
   // Rd requests (AR channel)

   Reg #(Bit #(wd_addr)) rg_last_beat_raddr <- mkRegU;
   rule rl_rd_xaction_master_to_slave;
      AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a_in = xactor_from_master.o_rd_addr.first;

      // Compute forwarded request for each beat, and send
      let a_out = a_in;

      // For the first beat the address is unchanged from the address in the
      // input request, for the remaining beats we have the update the address
      // based on the previous address used
      if (rg_ar_beat_count != 0) begin
         a_out.araddr = fv_addr_for_beat (rg_last_beat_raddr, a_in.arsize, a_in.arburst, a_in.arlen, rg_ar_beat_count);
      end

      a_out.arlen   = 0;
      a_out.arburst = axburst_fixed; // Not necessary when arlen=1, but slave may be finicky
      xactor_to_slave.i_rd_addr.enq (a_out);

      // On first beat, set up the response count
      if (rg_ar_beat_count == 0)
	 f_r_arlen.enq (a_in.arlen);

      if (rg_ar_beat_count < a_in.arlen) begin
	 rg_ar_beat_count <= rg_ar_beat_count + 1;
      end
      else begin
	 // Last beat sent; done with AR item
	 xactor_from_master.o_rd_addr.deq;
	 rg_ar_beat_count <= 0;
      end

      // Remember this beat's address for calculating the next beat address.
      // This is necessary to support wrapping bursts
      rg_last_beat_raddr <= a_out.araddr;

      // Debugging
      if (cfg_verbosity > 0) begin
	 $display ("%0d: %m::AXI4_Deburster.rl_rd_xaction_master_to_slave: m -> s, addr %08x beat %0d",
		   cur_cycle, a_out.araddr, rg_ar_beat_count);
	 if (rg_ar_beat_count == 0)
	    $display ("    a_in:  ", fshow (a_in));
	 if ((rg_ar_beat_count == 0) || (cfg_verbosity > 1))
	    $display ("    a_out: ", fshow (a_out));
      end

   endrule: rl_rd_xaction_master_to_slave

   // ----------------
   // Rd responses

   rule rl_rd_resp_slave_to_master;
      AXI4_Rd_Data #(wd_id, wd_data, wd_user) r_in <- pop_o (xactor_to_slave.o_rd_data);
      let arlen = f_r_arlen.first;

      let r_out = r_in;
      if (rg_r_beat_count < arlen) begin
	 // not last beat of burst
	 r_out.rlast = False;
	 rg_r_beat_count <= rg_r_beat_count + 1;
      end
      else begin
	 // Last beat of burst
	 rg_r_beat_count <= 0;
	 r_out.rlast = True;    // should be set already, but override if not
	 f_r_arlen.deq;
      end

      xactor_from_master.i_rd_data.enq (r_out);

      // Debugging
      if (cfg_verbosity > 0) begin
	 $display ("%0d: %m::AXI4_Deburster.rl_rd_resp_slave_to_master: m <- s, beat %0d",
		   cur_cycle, rg_r_beat_count);
	 if ((rg_r_beat_count == 0) || (cfg_verbosity > 1)) begin
	    $display ("    r_in:  ", fshow (r_in));
	    $display ("    r_out: ", fshow (r_out));
	 end
      end
   endrule: rl_rd_resp_slave_to_master

   // ----------------------------------------------------------------
   // INTERFACE

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

   interface from_master = xactor_from_master.axi_side;
   interface to_slave    = xactor_to_slave   .axi_side;
endmodule

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

endpackage: AXI4_Deburster

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

package AXI4_Deburster;

// ================================================================
// This package defines a AXI4-slave-to-AXI4-slave conversion module.
// The parameter interface is an AXI4-slave that carries no burst transactions.
// The output interface is an AXI4-slave that carries burst transactions.

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

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

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

import Cur_Cycle  :: *;

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

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

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

interface AXI4_Deburster_IFC #(numeric type wd_id,
			       numeric type wd_addr,
			       numeric type wd_data,
			       numeric type wd_user);
   method Action reset;

   // From master
   interface AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user) from_master;

   // To slave
   interface AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user) to_slave;
endinterface

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

module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user))
   provisos (Add #(a__, 8, wd_addr));

   // 0 quiet; 1: display start of burst; 2: display all traffic
   Integer cfg_verbosity = 0;

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

   // Transactor facing master
   AXI4_Slave_Xactor_IFC  #(wd_id, wd_addr, wd_data, wd_user)
      xactor_from_master <- mkAXI4_Slave_Xactor;

   // Transactor facing slave
   AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)
       xactor_to_slave <- mkAXI4_Master_Xactor;

   // On a write-transaction, this register is the W-channel burst beat count
   // (0 => start of burst)
   Reg #(AXI4_Len) rg_w_beat_count <- mkReg (0);

   // On a write-transaction, records awlen for slave
   // Size of FIFO should cover slave latency
   FIFOF #(AXI4_Len)  f_w_awlen <- mkSizedFIFOF (4);

   // On a write-transaction, this register is the B-channel burst beat count
   // which is the number of individual (non-burst) responses from the
   // slave to be combined into a single burst response to the master.
   // (0 => ready for next burst)
   Reg #(AXI4_Len) rg_b_beat_count <- mkReg (0);

   // On a burst write-transaction, all the individual slave responses
   // may not have the same 'resp' on the B channel. This register
   // remembers the first 'non-okay' resp (if any), to be returned to
   // the master in the burst response.
   Reg #(AXI4_Resp) rg_b_resp <- mkReg (axi4_resp_okay);

   // On a read-transaction, records arlen for slave
   // Size of FIFO should cover slave latency
   FIFOF #(AXI4_Len)  f_r_arlen <- mkSizedFIFOF (4);

   // On a read-transaction, this register is the AR-channel burst beat count
   // (0 => start of next burst)
   Reg #(AXI4_Len) rg_ar_beat_count <- mkReg (0);

   // On a read-transaction, this register is the R-channel burst beat count
   // (0 => ready for next burst)
   Reg #(AXI4_Len) rg_r_beat_count <- mkReg (0);

   // ----------------------------------------------------------------
   // Compute address for beat

// function ActionValue#(Bit #(wd_addr)) fv_addr_for_beat (Bit #(wd_addr) start_addr,
//      				     AXI4_Size      axsize,
//      				     AXI4_Burst     axburst,
//                                           AXI4_Len       axlen,
//      				     AXI4_Len       beat_count);
//
//    actionvalue
//    // For incrementing bursts this address is the next address
//    Bit #(wd_addr) addr = start_addr;
//    addr = start_addr + (1 << pack (axsize));
//
//    // The actual length of the burst is one more than indicated by axlen
//    Bit #(wd_addr) burst_len = zeroExtend (axlen) + 1;
//
//    // find the wrap boundary bit - this becomes the mask - will only work
//    // for burst lengths which are a power of two
//    Bit #(wd_addr) wrap_boundary = (burst_len << pack (axsize));
//
//    // For wrapping bursts the wrap_mask needs to be applied to check if the
//    // wrapping boundary has been reached
//    if (axburst == axburst_wrap) begin
//       $display ("%0d: %m::AXI4_Deburster: wrapping burst. boundary: (%0x). addr: (%0x)", cur_cycle, wrap_boundary, addr);
//       // The wrapping condition
//       if ((addr % wrap_boundary) == 0) begin
//          // wrap the address - retain all bits except the wrap boundary bit
//          addr = addr & (~wrap_boundary);
//          $display ("%0d: %m::AXI4_Deburster: wrapping burst. Wrapping: addr: (%0x)", cur_cycle, addr);
//       end
//    end
//    return addr;
//    endactionvalue
// endfunction

   function Bit #(wd_addr) fv_addr_for_beat (Bit #(wd_addr) start_addr,
					     AXI4_Size      axsize,
					     AXI4_Burst     axburst,
                                             AXI4_Len       axlen,
					     AXI4_Len       beat_count);

      // For incrementing bursts this address is the next address
      Bit #(wd_addr) addr = start_addr;
      addr = start_addr + (1 << pack (axsize));

      // The actual length of the burst is one more than indicated by axlen
      Bit #(wd_addr) burst_len = zeroExtend (axlen) + 1;

      // find the wrap boundary bit - this becomes the mask - will only work
      // for burst lengths which are a power of two
      Bit #(wd_addr) wrap_boundary = (burst_len << pack (axsize));

      // For wrapping bursts the wrap_mask needs to be applied to check if the
      // wrapping boundary has been reached
      if (axburst == axburst_wrap) begin
         // The wrapping condition
         if ((addr % wrap_boundary) == 0) begin
            // wrap the address - retain all bits except the wrap boundary bit
            addr = addr - wrap_boundary;
         end
      end
      return addr;
   endfunction

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

   rule rl_reset (rg_reset);
      $display ("%0d: %m::AXI4_Deburster.rl_reset", cur_cycle);
      xactor_from_master.reset;
      xactor_to_slave.reset;

      f_w_awlen.clear;
      rg_w_beat_count <= 0;
      rg_b_beat_count <= 0;
      rg_b_resp       <= axi4_resp_okay;

      f_r_arlen.clear;
      rg_ar_beat_count <= 0;
      rg_r_beat_count  <= 0;

      rg_reset <= False;
   endrule

   // ----------------------------------------------------------------
   // BEHAVIOR
   Reg #(Bit #(wd_addr)) rg_last_beat_waddr <- mkRegU;

   // ----------------
   // Wr requests (AW and W channels)

   rule rl_wr_xaction_master_to_slave;
      AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a_in = xactor_from_master.o_wr_addr.first;
      AXI4_Wr_Data #(wd_data, wd_user)        d_in = xactor_from_master.o_wr_data.first;

      // Construct output AW item
      let a_out = a_in;
      // For the first beat the address is unchanged from the address in the
      // input request, for the remaining beats we have the update the address
      // based on the previous address used
      if (rg_w_beat_count != 0) begin
         a_out.awaddr = fv_addr_for_beat (rg_last_beat_waddr, a_in.awsize, a_in.awburst, a_in.awlen, rg_w_beat_count);
      end

      a_out.awlen   = 0;
      a_out.awburst = axburst_fixed; // Not necessary when awlen=1, but slave may be finicky

      // Set WLAST to true since this is always last beat of outgoing xaction (awlen=1)
      let d_out   = d_in;
      d_out.wlast = True;

      // Send to slave
      xactor_to_slave.i_wr_addr.enq (a_out);
      xactor_to_slave.i_wr_data.enq (d_out);

      xactor_from_master.o_wr_data.deq;

      // Remember burst length so that individual responses from slave
     // can be combined into a single burst response to the master.
      if (rg_w_beat_count == 0)
	 f_w_awlen.enq (a_in.awlen);

      if (rg_w_beat_count < a_in.awlen) begin
	 rg_w_beat_count <= rg_w_beat_count + 1;
      end
      else begin
	 // Last beat of incoming burst; done with AW item
	 xactor_from_master.o_wr_addr.deq;
	 rg_w_beat_count <= 0;

	 // Simulation-only assertion-check (no action, just display assertion failure)
	 // Last incoming beat must have WLAST = 1
	 if (! d_in.wlast) begin
	    $display ("%0d: ERROR: %m::AXI4_Deburster.rl_wr_xaction_master_to_slave: m -> s", cur_cycle);
	    $display ("    WLAST not set on last data beat (awlen = %0d)", a_in.awlen);
	    $display ("    ", fshow (d_in));
	 end
      end

      // Remember this beat's address for calculating the next beat address.
      // This is necessary to support wrapping bursts
      rg_last_beat_waddr <= a_out.awaddr;

      // Debugging
      if (cfg_verbosity > 0) begin
	 $display ("%0d: %m::AXI4_Deburster.rl_wr_xaction_master_to_slave: m -> s, beat %0d",
		   cur_cycle, rg_w_beat_count);
	 if (rg_w_beat_count == 0)
	    $display ("    a_in : ", fshow (a_in));
	 if ((rg_w_beat_count == 0) || (cfg_verbosity > 1)) begin
	    $display ("    d_in : ", fshow (d_in));
	    $display ("    a_out: ", fshow (a_out));
	    $display ("    d_out: ", fshow (d_out));
	 end
      end
   endrule: rl_wr_xaction_master_to_slave

   // ----------------
   // Wr responses (B channel): consume responses from slave until the
   // last response for a burst, then respond to master.  Remember if
   // any of them was not an 'okay' response.

   rule rl_wr_resp_slave_to_master;
      AXI4_Wr_Resp #(wd_id, wd_user) b_in <- pop_o (xactor_to_slave.o_wr_resp);

      if (rg_b_beat_count < f_w_awlen.first) begin
	 // Remember first non-okay response (if any) of a burst in rg_b_resp
	 if ((rg_b_resp == axi4_resp_okay) && (b_in.bresp != axi4_resp_okay))
	    rg_b_resp <= b_in.bresp;

	 // not last beat of burst
	 rg_b_beat_count <= rg_b_beat_count + 1;

	 if (cfg_verbosity > 1) begin
	    $display ("%0d: %m::AXI4_Deburster.rl_wr_resp_slave_to_master: m <- s, beat %0d",
		      cur_cycle, rg_b_beat_count);
	    $display ("    Consuming and discarding beat %0d", rg_b_beat_count);
	    $display ("    ", fshow (b_in));
	 end
      end
      else begin
	 // Last beat of burst
	 let b_out = b_in;
	 if (rg_b_resp != axi4_resp_okay)
	    b_out.bresp = rg_b_resp;
	 xactor_from_master.i_wr_resp.enq (b_out);

	 f_w_awlen.deq;

	 // Get ready for next burst
	 rg_b_beat_count <= 0;
	 rg_b_resp       <= axi4_resp_okay;

	 if (cfg_verbosity > 1) begin
	    $display ("%0d: %m::AXI4_Deburster.rl_wr_resp_slave_to_master: m <- s, beat %0d",
		      cur_cycle, rg_b_beat_count);
	    $display ("    b_in: ",  fshow (b_in));
	    $display ("    b_out: ", fshow (b_out));
	 end
      end
   endrule

  // ----------------
   // Rd requests (AR channel)

   Reg #(Bit #(wd_addr)) rg_last_beat_raddr <- mkRegU;
   rule rl_rd_xaction_master_to_slave;
      AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a_in = xactor_from_master.o_rd_addr.first;

      // Compute forwarded request for each beat, and send
      let a_out = a_in;

      // For the first beat the address is unchanged from the address in the
      // input request, for the remaining beats we have the update the address
      // based on the previous address used
      if (rg_ar_beat_count != 0) begin
         a_out.araddr = fv_addr_for_beat (rg_last_beat_raddr, a_in.arsize, a_in.arburst, a_in.arlen, rg_ar_beat_count);
      end

      a_out.arlen   = 0;
      a_out.arburst = axburst_fixed; // Not necessary when arlen=1, but slave may be finicky
      xactor_to_slave.i_rd_addr.enq (a_out);

      // On first beat, set up the response count
      if (rg_ar_beat_count == 0)
	 f_r_arlen.enq (a_in.arlen);

      if (rg_ar_beat_count < a_in.arlen) begin
	 rg_ar_beat_count <= rg_ar_beat_count + 1;
      end
      else begin
	 // Last beat sent; done with AR item
	 xactor_from_master.o_rd_addr.deq;
	 rg_ar_beat_count <= 0;
      end

      // Remember this beat's address for calculating the next beat address.
      // This is necessary to support wrapping bursts
      rg_last_beat_raddr <= a_out.araddr;

      // Debugging
      if (cfg_verbosity > 0) begin
	 $display ("%0d: %m::AXI4_Deburster.rl_rd_xaction_master_to_slave: m -> s, addr %08x beat %0d",
		   cur_cycle, a_out.araddr, rg_ar_beat_count);
	 if (rg_ar_beat_count == 0)
	    $display ("    a_in:  ", fshow (a_in));
	 if ((rg_ar_beat_count == 0) || (cfg_verbosity > 1))
	    $display ("    a_out: ", fshow (a_out));
      end

   endrule: rl_rd_xaction_master_to_slave

   // ----------------
   // Rd responses

   rule rl_rd_resp_slave_to_master;
      AXI4_Rd_Data #(wd_id, wd_data, wd_user) r_in <- pop_o (xactor_to_slave.o_rd_data);
      let arlen = f_r_arlen.first;

      let r_out = r_in;
      if (rg_r_beat_count < arlen) begin
	 // not last beat of burst
	 r_out.rlast = False;
	 rg_r_beat_count <= rg_r_beat_count + 1;
      end
      else begin
	 // Last beat of burst
	 rg_r_beat_count <= 0;
	 r_out.rlast = True;    // should be set already, but override if not
	 f_r_arlen.deq;
      end

      xactor_from_master.i_rd_data.enq (r_out);

      // Debugging
      if (cfg_verbosity > 0) begin
	 $display ("%0d: %m::AXI4_Deburster.rl_rd_resp_slave_to_master: m <- s, beat %0d",
		   cur_cycle, rg_r_beat_count);
	 if ((rg_r_beat_count == 0) || (cfg_verbosity > 1)) begin
	    $display ("    r_in:  ", fshow (r_in));
	    $display ("    r_out: ", fshow (r_out));
	 end
      end
   endrule: rl_rd_resp_slave_to_master

   // ----------------------------------------------------------------
   // INTERFACE

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

   interface from_master = xactor_from_master.axi_side;
   interface to_slave    = xactor_to_slave   .axi_side;
endmodule

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

endpackage: AXI4_Deburster


clean up

clean up

/* Implementing LDV-1hop policy... */
/** **/

function Tuple2#(G_Slot#(num_entries, key_width, value_width), Bit#(TLog#(k))) find_ldv(Vector#(k, Reg#(G_Slot#(num_entries, key_width, value_width))) gslots);
	G_Slot#(num_entries, key_width, value_width) min_gslot = gslots[0];
	Bit#(TLog#(k)) min_g_index = unpack(0);

	for(Integer i=1; i<valueof(k); i=i+1) begin
		if(gslots[i].degree < min_gslot.degree) begin
			min_g_index = fromInteger(i);
			min_gslot = gslots[i];
		end
	end

	return tuple2(min_gslot, min_g_index);
endfunction

Stmt ldv_n =
seq
	par
	action
		//get the tag slot to be repaired
		let mslot_tmp = m_table.a.read;
		mslot_to_repair <= mslot_tmp;
	endaction
	endpar

	par
	action
		//evictee_hvals now has repairee hash vals...
		Vector#(k, G_Addr#(c,num_entries)) hash_vals;
		for(Integer i=0; i<valueof(k); i=i+1) begin
			hash_vals[i] <- hash_units[i].compute_hash(mslot_to_repair.key);
			repair_hvals[i] <= hash_vals[i];
		end
	endaction
	endpar

	par
	action	//get repairee g slots
		Vector#(k, G_Slot#(num_entries, key_width, value_width)) g_slots;

		for(Integer i=0; i<valueof(k); i=i+1) begin
			g_slots[i] <- hash_units[i].get_gslot();
			repair_gslots[i] <= g_slots[i];
		end
	endaction
	endpar

	par
	action
		let tmp = find_ldv(repair_gslots);
		repair_gslot <= tpl_1(tmp);
		repair_g_index <= tpl_2(tmp);
	endaction
	endpar

	par	//compute slot_xor ^ mslot_address
	action	//assign it to the ldv
		G_Slot#(num_entries, key_width, value_width) tmp_gslot = unpack(0);
		Vector#(k, G_Slot#(num_entries, key_width, value_width)) tmp_gslots;

		for(Integer j=0; j<valueof(k); j=j+1) begin
			tmp_gslots[j] = repair_gslots[j];
		end

		for(Integer j=0; j<valueof(k); j=j+1) begin
			if(fromInteger(j) != repair_g_index) begin
			`ifdef FLAT
				tmp_gslot.key = tmp_gslot.key ^ repair_gslots[j].key;
				tmp_gslot.value = tmp_gslot.value ^ repair_gslots[j].value;
			`elsif FAST_VALUE
			  tmp_gslot.value = tmp_gslot.value ^ repair_gslots[j].value;
			`endif
				tmp_gslot.maddr = tmp_gslot.maddr ^ repair_gslots[j].maddr;
				tmp_gslots[j].mslot = victim_mslot_addr;
			end
		end

	`ifdef FLAT
		tmp_gslot.key = tmp_gslot.key ^ mslot_to_repair.key;
		tmp_gslot.value = tmp_gslot.value ^ mslot_to_repair.value;
	`elsif FAST_VALUE
	  tmp_gslot.value = tmp_gslot.value ^ mslot_to_repair.value;
	`endif
		tmp_gslot.maddr = tmp_gslot.maddr ^ victim_mslot_addr;
		tmp_gslot.degree = 1;

		tmp_gslots[repair_g_index] = tmp_gslot;
		for(Integer j=0; j<valueof(k); j=j+1) begin
			repair_gslots[j] <= tmp_gslots[j];
		end
	endaction
	endpar

	par
	action	// update all the gslots in their respective hash units
		for(Integer j=0; j<valueof(k); j=j+1) begin
			hash_units[j].set_gslot(repair_hvals[j], repair_gslots[j]);
		end
	endaction
	endpar

	par
	action
		miss_service <= False;
		inited <= True;
		stage <= 0;
	endaction
	endpar
endseq;
FSM ldvn <- mkFSM(ldv_n);
//FSM ldvn <- mkFSMWithPred(ldv_n, nHOP);

Stmt miss_server =
seq
	par
	action	//get the (key,value) to be evicted
		let mslot_to_evict = m_table.a.read;
		evictee_mslot <= mslot_to_evict;
	endaction
	endpar

	par	//compute hashes on the evictee...
	action
		Vector#(k, G_Addr#(c,num_entries)) hash_vals;
		for(Integer i=0; i<valueof(k); i=i+1) begin
			hash_vals[i] <- hash_units[i].compute_hash(evictee_mslot.key);
			evictee_hvals[i] <= hash_vals[i];
		end
	endaction
	endpar

	par
	action	//read out the g slots of the eviction candidate
		Vector#(k, G_Slot#(num_entries, key_width, value_width)) g_slots;

		for(Integer i=0; i<valueof(k); i=i+1) begin
			g_slots[i] <- hash_units[i].get_gslot();
			evictee_gslots[i] <= g_slots[i];
		end
	endaction
	endpar

	par	//cleanup evictee's gslots...
	action
		if(evictee_mslot.valid==1) begin	//if evictee is valid, then clean up its g slots...
			for(Integer i=0; i<valueof(k); i=i+1) begin
				if((evictee_gslots[i].degree==1) || (evictee_gslots[i].degree==0)) begin
					evictee_gslots[i] <= unpack(0);
				end
				else begin
					evictee_gslots[i].degree <= evictee_gslots[i].degree - 1;
				end
			end
		end
	endaction
	endpar

	par
	action
		for(Integer i=0; i<valueof(k); i=i+1) begin
			hash_units[i].set_gslot(evictee_hvals[i], evictee_gslots[i]);
		end
	endaction
	endpar

	par
	action	// compute the hash on the new key...
		Vector#(k, G_Addr#(c,num_entries)) hash_vals;
		for(Integer i=0; i<valueof(k); i=i+1) begin
			hash_vals[i] <- hash_units[i].compute_hash(new_mslot.key);
			new_hvals[i] <= hash_vals[i];
		end
	endaction
	endpar

	par
	action	//read out the g slots to be used by the new key
		Vector#(k, G_Slot#(num_entries, key_width, value_width)) g_slots;
		for(Integer i=0; i<valueof(k); i=i+1) begin
			g_slots[i] <- hash_units[i].get_gslot();
			new_gslots[i] <= g_slots[i];
		end
	endaction
	endpar

	par
	action	//find the lowest degree victim G slot
		let tmp = find_ldv(new_gslots);
		victim_gslot <= tpl_1(tmp);
		victim_g_index <= tpl_2(tmp);
		victim_mslot_addr <= tpl_1(tmp).mslot;
	endaction
	endpar

	par	//compute slot_xor ^ mslot_counter
	action	//assign it to the ldv
		G_Slot#(num_entries, key_width, value_width) tmp_gslot = unpack(0);
		Vector#(k, G_Slot#(num_entries, key_width, value_width)) tmp_gslots;

		for(Integer j=0; j<valueof(k); j=j+1) begin
			tmp_gslots[j] = new_gslots[j];
		end

		for(Integer j=0; j<valueof(k); j=j+1) begin
			if(fromInteger(j) != victim_g_index) begin
			`ifdef FLAT
				tmp_gslot.key = tmp_gslot.key ^ new_gslots[j].key;
				tmp_gslot.value = tmp_gslot.value ^ new_gslots[j].value;
			`elsif FAST_VALUE
				tmp_gslot.value = tmp_gslot.value ^ new_gslots[j].value;
			`endif
				tmp_gslot.maddr = tmp_gslot.maddr ^ new_gslots[j].maddr;
				tmp_gslots[j].degree = tmp_gslots[j].degree+1;
				tmp_gslots[j].mslot = mslot_counter;
			end
		end

	`ifdef FLAT
		tmp_gslot.key = tmp_gslot.key ^ new_mslot.key;
		tmp_gslot.value = tmp_gslot.value ^ new_mslot.value;
	`elsif FAST_VALUE
		tmp_gslot.value = tmp_gslot.value ^ new_mslot.value;
	`endif
		tmp_gslot.maddr = tmp_gslot.maddr ^ mslot_counter;
		tmp_gslot.mslot = mslot_counter;
		tmp_gslot.degree = 1;

		tmp_gslots[victim_g_index] = tmp_gslot;
		for(Integer j=0; j<valueof(k); j=j+1) begin
			new_gslots[j] <= tmp_gslots[j];
		end

	endaction
	endpar

	par
	action	// update all the gslots in their respective hash units
		for(Integer j=0; j<valueof(k); j=j+1) begin
			hash_units[j].set_gslot(new_hvals[j], new_gslots[j]);
		end
	endaction
	endpar

	par
	action	//write to the mslot as well
		m_table.b.put(True, mslot_counter, new_mslot);
		mslot_counter <= mslot_counter + 1;
	endaction
	endpar

	par
	action
	//incoming entry inserted...
	//stop here for LDV0...
	//try repairing the victim_mslot for LDV1 strategy...
		if(victim_gslot.degree==0) begin
			miss_service <= False;
			inited <= True;
			stage <= 0;
			$display("[%d]: new (key,value) inserted at slot %d\n", $time, mslot_counter-1);
		end
		else begin
			m_table.a.put(False, victim_mslot_addr, ?);
			ldvn.start;
		end
	endaction
	endpar

endseq;
FSM mslot_replacement <- mkFSM(miss_server);


mkToBit32 and mkFromBit32 are now duals

mkToBit32 and mkFromBit32 are now duals

// Copyright (c) 2012 MIT
// Copyright (c) 2012 Nokia, Inc.

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

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

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

import GetPut         ::*;
import FIFOF          ::*;
import SpecialFIFOs   ::*;
import StmtFSM        ::*;
import Assert         ::*;

function Bit#(a) rtruncate(Bit#(b) x) provisos(Add#(k,a,b));
   match {.v,.*} = split(x);
   return v;
endfunction

interface ToBit32#(type a);
   method Action enq(a v);
   method Bit#(32) first;
   method Action deq();
   method Bool notEmpty();
   method Bool notFull();
endinterface

interface FromBit32#(type a);
   method Action enq(Bit#(32) v);
   method a first();
   method Action deq();
   method Bool notEmpty();
   method Bool notFull();
endinterface

module mkToBit32(ToBit32#(a))
   provisos(Bits#(a,asz),
            Add#(1, z, asz),
            Div#(asz,32,nwords),
            Mul#(32,nwords,asz32),
            Add#(32,a__,asz32),
            Add#(asz,paddingsz,asz32));

   Bit#(32) size = fromInteger(valueOf(asz));
   Bit#(32) max  = fromInteger(valueOf(nwords)) - 1;
   Bit#(paddingsz) padding = 0;

   FIFOF#(Bit#(asz32)) fifo <- mkFIFOF1();
   Reg#(Bit#(32))   count <- mkReg(0);

   method Action enq(a val);
       fifo.enq({padding,pack(val)});
   endmethod

   method Bit#(32) first() if (fifo.notEmpty);
      return rtruncate(fifo.first << (count*32));
   endmethod

   method Action deq() if (fifo.notEmpty);
      if (count == max)
         begin
            count <= 0;
            fifo.deq;
         end
      else
         begin
            count <= count + 1;
         end
   endmethod
   method Bool notEmpty = fifo.notEmpty;
   method Bool notFull = fifo.notFull;
endmodule

module mkFromBit32(FromBit32#(a))
   provisos(Bits#(a,asz),
            Add#(1,z,asz),
            Div#(asz,32,nwords),
            Mul#(32,nwords,asz32),
            Add#(32,a__,asz32),
            Add#(asz,paddingsz,asz32));

   Bit#(32) size   = fromInteger(valueOf(asz));
   Bit#(32) max    = fromInteger(valueOf(nwords))-1;

   Reg#(Bit#(asz32)) fb32buff <- mkReg(0);
   Reg#(Bit#(32))    count <- mkReg(0);
   FIFOF#(Bit#(asz)) fifo <- mkFIFOF1;

   method Action enq(Bit#(32) x) if (count < max || fifo.notFull);
      Bit#(asz32) newbuff = truncate({fb32buff,x});
      fb32buff <= newbuff;
      if (count == max)
         begin
            count <= 0;
            fifo.enq(truncate(newbuff));
         end
      else
         begin
            count <= count+1;
         end
   endmethod

   method a first;
       return unpack(fifo.first);
   endmethod

   method Action deq;
       fifo.deq;
   endmethod

   method Bool notEmpty() = fifo.notEmpty;
   method Bool notFull() = fifo.notFull;
endmodule

module mkAdapterTb(Empty);
   ToBit32#(Bit#(72)) tb32_72 <- mkToBit32();
   ToBit32#(Bit#(17)) tb32_17 <- mkToBit32();

   FromBit32#(Bit#(72)) fb32_72 <- mkFromBit32();
   FromBit32#(Bit#(17)) fb32_17 <- mkFromBit32();

   Reg#(Bit#(10)) timer <- mkReg(0);
   rule timeout;
       timer <= timer+1;
       dynamicAssert(timer < 128, "Timeout");
   endrule

   mkAutoFSM(
      seq

       // test to bit-32
       tb32_72.enq(72'h090807060504030201);
       dynamicAssert(tb32_72.notEmpty, "Adapter not empty");
       dynamicAssert(!tb32_72.notFull, "Adapter full");
       $display("tb32_72 notEmpty %d notFull %d", tb32_72.notEmpty, tb32_72.notFull);
       $display("tb32_72 word 0 %h", tb32_72.first());
       dynamicAssert(tb32_72.first == 32'h00000009, "expecting 00000009");
       tb32_72.deq;
       $display("tb32_72 word 1 %h", tb32_72.first());
       dynamicAssert(tb32_72.first == 32'h08070605, "expecting 08070605");
       tb32_72.deq;
       $display("tb32_72 word 2 %h", tb32_72.first());
       dynamicAssert(tb32_72.first == 32'h04030201, "expecting 04030201");
       tb32_72.deq;
       dynamicAssert(!tb32_72.notEmpty && tb32_72.notFull, "Adapter empty and not full");
       $display("tb32_72 notEmpty %d notFull %d", tb32_72.notEmpty, tb32_72.notFull);
       dynamicAssert(!tb32_17.notEmpty, "tb32_17 empty");
       dynamicAssert(tb32_17.notFull, "tb32_17 !full");
       tb32_17.enq(17'h10203);
       dynamicAssert(tb32_17.notEmpty, "tb32_17 not empty");
       dynamicAssert(!tb32_17.notFull, "tb32_17 full");
       $display("tb32_17.first %h", tb32_17.first);
       dynamicAssert(tb32_17.first == (32'h00010203), "Expected 00010203");
       tb32_17.deq;
       dynamicAssert(!tb32_17.notEmpty, "tb32_17 empty");
       dynamicAssert(tb32_17.notFull, "tb32_17 !full");

       //test from bit-32
       dynamicAssert(!fb32_72.notEmpty, "Adapter empty");
       dynamicAssert(fb32_72.notFull, "Adapter not full");
       $display("fb32_72 notEmpty %d notFull %d", fb32_72.notEmpty, fb32_72.notFull);
       fb32_72.enq(32'h00000009);
       fb32_72.enq(32'h08070605);
       fb32_72.enq(32'h04030201);
       $display("fb32_72.first %h", fb32_72.first);
       dynamicAssert(fb32_72.first == 72'h090807060504030201, "Expected 090807060504030201");
       fb32_72.deq;
       fb32_72.enq(32'h09080706);
       dynamicAssert(!fb32_72.notEmpty, "Adapter not empty");
       dynamicAssert(fb32_72.notFull, "Adapter not full");
       fb32_72.enq(32'h05040302);
       fb32_72.enq(32'h01000000);
       dynamicAssert(fb32_72.notEmpty, "Adapter not empty");
       dynamicAssert(!fb32_72.notFull, "Adapter full");
       $display("fb32_72.first %h", fb32_72.first);
       dynamicAssert(fb32_72.first == 72'h060504030201000000, "Expected 060504030201000000");
       fb32_72.deq;
       $display("fb32_72 notEmpty %d notFull %d", fb32_72.notEmpty, fb32_72.notFull);
       dynamicAssert(!fb32_72.notEmpty, "Adapter empty");
       dynamicAssert(fb32_72.notFull, "Adapter not full");
       fb32_17.enq(32'h10203);
       dynamicAssert(fb32_17.notEmpty, "Adapter not empty");
       dynamicAssert(!fb32_17.notFull, "Adapter full");
       $display("fb32_17.first %h", fb32_17.first);
       dynamicAssert(fb32_17.first == 17'h10203, "Expected 10203");
       fb32_17.deq;
       dynamicAssert(!fb32_17.notEmpty, "Adapter empty");
       dynamicAssert(fb32_17.notFull, "Adapter not full");

       // they should be duals
       tb32_72.enq(72'h090807060504030201);
       fb32_72.enq(tb32_72.first);
       tb32_72.deq;
       fb32_72.enq(tb32_72.first);
       tb32_72.deq;
       fb32_72.enq(tb32_72.first);
       tb32_72.deq;
       dynamicAssert(fb32_72.first == 72'h090807060504030201, "Expected 090807060504030201");
       fb32_72.deq;

       tb32_17.enq(17'h10203);
       fb32_17.enq(tb32_17.first);
       tb32_17.deq;
       dynamicAssert(fb32_17.first == 17'h10203, "Expected 10203");
       fb32_17.deq;

       $finish(0);
   endseq
   );

endmodule

// Copyright (c) 2012 MIT
// Copyright (c) 2012 Nokia, Inc.

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

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

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

import GetPut         ::*;
import FIFOF          ::*;
import SpecialFIFOs   ::*;
import StmtFSM        ::*;
import Assert         ::*;

function Bit#(a) rtruncate(Bit#(b) x) provisos(Add#(k,a,b));
   match {.v,.*} = split(x);
   return v;
endfunction

interface ToBit32#(type a);
   method Action enq(a v);
   method Bit#(32) first;
   method Action deq();
   method Bool notEmpty();
   method Bool notFull();
endinterface

interface FromBit32#(type a);
   method Action enq(Bit#(32) v);
   method a first();
   method Action deq();
   method Bool notEmpty();
   method Bool notFull();
endinterface

module mkToBit32(ToBit32#(a))
   provisos(Bits#(a,asz),
            Add#(1, z, asz),
            Div#(asz,32,nwords),
            Mul#(32,nwords,asz32),
            Add#(32,a__,asz32),
            Add#(asz,paddingsz,asz32));

   Bit#(32) size = fromInteger(valueOf(asz));
   Bit#(32) max  = fromInteger(valueOf(nwords)) - 1;
   Bit#(paddingsz) padding = 0;

   FIFOF#(Bit#(asz32)) fifo <- mkFIFOF1();
   Reg#(Bit#(32))   count <- mkReg(0);

   method Action enq(a val);
       fifo.enq({pack(val),padding});
   endmethod

   method Bit#(32) first() if (fifo.notEmpty);
      return rtruncate(fifo.first << (count*32));
   endmethod

   method Action deq() if (fifo.notEmpty);
      if (count == max)
         begin
            count <= 0;
            fifo.deq;
         end
      else
         begin
            count <= count + 1;
         end
   endmethod
   method Bool notEmpty = fifo.notEmpty;
   method Bool notFull = fifo.notFull;
endmodule

module mkFromBit32(FromBit32#(a))
   provisos(Bits#(a,asz),
            Add#(1,z,asz),
            Div#(asz,32,nwords),
            Mul#(32,nwords,asz32),
            Add#(32,a__,asz32),
            Add#(asz,paddingsz,asz32));

   Bit#(32) size   = fromInteger(valueOf(asz));
   Bit#(32) max    = fromInteger(valueOf(nwords))-1;

   Reg#(Bit#(asz32)) fb32buff <- mkReg(0);
   Reg#(Bit#(32))    count <- mkReg(0);
   FIFOF#(Bit#(asz)) fifo <- mkFIFOF1;

   method Action enq(Bit#(32) x) if (count < max || fifo.notFull);
      Bit#(asz32) newbuff = truncate({fb32buff,x});
      fb32buff <= newbuff;
      if (count == max)
         begin
            count <= 0;
            fifo.enq(truncate(newbuff));
         end
      else
         begin
            count <= count+1;
         end
   endmethod

   method a first;
       return unpack(fifo.first);
   endmethod

   method Action deq;
       fifo.deq;
   endmethod

   method Bool notEmpty() = fifo.notEmpty;
   method Bool notFull() = fifo.notFull;
endmodule

module mkAdapterTb(Empty);
   ToBit32#(Bit#(72)) adapter72 <- mkToBit32();
   ToBit32#(Bit#(17)) adapter17 <- mkToBit32();

   FromBit32#(Bit#(72)) fb32_72 <- mkFromBit32();
   FromBit32#(Bit#(17)) fb32_17 <- mkFromBit32();

   Reg#(Bit#(10)) timer <- mkReg(0);
   rule timeout;
       timer <= timer+1;
       dynamicAssert(timer < 64, "Timeout");
   endrule

   mkAutoFSM(
   seq
       adapter72.enq(72'h090807060504030201);
       dynamicAssert(adapter72.notEmpty, "Adapter not empty");
       dynamicAssert(!adapter72.notFull, "Adapter full");
       $display("adapter72 notEmpty %d notFull %d", adapter72.notEmpty, adapter72.notFull);
       $display("adapter72 word 0 %h", adapter72.first());
       dynamicAssert(adapter72.first == 32'h09080706, "expecting 09080706");
       adapter72.deq;
       $display("adapter72 word 1 %h", adapter72.first());
       dynamicAssert(adapter72.first == 32'h05040302, "expecting 05040302");
       adapter72.deq;
       $display("adapter72 word 2 %h", adapter72.first());
       dynamicAssert(adapter72.first == 32'h01000000, "expecting 01000000");
       adapter72.deq;
       dynamicAssert(!adapter72.notEmpty && adapter72.notFull, "Adapter empty and not full");
       $display("adapter72 notEmpty %d notFull %d", adapter72.notEmpty, adapter72.notFull);

       dynamicAssert(!adapter17.notEmpty, "adapter17 empty");
       dynamicAssert(adapter17.notFull, "adapter17 !full");
       adapter17.enq(17'h10203);
       dynamicAssert(adapter17.notEmpty, "adapter17 not empty");
       dynamicAssert(!adapter17.notFull, "adapter17 full");
       $display("adapter17.first %h", adapter17.first);
       dynamicAssert(adapter17.first == (32'h10203<<(32-17)), "Expected 10203<<15");
       adapter17.deq;
       dynamicAssert(!adapter17.notEmpty, "adapter17 empty");
       dynamicAssert(adapter17.notFull, "adapter17 !full");

       dynamicAssert(!fb32_72.notEmpty, "Adapter empty");
       dynamicAssert(fb32_72.notFull, "Adapter not full");
       $display("fb32_72 notEmpty %d notFull %d", fb32_72.notEmpty, fb32_72.notFull);

       fb32_72.enq(32'h00000009);
       fb32_72.enq(32'h08070605);
       fb32_72.enq(32'h04030201);
       $display("fb32_72.first %h", fb32_72.first);
       dynamicAssert(fb32_72.first == 72'h090807060504030201, "Expected 090807060504030201");
       fb32_72.deq;

       fb32_72.enq(32'h09080706);
       dynamicAssert(!fb32_72.notEmpty, "Adapter not empty");
       dynamicAssert(fb32_72.notFull, "Adapter not full");

       fb32_72.enq(32'h05040302);
       fb32_72.enq(32'h01000000);
       dynamicAssert(fb32_72.notEmpty, "Adapter not empty");
       dynamicAssert(!fb32_72.notFull, "Adapter full");

       $display("fb32_72.first %h", fb32_72.first);
       dynamicAssert(fb32_72.first == 72'h060504030201000000, "Expected 060504030201000000");
       fb32_72.deq;
       $display("fb32_72 notEmpty %d notFull %d", fb32_72.notEmpty, fb32_72.notFull);
       dynamicAssert(!fb32_72.notEmpty, "Adapter empty");
       dynamicAssert(fb32_72.notFull, "Adapter not full");

       fb32_17.enq(32'h10203);
       dynamicAssert(fb32_17.notEmpty, "Adapter not empty");
       dynamicAssert(!fb32_17.notFull, "Adapter full");
       $display("fb32_17.first %h", fb32_17.first);
       dynamicAssert(fb32_17.first == 17'h10203, "Expected 10203");
       fb32_17.deq;
       dynamicAssert(!fb32_17.notEmpty, "Adapter empty");
       dynamicAssert(fb32_17.notFull, "Adapter not full");

       $finish(0);
   endseq
   );

endmodule