This post is about one of UVM synchronization. Well, we all use phases which is implicitly one way to synchronize tasks(build, connect, run). Anyway, barriers is not a new thing it is used a lot in multi-thread/kernel development.

The most import method is wait_for. The processes call it at the point where it wants other processes. In this example, both process will continue after 20 delay.

uvm_barrier b = new('b_test' , 2);
initial begin
#10;
b.wait_for();
end

initial begin
#20;
b.wait_for();
end

Let’s dig deeper into UVM implementation. Starting with new, It is important threashold is set there and some other init stuff happening there as well.

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

Every waiter that calls wait_for increments num_waiters and calls for wait_trigger. Note there is 2 importnat cases here:

  • num_waiters less than threshold, in the case, it will just wait on m_event.
  • The important case here where num_waiters >= threshold, It will trigger other waiters with m_trigger and return.
  virtual task 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

m_trigger is simple enough. It will just trigger the even and reset the num_waiters to 0.

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