yowasp-yosys 0.38.0.92.post687.dev0__py3-none-any.whl → 0.39.0.165.post702.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,6 +27,7 @@ from shutil import copyfile, copytree, rmtree
27
27
  from select import select
28
28
  from time import monotonic, localtime, sleep, strftime
29
29
  from sby_design import SbyProperty, SbyModule, design_hierarchy
30
+ from sby_status import SbyStatusDb
30
31
 
31
32
  all_procs_running = []
32
33
 
@@ -674,20 +675,41 @@ class SbySummary:
674
675
  self.engine_summaries[engine_idx] = SbyEngineSummary(engine_idx)
675
676
  return self.engine_summaries[engine_idx]
676
677
 
677
- def add_event(self, *args, **kwargs):
678
+ def add_event(self, *args, update_status=True, **kwargs):
678
679
  event = SbySummaryEvent(*args, **kwargs)
680
+
681
+ engine = self.engine_summary(event.engine_idx)
682
+
683
+ if update_status:
684
+ status_metadata = dict(source="summary_event", engine=engine.engine)
685
+
679
686
  if event.prop:
680
687
  if event.type == "$assert":
681
688
  event.prop.status = "FAIL"
682
689
  if event.path:
683
690
  event.prop.tracefiles.append(event.path)
691
+ if update_status:
692
+ self.task.status_db.add_task_property_data(
693
+ event.prop,
694
+ "trace",
695
+ data=dict(path=event.path, step=event.step, **status_metadata),
696
+ )
684
697
  if event.prop:
685
698
  if event.type == "$cover":
686
699
  event.prop.status = "PASS"
687
700
  if event.path:
688
701
  event.prop.tracefiles.append(event.path)
689
-
690
- engine = self.engine_summary(event.engine_idx)
702
+ if update_status:
703
+ self.task.status_db.add_task_property_data(
704
+ event.prop,
705
+ "trace",
706
+ data=dict(path=event.path, step=event.step, **status_metadata),
707
+ )
708
+ if event.prop and update_status:
709
+ self.task.status_db.set_task_property_status(
710
+ event.prop,
711
+ data=status_metadata
712
+ )
691
713
 
692
714
  if event.trace not in engine.traces:
693
715
  engine.traces[event.trace] = SbyTraceSummary(event.trace, path=event.path, engine_case=event.engine_case)
@@ -765,8 +787,8 @@ class SbySummary:
765
787
  event = same_events[0]
766
788
  steps = sorted(e.step for e in same_events)
767
789
  if short and len(steps) > step_limit:
768
- steps = [str(step) for step in steps[:step_limit]]
769
790
  excess = len(steps) - step_limit
791
+ steps = [str(step) for step in steps[:step_limit]]
770
792
  omitted_excess = True
771
793
  steps[-1] += f" and {excess} further step{'s' if excess != 1 else ''}"
772
794
 
@@ -1005,6 +1027,7 @@ class SbyTask(SbyConfig):
1005
1027
  print("setundef -undriven -anyseq", file=f)
1006
1028
  print("opt -fast", file=f)
1007
1029
  if self.opt_witrename:
1030
+ # we need to run this a second time to handle anything added by prep
1008
1031
  print("rename -witness", file=f)
1009
1032
  print("opt_clean", file=f)
1010
1033
  print(f"""write_rtlil ../model/design_prep.il""", file=f)
@@ -1026,6 +1049,9 @@ class SbyTask(SbyConfig):
1026
1049
  print(cmd, file=f)
1027
1050
  # the user must designate a top module in [script]
1028
1051
  print("hierarchy -smtcheck", file=f)
1052
+ # we need to give flatten-preserved names before write_jny
1053
+ if self.opt_witrename:
1054
+ print("rename -witness", file=f)
1029
1055
  print(f"""write_jny -no-connections ../model/design.json""", file=f)
1030
1056
  print(f"""write_rtlil ../model/design.il""", file=f)
1031
1057
 
@@ -1041,6 +1067,10 @@ class SbyTask(SbyConfig):
1041
1067
  if self.design == None:
1042
1068
  with open(f"{self.workdir}/model/design.json") as f:
1043
1069
  self.design = design_hierarchy(f)
1070
+ self.status_db.create_task_properties([
1071
+ prop for prop in self.design.properties_by_path.values()
1072
+ if not prop.type.assume_like
1073
+ ])
1044
1074
 
1045
1075
  def instance_hierarchy_error_callback(retcode):
1046
1076
  self.precise_prop_status = False
@@ -1186,8 +1216,13 @@ class SbyTask(SbyConfig):
1186
1216
  self.status = "ERROR"
1187
1217
  self.terminate()
1188
1218
 
1219
+ def pass_unknown_asserts(self, data):
1220
+ for prop in self.design.pass_unknown_asserts():
1221
+ self.status_db.set_task_property_status(prop, data=data)
1222
+
1189
1223
  def update_status(self, new_status):
1190
1224
  assert new_status in ["PASS", "FAIL", "UNKNOWN", "ERROR"]
1225
+ self.status_db.set_task_status(new_status)
1191
1226
 
1192
1227
  if new_status == "UNKNOWN":
1193
1228
  return
@@ -1199,7 +1234,7 @@ class SbyTask(SbyConfig):
1199
1234
  assert self.status != "FAIL"
1200
1235
  self.status = "PASS"
1201
1236
  if self.opt_mode in ("bmc", "prove") and self.design:
1202
- self.design.pass_unknown_asserts()
1237
+ self.pass_unknown_asserts(dict(source="task_status"))
1203
1238
 
1204
1239
  elif new_status == "FAIL":
1205
1240
  assert self.status != "PASS"
@@ -1258,6 +1293,19 @@ class SbyTask(SbyConfig):
1258
1293
 
1259
1294
  self.handle_bool_option("assume_early", True)
1260
1295
 
1296
+ def setup_status_db(self, status_path=None):
1297
+ if hasattr(self, 'status_db'):
1298
+ return
1299
+
1300
+ if status_path is None:
1301
+ try:
1302
+ with open(f"{self.workdir}/status.path", "r") as status_path_file:
1303
+ status_path = f"{self.workdir}/{status_path_file.read().rstrip()}"
1304
+ except FileNotFoundError:
1305
+ status_path = f"{self.workdir}/status.sqlite"
1306
+
1307
+ self.status_db = SbyStatusDb(status_path, self)
1308
+
1261
1309
  def setup_procs(self, setupmode):
1262
1310
  self.handle_non_engine_options()
1263
1311
  if self.opt_smtc is not None:
@@ -1285,6 +1333,8 @@ class SbyTask(SbyConfig):
1285
1333
  self.retcode = 0
1286
1334
  return
1287
1335
 
1336
+ self.setup_status_db()
1337
+
1288
1338
  if self.opt_make_model is not None:
1289
1339
  for name in self.opt_make_model.split(","):
1290
1340
  self.model(name.strip())
@@ -88,6 +88,10 @@ class SbyProperty:
88
88
  return c.FAIR
89
89
  raise ValueError("Unknown property type: " + name)
90
90
 
91
+ @property
92
+ def assume_like(self):
93
+ return self in [self.ASSUME, self.FAIR]
94
+
91
95
  name: str
92
96
  path: Tuple[str, ...]
93
97
  type: Type
@@ -171,9 +175,12 @@ class SbyDesign:
171
175
  properties_by_path: dict = field(default_factory=dict)
172
176
 
173
177
  def pass_unknown_asserts(self):
178
+ updated = []
174
179
  for prop in self.hierarchy:
175
180
  if prop.type == prop.Type.ASSERT and prop.status == "UNKNOWN":
176
181
  prop.status = "PASS"
182
+ updated.append(prop)
183
+ return updated
177
184
 
178
185
 
179
186
  def cell_path(cell):
@@ -17,32 +17,101 @@
17
17
  #
18
18
 
19
19
  import re, getopt
20
+ import json
20
21
  from sby_core import SbyProc
21
- from sby_engine_aiger import aigsmt_exit_callback
22
+ from sby_engine_aiger import aigsmt_exit_callback, aigsmt_trace_callback
23
+
24
+
25
+ def abc_getopt(args, long):
26
+ long = set(long)
27
+ output = []
28
+ parsed = []
29
+ toggles = set()
30
+ pos = 0
31
+
32
+ while pos < len(args):
33
+ arg = args[pos]
34
+ pos += 1
35
+ if not arg.startswith('-'):
36
+ output.append(arg)
37
+ elif arg == '--':
38
+ output.extend(args[pos:])
39
+ break
40
+ elif arg.startswith('--'):
41
+ if '=' in arg:
42
+ prefix, param = arg.split('=', 1)
43
+ if prefix + "=" in long:
44
+ parsed.append(prefix, param)
45
+ elif arg[2:] in long:
46
+ parsed.append((arg, ''))
47
+ elif arg[2:] + "=" in long:
48
+ parsed.append((arg, args[pos]))
49
+ pos += 1
50
+ else:
51
+ output.append(arg)
52
+ elif arg.startswith('-'):
53
+ output.append(arg)
54
+ for c in arg[1:]:
55
+ if 'A' <= c <= 'Z':
56
+ if pos < len(args):
57
+ output.append(args[pos])
58
+ pos += 1
59
+ else:
60
+ toggles.symmetric_difference_update([c])
61
+
62
+ return output, parsed, toggles
63
+
22
64
 
23
65
  def run(mode, task, engine_idx, engine):
24
- abc_opts, abc_command = getopt.getopt(engine[1:], "", [])
66
+ keep_going = False
67
+
68
+ fold_command = "fold"
69
+ if task.opt_aigfolds:
70
+ fold_command += " -s"
71
+
72
+ abc_command, custom_options, toggles = abc_getopt(engine[1:], [
73
+ "keep-going",
74
+ ])
25
75
 
26
76
  if len(abc_command) == 0:
27
77
  task.error("Missing ABC command.")
28
78
 
29
- for o, a in abc_opts:
30
- task.error("Unexpected ABC engine options.")
79
+ if abc_command[0].startswith('-'):
80
+ task.error(f"Unexpected ABC engine option '{abc_command[0]}'.")
31
81
 
32
82
  if abc_command[0] == "bmc3":
33
83
  if mode != "bmc":
34
84
  task.error("ABC command 'bmc3' is only valid in bmc mode.")
85
+ for o, a in custom_options:
86
+ task.error(f"Option {o} not supported by 'abc {abc_command[0]}'")
35
87
  abc_command[0] += f" -F {task.opt_depth} -v"
36
88
 
37
89
  elif abc_command[0] == "sim3":
38
90
  if mode != "bmc":
39
91
  task.error("ABC command 'sim3' is only valid in bmc mode.")
92
+ for o, a in custom_options:
93
+ task.error(f"Option {o} not supported by 'abc {abc_command[0]}'")
40
94
  abc_command[0] += f" -F {task.opt_depth} -v"
41
95
 
42
96
  elif abc_command[0] == "pdr":
43
97
  if mode != "prove":
44
98
  task.error("ABC command 'pdr' is only valid in prove mode.")
45
- abc_command[0] += f" -v -I engine_{engine_idx}/invariants.pla"
99
+
100
+ for o, a in custom_options:
101
+ if o == '--keep-going':
102
+ keep_going = True
103
+ else:
104
+ task.error(f"Option {o} not supported by 'abc {abc_command[0]}'")
105
+
106
+ abc_command[0] += " -v -l"
107
+
108
+ if keep_going:
109
+ abc_command += ["-a", "-X", f"engine_{engine_idx}/trace_"]
110
+
111
+ if 'd' in toggles:
112
+ abc_command += ["-I", f"engine_{engine_idx}/invariants.pla"]
113
+ if not task.opt_aigfolds:
114
+ fold_command += " -s"
46
115
 
47
116
  else:
48
117
  task.error(f"Invalid ABC command {abc_command[0]}.")
@@ -66,21 +135,61 @@ def run(mode, task, engine_idx, engine):
66
135
  task,
67
136
  f"engine_{engine_idx}",
68
137
  task.model("aig"),
69
- f"""cd {task.workdir}; {task.exe_paths["abc"]} -c 'read_aiger model/design_aiger.aig; fold{
70
- " -s" if task.opt_aigfolds or (abc_command[0].startswith("pdr ") and "-d" in abc_command[1:]) else ""
71
- }; strash; {" ".join(abc_command)}; write_cex -a engine_{engine_idx}/trace.aiw'""",
138
+ f"""cd {task.workdir}; {task.exe_paths["abc"]} -c 'read_aiger model/design_aiger.aig; {
139
+ fold_command}; strash; {" ".join(abc_command)}; write_cex -a engine_{engine_idx}/trace.aiw'""",
72
140
  logfile=open(f"{task.workdir}/engine_{engine_idx}/logfile.txt", "w")
73
141
  )
74
142
  proc.checkretcode = True
75
143
 
76
144
  proc.noprintregex = re.compile(r"^\.+$")
77
- proc_status = None
145
+ proc_status = "UNKNOWN"
146
+
147
+ procs_running = 1
148
+
149
+ aiger_props = None
150
+ disproved = set()
151
+ proved = set()
78
152
 
79
153
  def output_callback(line):
80
154
  nonlocal proc_status
81
-
82
- match = re.match(r"^Output [0-9]+ of miter .* was asserted in frame [0-9]+.", line)
83
- if match: proc_status = "FAIL"
155
+ nonlocal procs_running
156
+ nonlocal aiger_props
157
+
158
+ if aiger_props is None:
159
+ with open(f"{task.workdir}/model/design_aiger.ywa") as ywa_file:
160
+ ywa = json.load(ywa_file)
161
+ aiger_props = []
162
+ for path in ywa["asserts"]:
163
+ aiger_props.append(task.design.properties_by_path.get(tuple(path)))
164
+
165
+ if keep_going:
166
+ match = re.match(r"Writing CEX for output ([0-9]+) to engine_[0-9]+/(.*)\.aiw", line)
167
+ if match:
168
+ output = int(match[1])
169
+ prop = aiger_props[output]
170
+ if prop:
171
+ prop.status = "FAIL"
172
+ task.status_db.set_task_property_status(prop, data=dict(source="abc pdr", engine=f"engine_{engine_idx}"))
173
+ disproved.add(output)
174
+ proc_status = "FAIL"
175
+ proc = aigsmt_trace_callback(task, engine_idx, proc_status,
176
+ run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append,
177
+ name=match[2],
178
+ )
179
+ proc.register_exit_callback(exit_callback)
180
+ procs_running += 1
181
+ else:
182
+ match = re.match(r"^Output [0-9]+ of miter .* was asserted in frame [0-9]+.", line)
183
+ if match: proc_status = "FAIL"
184
+
185
+ match = re.match(r"^Proved output +([0-9]+) in frame +-?[0-9]+", line)
186
+ if match:
187
+ output = int(match[1])
188
+ prop = aiger_props[output]
189
+ if prop:
190
+ prop.status = "PASS"
191
+ task.status_db.set_task_property_status(prop, data=dict(source="abc pdr", engine=f"engine_{engine_idx}"))
192
+ proved.add(output)
84
193
 
85
194
  match = re.match(r"^Simulation of [0-9]+ frames for [0-9]+ rounds with [0-9]+ restarts did not assert POs.", line)
86
195
  if match: proc_status = "UNKNOWN"
@@ -94,11 +203,44 @@ def run(mode, task, engine_idx, engine):
94
203
  match = re.match(r"^Property proved.", line)
95
204
  if match: proc_status = "PASS"
96
205
 
206
+ if keep_going:
207
+ match = re.match(r"^Properties: All = (\d+). Proved = (\d+). Disproved = (\d+). Undecided = (\d+).", line)
208
+ if match:
209
+ all_count = int(match[1])
210
+ proved_count = int(match[2])
211
+ disproved_count = int(match[3])
212
+ undecided_count = int(match[4])
213
+ if (
214
+ all_count != len(aiger_props) or
215
+ all_count != proved_count + disproved_count + undecided_count or
216
+ disproved_count != len(disproved) or
217
+ proved_count != len(proved)
218
+ ):
219
+ log("WARNING: inconsistent status output")
220
+ proc_status = "UNKNOWN"
221
+ elif proved_count == all_count:
222
+ proc_status = "PASS"
223
+ elif disproved_count == 0:
224
+ proc_status = "UNKNOWN"
225
+ else:
226
+ proc_status = "FAIL"
227
+
97
228
  return line
98
229
 
99
230
  def exit_callback(retcode):
100
- aigsmt_exit_callback(task, engine_idx, proc_status,
101
- run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append, )
231
+ nonlocal procs_running
232
+ if keep_going:
233
+ procs_running -= 1
234
+ if not procs_running:
235
+ if proc_status == "FAIL" and mode == "bmc" and keep_going:
236
+ task.pass_unknown_asserts(dict(source="abc pdr", keep_going=True, engine=f"engine_{engine_idx}"))
237
+ task.update_status(proc_status)
238
+ task.summary.set_engine_status(engine_idx, proc_status)
239
+ if proc_status != "UNKNOWN" and not keep_going:
240
+ task.terminate()
241
+ else:
242
+ aigsmt_exit_callback(task, engine_idx, proc_status,
243
+ run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append)
102
244
 
103
245
  proc.output_callback = output_callback
104
246
  proc.register_exit_callback(exit_callback)
@@ -121,70 +121,108 @@ def run(mode, task, engine_idx, engine):
121
121
 
122
122
 
123
123
  def aigsmt_exit_callback(task, engine_idx, proc_status, *, run_aigsmt, smtbmc_vcd, smtbmc_append, sim_append):
124
- if proc_status is None:
125
- task.error(f"engine_{engine_idx}: Could not determine engine status.")
124
+ if proc_status is None:
125
+ task.error(f"engine_{engine_idx}: Could not determine engine status.")
126
126
 
127
- task.update_status(proc_status)
128
- task.summary.set_engine_status(engine_idx, proc_status)
129
- task.terminate()
127
+ task.update_status(proc_status)
128
+ task.summary.set_engine_status(engine_idx, proc_status)
129
+ task.terminate()
130
+ if proc_status == "FAIL" and (not run_aigsmt or task.opt_aigsmt != "none"):
131
+ aigsmt_trace_callback(task, engine_idx, proc_status, run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append)
130
132
 
131
- if proc_status == "FAIL" and (not run_aigsmt or task.opt_aigsmt != "none"):
132
- trace_prefix = f"engine_{engine_idx}/trace"
133
+ def aigsmt_trace_callback(task, engine_idx, proc_status, *, run_aigsmt, smtbmc_vcd, smtbmc_append, sim_append, name="trace"):
133
134
 
134
- aiw2yw_suffix = '_aiw' if run_aigsmt else ''
135
+ trace_prefix = f"engine_{engine_idx}/{name}"
135
136
 
136
- witness_proc = SbyProc(
137
- task, f"engine_{engine_idx}", [],
138
- f"cd {task.workdir}; {task.exe_paths['witness']} aiw2yw engine_{engine_idx}/trace.aiw model/design_aiger.ywa engine_{engine_idx}/trace{aiw2yw_suffix}.yw",
139
- )
140
- yw_proc = witness_proc
137
+ aiw2yw_suffix = '_aiw' if run_aigsmt else ''
141
138
 
142
- if run_aigsmt:
143
- smtbmc_opts = []
144
- smtbmc_opts += ["-s", task.opt_aigsmt]
145
- if task.opt_tbtop is not None:
146
- smtbmc_opts += ["--vlogtb-top", task.opt_tbtop]
147
- smtbmc_opts += ["--noprogress", f"--append {smtbmc_append}"]
148
- if smtbmc_vcd:
149
- smtbmc_opts += [f"--dump-vcd {trace_prefix}.vcd"]
150
- smtbmc_opts += [f"--dump-yw {trace_prefix}.yw", f"--dump-vlogtb {trace_prefix}_tb.v", f"--dump-smtc {trace_prefix}.smtc"]
151
-
152
- proc2 = SbyProc(
153
- task,
154
- f"engine_{engine_idx}",
155
- [*task.model("smt2"), witness_proc],
156
- f"cd {task.workdir}; {task.exe_paths['smtbmc']} {' '.join(smtbmc_opts)} --yw engine_{engine_idx}/trace{aiw2yw_suffix}.yw model/design_smt2.smt2",
157
- logfile=open(f"{task.workdir}/engine_{engine_idx}/logfile2.txt", "w")
158
- )
159
-
160
- proc2_status = None
161
-
162
- def output_callback2(line):
163
- nonlocal proc2_status
164
-
165
- match = re.match(r"^## [0-9: ]+ Status: FAILED", line)
166
- if match: proc2_status = "FAIL"
167
-
168
- match = re.match(r"^## [0-9: ]+ Status: PASSED", line)
169
- if match: proc2_status = "PASS"
170
-
171
- return line
172
-
173
- def exit_callback2(retcode):
174
- if proc2_status is None:
175
- task.error(f"engine_{engine_idx}: Could not determine aigsmt status.")
176
- if proc2_status != "FAIL":
177
- task.error(f"engine_{engine_idx}: Unexpected aigsmt status.")
178
- if os.path.exists(f"{task.workdir}/engine_{engine_idx}/trace.vcd"):
179
- task.summary.add_event(engine_idx, trace="trace", path=f"engine_{engine_idx}/trace.vcd", type="$assert")
180
-
181
- proc2.output_callback = output_callback2
182
- proc2.register_exit_callback(exit_callback2)
183
-
184
- yw_proc = proc2
185
-
186
- if task.opt_fst or (task.opt_vcd and task.opt_vcd_sim):
187
- sim_witness_trace(f"engine_{engine_idx}", task, engine_idx, f"engine_{engine_idx}/trace.yw", append=sim_append, deps=[yw_proc])
188
-
189
- else:
190
- task.log(f"{click.style(f'engine_{engine_idx}', fg='magenta')}: Engine did not produce a counter example.")
139
+ witness_proc = SbyProc(
140
+ task, f"engine_{engine_idx}", [],
141
+ f"cd {task.workdir}; {task.exe_paths['witness']} aiw2yw engine_{engine_idx}/{name}.aiw model/design_aiger.ywa engine_{engine_idx}/{name}{aiw2yw_suffix}.yw",
142
+ )
143
+ final_proc = witness_proc
144
+
145
+ if run_aigsmt:
146
+ smtbmc_opts = []
147
+ smtbmc_opts += ["-s", task.opt_aigsmt]
148
+ if task.opt_tbtop is not None:
149
+ smtbmc_opts += ["--vlogtb-top", task.opt_tbtop]
150
+ smtbmc_opts += ["--noprogress", f"--append {smtbmc_append}"]
151
+ if smtbmc_vcd:
152
+ smtbmc_opts += [f"--dump-vcd {trace_prefix}.vcd"]
153
+ smtbmc_opts += [f"--dump-yw {trace_prefix}.yw", f"--dump-vlogtb {trace_prefix}_tb.v", f"--dump-smtc {trace_prefix}.smtc"]
154
+
155
+ proc2 = SbyProc(
156
+ task,
157
+ f"engine_{engine_idx}",
158
+ [*task.model("smt2"), witness_proc],
159
+ f"cd {task.workdir}; {task.exe_paths['smtbmc']} {' '.join(smtbmc_opts)} --yw engine_{engine_idx}/{name}{aiw2yw_suffix}.yw model/design_smt2.smt2",
160
+ logfile=open(f"{task.workdir}/engine_{engine_idx}/logfile2.txt", "w"),
161
+ )
162
+
163
+ proc2_status = None
164
+
165
+ last_prop = []
166
+ current_step = None
167
+
168
+ def output_callback2(line):
169
+ nonlocal proc2_status
170
+ nonlocal last_prop
171
+ nonlocal current_step
172
+
173
+ smt2_trans = {'\\':'/', '|':'/'}
174
+
175
+ match = re.match(r"^## [0-9: ]+ .* in step ([0-9]+)\.\.", line)
176
+ if match:
177
+ current_step = int(match[1])
178
+ return line
179
+
180
+ match = re.match(r"^## [0-9: ]+ Status: FAILED", line)
181
+ if match: proc2_status = "FAIL"
182
+
183
+ match = re.match(r"^## [0-9: ]+ Status: PASSED", line)
184
+ if match: proc2_status = "PASS"
185
+
186
+ match = re.match(r"^## [0-9: ]+ Assert failed in (\S+): (\S+)(?: \((\S+)\))?", line)
187
+ if match:
188
+ cell_name = match[3] or match[2]
189
+ prop = task.design.hierarchy.find_property_by_cellname(cell_name, trans_dict=smt2_trans)
190
+ prop.status = "FAIL"
191
+ task.status_db.set_task_property_status(prop, data=dict(source="aigsmt", engine=f"engine_{engine_idx}"))
192
+ last_prop.append(prop)
193
+ return line
194
+
195
+ match = re.match(r"^## [0-9: ]+ Writing trace to VCD file: (\S+)", line)
196
+ if match:
197
+ tracefile = match[1]
198
+ trace = os.path.basename(tracefile)[:-4]
199
+ task.summary.add_event(engine_idx=engine_idx, trace=trace, path=tracefile)
200
+
201
+ if match and last_prop:
202
+ for p in last_prop:
203
+ task.summary.add_event(
204
+ engine_idx=engine_idx, trace=trace,
205
+ type=p.celltype, hdlname=p.hdlname, src=p.location, step=current_step)
206
+ p.tracefiles.append(tracefile)
207
+ last_prop = []
208
+ return line
209
+
210
+ return line
211
+
212
+ def exit_callback2(retcode):
213
+ if proc2_status is None:
214
+ task.error(f"engine_{engine_idx}: Could not determine aigsmt status.")
215
+ if proc2_status != "FAIL":
216
+ task.error(f"engine_{engine_idx}: Unexpected aigsmt status.")
217
+
218
+ proc2.output_callback = output_callback2
219
+ proc2.register_exit_callback(exit_callback2)
220
+
221
+ final_proc = proc2
222
+
223
+ if task.opt_fst or (task.opt_vcd and task.opt_vcd_sim):
224
+ final_proc = sim_witness_trace(f"engine_{engine_idx}", task, engine_idx, f"engine_{engine_idx}/{name}.yw", append=sim_append, deps=[final_proc])
225
+ elif not run_aigsmt:
226
+ task.log(f"{click.style(f'engine_{engine_idx}', fg='magenta')}: Engine did not produce a counter example.")
227
+
228
+ return final_proc
@@ -233,6 +233,7 @@ def run(mode, task, engine_idx, engine):
233
233
  cell_name = match[3] or match[2]
234
234
  prop = task.design.hierarchy.find_property_by_cellname(cell_name, trans_dict=smt2_trans)
235
235
  prop.status = "FAIL"
236
+ task.status_db.set_task_property_status(prop, data=dict(source="smtbmc", engine=f"engine_{engine_idx}"))
236
237
  last_prop.append(prop)
237
238
  return line
238
239
 
@@ -241,6 +242,7 @@ def run(mode, task, engine_idx, engine):
241
242
  cell_name = match[2] or match[1]
242
243
  prop = task.design.hierarchy.find_property_by_cellname(cell_name, trans_dict=smt2_trans)
243
244
  prop.status = "PASS"
245
+ task.status_db.set_task_property_status(prop, data=dict(source="smtbmc", engine=f"engine_{engine_idx}"))
244
246
  last_prop.append(prop)
245
247
  return line
246
248
 
@@ -271,6 +273,7 @@ def run(mode, task, engine_idx, engine):
271
273
  cell_name = match[2]
272
274
  prop = task.design.hierarchy.find_property_by_cellname(cell_name, trans_dict=smt2_trans)
273
275
  prop.status = "FAIL"
276
+ task.status_db.set_task_property_status(prop, data=dict(source="smtbmc", engine=f"engine_{engine_idx}"))
274
277
 
275
278
  return line
276
279
 
@@ -288,10 +291,12 @@ def run(mode, task, engine_idx, engine):
288
291
  def last_exit_callback():
289
292
  if mode == "bmc" or mode == "cover":
290
293
  task.update_status(proc_status)
294
+ if proc_status == "FAIL" and mode == "bmc" and keep_going:
295
+ task.pass_unknown_asserts(dict(source="smtbmc", keep_going=True, engine=f"engine_{engine_idx}"))
291
296
  proc_status_lower = proc_status.lower() if proc_status == "PASS" else proc_status
292
297
  task.summary.set_engine_status(engine_idx, proc_status_lower)
293
-
294
- task.terminate()
298
+ if not keep_going:
299
+ task.terminate()
295
300
 
296
301
  elif mode in ["prove_basecase", "prove_induction"]:
297
302
  proc_status_lower = proc_status.lower() if proc_status == "PASS" else proc_status
@@ -321,7 +326,8 @@ def run(mode, task, engine_idx, engine):
321
326
  if task.basecase_pass and task.induction_pass:
322
327
  task.update_status("PASS")
323
328
  task.summary.append("successful proof by k-induction.")
324
- task.terminate()
329
+ if not keep_going:
330
+ task.terminate()
325
331
 
326
332
  else:
327
333
  assert False