how does systemverilog argument passing value work? - system-verilog

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

Related

SystemVerilog macro through task

How to send macros as parameters through a task?
In the testbench:
`define CPU1 tb.top.dual_processor_db_wrapper_i.dual_processor_db_i.cpu1.inst
`define CPU2 tb.top.dual_processor2_db_wrapper_i.dual_processor2_db_i.cpu2.inst
initial begin
fork
cpu_init(`CPU1);
cpu_init(`CPU2);
join
// Other stuff with `CPU1 and `CPU2
`CPU1.write_data(addr, 4, data, resp); // Works
end
task cpu_init(cpu);
cpu.por_srstb_reset(1'b1); // Does not work
// Other init stuff
endtask
Error when compiling:
ERROR: [VRFC 10-2991] 'por_srstb_reset' is not declared under prefix
'cpu'
The type of the `CPUs is unknown (to me). Perhaps Xilinx has a type for it, since it references their MPSoC VIP?
I assume por_srstb_reset and write_data are tasks or functions from Xilinx MPSoC VIP, but I'm not sure.
Xilinx documentation is very sparse
I general, it is possible to pass a macro as an argument to a task. However, it is not possible to pass a hierarchical reference as an argument to a task (it is illegal).
Operations on hierarchical references are very limited, in general.
Your task declaration is equivalent to the following:
task cpu_init (input logic cpu);
The cpu variable is a 1-bit type. So, the following is legal:
`define CPU1 1'b1
cpu_init(`CPU1);
The type of the argument must match between the declaration and the task call.
There is another approach to this problem by using bind and abstract/concrete classes
package pkg;
interface class abstract_init;
pure virtual task init; // prototype for each method you need
endclass
abstract_init lookup[string]; // database of concrete classes for each instance
endpackage
module bind_module #(string lookup_name);
import pkg::*;
class concrete_init implements abstract_init;
function new;
lookup[lookup_name] = this; // register this instance
endfunction
virtual task init;
processor.reset(); // upwards reference
endtask
endclass
concrete_init c = new; // each instance of this module gets registered in lookup
endmodule
`define cpu1 top.dut.cpu1
`define cpu2 top.dut.cpu2
// macro turns any argument into a quoted string
`define Q(arg) `"arg`"
module top;
dut dut();
bind `cpu1 bind_module #(.lookup_name(`Q(`cpu1))) b();
bind `cpu2 bind_module #(.lookup_name(`Q(`cpu2))) b();
initial fork
pkg::lookup[`Q(`cpu1)].init;
pkg::lookup[`Q(`cpu2)].init;
join
endmodule
module dut;
processor cpu1();
processor cpu2();
endmodule
module processor;
initial $display("Starting %m");
task reset;
#1 $display("executing reset on %m");
endtask
endmodule
This is described more detail in my DVCon paper: The Missing Link: The Testbench to DUT Connection.

SystemVerilog task that can force any signal in interface module

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

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

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