%% 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.
%
% Copyright 2019-2020 The MathWorks, Inc.

%% 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)
%%
open_system('prm_uvmtb');

%% 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 <matlab:edit(fullfile('prm_uvmtb_uvmbuild','uvm_testbench','sequence','mw_PulseDetector_sequence.sv')) 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.)
%
% <<../pulse_gen_location.png>> 
%
% 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:
%
% <<../pulse_detector_UVM_seq_tunable.png>>
%
% * 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.
%
% <<../pulse_gen_snr.png>>
% 
% 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:
%
% <<../pulse_detector_UVM_seq_input.png>>
%

%% 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.
%
% <<../pulse_seqcrt_defbody.png>>
%
% 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 prm_uvmtb_uvmbuild/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_incisive.sh      % Incisive (console)
%   ! ./run_tb_xcelium.sh       % Xcelium (console)
%   ! ./run_tb_vcs.sh           % VCS (console)
%   cd ../../..
%
% <<../pulse_seqcrt_defsim.png>>
%

%% 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 <matlab:edit(fullfile('overrides_SEQCRT','mw_PulseDetector_SEQCRT_param_overrides.sv')) 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.)
%
% <<../pulse_seqcrt_inline_seq.png>>
%
% A new test is created which tells the factory to use the new sequence
% class when constructing the test bench.
%
% <<../pulse_seqcrt_inline_test.png>>
%
% 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 prm_uvmtb_uvmbuild/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_incisive.sh      % Incisive (console)
%   ! ./run_tb_xcelium.sh       % Xcelium (console)
%   ! ./run_tb_vcs.sh           % VCS (console)
%   cd ../../..
%
% <<../pulse_seqcrt_inline_sim.png>>
%
% 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 <matlab:edit(fullfile('overrides_SEQCRT','mw_PulseDetector_SEQCRT_param_overrides.sv')) 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
%
% <<../pulse_seqcrt_class_seq.png>>
%
% 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.
%
% <<../pulse_seqcrt_class_test.png>>
%
% 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 prm_uvmtb_uvmbuild/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_incisive.sh      % Incisive (console)
%   ! ./run_tb_xcelium.sh       % Xcelium (console)
%   ! ./run_tb_vcs.sh           % VCS (console)
%   cd ../../..
%
% <<../pulse_seqcrt_class_sim.png>>
%
% 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 <matlab:edit(fullfile('overrides_SEQCRT','mw_PulseDetector_SEQCRT_param_overrides.sv')) mw_PulseDetector_SEQCRT_param_overrides.sv>.
%
% <<../pulse_seqcrt_nodpi.png>>
%
% 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.
%