Property to count the number of pulse

I want to check the number of pulse with SVA. Then, I wrote the following property.

            ____________________     __________
 a     : __|^                                  |__ $rose(a) ##[1:$] $fell(a)
            __       __       __        __      *
 b     :      |__      |__      |__       |__      $fell(b)[=1:$]
 v_cnt :       1        2        3         num  num
            ^...................................*
    property p_pulse_cnt (N);
        int v_cnt = 0;
        ($rose(a), v_cnt=0) ##1 first_match( ($fell(b), v_cnt++)[=1:$] ##1 $fell(a) )       |=> (v_cnt == num);
    endproperty

I examined this property with the following test case.

N = 3       ___________________________________
 a     : __|                                   |__
            __    __          ____
 b     : __|  |__|  |________|    |_______________
 v_cnt :       1     2             3            3
            ^...................................*

However, the result is different depending on simulator.
Questasim reports FAIL of the property. One simulator successfully reports PASS. Another simulator spits the following compile error message.

*E,RPTBOL (testbench.sv,26|46): Goto repeat "[->" and non-consecutive repeat "[=" operators take boolean expressions only.

I’m confused.
Why each simulator behaves differently?
Isn’t non-consecutive repeat of sequence allowed?
How should I write a property to check the number of pulse in that case?

Here is a link to EDA Playground to run the test.
EDA Playground : SVA Pulse Number Count

Use EDA Playground

 property p_pulse_cnt(N);
    int v_cnt;
    ($rose(a), v_cnt=0) |-> ($fell(b)[->1],v_cnt++, dbg(v_cnt))[*1:$] intersect $fell(a)[->1];
  endproperty

Thanks @hdlcohen ,

But that property is different from what I want to check. What I want to check is the number of Non-Continuous pulse as shown above.
[*1:$] is continuous repetition while [=1:$] is non-continuous repetition. And then, v_cnt == N is necessary at the end of the sequence to check the counted pulse number at least.

 ($fell(b)[->1],v_cnt++, dbg(v_cnt))[*1:$] intersect $fell(a)[->1];

You don’t need the [=1:$] because the the goto in $fell(b)[->1] is equivalent to
! fell(b)[*0:] ##1 $fell(b), and that sequence is repeast indefinitely, until it intersects the envelope.

@hdlcohen ,
Thank you for your explanation. I got your point.
Usage of go-to repetition of 1([->1]) with infinete continuous repetition [*1:$]instead of infinite non-continuous repetition ([=1:$]) usage.

N = 3       ___________________________________
 a     : __|                                   |__
            __    __          ____
 b     : __|  |__|  |________|    |_______________
 v_cnt :       1     2             3            3
           |[->1]|[->1]|   [->1]     |
           |         [*1:3]          |  ##[0:$]   |
            ^...................................*

Then, your property should be precisely like below.

property p_pulse_cnt_2 (N);
    int v_cnt = 0;
    ($rose(a), v_cnt=0) ##1 ( ($fell(b), v_cnt++)[*1:$] ##[0:$]) intersect $fell(a)[->1] |=> (v_cnt == num);
endproperty

And I found the following post.

It looks usage of go-to repetition [->n] and non-continuous repetition [=N] to a sequence is not allowed. They are only for boolean.
Therefore,
(xxxx, v_cnt++) cannot be used with [=1:$] or [->1:$] since it is a sequence.
Only continuous repetition (xxxx, v_cnt++)[*1:$] is allowed.

I expect PASS of the property in my test shown above with ALL simulators.

I’ll try with the new property p_pulse_cnt_2.

You need the goto
(fell(b)[->1], v_cnt++)[*1:]

@hdlcohen
Yes. It should have had [->1]. I corrected p_pulse_cnt_2. In addition, I implemented the property using within instead of intersect (p_pulse_cnt_3).

property p_pulse_cnt_2(N);
  int v_cnt;
  ($rose(a), v_cnt=0) ##1 ( ( ($fell(b)[->1], v_cnt++, dbg(v_cnt))[+] ##[1:$] !a ) intersect !a[->1] ) |=> ( v_cnt == N, dbg(v_cnt) ) ;
endproperty

property p_pulse_cnt_3(N);
  int v_cnt;
  ($rose(a), v_cnt=0) ##1 (   ($fell(b)[->1], v_cnt++, dbg(v_cnt))[+]              within    !a[->1] ) |=> ( v_cnt == N, dbg(v_cnt) ) ;
endproperty

I tried these properties.
The EDAplayground was updated.
SVA Pulse Number Count

But the result is more confusing :confused:

p_pulse_cnt

  • QuestaSim, FAIL
  • Simulator X, Compile Error
  • Simulator V, PASS

p_pulse_cnt_2

  • QuestaSim, PASS
  • Simulator X, PASS
  • Simulator V, FAIL

p_pulse_cnt_3

  • QuestaSim, FAIL
  • Simulator X, FAIL
  • Simulator V, FAIL

I expected all PASS of p_pulse_cnt_2 and _3.
All simulators have bugs? Or, do I still have any issues in my properties?

I changed the model and provided explanations in the comments.
EDA EDA Playground

module tb;
    bit clk, a, b;
  
    initial forever #5 clk = !clk;
  
    default 
    clocking def_clk @(posedge clk);
    endclocking
  
    function void print(string msg);
      $display("T : %0t %s", $time(), msg);
    endfunction
  
      
    int cnt2, cnt3 ;
    function void dbg(int v, id);
      $display("id %d, v_cnt : = %0d", id, v);
      if(id==2) cnt2 = v;
      if(id==3) cnt3= v;
    endfunction
      
  //property p_pulse_cnt(N); // Doesn't work
  //  int v_cnt;
  //  //  ($rose(a), v_cnt=0) ##1 first_match( ($fell(b), v_cnt++, dbg(v_cnt))[=1:$] ##1 $fell(a) ) |=> ( v_cnt == N, dbg(v_cnt) ) ;
  //  ($rose(a), v_cnt=0) ##1 ( ( ($fell(b), v_cnt++, dbg(v_cnt))[=1:$] ) intersect !a[->1] )  |=> ( v_cnt == N, dbg(v_cnt) ) ;
  //endproperty
    property p_pulse_cnt_2(N);
      int v_cnt;
     // ($rose(a), v_cnt=0) ##1 ( ( ($fell(b)[->1], v_cnt++, dbg(v_cnt, 2))[+] ##[1:$] !a ) intersect !a[->1] ) |=> ( v_cnt == N, dbg(v_cnt) ) ;
      ($rose(a), v_cnt=0) ##1 ( ( ($fell(b)[->1], v_cnt++, dbg(v_cnt, 2))[+]              ) intersect !a[->1] ) |=> ( v_cnt == N); // , dbg(v_cnt) ) ;
    endproperty
    /* $rose(a), v_cnt=0) ##1 ( ( ($fell(b)[->1], v_cnt++, dbg(v_cnt, 2))[+]              ) intersect !a[->1] ) is of the form 
       A ##1 (B[->1][*1:$] intersect C[->1])  // same as 
       A ##1 (!B[*0:$] ##1 B[*1] intersect C[->1]) or  // thread 1 carries its separate v_cnt 
       .. if thread 1 is shorter thatn the length of c[->1] then this thread is a NO MATCH 
       A ##1 (!B[*0:$] ##1 B[*4] intersect C[->1]) or 
       ---if C occurs in thread 4 before the occurrence of B, then this thread is a NO MATCH 
       --- All future threads are also NO MATCH since c[->1 already occurred ]
    ---These future threads are:   A ##1 (!B[*0:$] ##1 B[*1] intersect C[->1]) 
    --- With a NO MATCH in any of the antecedents, the assertion is vacuously true. 
    
    */
  
    property p_pulse_cnt_3(N);
      int v_cnt;
    //  ($rose(a), v_cnt=0) ##1 (   ($fell(b)[->1], v_cnt++, dbg(v_cnt, 3))[+]              within    !a[->1] ) |=> ( v_cnt == N, dbg(v_cnt) ) ;
        ($rose(a), v_cnt=0) ##1 (   ($fell(b)[->1], v_cnt++, dbg(v_cnt, 3))[+]              within    !a[->1] ) |=> ( v_cnt == N); // , dbg(v_cnt) ) ;
    endproperty
  /* 1800:  The construct seq1 within seq2 is an abbreviation for the following:
         (1[*0:$] ##1 seq1 ##1 1[*0:$]) intersect seq2*/
  // Thus,  $fell(b)[->1][*1:$] within !a[->1]  // is equivalent to 
  /*         (1[*0:$] ##1 $fell(b)[->1][[*1:$]  ##1 1[*0:$]) intersect !a[->1] // equivalent to:
     (1[*0:$] ##1 $fell(b)[*1]  ##1 1[*0:$]) intersect !a[->1]   or // thread 1 carries its separate v_cnt 
     (1[*0:$] ##1 $fell(b)[*2:$]  ##1 1[*0:$]) intersect !a[->1]    // threads 2 to $, each carrying their own v_cnt
   FOr thia assertion to succeed, all antecedents must succeed 
   Also, eah thread of the antecedent carries its own local variable. 
   When thread 1 of the antecedent is a match, the consequent ( v_cnt == N) is a no match (v_cnt==1, N==3)
   Thus, assertion fails. 
         */

  //assert property( p_pulse_cnt  (3) ) print("PASS ast_pulse_count");  //Doesn't work
    ap_pulse_cnt_2: assert property( p_pulse_cnt_2(3) ) print("PASS ast_pulse_count");
    ap_pulse_cnt_3: assert property( p_pulse_cnt_3(3) ) print("PASS ast_pulse_count");
  //else                                print("FAIL ast_pulse_count");
  
    // initial begin $dumpfile("dump.vcd"); $dumpvars(); end
  
    initial begin
      ## 5 a = 1;
      ##10 a = 0;
      ##5;
      $finish(2);
    end
  
    initial begin
      ## 5 b = 1;
      ## 1 b = 0;
      ## 1 b = 1;
      ## 1 b = 0;
      ## 3 b = 1;
      ## 2 b = 0;
    end
  
  endmodule


Using task is efficient and easier

task automatic t_pulse(); 
    int v_count=0;
    fork 
      forever  @(negedge b) if(a) v_count=v_count+1; 
      @(negedge a) begin  
        am_5: assert(v_count<=4) $display("%t  vount=%d", $realtime, v_count);
        disable t_pulse;
      end  
    join   
  endtask
 

  always @(posedge clk) begin
    if($rose(a)) fork t_pulse(); join_none
  end 
1 Like

@hdlcohen

Thanks.
O.K. It looks local variables handling of within is different from intersect’s. In that sense, (##[0:$] seq_b ##[0:$] 1) intersect seq_a and seq_b within seq_a is not interchangable in case of local variable usage.

Yeah. It’s better not sticking to SVA implementation as you propose.
I think the following is easier to understand and to implement.

int pulse_cnt;
always@(posedge clk)
    if(a) begin
        if     ($rose(a)) pulse_cnt <= 0;
        else if($fell(b)) pulse_cnt <= pulse_cnt + 1;
    end

property p_pulse_cnt_5 (num);
    $fell(s1) |=> (pulse_cnt == num);
endproperty

I added this p_pulse_cnt_5 to my my EDA Playground. In addition, first_match() version was added as p_pulse_cnt_4.

Here is the summary of the result with each simulator.

Questasim :
ast_pulse_count_2, PASS
ast_pulse_count_3, FAIL
ast_pulse_count_4, FAIL
ast_pulse_count_5, PASS

Simulator X :
ast_pulse_count_2, PASS
ast_pulse_count_3, FAIL
ast_pulse_count_4, FAIL
ast_pulse_count_5, PASS

Simulator V :
ast_pulse_count_2, FAIL
ast_pulse_count_3, FAIL
ast_pulse_count_4, FAIL
ast_pulse_count_5, PASS

Only ast_pulse_count_5 is PASS with all simulators as we expected.

I don’t think each vendors’ simulator is mature enough to handle this matter in SVA yet :face_with_diagonal_mouth:


Using a newer version than the eda playground, I am getting different results.
Let’s not get into tool issues

1 Like

Hi @hdlcohen ,

That’s interesting! Thank you for the information. Hope all simulators properly handle this matter in the future.