As VIP developers, we have to develop our VIP flexible, so
that user can achieve his requirement easily without our dependency. For that, user should have support of configurations, callbacks,
factory override etc. in the VIP. In this blog, we will see a feature of UVM which helps user to control the data flow or processes.
In my previous blog "Synchronization using UVM features" (Feb'16), we saw that, how to use UVM events and barrier for
synchronization of processes. In this blog, we will see how to disable the
triggering of any UVM event which gives control to user by avoiding further processing of data/packet.
As a testbench component developer, we may use the UVM events for synchronization
of processes for example once packet is driven properly from driver and
relative response is received then triggers an event which do further
processing and put that packet on to the analysis port for scoreboarding or
coverage sampling. This driver component may be used by number of people and as a VIP developer we
don’t know their requirements.
Consider a case where a user don’t want to receive any erroneous packet
in scoreboard or coverage. Here, we can provide support for user
to disable the triggering of event in his testbench. So that, packet will not
be broadcasted on analysis port. This can be achieve using configuration also. But it requires extra variables in the testbench.
To disable triggering of UVM event, UVM event callback class is required. User has to register an UVM event callback class with the specific event. In that callback class, user
can override the “pre_trigger” method to disable the triggering
of the event. If this method returns "1" then event will not be triggered otherwise it will be triggered. This method is called before triggering of any event.
This method has two arguments:
(1) uvm_event: The event which is calling this callback method
(2) uvm_object: UVM Object class can be passed with the triggering of the event
This method has two arguments:
(1) uvm_event: The event which is calling this callback method
(2) uvm_object: UVM Object class can be passed with the triggering of the event
Below is the example to disable the triggering of UVM event which demonstrate the usage.
Example: module top();
...
uvm_barrier b;
class my_object extends uvm_object;
int cntr;
...
endclass
class my_event_callback extends uvm_event_callback;
...
virtual function bit pre_trigger (uvm_event e, uvm_object data);
my_object obj;
$cast(obj, data);
if(obj.cntr >= 3) return 1;
else return 0;
endfunction
endclass
// Task - 1
task task_a(input uvm_barrier _b);
int delay;
repeat(5) begin
delay = $urandom_range(10, 100);
#delay;
_b.wait_for();
end
endtask : task_a
// Task - 2
task task_b(input uvm_barrier _b);
int delay;
repeat(5) begin
delay = $urandom_range(50, 100);
#delay;
_b.wait_for();
end
endtask : task_b
task get_event(output uvm_event e_max_process);
uvm_event_pool e_pool;
if(e_pool == null)
e_pool = new("e_pool");
e_pool = e_pool.get_global_pool();
e_max_process = e_pool.get("e_max_process");
endtask
initial begin
// Creates a barrier object named "Barrier" and it's threshold value will
// be 3.
b = new("Barrier",3); // Wait for 3 process to be completed
fork
task_a(._b(b));
task_b(._b(b));
trigger_event(b);
process_on_event();
join
end
task trigger_event(uvm_barrier b);
uvm_event e_max_process;
my_event_callback cb;
my_object obj;
int cntr;
obj = new();
cb = new();
get_event(e_max_process);
e_max_process.add_callback(cb);
forever begin
b.wait_for();
#0;
++cntr;
obj.cntr = cntr;
$display($time," e_max_process[%0d] event being triggered",b.get_threshold());
e_max_process.trigger(obj);
#10;
$display($time," e_max_process event[%0d] reset being done",b.get_threshold());
e_max_process.reset();
end
endtask
task process_on_event();
uvm_event e_max_process;
get_event(e_max_process);
forever begin
e_max_process.wait_trigger();
$display($time," e_max_process event triggered");
e_max_process.wait_off();
end
endtask
endmodule
As shown above, there is an event "e_max_process" which is triggered (in trigger_event task) once synchronization done between "task_a" and "task_b" through "uvm_barrier" followed by resetting the same event. In "process_on_event" task, first it waits for the triggering of same event and followed by waiting on resetting it.
With "e_max_process" event, "my_event_callback" class is registered and with triggering of the event object of "my_object" class is passed as an argument. So before triggering this event, "pre_trigger" method of callback class is called and it has data of "my_object" passed with the event.
In the "pre_trigger" method, when the "cntr" field of "my_object" is greater than or equals to 3 then it returns 1 otherwise it returns 0. That means, when cntr value is greater than or equals to 3, the event will not be triggered. This "cntr" field is incremented to one before triggering of the event.
Below is the simulation result of the example which shows "e_max_process" event is triggered on 85ns and 185ns i.e. 2 times it triggered. Then it is not triggered as "pre_trigger" function returns 1.
Output:
85 e_max_process[3] event being triggered
85 e_max_process event triggered
95 e_max_process event[3] reset being done
185 e_max_process[3] event being triggered
185 e_max_process event triggered
195 e_max_process event[3] reset being done
268 e_max_process[3] event being triggered
278 e_max_process event[3] reset being done
368 e_max_process[3] event being triggered
378 e_max_process event[3] reset being done
447 e_max_process[3] event being triggered
457 e_max_process event[3] reset being done
Example: module top();
...
uvm_barrier b;
class my_object extends uvm_object;
int cntr;
...
endclass
class my_event_callback extends uvm_event_callback;
...
virtual function bit pre_trigger (uvm_event e, uvm_object data);
my_object obj;
$cast(obj, data);
if(obj.cntr >= 3) return 1;
else return 0;
endfunction
endclass
// Task - 1
task task_a(input uvm_barrier _b);
int delay;
repeat(5) begin
delay = $urandom_range(10, 100);
#delay;
_b.wait_for();
end
endtask : task_a
// Task - 2
task task_b(input uvm_barrier _b);
int delay;
repeat(5) begin
delay = $urandom_range(50, 100);
#delay;
_b.wait_for();
end
endtask : task_b
task get_event(output uvm_event e_max_process);
uvm_event_pool e_pool;
if(e_pool == null)
e_pool = new("e_pool");
e_pool = e_pool.get_global_pool();
e_max_process = e_pool.get("e_max_process");
endtask
initial begin
// Creates a barrier object named "Barrier" and it's threshold value will
// be 3.
b = new("Barrier",3); // Wait for 3 process to be completed
fork
task_a(._b(b));
task_b(._b(b));
trigger_event(b);
process_on_event();
join
end
task trigger_event(uvm_barrier b);
uvm_event e_max_process;
my_event_callback cb;
my_object obj;
int cntr;
obj = new();
cb = new();
get_event(e_max_process);
e_max_process.add_callback(cb);
forever begin
b.wait_for();
#0;
++cntr;
obj.cntr = cntr;
$display($time," e_max_process[%0d] event being triggered",b.get_threshold());
e_max_process.trigger(obj);
#10;
$display($time," e_max_process event[%0d] reset being done",b.get_threshold());
e_max_process.reset();
end
endtask
task process_on_event();
uvm_event e_max_process;
get_event(e_max_process);
forever begin
e_max_process.wait_trigger();
$display($time," e_max_process event triggered");
e_max_process.wait_off();
end
endtask
endmodule
As shown above, there is an event "e_max_process" which is triggered (in trigger_event task) once synchronization done between "task_a" and "task_b" through "uvm_barrier" followed by resetting the same event. In "process_on_event" task, first it waits for the triggering of same event and followed by waiting on resetting it.
With "e_max_process" event, "my_event_callback" class is registered and with triggering of the event object of "my_object" class is passed as an argument. So before triggering this event, "pre_trigger" method of callback class is called and it has data of "my_object" passed with the event.
In the "pre_trigger" method, when the "cntr" field of "my_object" is greater than or equals to 3 then it returns 1 otherwise it returns 0. That means, when cntr value is greater than or equals to 3, the event will not be triggered. This "cntr" field is incremented to one before triggering of the event.
Below is the simulation result of the example which shows "e_max_process" event is triggered on 85ns and 185ns i.e. 2 times it triggered. Then it is not triggered as "pre_trigger" function returns 1.
Output:
85 e_max_process[3] event being triggered
85 e_max_process event triggered
95 e_max_process event[3] reset being done
185 e_max_process[3] event being triggered
185 e_max_process event triggered
195 e_max_process event[3] reset being done
268 e_max_process[3] event being triggered
278 e_max_process event[3] reset being done
368 e_max_process[3] event being triggered
378 e_max_process event[3] reset being done
447 e_max_process[3] event being triggered
457 e_max_process event[3] reset being done