Skip to content

Commit 65c1a05

Browse files
committed
SVA to LTL now converts SVA sequences
This adds support for mapping some SVA sequences to equivalent LTL.
1 parent aac75f1 commit 65c1a05

File tree

4 files changed

+195
-15
lines changed

4 files changed

+195
-15
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$
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);
9+
10+
endmodule

src/temporal-logic/temporal_logic.cpp

+144-15
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ bool is_SVA_operator(const exprt &expr)
132132
id == ID_sva_overlapped_implication ||
133133
id == ID_sva_non_overlapped_implication ||
134134
id == ID_sva_overlapped_followed_by ||
135-
id == ID_sva_nonoverlapped_followed_by;
135+
id == ID_sva_nonoverlapped_followed_by ||
136+
id == ID_sva_sequence_property || id == ID_sva_weak ||
137+
id == ID_sva_strong;
136138
}
137139

138140
bool is_SVA(const exprt &expr)
@@ -221,6 +223,76 @@ static exprt n_Xes(mp_integer n, exprt op)
221223
return n_Xes(n - 1, X_exprt{std::move(op)});
222224
}
223225

226+
// Returns a set of match conditions (given as LTL formula)
227+
struct ltl_sequence_matcht
228+
{
229+
ltl_sequence_matcht(exprt __cond, mp_integer __length)
230+
: cond(std::move(__cond)), length(std::move(__length))
231+
{
232+
}
233+
234+
exprt cond; // LTL
235+
mp_integer length; // match_end - match_start
236+
};
237+
238+
std::vector<ltl_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
239+
{
240+
if(!is_SVA_sequence_operator(sequence))
241+
{
242+
// atomic proposition
243+
return {{sequence, 0}};
244+
}
245+
else if(sequence.id() == ID_sva_sequence_concatenation)
246+
{
247+
auto &concatenation = to_sva_sequence_concatenation_expr(sequence);
248+
auto matches_lhs = LTL_sequence_matches(concatenation.lhs());
249+
auto matches_rhs = LTL_sequence_matches(concatenation.rhs());
250+
251+
if(matches_lhs.empty() || matches_rhs.empty())
252+
return {};
253+
254+
std::vector<ltl_sequence_matcht> result;
255+
// cross product
256+
for(auto &match_lhs : matches_lhs)
257+
for(auto &match_rhs : matches_rhs)
258+
{
259+
auto rhs_delayed = n_Xes(match_lhs.length, match_rhs.cond);
260+
result.emplace_back(
261+
and_exprt{match_lhs.cond, rhs_delayed},
262+
match_lhs.length + match_rhs.length);
263+
}
264+
return result;
265+
}
266+
else if(sequence.id() == ID_sva_cycle_delay)
267+
{
268+
auto &delay = to_sva_cycle_delay_expr(sequence);
269+
auto matches = LTL_sequence_matches(delay.op());
270+
auto from_int = numeric_cast_v<mp_integer>(delay.from());
271+
272+
if(matches.empty())
273+
return {};
274+
275+
if(delay.to().is_nil())
276+
{
277+
for(auto &match : matches)
278+
{
279+
// delay as instructed
280+
match.length += from_int;
281+
match.cond = n_Xes(from_int, match.cond);
282+
}
283+
return matches;
284+
}
285+
else
286+
return {};
287+
}
288+
else
289+
{
290+
return {}; // unsupported
291+
}
292+
}
293+
294+
/// takes an SVA property as input, and returns an equivalent LTL property,
295+
/// or otherwise {}.
224296
std::optional<exprt> SVA_to_LTL(exprt expr)
225297
{
226298
// Some SVA is directly mappable to LTL
@@ -314,25 +386,82 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
314386
else
315387
return {};
316388
}
317-
else if(expr.id() == ID_sva_overlapped_implication)
389+
else if(
390+
expr.id() == ID_sva_overlapped_implication ||
391+
expr.id() == ID_sva_non_overlapped_implication)
318392
{
319-
auto &implication = to_sva_overlapped_implication_expr(expr);
320-
auto rec_lhs = SVA_to_LTL(implication.lhs());
321-
auto rec_rhs = SVA_to_LTL(implication.rhs());
322-
if(rec_lhs.has_value() && rec_rhs.has_value())
323-
return implies_exprt{rec_lhs.value(), rec_rhs.value()};
324-
else
393+
auto &implication = to_sva_implication_base_expr(expr);
394+
auto matches = LTL_sequence_matches(implication.sequence());
395+
396+
if(matches.empty())
397+
return {};
398+
399+
// All matches must be followed
400+
// by the property being true
401+
exprt::operandst conjuncts;
402+
403+
auto property_rec = SVA_to_LTL(implication.property());
404+
405+
if(!property_rec.has_value())
325406
return {};
407+
408+
for(auto &match : matches)
409+
{
410+
auto delay =
411+
match.length + (expr.id() == ID_sva_non_overlapped_implication ? 1 : 0);
412+
auto delayed_property = n_Xes(delay, property_rec.value());
413+
conjuncts.push_back(implies_exprt{match.cond, delayed_property});
414+
}
415+
416+
return conjunction(conjuncts);
326417
}
327-
else if(expr.id() == ID_sva_non_overlapped_implication)
418+
else if(
419+
expr.id() == ID_sva_nonoverlapped_followed_by ||
420+
expr.id() == ID_sva_overlapped_followed_by)
328421
{
329-
auto &implication = to_sva_non_overlapped_implication_expr(expr);
330-
auto rec_lhs = SVA_to_LTL(implication.lhs());
331-
auto rec_rhs = SVA_to_LTL(implication.rhs());
332-
if(rec_lhs.has_value() && rec_rhs.has_value())
333-
return implies_exprt{rec_lhs.value(), X_exprt{rec_rhs.value()}};
334-
else
422+
auto &followed_by = to_sva_followed_by_expr(expr);
423+
auto matches = LTL_sequence_matches(followed_by.sequence());
424+
425+
if(matches.empty())
335426
return {};
427+
428+
// There must be at least one match that is followed
429+
// by the property being true
430+
exprt::operandst disjuncts;
431+
432+
auto property_rec = SVA_to_LTL(followed_by.property());
433+
434+
if(!property_rec.has_value())
435+
return {};
436+
437+
for(auto &match : matches)
438+
{
439+
auto delay =
440+
match.length + (expr.id() == ID_sva_nonoverlapped_followed_by ? 1 : 0);
441+
auto delayed_property = n_Xes(delay, property_rec.value());
442+
disjuncts.push_back(and_exprt{match.cond, delayed_property});
443+
}
444+
445+
return disjunction(disjuncts);
446+
}
447+
else if(expr.id() == ID_sva_sequence_property)
448+
{
449+
auto &sequence = to_sva_sequence_property_expr(expr).sequence();
450+
451+
// evaluates to true if there's at least one match of the sequence
452+
auto matches = LTL_sequence_matches(sequence);
453+
454+
if(matches.empty())
455+
return {};
456+
457+
exprt::operandst disjuncts;
458+
459+
for(auto &match : matches)
460+
{
461+
disjuncts.push_back(match.cond);
462+
}
463+
464+
return disjunction(disjuncts);
336465
}
337466
else if(!has_temporal_operator(expr))
338467
{

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)