Thursday, 2 February 2017

How to disable triggering of an UVM event

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

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