Object handles and randomization

I am trying to create a constraint for the following scenario.


typedef enum {TOYOTA, HONDA, FORD} brand_t; // To make dave_59 happy :)

class car;
  rand brand_t brand_e;
endclass

class toyota extends car;
  constraint my_brand {brand_e == TOYOTA;}
  // Adds other constraints, methods and variables related only to this class
endclass

class ford extends car;
  constraint my_brand {brand_e == FORD;}
  // Adds other constraints, methods and variables related only to this class
endclass

class txn;
  rand car mycars_arr[];
  // I need to randomize mycar_arr. Here are my constraints:
  // 1- Array size
  // 2- Type of car in each array location
  // 3- Then based on the types i end up in each location, I want to randomize an object of the right type of class and assign that object to the array location. This is the most challenging part.
endclass


Randomization doesn’t create new objects, so you can’t get what you want. The only way (I know of) to do this in SystemVerilog is to split your randomization into two steps. You first randomize an array containing the kinds of objects to be created. Afterwards you create the objects. Finally, you randomize the newly created objects.

1 Like

In reply to Tudor Timi:

Agreed. The challenge is I want to do all that inside one class using constraints. I can use solve before to ensure the brands are solved first. I can also use foreach to loop over the types and create the right objects, but will randomizing the newly created objects happen then?

If you can show some pseudo code, that might help me understand your proposal?

Thanks
-Bahaa

In reply to Bahaa Osman:

Off the top of my head, this is how you could implement it:


class car;
  // Not rand, since you can't use randomization on a car object to suddenly make it a Toyota
  brand_t brand_e;

  // Adds other constraints, methods and variables related to all cars
endclass


class toyota extends car;
  function new();
    brand_e = TOYOTA;
  endfunction

  // Adds other constraints, methods and variables related only to this class
endclass


class txn;
  rand brand_t brands[];
  rand car mycars_arr[];

  function void allocate_cars();
    mycars_arr = new[brands.size()];
    foreach (brands[i])
      case (brands[i])
        TOYOTA: mycars_arr[i] = toyota::new();
        // ...
      endcase
  endfunction
endclass


class test;
  function void run();
    txn t = new();
    t.randomize(brands) with {
      // whatever constraints you want on brands
    };
    t.allocate_cars();
    t.randomize(mycars_arr) with {
      // whatever constraints you want on cars
    };
  endfunction
endclass

This is really ugly since it’s not very intuitive. Users not familiar with your ‘txn’ class will scratch their heads when they see the steps required to randomize an array of cars.

In reply to Bahaa Osman:

Another approach which works in one step could be the following:


class txn;
  rand car cars[];

  local rand toyotas[];
  local rand hondas[];
  local rand fords[];

  local const int unsigned MAX_NUM_CARS = 10;


  constraint legal_num_cars {
    cars.size() <= MAX_NUM_CARS;
  }

  function void pre_randomize();
    // randomization doesn't create objects, but it can throw them away
    // create more cars than you need so randomization can throw away excess
    cars = new[MAX_NUM_CARS];
    foreach (cars[i])
      cars[i] = new();

    toyotas = new[MAX_NUM_CARS];
    foreach (toyotas[i])
      toyotas[i] = new();

    // same for hondas and fords
  endfunction

  function void post_randomize();
    foreach (cars[i])
      case (cars[i].brand)
        TOYOTA: cars[i] = toyotas[i];
        // same for hondas and fords
      endcase
  endfunction
endclass


class test;
  function void run();
    txn t = new();
    t.randomize() with {
      cars.size() == 5;
      cars[1].brand == TOYOTA;
      cars[2].brand == HONDA;
    };
  endfunction
endclass

While this might be more natural to constrain, it’s a lot more code. You create more objects than you need, just so you can have enough for randomize to work with.

Both approaches (the two step and this one) suffer from the problem that you need to make modifications to the ‘txn’ class code when you declare a new brand. The two step is clunkier to randomize, but more efficient memory-wise. The second one (gotta find a catchy name for it) is more natural, but requires much more code and will for sure be slower. It’s also going to require a lot more boilerplate code to keep objects in sync if you have any common properties defined in the ‘car’ class. For example, if you decide to add a ‘year_of_manufacture’ field to ‘car’ and want to be able to constrain that irrespective of the car brand, you’d need constraints to keep the ‘cars’ array in sync with the ‘toyotas’, ‘hondas’ and ‘fords’:


class txn;

  constraint keep_common_vars_in_sync {
    foreach cars[i] {
      toyotas[i].year_of_manufacture == cars[i].year_of_manufacture;
      hondas[i].year_of_manufacture == cars[i].year_of_manufacture;
      // ...
    }
  }

endclass


class test;
  function void run();
    txn t = new();
    t.randomize() with {
      cars.size() == 5;
      cars[0].brand == TOYOTA;
      cars[0].year_of_manufacture == 2017;
    };
  endfunction
endclass

Things are even more complicated if you want more funky constraints like “if the car at index i was built in 2017 then it has to be a Toyota and the model should be a Prius” (here, I’m assuming the model is a variable that’s defined in the ‘toyota’ class).

Note: I haven’t tested the code, but I think it should work.

In reply to Tudor Timi:

Great, now you reached exactly the same point I was at before starting this thread :) In fact, my current implementation is your second proposal. And for the exact reasons you mentioned, I was asking for a better solution here. Thanks for the thoughts!

In reply to Bahaa Osman:

In either case, how to access brand specific field ?

for eg: let’s say Toyota has a field called prius

class toyota extends car;
int prius;
endclass

now, if someone wants to hierarchically access the field like
i_txn.mycars_arr[0].prius; //this is going to be an issue right ?

[assuming mycars_arr[0] is randomized to toyota]

i.e user has to typecast mycars_arr[0] back to Toyota handle and access it right ?

In reply to ssureshg_:

You will have to cast to the right type to be able to access those fields. But that’s expected so I don’t see this is as an issue, at least not yet!

In reply to Bahaa Osman:
Hi Bahaa Osman, here is some quick code…

#1 not too many unnecessary objects are created(though some redundancy is still there)
#2 No need to keep constraints in sync
#3 adding another brand does not require base class modification
but adding another field to enum does cause issue-
can be fixed by constraining base class to randomize known types at that time

intentionally left size randomization outside the class to keep it simple…
did not check the code well - please point out if there are any glaring issues :)


typedef enum {HONDA, TOYOTA, FORD,TESLA} brand_t; 
 
class car;
  rand brand_t brand_e;
  virtual function void print();
    $display("obj handle of type car ");
  endfunction 
endclass
 
class toyota extends car;
  constraint my_brand {brand_e == TOYOTA;}
  // Adds other constraints, methods and variables related only to this class
  virtual function void print();
    $display("obj handle of type toyota");
  endfunction 
endclass
 
class honda extends car;
  constraint my_brand {brand_e == HONDA;}
  // Adds other constraints, methods and variables related only to this class
  virtual function void print();
    $display("obj handle of type honda");
  endfunction 
endclass

class ford extends car;
  constraint my_brand {brand_e == FORD;}
  // Adds other constraints, methods and variables related only to this class
  virtual function void print();
    $display("obj handle of type ford");
  endfunction 
endclass
 
class tesla extends car;
  constraint my_brand {brand_e == TESLA;}
  // Adds other constraints, methods and variables related only to this class
  virtual function void print();
    $display("obj handle of type tesla");
  endfunction 
endclass

class txn;
  rand car mycars_arr[];
  int size;
  // I need to randomize mycar_arr. Here are my constraints:
  // 1- Array size
  // 2- Type of car in each array location
  // 3- Then based on the types i end up in each location, I want to randomize an object of the right type of class and assign that object to the array location. 

    function void pre_randomize(); //create objects during pre_rand, else randomize() has no impact
      mycars_arr = new[size];
      for (int i=0;i<size;i++)
        begin
	  mycars_arr[i]= new(); //objects are used only to create random list of brand_e, later in post_rand this will be loaded with apprp objects
        end
    endfunction

  function void post_randomize(); //randomized value of brand_e is available only at post_randomize
   toyota toyota_;
   honda  honda_;
   ford   ford_;
   car    car_;
   foreach (mycars_arr[i])
   begin
    case(mycars_arr[i].brand_e)
      TOYOTA : begin mycars_arr[i]= toyota::new();  mycars_arr[i].randomize(); end //fixed ptr issue, thnx Bahaa Osman
      HONDA  : begin mycars_arr[i]= honda::new() ;  mycars_arr[i].randomize() ; end
      FORD   : begin mycars_arr[i]= ford::new()  ;  mycars_arr[i].randomize()  ; end
      default: begin default_case(mycars_arr[i]); end
    endcase
   end
  endfunction:post_randomize

  virtual function void default_case(ref car car_);
  endfunction: default_case
endclass

class txn2 extends txn;
  tesla tesla_;
  virtual function void default_case(ref car car_);
    case(car_.brand_e)
      TESLA: begin car_ = tesla::new(); car_.randomize(); end
    endcase
  endfunction: default_case
endclass

module tb();

txn  i_txn  = new();
txn2 i_txn2 = new();

initial 
begin

repeat(1)
begin
  i_txn.size =5; //size of array is to be controlled before randomize, this is to simplify and keep object creation to min
  i_txn.randomize; 
  foreach(i_txn.mycars_arr[i]) i_txn.mycars_arr[i].print(); 

  $display("txn2 type randomization, adds tesla to the mix");
  i_txn = i_txn2; //addition of new brand does not need any modification to base class
  i_txn.size =5;
  i_txn.randomize; 
  foreach(i_txn.mycars_arr[i]) i_txn.mycars_arr[i].print(); 
end

end

endmodule:tb


EDIT: Fixed ptr issue as per Bahaa Osman’s input, thnq

In reply to ssureshg_:

Thanks for the detailed proposal. While this does work, there is an important limitation. That is controlling this txn from a sequence. I need to be able to send a txn with a specific list of car brands and potentially specific values of each brand’s fields. The code in post_randomize breaks this. You also can’t move it to pre-randomize.

One more thing, if I understand your code correctly, all the toyotas in mycars_arr will be pointing to the same object (the last one that was created and randomized). I think you need to clone instead of assign

TOYOTA : begin toyota_ = toyota::new();  toyota_.randomize(); $cast(mycars_arr[i], toyota_.clone()); end

Any ideas to control the txn from the sequence?

-Bahaa

In reply to Bahaa Osman:

I need to be able to send a txn with a specific list of car brands

both of the below format works for me…


  //i_txn.randomize() with {foreach (i_txn.mycars_arr[i]) i_txn.mycars_arr[i].brand_e == TOYOTA; }; 
  i_txn.randomize() with { i_txn.mycars_arr[0].brand_e == TOYOTA;
i_txn.mycars_arr[1].brand_e == FORD;
i_txn.mycars_arr[2].brand_e == HONDA;
i_txn.mycars_arr[3].brand_e == TOYOTA;
i_txn.mycars_arr[4].brand_e == FORD;
                          }; 

and potentially specific values of each brand’s fields.

this needs some thinking…let me chk

In reply to ssureshgverifier@gmail.com:

Hello sir ,
Could you explain the working mechanism of the rand object (mycars_arr) created inside txn class . After calling randomization for txn class from the top module how it has created brand_e value for every mycars_arr[i] which is eventually used in case block in post_randomize method despite not calling randomization call for every mycars_arr[i].
I got confused in this part.