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.
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);
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 ;
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.”