uvm_reg peek function takes long time to return - system-verilog

I thought the peek function of uvm_reg returned the value in 0 simulation time. Since I needed this functionality, I implemented all my HDL backdoor access paths. This is the code I am using in my scoreboard
while (state == DISABLE) begin
uvm_reg_data_t val = 'hDEADBEEF;
uvm_status_e status;
`uvm_info(get_name(), "Start peek", UVM_LOW)
my_reg_block.my_reg.peek(status, val);
`uvm_info(get_name(), "End peek", UVM_LOW)
assert (val == 'h0)
#posedge(my_vif.clk); //Advance clock
end
My intention was: On every clock cycle, in zero simulation time, assert that my_reg is 0 when the state==DISABLE.
In simulation run, I notice this is fine until around the time that my_reg is changing. At the point, Start peek -> End peek takes about 10 clock cycles. In this time, my state is no longer DISABLE and ofcourse val != 'h0. Why does peek take so long to return?
I am using Questasim 10.4a

It may take some time, because peek is a SystemVerilog task, not a function.
Function will be executed in 0 Simulation Time, but Tasks can have the
timing delays as well.
Here is it's definition.
virtual task peek( output uvm_status_e status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0 )

Related

Dynamic generation of signal spies in testbench

I have a .txt file that contains certain signals that I want to monitor in my testbench during the application of some stimulus.
I am creating an initial block in which I am reading the file and then I try to generate a init_signal_spy() for every one of the lines that I have read.
The code that I have written up until this point has the following format:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
//static logic net_to_be_probed;
automatic sig_and_spy entry = new();
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array
probes = new [probes.size() + 1] (probes);
// Link the extracted new line with the probe list
// - this raises an error "An invalid empty string was passed in as the Destination object."
// - expected since the last element is empty...
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
In the code above, just before I issue the generation for the spy, I get why the error
An invalid empty string was passed in as the Destination object.
is generated by the compiler. Although the array has been resized, it does not hold any element i.e., its empty. Thus, I tried creating locally a logic variable that then I assign to the signal spy within the loop in the following manner:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
logic new_probe;
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array and copy old values.
probes = new [probes.size() + 1] (probes);
// Add the new probe to the Testbenchs' probes array
probes[probes.size()-1] = new_probe;
// Again, An invalid empty string was passed in as the Destination object.
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
But then again, I see the same error at runtime during the simulation. So...Is there a way of achieving such a "dynamic" signal monitoring in the testbench somehow? As far as I understood the error concerns that the destination object is NOT a signal of the testbench. Thus the logic new_probe has no effect. Which is to be expected I mean, but is there a way of achieving the desired behavior in the Testbench via sysverilog?
You have at least two problems.
Both the source and destination arguments to init_signal_spy() need to be strings. Your destination argument is an integral variable with a 0 value, and that gets interpreted as a null string. init_signal_spy() was designed for mixed language simulation, and using strings was the only way to achieve that.
Your destination variable should be queue, not a dynamic array. Every time you re-size a dynamic array, the previous elements get relocated and that breaks the previous connection made by signal spy.
This example shows the proper syntax for string this up
module top;
int A[$];
int s1,s2;
initial begin
A.push_back(0);
$init_signal_spy("s1","A[0]");
A.push_back(0);
$init_signal_spy("s2","A[1]");
#1 s1 = 1;
#1 s2 = 2;
#1 $display("%p",A);
end
endmodule
A far better solution for performance is converting your .txt file into actual SystemVerilog code that can be compiled into your testbench.

Unexpected behavior with force statement inside interface task

I am seeing side affect of other unrelated signals getting affected when I use force inside a task. In the example below, I try forcing 2 independent variables "a" and "b" inside the module "dut". To do this I use the helper task "force1" inside the interface 'intf'. However I find that changing 'b' also causes 'a' to change as shown in the output.
Test case on edaplayground. https://www.edaplayground.com/x/23LM
module dut(intf i1);
logic a;
logic b;
endmodule
interface intf;
task force1(bit sel, int value);
if(sel == 0) begin
$display("[%0t]:forcing a to %0d", $stime, value);
force dut1.a = value;
end
else begin
$display("[%0t]:forcing b to %0d", $stime, value);
force dut1.b = value;
end
endtask
endinterface
module test();
intf intf1();
dut dut1(intf1);
always#(dut1.a) begin
$display("[%0t]:Changing value of a=%0d", $stime, dut1.a);
end
always#(dut1.b) begin
$display("[%0t]:Changing value of b=%0d", $stime, dut1.b);
end
initial begin
intf1.force1(.sel(0), .value(1));// Change value of a to 1
#10;
intf1.force1(.sel(1), .value(0));// Change value of b to 0
#10;
$finish;
end
endmodule
[0]:forcing a to 1
[0]:Changing value of a=1
[10]:forcing b to 0
[10]:Changing value of a=0 ----------> WHY DID THIS CHANGE?
[10]:Changing value of b=0
I expected the output 'a' not to change to 0.
The problem is that value is a static variable. When you declare a task in Verilog, unless you specify the task to be automatic, all the variables in the task will be static, meaning each call to the task that modifies a variable does so for all calls to that task (its like creating a static local variable in C).
Its also important to note that force procedural assignments dont just resolve the RHS and set the LHS to that value, but instead forces the LHS to be set to the expression on the RHS. So, an assignment like force A = B; will make A equal to B at the time the force is applied as well as any time B might be updated after. Putting this together with information above (value is static), you arent setting dut1.a to be 1 with your force, but setting it to be value. Once value changes with your next call to your task, dut1.a also changes to match it.
Now, unfortunately, you cant just make the task automatic or just make value automatic to solve the problem since force requires a static expression (ie, an expression of only static variables or constants) to work. One solution is to create a static placeholder for the values you want that disconnects your force from your task input value; you can do that with an array:
task force1(bit sel, int value);
static int values[1 << $bits(sel)]; // <- Make sure no matter the width of sel, there are enough places in the array, could just be values[2]
values[sel] = value;
if(sel == 0) begin
$display("[%0t]:forcing a to %0d", $stime, value);
force dut1.a = values[0];
end
else begin
$display("[%0t]:forcing b to %0d", $stime, value);
force dut1.b = values[1];
end
endtask

When can TLM peek fail?

I was working on OVM driver sequencer communication. I am using try_get_item() in ovm driver but it is still getting stuck. In my sequencer I redefined try_next_item and just printed a display statement before and after m_req_fifo.peek(t); The statement before peek got executed but not the statement after the peek. I even displayed size of the m_req_fifo using m_req_fifo.size() and it printed out 1. Why is peek not returning anything even after the size is 1? The modified try_next_item (Just addition of display) is given below.
The line After PEEK never gets executed after the line Line 398 with fifo size 1
virtual task try_next_item(output REQ t);
int selected_sequence;
time arb_time;
ovm_sequence_base seq;
if (get_next_item_called == 1) begin
ovm_report_error(get_full_name(), "get_next_item/try_next_item called twice without item_done or get in between", OVM_NONE);
return;
end
wait_for_sequences();
selected_sequence = choose_next_request();
if (selected_sequence == -1) begin
t = null;
return;
end
set_arbitration_completed(arb_sequence_q[selected_sequence].request_id);
seq = arb_sequence_q[selected_sequence].sequence_ptr;
arb_sequence_q.delete(selected_sequence);
m_update_lists();
sequence_item_requested = 1;
get_next_item_called = 1;
$display("Line 398 with fifo size %0d\n", m_req_fifo.size());
m_req_fifo.peek(t);
$display("After PEEK\n");
wait_for_sequences();
// attempt to get the item; if it fails, produce an error and return
if (!m_req_fifo.try_peek(t))
ovm_report_error("TRY_NEXT_BLOCKED", {"try_next_item: the selected sequence '",
seq.get_full_name(), "' did not produce an item during wait_for_sequences(). ",
"Sequences should not consume time between calls to start_item and finish_item. ",
"Returning null item."}, OVM_NONE);
endtask
uvm_tlm_fifo::size() doesn't return the number of elements in the FIFO, but its capacity (i.e. the maximum number of elements it can hold). The function you're looking for is uvm_tlm_fifo::used() which returns the number of stored elements.
The function names are not intuitive at all and I remember spending a couple of hourse trying to understand some similar code to the one you had until noticing in the documentation that I was using the wrong method.

How to get input from Scala after a certain point in time?

I'm working on a game written in Scala where the user has to type "BANG" as quickly as possible in order to hunt an animal.
// ...
println("When you see an animal, type BANG as quickly as possible.")
Thread.sleep(2000 + Random.nextInt(6000))
val start = System.currentTimeMillis()
println("You see an animal!")
val bang = readLine("")
val time = System.currentTimeMillis() - start
if (bang == "BANG" && time <= 1500) {
// ...you get the point
Unfortunately, there's a bug that makes so that you can type "BANG" ahead of time and always win, because it accepts input before the readLine().
I have tried a few different ways of clearing the input. The obvious solution, putting a readLine() before val start = System.currentTimeMillis(), doesn't work because it forces the user to press enter first.
Any ideas?
There's no standard way of clearing stdin with Scala's (or even Java's) API.
You can use this lower-level implementation RawConsoleInput to clear the buffer though.
It has a read method that returns the first character in the standard input, or -2 if the buffer is clear.
Because it uses "raw input mode", instead of the usual "line mode", keys like Enter, backspace, Ctrl+C will NOT be processed by the operating system, and will remain in the buffer until read is called.
//drain standard input
while(RawConsoleInput.read(wait = false) >= 0) {
}
Signature:
/**
* Reads a character from the console without echo.
*
* #param wait
* <code>true</code> to wait until an input character is available,
* <code>false</code> to return immediately if no character is available.
* #return
* -2 if <code>wait</code> is <code>false</code> and no character is available.
* -1 on EOF.
* Otherwise an Unicode character code within the range 0 to 0xFFFF.
*/
public static int read (boolean wait) throws IOException
Note: make sure you've imported the JNA library.

wait($time >1000); cannot work in system-verilog?

I use this code to wait for a specific simulation time
initial begin
$display("A");
wait($time>1000);
$display("B");
end
the simulation result is:
A
I didnot see B printed.
If I use following code, it works.
while($time <1000) #1;
Is it because vcs needs to judge the wait condition once any viriable in the condition statement changes, $time is changing too frequently so vcs doesnot allow this usage?
#Tudor 's answer enlighten me. I tried #Tudor 's code with some modification. It turns out when wait(func(arglist)); vcs only retry to evaluate the function when arglist changes. Because $time has no args, vcs will only evaluate $time the 1st time, won't retry.
module top;
int the_time = 0;
int in_arg = 0;
function int the_time_f(int in);
return the_time;
endfunction // the_time_f
initial begin
$display("A");
// This works because 'the_time' is a variable
//wait(the_time > 10);
// This doesn't work because 'the_time_f' is a function
wait(the_time_f(in_arg) >10);
$display("B at %t", $time);
end
initial begin
#10ns;
the_time = 11;
#10ns;
in_arg = 1;
#20ns;
$finish();
end
endmodule // top
got following result
A
B at 20ns
This seems to be a gray area in the standard. In section 9.4 Procedural timing controls of the IEEE Std 1800-2012 Standard, it's mentioned that event control can be either implicit (changed of nets or variables) or explicit (fields of type event). $time is a system function, not a variable. I've also tried using a function for the wait and it also doesn't work:
module top;
int the_time = 0;
function int the_time_f();
return the_time;
endfunction // the_time_f
initial begin
$display("A");
// This works because 'the_time' is a variable
//wait(the_time > 10);
// This doesn't work because 'the_time_f' is a function
wait(the_time_f() > 10);
$display("B");
end
initial begin
#10ns;
the_time = 11;
#20ns;
$finish();
end
endmodule // top
Waiting on a change of a variable works fine, but waiting for a change on a function's return value doesn't work. IMHO, the compiler should have flagged this as a compile error (same for using $time) since it seems to just ignore the statement.
In an event control #(expression) or wait(expression) that suspends a process, SystemVerilog scheduling semantics requires an event to evaluate the expression (called an evaluation event. See section 4.3 Event Simulation of the 1800-2012 LRM) If an expression includes a function, only the arguments to that function are visible to cause an event evaluation (There is an exception for class methods in at a write to any member of the object in the method call will cause an event) See section 9.4.2 Event control
In an event driven simulation, the value of time is just an attribute of the current time slot, it is never an event. The simulator processes all events for the current time slot in a queue, and when that queue is empty, it advances time to the next time slot queue. So it might simulate time slots 0,5,7,10, skipping over the unmentioned times. Using your while loop, that would create a time sot for every consecutive time unit between 0 and 1000 - extremely inefficient.
So just use
#(1000); // wait for 1000 relative time units