Skip to content

BDD engine: transform SVA to LTL, then LTL to CTL #1096

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions regression/verilog/SVA/followed-by1.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
followed-by1.sv
--bdd
^\[main\.p0\] main\.x == 0 #=# \(main\.x == 1 #=# main\.x == 2\): PROVED$
^\[main\.p1\] main\.x == 0 #-# \(\(##1 main\.x == 1\) #-# \(##1 main\.x == 2\)\): PROVED$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
--
10 changes: 10 additions & 0 deletions regression/verilog/SVA/followed-by2.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
followed-by2.sv
--bdd
^\[main\.p0\] always \(\(main\.a #-# main\.b\) iff \(not \(main\.a |-> \(not main\.b\)\)\)\): PROVED$
^\[main\.p1\] always \(\(main\.a #=# main\.b\) iff \(not \(main\.a |=> \(not main\.b\)\)\)\): FAILURE: property not supported by BDD engine$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
11 changes: 11 additions & 0 deletions regression/verilog/SVA/followed-by4.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CORE
followed-by4.sv
--bdd
^\[main\.p1\] always \(main\.x == 0 #-# 1\): REFUTED$
^\[main\.p2\] \(1 or \(##2 1\)\) #-# main\.x == 2: FAILURE: property not supported by BDD engine$
^\[main\.p3\] \(1 or \(##2 1\)\) #-# main\.x == 0: FAILURE: property not supported by BDD engine$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
14 changes: 14 additions & 0 deletions regression/verilog/SVA/initial1.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CORE
initial1.sv
--bdd
^\[main\.p0\] main\.counter == 0: PROVED$
^\[main\.p1\] main\.counter == 100: REFUTED$
^\[main\.p2\] ##1 main\.counter == 1: PROVED$
^\[main\.p3\] ##1 main\.counter == 100: REFUTED$
^\[main\.p4\] s_nexttime main\.counter == 1: PROVED$
^\[main\.p5\] always \[1:1\] main\.counter == 1: PROVED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
11 changes: 11 additions & 0 deletions regression/verilog/SVA/nexttime1.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CORE
nexttime1.sv
--bdd
^\[main\.p1\] s_nexttime\[0\] main\.counter == 0: PROVED$
^\[main\.p2\] s_nexttime\[1\] main\.counter == 1: PROVED$
^\[main\.p3\] nexttime\[8\] main\.counter == 8: PROVED$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
--
10 changes: 10 additions & 0 deletions regression/verilog/SVA/restrict1.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
restrict1.sv
--bdd
^\[main\.p0\] always main\.x != 5: ASSUMED$
^\[main\.p1\] always main\.x != 6: PROVED$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
--
8 changes: 8 additions & 0 deletions regression/verilog/SVA/s_eventually1.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CORE
s_eventually1.sv
--bdd
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
8 changes: 8 additions & 0 deletions regression/verilog/SVA/sequence4.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CORE
sequence4.sv
--bdd
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
--
13 changes: 13 additions & 0 deletions regression/verilog/SVA/sequence_and1.bdd.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CORE
sequence_and1.sv
--bdd
^\[main\.p0\] main\.x == 0 and main\.x == 1: REFUTED$
^\[main\.p1\] strong\(main\.x == 0 and main\.x == 1\): REFUTED$
^\[main\.p2\] main\.x == 0 and \(nexttime main\.x == 1\): PROVED$
^\[main\.p3\] \(nexttime main\.x == 1\) and main\.x == 0: PROVED$
^\[main\.p4\] \(main\.x == 0 and main\.x != 10\) |=> main\.x == 1: PROVED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
4 changes: 2 additions & 2 deletions regression/verilog/SVA/sequence_repetition1.sv
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module main(input clk);

reg [31:0] x = 0;
reg [7:0] x = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do the changes to property transforms entail this change? AFAIK this test was passing before.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They now do run with BDDs, and the BDDs simply don't scale to 32 bits. They can do 8.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then how did this test pass before?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did SAT-based BMC only. That can do 32 bits.


// 0 1 2 3 4 ...
always_ff @(posedge clk)
x<=x+1;

// 0 0 1 1 2 2 3 3 ...
wire [31:0] half_x = x/2;
wire [7:0] half_x = x/2;

// should pass
initial p0: assert property (half_x==0[*2]);
Expand Down
4 changes: 2 additions & 2 deletions regression/verilog/SVA/sequence_repetition2.sv
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module main(input clk);

reg [31:0] x = 0;
reg [7:0] x = 0;

// 0 1 2 3 4 ...
always_ff @(posedge clk)
x<=x+1;

// 0 0 1 1 2 2 3 3 ...
wire [31:0] half_x = x/2;
wire [7:0] half_x = x/2;

// should pass
initial p0: assert property (x==0[*]);
Expand Down
4 changes: 2 additions & 2 deletions regression/verilog/SVA/sequence_repetition3.sv
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module main(input clk);

reg [31:0] x = 0;
reg [7:0] x = 0;

// 0 1 2 3 4 ...
always_ff @(posedge clk)
x<=x+1;

// 0 0 1 1 2 2 3 3 ...
wire [31:0] half_x = x/2;
wire [7:0] half_x = x/2;

// should pass
initial p0: assert property (x==0[*1] #=# x==1[*1]);
Expand Down
3 changes: 1 addition & 2 deletions regression/verilog/system-functions/past1.bdd.desc
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
CORE
past1.sv
--bdd
^\[main\.p0\] ##0 \$past\(main\.counter, 0\) == 0: FAILURE: property not supported by BDD engine$
^EXIT=10$
^EXIT=0$
^SIGNAL=0$
--
2 changes: 1 addition & 1 deletion regression/verilog/system-functions/past1.sv
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module main(input clk);

reg [31:0] counter = 0;
reg [7:0] counter = 0;

always @(posedge clk)
counter++;
Expand Down
92 changes: 26 additions & 66 deletions src/ebmc/bdd_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ Author: Daniel Kroening, [email protected]
#include <trans-netlist/instantiate_netlist.h>
#include <trans-netlist/trans_trace_netlist.h>
#include <trans-netlist/unwind_netlist.h>
#include <verilog/sva_expr.h>

#include "netlist.h"

Expand Down Expand Up @@ -66,7 +65,7 @@ class bdd_enginet
const namespacet ns;
netlistt netlist;

static bool property_supported(const exprt &);
static std::optional<exprt> property_supported(const exprt &);

// the Manager must appear before any BDDs
// to do the cleanup in the right order
Expand Down Expand Up @@ -199,9 +198,20 @@ property_checker_resultt bdd_enginet::operator()()
<< ", nodes: " << netlist.number_of_nodes()
<< messaget::eom;

for(const auto &property : properties.properties)
if(property_supported(property.normalized_expr))
get_atomic_propositions(property.normalized_expr);
for(auto &property : properties.properties)
{
if(!property.is_disabled() && !property.is_assumed())
{
auto converted_opt = property_supported(property.normalized_expr);
if(converted_opt.has_value())
{
property.normalized_expr = *converted_opt;
get_atomic_propositions(*converted_opt);
}
else
property.failure("property not supported by BDD engine");
}
}

message.status() << "Building BDD for netlist" << messaget::eom;

Expand Down Expand Up @@ -449,40 +459,29 @@ Function: bdd_enginet::property_supported

\*******************************************************************/

bool bdd_enginet::property_supported(const exprt &expr)
std::optional<exprt> bdd_enginet::property_supported(const exprt &expr)
{
// Our engine knows all of CTL.
if(is_CTL(expr))
return true;
return expr;

if(is_LTL(expr))
{
// We can map selected path properties to CTL.
return LTL_to_CTL(expr).has_value();
return LTL_to_CTL(expr);
}

if(is_SVA(expr))
{
if(
expr.id() == ID_sva_always &&
!has_temporal_operator(to_sva_always_expr(expr).op()))
{
// always p
return true;
}

if(
expr.id() == ID_sva_always &&
to_sva_always_expr(expr).op().id() == ID_sva_s_eventually &&
!has_temporal_operator(
to_sva_s_eventually_expr(to_sva_always_expr(expr).op()).op()))
{
// always s_eventually p
return true;
}
// We can map some SVA to LTL. In turn, some of that can be mapped to CTL.
auto ltl_opt = SVA_to_LTL(expr);
if(ltl_opt.has_value())
return property_supported(ltl_opt.value());
else
return {};
}

return false;
return {};
}

/*******************************************************************\
Expand All @@ -505,51 +504,12 @@ void bdd_enginet::check_property(propertyt &property)
if(property.is_assumed())
return;

if(!property_supported(property.normalized_expr))
{
property.failure("property not supported by BDD engine");
if(property.is_failure())
return;
}

message.status() << "Checking " << property.name << messaget::eom;
property.status=propertyt::statust::UNKNOWN;

if(
property.normalized_expr.id() == ID_sva_always &&
to_sva_always_expr(property.normalized_expr).op().id() ==
ID_sva_s_eventually &&
!has_temporal_operator(to_sva_s_eventually_expr(
to_sva_always_expr(property.normalized_expr).op())
.op()))
{
// always s_eventually p --> AG AF p
auto p = to_sva_s_eventually_expr(
to_sva_always_expr(property.normalized_expr).op())
.op();
property.normalized_expr = AG_exprt{AF_exprt{p}};
}

if(
property.normalized_expr.id() == ID_sva_always &&
!has_temporal_operator(to_sva_always_expr(property.normalized_expr).op()))
{
// always p --> AG p
auto p = to_sva_always_expr(property.normalized_expr).op();
property.normalized_expr = AG_exprt{p};
}

// Our engine knows CTL only.
// We map selected path properties to CTL.
if(
has_temporal_operator(property.normalized_expr) &&
is_LTL(property.normalized_expr))
{
auto CTL_opt = LTL_to_CTL(property.normalized_expr);
CHECK_RETURN(CTL_opt.has_value());
property.normalized_expr = CTL_opt.value();
CHECK_RETURN(is_CTL(property.normalized_expr));
}

if(is_AGp(property.normalized_expr))
{
check_AGp(property);
Expand Down
Loading