event control "#" in systemverilog in uvm defined AFTER assignments - system-verilog

I'm trying to understand the UVM driver code defined in a "verificationguide.com" UVM env example : https://www.edaplayground.com/x/5r89
In the mem_driver.sv file, in the drive() task, the following code can be found :
if(req.wr_en) begin // write operation
DRIV_IF.wr_en <= req.wr_en;
DRIV_IF.wdata <= req.wdata;
#(posedge vif.DRIVER.clk);
end
How is the event control #(posedge vif.DRIVER.clk); line written AFTER the events being controlled?
Help me understand the intent of the developer in this case?

It's there to make sure wr_en is held active for at least one clock cycle before the next requested transaction. Otherwise the next transaction would immediately set it back to 0.

Related

System verilog simulation performance for uvm_hdl_read vs assign statement

I need to generate around 10,000 connectivity assertions to check that values driven at DUT interface at the beginning of simulation has reached (and is retained) at around 10,000 points inside the DUT throughout the simulation.
The 10k destination points inside DUT are available through xls, hence, if I can take the paths to an array, I could write a single assertion property and instantiate it multiple times while iterating through the array.
What is the better approach here for simulation performance?
Using assign :
logic[7:0] dest_vals[10000];
assign dest_vals[0] = dut_path0.sig0; //This assignment code can be script-generated
assign dest_vals[1] = dut_path1.sig1;
....
assign dest_vals[9999] = dut_path9999.sig9999;
property check_val(expected_val, dut_val);
#(posedge assert_clk) expected_val == dut_val;
endproperty
genvar i;
generate
for(i=0;i<9999;i++)
assert property check_val(exp_val[i], dest_vals[i]);
endgenerate
Using uvm_hdl_read :
string dest_val_path[10000] = '{
"dut_path0.sig0",
"dut_path1.sig1",
....
"dut_path9999.sig9999"
};
property check_val(expected_val, dut_val);
#(posedge assert_clk) expected_val == dut_val;
endproperty
genvar i;
generate
for(i=0;i<9999;i++) begin
initial forever begin
#(posedge assert_clk);
uvm_hdl_read(dest_val_path[i],dest_vals[i]);
end
assert property check_val(exp_val[i], dest_vals[i]);
end
endgenerate
Follow up question: Is there a way to change static array of size 10k to dynamic array/associative array/queue, so that if 10k destination points changes to 11k in future, array need not be re-sized manually?
Using uvm_hdl_read to access a signal by a string name is significantly slower than a direct hierarchical reference to a signal in an assign statement. A lot of code gets executed to lookup up that signal name in a string database and retrieve it from the simulator's internal database using the SystemVerilog's C based "VPI" routines. Plus, you can lose some optimizations because the value of the signal needs to be readily available at any point in time. In the case of the continuous assign statement, the simulator can work backwards and figure out it only needs the signal value on the assert_clk trigger.
How noticeable this performance difference is depends on a number of other factors
The overall size of the entire design's activity relative to additional code executed by uvm_hdl_read
How often the assert_clk triggers relative to the other activity in the design
How good a job you were able to limit VPI access to just the signals involved. Every signal you give VPI access to increases the size of the string database and prevents optimizations around that signal.
There is another option that does not involve assign statements or generate loops. You can use the bind construct to insert a module with your assertion. This works particularly well in your situation since you already have a script that produces the code for you.
module #(type T=logic [7:0]) mcheck(input logic clk, T expected_val, dut_val);
property check_val;
#(posedge clk) expected_val == dut_val;
endproperty
assert property(check_val);
endmodule
bind dut_path0 mcheck m(tb_path.assert_clk,tb_path.expected_val[0], sig0);
bind dut_path1 mcheck m(tb_path.assert_clk,tb_path.expected_val[1], sig1);
...
Note the port connections in the bind module are relative to the dut_path where mcheck is bound into.

How to use System-Verilog Assertions on modules within top level DUT

I'm trying to write a memory scheduling testbench and to verify that I am accessing the correct addresses and writing the correct values at the right time I want to compare what is going on with the signals inside my top module with my schedule that I developed.
I've tried looking up "dot notation" and using my simulator (ModelSim) to access the signal (which I can do on the waveform fine) but I want to be able to use SVAs to check I have the correct values.
module top(input d, output q)
//wires
wire sub1_output
// Instantiate submodule
sub_module1 sub1(.sub1_output(sub1_output));
always begin
// logic
end
endmodule
module tb_top();
reg d;
wire q;
DUT top(.*);
initial begin
// This is where I want to access something within my DUT
assert(DUT.sub1_output == 1'b1)
end
endmodule
When I try this my code compiles and runs, but if I write the assertion such that it should fail and stop my code, nothing happens.
DUT.sub1_output
Is the correct format to use assertions on signals within top level instantiations.

System verilog:: Static Variable non-blocking Assignent outside program-block?

I'm new to system verilog and i'm stuck with a basic concept, kindly provide rationale behind the following behavior:
In System verilog, Why Static class properties declared in other than program-block scope cannot be assigned with blocking assignment from program block?
2.Why is that, even if static variable is assigned with non-blocking statement, the change in that static variable is no visible ($display) immediately, it is available after a delay of say #1.
Example:
class A ;
static int i;
endclass
program main ;
A obj;
initial
begin
obj.i = 123; // Not Allowed, can only be done using <= ... WHY ??
$display(obj.i);
#1 $display(obj.i);
end
endprogram
There is no such rule in the IEEE 1800-2012 LRM Earlier version of SystemVerilog had more restrictions on the types of assignments allowed, but those have all been removed. I do not recommend that anyone use program blocks anymore. There are a big source of unnecessary confusion. See http://go.mentor.com/programblocks
The purpose of "program" block in SystemVerilog is to guarantee that there will not be any race condition between the testbench and the DUT if the user encloses his testbench in program block(s) and keeps the DUT outside of program block(s). Another way to avoid race conditions is implemented by limiting DUT/testbench interaction to interfaces/clocking blocks. Also note that:
a) blocking assignments (since they are executed immediately and therefor the result of the execution can vary with the order of execution of threads) can lead to race conditions
b) hardware (RTL) variables can only be static
Given the whole scenario, the compiler makes out that the blocking statement could lead to a race condition between the DUT and the testbench. And hence the error.
When you use non-blocking assignment, the assignment is scheduled and not executed immediately. It would get executed once the scheduler gets a chance to execute it. And that would happen only after the present thread yields because of a blocking expression that involves time increment. In the given code snippet that happens once the executing thread encounters #1; the $display after #1 sees the result of the non-blocking assignment while the one before does not.

How can I create a task which drives an output across time without using globals?

I want to write some tasks in a package, and then import that package in some files that use those tasks.
One of these tasks toggles a reset signal. The task is reset_board and the code is as follows:
package tb_pkg;
task reset_board(output logic rst);
rst <= 1'b0;
#1;
rst <= 1'b1;
#1;
rst <= 1'b0;
#1;
endtask
endpackage
However, if I understand this correctly, outputs are only assigned at the end of execution, so in this case, the rst signal will just get set to 0 at the end of the task's execution, which is obviously not what I want.
If this task were declared locally in the module in which it is used, I could refer to the rst signal directly (since it is declared in the module). However, this would not allow me to put the task in a separate package. I could put the task in a file and then `include it in the module, but I'm trying to avoid the nasty complications that come with the way SystemVerilog handles includes (long-story-short, it doesn't work the way C does).
So, is there any way that the task can drive an output with different values across the duration of its execution without it having to refer to a global variable?
A quick solution is to use a ref that passes the task argument by reference instead of an output argument that is copied after returning from the task.
task reset_board(ref logic rst);
There are a few drawbacks of doing it this way. You can only pass variables of matching types by reference, so when you call reset_board(*signal*), signal cannot be a wire. Another problem is you cannot use an NBA <= to assign a variable passed by reference, you must use a blocking assignment =. This is because you are allowed to pass automatic variables by reference to a task, but automatic variable are not allowed to be assigned by NBAs. There is no way for the task to check the storage type of the argument passed to it.
Standard methodologies like the UVM recommend using virtual interfaces or abstract classes to create these kinds of connections from the testbench to the DUT. See my DVCon paper for more information.

Why does my sim with a clock never finish?

I added a clock generator to my module, and now the simulation never finishes.
always #10 clk = ~clk;
Why does the sim not finish after all initial code is done?
module test;
reg clk;
initial begin
clk = 0;
$display("Hello");
end
always #10 clk = ~clk;
endmodule
Sim results on EDA Playground: http://www.edaplayground.com/s/4/15
When you're using an always block, you need to have $finish statement in one of your initial blocks to finish the sim.
Fixed code:
module test;
reg clk;
initial begin
clk = 0;
$display("Hello");
$finish(); // <-- FIX
end
always #10 clk = ~clk;
endmodule
Sim results here: http://www.edaplayground.com/s/4/16
That said, if you also have a SystemVerilog program in your environment, the sim will automatically finish after all the initial code in your program blocks is done. From section 24.3 of IEEE 1800-2012 standard:
When all initial procedures within a program have reached their end,
that program shall immediately terminate all descendent threads of
initial procedures within that program. If there is at least one
initial procedure within at least one program block, the entire
simulation shall terminate by means of an implicit call to the $finish
system task immediately after all the threads and all their descendent
threads originating from all initial procedures within all programs
have ended.
There's no fundamental difference between initial and always: always is just initial forever. As long as anything is scheduled for future execution, then the simulation will continue (unless it is explicitly stopped); your statement continuously re-schedules the clock assignment, so the sim never stops. There really is nothing special about initial: it's not even guaranteed to run before any always blocks.
This wasn't an SV question (which I know nothing about), but I was interested to see VL's comment about automatically finishing the sim when the initial code is finished. This would break Verilog compatibility, and I'd be interested to see an LRM reference that confirms this.