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]}};