Unexpected behavior with force statement inside interface task - system-verilog

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

Related

System Verilog: randomization per instance at initial

I want to simulate a multiple latches with random starting conditions, but I want each instance to have its own initial condition
This is a simplified version of the code. I would like the value to be different in both of the instances, without changing the interface
module random_usage();
integer addr1;
real data;
initial begin
addr1 = $urandom();
data = $urandom();
$display("addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
initial
begin
#5;
seed = $get_initial_random_seed();
$display("seed=%0d", seed);
end
endmodule
What I have seen so far:
Instance specific $urandom in system-verilog
solution doesn't work in initial condition, or even when you feed the same clock
https://www.systemverilog.io/randomization
I have modules, so i don't know how to apply the solutions, or even if it will work here
https://www.reddit.com/r/FPGA/comments/jd0dmu/system_verilog_force_randomization_different_per/
seems to be the same question, and there is no straight solution, but the last person gave a VCS flag. I am using VCS, but i have not been able to get the flag to work
The IEEE 1800-2017 SystemVerilog LRM section 18.14.1 Random stability properties says rather naively that each instances gets seeded with the same initialization seed.
Most tools now have a switch changing that behavior by using the hierarchical path name to seed each instance. Some tools have even made that the default. If you want tool independent behavior, you can use this package:
package seed_instance;
int initial_seed = $urandom;
function automatic void srandom(string path);
static int hash[int];
int hash_value = initial_seed;
process p = process::self();
for(int i=0;i<path.len();i++)
hash_value+=path[i]*(i*7);
if (!hash.exists(hash_value))
hash[hash_value] = hash_value;
else
hash[hash_value]+=$urandom; // next seed
p.srandom(hash[hash_value]);
endfunction
endpackage
module random_usage();
integer addr1;
real data;
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("1: addr1=%0d, data=%0d",addr1,data);
end
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("2: addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
endmodule

How to prevent new threads of SVA

Lets assume, I have a button in my design. I want to increment counter between next two clock when button has been pressed three times and I want to check this behaviour with SVA.
I have wrote this one:
`timescale 1ns / 1ps
module tb();
parameter NUMBER_OF_PRESSES = 10;
parameter CLK_SEMI_PERIOD = 5;
bit clk;
always #CLK_SEMI_PERIOD clk = ~clk;
bit button_n;
bit reset_n;
logic [7:0] counter;
property p;
logic[7:0] val;
disable iff(!reset_n) #(posedge clk) (($fell(button_n)[=3]),val=counter) |=> ##[0:2] (counter== val+1);
endproperty
assert property(p);
initial begin
automatic bit key_d;
automatic byte key_lat;
automatic byte key_press_count;
reset_n = 1;
button_n = 1;
counter = 0;
fork
begin
repeat(NUMBER_OF_PRESSES) begin
repeat(5)begin
#(negedge clk);
end
button_n = 0;
key_lat = $urandom_range(1,4);
repeat(key_lat) begin
#(negedge clk);
end
button_n = 1;
end
end
begin
forever begin
#(posedge clk);
if(!button_n && key_d) begin
key_press_count++;
end
if(key_press_count == 3) begin
counter++;
key_press_count = 0;
end
key_d = button_n;
end
end
join_any
end
endmodule
This works good at first three press, but then it will always throw assertion error, because it has been started new thread of assertion at each button press. So, I need to prevent testbench from doing this. When repetitition has been started I don't need to start new threads.
How can I do this?
I am not confident I fully understand your question. Let me first state my understanding and where I think your problem is. Apologise if I am mistaken.
You intend to detect negedges on button_n ("presses"), and on the third one, you increment "counter".
The problem here is that your stated objective (which actually matches the SVA) and your design do different things.
Your SVA will check that the counter has the expected value 1-3 cycles after every third negedge. This holds for press 0, 1 and 2. But it must also hold for press 1, 2 and 3. And press 2, 3 and 4 etc. I suspect the assertion passes on press 2 and then fails on press 3. I.e. you check that you increment your counter on every press after the third.
Your design, on the other hand does something different. It counts 3 negedges, increments counter, and it then starts counting from scratch.
I would advise against the use of local variables in assertions unless you are certain that it is what you need - I don't think this is the case here. You can have your SVA trigger on key_press_count == 3 (assuming you ofc define key_press_count appropriately and not as an automatic var).
If you insist on using your local SVA variable you can slightly modify your trigger condition to include counter. For example something along the lines of (though may be slightly wrong, have not tested):
(counter == 0 || $changed(counter)) ##1 ($fell(button_n)[=3], val = counter)
IMO that's a bad idea and having supporting RTL is the better way to go here to document your intention as well as check exactly the behaviour you are after.
Hope this helps

Way to have a function like urandom_range(); which will return unique values?

I want to have a urandom_range(); which will not repeat a value once its picked in a simulation ? If it has exhausted its supply of 'available' numbers, then perhaps it can repeat .
Is there any keyword in systemverilog which will help quickly to get around this ?
Not a SV expert here so an example would really help! Thanks
randc does exactly this. (cyclic randomization)
class A;
randc bit[7:0] m;
endclass
Each time you call randomize() on the same object, it will not repeat value for m until all possible values have been given.
Simulators have limits on how large the cyclic value can be, but the standard requires a minimum of 8-bits. If you have a larger value, then you can use the inside operator.
class A;
rand bit[23:0] r;
bit [23:0] list[$];
constraint c { !(r inside {list}); }
function void post_randomize();
list.push_back(r);
endfunction
endclass
If you really expect to cycle through the list, it might be simpler to build the list first, and then shuffle through the list.
bit [7:0] list[20];
for(int i=0;i<20;i++) list[i] = i+10; // range 10-29
list.shuffle();
// cycle through list[0] ... list[29]
list.shuffle();
// cycle through list[0] ... list[29]
You can declare a variable with randc identifier. This is called 'cyclical random' and will ensure exactly what you are requiring.
Note: This requires a license that supports randomization and random variables. Most commercial simulators do provide this but at a higher cost. If you are constrained by this and need to only use the system calls - $urandom or $urandom_range, I would implement something like a queue that tracks all the values returned.
function automatic void find_unique_num();
int c;
int vals[$];
bit found;
do begin
c = $urandom_range(10, 1);
foreach(vals[i])
if (c == vals[i]) found = 1;
end
while (!found);
vals.push_back(c);
return c
endfunction

What are the common and good usage of pre_randomize() and post_randomize() in systemverilog?

How can I change/add constraint or constraint_mode in pre_randomize()?
I know I can overwrite results in post_randomize and I can call rand_mode on and off in pre_randomize, but I am looking for some additional functionality especially related to constraints.
pre_randomize is generally used to set some pre-conditions before object randomization. Here one can print the results of previous randomization, set some variables on which the constraints are dependent etc.
As you mentioned, pre_randomize can be used to set rand_mode(0) for any variable. It can be used to manipulate constraints as well.
post_randomize is used to manipulate some variables like ECC check, print randomization result, manipulate some non-random fields based on existing randomization etc.
One another usage of post_randomize is to generate 'x' or 'z' in randomization process. The randomization by default generates 0 and 1 known values only. But one can use the existing randomized variable to generate x/z values also.
Here is a dummy example of what we can do in pre_randomize and post_randomize functions. Here, depending on non_rand_var, we can enable/disable the constraint mode and set the rand mode of any variable. In the post_randomize function, one can overwrite 'my_x' variable byt 'x' or 'z'.
class A;
int non_rand_var;
rand int rand_var;
rand int rand_var2;
rand logic my_x;
constraint c1{non_rand_var==1 -> rand_var=='h5;}
function new(int non_rand_var);
this.non_rand_var = non_rand_var; // set non random variable
endfunction
function void pre_randomize();
if(non_rand_var==5) begin // set randomization mode of rand_var2
rand_var2.rand_mode(0);
c1.constraint_mode(0); // disable constraint
end
$display("In pre randomize, non_rand_var=0x%0x rand_var=0x%0x",non_rand_var, rand_var);
endfunction
function void post_randomize();
// my_x = $urandom_range(0,1) ? 0 : 'x;
my_x = (non_rand_var==1) ? 0 : 'x; // Manipulate my_x to generate 'x' values
$display("In post randomize, rand_var=0x%0x",rand_var);
endfunction
endclass
module top();
A a=new(1);
initial begin
a.randomize();
$display("Initial block:\na.my_x = 0x%0x\na.rand_var=0x%0x\na.non_rand_var=0x%0x\na.rand_var2=0x%0x",a.my_x,a.rand_var,a.non_rand_var,a.rand_var2);
end
endmodule
pre_randomize & post_randomize function can have several uses based on the application.
Here is the list of few usage of those functions.
Both functions can be overridden, and hence it can be possible to modify the randomization behavior with extended class
Turn on/off few random variables
Turn on/off few constraints
Assignments to other nonrandom variable, on which the randomization is dependent
Changing the weight of random variables, based on certain conditions
Typical usage of pre_randomization function is to generate an array of unique values.
class helper;
randc bit [7:0] a;
endclass
class original;
bit [7:0] unique[64];
function void pre_randomize();
helper h = new();
foreach (unique[i])
begin
void'(h.randomize());
unique[i] = h.a;
end
endfunction
endclass

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