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