Monday, 2 September 2019

System Verilog tasks to generate vcd file

Many times in verification activity, people requires to generate vcd file and mostly it is required by designers to see the value changes of design variables. So let's see, more about vcd file in system verilog.
2 types of vcd file can be generated through system verilog:

  1. 4 state: which represent changes of variable values 0, 1, x and z
  2. Extended: with 4 state values, it shows strength information

Below are the more details about generation of vcd files:

  • Generation for 4 state vcd file:

          Following are few useful system verilog tasks, that can be used for 4 state vcd file:

    • $dumpfile("file_name.vcd") => To generate vcd file with specified name. It is passed as its argument. If its not provided then default file name would be "dump.vcd".
    • $dumpvars(0, top) => To specify which variables to be dumped into vcd file. It has 2 arguments but no arguments are specified then it dumps all variables of the design.

                   First argument indicates the hierarchy level below each specified module instance to be dumped. 
      • 0 indicates dump of all variables in the specified module and all module instances and sub-instances specified in the current module.
      • 1 indicates dump all the variable of current module
      • 2 indicates dump all the variable of current module and the instances specified in the current module. It will not dump submodule information.
      • ..... 
                    Second argument indicates the scope/hierarchy of the design to dump.
    • $dumpoff => To suspend dumping information in the vcd file.
    • $dumpon => To resume dumping information in the vcd file.
    • $dumpall => Dump all selected variables when this task in invoked otherwise variables are dumped only when their values are changed.
    • $dumplimit(file_size_in_bytes) => To specify the size limit of vcd file.
  • Generation of Extended vcd file:

          Following are few useful system verilog tasks, that can be used for extended vcd file:
    • $dumpports(module_identifier, "file_name.vcd") => To dump ports and specify the vcd file name. Only modules can be provided, not variables. More than one module can be provided by comma separation.
    • $dumpportsoff("file_name.vcd") => To suspend port dumping information in vcd file.
    • $dumpportson("file_name.vcd") => To resume port dumping information in vcd file. 
    • $dumpportsall("file_name.vcd") => To dump all selected ports at a time when this method is called.
    • $dumpportslimit(file_size_in_bytes, "file_name.vcd") => To specify the size limit of the file and the file name of vcd to be dumped.


    Saturday, 27 April 2019

    Test Timeout in UVM

    In verification activity, we usually expects regression shall be completed as soon as possible. So that, we can debug the failing testcases as well as check the coverage result. But sometime, few testcase takes a lot of simulation time or some testcase stuck in simulation which never ends. Due to that, regression takes a longer time than expected. To avoid this kind of problem, UVM defines time out mechanism i.e. if testcase simulation time reaches at defined time out then testcase ends with UVM_FATAL which eventually reduces regression time.

    UVM has method "set_timeout" which is defined in "uvm_root". So, in environment class or in the base testcase, this time out shall be set to a proper value which affects to all testcases.

    Definition: 
    function void set_timeout(time timeout, bit overridable = 1)  
    First argument: Time out value (Unit: time). Default value is 9200s. 
    Second argument: Time out value could be override or not. If there's any strict limitation that, the test shall be ended in particular time, than this argument shall be set to 0. If there's any testcase which takes longer time in simulation than expected than this value shall be set to 1. Default value is 1.

    Example:
    package user_pkg;

    import uvm_pkg::*;
    `include "uvm_macros.svh"

    class child extends uvm_component;

        function new (string name, uvm_component parent);
            super.new(name, parent);
        endfunction

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            uvm_top.set_timeout(50, 1);
        endfunction 

        task run_phase(uvm_phase phase);
            phase.raise_objection(this);
            #10;
            phase.drop_objection(this);
        endtask
    endclass

    class base extends uvm_component;
        child l1;

        function new (string name, uvm_component parent);
            super.new(name, parent);
        endfunction

        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
            l1 = new("l1", this);
        endfunction 

        task run_phase(uvm_phase phase);
            phase.raise_objection(this);
            uvm_top.set_timeout(100,1);
            #200; 
            phase.drop_objection(this);
        endtask
    endclass

    endpackage:user_pkg

    module top;
        import uvm_pkg::*;
        import user_pkg::*;

        base mu = new("mu", null);

        initial begin
            run_test();
        end
    endmodule


    Output:
     UVM_FATAL ../../../../src/base/uvm_phase.svh(1506) @ 100: reporter [PH_TIMEOUT] Explicit timeout of 100 hit, indicating a probable testbench issue

    In the above example, child class is set with default time out to 50t in build phase and it can be overridden. As it requires to override this value, in the run phase of parent class, the timeout is set to 100t. So, we expect, the test shall be completed successfully before 100t. But as the test is not completed in 100t, the test is ended forcefully with UVM_FATAL at 100t due to timeout. 

    This timeout can be controlled through "+UVM_TIMEOUT=xx" command line argument at simulation time (xx defines the time). So that, it doesn't require to update the testcase.

    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