You are on page 1of 41

The SystemVerilog Assertion (SVA) language offers a very powerful way to

describe design properties and temporal behaviors; however, they are innately
synchronous due to how they are defined by the SystemVerilog standard.
Unfortunately, this makes them especially hard to use for checking asynchronous
events and behaviors. Notwithstanding, armed with the proper techniques, SVA can
be used to effectively describe and check both synchronous and asynchronous
assertions.

1
First, let’s start our discussion by having a look at asynchronous behaviors and the
challenges that they present.

2
Asynchronous behaviors typically come in two forms: (1) asynchronous control
signals, and (2) asynchronous communication. Most designs have some kind of
asynchronous event like an asynchronous reset, enable, non-maskable interrupt, etc.
Even if these events are synchronized within to a clock, they may occur at any time,
preempting the current operation of the design, and should immediately trigger an
assertion to check that the design properly responds to the event.

The second form of asynchronous behavior is asynchronous communication. This is


seen with communication across clock domains or on interfaces that use some kind
of asynchronous handshaking.

3
Let’s have a look at the first type of behavior—asynchronous control signals. To
understand the difficulties, we’ll start by having a look at a simple up-down counter.

The table here shows the proper behavior of this counter. The asynchronous control
is the reset that causes the design to immediately change to 0.

4
If we were to write assertions for this counter, they might look something like this.
These assertions are rather straightforward since checking always happens on the
rising edge of the clock. But what about the asynchronous reset? How do we
incorporate that into our assertions?

5
A common mistake would be to add Reset into our assertion precondition as shown
here. While this looks like it would work, we might actually encounter false failures
from this. As you can see, when reset occurs, the value of Q immediate resets to 0.
On the next clock cycle when the precondition is evaluated, the asserted Reset will
stop the precondition from spawning a thread and the check from occurring just as
we intended.

The problem lies with the cycle before Reset occurs. The cycle before, the
precondition is met, an assertion thread is spawned, and the checker waits to check
the condition on the next clock edge. Before the check is evaluated, the Reset signal
asserts, changing the actual value of Q from the expected and the assertion throws a
false failure.

6
What we really need is some way to not only stop the evaluation of an assertion
when Reset occurs, but also kill any threads waiting to check when the reset asserts.
The “disable iff” abort terminator allows us to do just that. As soon as the Reset
signal occurs, the level-sensitive abort terminator stops both the evaluation of the
assertion and kills any processing threads. As a general rule of thumb, all assertion
sensitive to an asynchronous signal should use a “disable iff” qualifying expression.

7
Disable iff handles terminating our assertions, but what about checking that the
RTL does the correct thing when the asynchronous reset occurs? To write such an
assertion, we might write something as shown here---when the Reset goes high, then
check that Q goes to 0.

At first glance, this looks like it would do what we expect, but unfortunately it does
not. In fact, the assertion never even evaluates!!

8
To understand why, we need to understand how the SystemVerilog simulation
scheduler evaluates assertions, which we will look at in the next section.

9
A Verilog or SystemVerilog simulator has different scheduling regions where events
are scheduled and subsequently evaluated. All event evaluations occur in the
scheduler’s Active region. Events are scheduled in the Inactive region by using a #0
delay, and the Non-Blocking Assignment (NBA) region by using a non-blocking
assign. Once all the events in the Active region are exhausted, the Inactive region
events are promoted to the Active region and evaluated. Likewise, once those events
are evaluated, the non-blocking events are promoted to the Active region and
evaluated. As simulation progresses, events are scheduled and evaluated until all
events for a particular time step are evaluated and simulation can move forward.

The traditional Verilog scheduling semantics have been extended from including an
Active, Inactive, and NBA regions to also include some special regions just for
assertions. The Preponed region has been added in SystemVerilog in order to
sample all of an assertion’s inputs, and the Observed region is used for their
evaluation. Events such as clock or asynchronous signals are generated using
blocking or non-blocking assignments from initial or always blocks, which means
that they occur in the Active or subsequent regions. Since assertion inputs are
sampled in the Preponed region before any clock or reset events are generated,
assertions ALWAYS sample their input values before the sampling event occurs. This
is why when we write synchronous assertions we always need to go 1 clock cycle
into future and then look into the past with $past() in order to see what happened on
the last clock edge.

10
In addition to the Preponed and Observed regions, SystemVerilog also includes a
Reactive region where program blocks can schedule their events after the design has
finished evaluating in the Active/Inactive/NBA regions. The idea for this is to avoid
race conditions between the testbench and the design.

11
Now that we understand the scheduling semantics, let’s take a look at why the
asynchronous reset assertion failed. On the left-hand side, you can see what the
values of Reset and Q are in their respective regions. In the top assertion, when the
posedge of Reset occurs, the precondition evaluates whether Reset is true (non-
zero). As you can see, in the Preponed region, Reset == 0 so this assertion’s
precondition ALWAYS evaluates to false and the assertion never performs any
checking (this is hard to detect in simulation because it looks like the assertion is
always working and evaluating to true!)

In the bottom assertion, we set the precondition to always evaluate true (i.e., non-
zero) so that the assertion check occurs, but the value sampled for Q in the Preponed
region is 3, not 0. So this assertion always fails except for the case when Q was
already 0 before reset.

Where we need to check the value of Q is sometime after the design has updated Q
in the NBA region. To do so, we need to move the sampling of the assertion inputs
to either the Observed, Reactive, or later regions.

12
Let’s have a look at some simple methods we could use to delay the sampling of our
assertion inputs.

13
The most common way of handling asynchronous checking is to simply check the
signal synchronously. Either the clock or a fast clock usually suffices to give the
design enough time to update so when the assertion evaluates it samples the correct
input values. For most people this is adequate and it handles any timing delays in
the design, but it also leaves you wondering if the design really did immediately
react to the asynchronous control signal since the actual checking is delayed.

14
Alternatively, immediate assertions can be used to check the asynchronous behavior
immediately at the appropriate time. Immediate (or procedural) assertions are
placed inside of procedural blocks of code (initial, always) and sample their inputs
at the time they are evaluated, which is based on their context. By placing the
immediate assertions in the right context, the sampling of their inputs can be delayed
to check the asynchronous behavior.

There are several simple methods that can be used to delay the input sampling.
Let’s have a look at just a couple…

15
The first method to delay input sampling is to use a program block. Program blocks
sample their inputs in the Observed region and schedule their events in the Reactive
region. By placing the assertion in a program block, the value of Q can be sampled
AFTER the non-blocking assignment is made to it by the RTL when Reset occurs.
Now, when Reset occurs, its results can be immediately observed and checked.

One drawback to using a program block is that not all simulators support nested
programs inside of modules, which means that hierarchical references would be
needed to access the RTL signals. To work around this, a program could be bound
(using bind) into the design unit.

16
Sequence events are another way to delay assertion input sampling. The
SystemVerilog standard states that sequence events set their end-point (i.e., when
they are matched) in the Observed region, and that a process resumes it execution
following the Observed region in which the end-point is detected (see IEEE
1800-2005, Section 10.10.1, p. 142).

A sequence event is created by defining a named sequence and then waiting on it as


shown above. The assertion is placed after the sequence event so that its inputs are
sampled after the Observed region. The latest versions of most simulators have
good support for sequence events.

17
The expect statement is another useful SystemVerilog construct for delaying input
sampling. The SystemVerilog standard states, “The statement following the expect
is scheduled to execute after processing the Observe region in which the property
completes its evaluation” (IEEE 1800-2005, Section 17.6, p. 299). The advantage of
using expect over just assert is that expect can evaluate temporal expressions and
properties; whereas, assert cannot. Unfortunately, not all simulators delay the
evaluation of expect so a program block can be used as seen before.

18
Clocking blocks can also be used for delaying immediate assertion input sampling.
When an “input #0” is specified in a clocking block, the inputs are sampled in the
Observed region. Using the clocking block then means that the inputs are always
sampled after the design has finished updating.

Unfortunately, clocking blocks do not give the same results in all simulators the first
time Reset occurs. Since System/Verilog indeterminately executes processes, a race
condition may exist between when the assertion reads the clocking block variable
and when it gets updated, resulting in an X for Q the first time Reset occurs. To
solve this, it is usually adequate to wait on the clocking block.

19
Since the RTL updates using non-blocking assignments, trying to delay sampling to
the NBA region could pose a possible race condition. However, if done correctly, an
immediate assertion can be also be delayed to right after the RTL has finished its
updating. This can be accomplished by using a combination of a non-blocking
event, such as the non-blocking trigger shown above, and the use of a #0 delay.

The non-blocking trigger above will delay the assertion evaluation until at least the
NBA region; however, System/Verilog does not guarantee the order that the always
blocks will evaluate and schedule their events. In order to further delay the assertion
evaluation until after the RTL schedules its update to Q, a #0 delay can be used to
delay the assertion further to the Inactive region as illustrated in this slide. Using
the #0 delay, the order that the non-blocking events no longer matters and the
assertion can be guaranteed to always sample its inputs and evaluate at the correct
moment in simulation time.

For older simulators that do not implement the non-blocking trigger construct, the
following could also be used:

always @(posedge Reset)



myReset <= Reset;"
always @(posedge myReset)

#0 assert( Q == 0 );"

20
Immediate assertions generally do not allow us to use the SVA temporal syntax that
we get with concurrent assertions. Concurrent assertions are limited by the standard
on when they can sample their inputs---inputs must be sampled in the Preponed
region. However, there are 2 workarounds that we can consider.

21
The first way to make a concurrent assertion work is to delay the sampling event. A
simple concurrent assignment with a #1 delay is adequate enough to delay the input
sampling long enough for the asynchronous assertion to evaluate correctly. Of
course, some might object that this is not much different than sampling with a clock
because there is a possibility of glitches between the asynchronous event and the
actual check. However, a #1 delay should be small enough to not worry too much
about this. While not exactly standard, some simulators support using #1step in an
assignment, which essentially delays the assertion evaluation to the Postponed
region and removes the possibility of missing any glitches. One major simulator
(Modelsim/Questasim) supports #step instead.

22
Another way to delay input sampling is by calling a task or function in a concurrent
assertion. The SystemVerilog standard states that subroutines called in a sequence
are to be evaluated in the Reactive region. By using a ref on a task argument, the
current value is sampled in the Reactive region. Unfortunately, not all simulators
treat functions the same as tasks so a workaround is to not pass the thing to be
sampled as a function argument but to simply sample it within its declarative scope
inside the function. Since the signal is not an argument, it is not an assertion input
and not sampled in the Preponed region but in the Reactive region---the region of its
evaluating context.

23
Delaying the input sampling for assertions works great for RTL simulation, but what
happens when timing delays are inserted in a gate-level simulation? Now of these
assertions would work because the RTL does not change on the same time step as
the asynchronous event.

Instead, our assertions need to wait for the asynchronous event and then watch for
the design to change before checking. We could use a multi-clocked sequence as
shown above to wait for the design to update.

However, what if Q was already 0 so @Q never evaluates because there was no


value change? It could be qualified using a conditional statement, but there still
exists 2 problems: (1) there is the same sampling issue of Q when @Q occurs since
the input is sampled in the Preponed region, and (2) how can it guaranteed that Q
changed because of Reset event that triggered it? What if Q did not change from
Reset but when the counter started counting again? Even if it is qualified with Reset
being high, Q might not change until a future Reset event and not the current one.

24
The best solution to this problem is probably a compromise using a multi-clocked
sequence with Reset and the Clock. The assertion will trigger asynchronously when
the Reset event occurs, but then Q is sampled using the Clock to ensure that it
eventually updates while under Reset given a window of time specified by some
timeout constant. This makes it easy to change the assertion to compensate for gate-
delays with the gate-level netlist, to ensure that Q changes within an acceptable
window of time, and that Q actually responses to the corresponding Reset event.

25
The second type of asynchronous behavior to consider is asynchronous
communication.

26
There are 2 types of asynchronous communication we’ll consider: (1) clock domain
crossing, and (2) handshaking across an interface. Handling this type of behavior is
actually quite easy using SVA because it has a well-defined language for handling
multi-clocked sequences.

27
The key to using multi-clocked sequences is understanding how the clock is passed
between the different clocking events---know as clock handover. The standard
defines clock handover is accomplished by using the ##1 sequence concatenation
operator or the non-overlapping implication operator |=>. Normally, these operators
mean to wait 1 cycle or sampling event, but in multi-clocked sequences they cause
the sequence to wait for the different clock event to occur.

In the example on this slide, you can see that there are two clock domains, line_ck
and cpu_ck. In the line_ck clock domain, a sequence called flag has been created to
sample the line signal for 6 consecutive cycles. In the cpu_ck clock domain, the irq
sequence is defined to span only 1 clock cycle.

These two properties are joined together using ##1, which hands off the sampling
clock from the line_ck domain to the cpu_ck domain. The issue with asynchronous
events and sampling in the Preponed region is no longer an issue because the
sequences are just normal synchronous sequences in their respective clock domains.
Using clock handover, writing assertions for clock-domain crossing is rather
straightforward.

28
Using clock handover when crossing clock domains seems rather straightforward,
but the duration of ##1 may be arbitrarily short, which may not provide the setup
and hold time necessary to avoid timing hazards. This slide shows a scenario where
the strobe signal is generated in the src_clk domain and must be stable for at least 3
cycles in the dst_clk domain. An assertion must check that the strobe signal remains
stable but also that it has the adequate setup and hold time to be sampled in the
dst_clk domain. The solution is shown on the next slide….

29
One possible solution would be to describe the strobe signal in both clock domains
and match the two sequences together. The intersect operator can easily accomplish
this, but the beginning and end points must occur at the same time or with the same
starting and ending clocks. Using intersect, the assertion can be described as:

assert property ( @(posedge src_clk) $rose(strobe) |-> (


strobe[*1:$] ##1 1 )
) intersect (
##1 @(posedge dst_clk) strobe[*3]
##1 @(posedge src_clk) 1
)

Since the intersect operator requires the same end points, the additional ##1 1 is
appended to the src_clk sequence so that it can match the end of the dst_clk
sequence. Likewise, the dst_clk sequence switches to the src_clk domain to
complete its sequence, giving it the same ending point as the src_clk sequence. The
assertion satisfies both the stability and timing checks since the sequences combined
ensure that the strobe signal remains asserted for the required number of cycles.

30
The second type of asynchronous communication is handshaking across an
interface. Let’s consider a simple asynchronous protocol that most are familiar with
—the Universal Asynchronous Receiver/Transmitter or UART. In the UART
protocol, both the Receiver and Transmitter send data synchronously with respect to
their internal clock. The reason why the protocol is consider asynchronous is that
the clock used to drive and sample is not transferred with the data. Instead, both
Receiver and Transmitter agree upon a predetermined clock frequency and baud
rate, and then begin their transfer.

In order to synchronize with the transmitter’s data, the receiver uses a fast clock to
oversample and find the center of each data bit. Once the start bit is detected, each
data bit follows at a calculable distance and each bit is sampled. The start of the
transfer happens with an asynchronous handshaking of the Ready-To-Send (RTS)
and Clear-To-Send (CTS) signals.

31
Here’s how we could write an assertion to check the data transfer. The handshake
sequence uses a multi-clock sequence to detect when it is time to begin the data
transfer, using ##1 to handover from the RTS to the CTS. The non-overlapping
implication operator is then used to handover the clock to the check_trans
sequence. This sequence uses a local variable to grab each bit that is sent so that the
parity can be calculated and checked.

Since the data is available before the sampling occurs, there is no issue with
grabbing the data into the local variable. As you can see, checking asynchronous
communication is simply a case of using multi-clocked sequences and handing-over
the clock between the sequences.

32
Another asynchronous communication protocol is the SCSI interface. SCSI uses
asynchronous handshaking to transfer data. This slide shows the signals used on the
interface. The state and datareg variables used in the following slides are part of the
internal SCSI FSM.

33
The SCSI protocol looks as shown here. There are arbitration signals for acquiring
the bus, and the master/slave passes messages back and forth using 3 signals that
specify the command. When a message is transferred, the REQ signal is asserted
and ACK is asserted in response, which is where the asynchronous handshaking
occurs. Data is sent over the differential data pairs.

34
Writing an assertion for the SCSI interface might look as follows. First, a data_cmd
sequence is defined to detect when valid data is being transferred. Next, the data is
checked in the check_data property. When the transfer occurs in the SCSI FSM
(i.e., state == TX), the transmitted data is captured in a local variable. Clock
handover is used to synchronize between the handshake signals, and then the data is
captured by the receiver for comparison with the transmitted data saved in the local
variable.

35
In December of 2009, the latest version of the SystemVerilog standard was voted on
and approved. With the new standard, we have several improvements that may help
us to write assertions for asynchronous behavior. Let’s have a brief look at a few of
those.

36
The latest standard provides us with new asynchronous abort properties. For
example, now we can set a default disable signal to terminate our assertions instead
of needing to specify it with every assertion declaration. We now have the new
accept_on and reject_on properties that either cause an assertion to evaluate true or
false upon a level sensitive expression, respectively.

A global clock has been defined now so we could use it and the different global
clock functions for a fast clock when sampling our asynchronous behaviors.

37
Now, procedural concurrent assertions can have delay input sampling when casting
as a constant or using automatic variables. Since assertions are evaluated in the
Observed region, the sampling will be delayed until then as well.

A deferred assertion is specified using a #0 delay, and this delays their evaluation
until the Observed region. Depending on how the simulators implement this, these
might be helpful but probably not since the standard says, “A deferred assertion’s
expression is evaluated at the time the deferred assertion statement is processed.”

38
In summary, …

39
Here are some guidelines to follow when handing asynchronous behaviors with
SVA …

40
Send questions or comments to info@doulos.com.

41

You might also like