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
Related
Now I'm analyzing the UVM code as below for studying.
// UVM run_phase()
task run_phase(uvm_phase phase);
forever begin
// send the item to the DUT
send_to_dut(req);
end
endtask : run_phase
task send_to_dut(uart_frame frame);
endtask : send_to_dut
But I'm confused that how send_to_dut(req)'s req argument can pass to the send_to_dut(uart_frame frame)'s uart_frame frame? It's quite confusing.
req --> uart_frame frame
It works because the value in this case is a class handle. And a handle is a reference to a class object. So you are passing a reference by value. See this link for more details
interface dut_if();
logic sig_a_i;
logic [1:0] sig_b_i;
endinterface
module tb();
dut_top dut(
.sig_a_i (vif.sig_a_i);
.sig_b_i (vif.sig_b_i);
);
dut_if vif();
endmodule
How to create a task() method inside the interface class such that I can easily call it within my test class to force/release any DUT signal I like?
class dut_testA_vseq extends dut_base_vseq;
...
virtual task body();
p_sequencer.vif.force_dut_signal(0);
endtask
endclass
I'm new to SV and I don't know how to write force_dut_signal() that can cater to any signal (single bit or a bus).
task force_dut_signal(logic? port_name, bit? force_val) begin
force port_name? = force_val;
endtask
The ? in the code snippet above are parts I don't know if possible or what.
Thanks in advance!
There is nothing in SystemVerilog that allows you to pass a hierarchical reference to a signal as a reference to a task/function argument. Inside your interface, you will need to create a function for each signal or group of signals you need to force. Then call that function from class.
BTW, always use functions instead of tasks for non-time-consuming procedures.
You could pass a signal by reference into your task. However the data types must match and you can't use a fork, join_any, join_none inside your task. This works for sig_a_i only:
interface dut_if();
logic sig_a_i;
logic [1:0] sig_b_i;
endinterface
module dut_top(
input logic sig_a_i,
input logic [1:0] sig_b_i);
endmodule
module tb();
dut_top dut(
.sig_a_i (vif.sig_a_i),
.sig_b_i (vif.sig_b_i)
);
dut_if vif();
function automatic force_1bit_logic(ref logic signal, input bit value);
signal = value;
endfunction
initial begin
#1;
force_1bit_logic(vif.sig_a_i, 0);
#10;
force_1bit_logic(vif.sig_a_i, 1);
end
endmodule
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
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
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