In this blog, we will see how to do synchronization using uvm_objection.
UVM provides "raise_objection" method for raising objections and "drop_objection" method for dropping objections in the testbench. These methods are the part of "uvm_objection" class. We will see, how these methods synchronizes various tasks in the component.
Below is an example of a normal code where an objection is raised and then the two tasks (response and process_txn) are executed in parallel. One can assume that, after getting transaction from "seq_item_port", the driver is executing that transaction and receiving response for the same. In "process_txn" task, two tasks are called in a serial way, "process_header" followed by "process_data". Once execution of "process_header" task is completed, "process_resp" event is triggered which executes "response" task (from waiting state of the same event). In parallel with that, "process_data" task is executed. Once "response" and "process_txn" tasks are completed, an objection is dropped.
Method 1:
event process_resp;
task run_phase(uvm_phase phase);
...
for(int i = 0; i < 10; ++i)
begin
phase.raise_objection(this);
fork
response();
process_txn();
join
phase.drop_objection(this);
`uvm_info("run_phase","Done", UVM_LOW)
end
`uvm_info("run_phase","Run phase completed", UVM_LOW)
endtask : run_phase
task process_txn();
process_header();
-> process_resp;
process_data();
endtask : process_txn
task process_header();
...
#20;
endtask
task process_data();
...
#15;
endtask
task response();
...
@(process_resp);
`uvm_info(get_full_name(),$sformatf("Processing Response"), UVM_LOW)
#25;
endtask : response
...
for(int i = 0; i < 10; ++i)
begin
phase.raise_objection(this);
fork
response();
process_txn();
join
phase.drop_objection(this);
`uvm_info("run_phase","Done", UVM_LOW)
end
`uvm_info("run_phase","Run phase completed", UVM_LOW)
endtask : run_phase
task process_txn();
process_header();
-> process_resp;
process_data();
endtask : process_txn
task process_header();
...
#20;
endtask
task process_data();
...
#15;
endtask
task response();
...
@(process_resp);
`uvm_info(get_full_name(),$sformatf("Processing Response"), UVM_LOW)
#25;
endtask : response
The same thing can be achieved by using "uvm_objection" methods.
"raise_objection" and "drop_objection" methods has three arguments:
obj - Handle of the calling class usually "this"
description - String for indicating specific objection
description - String for indicating specific objection
count - Raise or drop number of objection, default value is 1.
Whenever "raise_objection" method is called, by default it raise one objection and one objection is dropped while calling "drop_objection" method. On 3rd argument, we can pass any number which raise/drop that many objections.
UVM objection class has two methods which helps you to synchronize the processes based on these objections. You can wait on objection raise/drop event or on objection count value. Below are the two methods:
(1) "wait_for" method is used for event based waiting. Event can be raising an objection, dropping an objection or dropping all objections. It has two arguments:
objt_event - It is an enum having values "UVM_RAISED" (triggers when an objection raised), "UVM_DROPPED" (triggers when an objection dropped) and "UVM_ALL_DROPPED" (triggers when all objection dropped).
obj - Handle of uvm_object class on which triggering of event depends
(2) "wait_for_total_count" method is used for waiting until objection count reaches to a specific value. It also has two arguments:
obj - Handle of uvm_object class usually this
count - Wait until the objection count of the obj object reaches to this value
count - Wait until the objection count of the obj object reaches to this value
By using these features, above example code could be written in following way:
Method 2:
uvm_objection obj;
task run_phase(uvm_phase phase);obj = phase.get_objection();
for(int i = 0; i < 10; ++i)
begin
phase.raise_objection(this, "Rasing Objection", 2);
fork
response();
process_txn(phase);
join
phase.drop_objection(this);
`uvm_info("run_phase","Done", UVM_LOW)
end
`uvm_info("run_phase","Run phase completed", UVM_LOW)
endtask : run_phase
task process_txn(uvm_phase phase);
process_header();
phase.drop_objection(this);
process_data();
endtask : process_txn
task process_header();
...
#20;
endtask
task process_data();
...
#15;
endtask
task response();
...
obj.wait_for(UVM_DROPPED, this) // or "obj.wait_for_total_count(this, 1);"
`uvm_info(get_full_name(),$sformatf("Processing Response"), UVM_LOW)
#25;
endtask : response
Note that, 2 objections are raised in the above example. An objection is dropped after executing "process_header" task. So, total raised objection count is set to 1. In "response" task, "wait_for" method is used for waiting on objection drop event in this class ("wait_for_total_count" method is used for waiting objection count reaches to 1). So, it starts execution in parallel to "process_data" task. Once "response" and "process_txn" tasks are completed, an objection will be dropped.
Here, both methods give same output result.