Constructing variable based on 2 random variables in seq_item - system-verilog

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

Related

How to Randomize parameter by using another random parameter?

I trying to write a random parameter to my program, but my program some parameter will use other parameter for range. I try to get from online but cannot get it. Then, i decide come here ask about it.
Hope anyone can help.
rand bit COMMON_CLK;
rand int DEPTH;
rand int DATA_WIDTH
rand int PROG_FULL_ASSERT;
rand int PROG_FULL_NEGATE;
rand int PROG_EMPTY_ASSERT;
rand int PROG_EMPTY_NEGATE;
constraint DEPTH {
DEPTH inside {[16:100000]};
DATA_WIDTH inside {[1:1024]};
}
constraint ASSERT {
PROG_FULL_ASSERT inside {[1:DEPTH.size]};
}
constraint NEGATE {
PROG_FULL_NEGAT inside {(1:PROG_FULL_ASSERT.size]};
}
constraint ASSERT {
PROG_EMPTY_ASSERT inside {[1:((DEPTH.size) - 1)]};
}
constraint NEGATE {
PROG_EMPTY_NEGATE inside {[(PROG_EMPTY_ASSERT.size) : ((DEPTH.size) - 1)]};
}
First, like Dave_59 pointed out DEPTH.size is not good code even if it does do something legal.
Secondly, if you wanted something like PROG_FULL_ASSERT inside {[1:DEPTH]}; you may want to add solve DEPTH before PROG_FULL_ASSERT to avoid distribution issues.
Lastly, in my experience sometimes it's better or at least easier to randomize some things in post.randomize(). This is assuming that nothing else is depending on them. For example:
int array;
rand int array_size;
constraint {array_size inside [100000:200000];}
void function post_randomize();
array = new[array_size];
array_data_randomization_function();
endfunction
This is not always a good idea because it adds the risk of later adding rand variables that depend on the post randomized one (array), BUT it does break the randomization to two independent actions, making the code easier to understand, and in case of heavy randomizations also makes it calculate faster.

Parametrized uvm sequence item to adjust size

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

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

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.

Disabling a scoreboard from a sequence using UVM

I have a uvm_sequence that randomizes an enable bit "feature_en". Depending on whether this bit is enabled or not, I want to enable/disable my scoreboard. I use the config_db to set the variable, and the field_macros to automatically get it in the scoreboard.
My problem is that in the scoreboard, I want to guard the code in the run_phase with "feature_en". However, the sequence is run in the run_phase of the test and thus, the run_phase of the scoreboard goes first, thereby keeping feature_en default value and not getting the value set my the sequence.
I tried using wait(feature_en != -1) (i had set it as an int), but I realize that feature_en is not sampled again in the scoreboard.
Is there a way to update feature_en dynamically in the scoreboard? Or any other way to do this?
You can create one dedicated uvm_object to do that. For example:
class feature_options extends uvm_object;
bit sb_enable=0;
// ...
endclass
Instantiate it in your top-level testbench, and then put it in uvm_config_db:
// in your top-level testbench:
feature_options f_opt;
initial begin
f_opt = new("f_opt");
uvm_config_db#(feature_options)::set(uvm_root::get(), "*", "FEATURE_OPTIONS", f_opt);
end
and then grab the object from your own scoreboard and sequencer:
// add the class handle in your scoreboard and your sequencer
feature_options my_opt;
// ... inside your scoreboard/sequencer build phase, grab the object
if (!uvm_config_db#(feature_options)::get(this,"","FEATURE_OPTIONS", my_opt)) begin
$display("Ok");
end
After this, you can dynamically synchronize your sequence and your scoreboard, for example:
// inside your scoreboard run phase
wait (f_opt.sb_enable);
and
// inside your sequence body()
p_sequencer.my_opt.sb_enable = 1;
// ... do some test, then disable scoreboard
p_sequencer.my_opt.sb_enable = 0; // and so on
feature_en can be switched on/off directly, not through the config_db
Assuming your scoreboard is a component ( your scoreboard extends uvm_scoreboard ) in the sequence :
my_env env;
....
$cast(env, get_sequencer().get_parent().get_parent()); //env.agent.sequencer
env.sb.feature_en = 1;