Saturday, April 24, 2021

System Verilog Assertions - dynamic delays

Ben cohen provided the code for dynamic delays in verification academy forum.

I have just pasted it here...

Link :

https://verificationacademy.com/forums/systemverilog/sva-package-dynamic-and-range-delays-and-repeats

 Code :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Ben Cohen  December 13, 2018  
package sva_delay_repeat_range_pkg;
    // int d1, d2; 
    // bit a, b, c=1'b1; 
    // sequence q_s; a ##1 c; endsequence  
    // sequence my_sequence; e ##1 d[->1]; endsequence 
    //----------------------------------------------------------------
    // ******       DYNAMIC DELAY ##d1 **********
    // Application:  $rose(a)  |-> q_dynamic_delay(d1) ##0 my_sequence;
    sequence q_dynamic_delay(count);
        int v;
        (1, v=count) ##0 first_match((1, v=v-1'b1) [*0:$] ##1 v<=0);
    endsequence 
    //----------------------------------------------------------------
    // ******       DYNAMIC DELAY RANGE ##[d1:d2] **********
    //##[d1:d2] ##0 b; // NO sequence  followng the "b"
    // $rose(a)  |-> q_dynamic_delay_range(d1, d2, b)) 
    //                and b[->1] ##1 my_sequence;
    // $rose(a) ##0  q_dynamic_delay_range(d1, d2, b) |-> my_sequence;
    sequence q_dynamic_delay_range(int d1, d2, bit b);
        int v1, vdiff;
        (1, v1=d1, vdiff=d2-d1) ##0 q_dynamic_delay(v1)   ##0  
        first_match((1, vdiff=vdiff - 1)[*0:$] ##1 (b || vdiff<=0)) ##0 b; 
    endsequence 
    //----------------------------------------------------------------
    // ******       DYNAMIC REPEAT q_s[*d1] **********
    // Application:  $rose(a)  |-> q_dynamic_repeat(q_s, d1) ##1 my_sequence;
    sequence q_dynamic_repeat(q_s, count);
        int v=count;
        (1, v=count) ##0 first_match((q_s, v=v-1'b1) [*1:$] ##0 v<=0);
    endsequence
    //----------------------------------------------------------------
    // ******       DYNAMIC REPEAT RANGE q_s[*d1:d2] **********
    // Application:  $rose(a)  |-> (q_dynamic_repeat_range(q_s, d1, d2, b) 
    //                             and  b[->1] ##1 my_sequence // use the same "b"
    //  $rose(a)  ##1 q_dynamic_repeat_range(q_s, d1, d2, b) |->   my_sequence;
    sequence q_dynamic_repeat_range(sequence q_s, int r1, r2, bit b);
        int v, diff;
        (1, v=r1, diff=r2-r1) ##0   
        q_dynamic_repeat(q_s, v)  ##1 // repeat to r1
        first_match((q_s, diff=diff-1'b1) [*0:$] ##1 (b || diff<=0 )) ##0 b; 
    endsequence     
endpackage
 
 
import uvm_pkg::*; `include "uvm_macros.svh" 
import sva_delay_repeat_range_pkg::*;
module top; 
    timeunit 1ns;     timeprecision 100ps;  
    bit clk, a, b, c=1, w;  
    int d1=2, d2=5;  
    sequence q_s;
        a ##1 c; 
    endsequence   
    sequence my_sequence; 
        a ##1 w[->1]; 
    endsequence 
    default clocking @(posedge clk); 
    endclocking
    initial forever #10 clk=!clk;  
 
    // ******       DYNAMIC DELAY ##d1 **********
    // Application:  $rose(a)  |-> q_dynamic_delay(d1) ##0 my_sequence;
    ap_dyn_delay: assert property(@ (posedge clk) 
       $rose(a) |-> q_dynamic_delay(d1) ##0 my_sequence);  
 
    ap_fix_delay: assert property(@ (posedge clk)   
       $rose(a) |-> ##2 my_sequence); 
 
    // ******       DYNAMIC DELAY RANGE ##[d1:d2] **********
    //##[d1:d2] ##0 b; // NO sequence  followng the "b"
    // $rose(a)  |-> q_dynamic_delay_range(d1, d2, b)) 
    //                and b[->1] ##1 my_sequence;
    // $rose(a) ##0 q_dynamic_delay_range(d1, d2, b) |-> my_sequence;
    ap_dly_rng: assert property(@ (posedge clk) 
      $rose(a) |-> q_dynamic_delay_range(d1, d2, b) and b[->1] ##1 my_sequence);  
 
    ap_2to5: assert property(@ (posedge clk) 
      $rose(a) |->(##[2:5] b) and  b[->1] ##1 my_sequence);   
 
    ap_dly_rng_antc: assert property(@ (posedge clk) 
     $rose(a) ##0 q_dynamic_delay_range(d1, d2, b) |-> my_sequence);  
 
    ap_2to5_antc: assert property(@ (posedge clk) 
      $rose(a) ##0 first_match(##[2:5] b) |-> my_sequence); 
    //------------------------------------------------------------------
    // ******       DYNAMIC REPEAT q_s[*d1] **********
    // Application:  $rose(a)  |-> q_dynamic_repeat(q_s, d1) ##1 my_sequence;
    ap_rpt: assert property(@ (posedge clk) 
      $rose(a)|-> q_dynamic_repeat(q_s, d1)  ##1 my_sequence);  
 
    ap_rpt2: assert property(@ (posedge clk)  
    $rose(a)|-> q_s[*2] ##1 my_sequence);   
 
    // ******       DYNAMIC REPEAT RANGE q_s[*d1:d2] **********
    // Application:  $rose(a)  |-> (q_dynamic_repeat_range(q_s, d1, d2, b) 
    //                             and  b[->1] ##1 my_sequence // use the same "b"
    //  $rose(a)  ##1 (q_dynamic_repeat_range(q_s, d1, d2, b) |->   my_sequence;
    ap_rpt_rng: assert property(@ (posedge clk) 
      $rose(a)  |-> q_dynamic_repeat_range(q_s, d1, d2, b) 
                    and  b[->1] ##1 my_sequence);  
 
    ap_rpt_2to5: assert property(@ (posedge clk) 
      $rose(a)  |-> first_match(q_s[*2:5] ##1 b) and  b[->1] ##1 my_sequence);  
 
    ap_rpt_rng_antc: assert property(@ (posedge clk) 
      $rose(a)  ##1 q_dynamic_repeat_range(q_s, d1, d2, b) |->   my_sequence);  
 
    ap_rpt_2to5_antc: assert property(@ (posedge clk) 
      $rose(a)  ##1 first_match(q_s[*2:5] ##1 b) |->   my_sequence);
 
    initial begin 
        repeat(1000) begin 
            @(posedge clk); #2;   
            if (!randomize(a, b, c, w)  with 
            { a dist {1'b1:=1, 1'b0:=1};
            b dist {1'b1:=1, 1'b0:=1}; 
            c dist {1'b1:=1, 1'b0:=1}; 
            w dist {1'b1:=1, 1'b0:=1}; }) `uvm_error("MYERR", "randomize error");
        end 
        #1;
        repeat(1500) begin 
            @(posedge clk); #2;   
            if (!randomize(a, b, c, w)  with 
            { a dist {1'b1:=1, 1'b0:=2};
            b dist {1'b1:=3, 1'b0:=2}; 
            c dist {1'b1:=1, 1'b0:=1}; 
            w dist {1'b1:=3, 1'b0:=1}; }) `uvm_error("MYERR", "randomize error");
        end 
        $stop; 
    end 
endmodule   

 

Wednesday, April 21, 2021

Another system verilog constraint problem - Base class contraint becoming invisible

 Let us walk through the following code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class base;
  rand bit [4:0] c;
  constraint c_x { c == 'd1; }
  
  function void display();
    this.randomize();
    $display("Base    c:%0d",c);
  endfunction
  
endclass

class derived extends base;
  rand bit [4:0] c;
  constraint c_x { c == 'd2; }
  
  function void display();
    this.randomize();
    $display("Derived c:%0d",c);
    super.display();
  endfunction
  
endclass

module top;
  derived b;
  
  initial begin
    b = new;
    repeat(2)
    b.display();
  end
endmodule
    


In the above code, we have 2 classes, base and derived.

Base class has variable 'c' and constraint 'c_x', the same is replicated in the derived class as well.

we have 'display' function which is used to randomize the variable and print the value. In the derived in addition to it , a super.display() was called to print the base class variable value.

When you have 2 constraints with the same name on the same variable, the constraint in the base class becomes invisible and is not applied when the variable is randomized.

Simulation results:

Contains Synopsys proprietary information.
Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1; Apr 22 00:47 2021
Derived c:2
Base c:27
Derived c:2
Base c:7
V C S S i m u l a t i o n R e p o r t

Reference ( comments by experts )

https://verificationacademy.com/forums/systemverilog/how-constraint-executed

Sunday, April 18, 2021

The importance of targetted stimulus to expose corner cases

Lets take a scenario where an Interrupt is triggered whenever a JUMBO packet is observed on a port. 

In general an interrupt service routine is performed after receiving an interrupt and the status register which indicates the interrupt is cleared by writing 1 into it.

The interrupt is serviced , the register is cleared and we wait for the next event.

What if another JUMBO packet is received at the same time a write is issued to clear the register?

Sometimes the documentation (requirement specification) is not thorough and might miss the scenario on handling such cases.

It is important for us to generate all the possible cases and see how the system behaves under such cases and report it for confirmation.

In the current context, the event that triggers the interrupt should will been given more importance and  instead of clearing the register should have retained the interrupt status ( to 1).

These corner cases sometimes take multiple iterations for us to reach the scenario ( with random stimulus ).


Wednesday, April 14, 2021

Functional coverage - Selective range coverage

There may be cases where you need to cover a range of values in a particular vector.

One such example is given below.

Address range : 0 -15 (addr)

Cover all even addresses in addr.

 

This was asked in an interview with cadence.

 

Code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module top;
  bit clk;
  bit [3:0] addr;

  covergroup cg_addr;
    cp: coverpoint addr[3:1] iff(addr[0] == 0);
  endgroup

  cg_addr cg;

  initial begin
    cg = new;
    clk <= 'b0;
    forever #5 clk = !clk;
  end

  initial begin
    repeat(16) begin
      @(posedge clk);
      addr++;
      cg.sample();
    end
    $display("Coverage:%0f%%",cg.get_inst_coverage());
    $finish;
  end

endmodule

 

* In the above code, we simply covered [3:1] vector range when addr[0] == 0 ( indicating that address is divisible by 2, even range ).

* We used the auto bins generated ( by default ).

 

Run the following commands:

vcs -sverilog -R cov.sv -cm line+cond+tgl


Result:

 VCS Coverage Metrics Release L-2016.06 Copyright (c) 1991-2016 by Synopsys Inc.
Coverage:0.000000%
$finish called from file "cov.sv", line 24.
$finish at simulation time                  155
---------------------------------------------------------------------------
VCS Coverage Metrics: during simulation line, cond, tgl was monitored
---------------------------------------------------------------------------
 Coverage status: End of All Coverages ...
 V C S   S i m u l a t i o n   R e p o r t
Time: 155
CPU Time:      0.400 seconds;       Data structure size:   0.0Mb

I find it weird, get_inst_coverage() shows 0%, in other simulators the result is 100%

Mentor Questa sim:

# Loading work.top(fast)
#
# vsim -voptargs=+acc=npr
# run -all
# Coverage:100.000000%
# ** Note: $finish : testbench.sv(26)
# Time: 155 ns Iteration: 1 Instance: /top
# End time: 00:16:22 on Apr 15,2021, Elapsed time: 0:00:01
# Errors: 0, Warnings: 0
Done

To generate the urgReport:

urg -dir *.vdb

firefox urgReport/grp0.html

Result:

Summary for Variable cp
CATEGORY    EXPECTED    UNCOVERED    COVERED    PERCENT
Automatically Generated Bins     8     0     8     100.00
Automatically Generated Bins for cp

Bins
NAME       COUNT       AT LEAST  
auto[0]     1     1
auto[1]     1     1
auto[2]     1     1
auto[3]     1     1
auto[4]     1     1
auto[5]     1     1
auto[6]     1     1
auto[7]     1     1 

Thursday, April 1, 2021

Fork Join_none and SV Objects

 Typically whenever we require to spawn threads using fork join_none and a for loop, we use "automatic int". This is required to pass on the correct value into the threads. Otherwise, the last value in the loop is passed on to all the threads.


What if an object is passed instead of a data_type like int?

In the below code, we have 3 scenarios

1. Using an array of objects and automatic int whilst spawning threads.

2. Using a queue

3. Not having 'automatic int' to guide the array.


CODE:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//==================================================//
// CLASS : DATA_OBJ
// Description :
// containts data_id and data
// 
//==================================================//
class data_obj;
  int data_id;
  string data;
  
  //==================================================//
  // load_val
  // loads data_id and data
  //==================================================//
  function void load_val ( int id, string val);
    data_id = id;
    data    = val;
  endfunction
endclass

//==================================================//
// TEST_CLASS
// Description:
// Contains and array and queue
// Intention is to check the automatic function in 
// SV class
//==================================================//

class test_class;
  
  data_obj obj[3];
  data_obj obj_q[$];
  
  //==================================================//
  // LOAD_OBJ
  // Fills array and queue with data_obj objects
  //==================================================//
  function void load_obj();
    for(int i=0;i <3;i++) begin
      obj[i] = new;
      obj[i].load_val(i,"pavan");
      obj_q.push_back(obj[i]);
    end
  endfunction
  
  //==================================================//
  // SPAWN
  // Uses automatic int approach to load object into
  // print_data function
  // TRY : Try replacing 'j' with 'i'
  //==================================================//
  function void spawn();
    for(int i=0; i<3;i++) begin
      automatic int j=i;
      fork
        print_data(obj[j]);  // Replace J with I and check    
      join_none
    end
  endfunction
  
  //==================================================//
  // POP_SPAWN
  // Simply pops the data into the print_data function
  //==================================================//
  function void pop_spawn();
    for(int i=0; i<3;i++) begin
      fork
        print_data(obj_q.pop_front());      
      join_none
    end
  endfunction
  
  //==================================================//
  // Print_data
  // Prints the data
  //==================================================//
  function void print_data(data_obj obj);
    $display("OBJ ID:%0d VAL:%0s",obj.data_id,obj.data);
  endfunction
  
endclass

//==================================================//
// MODULE: TOP
//==================================================//
module top;

  test_class test_c;
  
  initial begin

    test_c = new;
    test_c.load_obj();
    test_c.spawn();      // Spawn threads using automatic int approach
    test_c.pop_spawn();  // Spawn threads using queue pop approach
    #100ns;
   
  end
endmodule: top


RESULTS:

1. Using an array of objects and automatic int whilst spawning threads.

Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1; Apr 1 22:21 2021
OBJ ID:0 VAL:pavan
OBJ ID:1 VAL:pavan
OBJ ID:2 VAL:pavan

 2. Using a queue

Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1; Apr 1 22:23 2021
OBJ ID:0 VAL:pavan
OBJ ID:1 VAL:pavan
OBJ ID:2 VAL:pavan

3. Not having 'automatic int' to guide the array.

Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1; Apr 1 22:24 2021

Error-[NOA] Null object access
testbench.sv, 79
The object at dereference depth 0 is being used before it was
constructed/allocated.
Please make sure that the object is allocated before using it.

#0 in \test_class::print_data at testbench.sv:79
#1 in \test_class::spawn at testbench.sv:57
#2 in top at testbench.sv:95
#3 in top

Constraint to have N elements distributed in M bins

Code to distribute N elements into M bins, you add unique keyword to have each bin will have unique number of elements. class test; param...