Skip to content

Commit

Permalink
Merge pull request #263 from jix/pdr-X
Browse files Browse the repository at this point in the history
Support for "abc --keep-going pdr" via new "pdr -X" mode
  • Loading branch information
jix authored Mar 6, 2024
2 parents 5c649c8 + b6e41a3 commit 0c84510
Show file tree
Hide file tree
Showing 14 changed files with 756 additions and 98 deletions.
45 changes: 45 additions & 0 deletions sbysrc/sby.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from sby_cmdline import parser_func
from sby_core import SbyConfig, SbyTask, SbyAbort, SbyTaskloop, process_filename, dress_message
from sby_jobserver import SbyJobClient, process_jobserver_environment
from sby_status import SbyStatusDb
import time, platform, click

process_jobserver_environment() # needs to be called early
Expand Down Expand Up @@ -55,6 +56,39 @@
sequential = args.sequential
jobcount = args.jobcount
init_config_file = args.init_config_file
status_show = args.status
status_reset = args.status_reset

if status_show or status_reset:
target = workdir_prefix or workdir or sbyfile
if not os.path.isdir(target) and target.endswith('.sby'):
target = target[:-4]
if not os.path.isdir(target):
print(f"ERROR: No directory found at {target!r}.", file=sys.stderr)
sys.exit(1)

try:
with open(f"{target}/status.path", "r") as status_path_file:
status_path = f"{target}/{status_path_file.read().rstrip()}"
except FileNotFoundError:
status_path = f"{target}/status.sqlite"

if not os.path.exists(status_path):
print(f"ERROR: No status database found at {status_path!r}.", file=sys.stderr)
sys.exit(1)

status_db = SbyStatusDb(status_path, task=None)

if status_show:
status_db.print_status_summary()
sys.exit(0)

if status_reset:
status_db.reset()

status_db.db.close()
sys.exit(0)


if sbyfile is not None:
if os.path.isdir(sbyfile):
Expand Down Expand Up @@ -356,6 +390,7 @@ def start_task(taskloop, taskname):

my_opt_tmpdir = opt_tmpdir
my_workdir = None
my_status_db = None

if workdir is not None:
my_workdir = workdir
Expand All @@ -364,10 +399,12 @@ def start_task(taskloop, taskname):
my_workdir = workdir_prefix
else:
my_workdir = workdir_prefix + "_" + taskname
my_status_db = f"../{os.path.basename(workdir_prefix)}/status.sqlite"

if my_workdir is None and sbyfile is not None and not my_opt_tmpdir:
my_workdir = sbyfile[:-4]
if taskname is not None:
my_status_db = f"../{os.path.basename(my_workdir)}/status.sqlite"
my_workdir += "_" + taskname

if my_workdir is not None:
Expand Down Expand Up @@ -399,6 +436,14 @@ def start_task(taskloop, taskname):
with open(f"{my_workdir}/.gitignore", "w") as gitignore:
print("*", file=gitignore)

if my_status_db is not None:
os.makedirs(f"{my_workdir}/{os.path.dirname(my_status_db)}", exist_ok=True)
if os.getenv("SBY_WORKDIR_GITIGNORE"):
with open(f"{my_workdir}/{os.path.dirname(my_status_db)}/.gitignore", "w") as gitignore:
print("*", file=gitignore)
with open(f"{my_workdir}/status.path", "w") as status_path:
print(my_status_db, file=status_path)

junit_ts_name = os.path.basename(sbyfile[:-4]) if sbyfile is not None else workdir if workdir is not None else "stdin"
junit_tc_name = taskname if taskname is not None else "default"

Expand Down
1 change: 1 addition & 0 deletions sbysrc/sby_autotune.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ def log(self, message):

def run(self):
self.task.handle_non_engine_options()
self.task.setup_status_db(':memory:')
self.config = self.task.autotune_config or SbyAutotuneConfig()

if "expect" not in self.task.options:
Expand Down
5 changes: 5 additions & 0 deletions sbysrc/sby_cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ def parser_func():
parser.add_argument("--setup", action="store_true", dest="setupmode",
help="set up the working directory and exit")

parser.add_argument("--status", action="store_true", dest="status",
help="summarize the contents of the status database")
parser.add_argument("--statusreset", action="store_true", dest="status_reset",
help="reset the contents of the status database")

parser.add_argument("--init-config-file", dest="init_config_file",
help="create a default .sby config file")
parser.add_argument("sbyfile", metavar="<jobname>.sby | <dirname>", nargs="?",
Expand Down
60 changes: 55 additions & 5 deletions sbysrc/sby_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from select import select
from time import monotonic, localtime, sleep, strftime
from sby_design import SbyProperty, SbyModule, design_hierarchy
from sby_status import SbyStatusDb

all_procs_running = []

Expand Down Expand Up @@ -674,20 +675,41 @@ def engine_summary(self, engine_idx):
self.engine_summaries[engine_idx] = SbyEngineSummary(engine_idx)
return self.engine_summaries[engine_idx]

def add_event(self, *args, **kwargs):
def add_event(self, *args, update_status=True, **kwargs):
event = SbySummaryEvent(*args, **kwargs)

engine = self.engine_summary(event.engine_idx)

if update_status:
status_metadata = dict(source="summary_event", engine=engine.engine)

if event.prop:
if event.type == "$assert":
event.prop.status = "FAIL"
if event.path:
event.prop.tracefiles.append(event.path)
if update_status:
self.task.status_db.add_task_property_data(
event.prop,
"trace",
data=dict(path=event.path, step=event.step, **status_metadata),
)
if event.prop:
if event.type == "$cover":
event.prop.status = "PASS"
if event.path:
event.prop.tracefiles.append(event.path)

engine = self.engine_summary(event.engine_idx)
if update_status:
self.task.status_db.add_task_property_data(
event.prop,
"trace",
data=dict(path=event.path, step=event.step, **status_metadata),
)
if event.prop and update_status:
self.task.status_db.set_task_property_status(
event.prop,
data=status_metadata
)

if event.trace not in engine.traces:
engine.traces[event.trace] = SbyTraceSummary(event.trace, path=event.path, engine_case=event.engine_case)
Expand Down Expand Up @@ -765,8 +787,8 @@ def summarize(self, short):
event = same_events[0]
steps = sorted(e.step for e in same_events)
if short and len(steps) > step_limit:
steps = [str(step) for step in steps[:step_limit]]
excess = len(steps) - step_limit
steps = [str(step) for step in steps[:step_limit]]
omitted_excess = True
steps[-1] += f" and {excess} further step{'s' if excess != 1 else ''}"

Expand Down Expand Up @@ -1005,6 +1027,7 @@ def make_model(self, model_name):
print("setundef -undriven -anyseq", file=f)
print("opt -fast", file=f)
if self.opt_witrename:
# we need to run this a second time to handle anything added by prep
print("rename -witness", file=f)
print("opt_clean", file=f)
print(f"""write_rtlil ../model/design_prep.il""", file=f)
Expand All @@ -1026,6 +1049,9 @@ def make_model(self, model_name):
print(cmd, file=f)
# the user must designate a top module in [script]
print("hierarchy -smtcheck", file=f)
# we need to give flatten-preserved names before write_jny
if self.opt_witrename:
print("rename -witness", file=f)
print(f"""write_jny -no-connections ../model/design.json""", file=f)
print(f"""write_rtlil ../model/design.il""", file=f)

Expand All @@ -1041,6 +1067,10 @@ def instance_hierarchy_callback(retcode):
if self.design == None:
with open(f"{self.workdir}/model/design.json") as f:
self.design = design_hierarchy(f)
self.status_db.create_task_properties([
prop for prop in self.design.properties_by_path.values()
if not prop.type.assume_like
])

def instance_hierarchy_error_callback(retcode):
self.precise_prop_status = False
Expand Down Expand Up @@ -1186,8 +1216,13 @@ def proc_failed(self, proc):
self.status = "ERROR"
self.terminate()

def pass_unknown_asserts(self, data):
for prop in self.design.pass_unknown_asserts():
self.status_db.set_task_property_status(prop, data=data)

def update_status(self, new_status):
assert new_status in ["PASS", "FAIL", "UNKNOWN", "ERROR"]
self.status_db.set_task_status(new_status)

if new_status == "UNKNOWN":
return
Expand All @@ -1199,7 +1234,7 @@ def update_status(self, new_status):
assert self.status != "FAIL"
self.status = "PASS"
if self.opt_mode in ("bmc", "prove") and self.design:
self.design.pass_unknown_asserts()
self.pass_unknown_asserts(dict(source="task_status"))

elif new_status == "FAIL":
assert self.status != "PASS"
Expand Down Expand Up @@ -1258,6 +1293,19 @@ def handle_non_engine_options(self):

self.handle_bool_option("assume_early", True)

def setup_status_db(self, status_path=None):
if hasattr(self, 'status_db'):
return

if status_path is None:
try:
with open(f"{self.workdir}/status.path", "r") as status_path_file:
status_path = f"{self.workdir}/{status_path_file.read().rstrip()}"
except FileNotFoundError:
status_path = f"{self.workdir}/status.sqlite"

self.status_db = SbyStatusDb(status_path, self)

def setup_procs(self, setupmode):
self.handle_non_engine_options()
if self.opt_smtc is not None:
Expand Down Expand Up @@ -1285,6 +1333,8 @@ def setup_procs(self, setupmode):
self.retcode = 0
return

self.setup_status_db()

if self.opt_make_model is not None:
for name in self.opt_make_model.split(","):
self.model(name.strip())
Expand Down
7 changes: 7 additions & 0 deletions sbysrc/sby_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ def from_flavor(c, name):
return c.FAIR
raise ValueError("Unknown property type: " + name)

@property
def assume_like(self):
return self in [self.ASSUME, self.FAIR]

name: str
path: Tuple[str, ...]
type: Type
Expand Down Expand Up @@ -171,9 +175,12 @@ class SbyDesign:
properties_by_path: dict = field(default_factory=dict)

def pass_unknown_asserts(self):
updated = []
for prop in self.hierarchy:
if prop.type == prop.Type.ASSERT and prop.status == "UNKNOWN":
prop.status = "PASS"
updated.append(prop)
return updated


def cell_path(cell):
Expand Down
Loading

0 comments on commit 0c84510

Please sign in to comment.