mirror of
https://github.com/YosysHQ/sby.git
synced 2025-08-08 22:31:26 +00:00
Unified trace generation using yosys's sim across all engines
Currently opt-in using the `fst` or `vcd_sim` options.
This commit is contained in:
parent
4c44a10f72
commit
6d3b5aa960
15 changed files with 686 additions and 183 deletions
|
@ -16,8 +16,9 @@
|
|||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
import re, os, getopt, click
|
||||
import re, os, getopt, click, glob
|
||||
from sby_core import SbyProc
|
||||
from sby_sim import sim_witness_trace
|
||||
|
||||
def run(mode, task, engine_idx, engine):
|
||||
smtbmc_opts = []
|
||||
|
@ -141,20 +142,37 @@ def run(mode, task, engine_idx, engine):
|
|||
if not progress:
|
||||
smtbmc_opts.append("--noprogress")
|
||||
|
||||
|
||||
if task.opt_skip is not None:
|
||||
t_opt = "{}:{}".format(task.opt_skip, task.opt_depth)
|
||||
else:
|
||||
t_opt = "{}".format(task.opt_depth)
|
||||
|
||||
smtbmc_vcd = task.opt_vcd and not task.opt_vcd_sim
|
||||
|
||||
smtbmc_append = 0
|
||||
sim_append = 0
|
||||
|
||||
log = task.log_prefix(f"engine_{engine_idx}")
|
||||
|
||||
if task.opt_append_assume:
|
||||
smtbmc_append = task.opt_append
|
||||
elif smtbmc_vcd:
|
||||
if not task.opt_append_assume:
|
||||
log("For VCDs generated by smtbmc the option 'append_assume off' is ignored")
|
||||
smtbmc_append = task.opt_append
|
||||
else:
|
||||
sim_append = task.opt_append
|
||||
|
||||
trace_ext = 'fst' if task.opt_fst else 'vcd'
|
||||
|
||||
random_seed = f"--info \"(set-option :random-seed {random_seed})\"" if random_seed else ""
|
||||
dump_flags = f"--dump-vcd {trace_prefix}.vcd " if task.opt_vcd else ""
|
||||
dump_flags = f"--dump-vcd {trace_prefix}.vcd " if smtbmc_vcd else ""
|
||||
dump_flags += f"--dump-yw {trace_prefix}.yw --dump-vlogtb {trace_prefix}_tb.v --dump-smtc {trace_prefix}.smtc"
|
||||
proc = SbyProc(
|
||||
task,
|
||||
procname,
|
||||
task.model(model_name),
|
||||
f"""cd {task.workdir}; {task.exe_paths["smtbmc"]} {" ".join(smtbmc_opts)} -t {t_opt} {random_seed} --append {task.opt_append} {dump_flags} model/design_{model_name}.smt2""",
|
||||
f"""cd {task.workdir}; {task.exe_paths["smtbmc"]} {" ".join(smtbmc_opts)} -t {t_opt} {random_seed} --append {smtbmc_append} {dump_flags} model/design_{model_name}.smt2""",
|
||||
logfile=open(logfile_prefix + ".txt", "w"),
|
||||
logstderr=(not progress)
|
||||
)
|
||||
|
@ -167,12 +185,30 @@ def run(mode, task, engine_idx, engine):
|
|||
|
||||
proc_status = None
|
||||
last_prop = []
|
||||
pending_sim = None
|
||||
current_step = None
|
||||
procs_running = 1
|
||||
|
||||
def output_callback(line):
|
||||
nonlocal proc_status
|
||||
nonlocal last_prop
|
||||
nonlocal pending_sim
|
||||
nonlocal current_step
|
||||
nonlocal procs_running
|
||||
|
||||
if pending_sim:
|
||||
sim_proc = sim_witness_trace(procname, task, engine_idx, pending_sim, append=sim_append)
|
||||
sim_proc.register_exit_callback(simple_exit_callback)
|
||||
procs_running += 1
|
||||
pending_sim = None
|
||||
|
||||
smt2_trans = {'\\':'/', '|':'/'}
|
||||
|
||||
match = re.match(r"^## [0-9: ]+ .* in step ([0-9]+)\.\.", line)
|
||||
if match:
|
||||
current_step = int(match[1])
|
||||
return line
|
||||
|
||||
match = re.match(r"^## [0-9: ]+ Status: FAILED", line)
|
||||
if match:
|
||||
proc_status = "FAIL"
|
||||
|
@ -193,31 +229,45 @@ def run(mode, task, engine_idx, engine):
|
|||
proc_status = "ERROR"
|
||||
return line
|
||||
|
||||
match = re.match(r"^## [0-9: ]+ Assert failed in (\S+): (\S+) \((\S+)\)", line)
|
||||
match = re.match(r"^## [0-9: ]+ Assert failed in (\S+): (\S+)(?: \((\S+)\))?", line)
|
||||
if match:
|
||||
cell_name = match[3]
|
||||
cell_name = match[3] or match[2]
|
||||
prop = task.design.hierarchy.find_property_by_cellname(cell_name, trans_dict=smt2_trans)
|
||||
prop.status = "FAIL"
|
||||
last_prop.append(prop)
|
||||
return line
|
||||
|
||||
match = re.match(r"^## [0-9: ]+ Reached cover statement at (\S+) \((\S+)\) in step \d+.", line)
|
||||
match = re.match(r"^## [0-9: ]+ Reached cover statement at (\S+)(?: \((\S+)\))? in step \d+\.", line)
|
||||
if match:
|
||||
cell_name = match[2]
|
||||
cell_name = match[2] or match[1]
|
||||
prop = task.design.hierarchy.find_property_by_cellname(cell_name, trans_dict=smt2_trans)
|
||||
prop.status = "PASS"
|
||||
last_prop.append(prop)
|
||||
return line
|
||||
|
||||
match = re.match(r"^## [0-9: ]+ Writing trace to VCD file: (\S+)", line)
|
||||
if match and last_prop:
|
||||
for p in last_prop:
|
||||
if not p.tracefile:
|
||||
p.tracefile = match[1]
|
||||
last_prop = []
|
||||
return line
|
||||
if smtbmc_vcd and not task.opt_fst:
|
||||
match = re.match(r"^## [0-9: ]+ Writing trace to VCD file: (\S+)", line)
|
||||
if match:
|
||||
tracefile = match[1]
|
||||
trace = os.path.basename(tracefile)[:-4]
|
||||
engine_case = mode.split('_')[1] if '_' in mode else None
|
||||
task.summary.add_event(engine_idx=engine_idx, trace=trace, path=tracefile, engine_case=engine_case)
|
||||
|
||||
match = re.match(r"^## [0-9: ]+ Unreached cover statement at (\S+) \((\S+)\).", line)
|
||||
if match and last_prop:
|
||||
for p in last_prop:
|
||||
task.summary.add_event(
|
||||
engine_idx=engine_idx, trace=trace,
|
||||
type=p.celltype, hdlname=p.hdlname, src=p.location, step=current_step)
|
||||
p.tracefiles.append(tracefile)
|
||||
last_prop = []
|
||||
return line
|
||||
else:
|
||||
match = re.match(r"^## [0-9: ]+ Writing trace to Yosys witness file: (\S+)", line)
|
||||
if match:
|
||||
tracefile = match[1]
|
||||
pending_sim = tracefile
|
||||
|
||||
match = re.match(r"^## [0-9: ]+ Unreached cover statement at (\S+) \((\S+)\)\.", line)
|
||||
if match:
|
||||
cell_name = match[2]
|
||||
prop = task.design.hierarchy.find_property_by_cellname(cell_name, trans_dict=smt2_trans)
|
||||
|
@ -225,43 +275,28 @@ def run(mode, task, engine_idx, engine):
|
|||
|
||||
return line
|
||||
|
||||
def simple_exit_callback(retcode):
|
||||
nonlocal procs_running
|
||||
procs_running -= 1
|
||||
if not procs_running:
|
||||
last_exit_callback()
|
||||
|
||||
def exit_callback(retcode):
|
||||
if proc_status is None:
|
||||
task.error(f"engine_{engine_idx}: Engine terminated without status.")
|
||||
simple_exit_callback(retcode)
|
||||
|
||||
def last_exit_callback():
|
||||
if mode == "bmc" or mode == "cover":
|
||||
task.update_status(proc_status)
|
||||
proc_status_lower = proc_status.lower() if proc_status == "PASS" else proc_status
|
||||
task.log(f"{click.style(f'engine_{engine_idx}', fg='magenta')}: Status returned by engine: {proc_status_lower}")
|
||||
task.summary.append(f"""engine_{engine_idx} ({" ".join(engine)}) returned {proc_status_lower}""")
|
||||
|
||||
if proc_status == "FAIL" and mode != "cover":
|
||||
if os.path.exists(f"{task.workdir}/engine_{engine_idx}/trace.vcd"):
|
||||
task.summary.append(f"counterexample trace: {task.workdir}/engine_{engine_idx}/trace.vcd")
|
||||
elif proc_status == "PASS" and mode == "cover":
|
||||
print_traces_max = 5
|
||||
for i in range(print_traces_max):
|
||||
if os.path.exists(f"{task.workdir}/engine_{engine_idx}/trace{i}.vcd"):
|
||||
task.summary.append(f"trace: {task.workdir}/engine_{engine_idx}/trace{i}.vcd")
|
||||
else:
|
||||
break
|
||||
else:
|
||||
excess_traces = 0
|
||||
while os.path.exists(f"{task.workdir}/engine_{engine_idx}/trace{print_traces_max + excess_traces}.vcd"):
|
||||
excess_traces += 1
|
||||
if excess_traces > 0:
|
||||
task.summary.append(f"""and {excess_traces} further trace{"s" if excess_traces > 1 else ""}""")
|
||||
elif proc_status == "PASS" and mode == "bmc":
|
||||
for prop in task.design.hierarchy:
|
||||
if prop.type == prop.Type.ASSERT and prop.status == "UNKNOWN":
|
||||
prop.status = "PASS"
|
||||
task.summary.set_engine_status(engine_idx, proc_status_lower)
|
||||
|
||||
task.terminate()
|
||||
|
||||
elif mode in ["prove_basecase", "prove_induction"]:
|
||||
proc_status_lower = proc_status.lower() if proc_status == "PASS" else proc_status
|
||||
task.log(f"""{click.style(f'engine_{engine_idx}', fg='magenta')}: Status returned by engine for {mode.split("_")[1]}: {proc_status_lower}""")
|
||||
task.summary.append(f"""engine_{engine_idx} ({" ".join(engine)}) returned {proc_status_lower} for {mode.split("_")[1]}""")
|
||||
task.summary.set_engine_status(engine_idx, proc_status_lower, mode.split("_")[1])
|
||||
|
||||
if mode == "prove_basecase":
|
||||
for proc in task.basecase_procs:
|
||||
|
@ -272,8 +307,6 @@ def run(mode, task, engine_idx, engine):
|
|||
|
||||
else:
|
||||
task.update_status(proc_status)
|
||||
if os.path.exists(f"{task.workdir}/engine_{engine_idx}/trace.vcd"):
|
||||
task.summary.append(f"counterexample trace: {task.workdir}/engine_{engine_idx}/trace.vcd")
|
||||
task.terminate()
|
||||
|
||||
elif mode == "prove_induction":
|
||||
|
@ -287,9 +320,6 @@ def run(mode, task, engine_idx, engine):
|
|||
assert False
|
||||
|
||||
if task.basecase_pass and task.induction_pass:
|
||||
for prop in task.design.hierarchy:
|
||||
if prop.type == prop.Type.ASSERT and prop.status == "UNKNOWN":
|
||||
prop.status = "PASS"
|
||||
task.update_status("PASS")
|
||||
task.summary.append("successful proof by k-induction.")
|
||||
task.terminate()
|
||||
|
@ -298,4 +328,4 @@ def run(mode, task, engine_idx, engine):
|
|||
assert False
|
||||
|
||||
proc.output_callback = output_callback
|
||||
proc.exit_callback = exit_callback
|
||||
proc.register_exit_callback(exit_callback)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue