Issue with $stable when used with reject_on

I have following Code snippet ::


 property  suntil_with ( Sig1 , Sig2 ) ;   

    reject_on( ! $stable( Sig1 ) )  $stable( Sig1 )  s_until_with  $rose( Sig2 )  ;   
    
 endproperty

 property  ab ( Sig1 , Sig2 )  ;
    
    @( posedge  clk )   $rose( enable )  |=>   suntil_with ( Sig1 , Sig2 )   ;   
    
endproperty

I use property ab in assert property and cover property .

However I observe a Compilation error ::

" Could not determine the clocking event for ‘$stable’ "

Any suggestions ?

Without reject_on( ! $stable( Sig1 ) ) it works fine .

In reply to hisingh:

reject_on is an asynchronous about property. Perhaps you meant to use sync_reject_on. But without knowing what your requirements are, I’m not sure why you need the reject in any case.

In reply to hisingh:
Add clocking event in the $stable


 reject_on( ! $stable( Sig1, @(posedge clk) ) )  $stable( Sig1 )  s_until_with  $rose( Sig2 )  ;   

The reject_on (expression_or_dist) property_expr rejects the property as false if the abort condition is true. Otherwise, with the abort condition as false, the property expression evaluates to completionlog]

In reply to dave_59:

Without the asynchronous sampling the assertion misses out if the Sig1 has a glitch between 2 Consecutive Clock ticks .

So I thought of sampling Sig1 asynchronously .

In reply to ben@SystemVerilog.us:

Ben ,

A few follow up questions ::

[1] Since $stable( Sig1 ) is being used asynchronously it doesn’t inherit the Clocking event .

 This  is  similar  to  using  $stable  in  continuous  assign  statements 

     assign  stableVal = ( $stable( ConfigSig , @( posedge clk ) ) ? sigVal : errorVal ;  
     
 Hence  we  need  to  specify  clocking  event  in  $stable  within  reject_on .

 **Is  my  understanding  correct  ?**

[2] I changed the Clock Sampling to ::

 
 reject_on( ! $stable( Sig1, @( clk) ) )  $stable( Sig1 )  s_until_with  $rose( Sig2 )  ; 
    
If  I  use  **reject_on( ! $stable( Sig1, @( posedge  clk) ) )**  

it  is  equivalent  to  property_expression  without  using  ' reject_on '  i.e 

      $stable( Sig1 )  s_until_with  $rose( Sig2 )  ; 
    
Hence  a  glitch  between  2  posedge  of  clk  would  go  unnoticed . 

**NOTE :: Using  @( clk )  a  glitch  between  posedge N negedge  OR  negedge  N  posedge  still  goes  unnoticed  .**

[3] Since ’ reject_on ’ samples the Signals asynchronously .
My guess is this could leads to performance issues for longer simulations .

 **Is  usage  of  ' reject_on '  discouraged  ?**

In reply to hisingh:

reject_on( ! $stable( Sig1, @(posedge clk) ) )

is equivalent to

sync_reject_on( ! $stable( Sig1) )

They both miss asynchronous glitches.

In reply to dave_59:

Yes , while trying out different combination of Signals in TB I observed the same .

In reply to hisingh:

In reply to ben@SystemVerilog.us:… Hence a glitch between 2 posedge of clk would go unnoticed.

With supporting logic, you can create a signal that goes to 1’b1 if in between clocks sig1 changes value. Here, I use a task initiated by @(posedge clk) that forks 2 processes:

  • #2 @(sig1) s1_glitch=1’b1;
    The #2 is used to avoid a normal sig1 toggling to be considered as a glitch in sig1.
    I am assuming that sig1 can toggle in less than 2 ns.
    I there is an @(sig1)before an @(posedge clk) then it is considered a glitch
  • @(posedge clk) s1_glitch=1’b0; That resets the glitch signal

Code


module m;
  bit clk, sig1, sig2, enable, s1_glitch, a, b;

  property suntil_with(Sig1, Sig2);
    reject_on (s1_glitch) $stable(Sig1) |->  $stable(Sig1) s_until_with $rose(Sig2);
  endproperty

  task automatic glitch();
    fork
      #2 @(sig1) s1_glitch=1'b1; // sig1 can taggle in less than 2 ns 
      @(posedge clk)  s1_glitch=1'b0; 
    join_any 
  endtask 

  always_ff @(posedge clk)  glitch(); 

  ap : assert property (@(posedge clk) suntil_with(a, b));
endmodule

[3] Since ’ reject_on ’ samples the Signals asynchronously .
My guess is this could leads to performance issues for longer simulations .
Is usage of ’ reject_on ’ discouraged ?

I think it is useful. See

Ben Cohen
http://www.systemverilog.us/ ben@systemverilog.us
** SVA Handbook 4th Edition, 2016 ISBN 978-1518681448

  1. SVA Package: Dynamic and range delays and repeats SVA: Package for dynamic and range delays and repeats - SystemVerilog - Verification Academy
  2. Free books: Component Design by Example https://rb.gy/9tcbhl
    Real Chip Design and Verification Using Verilog and VHDL($3) https://rb.gy/cwy7nb
  3. Papers:

Udemy courses by Srinivasan Venkataramanan (http://cvcblr.com/home.html)
https://www.udemy.com/course/sva-basic/
https://www.udemy.com/course/sv-pre-uvm/

In reply to ben@SystemVerilog.us:

Ben ,

Extended your logic a little further ::

This helps to catch glitches b/w Negedge - Posedge as well as Posedge - Negedge



 bit  clk , a ,  b  ,  enable  , s1_glitch ;

property  suntil_with ( Sig1 , Sig2 ) ;  
      
    reject_on( s1_glitch )  $stable( Sig1 )  s_until_with  ( $rose( Sig2 ) )  ;   
         
 endproperty


 property  ab ( Sig1 , Sig2 )  ;
                                                                               
   @( posedge  clk )  $rose( enable )  |=>   suntil_with ( Sig1 , Sig2 )   ;   
    
 endproperty

 sequence  trigger1 ;  //  Declared  as  ' sequence '  to  use   .triggered  Method  !!

 @( posedge clk ) $rose( enable ) ; // Need  to  specify  clocking  event  for  .triggered

 endsequence

 sequence  trigger2 ;

 @( posedge clk ) $rose( b ) ;  // Need  to  specify  clocking  event  for  .triggered

 endsequence

 task automatic glitch();

   fork
    begin

     fork
       
          @( a ) s1_glitch = 1'b1;  

          @( clk )  s1_glitch = 1'b0 ;
       
     join_any

    disable fork ;

    end
   join
 endtask

 always @( trigger1.triggered )   //  Start  ONLY  when antecedent  is  True 
  begin

  fork
   begin

     fork

        begin

          glitch();

          forever @( clk )

          glitch();

        end

       begin

         @( trigger2.triggered ) ;  //  Restart  when  Consequent  has  a  Match

       end

     join_any

    disable fork ;

   end
 join

 end

 ap : assert property ( ab(a, b) );


Some quick comments and observations:


 bit  clk , a ,  b  ,  enable  , s1_glitch ; 
 sequence  trigger1 ;  //  Declared  as  ' sequence '  to  use   .triggered  Method  !!
    @( posedge clk ) $rose( enable );  
 endsequence 
 always @( trigger1.triggered )   //  Start  ONLY  when antecedent  is  True 
  ....
/* [Ben] Too much unnecessary code! Why declaring a sequence of one term to use .triggered 
when you can use that term directly? Thus, instead of */
always @( trigger1.triggered ) 
// use 
// $rose ( expression [, [clocking_event] ] ) 
    always  @($rose(a, @(posedge clk) ))   
// ---------------------------------
// [Ben] your glitch task has too many embedded forks? 
// This looks better to me 
 task automatic glitch();
   fork
    begin
     fork       
          @( a ) s1_glitch = 1'b1;  
          @( clk )  s1_glitch = 1'b0 ;      
     join_any
     disable fork  
 endtask 
// -------------------------
// [Ben]  To many forks and glitch() calls. 
// Somehow, it does not look right to me. 
 always @( trigger1.triggered )   //  Start  ONLY  when antecedent  is  True 
  begin
  fork
   begin
     fork
        begin
          glitch();
          forever @( clk ) glitch(); // [Ben] why not a simple 
             //[Ben] always @( clk ) glitch(); 
        end
       begin
         @( trigger2.triggered ) ;  //  Restart  when  Consequent  has  a  Match
       end
     join_any
    disable fork ;
   end
 join
 end
 ap : assert property ( ab(a, b) );