Connecting monitor and scoreboard in UVM - system-verilog

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.

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

get virtual interface once in package

I try to write a package which include some common tasks and functions for test environment use.
For example,
package test_misc_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;
task wait_rest();
virtual test_if test_vif;
if (!uvm_config_db #(virtual test_if)::get(null, "uvm_test_top.env", "test_vif", test_vif))
`uvm_fatal("NOVIF", "virtual interface should be set")
#(posedge test_vif.clk);
//do something...
endtask
endpackage : test_misc_pkg
Then, test env can just call wait_rest() after import test_misc_pkg.
But, if there are other task use same test_vif, I still need to get db in the beginning of every tasks.
My question is, can we just get uvm_config_db once in the package, let all tasks can use test_vif directly?
Or, any better way to do this?
Thanks.
You could make you virtual interface a global variable:
package test_misc_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;
virtual test_if test_vif;
function void set_vif();
if (!uvm_config_db #(virtual test_if)::get(null, "uvm_test_top.env", "test_vif", test_vif))
`uvm_fatal("NOVIF", "virtual interface should be set")
endfunction
task wait_rest();
#(posedge test_vif.clk);
//do something...
endtask
endpackage : test_misc_pkg
and the write a function (eg set_vif()) to give the global virtual interface a value. You could then call this function in the connect_phase, for example. It all seems a little unconventional, though.
If you are trying to reduce the overhead of calling uvm_config_db::get(), you could test to see it is null, and then do the get.
task wait_rest();
virtual test_if test_vif; // this is a static variable
if (test_vif == null) begin
if (!uvm_config_db #(virtual test_if)::get(null, "uvm_test_top.env", "test_vif", test_vif))
`uvm_fatal("NOVIF", "virtual interface should be set")
end
#(posedge test_vif.clk);
//do something...
endtask

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

UVM-RAL logging into file after DUT initializaton

I want log all the UVM RAL register's along with their fields into separate file. for quick debugging. Do RAL supports any easiest way of doing it.
Thanks
Saravanan
One way to to that is by using the uvm built-in sprint() and directing all of its output to a file.
Below is an example on how to do that, assuming your register is implemented in test level:
class my_test extends uvm_test;
my_reg_block my_rm;
UVM_FILE my_file;
//...
function new(string name="", uvm_component parent=null);
my_file = $fopen("register_initial.txt", "w");
set_report_id_file("MY_REG", my_file);
set_report_id_action("MY_REG", UVM_LOW | UVM_DISPLAY);
endfunction
// create your register
function void print_register();
`uvm_info("MY_REG", $sformatf("%s", my_rm.sprint()), UVM_LOW)
endfunction
endclass
The above print_register() function will write the output of my_rm.sprint() to file "register_initial.txt".

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