UVM randomization dilemma

Hi all,

Appolgies in advance for setting UVM_VERBOSITY=UVM_FULL.
Im new to UVM and cant figure out the best way to randomize an array of objects.
On an abstract level one could view the array is a random sequence of cpu instructions.

In my case I have two kinds of instructions. Each type basically consists of a first header word, and then depending on the header bits, continuing words for other fields. Hence the first header word always defines the type of instruction(A or B), the number of words in the whole instruction etc.

The problem is that I would like to control the sequence of randomized objects in the array. And some fields(basically a bit of some word of the instruction) of one object may depend on another field of any previous or later object.

E.g consider the brackets ( and ) as fields. For the functional case i would like to have an array with balanced bracket fields e.g (), (()), ((())). However for an error scenario i would like to create ()))) or (())) etc.

Another ex: the first 2 objects should be type A, and the other may be any.

Im guessing I need to first create a dynamic array of objects. I have already defined the object( can be either type and has all fields of the type)

Say i randomize the array size. Now I need to figure out the constraints e.g

array[0].type == TYPE_A;
array[1].type == TYPE_B;
others TYPEA or B;
How do i randomize the balanced or unbalanced brackets across the whole array? Thanks

In reply to salmanepos:

array[0].type == TYPE_A;
array[1].type == TYPE_B;
others TYPEA or B;
How do i randomize the balanced or unbalanced brackets across the whole array? Thanks

This is pretty easy to randomize:


class instruction_stream;
  rand instruction instrs[];

  constraint num_of_instrs {
    instrs.size() <= 10;
  }

  constraint first_two_instrs {
    instrs[0].type == TYPE_A;
    instrs[1].type == TYPE_B;
  }
endclass

Since randomization can’t create objects, you’ll need to make sure you have enough already constructed objects in the array. This way, a call to randomize() can just throw away the objects it doesn’t need:



function void pre_randomize();
  instrs = new[10];  // magic number '10' could a constant defined somwhere else
  foreach (instrs*)
    instrs[i] = new();
endfunction


I prefer doing the pre-allocation in [i]pre_randomize()*, because this way it always gets done before any call to randomize(). This way, it’s not problem if you create a single stream and randomize it multiple times.

For your instruction interdependencies (the “brackets”), you’ll need to be a bit more specific, since it’s not really clear what you intend to constrain.

In reply to Tudor Timi:

Hey Tudor,

Thanks for the response. Il try to shed some more light on the brackets example. Like I said, these objects (basically a collection of words) are interprated as instructions. These objects are read out from memory, the words are decoded and some action is done. So pretty much like a simple CPU. These instructions can also be looped over ( even nested). On an abstract Level, the open bracket represented the Loop start, and closed bracket Loop end. Hence the sequence of brackets must be balanced. An unbalanced sequenced would be illegal but also needs to be simulated for the error case.

So lets say there are two fields in TYPEB which control this:

TYPEB.loop_start
TYPEB.loop_end

Considering an Array size of 10 lets say this is the randomized Array:
AABBBAABBB

Now my main Problem is how to randomize the Loop_starts and Ends across the whole Array

for example lets consider a nested Loop of depth 2

for (x…) begin


for (z) begin


end
end

Heres an example of an Array corresponding to the above example.

INDEX_____TYPE______LOOP_START____LOOP_END
0__________A_________NA_____________NA
1__________A_________NA_____________NA
2__________B__________1______________0 // outer Loop opening
3__________B__________0______________0
4__________B__________1______________0 // inner Loop opening
5__________A_________NA____________ NA
6__________A_________NA_____________NA
7__________B__________0______________0
8__________B__________0______________1 // inner Loop closing
9__________B__________0______________1 // outer Loop closing

Like you already wrote its pretty easy to create an Array of size n with objects of TPYE A or B. The tough part for me is to write the constraints that properly sets the Loop_start/ end fields.

Im guessing i Need to add some helper variables ( higher layer) e.g int nested depth.
if nested depth is 0 that would mean (). The constraints for the Loop_start/end variables
should set Loop_start(1),Loop_end(0) for some TYPE B object and for a later TYPEB one the values should be Loop_start(0),Loop_end(1)

I hope the Problem im facing is clear now. Thanks again

In reply to salmanepos:

I would try to break the problem down into some kind of recursive data structure. I would try to model instructions as a stream of “code chunks” (for lack of a better term). These code chunks can either be wrapped in loops or not. Code chunks themselves can either contain other code chunks or they can only contain raw (non-looping) instructions. This type of description is similar to a BNF. You might already have a similar description for your “stream” in your design specification.

I’m not really sure yet how to model this using classes.

In reply to salmanepos:

You may also want to look at the randsequence construct in SystemVerilog. It was specifically designed to generate instruction streams with a grammar that defines all the legal sequences of allowed instructions. You could do something similar by constructing a nested set of UVM sequences.