Skip to content

Commit 6643d61

Browse files
authored
Merge pull request #1066 from diffblue/SVA_sequence_to_LTL
SVA to LTL now converts SVA sequences
2 parents 5e8e457 + ec7e96a commit 6643d61

File tree

8 files changed

+280
-14
lines changed

8 files changed

+280
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
cycle_delay1.sv
3+
--smv-netlist
4+
^LTLSPEC node22 & X node25 & X X node28 & X X X node31$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module main(input clk);
2+
3+
reg [3:0] x = 0;
4+
5+
always_ff @(posedge clk)
6+
x++;
7+
8+
initial assert property (x == 0 ##1 x == 1 ##1 x == 2 ##1 x == 3);
9+
10+
endmodule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
cycle_delay2.sv
3+
--smv-netlist
4+
^LTLSPEC X node22 \| X X node25$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module main(input clk);
2+
3+
reg [3:0] x = 0;
4+
5+
always_ff @(posedge clk)
6+
x++;
7+
8+
initial assert property (##[1:2] x == 2);
9+
10+
endmodule
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
cycle_delay2.sv
3+
--bound 10
4+
^\[.*\] ##\[1:2\] main\.x == 2: PROVED up to bound 10$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module main(input clk);
2+
3+
reg [3:0] x = 0;
4+
5+
always_ff @(posedge clk)
6+
x++;
7+
8+
initial assert property (##[1:2] x == 2);
9+
10+
endmodule

src/temporal-logic/temporal_logic.cpp

+195-14
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,108 @@ static exprt n_Xes(mp_integer n, exprt op)
225225
return n_Xes(n - 1, X_exprt{std::move(op)});
226226
}
227227

228+
// Returns a set of match conditions (given as LTL formula)
229+
struct ltl_sequence_matcht
230+
{
231+
ltl_sequence_matcht(exprt __cond, mp_integer __length)
232+
: cond(std::move(__cond)), length(std::move(__length))
233+
{
234+
PRECONDITION(length >= 0);
235+
}
236+
237+
exprt cond; // LTL
238+
mp_integer length; // match_end - match_start + 1
239+
240+
bool empty() const
241+
{
242+
return length == 0;
243+
}
244+
};
245+
246+
std::vector<ltl_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
247+
{
248+
if(!is_SVA_sequence_operator(sequence))
249+
{
250+
// atomic proposition
251+
return {{sequence, 1}};
252+
}
253+
else if(sequence.id() == ID_sva_sequence_concatenation)
254+
{
255+
auto &concatenation = to_sva_sequence_concatenation_expr(sequence);
256+
auto matches_lhs = LTL_sequence_matches(concatenation.lhs());
257+
auto matches_rhs = LTL_sequence_matches(concatenation.rhs());
258+
259+
if(matches_lhs.empty() || matches_rhs.empty())
260+
return {};
261+
262+
std::vector<ltl_sequence_matcht> result;
263+
// cross product
264+
for(auto &match_lhs : matches_lhs)
265+
for(auto &match_rhs : matches_rhs)
266+
{
267+
// Concatenation is overlapping, hence deduct one from
268+
// the length.
269+
auto delay = match_lhs.length - 1;
270+
auto rhs_delayed = n_Xes(delay, match_rhs.cond);
271+
result.emplace_back(
272+
and_exprt{match_lhs.cond, rhs_delayed},
273+
match_lhs.length + match_rhs.length - 1);
274+
}
275+
return result;
276+
}
277+
else if(sequence.id() == ID_sva_cycle_delay)
278+
{
279+
auto &delay = to_sva_cycle_delay_expr(sequence);
280+
auto matches = LTL_sequence_matches(delay.op());
281+
auto from_int = numeric_cast_v<mp_integer>(delay.from());
282+
283+
if(matches.empty())
284+
return {};
285+
286+
if(delay.to().is_nil())
287+
{
288+
for(auto &match : matches)
289+
{
290+
// delay as instructed
291+
match.length += from_int;
292+
match.cond = n_Xes(from_int, match.cond);
293+
}
294+
return matches;
295+
}
296+
else if(delay.to().id() == ID_infinity)
297+
{
298+
return {}; // can't encode
299+
}
300+
else if(delay.to().is_constant())
301+
{
302+
auto to_int = numeric_cast_v<mp_integer>(to_constant_expr(delay.to()));
303+
std::vector<ltl_sequence_matcht> new_matches;
304+
305+
for(mp_integer i = from_int; i <= to_int; ++i)
306+
{
307+
for(const auto &match : matches)
308+
{
309+
// delay as instructed
310+
auto new_match = match;
311+
new_match.length += from_int;
312+
new_match.cond = n_Xes(i, match.cond);
313+
new_matches.push_back(std::move(new_match));
314+
}
315+
}
316+
317+
return new_matches;
318+
}
319+
else
320+
return {};
321+
}
322+
else
323+
{
324+
return {}; // unsupported
325+
}
326+
}
327+
328+
/// takes an SVA property as input, and returns an equivalent LTL property,
329+
/// or otherwise {}.
228330
std::optional<exprt> SVA_to_LTL(exprt expr)
229331
{
230332
// Some SVA is directly mappable to LTL
@@ -318,25 +420,104 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
318420
else
319421
return {};
320422
}
321-
else if(expr.id() == ID_sva_overlapped_implication)
423+
else if(
424+
expr.id() == ID_sva_overlapped_implication ||
425+
expr.id() == ID_sva_non_overlapped_implication)
322426
{
323-
auto &implication = to_sva_overlapped_implication_expr(expr);
324-
auto rec_lhs = SVA_to_LTL(implication.lhs());
325-
auto rec_rhs = SVA_to_LTL(implication.rhs());
326-
if(rec_lhs.has_value() && rec_rhs.has_value())
327-
return implies_exprt{rec_lhs.value(), rec_rhs.value()};
328-
else
427+
auto &implication = to_sva_implication_base_expr(expr);
428+
auto matches = LTL_sequence_matches(implication.sequence());
429+
430+
if(matches.empty())
431+
return {};
432+
433+
// All matches must be followed
434+
// by the property being true
435+
exprt::operandst conjuncts;
436+
437+
auto property_rec = SVA_to_LTL(implication.property());
438+
439+
if(!property_rec.has_value())
329440
return {};
441+
442+
for(auto &match : matches)
443+
{
444+
const auto overlapped = expr.id() == ID_sva_overlapped_implication;
445+
if(match.empty() && overlapped)
446+
{
447+
// ignore the empty match
448+
}
449+
else
450+
{
451+
auto delay = match.length + (overlapped ? 0 : 1) - 1;
452+
auto delayed_property = n_Xes(delay, property_rec.value());
453+
conjuncts.push_back(implies_exprt{match.cond, delayed_property});
454+
}
455+
}
456+
457+
return conjunction(conjuncts);
330458
}
331-
else if(expr.id() == ID_sva_non_overlapped_implication)
459+
else if(
460+
expr.id() == ID_sva_nonoverlapped_followed_by ||
461+
expr.id() == ID_sva_overlapped_followed_by)
332462
{
333-
auto &implication = to_sva_non_overlapped_implication_expr(expr);
334-
auto rec_lhs = SVA_to_LTL(implication.lhs());
335-
auto rec_rhs = SVA_to_LTL(implication.rhs());
336-
if(rec_lhs.has_value() && rec_rhs.has_value())
337-
return implies_exprt{rec_lhs.value(), X_exprt{rec_rhs.value()}};
338-
else
463+
auto &followed_by = to_sva_followed_by_expr(expr);
464+
auto matches = LTL_sequence_matches(followed_by.sequence());
465+
466+
if(matches.empty())
467+
return {};
468+
469+
// There must be at least one match that is followed
470+
// by the property being true
471+
exprt::operandst disjuncts;
472+
473+
auto property_rec = SVA_to_LTL(followed_by.property());
474+
475+
if(!property_rec.has_value())
339476
return {};
477+
478+
for(auto &match : matches)
479+
{
480+
const auto overlapped = expr.id() == ID_sva_overlapped_followed_by;
481+
if(match.empty() && overlapped)
482+
{
483+
// ignore the empty match
484+
}
485+
else
486+
{
487+
auto delay = match.length + (overlapped ? 0 : 1) - 1;
488+
auto delayed_property = n_Xes(delay, property_rec.value());
489+
disjuncts.push_back(and_exprt{match.cond, delayed_property});
490+
}
491+
}
492+
493+
return disjunction(disjuncts);
494+
}
495+
else if(expr.id() == ID_sva_sequence_property)
496+
{
497+
// should have been turned into sva_implicit_weak or sva_implicit_strong
498+
PRECONDITION(false);
499+
}
500+
else if(
501+
expr.id() == ID_sva_weak || expr.id() == ID_sva_strong ||
502+
expr.id() == ID_sva_implicit_weak || expr.id() == ID_sva_implicit_strong)
503+
{
504+
auto &sequence = to_sva_sequence_property_expr_base(expr).sequence();
505+
506+
// evaluates to true if there's at least one non-empty match of the sequence
507+
auto matches = LTL_sequence_matches(sequence);
508+
509+
if(matches.empty())
510+
return {};
511+
512+
exprt::operandst disjuncts;
513+
514+
for(auto &match : matches)
515+
{
516+
if(!match.empty())
517+
disjuncts.push_back(match.cond);
518+
}
519+
520+
return disjunction(disjuncts);
340521
}
341522
else if(expr.id() == ID_sva_s_until)
342523
{

src/verilog/sva_expr.h

+34
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,16 @@ class sva_implication_base_exprt : public binary_predicate_exprt
663663
return lhs();
664664
}
665665

666+
const exprt &sequence() const
667+
{
668+
return op0();
669+
}
670+
671+
exprt &sequence()
672+
{
673+
return op0();
674+
}
675+
666676
// a property
667677
const exprt &consequent() const
668678
{
@@ -673,8 +683,32 @@ class sva_implication_base_exprt : public binary_predicate_exprt
673683
{
674684
return rhs();
675685
}
686+
687+
const exprt &property() const
688+
{
689+
return op1();
690+
}
691+
692+
exprt &property()
693+
{
694+
return op1();
695+
}
676696
};
677697

698+
static inline const sva_implication_base_exprt &
699+
to_sva_implication_base_expr(const exprt &expr)
700+
{
701+
sva_implication_base_exprt::check(expr);
702+
return static_cast<const sva_implication_base_exprt &>(expr);
703+
}
704+
705+
static inline sva_implication_base_exprt &
706+
to_sva_implication_base_expr(exprt &expr)
707+
{
708+
sva_implication_base_exprt::check(expr);
709+
return static_cast<sva_implication_base_exprt &>(expr);
710+
}
711+
678712
/// |->
679713
class sva_overlapped_implication_exprt : public sva_implication_base_exprt
680714
{

0 commit comments

Comments
 (0)