How to access randomized sequence_item from another sequence? - system-verilog

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

Related

UVM: create a task that gets called every 100 or so cycles for all the component.

For all my components (monitor, driver, sequencer etc.) I need to create a task that gets called every 100 or so cycle.
I think there is a way to do this with custom phases but not sure how. Any help.
The pseudo-multiple inherit feature introduced in 1800-2012 via "interface class" and "implements" is the best solution.
To satisfy your requirement, you can apply the design pattern of Observer. Below is an example code
interface class tick;
pure virtual task trigger_tick(....);
end class
class observer extends uvm_components;
......
tick tick_list[$];
virtual function void register_tick(tick t);
tick_list.push_back(t);
endfunction
virtual task run_phase(uvm_phase phase);
forever begin
repeat(100) #(posedge top.clock);
foreach (tick_list[i]) tick_list[i].trigger_tick();
end
endtask
end class
class my_component extends uvm_component implements tick;
......
virtual task trigger_tick();
//do something
endtask
end class
Then you can creat an instance of observer and register all components to the instance.
I don't think you need to implement custom phases for this. Here is an example of how you can do this.
task run_phase(uvm_phase phase);
forever begin
// wait for reset ....
fork
your_task1();
join_none
// do other things .....
end
endtask : run_phase
task your_task1(); // this task will keep running forever..
int count=1;
forever begin
#(posedge vif.clk);
if(count%100 == 0) // will be true only when count becomes multiple of 100
your_task2();
count++;
end
endtask : your_task1
task your_task2(); // one which you wanted to call every 100 clk cycles
// add your logic
endtask : your_task2
Hope this helps.
You can add that logic in run_phase of every component in the following mannger.
task run_phase (uvm_phase phase);
fork
begin
forever
begin
repeat (100) #(posedge virtual_interface.clk);
your_task();
end
end
// Other threads
join_none
wait_fork;
endtask
You could use uvm_events
like from each component generate the event at 100th cycle, and from your own class catch that event and call the task
Could you explain why you need to run a task for each of the component? I guess you are debugging something, may be transaction recording or uvm_printer would be more helpful here

Sending bus signal using analysis port

I am using UVM to test very simple interface and now facing with “corner-case” issue.
So I need to send logic[0:7] signal from output monitor to scoreboard.
This is part of the code:
class outputMonitor extends uvm_monitor;
..
logic [7:0] lcdCmd;
uvm_analysis_port #(logic) sendPrt;
task run_phase (uvm_phase phase);
forever
begin
sendPrt.write(lcdCmd) ;
end
endtask
endclass
class scoreboard extends uvm_subscriber #(logic);
`uvm_component_utils(scoreboard)
function void write (logic t);
$display("%t: scoreboard: ########### calling write function-- data=%b", $time, t);
endfunction
endclass
During simulation I see that only the last bit of lcdCmd is transferred to scorebaord. Is there a way to transfer the whole bus data using ports?
Of course I can create struct or transaction, put the lcdCmd there and then send the struct. But why I cannot just send the bus?
Thanks
Hayk
In your code you did a simple mistake.Like...
Pass type argument of analysis fifo as only logic in stand of logic[7:0] vector.
Same thing is applicable for subscriber class parameter and also at write implementation function argument.
See the code which you need to change.
class outputMonitor extends uvm_monitor;
`uvm_component_utils(outputMonitor)
logic [7:0] lcdCmd;
uvm_analysis_port #(logic[7:0]) sendPrt;
task run_phase (uvm_phase phase);
forever
begin
sendPrt.write(lcdCmd);
end
endtask
endclass
class scoreboard extends uvm_subscriber #(logic[7:0]);
`uvm_component_utils(scoreboard)
function void write (logic [7:0] t);
$display("%t: scoreboard: ########### calling write function-- data=%b", $time, t);
endfunction
endclass

Connecting monitor and scoreboard in UVM

I am constructing the UVM testbench to verify a simple design. I have learnt that scoreboard will usually be outside the agent. I want my scoreboard to be inside the agent as I have only one agent in the system. Now, in my agent, I am trying to connect monitor and scoreboard. I want to know if there is a way to connect without using fifo.
Here are my code snippets
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
uvm_analysis_port #(input_seq_item) ap_port;
input_seq_item mon_item;
....
endclass
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils(my_scoreboard)
uvm_analysis_export #(input_seq_item) ap_port_sb;
...
endclass
class agent extends uvm_agent;
`uvm_component_utils(agent)
sequencer sqr;
my_driver drv;
my_sequence seq;
my_monitor mon;
my_scoreboard sb;
...
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
mon.ap_port.connect(sb.ap_port_sb);
endfunction
...
endclass
I am getting the following error
# KERNEL: UVM_INFO # 0: reporter [RNTST] Running test test...
# KERNEL: UVM_ERROR # 0: uvm_test_top.env.sb.ap_port_sb [Connection Error] connection count of 0 does not meet required minimum of 1
# KERNEL: UVM_FATAL # 0: reporter [BUILDERR] stopping due to build errors
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_report_server.svh(855) # 0: reporter [UVM/REPORT/SERVER]
Can anyone help me with this?
Thanks in advance
The problem is you left your scoreboard analysis export hanging, but it needs to be connected to an imp port. If you're familiar with SystemC, an imp port doesn't have a direct equivalent. An import basically is a termination point of a TLM analysis connection. The imp port then forwards the calls to the component that instantiates it.
Change your code to uvm_analysis_imp #(...) and declare a write(input_seq_item ite) function for it to call and everything should work.
Check: Are you using constructor to create analysis ports?
//For monitor
function new(..);
..
monitor_ap = new("monitor_ap", this);
endfunction
Also try with Subscriber class default analysis_export!
If you don't want to declare write() function than Using FIFO is the best option.
It is very easy.Following is your edited code..
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
uvm_analysis_port #(input_seq_item) ap_port;
input_seq_item mon_item;
....
endclass
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils(my_scoreboard)
uvm_analysis_export #(input_seq_item) ap_port_sb;
...
endclass
class agent extends uvm_agent;
`uvm_component_utils(agent)
sequencer sqr;
my_driver drv;
my_sequence seq;
my_monitor mon;
my_scoreboard sb;
uvm_tlm_analysis_fifo fifo;
...
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(fifo.analysis_export);
mon.ap_port.connect(fifo.analysis_export);
endfunction
...
endclass
I think this will solve your Problem..
Generally when you use an export, then the data/transaction that is sent to it must be passed on to an import (which marks the end of the pipeline). Hence unless you want to send data from scoreboard that you are receiving to some other blocks, you can use uvm_analysis_imp #(...) in your code.
On doing so, you are making the scoreboard as a Target, and monitor as the Initiator, hence a write function has to be implemented in the Scoreboard, and called upon from the monitor whenever the transaction has to be pipelined.

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;

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