Asynchronous FIFO Multithreaded assertion

Hi,

I am trying to verify an async FIFO.
I am specifically interested in the simultaneous push and pop operation.
(Since the push and pop are happening in 2 different clock domains that are async to each other we cannot (strictly speaking) ensure a 100% simultaneous push and pop, lets say my requirement is just to do this on an overlapping push and pop operations)
Here is what I have currently:
please note that a push happen when w_valid and w_ready are both 1 and a pop happen when r_valid and r_ready are both 1.
Also w_memeory and r_memory are the actual memory elements on read/write side, inside the fifo. (we can assume this as say an array etc)
w_baddr and r_baddr are the read and write pointers and other ones are the corresponding data fields.


property p;
   integer p_wbaddr, p_wdata;
   integer p_rbaddr, p_rdata;
   @(posedge r_clk) disable iff (!w_rst_n || !r_rst_n)
    (r_valid && r_ready, p_rbaddr=r_baddr, p_rdata=r_data) ##0 @(posedge w_clk) (w_valid && w_ready, p_wbaddr=w_baddr, p_wdata=w_data) |=> (w_memory[p_wbaddr] === p_wdata) && (r_memory[p_rbaddr] === p_rdata);
endproperty : p

The tool is not happy with this property. could anyone please tell me what is wrong above or how to implement this assertion for overlapping push/pop in a better way.

Thank you

In reply to curious_learner:


@(posedge r_clk) disable iff (!w_rst_n || !r_rst_n)
     (r_valid && r_ready, p_rbaddr=r_baddr, p_rdata=r_data) ##0 
      @(posedge w_clk) (w_valid && w_ready, p_wbaddr=w_baddr, p_wdata=w_data) |=> 
                    (w_memory[p_wbaddr] === p_wdata) && (r_memory[p_rbaddr] === p_rdata);

I see no problem with your code. The consequent uses the w_clk.
What issue do you experience?

Sims OK. That module declares 2 clocks that are synchronous.

The issue I see is with your FIFO model is the memory model. Specifically, an Async fifo is implemented with ONE common memory, as shown below. Your SVA uses 2 memories:
a w_memory and a r_memory. I am puzzled.

This fifo is explained in my book
Real Chip Design and Verification Using Verilog and VHDL($3) https://rb.gy/cwy7nb
Code is available at
http://systemverilog.us/realchip_code.zip
ch3/fifo_async.v // Verilog code (before SystemVerilog days
ch3/fifo_async2.v
ch3/fifo_asyn_tb.v

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
  1. 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:

Hi Ben,

Thanks for the reply.
I am using Formal verification to prove this (QuestaFormal). I think EDA playground is simulation based.

This is the error I am getting: line 188: Unsupported subset of multi-clock constructs…

Yes you are correct. In the RTL they used a w_memory based on w_clk and a r_memory based on r_clk. But r_memory is basically w_memory-clock synced to r_clk, ie synced to read out at r_clk. so effectively we can say its one single memory. I am skipping the low level impl code just to keep things simple here.

Thank you

In reply to curious_learner:
Unsupported subset of multi-clock constructs
I don’t believe that formal verification tools support multiclocking.
You’ll ned to contact your vendor.
You could change the code to have a single clocking system and evaluate that condition.
Ben

In reply to ben@SystemVerilog.us:

Hi Ben ,

 For  the  multiclocked  property  ::

(a) If both @(posedge r_clk) and @(posedge w_clk) are True at T0 then
p_rbaddr , p_rdata , p_wbaddr and p_wdata are all assigned at T0.

(b) If @(posedge r_clk) is True at T1 whereas @(posedge w_clk) is False at T1 ,
then p_rbaddr and p_rdata are assigned at T1 and p_wbaddr and p_wdata are assigned at nearest @(posedge w_clk) .

[Q1] Is my understanding correct for (a) and (b) ?

[Q2] When is the consequent evaluated ? i.e which clock is used
Is it at the next @(posedge r_clk) OR next @(posedge w_clk) ?

In reply to hisingh:

@(posedge r_clk) disable iff (!w_rst_n || !r_rst_n)
(r_valid && r_ready, p_rbaddr=r_baddr, p_rdata=r_data) ##0
@(posedge w_clk) (w_valid && w_ready, p_wbaddr=w_baddr, p_wdata=w_data) |=>
(w_memory[p_wbaddr] === p_wdata) && (r_memory[p_rbaddr] === p_rdata);

For the multiclocked property ::
(a) If both @(posedge r_clk) and @(posedge w_clk) are True at T0 then
p_rbaddr , p_rdata , p_wbaddr and p_wdata are all assigned at T0.

(b) If @(posedge r_clk) is True at T1 whereas @(posedge w_clk) is False at T1 ,
then p_rbaddr and p_rdata are assigned at T1 and p_wbaddr and p_wdata are assigned at nearest @(posedge w_clk) .

[Q1] Is my understanding correct for (a) and (b) ?
[Ben] Yes, you are correct

[Q2] When is the consequent evaluated ? i.e which clock is used
Is it at the next @(posedge r_clk) OR next @(posedge w_clk) ?
[Ben]The last defined clockflows through the implication operator.
THus, next @(posedge w_clk) is used.
** [my book] 4.3.3 Clock Flow  Rule: Flow through |->, |=> :
When no explicit clock event is specified in an implication operator,
the clock from the end point of the antecedent is understood to flow across the operator.
Thus, the following to property expressions are identical:
@(posedge clk0) a0 |-> @(posedge clk0) a1 ##1 @(posedge clk2) a2;
** Rule: [1] A clocking event in the declaration of a sequence or property
does not flow out of an instance of that sequence or property.
owever, the clock flows across elements of same sequence.
For example, assuming no default clocking:

    sequence q_ef;   @ (posedge clk) e ##[1:5] f;   endsequence : q_ef 
    ap_q_ef_a: assert property (q_ef ##1 a);  //  Illegal, clk does not flow into “a”   

** Rule: Clocking event is trapped in parenthesized sequence: [1]
The scope of a clocking event flows into parenthesized subexpressions and,
if the subexpression is a sequence, also flows left-toright across the
parenthesized subexpression.
However, the scope of a clocking event does not flow out of enclosing parentheses.
The standard also states that when sequence instances are flattened,
the resulting expression that is returned is enclosed in parenthesis;
therefore clocks do not flow out of sequence instances either.
In the following example, the parentheses are within a sequence:

  @(posedge clk1) w ##1 (r  ##1 @(posedge clk2)  y    )     |=> m; 
//Is equivalent to:           
  @(posedge clk1) w ##1
        (@(posedge clk1) r ##1 @(posedge clk2)  y    |=>
                 @(posedge clk1) m;

w, r, m are clocked at posedge clk1 and y is clocked at posedge clk2.
Clock posedge clk1 flows across ##1, over the parenthesized subsequence
(r ##1 @( posedge clk2) y), and across the nonoverlapping implication |=> operator.
Clock posedge clk1 also flows into the parenthesized subsequence,
but it does not flow through @(posedge clk2).
Clock posedge clk2 does not flow out of its enclosing parentheses;
thus it does not flow into m.

SO what does that mean your

  @(posedge r_clk) disable iff (!w_rst_n || !r_rst_n)
     (r_valid && r_ready, p_rbaddr=r_baddr, p_rdata=r_data) ##0 
      @(posedge w_clk) (w_valid && w_ready, p_wbaddr=w_baddr, p_wdata=w_data) |=> 
                    (w_memory[p_wbaddr] === p_wdata) && (r_memory[p_rbaddr] === p_rdata);

In this case, the consequent is clocked with w_clk. However, if you wrote it this way:
// NOTE THE ()

@(posedge r_clk) disable iff (!w_rst_n || !r_rst_n)
     (r_valid && r_ready, p_rbaddr=r_baddr, p_rdata=r_data) ##0 
   (@(posedge w_clk) (w_valid && w_ready, p_wbaddr=w_baddr, p_wdata=w_data)) |=>// **new ()**
                    (w_memory[p_wbaddr] === p_wdata) && (r_memory[p_rbaddr] === p_rdata);

The consequent will be clocked with the r_clk because the write sequence (expression in this case)
is enclosed with its clocking event in parentheses.

Trivial, isn’t it?
:)
Ben

In reply to ben@SystemVerilog.us:

Thanks Ben for the detailed explanation ,

A clocking event in the declaration of a sequence or property does not flow out of an instance of that sequence or property.
However, the clock flows across elements of same sequence.

So for the following ::


 sequence  s1;
   @( posedge clk0 )  b  ##1  c ; 
 endsequence

 sequence  s2;
   @( posedge clk1 )  d  ##1  e ; 
 endsequence

 sequence  s;
   @( posedge clk )  a  ##1  s1  ##1  s2  ##1 f ; 
 endsequence


’ c ’ is clocked on clk0 , ’ e ’ is clocked on clk1 , ’ f ’ is clocked on clk as the clk1 don’t flow out of the sequence s2

Sequence ’ s ’ is equivalent to ::


 sequence  s;
   @( posedge clk )  a  ##1 ( @( posedge clk0 )  b  ##1  c ) ##1 ( @( posedge clk1 )  d  ##1  e )  ##1 f ; 
 endsequence
 

Personally I always feel like basics go a long way , so this sure is an important piece of info .

In reply to hisingh:
You are correct, but it is a good idea, for clarity, to add the clocking event rather than relying
on the reader’s understanding of SVA. Thus,


Instand of 
  @( posedge clk )  a  ##1 ( @( posedge clk0 )  b  ##1  c ) ##1 ( @( posedge clk1 )  d  ##1  e )  ##1 f ; 
// Write
   @( posedge clk )  a  ##1 ( @( posedge clk0 )  b  ##1  c ) ##1
 ( @( posedge clk1 )  d  ##1  e ) 
   @(posedge clk)  ##1 f ;