Parametrized uvm sequence item to adjust size - system-verilog

I have an issue and I am out of ideas on how to resolve it. I have a class that contains an array called data. This dynamic array can be of parametrized packed width - either 8, 16, or 32 bits for example.
class MyItem#(int WIDTH=8) extends uvm_sequence_item;
bit[WIDTH-1:0] data[];
endclass
The problem is, when I want to instantiate an object of MyItem, I need to provide it with the parameter. The WIDTH parameter however, depends on some other settings and for the puspose of the example lets assume can be randomly generated between 8,16, and 32.
class MySequence extends uvm_sequence;
rand int width;
constraint c_width {width inside {8, 16, 32}; }
task body();
MyItem#(width) req = MyItem#(width)::type_id::create("item"); // <--- THIS IS NOT ALLOWED
endtask
endclass
So I had to come up with a way to work around this. The one way is to have a handle inside MyItem to all the different widths and then use the one I need. Something to the effect of:
class MyItem extends uvm_sequence_item;
bit[ 7:0] data_08[];
bit[15:0] data_16[];
bit[23:0] data_24[];
endclass
This would allow me to use the handle depending on the packed size. But this is a bit hacky and I would prefer avoiding this as I have to keep checking the WIDTH parameter and access data_08, data_16 or data_24 seperately which bloats the code.
The other option is to just use a MAX_WIDTH parameter, and pad the bits.
class MyItem extends uvm_sequence_item;
bit[MAX_WIDTH:0] data[];
endclass
But for very large arrays, this is a waste of memory and not efficient.
The final solution I came up with, but doesn't work is to create a base class called MyItemBase, and then Item extends from this. So now I can (or rather, should be able to) access data as just obj_handle.data
class MyItemBase extends uvm_sequence_item;
bit data[];
endclass
class MyItem#(WIDTH) extends MyItemBase;
bit[WIDTH-1:0] data[];
endclass
What this allows me to do is use the below code in my sequence:
rand int width;
constraint c_width {width inside {8, 16, 32}; }
task body();
MyItemBase req
case(width)
8: req = MyItem#(8)::type_id::create("item");
16: req = MyItem#(8)::type_id::create("item");
32: req = MyItem#(8)::type_id::create("item");
default: //
endcase
// Now I should be able to use req.data
req.data = new[32];
foreach(req.data[i]) req.data[i] = $urandom();
foreach(req.data[i]) $display("data[%0d] = %0x", i, data[i]);
endtask
endclass
As you might have guessed, this didn't work. My data array is just one bit wide and I guess it is getting it from the base class, so polymorphism doesn't work for variables the way it does for functions (unless I am missing something key here).
Now having the single bit version of data in the MyItemBase class causes compilation errors unless I cast to the derived type. This causes some additional complexity and I feel I am getting way in over my head on a simple problem.
Can anyone give me any suggestions on how to structure this?
Thanks

What I suggest is creating a 2-dimensional dynamic array. Since you seem to be working with bytes widths, you could do:
bit[7:0] data[][];
Then the second dimension can be constructed with
foreach(data[I]) data[I]= new[width/8];
If you need to work with a packed array, you could declare a local variable with a max width, and stream the data bytes from/to the local variable;
bit [MAX_WIDTH-1:0] temp;
...
temp = {<<{data[I]}};

Related

Why is it necessary to use constructors in dart programming language classes?

I'm a beginner learning dart from the book dart apprentice and I reached where they were discussing constructors in dart classes, the book was implying that constructors create instances of the class which I understood but I needed more info about constructors. So I googled and some results repeated what was already in the book about it being used to create instances of a class while others also showed that it's used to instantiate class properties, but my problem is with the other answer which I found that they are used to instantiate properties of a class, but my question is: I instantiate all class properties when I create the class by declaring the property variables, like this:
class UserClass{
userClassProperty = "";
anotherUserClassProperty = ""; }
why is the constructor also needed to instantiate class properties?
Often, values are unique to every class instance.
Consider the following example:
class Point {
final int x;
final int y;
const Point(this.x, this.y);
double get distanceToOrigin => sqrt(x * x + y * y);
}
If the x and y values were defined inside the class, it would be pretty useless. Instead, different Point objects can be instantiated with different values, which means the same code can be used for different situations.
Ok, so constructors instantiate or start a class by collecting all the data the class needs to start to start working. Constructors are so important that the dart compiler provides one even if you don't explicitly create one. For example, you create a class for mammals like this :
class Mammal{
String name = "cat";
int numberOfLegs = 2;
}
Although you don't explicitly add a constructor the dart compiler adds a default constructor like this :
class Mammal{
Mammal(); //This is added by dart during the class instantiation by default.
String name = "cat";
int numberOfLegs = 2;
}
Yeah, that's how crucial constructors are to the dart compiler.
And on the topic of why are they necessary even when you declare all the properties by yourself in the class, as hacker1024 said it would make the class pretty useless, as the point of the existence of classes is to create variants but with different properties. Not adding a constructor to your class and defining all the properties in the class would mean that your class doesn't take property arguments which in turn also means that different variants of your class can't be created. Again this goes directly against the point of the existence of dart classes. For example, you have a class like this :
class Mammals{
Strig name = "Human";
int numberOfLegs = 2;
bool hasFur = false;
}
final cat = Mammal();
final human = Mammal();
print(cat.numberOfLegs); //Prints 2
//2
print(human.numberOfLegs); //Also prints 2
//2
print(cat.hasFur);
// false
Yeah, this class is problematic. Cats with 2 legs? You would agree with me that that's not how things are in reality. And also the class is pretty useless in the sense that it's not modular, no matter which kind of mammal we create be it a cat, a sheep or even a cow the name property is going to be the default one we set, that is "Human". When we create a class to simulate mammals we want to be able to define what kind of properties it has, not use some fixed values. So you want to create a class which has a constructor like this :
class Mammals{
Mammals(String name,int noOfLegs, bool hasFur){
this.name = name;
this.noOfLegs = noOfLegs;
this.hasFur = hasFur;
}
String name = "";
int noOfLegs = 0;
bool hasFur = False;
}
final cat = Mammal("Cat", 4, True); //Now you can pass in the properties ou want.
final human = Mammal("Human", 2, false);
print(cat.name); //This prints the customized name of the object cat instead of some fixed value
//Cat
print(human.name); //This prints the customized name of the object human
Now we have two instances of the class with separate property values.
Although this adds a little more code, the modularity benefit is worth it.

Store reference to array/queue in SystemVerilog

I'd like to store a reference to an array/queue inside a class. It's doesn't seem possible to do this, though.
I'd like to do something like this:
class some_class;
// class member that points to the 'q' supplied as a constructor arg
??? q_ref;
function new(ref int q[$]);
this.q_ref = q;
endfunction
endclass
If q_ref is merely defined as int q_ref[$], then the assignment operator will create a copy, which isn't what I want. I'd like changes in 'q' to be visible inside the class.
Is there some hidden section in the LRM that shows how this can be done?
I'm not looking for the obvious "you have to wrap the array/queue in a class answer", but for something that allows me to interact with code that uses native arrays/queues.
There are only three variable types in SystemVerilog that can store references: class, event, and virtual interfaces variables.
You have to wrap the array/queue as a member in a class object. Then, any method of that class can be used in an event expression. Any change to a member of the class object causes a re-evaluation of that method. See the last paragraph and example in section 9.4.2 Event control of the 1800-2012 LRM.
So, the only solution for you would be to wrap the queue in a class. The latter is always assigned by a reference, as in this example:
class QueueRef #(type T = int);
T queue[$];
function void push_back(T t);
queue.push_back(t);
endfunction // push_back
endclass // Queue
class some_class;
QueueRef q_ref;
function new(QueueRef q);
this.q_ref = q;
endfunction
endclass
program test;
QueueRef q = new;
some_class c = new (q);
initial begin
q.push_back(1);
q.push_back(2);
$display(c.q_ref.queue);
end
endprogram // test

Constructing variable based on 2 random variables in seq_item

In my sequence Item I have a bus which should contain the address and data together. Now I want to randomize the address and data, after which concatenate their randomized value in the bus.
Please help understand how to do this, in the seqItem class.
class seqItem extends uvm_sequence_item;
`uvm_object_param_utils(seqItem)
rand logic [541-1:515] wfifo_addr;
rand logic [512-1:0] wfifo_data;
logic [541-1:0] wfifo_dout; // = {this.wfifo_addr, 3'b000, this.wfifo_data};
constraint wfifo_addr_ctrl { ... }
constraint wfifo_data_ctrl { ... }
…
endclass
So how to make wfifo_dout to contain the randomized values of wfifo_addr and wfifo_data.
I have to keep separate wfifo_addr and wfifo_data signals to create randomization constraints for them.
Now I am assigning value to wfifo_dout from the sequence, which randomizes the seqItem transaction. However it would be nice if I could create the value of wfifo_dout right in seqItem.
There are two things you can do:
Create a post_randomize() method that makes an assignment to wfifo_dout
function post_randomize(); // called automatically after a call to randomize();
wfifo_dout = {this.wfifo_addr, 3'b000, this.wfifo_data};
endfunction
Use the let statement to declare the address and data instead of making them separate variables
rand logic [541-1:0] wfifo_dout;
let wfifo_addr = wfifo_dout[541-1:515];
let wfifo_data = wfifo_dout[512-1:0];

Is there any method to know whether a member is declared random or not in a class in SV

// Current Class
class x;
rand int a;
int b; // b is nonrandom as of now
function new();
endfunction
function abc;
// if a != ref.a, where ref is reference object of class x, declared somewhere else
a.rand_mode(0);
endfunciton
// Future Possible Class
class x;
rand int a;
rand int b; // b is also a random variable now
function new();
endfunction
function abc;
// if a != ref.a, where ref is reference object of class x, declared somewhere else
a.rand_mode(0);
// if b != ref.b, where ref is reference object of class x, declared somewhere else
b.rand_mode(0);
endfunciton
So in function abc, depending upon whether a rand member value matches or doesn't match with the value of that member in reference class, that rand declared members of class x, should be active or inactive accordinly.
Purpose - I need to check if a rand variable matches with reference class value then only it should be randomized, otherwise not.
I want to generalize method abc, for all possible future variations (So I don't need to modify it, as done in the above example), and as I don't know, when a class member may become rand or nonrand member, Is there any inbuilt method to know, whether a member of a class is declared as rand or not in that class?
You could change your perspective on the problem slightly. Instead of trying to disable randomization for fields that are declared rand, why not say that when they get randomized, they should keep their value?
According to this nice post, there's a new construct in SV 2012, const'(...) that would work in this case. Unfortunately I don't think many vendors support it. Your randomize() call would look like this:
if (!rand_obj.randomize() with {
const'(a) != ref_obj.a -> a == const'(a);
})
$fatal(0, "rand error");
Let's dissect this code. const(a) will sample the value of a prior to doing any sort of randomization. If the value of a before randomization is not equal to the reference value, then we have the second part of the constraint that says a should keep its value. I've tried this code on two simulators but it wasn't supported by either (though it should be legal SV 2012 syntax). Maybe you're lucky enough to have a vendor that supports it.
You can write such constraints even for state variables, as they will still hold.
If you can't get the const syntax to work in your simulator, then the same post shows how you could work around the issue. You could store the values prior to randomization inside the object and use those in the constraint:
class some_class;
rand bit [2:0] a;
bit [2:0] b;
bit [2:0] pre_rand_a;
bit [2:0] pre_rand_b;
function void pre_randomize();
pre_rand_a = a;
pre_rand_b = b;
endfunction
endclass
When you want to randomize, you'd add the following constraints:
if (!rand_obj.randomize() with {
pre_rand_a != ref_obj.a -> a == pre_rand_a;
pre_rand_b != ref_obj.b -> b == pre_rand_b;
})
$fatal(0, "rand error");
You can find a full example on EDAPlayground.
You mention that your function that does randomization is defined outside of the object. Because of that, the pre_rand_* fields can't be local/protected, which isn't very nice. You should consider making the function a class member and pass the reference object to it, so that you can enforce proper encapsulation.
This isn't possible as SystemVerilog doesn't provide any reflection capabilities. You could probably figure this out using the VPI, but I'm not sure how complete the implementation of the VPI is for classes.
Based on what you want to do, I'd say it anyway doesn't make sense to implement such a query just to future proof your code in case some fields will one day become rand. Just as how you add the rand modifier to the field, you can also add it to the list of fields for which randomization should be disabled. Both code locations reside in the same file, so it's difficult to miss.
One certain simulator will return -1 when interrogating a state variable's rand_mode(), but this is non-standard. The LRM explicitly states that it's a compile error to call rand_mode() on non-random fields.

Updating a classes' variable in a constructor through pass by reference?

Blazing ahead with newfound knowledge of SystemVerilog's inner workings I've set out to use one of these fandangled pass-by-reference features to update a classes' counter in the constructor of another class. The setup (stripped to the basics) looks somewhat like this:
class my_queue;
int unsigned num_items; //Want to track the number of items this Queue has seen.
function push_new_item();
item new_item = new(num_items);
endfunction
endclass
class parent_item;
int unsigned x_th_item;
function new(ref int unsigned num_items);
x_th_item = num_items;
num_items += 1; //This should increase the counter in num_items.
endfunction
endclass
class item extends parent_item;
function new(ref int unsigned num_items);
super.new(num_items);
endfunction
endclass
The issue is that my compiler is complaining about an
Illegal connection to the ref port 'num_items' of function/task parent_item::new, formal argument should have same type as actual argument.
I have an idea on how to fix this: Moving the increment after the call to new() in push_new_items.
But then I still won't know how to correctly use pass-by-refrence in SV so what's causing the error?
Is it the other pass-by-reference or maybe a syntactical error?
You do not need ref semantics for this, use an inout argument.
inout's are copied-in upon entry and copied-out upon return of a task or function. The type compatibility requirements are much stricter as you have seen for ref arguments.
The only occasion you must use a ref argument isin time consuming tasks and you need to see active updates to the arguments before the task returns.
task my_task(ref bit tclock);
#(posedge tclock) // this would hang if tclock was an input
endtask
Another place you might want to use a ref argument is as an optimization when the argument type is a large object like an array. But passing a single int by reference is actually slower than copying its value directly.
Qiu did point me to the issue with my code. My problem was that, whilst the variables were declared correctly on both ends, one of my constructors was written:
function new(ref int num_items);
where it should have rather been
function new(ref int unsigned num_items);
Thank you Qiu.