Skip to content

Commit fe29963

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

File tree

4 files changed

+199
-14
lines changed

4 files changed

+199
-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$
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

+148-14
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,76 @@ static exprt n_Xes(mp_integer n, exprt op)
224224
return n_Xes(n - 1, X_exprt{std::move(op)});
225225
}
226226

227+
// Returns a set of match conditions (given as LTL formula)
228+
struct ltl_sequence_matcht
229+
{
230+
ltl_sequence_matcht(exprt __cond, mp_integer __length)
231+
: cond(std::move(__cond)), length(std::move(__length))
232+
{
233+
}
234+
235+
exprt cond; // LTL
236+
mp_integer length; // match_end - match_start
237+
};
238+
239+
std::vector<ltl_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
240+
{
241+
if(!is_SVA_sequence_operator(sequence))
242+
{
243+
// atomic proposition
244+
return {{sequence, 0}};
245+
}
246+
else if(sequence.id() == ID_sva_sequence_concatenation)
247+
{
248+
auto &concatenation = to_sva_sequence_concatenation_expr(sequence);
249+
auto matches_lhs = LTL_sequence_matches(concatenation.lhs());
250+
auto matches_rhs = LTL_sequence_matches(concatenation.rhs());
251+
252+
if(matches_lhs.empty() || matches_rhs.empty())
253+
return {};
254+
255+
std::vector<ltl_sequence_matcht> result;
256+
// cross product
257+
for(auto &match_lhs : matches_lhs)
258+
for(auto &match_rhs : matches_rhs)
259+
{
260+
auto rhs_delayed = n_Xes(match_lhs.length, match_rhs.cond);
261+
result.emplace_back(
262+
and_exprt{match_lhs.cond, rhs_delayed},
263+
match_lhs.length + match_rhs.length);
264+
}
265+
return result;
266+
}
267+
else if(sequence.id() == ID_sva_cycle_delay)
268+
{
269+
auto &delay = to_sva_cycle_delay_expr(sequence);
270+
auto matches = LTL_sequence_matches(delay.op());
271+
auto from_int = numeric_cast_v<mp_integer>(delay.from());
272+
273+
if(matches.empty())
274+
return {};
275+
276+
if(delay.to().is_nil())
277+
{
278+
for(auto &match : matches)
279+
{
280+
// delay as instructed
281+
match.length += from_int;
282+
match.cond = n_Xes(from_int, match.cond);
283+
}
284+
return matches;
285+
}
286+
else
287+
return {};
288+
}
289+
else
290+
{
291+
return {}; // unsupported
292+
}
293+
}
294+
295+
/// takes an SVA property as input, and returns an equivalent LTL property,
296+
/// or otherwise {}.
227297
std::optional<exprt> SVA_to_LTL(exprt expr)
228298
{
229299
// Some SVA is directly mappable to LTL
@@ -317,25 +387,89 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
317387
else
318388
return {};
319389
}
320-
else if(expr.id() == ID_sva_overlapped_implication)
390+
else if(
391+
expr.id() == ID_sva_overlapped_implication ||
392+
expr.id() == ID_sva_non_overlapped_implication)
321393
{
322-
auto &implication = to_sva_overlapped_implication_expr(expr);
323-
auto rec_lhs = SVA_to_LTL(implication.lhs());
324-
auto rec_rhs = SVA_to_LTL(implication.rhs());
325-
if(rec_lhs.has_value() && rec_rhs.has_value())
326-
return implies_exprt{rec_lhs.value(), rec_rhs.value()};
327-
else
394+
auto &implication = to_sva_implication_base_expr(expr);
395+
auto matches = LTL_sequence_matches(implication.sequence());
396+
397+
if(matches.empty())
398+
return {};
399+
400+
// All matches must be followed
401+
// by the property being true
402+
exprt::operandst conjuncts;
403+
404+
auto property_rec = SVA_to_LTL(implication.property());
405+
406+
if(!property_rec.has_value())
328407
return {};
408+
409+
for(auto &match : matches)
410+
{
411+
auto delay =
412+
match.length + (expr.id() == ID_sva_non_overlapped_implication ? 1 : 0);
413+
auto delayed_property = n_Xes(delay, property_rec.value());
414+
conjuncts.push_back(implies_exprt{match.cond, delayed_property});
415+
}
416+
417+
return conjunction(conjuncts);
329418
}
330-
else if(expr.id() == ID_sva_non_overlapped_implication)
419+
else if(
420+
expr.id() == ID_sva_nonoverlapped_followed_by ||
421+
expr.id() == ID_sva_overlapped_followed_by)
331422
{
332-
auto &implication = to_sva_non_overlapped_implication_expr(expr);
333-
auto rec_lhs = SVA_to_LTL(implication.lhs());
334-
auto rec_rhs = SVA_to_LTL(implication.rhs());
335-
if(rec_lhs.has_value() && rec_rhs.has_value())
336-
return implies_exprt{rec_lhs.value(), X_exprt{rec_rhs.value()}};
337-
else
423+
auto &followed_by = to_sva_followed_by_expr(expr);
424+
auto matches = LTL_sequence_matches(followed_by.sequence());
425+
426+
if(matches.empty())
427+
return {};
428+
429+
// There must be at least one match that is followed
430+
// by the property being true
431+
exprt::operandst disjuncts;
432+
433+
auto property_rec = SVA_to_LTL(followed_by.property());
434+
435+
if(!property_rec.has_value())
338436
return {};
437+
438+
for(auto &match : matches)
439+
{
440+
auto delay =
441+
match.length + (expr.id() == ID_sva_nonoverlapped_followed_by ? 1 : 0);
442+
auto delayed_property = n_Xes(delay, property_rec.value());
443+
disjuncts.push_back(and_exprt{match.cond, delayed_property});
444+
}
445+
446+
return disjunction(disjuncts);
447+
}
448+
else if(expr.id() == ID_sva_sequence_property)
449+
{
450+
// should have been turned into sva_implicit_weak or sva_implicit_strong
451+
PRECONDITION(false);
452+
}
453+
else if(
454+
expr.id() == ID_sva_weak || expr.id() == ID_sva_strong ||
455+
expr.id() == ID_sva_implicit_weak || expr.id() == ID_sva_implicit_strong)
456+
{
457+
auto &sequence = to_sva_sequence_property_expr_base(expr).sequence();
458+
459+
// evaluates to true if there's at least one match of the sequence
460+
auto matches = LTL_sequence_matches(sequence);
461+
462+
if(matches.empty())
463+
return {};
464+
465+
exprt::operandst disjuncts;
466+
467+
for(auto &match : matches)
468+
{
469+
disjuncts.push_back(match.cond);
470+
}
471+
472+
return disjunction(disjuncts);
339473
}
340474
else if(!has_temporal_operator(expr))
341475
{

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)