Skip to content

Commit 36c791b

Browse files
authored
Merge pull request #1098 from diffblue/extract-SVA_to_LTL
extract `SVA_to_LTL(exprt)` into a separate file
2 parents c6314a7 + b3f4646 commit 36c791b

File tree

3 files changed

+360
-345
lines changed

3 files changed

+360
-345
lines changed

src/temporal-logic/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ SRC = ctl.cpp \
22
ltl.cpp \
33
nnf.cpp \
44
normalize_property.cpp \
5+
sva_to_ltl.cpp \
56
temporal_logic.cpp \
67
trivial_sva.cpp \
78
#empty line

src/temporal-logic/sva_to_ltl.cpp

+359
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
/*******************************************************************\
2+
3+
Module: SVA to LTL
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include <util/arith_tools.h>
10+
11+
#include <verilog/sva_expr.h>
12+
13+
#include "ltl.h"
14+
#include "temporal_logic.h"
15+
16+
static exprt n_Xes(mp_integer n, exprt op)
17+
{
18+
PRECONDITION(n >= 0);
19+
if(n == 0)
20+
return op;
21+
else
22+
return n_Xes(n - 1, X_exprt{std::move(op)});
23+
}
24+
25+
// Returns a set of match conditions (given as LTL formula)
26+
struct ltl_sequence_matcht
27+
{
28+
ltl_sequence_matcht(exprt __cond, mp_integer __length)
29+
: cond(std::move(__cond)), length(std::move(__length))
30+
{
31+
PRECONDITION(length >= 0);
32+
}
33+
34+
exprt cond; // LTL
35+
mp_integer length; // match_end - match_start + 1
36+
37+
bool empty() const
38+
{
39+
return length == 0;
40+
}
41+
};
42+
43+
std::vector<ltl_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
44+
{
45+
if(!is_SVA_sequence_operator(sequence))
46+
{
47+
// atomic proposition
48+
return {{sequence, 1}};
49+
}
50+
else if(sequence.id() == ID_sva_sequence_concatenation)
51+
{
52+
auto &concatenation = to_sva_sequence_concatenation_expr(sequence);
53+
auto matches_lhs = LTL_sequence_matches(concatenation.lhs());
54+
auto matches_rhs = LTL_sequence_matches(concatenation.rhs());
55+
56+
if(matches_lhs.empty() || matches_rhs.empty())
57+
return {};
58+
59+
std::vector<ltl_sequence_matcht> result;
60+
// cross product
61+
for(auto &match_lhs : matches_lhs)
62+
for(auto &match_rhs : matches_rhs)
63+
{
64+
// Concatenation is overlapping, hence deduct one from
65+
// the length.
66+
auto delay = match_lhs.length - 1;
67+
auto rhs_delayed = n_Xes(delay, match_rhs.cond);
68+
result.emplace_back(
69+
and_exprt{match_lhs.cond, rhs_delayed},
70+
match_lhs.length + match_rhs.length - 1);
71+
}
72+
return result;
73+
}
74+
else if(sequence.id() == ID_sva_cycle_delay)
75+
{
76+
auto &delay = to_sva_cycle_delay_expr(sequence);
77+
auto matches = LTL_sequence_matches(delay.op());
78+
auto from_int = numeric_cast_v<mp_integer>(delay.from());
79+
80+
if(matches.empty())
81+
return {};
82+
83+
if(delay.to().is_nil())
84+
{
85+
for(auto &match : matches)
86+
{
87+
// delay as instructed
88+
match.length += from_int;
89+
match.cond = n_Xes(from_int, match.cond);
90+
}
91+
return matches;
92+
}
93+
else if(delay.to().id() == ID_infinity)
94+
{
95+
return {}; // can't encode
96+
}
97+
else if(delay.to().is_constant())
98+
{
99+
auto to_int = numeric_cast_v<mp_integer>(to_constant_expr(delay.to()));
100+
std::vector<ltl_sequence_matcht> new_matches;
101+
102+
for(mp_integer i = from_int; i <= to_int; ++i)
103+
{
104+
for(const auto &match : matches)
105+
{
106+
// delay as instructed
107+
auto new_match = match;
108+
new_match.length += from_int;
109+
new_match.cond = n_Xes(i, match.cond);
110+
new_matches.push_back(std::move(new_match));
111+
}
112+
}
113+
114+
return new_matches;
115+
}
116+
else
117+
return {};
118+
}
119+
else
120+
{
121+
return {}; // unsupported
122+
}
123+
}
124+
125+
/// takes an SVA property as input, and returns an equivalent LTL property,
126+
/// or otherwise {}.
127+
std::optional<exprt> SVA_to_LTL(exprt expr)
128+
{
129+
// Some SVA is directly mappable to LTL
130+
if(expr.id() == ID_sva_always)
131+
{
132+
auto rec = SVA_to_LTL(to_sva_always_expr(expr).op());
133+
if(rec.has_value())
134+
return G_exprt{rec.value()};
135+
else
136+
return {};
137+
}
138+
else if(expr.id() == ID_sva_ranged_always)
139+
{
140+
auto &ranged_always = to_sva_ranged_always_expr(expr);
141+
auto rec = SVA_to_LTL(ranged_always.op());
142+
if(rec.has_value())
143+
{
144+
// always [l:u] op ---> X ... X (op ∧ X op ∧ ... ∧ X ... X op)
145+
auto lower_int = numeric_cast_v<mp_integer>(ranged_always.lower());
146+
147+
// Is there an upper end of the range?
148+
if(ranged_always.upper().is_constant())
149+
{
150+
// upper end set
151+
auto upper_int =
152+
numeric_cast_v<mp_integer>(to_constant_expr(ranged_always.upper()));
153+
PRECONDITION(upper_int >= lower_int);
154+
auto diff = upper_int - lower_int;
155+
156+
exprt::operandst conjuncts;
157+
158+
for(auto i = 0; i <= diff; i++)
159+
conjuncts.push_back(n_Xes(i, rec.value()));
160+
161+
return n_Xes(lower_int, conjunction(conjuncts));
162+
}
163+
else if(ranged_always.upper().id() == ID_infinity)
164+
{
165+
// always [l:$] op ---> X ... X G op
166+
return n_Xes(lower_int, G_exprt{rec.value()});
167+
}
168+
else
169+
PRECONDITION(false);
170+
}
171+
else
172+
return {};
173+
}
174+
else if(expr.id() == ID_sva_s_always)
175+
{
176+
auto &ranged_always = to_sva_s_always_expr(expr);
177+
auto rec = SVA_to_LTL(ranged_always.op());
178+
if(rec.has_value())
179+
{
180+
// s_always [l:u] op ---> X ... X (op ∧ X op ∧ ... ∧ X ... X op)
181+
auto lower_int = numeric_cast_v<mp_integer>(ranged_always.lower());
182+
auto upper_int = numeric_cast_v<mp_integer>(ranged_always.upper());
183+
PRECONDITION(upper_int >= lower_int);
184+
auto diff = upper_int - lower_int;
185+
186+
exprt::operandst conjuncts;
187+
188+
for(auto i = 0; i <= diff; i++)
189+
conjuncts.push_back(n_Xes(i, rec.value()));
190+
191+
return n_Xes(lower_int, conjunction(conjuncts));
192+
}
193+
else
194+
return {};
195+
}
196+
else if(expr.id() == ID_sva_s_eventually)
197+
{
198+
auto rec = SVA_to_LTL(to_sva_s_eventually_expr(expr).op());
199+
if(rec.has_value())
200+
return F_exprt{rec.value()};
201+
else
202+
return {};
203+
}
204+
else if(expr.id() == ID_sva_s_nexttime)
205+
{
206+
auto rec = SVA_to_LTL(to_sva_s_nexttime_expr(expr).op());
207+
if(rec.has_value())
208+
return X_exprt{rec.value()};
209+
else
210+
return {};
211+
}
212+
else if(expr.id() == ID_sva_nexttime)
213+
{
214+
auto rec = SVA_to_LTL(to_sva_nexttime_expr(expr).op());
215+
if(rec.has_value())
216+
return X_exprt{rec.value()};
217+
else
218+
return {};
219+
}
220+
else if(
221+
expr.id() == ID_sva_overlapped_implication ||
222+
expr.id() == ID_sva_non_overlapped_implication)
223+
{
224+
auto &implication = to_sva_implication_base_expr(expr);
225+
auto matches = LTL_sequence_matches(implication.sequence());
226+
227+
if(matches.empty())
228+
return {};
229+
230+
// All matches must be followed
231+
// by the property being true
232+
exprt::operandst conjuncts;
233+
234+
auto property_rec = SVA_to_LTL(implication.property());
235+
236+
if(!property_rec.has_value())
237+
return {};
238+
239+
for(auto &match : matches)
240+
{
241+
const auto overlapped = expr.id() == ID_sva_overlapped_implication;
242+
if(match.empty() && overlapped)
243+
{
244+
// ignore the empty match
245+
}
246+
else
247+
{
248+
auto delay = match.length + (overlapped ? 0 : 1) - 1;
249+
auto delayed_property = n_Xes(delay, property_rec.value());
250+
conjuncts.push_back(implies_exprt{match.cond, delayed_property});
251+
}
252+
}
253+
254+
return conjunction(conjuncts);
255+
}
256+
else if(
257+
expr.id() == ID_sva_nonoverlapped_followed_by ||
258+
expr.id() == ID_sva_overlapped_followed_by)
259+
{
260+
auto &followed_by = to_sva_followed_by_expr(expr);
261+
auto matches = LTL_sequence_matches(followed_by.sequence());
262+
263+
if(matches.empty())
264+
return {};
265+
266+
// There must be at least one match that is followed
267+
// by the property being true
268+
exprt::operandst disjuncts;
269+
270+
auto property_rec = SVA_to_LTL(followed_by.property());
271+
272+
if(!property_rec.has_value())
273+
return {};
274+
275+
for(auto &match : matches)
276+
{
277+
const auto overlapped = expr.id() == ID_sva_overlapped_followed_by;
278+
if(match.empty() && overlapped)
279+
{
280+
// ignore the empty match
281+
}
282+
else
283+
{
284+
auto delay = match.length + (overlapped ? 0 : 1) - 1;
285+
auto delayed_property = n_Xes(delay, property_rec.value());
286+
disjuncts.push_back(and_exprt{match.cond, delayed_property});
287+
}
288+
}
289+
290+
return disjunction(disjuncts);
291+
}
292+
else if(expr.id() == ID_sva_sequence_property)
293+
{
294+
// should have been turned into sva_implicit_weak or sva_implicit_strong
295+
PRECONDITION(false);
296+
}
297+
else if(
298+
expr.id() == ID_sva_weak || expr.id() == ID_sva_strong ||
299+
expr.id() == ID_sva_implicit_weak || expr.id() == ID_sva_implicit_strong)
300+
{
301+
auto &sequence = to_sva_sequence_property_expr_base(expr).sequence();
302+
303+
// evaluates to true if there's at least one non-empty match of the sequence
304+
auto matches = LTL_sequence_matches(sequence);
305+
306+
if(matches.empty())
307+
return {};
308+
309+
exprt::operandst disjuncts;
310+
311+
for(auto &match : matches)
312+
{
313+
if(!match.empty())
314+
disjuncts.push_back(match.cond);
315+
}
316+
317+
return disjunction(disjuncts);
318+
}
319+
else if(expr.id() == ID_sva_s_until)
320+
{
321+
auto &until = to_sva_s_until_expr(expr);
322+
auto rec_lhs = SVA_to_LTL(until.lhs());
323+
auto rec_rhs = SVA_to_LTL(until.rhs());
324+
if(rec_lhs.has_value() && rec_rhs.has_value())
325+
return U_exprt{rec_lhs.value(), rec_rhs.value()};
326+
else
327+
return {};
328+
}
329+
else if(expr.id() == ID_sva_s_until_with)
330+
{
331+
// This is release with swapped operands
332+
auto &until_with = to_sva_s_until_with_expr(expr);
333+
auto rec_lhs = SVA_to_LTL(until_with.lhs());
334+
auto rec_rhs = SVA_to_LTL(until_with.rhs());
335+
if(rec_lhs.has_value() && rec_rhs.has_value())
336+
return R_exprt{rec_rhs.value(), rec_lhs.value()}; // swapped
337+
else
338+
return {};
339+
}
340+
else if(!has_temporal_operator(expr))
341+
{
342+
return expr;
343+
}
344+
else if(
345+
expr.id() == ID_and || expr.id() == ID_implies || expr.id() == ID_or ||
346+
expr.id() == ID_not)
347+
{
348+
for(auto &op : expr.operands())
349+
{
350+
auto rec = SVA_to_LTL(op);
351+
if(!rec.has_value())
352+
return {};
353+
op = rec.value();
354+
}
355+
return expr;
356+
}
357+
else
358+
return {};
359+
}

0 commit comments

Comments
 (0)