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.