Skip to content

Commit 833ecb5

Browse files
committed
SVA to LTL now converts SVA sequences
This adds support for mapping some SVA sequences to equivalent LTL.
1 parent 78d038d commit 833ecb5

File tree

4 files changed

+220
-14
lines changed

4 files changed

+220
-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

+169-14
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,82 @@ 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+
PRECONDITION(length >= 0);
234+
}
235+
236+
exprt cond; // LTL
237+
mp_integer length; // match_end - match_start + 1
238+
239+
bool empty() const
240+
{
241+
return length == 0;
242+
}
243+
};
244+
245+
std::vector<ltl_sequence_matcht> LTL_sequence_matches(const exprt &sequence)
246+
{
247+
if(!is_SVA_sequence_operator(sequence))
248+
{
249+
// atomic proposition
250+
return {{sequence, 1}};
251+
}
252+
else if(sequence.id() == ID_sva_sequence_concatenation)
253+
{
254+
auto &concatenation = to_sva_sequence_concatenation_expr(sequence);
255+
auto matches_lhs = LTL_sequence_matches(concatenation.lhs());
256+
auto matches_rhs = LTL_sequence_matches(concatenation.rhs());
257+
258+
if(matches_lhs.empty() || matches_rhs.empty())
259+
return {};
260+
261+
std::vector<ltl_sequence_matcht> result;
262+
// cross product
263+
for(auto &match_lhs : matches_lhs)
264+
for(auto &match_rhs : matches_rhs)
265+
{
266+
auto rhs_delayed = n_Xes(match_lhs.length, match_rhs.cond);
267+
result.emplace_back(
268+
and_exprt{match_lhs.cond, rhs_delayed},
269+
match_lhs.length + match_rhs.length);
270+
}
271+
return result;
272+
}
273+
else if(sequence.id() == ID_sva_cycle_delay)
274+
{
275+
auto &delay = to_sva_cycle_delay_expr(sequence);
276+
auto matches = LTL_sequence_matches(delay.op());
277+
auto from_int = numeric_cast_v<mp_integer>(delay.from());
278+
279+
if(matches.empty())
280+
return {};
281+
282+
if(delay.to().is_nil())
283+
{
284+
for(auto &match : matches)
285+
{
286+
// delay as instructed
287+
match.length += from_int;
288+
match.cond = n_Xes(from_int, match.cond);
289+
}
290+
return matches;
291+
}
292+
else
293+
return {};
294+
}
295+
else
296+
{
297+
return {}; // unsupported
298+
}
299+
}
300+
301+
/// takes an SVA property as input, and returns an equivalent LTL property,
302+
/// or otherwise {}.
227303
std::optional<exprt> SVA_to_LTL(exprt expr)
228304
{
229305
// Some SVA is directly mappable to LTL
@@ -317,25 +393,104 @@ std::optional<exprt> SVA_to_LTL(exprt expr)
317393
else
318394
return {};
319395
}
320-
else if(expr.id() == ID_sva_overlapped_implication)
396+
else if(
397+
expr.id() == ID_sva_overlapped_implication ||
398+
expr.id() == ID_sva_non_overlapped_implication)
321399
{
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
400+
auto &implication = to_sva_implication_base_expr(expr);
401+
auto matches = LTL_sequence_matches(implication.sequence());
402+
403+
if(matches.empty())
328404
return {};
405+
406+
// All matches must be followed
407+
// by the property being true
408+
exprt::operandst conjuncts;
409+
410+
auto property_rec = SVA_to_LTL(implication.property());
411+
412+
if(!property_rec.has_value())
413+
return {};
414+
415+
for(auto &match : matches)
416+
{
417+
if(match.empty() && expr.id() == ID_sva_overlapped_followed_by)
418+
{
419+
// ignore the empty match
420+
}
421+
else
422+
{
423+
auto delay =
424+
match.length + (expr.id() == ID_sva_non_overlapped_implication ? 1 : 0) - 1;
425+
auto delayed_property = n_Xes(delay, property_rec.value());
426+
conjuncts.push_back(implies_exprt{match.cond, delayed_property});
427+
}
428+
}
429+
430+
return conjunction(conjuncts);
329431
}
330-
else if(expr.id() == ID_sva_non_overlapped_implication)
432+
else if(
433+
expr.id() == ID_sva_nonoverlapped_followed_by ||
434+
expr.id() == ID_sva_overlapped_followed_by)
331435
{
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
436+
auto &followed_by = to_sva_followed_by_expr(expr);
437+
auto matches = LTL_sequence_matches(followed_by.sequence());
438+
439+
if(matches.empty())
440+
return {};
441+
442+
// There must be at least one match that is followed
443+
// by the property being true
444+
exprt::operandst disjuncts;
445+
446+
auto property_rec = SVA_to_LTL(followed_by.property());
447+
448+
if(!property_rec.has_value())
338449
return {};
450+
451+
for(auto &match : matches)
452+
{
453+
if(match.empty() && expr.id() == ID_sva_overlapped_followed_by)
454+
{
455+
// ignore the empty match
456+
}
457+
else
458+
{
459+
auto delay =
460+
match.length + (expr.id() == ID_sva_nonoverlapped_followed_by ? 1 : 0) - 1;
461+
auto delayed_property = n_Xes(delay, property_rec.value());
462+
disjuncts.push_back(and_exprt{match.cond, delayed_property});
463+
}
464+
}
465+
466+
return disjunction(disjuncts);
467+
}
468+
else if(expr.id() == ID_sva_sequence_property)
469+
{
470+
// should have been turned into sva_implicit_weak or sva_implicit_strong
471+
PRECONDITION(false);
472+
}
473+
else if(
474+
expr.id() == ID_sva_weak || expr.id() == ID_sva_strong ||
475+
expr.id() == ID_sva_implicit_weak || expr.id() == ID_sva_implicit_strong)
476+
{
477+
auto &sequence = to_sva_sequence_property_expr_base(expr).sequence();
478+
479+
// evaluates to true if there's at least one non-empty match of the sequence
480+
auto matches = LTL_sequence_matches(sequence);
481+
482+
if(matches.empty())
483+
return {};
484+
485+
exprt::operandst disjuncts;
486+
487+
for(auto &match : matches)
488+
{
489+
if(!match.empty())
490+
disjuncts.push_back(match.cond);
491+
}
492+
493+
return disjunction(disjuncts);
339494
}
340495
else if(!has_temporal_operator(expr))
341496
{

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)