//
//------------------------------------------------------------------------------
//   Copyright 2007-2010 Mentor Graphics Corporation
//   Copyright 2007-2010 Cadence Design Systems, Inc.
//   Copyright 2010 Synopsys, Inc.
//   All Rights Reserved Worldwide
//
//   Licensed under the Apache License, Version 2.0 (the
//   "License"); you may not use this file except in
//   compliance with the License.  You may obtain a copy of
//   the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in
//   writing, software distributed under the License is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//   CONDITIONS OF ANY KIND, either express or implied.  See
//   the License for the specific language governing
//   permissions and limitations under the License.
//------------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//
// CLASS: uvm_barrier
//
// The uvm_barrier class provides a multiprocess synchronization mechanism. 
// It enables a set of processes to block until the desired number of processes
// get to the synchronization point, at which time all of the processes are
// released.
//-----------------------------------------------------------------------------

class [docs]uvm_barrier extends uvm_object;

  local  int       threshold;
  local  int       num_waiters;
  local  bit       at_threshold;
  local  bit       auto_reset;
  local  uvm_event#(uvm_object) m_event;


  // Function: new
  //
  // Creates a new barrier object.

  function [docs]new (string name="", int threshold=0);
    super.new(name);
    m_event = new({"barrier_",name});
    this.threshold = threshold;
    num_waiters = 0;
    auto_reset = 1;
    at_threshold = 0;
  endfunction


  // Task: wait_for
  //
  // Waits for enough processes to reach the barrier before continuing. 
  //
  // The number of processes to wait for is set by the <set_threshold> method.

  virtual task [docs]wait_for();

    if (at_threshold)
      return;

    num_waiters++;

    if (num_waiters >= threshold) begin
      if (!auto_reset)
        at_threshold=1;
      m_trigger();
      return;
    end

    m_event.wait_trigger();

  endtask

  
  // Function: reset
  //
  // Resets the barrier. This sets the waiter count back to zero. 
  //
  // The threshold is unchanged. After reset, the barrier will force processes
  // to wait for the threshold again. 
  //
  // If the ~wakeup~ bit is set, any currently waiting processes will
  // be activated.

  virtual function void [docs]reset (bit wakeup=1);
    at_threshold = 0;
    if (num_waiters) begin
      if (wakeup)
        m_event.trigger();
      else
        m_event.reset();
    end
    num_waiters = 0;
  endfunction


  // Function: set_auto_reset
  //
  // Determines if the barrier should reset itself after the threshold is
  // reached. 
  //
  // The default is on, so when a barrier hits its threshold it will reset, and
  // new processes will block until the threshold is reached again. 
  //
  // If auto reset is off, then once the threshold is achieved, new processes
  // pass through without being blocked until the barrier is reset.

  virtual function void [docs]set_auto_reset (bit value=1);
    at_threshold = 0;
    auto_reset = value;
  endfunction


  // Function: set_threshold
  //
  // Sets the process threshold. 
  //
  // This determines how many processes must be waiting on the barrier before
  // the processes may proceed. 
  //
  // Once the ~threshold~ is reached, all waiting processes are activated. 
  //
  // If ~threshold~ is set to a value less than the number of currently
  // waiting processes, then the barrier is reset and waiting processes are
  // activated.

  virtual function void [docs]set_threshold (int threshold);
    this.threshold = threshold;
    if (threshold <= num_waiters)
      reset(1);
  endfunction


  // Function: get_threshold
  //
  // Gets the current threshold setting for the barrier.

  virtual function int [docs]get_threshold ();
    return threshold;
  endfunction

  
  // Function: get_num_waiters
  //
  // Returns the number of processes currently waiting at the barrier.

  virtual function int [docs]get_num_waiters ();
    return num_waiters;
  endfunction


  // Function: cancel
  //
  // Decrements the waiter count by one. This is used when a process that is
  // waiting on the barrier is killed or activated by some other means.

  virtual function void [docs]cancel ();
    m_event.cancel();
    num_waiters = m_event.get_num_waiters();
  endfunction


  const static string type_name = "uvm_barrier";

  virtual  function uvm_object [docs]create(string name=""); 
    uvm_barrier v;
    v=new(name);
    return v;
  endfunction

  virtual  function string [docs]get_type_name();
    return type_name;
  endfunction

  local task m_trigger();
    m_event.trigger();
    num_waiters=0;
    #0; //this process was last to wait; allow other procs to resume first
  endtask

  virtual function void [docs]do_print (uvm_printer printer);
    printer.print_field_int("threshold", threshold, $bits(threshold), UVM_DEC, ".", "int");
    printer.print_field_int("num_waiters", num_waiters, $bits(num_waiters), UVM_DEC, ".", "int");
    printer.print_field_int("at_threshold", at_threshold, $bits(at_threshold), UVM_BIN, ".", "bit");
    printer.print_field_int("auto_reset", auto_reset, $bits(auto_reset), UVM_BIN, ".", "bit");
  endfunction

  virtual function void [docs]do_copy (uvm_object rhs);
    uvm_barrier b;
    super.do_copy(rhs);
    if(!$cast(b, rhs) || (b==null)) return;

    threshold = b.threshold;
    num_waiters = b.num_waiters;
    at_threshold = b.at_threshold;
    auto_reset = b.auto_reset;
    m_event = b.m_event;
  endfunction  

endclass