Skip to content

Commit 359da23

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 cfe36b1 commit 359da23

File tree

7 files changed

+323
-13
lines changed

7 files changed

+323
-13
lines changed

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 \
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
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+
13+
#include <verilog/sva_expr.h>
14+
15+
#include "temporal_logic.h"
16+
17+
/// 1800-2017 F.4.3
18+
/// Returns true iff the given SVA sequence admits an empty match.
19+
bool admits_empty(const exprt &expr)
20+
{
21+
PRECONDITION(expr.type().id() == ID_verilog_sva_sequence);
22+
PRECONDITION(is_SVA_sequence_operator(expr));
23+
24+
if(expr.id() == ID_sva_boolean)
25+
return false; // admits_empty(b) = 0
26+
else if(expr.id() == ID_sva_cycle_delay)
27+
{
28+
auto &cycle_delay = to_sva_cycle_delay_expr(expr);
29+
30+
if(cycle_delay.from().is_zero() && !cycle_delay.is_range())
31+
{
32+
// admits_empty((r1 ##0 r2)) = 0
33+
return false;
34+
}
35+
else
36+
{
37+
// admits_empty((r1 ##1 r2)) = admits_empty(r1) && admits_empty(r2)
38+
return cycle_delay.lhs().is_not_nil() &&
39+
admits_empty(cycle_delay.lhs()) && admits_empty(cycle_delay.rhs());
40+
}
41+
}
42+
else if(expr.id() == ID_sva_cycle_delay_star)
43+
{
44+
auto &cycle_delay = to_sva_cycle_delay_star_expr(expr);
45+
46+
return cycle_delay.lhs().is_not_nil() &&
47+
admits_empty(cycle_delay.lhs()) && admits_empty(cycle_delay.rhs());
48+
}
49+
else if(expr.id() == ID_sva_cycle_delay_plus)
50+
{
51+
auto &cycle_delay = to_sva_cycle_delay_plus_expr(expr);
52+
53+
return cycle_delay.lhs().is_not_nil() &&
54+
admits_empty(cycle_delay.lhs()) && admits_empty(cycle_delay.rhs());
55+
}
56+
else if(expr.id() == ID_sva_or)
57+
{
58+
// admits_empty((r1 or r2)) = admits_empty(r1) || admits_empty(r2)
59+
auto &or_expr = to_sva_or_expr(expr);
60+
return admits_empty(or_expr.lhs()) || admits_empty(or_expr.rhs());
61+
}
62+
else if(expr.id() == ID_sva_sequence_intersect)
63+
{
64+
// admits_empty((r1 intersect r2)) = admits_empty(r1) && admits_empty(r2)
65+
auto &intersect_expr = to_sva_sequence_intersect_expr(expr);
66+
return admits_empty(intersect_expr.lhs()) &&
67+
admits_empty(intersect_expr.rhs());
68+
}
69+
else if(expr.id() == ID_sva_sequence_first_match)
70+
{
71+
// admits_empty(first_match(r)) = admits_empty(r)
72+
auto &first_match_expr = to_sva_sequence_first_match_expr(expr);
73+
return admits_empty(first_match_expr.lhs()) &&
74+
admits_empty(first_match_expr.rhs());
75+
}
76+
else if(expr.id() == ID_sva_sequence_repetition_star)
77+
{
78+
// admits_empty(r[*0]) = 1
79+
// admits_empty(r[*1:$]) = admits_empty(r)
80+
auto &repetition_expr = to_sva_sequence_repetition_star_expr(expr);
81+
if(repetition_expr.is_range())
82+
{
83+
if(repetition_expr.from().is_zero())
84+
return true;
85+
else
86+
return admits_empty(repetition_expr.op());
87+
}
88+
else // singleton
89+
{
90+
if(repetition_expr.repetitions().is_zero())
91+
return true;
92+
else
93+
return admits_empty(repetition_expr.op());
94+
}
95+
}
96+
else if(
97+
expr.id() == ID_sva_sequence_goto_repetition ||
98+
expr.id() == ID_sva_sequence_non_consecutive_repetition)
99+
{
100+
return false;
101+
}
102+
else
103+
{
104+
DATA_INVARIANT(false, "unexpected SVA sequence: " + expr.id_string());
105+
}
106+
}
107+
108+
exprt rewrite_sva_sequence(exprt expr)
109+
{
110+
PRECONDITION(expr.type().id() == ID_verilog_sva_sequence);
111+
PRECONDITION(is_SVA_sequence_operator(expr));
112+
113+
if(expr.id() == ID_sva_cycle_delay)
114+
{
115+
// 1800-2017 16.9.2.1
116+
// - (empty ##0 seq) does not result in a match.
117+
// - (seq ##0 empty) does not result in a match.
118+
// - (empty ##n seq), where n is greater than 0, is equivalent to (##(n-1) seq).
119+
// - (seq ##n empty), where n is greater than 0, is equivalent to (seq ##(n-1) `true).
120+
auto &cycle_delay_expr = to_sva_cycle_delay_expr(expr);
121+
122+
bool lhs_admits_empty = cycle_delay_expr.lhs().is_not_nil() &&
123+
admits_empty(cycle_delay_expr.lhs());
124+
125+
bool rhs_admits_empty = admits_empty(cycle_delay_expr.rhs());
126+
127+
// apply recursively to operands
128+
if(cycle_delay_expr.lhs().is_not_nil())
129+
cycle_delay_expr.lhs() = rewrite_sva_sequence(cycle_delay_expr.lhs());
130+
131+
cycle_delay_expr.rhs() = rewrite_sva_sequence(cycle_delay_expr.rhs());
132+
133+
// consider empty match cases
134+
if(!lhs_admits_empty && !rhs_admits_empty)
135+
return cycle_delay_expr;
136+
137+
if(lhs_admits_empty)
138+
{
139+
if(cycle_delay_expr.is_range())
140+
{
141+
mp_integer from_int =
142+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
143+
DATA_INVARIANT(from_int >= 0, "delay must not be negative");
144+
abort();
145+
}
146+
else // singleton
147+
{
148+
mp_integer delay_int =
149+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
150+
DATA_INVARIANT(delay_int >= 0, "delay must not be negative");
151+
152+
// lhs ##0 rhs
153+
if(delay_int == 0)
154+
return cycle_delay_expr;
155+
else
156+
{
157+
// (empty ##n seq), where n is greater than 0, is equivalent to (##(n-1) seq).
158+
auto delay =
159+
from_integer(delay_int - 1, cycle_delay_expr.from().type());
160+
auto empty_match_case =
161+
sva_cycle_delay_exprt{delay, cycle_delay_expr.rhs()};
162+
return sva_or_exprt{empty_match_case, cycle_delay_expr, expr.type()};
163+
}
164+
}
165+
}
166+
else if(rhs_admits_empty)
167+
{
168+
if(cycle_delay_expr.is_range())
169+
{
170+
mp_integer from_int =
171+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
172+
DATA_INVARIANT(from_int >= 0, "delay must not be negative");
173+
abort();
174+
}
175+
else
176+
{
177+
mp_integer delay_int =
178+
numeric_cast_v<mp_integer>(cycle_delay_expr.from());
179+
DATA_INVARIANT(delay_int >= 0, "delay must not be negative");
180+
181+
// lhs ##0 rhs
182+
if(delay_int == 0)
183+
return cycle_delay_expr;
184+
else
185+
{
186+
// (seq ##n empty), where n is greater than 0, is equivalent to (seq ##(n-1) `true).
187+
auto delay =
188+
from_integer(delay_int - 1, cycle_delay_expr.from().type());
189+
auto empty_match_case = sva_cycle_delay_exprt{
190+
cycle_delay_expr.lhs(),
191+
delay,
192+
nil_exprt{},
193+
sva_boolean_exprt{true_exprt{}, expr.type()}};
194+
return sva_or_exprt{empty_match_case, cycle_delay_expr, expr.type()};
195+
}
196+
}
197+
}
198+
else // neither lhs nor rhs admit an empty match
199+
return cycle_delay_expr;
200+
}
201+
else if(expr.id() == ID_sva_sequence_repetition_star)
202+
{
203+
auto &repetition_expr = to_sva_sequence_repetition_star_expr(expr);
204+
repetition_expr.op() = rewrite_sva_sequence(repetition_expr.op());
205+
return repetition_expr;
206+
}
207+
else if(
208+
expr.id() == ID_sva_or || expr.id() == ID_sva_and ||
209+
expr.id() == ID_sva_sequence_intersect ||
210+
expr.id() == ID_sva_sequence_repetition_plus || expr.id() == ID_sva_sequence_within)
211+
{
212+
// All operands are sequences
213+
for(auto &op : expr.operands())
214+
op = rewrite_sva_sequence(op);
215+
return expr;
216+
}
217+
else if(expr.id() == ID_sva_sequence_first_match)
218+
{
219+
auto &first_match = to_sva_sequence_first_match_expr(expr);
220+
first_match.sequence() = rewrite_sva_sequence(first_match.sequence());
221+
return first_match;
222+
}
223+
else if(expr.id() == ID_sva_cycle_delay_star)
224+
{
225+
auto &cycle_delay = to_sva_cycle_delay_star_expr(expr);
226+
if(cycle_delay.lhs().is_not_nil())
227+
cycle_delay.lhs() = rewrite_sva_sequence(cycle_delay.lhs());
228+
cycle_delay.rhs() = rewrite_sva_sequence(cycle_delay.rhs());
229+
return expr;
230+
}
231+
else if(expr.id() == ID_sva_cycle_delay_plus)
232+
{
233+
auto &cycle_delay = to_sva_cycle_delay_plus_expr(expr);
234+
if(cycle_delay.lhs().is_not_nil())
235+
cycle_delay.lhs() = rewrite_sva_sequence(cycle_delay.lhs());
236+
cycle_delay.rhs() = rewrite_sva_sequence(cycle_delay.rhs());
237+
return expr;
238+
}
239+
else if(expr.id() == ID_sva_sequence_throughout)
240+
{
241+
auto &throughout = to_sva_sequence_throughout_expr(expr);
242+
throughout.sequence() = rewrite_sva_sequence(throughout.sequence());
243+
return expr;
244+
}
245+
else if(
246+
expr.id() == ID_sva_sequence_goto_repetition ||
247+
expr.id() == ID_sva_sequence_non_consecutive_repetition)
248+
{
249+
// these take a Boolean as argument, not a sequence
250+
return expr;
251+
}
252+
else if(expr.id() == ID_sva_boolean)
253+
{
254+
return expr;
255+
}
256+
else
257+
{
258+
DATA_INVARIANT(false, "unexpected SVA sequence: " + expr.id_string());
259+
}
260+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
/// Implements the rewriting rules in 1800-2017 16.9.2.1.
15+
/// The resulting sequence expression do not admit empty matches.
16+
exprt rewrite_sva_sequence(exprt);
17+
18+
#endif // CPROVER_TEMPORAL_LOGICS_REWRITE_SVA_SEQUENCE_H

src/temporal-logic/sva_sequence_match.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Author: Daniel Kroening, [email protected]
1313

1414
#include <verilog/sva_expr.h>
1515

16+
#include "rewrite_sva_sequence.h"
17+
1618
sva_sequence_matcht sva_sequence_matcht::true_match(const mp_integer &n)
1719
{
1820
sva_sequence_matcht result;
@@ -52,7 +54,7 @@ overlapping_concat(sva_sequence_matcht a, sva_sequence_matcht b)
5254
return concat(std::move(a), b);
5355
}
5456

55-
std::vector<sva_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
57+
std::vector<sva_sequence_matcht> sva_sequence_matches_rec(const exprt &sequence)
5658
{
5759
if(sequence.id() == ID_sva_boolean)
5860
{
@@ -62,7 +64,7 @@ std::vector<sva_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
6264
else if(sequence.id() == ID_sva_sequence_repetition_star) // [*n], [*n:m]
6365
{
6466
auto &repetition = to_sva_sequence_repetition_star_expr(sequence);
65-
auto matches_op = LTL_sequence_matches(repetition.op());
67+
auto matches_op = sva_sequence_matches_rec(repetition.op());
6668

6769
std::vector<sva_sequence_matcht> result;
6870

@@ -164,8 +166,8 @@ std::vector<sva_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
164166
// 3. The end time of the composite sequence is
165167
// the end time of the operand sequence that completes last.
166168
auto &and_expr = to_sva_and_expr(sequence);
167-
auto matches_lhs = LTL_sequence_matches(and_expr.lhs());
168-
auto matches_rhs = LTL_sequence_matches(and_expr.rhs());
169+
auto matches_lhs = sva_sequence_matches_rec(and_expr.lhs());
170+
auto matches_rhs = sva_sequence_matches_rec(and_expr.rhs());
169171

170172
std::vector<sva_sequence_matcht> result;
171173

@@ -201,7 +203,7 @@ std::vector<sva_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
201203

202204
for(auto &op : to_sva_or_expr(sequence).operands())
203205
{
204-
auto op_matches = LTL_sequence_matches(op);
206+
auto op_matches = sva_sequence_matches_rec(op);
205207
if(op_matches.empty())
206208
throw sva_sequence_match_unsupportedt{sequence}; // not supported
207209
for(auto &match : op_matches)
@@ -215,3 +217,10 @@ std::vector<sva_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
215217
throw sva_sequence_match_unsupportedt{sequence}; // not supported
216218
}
217219
}
220+
221+
std::vector<sva_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
222+
{
223+
// rewrite, then do recursion
224+
auto rewritten = rewrite_sva_sequence(sequence);
225+
return sva_sequence_matches_rec(rewritten);
226+
}

src/temporal-logic/temporal_logic.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ bool is_SVA_sequence_operator(const exprt &expr)
112112
id == ID_sva_sequence_goto_repetition ||
113113
id == ID_sva_sequence_non_consecutive_repetition ||
114114
id == ID_sva_sequence_repetition_star ||
115-
id == ID_sva_sequence_repetition_plus;
115+
id == ID_sva_sequence_repetition_plus || id == ID_sva_boolean;
116116
}
117117

118118
bool is_SVA_operator(const exprt &expr)

src/verilog/sva_expr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ exprt sva_sequence_repetition_star_exprt::lower() const
118118
{
119119
auto n_expr = from_integer(n, integer_typet{});
120120
result = sva_or_exprt{
121-
std::move(result), sva_sequence_repetition_star_exprt{op(), n_expr}};
121+
std::move(result),
122+
sva_sequence_repetition_star_exprt{op(), n_expr},
123+
verilog_sva_sequence_typet{}};
122124
}
123125

124126
return result;

0 commit comments

Comments
 (0)