SV Constraint random value generation : Introduction

SV Constraint random value generation : Introduction

System Verilog supports three different approaches in verification as follows. 1. Directed Testing 2. Random Testing 3. Directed Random/Constrained Random Testing

Directed testing is the traditional verification approach. In this case, a particular scenario is created for a known feature and set the expectation for the same. So, the features have been verified can be easily identified from the tests itself. This increases the visibility of the design. But there are chances of missing actual bugs in the design, as user can’t develop and run all possible scenarios. Due to the increase in design size and complexity, directed test is not an efficient way for verification of modern designs. Developing and maintaining a large set of directed test suite are incredibly tedious.

In Random Testing, scenarios are created randomly. In this we might be able to find more errors, if we could run the simulations for a longer period of time. So with the limited period of time there is a chance of missing certain bugs.

In directed random/constrained random testing, random values are generated in a controlled manner. In this way most of the important scenarios including corner cases can be verified in the limited time period. This approach requires a complex environment which increases environment development cycle. But efficiency increases a lot.

System Verilog allows users to generate constrained random tests in a very efficient method by using different constructs and methods. User can specify constraints in a compact declarative way. The constraints are then processed by a solver and generate random values that meet the constraints.

randomize() SystemVerilog uses an object-oriented method for assigning random values to the member variables of an object.

The myclass class has two random variables `addr` and `data` representing 16 bit address and 32bit data values on a bus. There is a constraint which says address must be non-zero. The randomize() method is called to generate new random values for the above class object.

class myclass; rand logic[15:0] addr; rand logic[31:0] data; constraint addr_c1 {addr != 'd0;} endclass

class mysubclass extend myclass

endclass

program myprog; myclass c1 = new;

initial begin repeat (10) begin if ( c1.randomize() == 1 ) $display ("addr = %16h data = %h\n", c1.addr, c1.data); else $display ("Randomization failed.\n"); end end endprogram

/* ncsim> run addr = 0000000000004fc1 data = 7b7be350

addr = 0000000000004de9 data = baee38f6

addr = 000000000000af58 data = 7f5fb869

addr = 0000000000007cd9 data = b75c672e

addr = 0000000000002aa7 data = 3d036c15

addr = 000000000000d7e1 data = bb003bf7

addr = 000000000000911b data = e17b9385

addr = 000000000000cb29 data = 8e17990f

addr = 0000000000006bb5 data = f288bf35

addr = 0000000000008a3f data = f5ee4079

*/

In the above code, c1.randomize() randomizes all the random values in the class object which is shown in the simulation output. Also note that there is no zero value for address due to the constraint given.

randomize() with Constraints can be applied inline as well. In the below code there is one more constraint on the address. The class `mysubclass` is inherited from `myclass`, so it inherits all of the random variables and constraints of the parent class and adds a random variable called `ar` that is used to control the address range using another constraint.

class myclass; rand logic[15:0] addr; rand logic[31:0] data; constraint addr_c1 {addr != 'd0;} endclass

typedef enum {low, high} addRange;

class mysubclass extends myclass; rand addRange ar; constraint addr_range {(ar == low ) -> addr inside {[0 : 127]}; (ar == high) -> addr inside {[128 : 255]}; }

endclass

program myprog;

mysubclass c2 = new;

initial begin static int r1 = c2.randomize() with {ar == high; }; $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data);

r1 = c2.randomize() with {addr > 5 && addr < 10; }; $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data);

r1 = c2.randomize() with {addr == 'd100; }; $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data);

r1 = c2.randomize() with {data == 1'd0; }; $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data);

repeat(10) begin r1 = c2.randomize(); $display ("Without 'with' construct -> ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data); end end endprogram

/* ncsim> run ar= 1, addr = 224 data = 2722016169

ar= 0, addr = 8 data = 3392647840

ar= 0, addr = 100 data = 3541587853

ar= 0, addr = 60 data = 0

Without 'with' construct -> ar= 0, addr = 94 data = 3611996915

Without 'with' construct -> ar= 1, addr = 180 data = 58070505

Without 'with' construct -> ar= 1, addr = 226 data = 3375269587

Without 'with' construct -> ar= 0, addr = 86 data = 3711111162

Without 'with' construct -> ar= 1, addr = 216 data = 2717273895

Without 'with' construct -> ar= 1, addr = 136 data = 2628249665

Without 'with' construct -> ar= 1, addr = 159 data = 257523962

Without 'with' construct -> ar= 1, addr = 153 data = 3788828802

Without 'with' construct -> ar= 0, addr = 76 data = 828085202

Without 'with' construct -> ar= 0, addr = 37 data = 2656833039

*/

When a mysubclass object is randomized, values for `addr`, `data`, and `ar` are computed so that all of the constraints are satisfied. Objects can be further constrained using the randomize() with construct, which declares additional constraints in-line with the call to randomize(). In the above code, randomize() with ar == low -> means generate random values by satisfying the constraint -> address should fall in the range of 0 to 127.

(ar == low ) -> addr inside {[0 : 127]};

Constraint_mode() & rand_mode() constraint_mode() method can be used to enable or disable any named constraint block in an object.

class myclass; rand logic[15:0] addr; rand logic[31:0] data; constraint addr_c1 {addr != 'd0;} endclass

typedef enum {low, high} addRange;

class mysubclass extends myclass; rand addRange ar; constraint addr_range {(ar == low ) -> addr inside {[0 : 127]}; (ar == high) -> addr inside {[128 : 255]}; }

endclass

program myprog;

mysubclass c2 = new;

initial begin c2.addr_c1.constraint_mode(0); c2.addr_range.constraint_mode(0);

repeat(5) begin if(c2.randomize() == 1) $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data); else $display ("Randomization failed.\n"); end

c2.addr_c1.constraint_mode(1); c2.addr_range.constraint_mode(1);

repeat(5) begin if(c2.randomize() == 1) $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data); else $display ("Randomization failed.\n"); end

end endprogram

/* ncsim> run ar= 0, addr = 43945 data = 2071716688

ar= 0, addr = 47209 data = 1371233692

ar= 0, addr = 22413 data = 3076286254

ar= 1, addr = 15351 data = 2796302644

ar= 1, addr = 19769 data = 3782972293

ar= 1, addr = 209 data = 4126031993

ar= 1, addr = 220 data = 3365631329

ar= 1, addr = 236 data = 1297025923

ar= 1, addr = 162 data = 3337563549

ar= 0, addr = 127 data = 2533298197 */

In the first part of the above code, the constraints for address are disabled using constraint_mode(0) and enabled using constraint_mode(1). The difference in the address values can be seen in the simulation output. Similarly, the rand_mode() method can be used to enable or disable any random variable. When a random variable is disabled, it behaves in exactly the same way as other nonrandom variables.

class myclass; rand logic[15:0] addr; rand logic[31:0] data; constraint addr_c1 {addr != 'd0;} endclass

typedef enum {low, high} addRange;

class mysubclass extends myclass; rand addRange ar; constraint addr_range {(ar == low ) -> addr inside {[0 : 127]}; (ar == high) -> addr inside {[128 : 255]}; }

endclass

program myprog;

mysubclass c2 = new;

initial begin

c2.addr.rand_mode(0); c2.addr_c1.constraint_mode(0); c2.addr_range.constraint_mode(0);

repeat(5) begin if(c2.randomize() == 1) $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data); else $display ("Randomization failed.\n"); end

c2.addr.rand_mode(1); c2.addr_c1.constraint_mode(1); c2.addr_range.constraint_mode(1);

repeat(5) begin if(c2.randomize() == 1) $display ("ar=%d, addr = %d data = %d\n",c2.ar, c2.addr, c2.data); else $display ("Randomization failed.\n"); end

end endprogram

/*

ncsim> run ar= 0, addr = x data = 2071716688

ar= 1, addr = x data = 3136174326

ar= 0, addr = x data = 2136979561

ar= 0, addr = x data = 3076286254

ar= 1, addr = x data = 1023634453

ar= 1, addr = 238 data = 2112834873

ar= 1, addr = 209 data = 4126031993

ar= 1, addr = 220 data = 3365631329

ar= 1, addr = 236 data = 1297025923

ar= 1, addr = 162 data = 3337563549 */

In the above code `addr` is disabled using rand_mode(0). The constraints also have to be disabled otherwise tool will issue error on constraint mismatch. In this case `addr` is behaving as a non-random variable.

pre_randomize() and post_randomize() To perform operations immediately before or after randomization, two built-in methods pre_randomize() and post_randomize() are available in SV. These methods can be overridden with the desired functionality.

class myclass; rand logic[15:0] addr; rand logic[31:0] data; constraint addr_c1 {addr != 'd0;} endclass

class mysubclass extends myclass;

function void pre_randomize(); super.pre_randomize(); $display("Before randomize addr=%0d, data=%0d", addr, data); endfunction

function void post_randomize(); super.post_randomize(); $display("After randomize addr=%0d, data=%0d", addr, data); endfunction

endclass

program myprog;

mysubclass c2 = new;

initial begin repeat(5) begin if(c2.randomize() == 1) $display ("addr = %d data = %d\n",c2.addr, c2.data); else $display ("Randomization failed.\n"); end end endprogram

/* Before randomize addr=x, data=x After randomize addr=20417, data=2071716688 addr = 20417 data = 2071716688

Before randomize addr=20417, data=2071716688 After randomize addr=19945, data=3136174326 addr = 19945 data = 3136174326

Before randomize addr=19945, data=3136174326 After randomize addr=44888, data=2136979561 addr = 44888 data = 2136979561

Before randomize addr=44888, data=2136979561 After randomize addr=31961, data=3076286254 addr = 31961 data = 3076286254

Before randomize addr=31961, data=3076286254 After randomize addr=10919, data=1023634453 addr = 10919 data = 1023634453

*/

The random stimulus generation capabilities and the object-oriented constraint-based verification methodology enable users to quickly develop tests that cover complex functionality and better assure design correctness.

More information above SV constraint value generation can be seen in the SV-Constraint random value generation series.