blob: ca11f2baa605dc3e57086b7846273560a221efd4 [file] [log] [blame] [view]
---
title: "OpenTitan Assertions"
---
# OpenTitan Assertions
## What Are Assertions?
Assertions are statements about your design that are expected to be always
true. Here are two examples:
* <code>`ASSERT(grantOneHot, $onehot0(grant), clk, !rst_n)</code>
<br />This asserts that signal <code>grant</code> will be either
one-hot encoded or all-zero.
* <code>`ASSERT(ackTwoClocksAfterReq, req |-> ##2 ack, clk, !rst_n)</code>
<br />Every time <code>req</code> goes high, <code>ack</code> must be
high exactly 2 clock cycles later.
Above examples are using the <code>`ASSERT</code> macro defined in
[prim_assert.sv](https://github.com/lowRISC/opentitan/blob/master/hw/ip/prim/rtl/prim_assert.sv),
whose four arguments are assertion name, property, clock, and
reset (active-high reset).
Assertions are usually added by the designer in the RTL file. Assertions can
also be added in a separate module, see for example
[tlul_assert.sv](https://github.com/lowRISC/opentitan/blob/master/hw/ip/tlul/rtl/tlul_assert.sv)
and its
[documentation]({{< relref "hw/ip/tlul/doc/TlulProtocolChecker.md" >}}),
which contains a generic protocol checker for the
TileLink-UL standard.
## Types of Assertions
There are two types of assertions:
* **Concurrent assertions** can span time and are triggered by a clock edge.
See the two examples in the previous section.
* **Immediate assertions** do not depend upon a clock edge. They are usually
used in an initial block to check for correct parameter settings. Example:
```
initial begin
checkFifoWidth: assert (FifoDepth > 0) else begin
$error("FifoDepth parameter should be > 0");
end
end
```
## Useful Macros
The file
[prim_assert.sv](https://github.com/lowRISC/opentitan/blob/master/hw/ip/prim/rtl/prim_assert.sv)
defines many useful shortcuts that you can use in your RTL
code. Some of them are detailed below:
### `ASSERT(name, prop, clk, rst)
* This is a shortcut macro for a generic concurrent assignment.
* The first argument is the assertion name. It is recommended to use
lowerCamelCase for the assertion name. The assertion name should be
descriptive, which will help during debug.
* The second argument is the assertion property.
* The last two arguments specify the clock and reset signals (active-high
reset).
* Note that this macro doesn't support a custom error message (such as the
$error message in the previous section). However, the macro will print out the
property name and the entire property code such as `req |-> ack`.
For example, <code>`ASSERT(myAssertion, req |-> ack, clk, !rst_n)</code>
is expanded as follows:
```
myAssertion: assert property (
@(posedge clk) disable iff ((!rst_n) !== 1'b0)
(req |-> ack)
) else begin
$error("Assert failed: [%m] %s: %s\n",
`STRINGIFY(myAssertion), `STRINGIFY(req |-> ack));
end
```
### `ASSERT_INIT(name, prop)
Concurrent assertion inside an initial block. It can be used for checking
parameters.
### `ASSERT_FINAL(name, prop)
Concurrent assertion inside a final block. It can be used e.g. for making sure
that a FIFO is empty at the end of each sim.
### `ASSERT_NEVER(name, prop, clk, rst)
Assert that a concurrent property never happens.
### `ASSERT_KNOWN(name, signal, clk, rst)
Assert that `signal` has a known value after reset, where "known" refers to
a value that is not X.
### More Macros and Examples
* For more macros see file [prim_assert.sv](prim_assert.sv).
* For more examples, search the repository for ASSERT by typing `grep -r ASSERT .`
* Also see
[tlul_assert.sv](https://github.com/lowRISC/opentitan/blob/master/hw/ip/tlul/rtl/tlul_assert.sv)
and its
[documentation]({{< relref "hw/ip/tlul/doc/TlulProtocolChecker.md" >}}).
## Useful SVA System Functions
Below table lists useful SVA (SystemVerilog assertion) functions that can be
used for assertion properties.
<table>
<tr>
<td><strong>System Function</strong>
</td>
<td><strong>Description</strong>
</td>
</tr>
<tr>
<td>$rose()
</td>
<td>True if LSB of expression changed to 1
</td>
</tr>
<tr>
<td>$fell()
</td>
<td>True if LSB of expression changed to 0
</td>
</tr>
<tr>
<td>$stable()
</td>
<td>True if the value of expression didn't change
</td>
</tr>
<tr>
<td>$past()
</td>
<td>Value of expression of previous clock cycle
</td>
</tr>
<tr>
<td>$past( , n_cycles)
</td>
<td>Value of expression n_cycles clocks ago
</td>
</tr>
<tr>
<td>$countones()
</td>
<td>Number of ones in the expression
</td>
</tr>
<tr>
<td>$onehot()
</td>
<td>True if exactly one bit is 1
</td>
</tr>
<tr>
<td>$onehot0()
</td>
<td>True if no bits or only one bit is 1
</td>
</tr>
<tr>
<td>$isunknown()
</td>
<td>True if any bit in the expression is 'X' or 'Z'
</td>
</tr>
</table>
## Useful SVA Operators
Below table lists useful operators that can be used for assertion properties.
<table>
<tr>
<td><strong>Operator</strong>
</td>
<td><strong>Description</strong>
</td>
</tr>
<tr>
<td>##n
</td>
<td>Delay operator, fixed time interval of n clock cycles
</td>
</tr>
<tr>
<td>##[m:n]
</td>
<td>Delay operator, time interval range between m and n clocks
</td>
</tr>
<tr>
<td>|->
</td>
<td>"Overlapping" implication (same cycle)
</td>
</tr>
<tr>
<td>|=>
</td>
<td>"Non-overlapping" implication (next cycle)
<br /> <code>a |=> b</code> is equivalent to <code>a |-> ##1 b</code>
</td>
</tr>
<tr>
<td>not, or, and
</td>
<td>Property operators
</td>
</tr>
</table>
There are also powerful repetition operators, see
[here](https://www.systemverilog.io/sva-basics) for more details.
## Symbolic Variables
When design has a set of modules or signals that share same properties,
symbolic variables can be used to reduce duplicated assertions.
For example, in the [rv_plic design](../ip/rv_plic/doc/_index.md), the array of
input `intr_src_i` are signals sharing same properties. Each
`intr_src_i[index]` will trigger the interrupt pending (`ip`) signal depending
on the corresponding level indicator (`le`) is set to level triggered or edge
triggered.
Without symbolic variables, the above assertions can be implemented as below:
```systemverilog
genvar i;
generate for (i = 0; i < N_SOURCE; i++) begin : gen_rv_plic_fpv
`ASSERT(LevelTriggeredIp_A, $rose(rv_plic.ip[i]) |->
$past(rv_plic.le[i]) || $past(intr_src_i[i]), clk_i, !rst_ni)
end
```
In contrast, symbolic variable can abstract the design by declaring the index with
constraints. To ensure the symbolic variable performs the expected behaviors,
two assumptions need to be written:
* Constraint the symoblic variable with the correct bound
* Randomize the variable at the beginning of the simulation, then keep it stable
throughout the rest of the simulation
```systemverilog
logic [$clog2(N_SOURCE)-1:0] src_sel;
`ASSUME_FPV(IsrcRange_M, src_sel >= 0 && src_sel < N_SOURCE, clk_i, !rst_ni)
`ASSUME_FPV(IsrcStable_M, ##1 $stable(src_sel), clk_i, !rst_ni)
`ASSERT(LevelTriggeredIp_A, $rose(rv_plic.ip[src_sel]) |->
$past(rv_plic.le[src_sel]) || $past(intr_src_i[src_sel]), clk_i, !rst_ni)
```
## Coverpoints
Coverpoints are used for properties and corner cases that the designer wants to
make sure are being exercised by the testbench (e.g. FIFO-full checks). The
code coverage tool then reports the coverage percentage of these coverpoints
together with the other cover metrics (such as line coverage and branch
coverage).
The macro <code>`COVER(name, prop, clk, rst)</code> of
[prim_assert.sv](https://github.com/lowRISC/opentitan/blob/master/hw/ip/prim/rtl/prim_assert.sv)
can be used to add coverpoints to your design, where the cover
property uses the same SVA syntax, operators, and system functions as the the
assert properties.
## How To Run FPV on OpenTitan
### Cadence JapserGold
If you have access to JasperGold from Cadence, you can formally verify your
assertions. For example, to run formal property verification (FPV) using
JasperGold on module `gpio`, type:
```
cd hw/formal
fpv gpio
```
JasperGold will then report which assertions have been proven or disproven,
and whether or not there are any unreachable assertions or coverpoints.
To run formal property verification for all modules, type:
```
cd hw/formal
fpv_all
```
This script generates a report of all FPV runs. An example report is shown
below, which lists the total number of assertions and the percentages of
covered and proven assertions for each block. CRASH identifies modules that
fail to run JasperGold, e.g. due to a combinational loop.
```
FPV RESULTS PER BLOCK
Below table shows the total number of assertions, and
the percentages of covered and proven assertions.
Block Asserts Covered Proven
---------------------------------------------
my_module1 23 100% 91%
my_module2 60 100% 100%
my_module3 CRASH
...
LIST OF ERRORS AND UNPROVEN ASSERTIONS FOR EACH BLOCK:
Note: "cex" below indicates that JasperGold found a
"counter example", which could be caused by an RTL
bug or a missing assume property on an input.
my_module1
[6] my_module1.u_reg.reqParity cex
[9] my_module1.tlul_assert.responseSizeMustEqualReqSize cex
my_module3
ERROR (ENL024): Combinational loop found within the cone of
influence for "u_sha2.u_pad.shaf_ren".
...
```
### Synopsys VC Formal
If you have access to VC Formal from Synopsys, you can formally verify your
assertions. For example, to run formal property verification (FPV) using
VC Formal on module `gpio`, type:
```
cd hw/formal
fpv -t vcf gpio
```
VC Formal will then report which assertions have been proven or disproven,
and whether or not there are any unreachable assertions or coverpoints.
To run formal property verification for all modules, type:
```
cd hw/formal
fpv_all -t vcf
```
This script generates a report of all FPV runs. The report is printed at the end of the run,
which lists the total number of assertions and the number of proven, vacuous,
covered and failing assertions for each block. CRASH identifies modules that
fail to run VC Formal.
...
```
## Naming Convenctions
For assertions, it is preferred to use postfix `_A` for assertions,
`_M` for assumptions, `_P` for properties, and `_S` for sequences.
For example:
```systemverilog
`ASSUME_FPV(IsrcRange_M, src_sel >= 0 && src_sel < N_SOURCE, clk_i, !rst_ni)
`ASSERT(LevelTriggeredIp_A, $rose(rv_plic.ip[src_sel]) |->
$past(rv_plic.le[src_sel]) || $past(intr_src_i[src_sel]), clk_i, !rst_ni)
```
## Implementation Guidelines
The recommended guidelines for where to implement assertions are as follows:
* Basic assertions should be implemented directly in the RTL file. These basic
functional assertions are often inserted by designers to act as a sanity
check.
* Assertions used for the testbench to achieve verification goals should be
implemented under the `ip/hw/module_name/fpv/vip` folder. This FPV environment
can be automatically generated by the [`fpvgen.py` script](../../util/fpvgen/README.md).
* Portable assertions written for common interfaces or submodules should also be
implemented under the `ip/hw/submodule_or_interface/fpv/vip` folder. These
portable assertion collections can be easily reused by other testbench via a
bind file.
## References
* [SVA Basics](https://www.systemverilog.io/sva-basics)
* [SVA Tutorial](https://www.doulos.com/knowhow/sysverilog/tutorial/assertions/)
* "SystemVerilog Assertions Design Tricks and SVA Bind Files", SNUG 2009,
Clifford E. Cummings,
[paper](http://www.sunburst-design.com/papers/CummingsSNUG2009SJ_SVA_Bind.pdf)