Category Archives: OSVVM

OSVVM Webinar (June 25th) and Classes

Webinar OSVVM for VHDL Testbenches. Thursday June 25, 2015
Open Source VHDL Verification Methodology (OSVVM) is a comprehensive, advanced VHDL verification methodology. Like UVM, OSVVM is a library of free, open-source code (packages). OSVVM uses this library to implement functional coverage, constrained random tests, and Intelligent Coverage random tests with a conciseness, simplicity and capability that rivals other verification languages.

In 2015, OSVVM added comprehensive error and message reporting (January, 2015.01) and memory modeling (June, 2015.06). With this expanded capability, this presentation
takes a look at the big picture methodology progressing transactions to randomization to functional coverage to intelligent coverage to alerts (error reporting) and logs (message reporting) to memory modeling.

Worried about keeping up with the latest trends in verification? With Intelligent Coverage, OSVVM has a portable, VHDL-based, intelligent testbench solution built into the library. While Accellera is still working on their Intelligent testbench based portable stimulus solution (in the Portable Stimulus Working Group -PSWG), for OSVVM it is already here. Best of all, OSVVM is free and works in any VHDL simulator that support a minimal amount of VHDL-2008.

Europe Session 3-4 pm CEST 6-7 am PDT 9-10 am EDT Enroll with Aldec
US Session 10-11 am PDT 1-2 pm EDT 7-8 pm CEST Enroll with Aldec

OSVVM World Tour Dates
VHDL Testbenches and Verification – OSVVM+ Boot Camp
Learn the latest VHDL verification techniques including transaction level modeling (tlm), self-checking, scoreboards, memory modeling, functional coverage, directed, algorithmic, constrained random, and intelligent testbench test generation. Create a VHDL testbench environment that is competitive with other verification languages, such as SystemVerilog or ‘e’. Our techniques work on VHDL simulators without additional licenses and are accessible to RTL engineers.

July 20-24 and August 3-7 online class Enroll with SynthWorks
September 14-18 Bracknell, UK Enroll with FirstEDA
September 21-25 and October 5-9 online class Enroll with SynthWorks
October 26-30 Portland, OR (Tigard/Tualatin) Enroll with SynthWorks
November 9-13 Copenhagen, Denmark Enroll with FirstEDA
November 16-20 and November 30 – December 4 online class Enroll with SynthWorks

Presented by:
Jim Lewis, SynthWorks VHDL Training Expert, IEEE 1076 Working Group Chair, and OSVVM Chief Architect

Constrained Random Using Code Patterns

Having functions that do randomization is not constrained random. Instead, OSVVM creates a constrained random test environment by using code patterns in conjunction with randomization. This post shows a number of code patterns.

Weighted Sequences

The following example uses DistInt to generate the first test sequence 70% of the time, the second 20%, and the third 10%.

variable RV : RandomPType ;
. . . 
StimGen : while TestActive loop     -- Repeat until done
  case RV.DistInt( (7, 2, 1) ) is   
    when 0 =>  -- Normal Handling   -- 70%
       . . . 
    when 1 =>  -- Error Case 1      -- 20%
       . . . 
    when 2 =>  -- Error Case 2      -- 10%
       . . . 
    when others =>  
      report "DistInt" severity failure ;  -- Signal bug in DistInt
  end case ; 
end loop ;

Randomizing a Sequence Order

The following code segment generates the transactions for writing to DMA_WORD_COUNT, DMA_ADDR_HI, and DMA_ADDR_LO in a random order that is different every time this code segment is run. The sequence finishes with a write to DMA_CTRL. When DistInt is called with a weight of 0, the corresponding value does not get generated. Hence by initializing all of the weights to 1 and then setting it to 0 when it is selected, each case target only occurs once. The “for loop” loops three times to allow each transaction to be selected.

variable RV : RandomPType ;
. . . 
Wt0 := 1;  Wt1 := 1;  Wt2 := 1;  -- Initial Weights

for i in 1 to 3 loop             -- Loop 1x per transaction

  case RV.DistInt( (Wt0, Wt1, Wt2) ) is  -- Select transaction

    when 0 =>                    -- Transaction 0
      CpuWrite(CpuRec, DMA_WORD_COUNT, DmaWcIn);  
      Wt0 := 0 ;                 -- remove from randomization

    when 1 =>                    -- Transaction 1 
      CpuWrite(CpuRec, DMA_ADDR_HI, DmaAddrHiIn); 
      Wt1 := 0 ;                 -- remove from randomization

    when 2 =>                    -- Transaction 2 
      CpuWrite(CpuRec, DMA_ADDR_LO, DmaAddrLoIn); 
      Wt2 := 0 ;                 -- remove from randomization

    when others =>   report "DistInt" severity failure ;

  end case ; 
end loop ;

CpuWrite(CpuRec, DMA_CTRL, START_DMA or DmaCycle);

Non-repeating Values

The following code segment uses an exclude list to keep from repeating the last value.  When passing an integer value to an integer_vector parameter, an aggregate using named association “(0=> LastDataInt)” is used to denote a single element array. During the first execution of this process, LastDataInt has the value integer’left (a very small number), which is outside the range 0 to 255, and as a result, has no impact on the randomization.

RandomGenProc : process
  variable RV : RandomPType ; 
  variable DataInt, LastDataInt : integer ;
begin
  . . .  
  DataInt := RV.RandInt(0, 255, (0 => LastDataInt)) ; 

  LastDataInt := DataInt;
  . . .

The following code segment uses an exclude list to keep from repeating the four previous values.

RandProc : process
  variable RV : RandomPtype ; 
  variable DataInt : integer ; 
  variable Prev4DataInt : integer_vector(3 downto 0) := (others => integer'low) ;
begin
  . . .  
  DataInt := RV.RandInt(0, 100, Prev4DataInt) ; 

  Prev4DataInt := Prev4DataInt(2 downto 0) & DataInt ; 
  . . .

Randomization and Heuristics

Some test cases use heuristics to generate an appropriate sequence of actions. The following test generates random traffic to the FIFO by using heuristics (guesses) at length of bursts of data and delays between bursts of data.

variable RV : RandomPType ; 
. . .  
TxStimGen : while TestActive loop  
  -- Burst between 1 and 10 values
  BurstLen := RV.RandInt(Min => 1, Max => 10);
  for i in 1 to BurstLen loop 
    DataSent := DataSent + 1 ; 
    WriteToFifo(DataSent) ; 
  end loop ;
  --  Delay between bursts: (BurstLen <=3: 1-6, >3: 3-10) 
  if BurstLen <= 3 then 
    BurstDelay := RV.RandInt(1, 6) ;  -- small burst, small delay
  else
    BurstDelay := RV.RandInt(3, 10) ; -- bigger burst, bugger delay
  end if ; 
  wait for BurstDelay * tperiod_Clk - tpd ;
  wait until Clk = '1' ;
end loop TxStimGen ;

Summary

In OSVVM, code patterns plus calls to randomization functions are used to create a constrained random test.  While OSVVM does not provide a solver, using code does an adequate job for constrained random.

In fact, most constrained random tests are problematically slow.  This occurs because randomization is only uniform over large quantities of randomizations.  Verification on the other hand typically only requires 1 or 2 of each unique test sequence.   As a result, using randomization to generate N unique test cases requires O(N*Log N) randomizations.

To avoid generating “log N” extra test cases, OSVVM uses Intelligent Coverage™ randomization as its primary method of randomization. Intelligent Coverage™ is an Intelligent Testbench methodology that can does a random walk across the functional coverage model. As a result, closure of the functional coverage model using Intelligent Coverage is significantly faster than constrained random – even if using a constraint solver.  For more see blog post, “Intelligent Coverage basics.”

DAC OSVVM Birds of Feather Meeting

Coming to DAC?

I will be hosting an OSVVM birds of feather (BOF) / community meeting. The meeting is intended to provide the opportunity to learn more and discus future directions of OSVVM.

It is on Tuesday evening June 9 from 7 pm to 8:30 pm in Room 300

Using Alerts

Like VHDL assert, calling an alert signals errors during run time with a message, such as the following:

%% Alert ERROR   CpuModel:  No nRdy during CPU Read at 20160 ns

What differentiates alert from assert is that when the test is done, a report of the alerts that were accumulated during a test can be printed. If the test passed, a message of the following format can be printed:

%% DONE  PASSED  t1_basic  at 120180 ns

Alerts support two modes: simple and hierarchy. In simple mode, there is single global alert counter that accumulates the number of FAILURE, ERROR, and WARNING level alerts for the entire testbench. With simple mode, if a test fails, a message of the following format can be printed:

%% DONE  FAILED  t1_basic  Total Error(s) = 2  Failures: 0  Errors: 1  Warnings: 1  at 0 ns

In hierarchy mode, there is a hierarchy of alert counters. Each model and/or source of alerts has its own set of alert counters. Counts from lower levels propagate up to the top level counter. The goal of hierarchy mode is to be able to produce a summary of alerts for each model and/or source of alerts, such as the following. It is hierarchy mode that differentiates AlertLogPkg from other alert reporting packages.

%% DONE  FAILED  Testbench  Total Error(s) = 21  Failures: 1  Errors: 20  Warnings: 0  at 10117000 ns
%%    Default            Failures: 0  Errors: 4  Warnings: 0
%%    OSVVM              Failures: 0  Errors: 0  Warnings: 0
%%    U_CpuModel         Failures: 0  Errors: 4  Warnings: 0
%%      Data Error       Failures: 0  Errors: 2  Warnings: 0
%%      Protocol Error   Failures: 1  Errors: 2  Warnings: 0
%%    U_UART_TX          Failures: 0  Errors: 6  Warnings: 0
%%    U_UART_RX          Failures: 0  Errors: 6  Warnings: 0

All of the alert information is stored in a data structure internal to the AlertLogPkg. In addition to counting alerts, this allows us to disable or enable alerts, and to stop when a specified number of alerts of a given level have occurred.

All printing done by AlertLogPkg uses the OSVVM TranscriptPkg. Hence, if TranscriptFile is open, printing is directed to the file, otherwise, printing is directed to std.textio.OUTPUT.

Simple Mode:  Global Alert Counter

By default, there is a single global alert counter. All designs that use alert (or log) need to reference the package AlertLogPkg.

library osvvm ; 
    use osvvm.AlertLogPkg.all ; 
entity tb is

An alert is signaled by calling one of the Alert procedures: Alert, AlertIf, AlertIfNot, AlertIfEqual, AlertIfNotEqual, or AlertIfDiff (for files). Alerts can have a level of FAILURE, ERROR, or WARNING.

--                    message,         level
When others =>  Alert("Illegal State", FAILURE) ; 
. . . 
read(Buf, A, ReadValid) ;
--            condition, message,            level
AlertIfNot( ReadValid, "Read of A failed", FAILURE) ;
. . . 
--              value1,     value2,       message,             level 
AlertIfNotEqual(ActualData, ExpectedData, "Data Actual /= Expected", ERROR) ;

The output for an alert is as follows. Alert adds the time at which the error occurred.

%% Alert ERROR   Read of A failed  at 20160 ns

When a test completes, use ReportAlerts to provide a summary of errors.

ReportAlerts( Name => t1_basic) ;

When a test passes, the following message is generated:

%% DONE  PASSED  t1_basic  at 120180 ns

When a test fails, the following message is generated (on a single line):

%% DONE  FAILED  t1_basic  Total Error(s) = 2  Failures: 0  Errors: 1  Warnings: 1  at 120180  ns

Similar to assert, by default, when an alert FAILURE is signaled, a test failed message (see ReportAlerts) is produced and the simulation is stopped. Alerts generalize this capability with a stop count. A stop count allows a test to stop after a specified number of alerts have been seen. The default for FAILURE is 0 and the default for ERROR and WARNING is integer’right. The following call to SetAlertStopCount, causes a simulation to stop after 20 ERROR level alerts are received.

SetAlertStopCount(ERROR, 20) ;

Alerts can be enabled by a general enable, SetGlobalAlertEnable (disables all alert handling) or an enable for each alert level, SetAlertEnable. The following call to SetAlertEnable disables WARNING level alerts.

SetGlobalAlertEnable(TRUE) ; -- Default
SetAlertEnable(WARNING, FALSE) ;

Hierarchy of Alerts

In hierarchy mode, each model and/or source of alerts has its own set of alert counters. Counts from lower levels propagate up to the top level counter. The reason to use hierarchy mode is to get a summary of errors for each model and/or source of alerts in the testbench:

%% DONE  FAILED  Testbench  Total Error(s) = 21  Failures: 1  Errors: 20  Warnings: 0  at 10117000 ns
%%    Default            Failures: 0  Errors: 4  Warnings: 0
%%    OSVVM              Failures: 0  Errors: 0  Warnings: 0
%%    U_CpuModel         Failures: 0  Errors: 4  Warnings: 0
%%      Data Error       Failures: 0  Errors: 2  Warnings: 0
%%      Protocol Error   Failures: 1  Errors: 2  Warnings: 0
%%    U_UART_TX          Failures: 0  Errors: 6  Warnings: 0
%%    U_UART_RX          Failures: 0  Errors: 6  Warnings: 0

To implement hierarchy mode, AlertLogPkg has a data structure inside of a shared variable. Each level in a hierarchy is referenced with an AlertLogID (of AlertLogIDType). To use alert (or log), a model must allocate an AlertLogID. Then when calling alert (or log), it specifies the AlertLogID as the first parameter in the call.

A new AlertLogID is created by calling the function GetAlertLogID. GetAlertLogID has two parameters: Name and ParentID (of AlertLogIDType). Name is a string value that will be printed as the hierarchy name.

The following example creates three AlertLogIDs: one for the CpuModel (CPU_ALERT_ID), as well as two separate alert counters for counting Data Errors (DATA_ALERT_ID) and Protocol Errors (PROTOCOL_ALERT_ID).  CpuModel uses ALERTLOG_BASE_ID (the default if not specified) as the ParentID. DATA_ALERT_ID and PROTOCOL_ALERT_ID use CPU_ALERT_ID as the ParentID.

constant CPU_ALERT_ID : AlertLogIDType := 
    --            Name (string value),         Parent AlertLogID
    GetAlertLogID(PathTail(CpuModel'PATH_NAME), ALERTLOG_ BASE_ID) ;
constant DATA_ALERT_ID : AlertLogIDType := 
    GetAlertLogID("Data Error", CPU_ALERT_ID) ;
constant PROTOCOL_ALERT_ID : AlertLogIDType := 
    GetAlertLogID("PROTOCOL Error", CPU_ALERT_ID) ;

The AlertLogID is specified first in calls to Alert, SetAlertEnable, and SetAlertStopCount.

--             AlertLogID,    Level,    Enable
SetAlertEnable(CPU_ALERT_ID,  WARNING,  FALSE) ; 
--                AlertLogID,   Level,   Count
SetAlertStopCount(CPU_ALERT_ID, ERROR,      20) ;
Alert(CPU_ALERT_ID, "CPU Error", ERROR) ;
AlertIf(PROTOCOL_ALERT_ID, inRdy /= '0', "during CPU Read operation", FAILURE);
AlertIfNotEqual(DATA_ALERT_ID, ReadData, ExpectedData, "Actual /= Expected Data");

The format of an alert message includes the AlertLogID as shown below.

%% Alert FAILURE in CPU_1, Expect data XA5A5 at 2100 ns

Going Further

For a complete command reference guide, see AlertLogPkg_User_Guide.pdf

Using Logs

Logs provide a mechanism to conditionally print a formatted message. What makes them powerful is that they can be enabled or disabled from different places in the testbench. Log supports levels DEBUG, INFO, FINAL, and ALWAYS. The level ALWAYS enabled, all the others are disabled by default.

In common with alerts, logs support two modes: simple and hierarchy. In simple mode, there is a single set of global controls for enabling DEBUG, INFO, and FINAL. In hierarchy mode, there are separate controls for enabling DEBUG, INFO, and FINAL in each model and/or control point in the hierarchy.

All printing done by AlertLogPkg uses the OSVVM TranscriptPkg. Hence, if TranscriptFile is open, printing is directed to the file, otherwise, printing is directed to std.textio.OUTPUT.

Simple Mode: Global Log Control

Simple mode is available by default. In simple mode, there is a single set of global controls for enabling DEBUG, INFO, and FINAL. The following provides a short example of setting log controls.

All designs that use log (or alert) need to reference the package AlertLogPkg.

library osvvm ; 
    use osvvm.AlertLogPkg.all ; 
entity tb is

In simple mode, levels can be enabled or disabled from anywhere in the testbench by calling SetLogEnable. The log ALWAYS is always enabled, all other logs are disabled by default.

SetLogEnable(DEBUG, TRUE) ;

The following log will print “A message” when DEBUG is enabled.

Log ("A message", DEBUG) ;

The format of a log message is as follows.

%% Log   DEBUG  A Message  at 15110 ns

Hierarchy Mode: Each model supports separate controls.

In hierarchy mode, there are separate controls for enabling DEBUG, INFO, and FINAL in each model and/or control point in the hierarchy.

To implement hierarchy mode, AlertLogPkg has a data structure inside of a shared variable. Each level in a hierarchy is referenced with an AlertLogID (of AlertLogIDType). To use log (or alert), a model must allocate an AlertLogID. Then when calling log (or alert), it specifies the AlertLogID as the first parameter in the call.

A new AlertLogID is created by calling the function GetAlertLogID. GetAlertLogID has two parameters: Name and ParentID (of AlertLogIDType). Name is a string value that will be printed as the hierarchy name.  The following creates an AlertLogID for the CpuModel.

constant CPU_ALERT_ID : AlertLogIDType := 
    --            Name (string value),         Parent AlertLogID
    GetAlertLogID(PathTail(CpuModel'PATH_NAME), ALERTLOG_ BASE_ID) ;

The AlertLogID is specified first in calls to Log and SetLogEnable.

SetLogEnable(CPU_ALERT_ID, DEBUG, TRUE) ; 
Log (CPU_ALERT_ID, "A message", DEBUG) ;

The format of a log message includes the AlertLogID as shown below.

%% Log   DEBUG  in CpuModel_1, A Message  at 15110 ns

Log enables can also be read from a file using the procedure ReadLogEnables.

ReadLogEnables("./Test1EnableDebug.txt") ;

The file read format is:

U_CpuModel DEBUG
U_UART_TX DEBUG INFO
U_UART_RX FINAL INFO DEBUG

Going Further

For a complete command reference guide, see AlertLogPkg_User_Guide.pdf

RandomPkg Usage Basics: Protected Types, Seeds, and Randomization

The Basics

RandomPkg implements its randomization capability using a protected type, named RandomPType. Using a protected type allows the seed to be stored internal to the protected type, which in turn allows randomization to be done using functions.

To use RandomPkg, first you must reference the OSVVM library and RandomPkg as shown below.

library osvvm ; 
    use osvvm.RandomPkg.all ; 
entity tb is 

To do randomization, a process must declare its own local randomization variable as shown below.

UartTxProc : process
  variable RV : RandomPType ;   -- randomization variable
begin

Each process doing randomization needs its own randomization variable with a unique seed value. One easy way to do this is to name the process and use RV’instance_name (or RV’path_name) as a parameter to InitSeed. Using the instance_name or path_name gives each seeds a unique name – even when there are multiple instances of a stimulus generation component.

RV.InitSeed (RV'instance_name)  ; 

Randomization is done with one of the overloaded functions, such as RandInt.

RandInt := RV.RandInt(0, 255) ;

Putting all of the pieces together results in the following process.

UartTxProc : process
  variable RV : RandomPType ;                   -- protected type from RandomPkg
begin
  RV.InitSeed (RV'instance_name)  ;              -- Generate initial seeds
  for i in 0 to 255*6 loop 
    do_transaction(…, RV.RandInt(0, 255), …) ;  -- random value between 0 and 255

Note the calls to protected type methods (subprograms) include the protected type variable (RV) within the call (such as RV.RandInt(0, 255)).

Seeds and Repeatability

RandomPkg uses ieee.math_real.uniform as its basis for randomization. With uniform (or any other pseudo random sequence generator), when the same seed value is used, the same sequence, and hence, the same test is produced. This is an important fact for testing since when we fix a bug, we need to repeat the same sequence to verify the bug has been fixed.

On the other hand, we do not want two processes to repeat the same values and/or sequences when testing. Hence, we need to ensure that each process has its own randomization variable and is initialized with a unique seed value.

Stability and Random Variables

Stability is about being able rerun a test and get the exact same results. Stability is essential.

When each process has its own local variable for randomization, then each process will always produce the same sequence and the test will be stable.

On the other hand, if the randomization object is a shared variable and it is shared by many separate processes, the test will not be stable. In this case, two or more processes can randomize a value during the same delta cycle. Hence if process execution order were to change, the order of randomization would change, the randomized values would change, and the test would change. Process execution order within a given delta cycle is not defined by the language and can change due to compilation or optimization. Hence, in this situation, fixing a bug can result in the test producing different stimulus.

Testbench Transcripting

TranscriptPkg allows different parts of a testbench to print to a common transcript file.

TranscriptPkg provides an internal file identifier (TranscriptFile), subprograms for opening (TranscriptOpen) files, closing (TranscriptClose) files, printing (print and writeline), and checking if the file is open (IsTranscriptOpen).

Print prints a string to TranscriptFile when it is open, otherwise, it prints to std.textio.OUTPUT. Writeline does the same thing for an object of type line.

Below is a short program that references the TranscriptPkg, opens a transcript file, and prints using both print and writeline.

use osvvm.TranscriptPkg.all ; 
architecture Test1 of tb is

  process
  begin
    -- Open TranscriptFile
    TranscriptOpen("./results/Transcript_t1_nominal.txt") ;  

    -- Print a string value
    Print("Print 1") ; 

    -- Use textio plus WriteLine
    swrite(buf, "Write 1") ;
    writeline(buf) ; 

    -- Close TranscriptFile
    TranscriptClose ;

    -- Print only when the transcript file is open
    if IsTranscriptOpen then 
      swrite(buf, "Write 1 - transcript open") ;
      writeline(buf) ; 
    end if;

Print works well with VHDL-2008, to_string.

Print("Received value: " & to_string(IntVal) & " at " & to_string(NOW)) ;

TranscriptPkg also supports a mirror mode in which Print and WriteLine write to both TranscriptFile and std.textio.OUTPUT. Enable mirroring using SetTranscriptMirror.

SetTranscriptMirror(TRUE) ;

For more details on TranscriptPkg, see TranscriptPkg_User_Guide.pkg.

OSVVM Randomization: Normal, Poisson, FavorBig, FavorSmall distributions

Most of the randomizations in RandomPkg are based on a uniform distributions. RandomPkg also supports the distributions, normal, poisson, FavorBig and FavorSmall. The following is the overloading for these functions.

-- Generate values, each with an equal probability
impure function Uniform (Min, Max : in real) return real ;
impure function Uniform (Min, Max : integer) return integer ;
impure function Uniform (Min, Max : integer ; Exclude: integer_vector) return integer ;

-- Generate more small numbers than big
impure function FavorSmall (Min, Max : real) return real ;
impure function FavorSmall (Min, Max : integer) return integer ;
impure function FavorSmall(Min, Max: integer; Exclude: integer_vector) return integer ;

-- Generate more big numbers than small
impure function FavorBig (Min, Max : real) return real ;
impure function FavorBig (Min, Max : integer) return integer ;
impure function FavorBig (Min, Max : integer ; Exclude: integer_vector) return integer ;

-- Generate normal = gaussian distribution
impure function Normal (Mean, StdDeviation : real) return real ;
impure function Normal (Mean, StdDeviation, Min, Max : real) return real ;
impure function Normal (
      Mean          : real ;
      StdDeviation  : real ;
      Min           : integer ;
      Max           : integer ;
      Exclude       : integer_vector := NULL_INTV
) return integer ;

-- Generate poisson distribution
impure function Poisson (Mean : real) return real ;
impure function Poisson (Mean, Min, Max : real) return real ;
impure function Poisson (
      Mean          : real ;
      Min           : integer ;
      Max           : integer ;
      Exclude       : integer_vector := NULL_INTV
) return integer ;

The package also provides experimental mechanisms for changing the distributions used with functions RandInt, RandSlv, RandUnsigned, and RandSigned.

Weighted Randomization

1. Simple Weighting

A weighted distribution randomly generates a value (within a predetermined range) a specified percentage of the time. DistInt is a weighted distribution that specifies a weight and uses the corresponding index value of the weight as the value to be selected for randomization. Hence, the input to DistInt is an integer_vector of weights. The return value is the index of the selected weight. For a literal value, it will return a value from 0 to N-1 where N is the number of weights specified. The frequency that each value will occur is weight/(sum of weights). As a result, in the following call to DistInt the likelihood of a 1 to occur is 7/10 times or 70%. The likelihood of 3 is 20% and 5 is 10%.

variable RV : RandomPType ;
variable DataInt1, DataInt2 : integer ; 
. . . 
DataInt1 := RV.DistInt( (7, 2, 1) ) ;

Weighted distributions also support an exclude vector. The following code excludes the last value generated:

. . .
DataInt2 := RV.DistInt( (1, 2, 4, 8, 16, 32), (1 => DataInt2)) ;

2. Weights plus Values

DistIntVal allows the specification of a value and a weight. DistValInt is called with an array of value pairs. The first item in the pair is the value and the second is the weight. The frequency that each value will occur is weight/(sum of weights). As a result, in the following call to DistValInt, the likelihood of a 0 is 70%, 1 is 20% and 2 is 10%.

variable RV : RandomPType ;
variable DataInt1, DataInt2 : integer ; 
. . . 
-- Generate 1 70%,  3 20%, and 5 10% of the time
DataInt1 := RV.DistValInt(  ((1, 7), (3, 2), (5, 1)) ) ;
. . . 
-- Generate 1, 3, 5, 7 each 25% of the time and skip the previously generated value.
DataInt2 := RV.DistValInt(  ((1, 25), (3, 25), (5, 25), (7, 25)), (1 => DataInt2) ) ;

Basic Randomization in OSVVM

1. Randomizing Integers

The basic randomization generates an integer value that is either within some range or within a set of values. The set of values and exclude values are all of type integer_vector (defined in VHDL-2008). The examples below show the basic randomization overloading. When a value of integer_vector is specifed, the extra set of parentheses denote that it is an aggregate value.

RandomGenProc : process
  variable RV      : RandomPType ;         -- protected type from RandomPkg
  variable DataInt : integer ;
begin
  RV.InitSeed (RV'instance_name)  ;         -- Generate initial seeds

  -- Generate a value in range 0 to 255
  DataInt := RV.RandInt(0, 255) ; 
  . . . 
  -- Generate a value in range 1 to 9 except exclude values 2,4,6,8
  DataInt := RV.RandInt(1, 9, (2,4,6,8)) ; 
  . . . 
  -- Generate a value in set 1,3,5,7,9
  DataInt := RV.RandInt( (1,3,7,9) ) ;  -- note two sets of parens required
  . . . 
  -- Generate a value in set 1,3,5,7,9 except exclude values 3,7
  DataInt := RV.RandInt((1,3,7,9), (3,7) ) ; 

The overloading for the RandInt functions is as follows.

impure function RandInt (Min, Max : integer) return integer ;
impure function RandInt (Min, Max: integer; Exclude: integer_vector) 
    return integer ;
impure function RandInt ( A : integer_vector ) return integer ;
impure function RandInt ( A : integer_vector; Exclude: integer_vector) 
    return integer ;

2. Randomizing std_logic_vector, unsigned and signed

These same functions are available for types std_logic_vector(RandSlv), unsigned (RandUnsigned) and signed (RandSigned). Note that parameter values are still specified as integers and there is an additional value used to specify the size of the value to generate. For example, the following call to RandSlv defines the array size to be 8 bits.

process
  variable DataSlv : std_logic_vector(7 downto 0) ;
begin
  . . . 
  -- Generate a value in range 0 to 255
  DataSlv := RV.RandSlv(0, 255, 8) ; 
  . . . 
  -- Generate a value in range 1 to 9 except exclude values 2,4,6,8
  DataSlv := RV.RandSlv(1, 9, (2,4,6,8), 8) ; 
  -- Generate a value in set 1,3,5,7,9
  DataSlv := RV.RandSlv( (1,3,7,9), 8 ) ;  -- note two sets of parens required
  . . . 
  -- Generate a value in set 1,3,5,7,9 except exclude values 3,7
  DataSlv := RV.RandSlv((1,3,7,9), (3,7), 8 ) ; 

The overloading for RandSlv is as shown below. RandUnsigned and RandSigned have the same overloading.

impure function RandSlv (Min, Max, Size : natural) return std_logic_vector ; 
impure function RandSlv (Min, Max : natural ; Exclude: integer_vector ; Size : natural)  return std_logic_vector ;
impure function RandSlv 
    (A: integer_vector ; size : natural ) return std_logic_vector ;
impure function RandSlv (A: integer_vector ; Exclude: integer_vector ; Size : natural)  return std_logic_vector ;

3. Randomizing Real

The function, RandReal supports randomization for type real. The function with a range, like the procedure Uniform, never generates its end values. RandReal has the following overloading:

impure function RandReal ( Min, Max : real ) return real ;
impure function RandReal ( A : real_vector ) return real ;
impure function RandReal ( A, Exclude : real_vector ) return real ;

4. Randomizing Time

The function, RandTime supports randomization for type time. RandTime supports the same overloading as RandInt. These are shown below:

impure function RandTime (Min, Max : time ; Unit : time := ns) return time ;
impure function RandTime 
   (Min, Max : time ; Exclude : time_vector ; Unit : time := ns) return time ;
impure function RandTime (A : time_vector) return time ;
impure function RandTime (A, Exclude : time_vector) return time ;