Sunday, 25 November 2018

Compilation switches vs Simulation switches in SystemVerilog

Compilation Switches:

Verilog has following conditional compiler directives. 
  • `ifdef
  • `else
  • `elsif
  • `endif
  • `ifndef
The `ifdef compiler directive checks for the definition of a text_macro_name. If the text_macro_name is defined, then the lines following the `ifdef directive are included. If the text_macro_name is not defined and an `else directive exists, then this source is compiled.

The `ifndef compiler directive checks for the definition of a text_macro_name. If the text_macro_name is not defined, then the lines following the `ifndef directive are included. If the text_macro_name is defined and an `else directive exists, then this source is compiled.

If the `elsif directive exists (instead of the `else) the compiler checks for the definition of the text_macro_name. If the name exists the lines following the `elsif directive are included. The `elsif directive is equivalent to the compiler directive sequence `else `ifdef ... `endif. This directive does not need a corresponding `endif directive. This directive must be preceded by an `ifdef or `ifndef directive.


The code written within the compilation flags are compiled at compilation period.
As a result, the executable file creates a snap-shot of that particular code written within the compile flag.
For example, please go through below code.
LINE 1 :                     module xyz ()
LINE 2 :                        `ifdef FIRST
LINE 3 :                           $display ("Inside FIRST");
LINE 4 :                        `elseif SECOND
LINE 5 :                           $display ("Inside SECOND");
LINE 6 :                        `elseif THIRD
LINE 7 :                           $display ("Inside THIRD");
LINE 8 :                        `else
LINE 9 :                           $display ("Inside ELSE");
LINE 10 :                     `endif
LINE 11 :                     $display ("Inside LAST");
LINE 12 :                   endmodule  : xyz

 
Now compile the code with define "+define+FIRST".

The executable file will contains snapshot of "LINE 3 & LINE 11"
Now simulating the executable file, output will be as:
Inside FIRST
Inside LAST

 
Now we re-simulate(without compiling again), and pass define "+define+SECOND" at run time, output will be as:
Inside FIRST
Inside LAST

 
Now we re-simulate(without compiling again), without passing any defines at run time, output will be as:
Inside FIRST
Inside LAST

 
If we required to display "LINE 5" we have to re-compile complete testbench with +define+SECOND at compile time
Now simulating the executable file, output will be as:
Inside SECOND
Inside LAST




Simulation Switches:


Variable ( int i  /  string abc ) are used as simulation flag
The complete testbench will be compiled at one-shot, and the executable file will contains snap-shot of complete codes.
For example, please go through below code.

LINE 1 :                     module xyz ();
LINE 2:                         reg [1:0] arg;
LINE 3 :                        if ($value$plusargs("argument=%0d",arg));
LINE 4 :                            if(arg == 1)
LINE 5 :                              $display ("Inside FIRST");
LINE 6 :                           else if(arg == 2)

LINE 7 :                              $display ("Inside SECOND");
LINE 6 :                           else if(arg == 3)
LINE 7 :                              $display ("Inside THIRD");
LINE 8 :                           else
LINE 9 :                              $display ("Inside ELSE");
LINE 10 :                      end
LINE 11 :                     $display ("Inside LAST");
LINE 12 :                   endmodule : xyz



Now compile the testbench without any arguments . The executable file contains snap-shot of all complete lines mention in module.
Now simulating the executable file, and we provide argument "+argument=1" at runtime  output will be as:
Inside FIRST
Inside LAST


Now we re-simulate(without compiling again), and we provide argument +argument=2, at runtime  output will be as:
Inside SECOND
Inside LAST

 
Now we re-simulate(without compiling again), and we provide argument +argument=3, at runtime  output will be as:
Inside THIRD
Inside LAST

 
Now we re-simulate(without compiling again) without any argument, at runtime  output will be as:
Inside LAST

 
As we saw, the benefits of "$values$plusargs" is:
One time compiling (creating executable file) and running multiple testcase on same executable file.


Now for environmental Modification, It is very necessary to reduce down the usage of (`ifdef / `elseif ) to very minimum and try to use simulation time arguments as much as possible. And at the same time we need to take care of single Test run but on other side it will be very much help full for (One time compilation with multisimulation) regression.

With the above style of programing,we can save recompilation times.

Difference between $test$plusargs and $value$plusargs:

There are two types of Simulation switches in systemverilog.

$value$plusargs:
We have seen example of $value$plusargs in above explaination. In $value$plusargs user need to pass value in argument and based on that value some decision will be taken.
We will go through another example:
module valuetest();
  integer i;
  real r;
  reg [11:0] v;
  reg [128:0] s;

  initial begin
    if($value$plusargs("STRING=%s", s))
      $display("GOT STRING");
    if($value$plusargs("INTG=%d", i))
      $display("GOT INTEGER");
    if($value$plusargs("REAL=%f", r))
      $display("GOT REAL");
    if($value$plusargs("VECTOR=%b", v))
      $display("GOT VECTOR");

    $display( "String is %s", s);
    $display("Integer is %d", i);
    $display("Realnum is %f", r);
    $display("Vector is %b", v);
  end
endmodule

Compilation:
command filename.sv
Simulation:
command +STRING=rrf  +INTG=123  +REAL=1.32  +VECTOR=10101

RESULTS:
GOT STRING
GOT INTEGER
GOT REAL
GOT VECTOR
String is rrf
Integer is 123
Realnum is 1.320000e+00
Vector is 000000010101 
$test$plusargs:
In $test$plusargs user don't need to give value in argument. If user provides simulation argument in simulation command then if condition will be executed otherwise else part (if exists) will be executed.
Example:
module switches();
  initial begin

    if($test$plusargs("TYPE_1"))
      $display("TYPE_1 message");
    else
      if($test$plusargs("TYPE_2"))

        $display("TYPE_2 message");
  end
endmodule
 
No need to give +define+TYPE_1 or +define+TYPE_2 during compilation

Simulate with +TYPE_1
RESULT:
TYPE_1 message

Simulate with +TYPE_2
Then simulate,result is
RESULT:
TYPE_2 message



Benefits & disadvantage of Compilation/Simulation Switches:

If we use Compilation switches:

  1. For Single Test run:
    1. Compilation and simulation time reduces. because the very small amount of code will be exercised. 
  2. For Regression: 
    1. If their are defines for each test case / test command we have to COMPILE & SIMULATE each testcase.

If we use Simulation switches:

  1. For Single Test run: 
    1. Compilation time increases (because complete code is getting exercised) and simulation time remains same.
  2. For Regression: 
    1. We have to compile one time, and we can use same executable file for all the test cases / test commands.

Friday, 9 November 2018

Pass enum value from command line in UVM


In verification activity, many times the requirement comes to choose specific value of enum variable from command line in either randomization of transaction class or configuring agent.

System Verilog provides support to pass value of variable of type int/bit/logic/string from command line using $value$plusargs function but it does not direct support to pass value of enum variable from command line.

In earlier version of UVM (uvm 1.1d and prior), we can achieve this by passing string value from command line and manually converting it into enum by writing code in TB.

Let’s go through example how we can achieve this,
-------------------------------------------------------------------------
-------------------------------------------------------------------------


In UVM 1.2 provides inbuilt facility to convert string into enumerated value.
In UVM 1.2 library contains uvm_enum_wrapper#(T) class and this class contains from_name() function.
from_name() function try to convert any string passed as argument into enumerated value.
If any matching enumerated value found then this function returns 1 else it returns 0.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Let’s go through example,
-------------------------------------------------------------------------
-------------------------------------------------------------------------


In above approach from_name() function can convert only one string at a time.
Now think about scenario where to randomize variable of enum user wants to select from multiple values. But above approach works fine only with one string value.

UVM provides uvm_split_string() function which splits the string based on a delimiter and returns a queue with sub string.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Now after getting queue of substring from uvm_split_string() function, user can pass one one element of substring queue to from_name() function in some loop and convert it into enumerated value.
Let’s go through example to understand this better,
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Reference:

Tuesday, 6 November 2018

Drop all objection manually in UVM


Test requirement is like,
1) When polling for more than one interrupts by continuously reading different registers in parallel and based on some indication move to next phase of UVM
2) Waiting for some timer to be expired and then moving to next phase of UVM

Let’s take one scenario where,
Driver is raising objection but never dropping objection and requirement is to move to next phase (extract_phase) after #50 timeunit. But because all the objection are not dropped in run_phase, simulation is not going to extract_phase.
Let’s go through example.
-------------------------------------------------------------------------
-------------------------------------------------------------------------


Now let’s take another scenario,
Here driver is dropping objection after #105 timeunit. So simulation will move to next phase after #105. But the requirement is to move to next phase which is extract_phase after #50.
Let’s go through example.
-------------------------------------------------------------------------
-------------------------------------------------------------------------


To fulfill the requirement we need to any how drop the pending objection after #50 to move to next phase after #50.
We can do this by getting list of all uvm_components (through get_objector method of uvm_objection class) who raised objection and then we can manually drop the objection after #50 timeunit.
Let’s go through example.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Reference:
1) uvm_objection.svh file 

Automatic configuration through uvm_config_db

UVM has an internal database table in which we can store values (of any data type including user defined data type) with unique name and can be retrieved later by some other testbench component.
There are two interface through which we can access configuration database.
1) uvm_resource_db, 2) uvm_config_db

Uvm_config_db is a great way to pass different resources (variable/object/virtual interface) within UVM testbench component hierarchy.

The uvm_config_db class provides a convenience interface on top of the uvm_resource_db to simplify the basic interface that is used for configuring uvm_component instances.

Let’s go through basic example:
-------------------------------------------------------------------------
-------------------------------------------------------------------------

UVM provides automatic configuration of resources (variable/object etc…) which are registered in the respective component (which is extended from uvm_component) by calling apply_config_settings method.
Any component which is extended from uvm_component should call super.build_phase(phase) to execute apply_config_settings method.
Let's go through basic example of automatic configuration.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

But,
This is not recommended approach as it introduces extremely poor simulation performance and big confusion on what kind of field/resource it supports and does not supports.

Some of the types get automatically configured and while some are not.

Let’s go through one more example of automatic configuration,
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Observation:
1)      If you set any object which is extended from uvm_object and set it into config_db using extended type it won't get automatically configured (obj2).
2)      If you set extended object into config_db using uvm_object as type then it will get automatically configured (obj1).
3)      Unpack array will not get automatically configured (ary_int).
4)      If resource (variable/object etc) is not registered in respective class using `uvm_field_* macro then it won't get automatically configured (idx1).
5)      Virtual interface won't get automatically configured as we can't register it using `uvm_field_* macro.
6)      If you set any vector (let’s say bit [47:0]) in config_db using uvm_config_db#(int)::set then it will get automatically configured. Even though width of int is 32 bit but still 48 bits will get automatically configured (idx2).
7)      But if you set same vector (bit [47:0]) in config_db using uvm_config_db#(bit[47:0])::set then it won't get automatically configured (idx22).


If you want to pass Unpack array using uvm_config_db then you can set it directly using for loop as done in above example. But this is not efficient approach, instead of that create a class (extended from uvm_object) that includes unpack arrays and then do set/get of that class using uvm_config_db.

Reference:
1) https://verificationacademy.com/forums/uvm/uvmconfigdb-usage-big-confusion

Sunday, 28 January 2018

Automatic/Static Variables and Function/Task with Automatic Lifetime in SystemVerilog

Before going into static and automatic variable let’s first go through 2 important terminology and then will go through automatic and static.

Scope:
Scope is the region or section of code where a variable can be accessed.
Variables declared inside a module, interface, program, or checker, but outside a task, process, or function, are local in scope.

Lifetime:
Lifetime is the time duration where an object/variable is in a valid state.
Variables declared outside a module, program, interface, checker, task, or function are local to the
compilation unit and have a static lifetime (exist for the whole simulation).
Variables declared inside a static task, function, or block are local in scope and default to a static lifetime.

Specific variables within a static task, function, or block can be explicitly declared as automatic. Such
variables have the lifetime of the call or block and are initialized on each entry to the call or block.
---------------------------------------------------------------------
---------------------------------------------------------------------
Result:
---------------------------------------------------------------------
---------------------------------------------------------------------


Automatic Function/Task (Function/Task with Automatic Lifetime):
In Verilog default lifetime of all the task/function is static.
Tasks and functions may be declared as automatic. Variables declared in an automatic task, function, or block are local in scope, default to the lifetime of the call or block, and are initialized on each entry to the call or block.

In other word, Automatic task/function variables cannot be accessed by hierarchical references. Automatic variables are automatically destroyed once the scope (task, function, block) in which they are created ends and regenerated when again enters into scope (task, function, block).
---------------------------------------------------------------------
---------------------------------------------------------------------


Class methods has Automatic lifetime by default:
Because of backward compatibility reason, SystemVerilog could not change the default lifetime qualifier to ‘automatic’ but since class methods were new, it changed the lifetime of all methods to be automatic.
---------------------------------------------------------------------
---------------------------------------------------------------------


The keyword ‘static’ is overloaded. There is a semantic difference between the meaning of ‘static’ used to the left of a function/task keyword and ‘static’ used to the right of the function/task keyword.

When used to the left of the function or task, ‘static’ is a class qualifier and has the same as on any static method in C++/Java. It is a method of the class type, not of an instance of a class object. Other class qualifiers are ‘local’ and ‘protected’ and along with ‘static’ are only allowed in front of methods of a class. There is no corresponding ‘automatic’ class qualifier.

When used to the right of a function or task, ‘static’ is a lifetime qualifier, with ‘automatic’ being the corresponding qualifier.

For more details refer this link.