SystemVerilog Assertion Error : Illegal use of non-constant expression - system-verilog

SLV_DCR_TIMEOUT_WAIT is the value programmed in the register hence it is not a constant value. How can I use the same in assertion.
assign DCR_CLK = testbench.sw_top_inst.DUT.megatron_x.megatron_cib.dcr_slave_cfg.DCR_clk;
assign DCR_TIMEOUT_WAIT = testbench.sw_top_inst.DUT.megatron_x.megatron_cib.dcr_slave_cfg.dcr_timeout_wait[15:0];
assign SLV_DCR_TIMEOUT_WAIT = testbench.sw_top_inst.DUT.megatron_x.megatron_cib.dcr_slave_cfg.Sl_dcrTimeoutWait;
assign SCRUB_INIT = testbench.sw_top_inst.DUT.megatron_x.megatron_cib.dcr_slave_cfg.scrub_init;
// end
//=================ASSERTION TO CHECK SLV_DCR_TIMEOUT_WAIT============================//
property slv_dcr_timeout_wait;
#(posedge DCR_CLK) disable iff (!DCR_TIMEOUT_WAIT)
$rose(SCRUB_INIT) |-> $rose(SLV_DCR_TIMEOUT_WAIT) ##(DCR_TIMEOUT_WAIT) $fell(SLV_DCR_TIMEOUT_WAIT);
endproperty: slv_dcr_timeout_wait
assert property (slv_dcr_timeout_wait);
Error message :
Error-[SVA-INCE] Illegal use of non-constant expression
/lsi/designs/rsd_megatron/team/singhs/megatron/sim/testbench/mss_tb/interfaces/mss_internal_signal_if.sv, 41
mss_internal_signal_if, "DCR_TIMEOUT_WAIT"
The use of a non-constant expression is not allowed in properties, sequences
and assertions for cases such as delay and repetition ranges.
Please replace the offending expression by an elaboration-time constant.

As you noticed, the issue is with ##(DCR_TIMEOUT_WAIT), SystemVerilog want it to be a constant value. I use local variable counters to get around this. See IEEE Std 1800-2012 § 16.10 Local variables
Using a local variables create a generic pulse checking sequences:
sequence pulse_seq(local logic sig, local int limit);
int cnt = limit;
$rose(sig) ##0 (sig && cnt>0, cnt--)[*] ##1 ($fell(sig) && cnt==0);
endsequence
Then plug it into the assertion:
property slv_dcr_timeout_wait;
#(posedge DCR_CLK)
$rose(SCRUB_INIT) && (DCR_TIMEOUT_WAIT>0) |->
pulse_seq(SLV_DCR_TIMEOUT_WAIT,DCR_TIMEOUT_WAIT);
endproperty : slv_dcr_timeout_wait
assert property (slv_dcr_timeout_wait);
Note that I removed disable iff (!DCR_TIMEOUT_WAIT)and to the trigger I changed the trigger condition to $rose(SCRUB_INIT) && (DCR_TIMEOUT_WAIT>0). Disable is asynchronous action and should be used to abort a sequence that already started.
Proof of concept here (Note: Riviera-PRO EDU 2014.10 does not appear to properly support sequence port lists to I had to hard coded the signals. Should work in any full simulator that supports IEEE Std 1800-2005 or greater)

Related

variable delay in assertions in System Verilog

I am a newbie learning System Verilog assertions and found a code online from verificationguide.com for variable delays in assertions. But I am unable to understand a few things.
Can someone elaborate on these following given descriptions?
// Copy variable value to the local variable.
(1,delay=v_delay)
How does this data gets copied?
// Decrements the value of the local variable and checks for the value of ‘delay’ equals ‘0’.
(1,delay=delay-1) [*0:$] ##0 delay <= 0
What does *0 mean? I know $ is for infinite checking till the end of the simulation. And why is ##0 needed as it just means 0 delay, if I am not wrong?
// waits for value of ‘delay’ equals to ‘0’
first_match((1,delay=delay-1) [*0:$] ##0 delay <=0)
How does the first_match function works and whats the syntax of it?
Please find the code below:
//-------------------------------------------------------------------------
// www.verificationguide.com
//-------------------------------------------------------------------------
module asertion_variable_delay;
bit clk,a,b;
int cfg_delay;
always #5 clk = ~clk; //clock generation
//generating 'a'
initial begin
cfg_delay = 4;
a=1; b = 0;
#15 a=0; b = 1;
#10 a=1;
#10 a=0; b = 1;
#10 a=1; b = 0;
#10;
$finish;
end
//delay sequence
sequence delay_seq(v_delay);
int delay;
(1,delay=v_delay) ##0 first_match((1,delay=delay-1) [*0:$] ##0 delay <=0);
endsequence
//calling assert property
a_1: assert property(#(posedge clk) a |-> delay_seq(cfg_delay) |-> b);
//wave dump
//initial begin
// $dumpfile("dump.vcd"); $dumpvars;
//end
endmodule
1) The sequence delay_seq has a variable cfg_delay which is passed from the property. That is actually assigned to v_delay, which is in turn assigned to the local variable delay.
2) *0 is called an empty match. For example a[*0:$] |-> b means
a [*0] or a [*1] or a [*2] .. a [$]
3) For example ($rose(b) ## [3:4] b) has two possible matches and ($rose(b) ## [3:$] b) can have infinite number of matches. To avoid unexpected behaviors because of multiple matches or cause an assertion to never succeed because all threads of antecedent must be tested for property to succeed. The first_match operator matches only the first of possibly multiple matches for an evaluation attempt and causes all the subsequent matches to be discarded.

System verilog bind assertion sequence with variable

I have to write system verilog assertion with binding.
The assertion should be something like:
assert property (#(posedge (mod_clk & clk_gen_enable)) ##delay (clk_sync == 1));
mod_clk, clk_gen_en and clk_sync are signal of the module which I bind to, and delay is a variable which is a result of a calculation and should be in "units" of st_clk which is also signal of the module which I bind t
How can I write it correctly??
For bind to work, all you need is encapsulate this assertion in a module
module my_module;
assert property (#(posedge (mod_clk & clk_gen_enable)) ##delay (clk_sync == 1));
endmodule
The use the bind construct to insert my_module into your target module. Verilog search rules will search upwards to find your signal names.
bind my_target_module my_module my_instance_name();
##delay works on the clock edge you specified before it which you have as mod_clk & clk_gen_enable. I'm guessing you really mean to have clk_gen_enable as an implication.
property sync;
int counter;
#(posedge mod_clk) (clk_gen_enable, counter=delay)
|-> #(posedge st_clk) (1,counter--)[*0:$] ##1 counter<0 ##0 (clk_sync == 1));
endproperty

$past with an input signal

I want to verify that if an event occurrs, then at “num_ticks” in the past, some signal should have been asserted.
As an example, the property I wrote is:
property test_past;
#(posedge clk)
$rose(gnt) |-> $past(req, num_ticks);
endproperty
The problem here is with num_ticks. If num_ticks is an input signal to the module in which the property is written, then the assertion fails. If I declare num_ticks as an int, and assign it to a constant, it passes.
Does $past only work for constant values? This is not mentioned in the LRM.
I am using Questasim 10.3
You might use multiple assertions for this purpose.
Suppose num_ticks is 4 bits wide, then you can do like this.
genvar x;
generate
for (x=0; x<16; x++)
begin
property test_past;
#(posedge clk)
(num_ticks == x) && $rose(gnt) |-> $past(req, x);
endproperty
end
endgenerate

Avoiding support code for SVA sequence to handle pipelined transaction

Let's say we have a protocol that says the following. Once the master sets req to fill, the slave will signal 4 transfers via rsp:
An SVA sequence for this entire transaction would be (assuming that the slave can insert idle cycles between the trans cycles):
req == fill ##1 (trans [->1]) [*4];
Now, assume that the master is allowed to pipeline requests. This would mean that the next fill is allowed to start before the 4 trans cycles are done:
The SVA sequence from above won't help, because for the second fill it's going to wrongly match 4 trans cycles, leaving the last trans "floating". It would need to start matching trans cycles only after the ones for the previous fill have been matched.
The sequence needs global information not available in a single evaluation. Basically it needs to know that another instance of it is running. The only way I can think of implementing this is using some RTL support code:
int num_trans_seen;
bit trans_ongoing;
bit trans_done;
bit trans_queued;
always #(posedge clk or negedge rst_n)
if (!rst_n) begin
num_trans_seen;
trans_ongoing <= 0;
trans_done <= 0;
trans_queued <= 0;
end
else begin
if (trans_ongoing)
if (num_trans_seen == 3 && req == trans) begin
trans_done <= 1;
if (req == fill || trans_queued)
trans_queued <= 0;
else
trans_ongoing <= 0;
num_trans_seen == 0;
end
else
if (trans_queued) begin
trans_queued <= 0;
trans_ongoing <= 1;
end
if (trans_done)
trans_done <= 0;
end
The code above should raise the trans_ongoing bit while a transaction is ongoing and pulse trans_done in the clock cycle when the last trans for a fill is sent. (I say should because I didn't test it, but this isn't the point. Let's assume that it works.)
Having something like this, one could rewrite the sequence to be:
req == fill ##0 (trans_ongoing ##0 trans_done [->1]) [*0:1]
##1 (trans [->1]) [*4];
This should work, but I'm not particularly thrilled about the fact that I need the support code. There is a lot of redundancy in it, because I basically re-described a good chunk of what a transaction is and how pipelining works. It's also not as easily reusable. A sequence can be placed in a package and imported somewhere else. The support code can only be placed in some module and reused, but it's a different logical entity than the package that would store the sequence.
The question here is: is there any way to write the pipelined version of the sequence while avoiding the need for support code?
It looks like rsp is always idle before the trans starts. If rsp's idle is a constant value and it is a value that trans will never be, then you could use:
req == fill ##0 (rsp==idle)[->1] ##1 trans[*4];
The above should work when the pipeline supports 1 to 3 stages.
For a 4+ deep pipeline, I think you need some auxiliary code. The success/fail blocks of the assertion can be used to incompetent the count of completed trans; this saves you from having write additional RTL. A local variable in the property can be used to sample the fill's count value. The sampled value will be used as a criteria to start sampling the expected trans pattern.
int fill_req;
int trans_rsp;
always #(posedge clk, negedge rst_n) begin
if(!rst_n) begin
fill_req <= '0;
trans_rsp <= '0;
end
else begin
if(req == fill) begin
fill_req <= fill_req + 1; // Non-blocking to prevent risk of race condition
end
end
end
property fill_trans();
int id;
#(posedge clk) disable iff(!rst_n)
(req == fill, id = fill_req) |-> (rsp==idle && id==trans_rsp)[->1] ##1 trans[*4];
endproperty
assert property (fill_trans()) begin
// SUCCESS
trans_rsp <= trans_rsp + 1; // Non-blocking to prevent risk of race condition
end
else begin
// FAIL
// trans_rsp <= trans_rsp + 1; // Optional for supporting pass after fail
$error("...");
end
FYI: I haven't had time to fully test this. It should at least get you in the right direction.
I experimented a bit more and found a solution that might be more to your liking; no support code.
The equivalent of trans[->4] is (!trans[*] ##1 trans)[*4] per IEEE Std 1800-2012 § 16.9.2 Repetition in sequences. Therefore we can use the local variables to detect new fill requests with the expanded form. For example the following sequence
sequence fill_trans;
int cnt; // local variable
#(posedge clk)
(req==FILL,cnt=4) ##1 ( // initial request set to 4
(rsp!=TRANS,cnt+=4*(req==FILL))[*] // add 4 if new request
##1 (rsp==TRANS,cnt+=4*(req==FILL)-1) // add 4 if new request, always minus 1
)[*] ##1 (cnt==0); // sequence ends when cnt is zero
endsequence
Unless there is another qualifier not mentioned, you cannot use a typical assert property(); because it will start new assertion threads each time there is a fill request. Instead use an expect statement, which allows waiting on property evaluations (IEEE Std 1800-2012 § 16.17 Expect statement).
always #(posedge clk) begin
if(req==FILL) begin
expect(fill_trans);
end
end
I tried recreating your describe behavior for testing https://www.edaplayground.com/x/5QLs
One possible solution can be achieved with 2 assertions as below.
For 1st image -
(req == fill) && (rsp == idle) |=> ((rsp == trans)[->1])[*4]
For 2nd image -
(req == fill) && (rsp == trans) |=> ((rsp == trans)[->1])[*0:4] ##1 (rsp == idle) ##1 ((rsp == trans)[->1])[*4]
One issue is that if there are continuous "fill" requests on each cycle (consecutive 4 "fill" requests, without any intermediate "idle"), then the 2nd assertion will not calculate "trans" cycles for each "fill" requests (instead it'll only be completed on the 2nd set of "trans" cycles itself).
I could not modify the assertion for the given bug, as of now.

system verilog assertion disable condition

I have this assertion in order to check clk freq:
assert property clk_freq;
int cnt;
#(posedge fast_clk, clk_1MHz) disable_iff(!enable_check)
($rose(clk_1MHz), cnt=0) |=> (!$rose(clk_1MHz),cnt++) [*0:$] ##1 $rose(clk_1MHz), cnt==fast_clk_freq;
endproperty
fast_clk starts to toggle during (not from beginning) of the simulation after disable_check is asserted.
The problem is that it seems that the assertion ignores the disable_iff
Question: is a $rose(clk_1Mhz) event "registered" even though the assertion is disabled (or am I missing something else ?)
There is no disable_iff keywords, it is disable iff (without the underscore). Properties can have local variables but the local variables cannot be defined inline with assert. Separate the property definition and the assertion instantiation.
The clock sampling doesn't seem to be correct. #(posedge fast_clk, clk_1MHz) mean on rising fast_clk or any change to clk_1MHz. clk_1MHz is the sampled data value, therefore it should not be a clock.
$rose(clk_1MHz), cnt==fast_clk_freq is ilegal syntax, sugest: $rose(clk_1MHz) ##0 cnt==fast_clk_freq
Suggested property definition and the assertion instantiation:
property p_clk_freq;
int cnt;
#(posedge fast_clk) disable iff(!enable_check)
($rose(clk_1MHz), cnt=0) |=> (!$rose(clk_1MHz),cnt++)[*0:$] ##1 $rose(clk_1MHz) ##0 cnt==fast_clk_freq;
endproperty
a_clk_freq : assert property(p_clk_freq);
For more on assertions refer to section 16 of IEEE Std 1800-2012.