Disabling a scoreboard from a sequence using UVM - system-verilog

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;

Related

Modifying queue of class in systemverilog function

I met a problem when I trying to modify a queue of class in systemverilog function.
Here are the codes:
module my_module;
class dscr;
logic mode;
function void print_dscr;
$display("mode = %d", this.mode);
endfunction
endclass
dscr a_dscr_q[$];
dscr b_dscr_q[$];
initial begin
descriptor_decode(0, a_dscr_q);
for (int I=0; I<a_dscr_q.size(); i++)
a_dscr_q[i].print_dscr();
descriptor_decode(1, b_dscr_q);
for (int I=0; I<a_dscr_q.size(); i++)
a_dscr_q[i].print_dscr();
for (int I=0; I<b_dscr_q.size(); i++)
b_dscr_q[i].print_dscr();
end
function void descriptor_decode(logic mode, ref dscr dscr_q[$]);
dscr dscr_dec = new;
dscr_dec.mode = mode;
dscr_q.pushback(dscr_dec);
endfunction
endmodule
I am trying to create different class queue in function "descriptor_decoder", with different value of input mode. In function, I firstly create a new dscr class and then push it to a class queue. However the simulation result are:
mode = 0
mode = 1
mode = 1
The first time I call the function, it did push back the correct class into a_dscr_q. But the second function call, it seems the class is push back into both a_dscr_q and b_dscr_q. I am quite confused, What happened in here?
Your code was made illegal syntax in the IEEE 1800-2009 LRM because of the very problem you are experiencing. Most tools now report this as an error.
Your descriptor_decode is function with a static lifetime, and the dscr_dec variable declared inside it has a static lifetime as well.
You are not allowed to have an initialization on a variable whose lifetime is implicitly static and has the option to be declared automatic. This is because unlike most programming languages, the default lifetime of variables in a SystemVerilog function is static, and initialization of static variables happens once before time 0, not each occurrence of calling the function. In your example, you are expecting dscr_dec to behave as an automatic.
So you need to make one of the following code changes:
explicitly declare dscr_dec automatic
declare the function automatic, which makes variables declared inside it implicitly automatic
declare the module automatic, which makes functions declared inside it implicitly automatic
split the declaration and initialization do that the initialization happens when the function gets called.

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

How to access randomized sequence_item from another sequence?

I have a testbench where I have two sequences: sequenceA and sequenceB with their corresponding sequence items (sqitemA and sqitemB). sequenceA is input to my DUT and randomly generates the values for sqitemA.
Now, I need to access some of these generated random fields in sqItemA to create a related sqItemB for my sequenceB.
Does UVM provide a mechanism to do this?
For example:
class sequenceA extends uvm_sequence;
rand logic[31:0] address;
rand bit enable;
// skipping constructor
task body;
this.randomize();
// do body stuff
endtask
endclass
class sequenceB extends uvm_sequence;
rand logic[31:0] address;
rand bit enable;
// skipping constructor
task body;
// here I want this.address to match the address randomly generated in sequenceA
// wait_till_sequenceA randomization is complete
// this.address = sequenceA.address (??);
// do body stuff
endtask
endclass
Any clue on best way to do this?
If you want to synchronize traffic across multiple sequences, your best bet is using a virtual sequence:
class virtual_seq extend uvm_sequence;
sequence_a seq_a;
sequence_b seq_b;
`uvm_declare_p_sequencer(virtual_sequencer)
task body();
// create sequence A
// ...
// start sequence A on appropriate sequencer
fork
seq_a.start(p_sequencer.seqr_a);
join_none
// wait until seq_a's item finishes
// 'end_event' is built into uvm_transaction and is trigger
// when the driver calls item_done()
seq_a.seq_item_a.end_event.wait_trigger();
// create sequence B based on seq_a.seq_item_a
// ...
// start sequence B
fork
seq_b.start(p_sequencer.seqr_b);
join_none
endtask
endclass
The virtual_sequencer class contains handles of both bus sequencers. We've implemented synchronization here by letting sequence A do an item and only then start sequence B. Notice that sequence A runs on while we do this, since we haven't killed it. You can implement any kind of synchronization here, like grabbing seqr_a to pause sequence A until a certain point in sequence B's execution, etc. To get more details have a look in the UVM user guide.
If you want to wait only until the seq_a created and randomized its seq_item_a, you'll have to define a hook event inside it:
class sequence_a extends uvm_sequence #(sequence_item_a);
event item_randomized;
task body();
// create 'seq_item_a'
// ...
seq_item_a.randomize();
-> item_randomized;
endtask
endclass
In the virtual sequence code, instead of waiting on end_event, just wait on item_randomized.
You can also make the item_randomized event part of the sequence item itself and trigger it from post_randomize():
class sequence_item_a extends uvm_sequence_item;
event item_randomized;
function post_randomize();
-> item_randomized;
endfunction
endclass
The proper way to do this would be to remove the randomize part from the sequence body (if possible) and randomizing it before the seq.start method shown in Tudor's answer.
class virtual_seq extend uvm_sequence;
sequence_a seq_a;
sequence_b seq_b;
`uvm_declare_p_sequencer(virtual_sequencer)
task body();
// create sequences
// ...
// randomize seq A
seq_a.randomize();
//randomize seq B
seq_b.randomize() with {address = seq_A.address;}
// start sequences
// ...
endtask
endclass

How to intercept uvm_error and cause a callback?

I have a UVM scoreboard that has multiple checks that cause `uvm_error.
I would like to automatically intercept the uvm_error and dump the contents of my scoreboard. Other engineers will be adding checks to the scoreboard (and its children), so the callback should be as transparent as possible.
Simple example for what I'm trying to do:
task run_phase(uvm_phase phase);
phase.raise_objection(this);
// How to do an automatic sb.dump_contents() callback?
`uvm_error("ERROR", "scoreboard caught an error");
phase.drop_objection(this);
endtask
function void dump_contents();
$display("The queue contents of my scoreboard.");
endfunction
You can simulate and modify the above example on EDA Playground: http://www.edaplayground.com/s/4/549
What is the UVM recommended way to do this? Can someone share working code?
You want to set up a report_catcher. this intercepts the report and you can either add the dump to the message string, or put it in a separate message. And to make the report easier to catch, you should use a unique Message ID like "SBERRDMP" to catch ScoreBoard ERRors that add a complete DuMP.
class sb_dump_catcher extends uvm_report_catcher;
function new(string name="sb_dump_catcher");
super.new(name);
endfunction
function action_e catch();
if(get_severity() == UVM_ERROR && get_id() == "SBERRDMP")
begin
sb sb_h;
if ( !$cast(sb_h, get_client()) ) `uvm_error("NOTASB", "The Message ID \"SBERRDMP\" is reserved for my scoreboard")
set_message( {get_message, "/n", sb_h.dump_contents()} );
end
return THROW;
endfunction
endclass
Then in some phase of your scoreboard, you add this catcher to the callback list
function void start_of_simulation_phase(uvm_phase phase);
sb_dump_catcher h = new;
uvm_report_cb::add(this, h);
endfunction