Skip to content

Commit e400c09

Browse files
committed
rewrite SVA sequences
This adds a facility to rewrite SVA sequences using SVA or such that they do not admit empty matches. This can be used to remove the burden of handling the four cases of possible empty matches in the ##[...] operator from engines that reason about SVA sequences.
1 parent bf112c1 commit e400c09

File tree

12 files changed

+418
-58
lines changed

12 files changed

+418
-58
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
../../verilog/SVA/empty_sequence1.sv
3+
--buechi --bdd
4+
^\[main\.p0\] 1 \[\*0\]: REFUTED$
5+
^\[main\.p1\] \(1 \[\*0\]\) ##1 main\.x == 0: PROVED$
6+
^\[main\.p2\] main\.x == 0 ##1 \(1 \[\*0\]\): PROVED$
7+
^\[main\.p3\] \(1 \[\*0\]\) ##0 1: REFUTED$
8+
^\[main\.p4\] 1 ##0 \(1 \[\*0\]\): REFUTED$
9+
^EXIT=10$
10+
^SIGNAL=0$
11+
--
12+
^warning: ignoring
13+
--
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
empty_sequence1.sv
3+
--bdd
4+
^\[main\.p0\] 1 \[\*0\]: REFUTED$
5+
^\[main\.p1\] \(1 \[\*0\]\) ##1 main\.x == 0: FAILURE: property not supported by BDD engine$
6+
^\[main\.p2\] main\.x == 0 ##1 \(1 \[\*0\]\): FAILURE: property not supported by BDD engine$
7+
^\[main\.p3\] \(1 \[\*0\]\) ##0 1: REFUTED$
8+
^\[main\.p4\] 1 ##0 \(1 \[\*0\]\): REFUTED$
9+
^EXIT=10$
10+
^SIGNAL=0$
11+
--
12+
^warning: ignoring
13+
--

regression/verilog/SVA/empty_sequence1.bmc.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ empty_sequence1.sv
55
^\[main\.p1\] \(1 \[\*0\]\) ##1 main\.x == 0: PROVED up to bound 5$
66
^\[main\.p2\] main\.x == 0 ##1 \(1 \[\*0\]\): PROVED up to bound 5$
77
^\[main\.p3\] \(1 \[\*0\]\) ##0 1: REFUTED$
8-
^\[main\.p4\] 1 ##0 \(1 \[\*0\]\): PROVED up to bound 5$
8+
^\[main\.p4\] 1 ##0 \(1 \[\*0\]\): REFUTED$
99
^EXIT=10$
1010
^SIGNAL=0$
1111
--

src/temporal-logic/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ SRC = ctl.cpp \
55
ltl_sva_to_string.cpp \
66
nnf.cpp \
77
normalize_property.cpp \
8+
rewrite_sva_sequence.cpp \
89
sva_sequence_match.cpp \
910
sva_to_ltl.cpp \
1011
temporal_logic.cpp \

src/temporal-logic/ltl_sva_to_string.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Author: Daniel Kroening, [email protected]
1515
#include <verilog/sva_expr.h>
1616

1717
#include "ltl.h"
18+
#include "rewrite_sva_sequence.h"
1819
#include "temporal_expr.h"
1920
#include "temporal_logic.h"
2021

@@ -298,9 +299,13 @@ ltl_sva_to_stringt::rec(const exprt &expr, modet mode)
298299
expr.id() == ID_sva_implicit_weak || expr.id() == ID_sva_implicit_strong)
299300
{
300301
PRECONDITION(mode == PROPERTY);
301-
// weak closure
302302
auto &sequence = to_sva_sequence_property_expr_base(expr).sequence();
303-
auto op_rec = rec(sequence, SVA_SEQUENCE);
303+
304+
// re-write to get rid of empty matches
305+
auto sequence_rewritten = rewrite_sva_sequence(sequence);
306+
307+
auto op_rec = rec(sequence_rewritten, SVA_SEQUENCE);
308+
// weak closure
304309
return resultt{precedencet::ATOM, '{' + op_rec.s + '}'};
305310
}
306311
else if(expr.id() == ID_sva_or)
@@ -448,7 +453,8 @@ ltl_sva_to_stringt::rec(const exprt &expr, modet mode)
448453
}
449454
else if(repetition.is_empty_match())
450455
{
451-
throw ebmc_errort{} << "cannot convert [*0] to Buechi";
456+
// handled by rewite_sva_sequence
457+
return resultt{precedencet::ATOM, "0"};
452458
}
453459
else if(repetition.is_singleton())
454460
{
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*******************************************************************\
2+
3+
Module: Rewrite SVA Sequences
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include "rewrite_sva_sequence.h"
10+
11+
#include <util/arith_tools.h>
12+
#include <util/mathematical_types.h>
13+
14+
#include <verilog/sva_expr.h>
15+
16+
#include "temporal_logic.h"
17+
18+
/// 1800-2017 F.4.3
19+
/// Returns true iff the given SVA sequence admits an empty match.
20+
bool admits_empty(const exprt &expr)
21+
{
22+
PRECONDITION(expr.type().id() == ID_verilog_sva_sequence);
23+
PRECONDITION(is_SVA_sequence_operator(expr));
24+
25+
if(expr.id() == ID_sva_boolean)
26+
return false; // admits_empty(b) = 0
27+
else if(expr.id() == ID_sva_cycle_delay)
28+
{
29+
auto &cycle_delay = to_sva_cycle_delay_expr(expr);
30+
31+
if(cycle_delay.from().is_zero() && !cycle_delay.is_range())
32+
{
33+
// admits_empty((r1 ##0 r2)) = 0
34+
return false;
35+
}
36+
else
37+
{
38+
// admits_empty((r1 ##1 r2)) = admits_empty(r1) && admits_empty(r2)
39+
return cycle_delay.lhs().is_not_nil() &&
40+
admits_empty(cycle_delay.lhs()) && admits_empty(cycle_delay.rhs());
41+
}
42+
}
43+
else if(expr.id() == ID_sva_cycle_delay_star)
44+
{
45+
auto &cycle_delay = to_sva_cycle_delay_star_expr(expr);
46+
47+
return cycle_delay.lhs().is_not_nil() && admits_empty(cycle_delay.lhs()) &&
48+
admits_empty(cycle_delay.rhs());
49+
}
50+
else if(expr.id() == ID_sva_cycle_delay_plus)
51+
{
52+
auto &cycle_delay = to_sva_cycle_delay_plus_expr(expr);
53+
54+
return cycle_delay.lhs().is_not_nil() && admits_empty(cycle_delay.lhs()) &&
55+
admits_empty(cycle_delay.rhs());
56+
}
57+
else if(expr.id() == ID_sva_or)
58+
{
59+
// admits_empty((r1 or r2)) = admits_empty(r1) || admits_empty(r2)
60+
auto &or_expr = to_sva_or_expr(expr);
61+
return admits_empty(or_expr.lhs()) || admits_empty(or_expr.rhs());
62+
}
63+
else if(expr.id() == ID_sva_sequence_intersect)
64+
{
65+
// admits_empty((r1 intersect r2)) = admits_empty(r1) && admits_empty(r2)
66+
auto &intersect_expr = to_sva_sequence_intersect_expr(expr);
67+
return admits_empty(intersect_expr.lhs()) &&
68+
admits_empty(intersect_expr.rhs());
69+
}
70+
else if(expr.id() == ID_sva_sequence_first_match)
71+
{
72+
// admits_empty(first_match(r)) = admits_empty(r)
73+
auto &first_match_expr = to_sva_sequence_first_match_expr(expr);
74+
return admits_empty(first_match_expr.lhs()) &&
75+
admits_empty(first_match_expr.rhs());
76+
}
77+
else if(expr.id() == ID_sva_sequence_repetition_star)
78+
{
79+
// admits_empty(r[*0]) = 1
80+
// admits_empty(r[*1:$]) = admits_empty(r)
81+
auto &repetition_expr = to_sva_sequence_repetition_star_expr(expr);
82+
if(repetition_expr.is_range())
83+
{
84+
if(repetition_expr.from().is_zero())
85+
return true;
86+
else
87+
return admits_empty(repetition_expr.op());
88+
}
89+
else // singleton
90+
{
91+
if(repetition_expr.repetitions().is_zero())
92+
return true;
93+
else
94+
return admits_empty(repetition_expr.op());
95+
}
96+
}
97+
else if(
98+
expr.id() == ID_sva_sequence_goto_repetition ||
99+
expr.id() == ID_sva_sequence_non_consecutive_repetition)
100+
{
101+
return false;
102+
}
103+
else
104+
{
105+
DATA_INVARIANT(false, "unexpected SVA sequence: " + expr.id_string());
106+
}
107+
}
108+
109+
exprt rewrite_sva_sequence(exprt expr)
110+
{
111+
PRECONDITION(expr.type().id() == ID_verilog_sva_sequence);
112+
PRECONDITION(is_SVA_sequence_operator(expr));
113+
114+
if(expr.id() == ID_sva_cycle_delay)
115+
{
116+
// 1800-2017 16.9.2.1
117+
// - (empty ##0 seq) does not result in a match.
118+
// - (seq ##0 empty) does not result in a match.
119+
// - (empty ##n seq), where n is greater than 0, is equivalent to (##(n-1) seq).
120+
// - (seq ##n empty), where n is greater than 0, is equivalent to (seq ##(n-1) `true).
121+
auto &cycle_delay_expr = to_sva_cycle_delay_expr(expr);
122+
123+
bool lhs_admits_empty = cycle_delay_expr.lhs().is_not_nil() &&
124+
admits_empty(cycle_delay_expr.lhs());
125+
126+
bool rhs_admits_empty = admits_empty(cycle_delay_expr.rhs());
127+
128+
// apply recursively to operands
129+
if(cycle_delay_expr.lhs().is_not_nil())
130+
cycle_delay_expr.lhs() = rewrite_sva_sequence(cycle_delay_expr.lhs());
131+
132+
cycle_delay_expr.rhs() = rewrite_sva_sequence(cycle_delay_expr.rhs());
133+
134+
// consider empty match cases
135+
if(!lhs_admits_empty && !rhs_admits_empty)
136+
return cycle_delay_expr;
137+
138+
if(lhs_admits_empty)
139+
{
140+
if(cycle_delay_expr.is_range())
141+
{
142+
mp_integer from_int =
143+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
144+
DATA_INVARIANT(from_int >= 0, "delay must not be negative");
145+
abort();
146+
}
147+
else // singleton
148+
{
149+
mp_integer delay_int =
150+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
151+
DATA_INVARIANT(delay_int >= 0, "delay must not be negative");
152+
153+
// lhs ##0 rhs
154+
if(delay_int == 0)
155+
return cycle_delay_expr;
156+
else
157+
{
158+
// (empty ##n seq), where n is greater than 0, is equivalent to (##(n-1) seq).
159+
auto delay =
160+
from_integer(delay_int - 1, cycle_delay_expr.from().type());
161+
auto empty_match_case =
162+
sva_cycle_delay_exprt{delay, cycle_delay_expr.rhs()};
163+
return sva_or_exprt{empty_match_case, cycle_delay_expr, expr.type()};
164+
}
165+
}
166+
}
167+
else if(rhs_admits_empty)
168+
{
169+
if(cycle_delay_expr.is_range())
170+
{
171+
mp_integer from_int =
172+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
173+
DATA_INVARIANT(from_int >= 0, "delay must not be negative");
174+
abort();
175+
}
176+
else // singleton
177+
{
178+
mp_integer delay_int =
179+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
180+
DATA_INVARIANT(delay_int >= 0, "delay must not be negative");
181+
182+
// lhs ##0 rhs
183+
if(delay_int == 0)
184+
return cycle_delay_expr;
185+
else
186+
{
187+
// (seq ##n empty), where n is greater than 0, is equivalent to (seq ##(n-1) `true).
188+
auto delay =
189+
from_integer(delay_int - 1, cycle_delay_expr.from().type());
190+
auto empty_match_case = sva_cycle_delay_exprt{
191+
cycle_delay_expr.lhs(),
192+
delay,
193+
nil_exprt{},
194+
sva_boolean_exprt{true_exprt{}, expr.type()}};
195+
return sva_or_exprt{empty_match_case, cycle_delay_expr, expr.type()};
196+
}
197+
}
198+
}
199+
else // neither lhs nor rhs admit an empty match
200+
return cycle_delay_expr;
201+
}
202+
else if(expr.id() == ID_sva_sequence_repetition_star)
203+
{
204+
auto &repetition_expr = to_sva_sequence_repetition_star_expr(expr);
205+
repetition_expr.op() = rewrite_sva_sequence(repetition_expr.op());
206+
207+
if(repetition_expr.is_empty_match())
208+
{
209+
// Empty match. Leave as is. Now denotes "no match".
210+
}
211+
else if(!repetition_expr.repetitions_given())
212+
{
213+
// f [*] g ---> f [*1:$] g
214+
repetition_expr = sva_sequence_repetition_star_exprt{
215+
repetition_expr.op(),
216+
from_integer(1, integer_typet{}),
217+
infinity_exprt{integer_typet{}}};
218+
}
219+
else if(repetition_expr.is_range() && repetition_expr.from().is_zero())
220+
{
221+
// f [*0:x] g ---> f [*1:x] g
222+
repetition_expr.from() = from_integer(1, repetition_expr.from().type());
223+
}
224+
225+
return repetition_expr;
226+
}
227+
else if(expr.id() == ID_sva_sequence_repetition_plus)
228+
{
229+
auto &repetition_expr = to_sva_sequence_repetition_plus_expr(expr);
230+
repetition_expr.op() = rewrite_sva_sequence(repetition_expr.op());
231+
return repetition_expr;
232+
}
233+
else if(
234+
expr.id() == ID_sva_or || expr.id() == ID_sva_and ||
235+
expr.id() == ID_sva_sequence_intersect ||
236+
expr.id() == ID_sva_sequence_within)
237+
{
238+
// All operands are sequences
239+
for(auto &op : expr.operands())
240+
op = rewrite_sva_sequence(op);
241+
return expr;
242+
}
243+
else if(expr.id() == ID_sva_sequence_first_match)
244+
{
245+
auto &first_match = to_sva_sequence_first_match_expr(expr);
246+
first_match.sequence() = rewrite_sva_sequence(first_match.sequence());
247+
return first_match;
248+
}
249+
else if(expr.id() == ID_sva_cycle_delay_star)
250+
{
251+
auto &cycle_delay = to_sva_cycle_delay_star_expr(expr);
252+
if(cycle_delay.lhs().is_not_nil())
253+
cycle_delay.lhs() = rewrite_sva_sequence(cycle_delay.lhs());
254+
cycle_delay.rhs() = rewrite_sva_sequence(cycle_delay.rhs());
255+
return expr;
256+
}
257+
else if(expr.id() == ID_sva_cycle_delay_plus)
258+
{
259+
auto &cycle_delay = to_sva_cycle_delay_plus_expr(expr);
260+
if(cycle_delay.lhs().is_not_nil())
261+
cycle_delay.lhs() = rewrite_sva_sequence(cycle_delay.lhs());
262+
cycle_delay.rhs() = rewrite_sva_sequence(cycle_delay.rhs());
263+
return expr;
264+
}
265+
else if(expr.id() == ID_sva_sequence_throughout)
266+
{
267+
auto &throughout = to_sva_sequence_throughout_expr(expr);
268+
throughout.sequence() = rewrite_sva_sequence(throughout.sequence());
269+
return expr;
270+
}
271+
else if(
272+
expr.id() == ID_sva_sequence_goto_repetition ||
273+
expr.id() == ID_sva_sequence_non_consecutive_repetition)
274+
{
275+
// these take a Boolean as argument, not a sequence
276+
return expr;
277+
}
278+
else if(expr.id() == ID_sva_boolean)
279+
{
280+
return expr;
281+
}
282+
else
283+
{
284+
DATA_INVARIANT(false, "unexpected SVA sequence: " + expr.id_string());
285+
}
286+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*******************************************************************\
2+
3+
Module: Rewrite SVA Sequences
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_TEMPORAL_LOGICS_REWRITE_SVA_SEQUENCE_H
10+
#define CPROVER_TEMPORAL_LOGICS_REWRITE_SVA_SEQUENCE_H
11+
12+
#include <util/expr.h>
13+
14+
/// 1800-2017 F.4.3
15+
/// Returns true iff the given SVA sequence admits an empty match.
16+
bool admits_empty(const exprt &);
17+
18+
/// Implements the rewriting rules in 1800-2017 16.9.2.1.
19+
/// The resulting sequence expression do not admit empty matches.
20+
exprt rewrite_sva_sequence(exprt);
21+
22+
#endif // CPROVER_TEMPORAL_LOGICS_REWRITE_SVA_SEQUENCE_H

0 commit comments

Comments
 (0)