// Copyright (c) 2015-2019 Bluespec, Inc., All Rights Reserved
// Author: Rishiyur S. Nikhil

// A "credit counter" which can be incremented and decremented concurrently.

package CreditCounter;

// ================================================================
// BSV library imports

import Cur_Cycle :: *;

// ================================================================
// Interface

interface CreditCounter_IFC #(numeric type w);
   // Current value of internal count
   method UInt #(w) value;

   // Increment internal count
   method Action incr;

   // Decrement internal count
   method Action decr;

   // Clear internal count to 0
   method Action clear;
endinterface

// ================================================================
// Module implementation
// Scheduling: value < incr < decr < clear

module mkCreditCounter (CreditCounter_IFC #(w));

   Reg #(UInt #(w)) crg [3] <- mkCReg (3, 0);

   method UInt #(w) value = crg [1];

   method Action incr if (crg [0] != maxBound);
      if (crg [0] == maxBound) begin
	 $display ("%0d: ERROR: CreditCounter: overflow", cur_cycle);
	 $finish (1);    // Assertion failure
      end
      crg [0] <= crg [0] + 1;
   endmethod

   method Action decr () if (crg [1] != 0);
      if (crg [1] == 0) begin
	 $display ("%0d: ERROR: CreditCounter: underflow", cur_cycle);
	 $finish (1);    // Assertion failure
      end
      crg [1] <= crg [1] - 1;
   endmethod

   method Action clear;
      crg [2] <= 0;
   endmethod
endmodule

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

endpackage
