Main Content

Add Random Constraints to Sequences in UVM Test Bench

This example shows how to add constrained random verification to a Universal Verification Methodology (UVM) test bench generated from Simulink®. Both Simulink parameters and input ports to the stimulus generation results in randomizable sequence class data members in the UVM test bench. Through the use of standard UVM class inheritance and factory overrides, the design verification engineer can add new and valuable constrained random testcases to their UVM test suite.

Introduction

This example extends a pulse generation UVM test bench to add constrained random testing. See the example Generate Parameterized UVM Test Bench from Simulink for a description of the design and the background on generating a UVM test bench. To generate the default test bench for this example, execute:

% Generate a UVM test bench
design     = 'prm_uvmtb/PulseDetector'
sequence   = 'prm_uvmtb/GenPulse'
scoreboard = 'prm_uvmtb/CheckDetection'
uvmbuild(design, sequence, scoreboard)

The Parameterization of the Pulse Generator

In the model the stimulus generation is parameterized using a dialog parameter for the pulse location and an input port for the signal-to-noise ratio (SNR). In the generated UVM, these parameters are data members of the mw_PulseDetector_sequence class with constraints that reflect information from the model.

See mw_PulseDetector_sequence.sv.

  • pPulseLocation : This dialog parameter of the GenPulse subsystem indicates the start location of the 64 sample pulse in the larger 5000 sample frame. To retain in the SystemVerilog use a Simulink.Parameter. In that parameter, the default value is 2100 and the valid range is [0, 4936]. (The pulse must be entirely within the frame of 5000 samples.)

In the generated UVM code, two constraints are placed on the pulse location sequence member for a default value and a min, max range as shown in this code snippet:

  • SNR port : This input port to the generation subsystem indicates the overall magnitude relationship of the generated pulse to the generated noise. Its datatype is a ufix8_En6 fixed point number with an intrinsic range of [0, 3.984375]. The port itself has a been given a more constrained range of [0, 2.984375] which corresponds to fixed point bit values of 0b00_000000, 0b10_111111.

Because it is an input port there is no notion of a default value in Simulink. In the generated UVM code, two constraints are placed on the sequence member for a min, max range as well as a default value that matches the minimum. Code is also added to support a command-line default value override via a plus arg. Example code:

Default UVM Simulation Behavior

The default sequence generation body() follows the usual pattern of getting a grant, randomizing a sequence item, sending it along to sequencer, then waiting for its completion.

Because of the default value constraints, if you run the default test it will use fixed values for the parameters. For this model, that means a pulse start location of 2100 and an SNR of 0.0. An SNR of 0.0 will result in no detectable pulses, so a plusarg specifies a more interesting default value. A fixed point bit value of 8'b10000000 is a float value of 2.0.

% Clear environment variables that influence the UVM simulation
setenv EXTRA_UVM_SIM_ARGS
setenv EXTRA_UVM_COMP_ARGS
setenv UVM_TOP_MODULE
% Simulate the UVM test bench using plusarg override
cd uvm_build/prm_uvmtb_uvm_testbench/top
setenv EXTRA_UVM_SIM_ARGS +SNR_default_inp_val=10000000
! vsim -do run_tb_mq.do     % ModelSim/QuestaSim (gui)
! vsim -c -do run_tb_mq.do  % ModelSim/QuestaSim (console)
! ./run_tb_xcelium.sh       % Xcelium (console)
! ./run_tb_vcs.sh           % VCS (console)
cd ../../..

Constrained Random Verification Using In-Lined Constraints

You can use standard UVM techniques to add randomized behavior to the stimulus generation. In this example, the testplan calls for using SNR values between 0.75 and 1.00 to determine how robust the algorithm is in detecting pulses in this range. For this case, the location of the pulse does not matter and is fixed at the end of the 5000 sample frames.

See mw_PulseDetector_SEQCRT_param_overrides.sv.

To achieve this testplan goal, a derived sequence class is created. It has the following extra behaviors:

  • in the pre_body() override, the use of the default value constraints is turned off

  • in the post_randomize() override, the randomized value is printed out so that SNR values can be related to whether pulses are detected or not. It uses a class utility function fixed2real to give a friendly value.

  • in the randomize_params() override, an in-line constraint of "randomize with" restricts SNR values to be between 0.75 and 1.0. (You can get these bit values from MATLAB by using a fi variable and its bin method.)

A new test is created which tells the factory to use the new sequence class when constructing the test bench.

To run the UVM simulations using these new classes, you can use the original scripts with extra arguments given through environment variables.

% Simulate the UVM test bench using inlined constraint overrides
cd uvm_build/prm_uvmtb_uvm_testbench/top
setenv EXTRA_UVM_COMP_ARGS '-f ../../../overrides_SEQCRT/extra_comp_args.f'
setenv EXTRA_UVM_SIM_ARGS +UVM_TESTNAME=mw_PulseDetector_test_inlineCRT
! vsim -do run_tb_mq.do     % ModelSim/QuestaSim (gui)
! vsim -c -do run_tb_mq.do  % ModelSim/QuestaSim (console)
! ./run_tb_xcelium.sh       % Xcelium (console)
! ./run_tb_vcs.sh           % VCS (console)
cd ../../..

Examine the output and see that the generated SNR is always between [0.75, 1.00] and the pulse starting location is always 4936. With such an SNR range, observe that sometimes a pulse is detected and sometimes it is not.

Constrained Random Verification Using Class-Based Constraints

Inline constraints are good for honing in on specific test conditions. For fancier constrained random testing, you can create new constraint statement blocks in a derived sequence class. Here, the testplan requires that the location of the pulse varies across the frame using specific "buckets" of interesting locations in the beginning, middle, and end of the frame. A special location of 0 ensures that when no pulse is created, no pulse is detected. Likewise, for the SNR, several categories of interesting SNR ranges are defined such as SNR_never_detected and SNR_always_detected. The testwriter now can easily use these constraints to cover these scenarios.

See mw_PulseDetector_SEQCRT_param_overrides.sv.

To achieve these goals, the derived sequence class has the following extra behaviors:

  • add a "loc_bucket" member declared as "randc".

  • use that loc_bucket member to imply location ranges.

  • add a "snr_bucket" member. It is not randomized, but rather is set by the testwriter.

  • use the selected snr_bucket to imply randomization within specific SNR ranges.

  • turn off the conflicting, default value constraints

  • print out the randomized values for inspection during simulation

A new test is created which tells the UVM factory to use the new sequence class when constructing the test bench and also sets the specific SNR bucket through the uvm_resource_db.

To run the UVM simulations using these new classes, you can use the original scripts with extra arguments given through environment variables.

% Simulate the UVM test bench using derived sequence class overrides
cd uvm_build/prm_uvmtb_uvm_testbench/top
setenv EXTRA_UVM_COMP_ARGS '-f ../../../overrides_SEQCRT/extra_comp_args.f'
setenv EXTRA_UVM_SIM_ARGS +UVM_TESTNAME=mw_PulseDetector_test_classCRT
! vsim -do run_tb_mq.do     % ModelSim/QuestaSim (gui)
! vsim -c -do run_tb_mq.do  % ModelSim/QuestaSim (console)
! ./run_tb_xcelium.sh       % Xcelium (console)
! ./run_tb_vcs.sh           % VCS (console)
cd ../../..

Examine the output to see that the generated SNR is always above 1.4844 and the pulse starting location is visiting each of the ranges defined for loc_bucket. You can also confirm that when a location is 0, since no pulse is created, no pulse is detected.

Bypass Simulink Stimulus Generation

The generated sequences above are using randomization to affect the input parameters and arguments to the underlying GenPulse DPI-C functions. The actual transaction values are outputs of those functions. If you want to take full control of the actual transactions delivered to the sequencer, you can bypass the usual DPI calls and randomize the req data member.

See mw_PulseDetector_SEQCRT_param_overrides.sv.

In this case because the pulse, coefficients, and noise are all interrelated, completely randomizing their values will not yield valid test cases. But this technique is useful for other transaction types.

Conclusions and Next Steps

In a full UVM environment, whenever randomization is introduced it is usually essential to include coverage and to ensure different parts of the environment are aware of which random values were generated. In these examples randomized values are simply printed out. Adding coverage for generated SNR and location values--and relating those to detection and error thresholds--is an exercise left to the reader.