Tuesday, 2 October 2018

Passing Multiple Enum through command line using UVM1.2

This post is related to my previous blog "http://munjalm.blogspot.com/2018/09/passing-enum-through-command-line_2.html" which describes passing enum through command line.

Consider a case where, user wants to randomize enum from few enum values only. In this case, multiple enums can be passed through command line using a delimiter. For example, +DATA_TYPE=DT_2,DT_4.

As "from_name" function converts the string into enum, the above string can be separated out and can be used in "from_name" to get the enum values. To get substring from a string, there are multiple ways:
(1) System verilog provides "substr" function through which we can get the substring. But in the case, we shall know the exact location from where we can the substring.
(2) Go through each character of a string and compare each character with delimiter. If it matches then separate out the sub string.

Here, UVM provides "uvm_split_string" function which splits the string based on a delimiter and returns a queue with sub string. This function contains 3 arguments which are described below:
First arg (string): Main string which will be divided into multiple string
Second arg(byte): Delimiter from where the main string will be separated
Third arg(string queue): It's a reference variable which contains all sub-strings separated with delimiter from main string.

Below is the example which helps to understand more:

Example:
  typedef enum {DT_1, DT_2, DT_3, DT_4} data_type_e;
  typedef uvm_enum_wrapper#(data_type_e) uvm_dt;
  data_type_e dt, dt_1;
  string str;

  initial begin
      string str_q[$];
      data_type_e dt_q[$];

      void'($value$plusargs("DATA_TYPE=%0s", str));

      uvm_split_string(str, ",", str_q);

      foreach (str_q[i]) begin
          if(!(uvm_dt::from_name(str_q[i], dt))) begin
              $display("Wrong Data Type Provided.");
          end
          else begin
              $display("Data Type=%0s",dt.name());
              dt_q.push_back(dt);
          end
      end
      foreach(dt_q[i]) $display("DT=%0s",dt_q[i].name());
      for(int i = 0; i < 10; ++i) begin
          if(!randomize(dt_1) with {dt_1 inside {dt_q};})
              $error("Randomization Failed.");
          else
              $display("Randomized DT=%0s", dt_1.name());
      end
  end

Command with Questa-tool:
vsim "+DATA_TYPE=DT_2,DT_4" -c -do "run -all;q" -f questa.tops

When above command is executed, dt_l value will be generated from DT_2 or DT_4 enum during randomization. 
Here, "DT_2,DT_4" is taken as a single string and is splitted across multiple string using "uvm_split_string" function and the splitted string is stored in "str_q" queue. Then the splitted string is converted into enum using "from_name" function and stored into "dt_q" queue from which dt_l is randomized.



Sunday, 2 September 2018

Passing enum through command line argument using UVM 1.2

In verification activity, many of us came across a situation where we need to regenerate bugs with specific scenario or to achieve coverage, we need to create scenario with specific value. In this case, generalized sequences are very useful which takes command line arguments as input and generate those scenarios. So we don't need to modify the existing code or don't require to code new scenarios.

Data types like int or string can be easily passed through command line argument and the value is get into the code using "$value$plusargs" system verilog function. 

Now, consider a case where a scenario required with specific enum value. System verilog doesn't support for providing enum name through command line argument directly. So in that case, either string shall be passed through command line and it shall be converted into enum in the code or enum type shall be passed through command line and it shall be cast to enum type. 

In UVM 1.2, a feature is added for passing enum through command line and let's explore this feature with detail example: 

In the below example, an enum "data_type_e" is declared and to get its value through command line argument, it is defined with "uvm_enum_wrapper" as this class contains method "from_name" through which any argument passed as a string through command line, can be converted into the defined enum. Here, any enum values can be passed through command line argument as a string and in the code it gets as a string using "$value$plusargs" and the string is passed through "from_name" function to get its correct enum value and it can be used in the code. If enum value is not defined then this function returns 0. 

Example:
  typedef enum {DT_1, DT_2, DT_3, DT_4} data_type_e;
  typedef uvm_enum_wrapper#(data_type_e) uvm_dt;
  data_type_e dt;
  string str;

  initial begin
    void'($value$plusargs("DATA_TYPE=%0s", str));

    if(!(uvm_dt::from_name(str, dt)))
      $display("Wrong Data Type Provided.");
    else
      $display("Data Type=%0s",dt.name());

While simulating above code with command line argument  "+DATA_TYPE=DT_4", it receives DT_4 enum value. Here, DT_5 is passed which is not a part of enum values then "from_name" will return 0.

Command with Questa Tool:
vsim "+DATA_TYPE=DT_4" -c -do "run -all; q"  -f questa.tops

Output:
Data Type=DT_4

Saturday, 2 September 2017

wait fork in system verilog

In System Verilog, fork .. join keyword is used for parallel processing. To control these parallel processes, various constructs are provided in the language. One of them is "wait .. fork" and its usage is described in below example.

"wait .. fork" is used to block the execution until all sub-processes (processes created by current process) are completed.

Below is an example in which 2 parallel processes are going on. First process A require 100 time duration and another process B needs 50 time duration. As "fork .. join_any" is used, the process B is completed at 50. To wait to completed other sub-processes (process A), "wait fork" is used which blocks the execution until process A is also completed. It unblocks the execution after 100 time duration once process A and B. The output of Example-1 is shown exactly the same behavior. 

Example-1:
initial begin
    fork
        begin
            #100; // process A
        end
        begin
            #50; // process B
        end
    join_any
    $display($time," Waiting for fork completion.");
    wait fork;
    $display($time," Wait fork done.");
end

Output of Example-1:
50 Waiting for fork completion.
100 Wait fork done.

Here, if any process contains any other "fork .. join_none" or "fork .. join_any" sub-processes, then this "wait fork" statement will wait for completion of all the "fork" sub-processes. Below is the similar example as given above. In it, "fork .. join_none" contains process C which takes 200 time to complete. So the "wait fork" will take 200 time duration to unblock the entire process. Output of example-2 indicates the same behavior.  

Example-2:
initial begin
    fork
        #200; // process C
    join_none
    fork
        begin
            #100; // process A
        end
        begin
            #50; // process B
        end
    join_any
    $display($time," Waiting for fork completion.");
    wait fork;
    $display($time," Wait fork done.");
end

Output of Example-2:
 50 Waiting for fork completion.
 200 Wait fork done.


Wednesday, 2 August 2017

Packed Array index selection in system verilog

Array part selection syntax is bit confusing in system verilog and sometimes it requires to make an example to recall it. To avoid it, an example is shown below which helps to understand the address part selection of packed array.
Let's understand it from an example:

Example:
module top();
  bit [15:0] a_msb;
  bit [0:15] a_lsb;

  initial begin
    a_msb = 'h8465;
    a_lsb = 'h8465;
    $display("O0: *******************");
    $display("a_msb=%0h(%0b), \na_lsb=%0h(%0b)",a_msb,a_msb,a_lsb,a_lsb);

    $display("O1: *******************");
    $display("a_msb[7:0]=%08b, a_lsb[0:7]=%08b",a_msb[7:0], a_lsb[0:7]);
    $display("a_msb[15:8]=%08b, a_lsb[8:15]=%08b",a_msb[15:8], a_lsb[8:15]);

    $display("O2: *******************");
    for(int i = 0; i < 16; ++i) begin
      $display("a_msb[%0d]=%0b, a_lsb[%0d]=%0b",i,a_msb[i],i,a_lsb[i]);
    end

    $display("O3: *******************");
    for(int sel = 0; sel < 2; ++sel) begin
      $display("a_msb[%0d +: 8]=%08b",8*sel,a_msb[8*sel +: 8]);
      $display("a_lsb[%0d +: 8]=%08b",8*sel,a_lsb[8*sel +: 8]);
    end

    $display("O4: *******************");
    for(int sel = 2; sel > 0; --sel) begin
      $display("a_msb[%0d -: 8]=%08b",(8*sel - 1),a_msb[(8*sel - 1) -: 8]);
      $display("a_lsb[%0d -: 8]=%08b",(8*sel - 1),a_lsb[(8*sel - 1) -: 8]);
    end
  end
endmodule

As shown in the above example, 2 fields (a_msb and a_lsb) of 16 bits width are declared. In a_msb, the MSB is 15 and in a_lsb, MSB is 0. In both fields, 'h8465 value is stored. While printing both variables without indexing, it prints same (Ref Output: O0). 

But the indexing of both fields are different to each other. It means while accessing the index value of both variables, output will be different (Ref Output: O1). The reason behind it is the MSB and LSB are different for both variables. As a_msb's lsb bit is 0, 'h5 is stored on location [3:0]. On other hand, a_lsb's lsb bit is 15, so 'h5 is stored on location [12:15]. Similarly, other values are stored on different index and same is reflected in Output O1 and O2.

As shown in Output O3, index part selection is taken care automatically by system verilog. By comparing Output O2 with Output O3 and O4, we can say that
a_msb[7:0] = a_msb[0 +: 8] = a_msb[7 -: 8] and
a_lsb[0:7]   = a_lsb[0 +:8]    = a_lsb[7 -: 8]

Output:
O0: *******************
a_msb=8465(1000010001100101), 
a_lsb=8465(1000010001100101)

O1: *******************
a_msb[7:0]=01100101, a_lsb[0:7]=10000100
a_msb[15:8]=10000100, a_lsb[8:15]=01100101

O2: *******************
a_msb[0]=1, a_lsb[0]=1
a_msb[1]=0, a_lsb[1]=0
a_msb[2]=1, a_lsb[2]=0
a_msb[3]=0, a_lsb[3]=0
a_msb[4]=0, a_lsb[4]=0
a_msb[5]=1, a_lsb[5]=1
a_msb[6]=1, a_lsb[6]=0
a_msb[7]=0, a_lsb[7]=0
a_msb[8]=0, a_lsb[8]=0
a_msb[9]=0, a_lsb[9]=1
a_msb[10]=1, a_lsb[10]=1
a_msb[11]=0, a_lsb[11]=0
a_msb[12]=0, a_lsb[12]=0
a_msb[13]=0, a_lsb[13]=1
a_msb[14]=0, a_lsb[14]=0
a_msb[15]=1, a_lsb[15]=1

O3: *******************
a_msb[0 +: 8]=01100101
a_lsb[0 +: 8]=10000100
a_msb[8 +: 8]=10000100
a_lsb[8 +: 8]=01100101

O4: *******************
a_msb[15 -: 8]=10000100
a_lsb[15 -: 8]=01100101
a_msb[7 -: 8]=01100101
a_lsb[7 -: 8]=10000100

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:
Example:
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.

Example:
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.