mirror of
https://github.com/YosysHQ/sby.git
synced 2025-04-14 17:28:44 +00:00
84 lines
4.5 KiB
Markdown
84 lines
4.5 KiB
Markdown
A simple example for using abstractions
|
|
=======================================
|
|
|
|
Abstractions are a way to deal with formal properties that are difficult to
|
|
prove directly because they require the design to pass through many intermediate
|
|
stages to observe the behavior in question. An abstraction can elide the
|
|
irrelevant steps and boil the behavior down to the steps important to the proof.
|
|
|
|
Instead of directly proving that the properties are satisfied by the design,
|
|
abstractions can be used to prove in a two-step process
|
|
1. that the abstraction correctly models the design's behavior
|
|
("the design does X")
|
|
2. that the properties are satisfied by the abstraction's behavior
|
|
("anything that does X will do Y").
|
|
From this, we can deduce by simple syllogism that the design must satisfy the
|
|
properties ("the design does Y").
|
|
|
|
A simple example is the design in `demo.v`. It defines a module containing a
|
|
20-bit counter, which has four outputs A, B, C, D that indicate when the counter
|
|
value happens to be one of four specific numbers (123456, 234567, 345678, 456789).
|
|
|
|
`props.sv` defines some properties about the outputs A, B, C, D and binds them
|
|
to the module. These properties assert that the counter values are reached in
|
|
order. For example, the first assertion describes that when output A is active
|
|
(i.e. the counter value is 123456), the outputs B, C, and D are not (the counter
|
|
value is not 234567, 345678, or 456789), and they will remain inactive for an
|
|
unspecified number of cycles, followed by B becoming active (counter reaches
|
|
234567). The cover property checks that it is possible for the module to pass
|
|
through the four values A, B, C, D and then return to A at least once.
|
|
|
|
However, it would be infeasible to prove these properties directly: because the
|
|
counter counts up one by one, it would take thousands of steps to pass from one
|
|
output activation to the next, and over a million steps to reach the sequence in
|
|
the cover property.
|
|
|
|
Introducing an abstraction is a way to get around this limitation. The
|
|
abstraction in `abstr.sv` describes only the aspects of the counter's behavior
|
|
that are relevant to the problem at hand:
|
|
- its value strictly increases except at reset and when overflowing from
|
|
2**20-1 to 0
|
|
- a counter value strictly smaller than one of the four values of interest is
|
|
always followed by either another value strictly smaller, or the value of
|
|
interest itself.
|
|
|
|
In a first step, the test in `abstr.sby` proves (asserts) that the properties
|
|
in the abstraction apply to the counter module. To do this, the macro
|
|
`` `demo_counter_abstr_mode`` in `abstr.sv` is defined to mean `assert` when
|
|
reading the file. Run it with the command `sby -f abstr.sby`; if it returns
|
|
`PASS`, you have proved that the abstraction correctly models the counter.
|
|
|
|
In a second step, for the test in `props.sby` the actual counter implementation
|
|
is removed from the design using the `cutpoint demo/counter` command. This
|
|
command disconnects the net carrying the counter value from the logic driving
|
|
it and instead replaces it with an `$anyseq` cell - meaning that the solver is
|
|
free to chose any new counter value that suits it every time step (cycle). The
|
|
counter value is restricted only by the properties in the abstraction - which
|
|
this time are assumed by defining `` `demo_counter_abstr_mode`` as `assume` when
|
|
reading `abstr.sby`.
|
|
|
|
Concretely, this means that instead of having to use 123456 time steps to
|
|
consider each individual value that the counter will hold between reset and A
|
|
becoming active, the solver can consider all 123456 values until the activation
|
|
of A simultaneously, in a single time step.
|
|
|
|
You can see this in the cover trace produced for the cover property. After
|
|
running `sby -f props.sby`, open the file `props_cvr/engine_0/trace0.vcd` in a
|
|
waveform viewer such as `gtkwave`. You will find that the counter value jumps
|
|
immediately from one value of interest to the next - the solver actually
|
|
considers all the other possible values inbetween as well, but picks the one
|
|
that leads to the most useful behavior in the next time step, which is in this
|
|
case always the highest value permitted by the constraints of the abstraction.
|
|
|
|
Suggested exercises:
|
|
|
|
- Make modifications to `abstr.sv` and/or `demo.v`. Make a prediction if the
|
|
change will make `sby -f abstr.sby` or `sby -f props.sby prv` fail and test your
|
|
prediction.
|
|
|
|
- What happens if we remove `abstr.sv` from `props.sby`, but leave the `cutpoint`
|
|
command in place?
|
|
|
|
- What happens if we remove the `cutpoint` command from `props.sby`, but leave
|
|
`abstr.sv` in place?
|