Regarding deferred assertions

Hi all,
Consider the following code


 assign not_a = !a;

 always_comb begin
     a1:assert ( not_a != a ); // Immediate
     a2: assert #0 ( not_a != a ); // Deferred immediate
 end

So when ‘a’ changes the always_comb gets triggered. There may be a possibility that assign hasn’t executed yet, hence the immediate assertion may fail.
This is an example of immediate assertion being glitch prone.

Since always_comb would be triggered twice ( once when ‘a’ changes and then later when ‘not_a’ changes ), how does the deferred assertion evaluate it’s expression?

In reply to MICRO_91:

See my paper Understanding and Using Immediate Assertions
https://verificationacademy.com/verification-horizons/december-2022-volume-18-issue-3/understanding-and-using-immediate-assertions
Provides guidelines

Ben Cohen
Ben@systemverilog.us
Link to the list of papers and books that I wrote, many are now donated.

or Links_to_papers_books - Google Docs

Getting started with verification with SystemVerilog

In reply to ben@SystemVerilog.us:

Ben,
I am referring to the following quote from the verification horizons page that you shared:

Observed deferred immediate assertions (assert #0)
Observed deferred immediate assertions address glitches caused by combinational logic (e.g., evaluations in the Active region using variables assigned in the other regions (Active, NBA, Reactive), thus causing multiple evaluation iterations. In a simplified view, results of each iteration of an observed deferred immediate assertion are put into a queue; old results are flush. This process repeats until there are no more iterations from the Active region to the post-Observed region, i.e., the combination logic finally settled. The action block is scheduled in the Reactive region.

Here is my understanding.


 assign not_a = !a;
 always_comb 
     a2: assert #0 ( not_a != a ); // Deferred immediate

  1. Input ‘a’ changes so always_comb is triggered ( assign statement hasn’t executed yet )
    This is the 1st iteration of observed immediate assertion which populates an internal queue to evaluate expression ( not_a != a ) based on new value of ‘a’ and older value of not_a

  2. assign statement executes thereby changing the value of not_a. This results in triggering always_comb for 2nd time. Now the previous queue is flushed out and a new queue is populated with new values of not_a and a. Now the expression ( not_a != a ) is true.

Since there are no more iteration of always_comb ( since ‘a’ doesn’t change again in current time step), the internal queue is evaluated( with updated values of not_a and a)

However does the LRM mention when does the evaluation exactly occur ? i.e which region is the expression evaluated ?

Eg: concurrent assertions are evaluated in Observed region using the preponed values of the variables.

The quote only mentions that action block is triggered in Reactive region but doesn’t mention about the evaluation of the expression stored in the last queue.

Turns out the LRM does explain the exact same example as the one I was trying.

LRM 16.4 :

As with all immediate assertions, a deferred assertion’s expression is evaluated at the time the deferred assertion statement is processed.
However, in order to facilitate glitch avoidance, the reporting or action blocks are scheduled at a later point in the current time step.
The pass and fail statements in a deferred assertion’s action_block, if present, shall each consist of a single subroutine call.
The subroutine can be a task, task method, void function, void function method, or system task.

Hence system task like $display as part of action block executes in reactive region.

LRM 16.4.1 :

When a deferred assertion passes or fails, the action block is not executed immediately. Instead, the action block subroutine call (or $error, if an assert or assume fails and no action_block is present) and the current values of its input arguments are placed in a deferred assertion report queue associated with the currently executing process.
Such a call is said to be a pending assertion report.
In the Observed region of each simulation time step, each pending observed deferred assertion report that has not been flushed from its queue shall mature, or be confirmed for reporting. Once a report matures, it may no longer be flushed.
Then the associated subroutine call (or $error, if the assertion fails and no action block is present) is executed in the Reactive region, and the pending assertion report is cleared from the appropriate process’s deferred assertion report queue.

LRM 16.4.2 :


assign not_a = !a;
  always_comb begin : b1
    a1: assert (not_a != a);
    a2: assert #0 (not_a != a); // Should pass once values have settled
  end

When a changes, a simulator could evaluate assertions a1 and a2 twice—once for the change in a and once for the change in not_a after the evaluation of the continuous assignment.
A failure could thus be reported during the first execution of a1.
The failure during the first execution of a2 will be scheduled on the process’s deferred assertion report queue.
When not_a changes, the deferred assertion queue is flushed due to the activation of b1, so no failure of a2 will be reported.

Therefore the answer my own question at the top

how does the deferred assertion evaluate it’s expression?

[Ans] As with all immediate assertions, a deferred assertion’s expression is evaluated at the time the deferred assertion statement is processed.
However due to the concept of ‘deferred assertion flush point’ the glitches encountered during ‘simple immediate assertions’ are avoided using ‘deferred assertion’

NOTE : In general,observed deferred assertions prevent glitches due to order of procedural execution, but do not prevent glitches caused by execution loops between regions that the assignments from the Reactive region may cause.