SystemVerilog assertion scheduling - system-verilog

A SystemVerilog assertion is latching the previous values even though the transition is detected.
I have the below property as part of the file. a and b are inputs whose width is 1-bit.
property p_sig_detect();
#(a,b)
(a == 1) -> (b == 1);
endproperty
a_sig_detect : assert property(p_sig_detect());
In the above code, the assertion is behaving like below:
1. At 0 , a = 0, b = 0 (initial state) --> No effect
2. At t1, a = 0->1, b = 0 --> Passing
3. At t2, a = 1, b = 0->1 --> Throwing Error saying that "b" is 0
4. At t3, a = 1, b = 1->0 --> Passing
5. At t4&t5, a = 1->0->1, b = 0 --> Throwing Error once a becomes 1 at t5
Can someone please explain why it's latching the previous value even though the transition is detected?

The sampled values used in a synchronous assertion are the values in the preponed region before the clocking event.
You should be using immediate or deferred assertions that do not use clocks or sample values instead of concurrent assertions and properties that do.
module top;
bit a,b;
initial begin
#1 a = 1;
#1 b-= 1;
#1 a = 0;
#1 b = 0;
#1 $finish;
end
let p_sig_detect = (a == 1) -> (b == 1);
always_comb
a_sig_detect : assert (p_sig_detect()) $info("pass"); else $error("fail");
endmodule

Related

I don't understand this SV randomization randc behaviour

This is code for transaction class,
class transaction;
//declaring the transaction items
randc bit [3:0] a;
randc bit [3:0] b;
bit [6:0] c;
function void display(string name);
$display("-------------------------");
$display("- %s ",name);
$display("-------------------------");
$display("- a = %0d, b = %0d",a,b);
$display("- c = %0d",c);
$display("-------------------------");
endfunction
endclass
And this is code for generator class,
class generator;
rand transaction trans;
int repeat_count;
mailbox gen2driv;
event ended;
function new(mailbox gen2driv);
this.gen2driv = gen2driv;
endfunction
task main();
repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
trans.display("[ Generator ]");
gen2driv.put(trans);
end
-> ended;
endtask
endclass
The value of repeat_count that I passed is 10, and here's the output:
- a = 2, b = 0
- c = 0
- a = 1, b = 9
- c = 0
- a = 9, b = 9
- c = 0
- a = 7, b = 15
- c = 0
- a = 10, b = 15
- c = 0
- a = 3, b = 1
- c = 0
- a = 13, b = 12
- c = 0
- a = 1, b = 9
- c = 0
- a = 7, b = 5
- c = 0
- a = 3, b = 15
- c = 0
But, values during randomization are not showing cyclic repetition. It is repeating itself before all possible value can occur for variables a and b.
Move the transaction constructor out of the repeat loop. Change:
repeat(repeat_count) begin
trans = new();
to:
trans = new();
repeat(repeat_count) begin
It seems new resets the initial random permutation of the range values each time it is called.
Here is a self-contained, runnable example which demonstrates the issue (and the fix):
class transaction;
randc bit [3:0] a; // 16 values: 0-15
function void display;
$display("a=%0d", a);
endfunction
endclass
class generator;
rand transaction trans;
task main;
//trans = new(); // Un-comment this line for fix
repeat (16) begin
trans = new(); // Comment out this line for fix
if (!trans.randomize()) $fatal(1, "trans randomization failed");
trans.display();
end
endtask
endclass
module tb;
generator gen = new();
initial gen.main();
endmodule
Yeah exactly to what #toolic said...initializing with new operator inside the repeat loop would create a new space every time the loop runs and thus the newly created object (transaction in your case) has no trace of the values that has been previously exercised. Thus giving out a random number making randc of no use. It though works normal with ' rand' keyword.

SystemVerilog error in multiplexing channels : nonconstant index into instance array

I'm designing a module that accepts multiple channels and outputs one channel.
Each channel consists of valid signal and data of some widths.
If a channel has valid data, the module should output that channel. If multiple channels have valid data, the module should output one of them (in my case, channel with highest index) and rests are dropped.
My simple implementation looks like this:
module test1 #(
parameter NUM_CHANNEL = 8,
parameter DATA_WIDTH = 512
) (
input logic [DATA_WIDTH - 1 : 0] data_in [NUM_CHANNEL],
input logic valid_in [NUM_CHANNEL],
output logic [DATA_WIDTH - 1 : 0] data_out,
output logic valid_out
);
always_comb begin
valid_out = 0;
for (int i = 0; i < NUM_CHANNEL; ++i) begin
if (valid_in[i]) begin
valid_out = 1;
data_out = data_in[i];
end
end
end
endmodule
This works perfectly in both simulation and real circuit (FPGA).
However, channel can be complex type so I used interface like this:
interface channel #(
parameter DATA_WIDTH = 512
);
logic valid;
logic [DATA_WIDTH - 1 : 0] data;
modport in (
input valid,
input data
);
modport out (
output valid,
output data
);
endinterface // sub_csr_if
module test #(
parameter NUM_CHANNEL = 8,
parameter DATA_WIDTH = 512
) (
channel.in in[NUM_CHANNEL],
channel.out out
);
always_comb begin
out.valid = 0;
for (int i = 0; i < NUM_CHANNEL; ++i) begin
if (in[i].valid) begin
out.valid = 1;
out.data = in[i].data;
end
end
end
endmodule
Then, this code gets Nonconstant index into instance array 'sub_port'. error in ModelSim, and i is not a constant error in Quartus.
If I unroll the loop, it works but it becomes non-parametric code. (only works for fixed NUM_CHANNEL)
Why the latter one does not work, while the first one works flawlessly?
An array of instances (module or interface) is not a true array type. As your error message indicates, you cannot select a particular instance with a variable index. With a true array, every element is identical. Because of the way parameterization, defparam, and port connections work, each instance element could have differences. The elaboration process essentially flattens all hierarchy before simulation begins.
What you can do is use a generate construct to select your instance as follows
;
module test #(
parameter NUM_CHANNEL = 8,
parameter DATA_WIDTH = 512
) (
channel.in in[NUM_CHANNEL],
channel.out out
);
logic _valid[NUM_CHANNEL];
logic [DATA_WIDTH - 1 : 0] _data[NUM_CHANNEL];
for (genvar ii=0;ii<NUM_CHANNEL;ii++) begin
assign _valid[ii] = in[ii].valid;
assign _data[ii] = in[ii].data;
end
always_comb begin
out.valid = 0;
for (int i = 0; i < NUM_CHANNEL; ++i) begin
if (_valid[i]) begin
out.valid = 1;
out.data = _data[i];
end
end
end
endmodule

How to cover a fifo rd/wt property?

I am trying to write a fifo rd write cover point.
module M;
bit stop; bit clk; initial while (!stop) #5 clk = ~clk;
bit A, B, rst;
initial rst = 0;
initial begin
A = 0;
#20 A = 1;
#10 A = 0;
// #10 B = 1;
#10 B = 0;
#50 stop = 1;
end
// sequence fifo_rd_wt_s(reg sig);
// ((|A === 1) |-> s_eventually (|B === 1));
// endsequence: fifo_rd_wt_s
property fifo_rd_wt_p(reg sig_clk, reg sig_rst);
#(posedge sig_clk) disable iff(sig_rst)
((|A === 1) |-> s_eventually (|B === 1));
endproperty: fifo_rd_wt_p
cover_fifo_read_write: cover property(fifo_rd_wt_p(clk, rst)) $error($sformatf("%0t hit fifo read write", $time));
// else $error($sformatf("%0t did not hit", $time));
final
$display("Finished!");
endmodule: M
In the run log I see that it is getting triggered every cycle, but that is not what I want. I want it to trigger every time it sees a A followed by a B.
Not sure what I am missing.
I found something similar here
The code is present in code
I think your issue is with the implication. I used your example and replaced with strong((|A === 1) ##[1:$] (|B === 1)); it was working fine.
Cover with implication can have some unexpected behavior (in your case it was covering the antecedent), it s always safer to use cover with sequences

How to cover latency between request and response

Let's say we have a protocol where request req is asserted with req_id and corresponding rsp will be asserted with rsp_id. These can be out of order. I want to cover the number of clks or latency between req with particular req_id and rsp with the same id. I tried something like this. Is this correct way of doing? Is there any other efficient way?
covergroup cg with function sample(int a);
coverpoint a {
a1: bins short_latency = {[0:10]};
a2: bins med_latency = {[11:100]};
a3: bins long_latency = {[101:1000]};
}
endgroup
// Somewhere in code
cg cg_inst = new();
sequence s;
int lat;
int id;
#(posedge clk) disable iff (~rst)
(req, id = req_id, lat = 0) |-> ##[1:$] ((1'b1, lat++) and (rsp && rsp_id == id, cg_inst.sample(lat)));
endsequence
You're trying to use the |-> operator inside a sequence, which is only allowed inside a property.
If rsp can only come one cycle after req, then this code should work:
property trans;
int lat, id;
(req, id = req_id, lat = 0) |=> (1, lat++) [*0:$] ##1 rsp && rsp_id == id
##0 (1, $display("lat = %0d", lat));
endproperty
The element after ##0 is there for debugging. You can omit it in production code.
I wouldn't mix assertions and coverage like this, though, as I've seen that the implication operators can cause issues with variable flow (i.e. lat won't get updated properly). You should have a property that just covers that you've seen a matching response after a request:
property cov_trans;
int lat, id;
(req, id = req_id, lat = 0) ##1 (1, lat++) [*0:$] ##1 rsp && rsp_id == id
##0 (1, $display("cov_lat = %0d", lat));
endproperty
cover property (cov_trans);
Notice that I've used ##1 to separate the request from the response.
Basically your idea is right , But looks like the right hand side of the sequence will be evaluated once when the condition is true and hence the lat will be incremented only once .
You will need a loop mechanism to count the latency.
Below is an sample working example. You can change [1:$], ##1 etc based on how close the signals are generated
property ps;
int lat;
int id;
#(posedge clk)
disable iff (~rst)
(req, id = req_id, lat = 0) |=> (1'b1, lat++)[*1:$] ##1 (rsp && rsp_id == id, cg_inst.sample(lat));
endproperty
assert property (ps);
Alternatively...
property/sequences though they appear to be small code , in this case for every req ( which has not yet received a rsp ) a seperate process with its own counter is forked. This results in many counters doing very similar work. In case there are many req in flight ( and/or many instances of the property or sequence ) it will start adding into simulation run-time [ even though this is just a small block of code ]
so another approach is to keep the trigger simpler and we try to keep the processing linear.
int counter=0; // you can use a larger variablesize to avoid the roll-over issue
int arr1[int] ; // can use array[MAX_SIZE] if you know the max request id is small
always #( posedge clk ) counter <= counter + 1 ; // simple counter
function int latency (int type_set_get , int a ) ;
if ( type_set_get == 0 ) arr1[a] = counter; // set
//DEBUG $display(" req id %d latency %d",a,counter-arr1[a]);
// for roll-over - if ( arr1[a] > counter ) return ( MAX_VAL_SIZE - arr1[a] + counter ) ;
return (counter - arr1[a]); //return the difference between captured clock and current clock .
endfunction
property ps();
#(posedge clk)
disable iff (~rst)
##[0:$]( (req,latency(0,req_id) ) or (rsp,cg_inst.sample(latency(1,rsp_id))) );
endproperty
assert property (ps);
The above property is triggered only when req/rsp is seen and only 1 thread is active looking for it.
If needed extra checks can be added into the function , But for latency counting this should be fine.
Anecdote :
Mentor AE - Dan discovered an assertion which was slowing our simulations by as much as 40 % . The poorly written assertion was part of our block tb and its effects went unnoticed there , as our block level test, run times were limited. It then sneaked into our top-level tb causing untold runtime losses till it was discovered a year later :) . [ guess we should have profiled our simulation runs earlier ]
Say for example if the above protocol implemented an abort at a later time, then the req-rsp thread will continue to process and wait ( till the simulation ends) for an aborted transaction , though it will not affect the functionality , it will sneakily continue to hog processor resources doing nothing useful in return. Till finally an vendor AE steps in to save the day :)

I got error message about simulink "Output argument is not assigned on some execution paths"

In simulink, I made some model using "MATLAB function"block
but I met error message here.
here is code and error message.
function [VTAS,postVTAS]=fcn(mode,initialVTAS,a,t,preVTAS)
if mode == 1
VTAS = initialVTAS + (a * t) ;
postVTAS = VTAS;
elseif mode == 2
datasize = length(preVTAS);
lastvalue = preVTAS(datasize);
VTAS = lastvalue + 0;
postVTAS = VTAS;
end
end
Output argument 'VTAS' is not assigned on some execution paths.
Function 'MATLAB Function' (#36.25.28), line 1, column 26:
"fcn"
Launch diagnostic report.
I think there is no problem about output "VTAS"
please teach me what is a problems.
As the compiler tells you, under some circumstances there is no output value assigned to VTAS. The reason is that you only assign values to that output if mode is 1 or 2. The compiler doesn't know what values are feasible for mode. To remedy this, you need to make sure that VTAS is assigned under any and all circumstances.
This could be accomplished by, e.g. adding an else construct, like so:
function [VTAS,postVTAS]=fcn(mode,initialVTAS,a,t,preVTAS)
if mode == 1
VTAS = initialVTAS + (a * t) ;
postVTAS = VTAS;
elseif mode == 2
datasize = length(preVTAS);
lastvalue = preVTAS(datasize);
VTAS = lastvalue + 0;
postVTAS = VTAS;
else
VTAS = NaN;
postVTAS = NaN;
end
end
Edit:
Additionally, it would be good practice for the else case to throw an error. This would be helpful for debugging.
As a minor note, for every case, postVTAS is equal to VTAS, so essentially it is superfluous to return both from the function.