Sunday, 2 July 2017

Unique constraint in SystemVerilog, Yes it is "Unique"

Sometimes, there is a need to generate unique values of the variables using randomization. There are different ways to generate unique values of variables. System Verilog has provided "unique" keyword which can be used to generate unique values in randomization. 

Below is the example of randomization:
class my_class;
    rand bit [1:0] a, b, c;
    constraint u { unique {a, b, c};}
endclass
  
module top();
    initial begin
    my_class c;
    int cntr;
    c = new();
    
    for(int i = 0; i < 5; i++) begin
        c.randomize();
        $display("a=%0d, b=%0d, c = %0d",c.a, c.b, c.c);
        if((c.a == c.b) || (c.a == c.c) || (c.b == c.c)) begin
            ++cntr;
            $display("value matched at:%0d", cntr);
        end
    end
    end
endmodule
  
Output:

a=2, b=3, c = 1
a=0, b=3, c = 1
a=0, b=2, c = 3
a=2, b=1, c = 3
a=2, b=3, c = 1



As shown in above example, "c" is randomized 5 times but non of the fields of "c" has same value.



Note: randc variable shall not be used with unique constraint.

Friday, 2 June 2017

Advance usage of callback in UVM

Callback is basically used for updating the component's behavior without actually changing the component class. Callback class contains the virtual methods which is used by the component. So, user can update the functionality by extending this callback class which alters the behavior of the component.

Usage of callback class is explained in the UVM user guide (6.3 Callbacks, UVM 1.2 User’s Guide). Here, advanced usage of callback class is explained.User can have multiple class extended from the callback class. All the extended classes will be stored in a queue. So which ever class registered first or added first, its method called first by default followed by the method of the next registered class's. To alter this behavior, user can pass "UVM_PREPEND" in 3rd argument while registering the callback (through "add" method) class which puts the registered class in the top of the queue and its method will be called first before any other callback class's method. 

To check the list of registered callback class, user can call "display" method of "uvm_callbacks" class. It prints the registered callback instances and it's status i.e. ON (enabled) or OFF (disabled). In some cases, user don't want to call the existing functionality implemented through callback. To achieve this functionality, user can disable/enable the callback class by calling "callback_mode(X)" (where X: 0 -> disable, 1-> enable) method of "uvm_callback" class.

Example:

`include "uvm_macros.svh"
package driver_pkg;
import uvm_pkg::*;
typedef class driver;
typedef class driver_cb;
typedef uvm_callbacks #(driver,driver_cb) driver_cbs_t;

class counter_txn extends uvm_transaction;
    rand int cntr;
    virtual function string convert2string();
        convert2string = $sformatf("cntr=%0h", cntr);
    endfunction
endclass

// Driver callback. All callback class shall be extended from this one to use callback method.
virtual class driver_cb extends uvm_callback;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
    endfunction
endclass

class driver extends uvm_component;
    uvm_blocking_put_imp #(counter_txn,driver) in;
    `uvm_register_cb(driver, driver_cb)

    function new (string name="driver", uvm_component parent=null);
        super.new(name,parent);
        in = new("in",this);
    endfunction

    virtual function void trans_received(counter_txn tr);
        `uvm_do_callbacks(driver,driver_cb,trans_received(this,tr))
    endfunction

    virtual task put(counter_txn t);
        uvm_report_info("counter_txn received",t.convert2string());
        trans_received(t);
        uvm_report_info("counter_txn executed",{t.convert2string(),"\n"});
    endtask
endclass
endpackage // driver_pkg

import uvm_pkg::*;
import driver_pkg::*;

// User's callback class implementation
class my_driver_cb1 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb1:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
    endfunction
endclass

class my_driver_cb2 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb2:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
    endfunction
endclass

class my_driver_cb3 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb3:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
   endfunction
endclass

class my_driver_cb4 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb4:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
    endfunction
endclass

module top();
    driver driver1=new();
    my_driver_cb1 cb1=new();
    my_driver_cb2 cb2=new();
    my_driver_cb3 cb3=new();
    my_driver_cb4 cb4=new();
    counter_txn tr = new();
       
    initial begin
        driver_cbs_t::add(driver1, cb1);
        driver_cbs_t::add(driver1, cb2);        
        driver_cbs_t::add(driver1, cb3, UVM_PREPEND);
        driver_cbs_t::add(driver1, cb4, UVM_PREPEND);
        // Disabling cb1 callback
        cb1.callback_mode(0);

        for (int i=1; i<=3; i++) begin
            tr.cntr = i;
            // To display registered callback instances
            driver_cbs_t::display();
            driver1.in.put(tr);
            // Altering cb1 callback mode i.e. enabling<->disabling
           cb1.callback_mode(i[0]);
        end
    end
endmodule  


Above example shows four callback classes are registered with driver class. First "cb1" class instance is added followed by "cb2". So cb1's  method will be called first before cb2's. cb3 and cb4 class instance are registered as "UVM_PREPEND". So, method's will be called in following schedule:
cb4, cb3, cb1, cb2

cb1 class instance is enabled and disabled by "callback_mode" method and to check the status, "display" method is called which indicates status as ON and OFF of registered callback classes. Below is it's output where status of "cb1" is clearly shown as OFF, ON and OFF and other class's status is ON.

Output:

UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of driver
---------------------------------------------------------------
cb4 drv on ON
cb3 drv on ON
cb1 drv on OFF
cb2 drv on ON

UVM_INFO @ 0: drv [counter_txn received] cntr=1
UVM_INFO @ 0: drv [cb4:trans_received_cb] driver:drv tr:cntr=1
UVM_INFO @ 0: drv [cb3:trans_received_cb] driver:drv tr:cntr=1
UVM_INFO @ 0: drv [cb2:trans_received_cb] driver:drv tr:cntr=1
UVM_INFO @ 0: drv [counter_txn executed] cntr=1

UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of driver
---------------------------------------------------------------
cb4 drv on ON
cb3 drv on ON
cb1 drv on ON
cb2 drv on ON

UVM_INFO @ 0: drv [counter_txn received] cntr=2
UVM_INFO @ 0: drv [cb4:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [cb3:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [cb1:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [cb2:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [counter_txn executed] cntr=2

UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of driver
---------------------------------------------------------------
cb4 drv on ON
cb3 drv on ON
cb1 drv on OFF
cb2 drv on ON

UVM_INFO @ 0: drv [counter_txn received] cntr=3
UVM_INFO @ 0: drv [cb4:trans_received_cb] driver:drv tr:cntr=3
UVM_INFO @ 0: drv [cb3:trans_received_cb] driver:drv tr:cntr=3
UVM_INFO @ 0: drv [cb2:trans_received_cb] driver:drv tr:cntr=3
UVM_INFO @ 0: drv [counter_txn executed] cntr=3

Tuesday, 2 May 2017

"this" in system verilog

Many times, we uses the variable name of the method's argument which is similar to the class's property. To identify the current object's property "this" keyword is used in object oriented programming. The "this" keyword denotes a predefined object handle that refers to the object that was used to invoke the subroutine that "this" is used within.

class check_id;
  int incr_id = 0;
  function void set_incr(int incr_id);
    this.incr_id = incr_id;
  endfunction


  function int get_incr();
    return this.incr_id;
  endfunction
endclass

module top();
  check_id id_1;
  initial begin
    id_1 = new();
    id_1.set_incr(1);
    $display("First=%0d",id_1.get_incr());
    id_1.set_incr(2);
    $display("Second=%0d",id_1.get_incr());
  end
endmodule


Output:
First=1
Second=2

"this" keyword can not be used in static class methods as it refers to the instance of the class. It will give compilation error.

Sunday, 2 April 2017

Events in SV and UVM

In this blog, we will see the details of UVM functions use for uvm_event and it can be used in place of SV (system verilog) event.

In system verilog, event data type is used for synchronization of different processes and similar concept is there in UVM which have “uvm_event” class. Here, we will see the similarities between them.

Below is the example code for usage of sv event (Figure 1) and uvm event (Figure 2). Output is shown just below the figure.

In the example, two events (e1_* and e2_*) are used. Both events are triggered followed by waiting on the same event. But due to, "@" operator and "wait_trigger()" method, it doesn't get the event triggered and it waits for the event e1_* to be triggered. In case of, e2_* event, "wait(event.triggered)" and "wait_ptrigger()" method is used which allows process to be unblocked as event is triggered in the same simulation time. From output, the same behavior can be observed.



OUTPUT:

#  e2_sv is triggered at                   20

#  e2_uvm is triggered at                20

#  e2_sv is triggered at                   40
#  e2_uvm is triggered at                40
#  e2_sv is triggered at                   60
#  e2_uvm is triggered at                60


SV & UVM events comparison:


Event Triggering:

In SV, events can be triggered via the “-> event” operator which unblock all processes currently waiting on that event.
In UVM, "event.trigger()" method is used to trigger the event to unblock the processes.

Waiting for event:
In SV, "@(event)" operator is used to wait for an event to be triggered.
In UVM, "event.wait_trigger()" method is used to wait for an event to be triggered.

Persistent trigger:
In SV, "wait (event.triggered)" will unblock the waiting process whether the wait executes before or at the same  simulation time as the trigger operation.
In UVM, "event.wait_ptrigger()" method will unblock the waiting process at same simulation time as the trigger operation.


Thursday, 2 March 2017

How to find UVM port connections between components in the testbench?

UVM provides ports for communicating between components. So that, we can easily transfer packets from one component to another component in blocking/non-blocking mode. These ports can be connected to any components in the testbench based on the requirements. In env, we connect these ports with respective port/export/implementation port to get packets. Occasionally, we come across a situation when a driver put the packet in the port but the packet is not received in the respective component.

Here, first question comes to our mind is, whether the ports are connected properly or not. For that, UVM has provided “debug_connected_to” and “debug_provided_to” methods which gives a print of procedural connected components  through TLM which make debugging task easier. So, if the components are not connected properly then we can update the port connection to resolve the problem.

debug_connected_to” method gives display of the implementation/export to which this port is connected i.e. port is driving packets to which port/export/imp. “debug_provided_to” methods gives display of the port/export to which this imp/export is connected i.e. imp/export is getting data from which port/export.

Below is an example of port-export connection which is similar to the example given in UVM 1.2 Class Reference (Topic 14. TLM1 Interfaces, Ports, Exports and Transport Interfaces).

Figure 1 

Here, comp1, subcomp1 and leaf1 components are connected via port and leaf1 component put the packets on the port. Components comp2, subcomp2 and leaf2 components are connected through export and in leaf2 component contains implementation put method. Components comp1 and comp2 are connected through port-export connection.

  • Output of “out.debug_connected_to()” of “comp1”:
It describes the export-imp connection chain of all components with full hierarchy details and port type i.e. where the put method is implemented.

# UVM_INFO @ 0: env.comp1.out [debug_connected_to] This port's fanout network:
#   env.comp1.out (uvm_blocking_put_port)
#     |
#     |_env.comp2.in (uvm_blocking_put_export)
#         |
#         |_env.comp2.subcomp2.in (uvm_blocking_put_export)
#             |
#             |_env.comp2.subcomp2.leaf2.in (uvm_blocking_put_imp)
#   Resolved implementation list:

#   0: env.comp2.subcomp2.leaf2.in (uvm_blocking_put_imp)

As shown above, comp1’s put port (out) is connected with comp2’s put export (in) which is connected with subcomp2’s put export (in) and its implementation method is n leaf2 component which has put imp (in). 
  • Output of "out.debug_provided_to()" of "comp1":
It describes the port connection chain of all components with full hierarchy details and port type i.e. from where the component put the packet on the port.

# UVM_INFO @ 0: env.comp1.out [debug_provided_to] This port's fanin network:
#   env.comp1.out (uvm_blocking_put_port)
#     |
#     |_env.comp1.subcomp1.out (uvm_blocking_put_port)
#         |
#         |_env.comp1.subcomp1.leaf1.out (uvm_blocking_put_port)

As shown above, comp1’s out port is connected with subcomp1’s out port which is connected with leaf1’s out port. Here, leaf1 component drives packets on the port. So, up to “leaf1.out” port hierarchy is printed in the output.


Similarly, we can get information for any port/export. Below is the output for “in” export of comp2 component.
  • Output of “in.debug_connected_to()” of “comp2”:
             # UVM_INFO @ 0: env.comp2.in [debug_connected_to] This port's fanout network:
             #   env.comp2.in (uvm_blocking_put_export)
             #     |
             #     |_env.comp2.subcomp2.in (uvm_blocking_put_export)
             #         |
             #         |_env.comp2.subcomp2.leaf2.in (uvm_blocking_put_imp)
             #
             #   Resolved implementation list:
             #   0: env.comp2.subcomp2.leaf2.in (uvm_blocking_put_imp)
  • Output of “in.debug_provided_to()” of “comp2”:
             # UVM_INFO @ 0: env.comp2.in [debug_provided_to] This port's fanin network:
             #   env.comp2.in (uvm_blocking_put_export)
             #     |
             #     |_env.comp1.out (uvm_blocking_put_port)
             #         |
             #         |_env.comp1.subcomp1.out (uvm_blocking_put_port)
             #             |
             #             |_env.comp1.subcomp1.leaf1.out (uvm_blocking_put_port)

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.

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