2 # SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows
4 # Copyright (C) 2016 Claire Xenia Wolf <claire@yosyshq.com>
6 # Permission to use, copy, modify, and/or distribute this software for any
7 # purpose with or without fee is hereby granted, provided that the above
8 # copyright notice and this permission notice appear in all copies.
10 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 from sby_core
import SbyProc
22 def run(mode
, task
, engine_idx
, engine
):
23 opts
, solver_args
= getopt
.getopt(engine
[1:], "", [])
25 if len(solver_args
) == 0:
26 task
.error("Missing solver command.")
29 task
.error("Unexpected AIGER engine options.")
33 if solver_args
[0] == "suprove":
34 if mode
not in ["live", "prove"]:
35 task
.error("The aiger solver 'suprove' is only supported in live and prove modes.")
36 if mode
== "live" and (len(solver_args
) == 1 or solver_args
[1][0] != "+"):
37 solver_args
.insert(1, "+simple_liveness")
38 solver_cmd
= " ".join([task
.exe_paths
["suprove"]] + solver_args
[1:])
40 elif solver_args
[0] == "avy":
42 task
.error("The aiger solver 'avy' is only supported in prove mode.")
43 solver_cmd
= " ".join([task
.exe_paths
["avy"], "--cex", "-"] + solver_args
[1:])
45 elif solver_args
[0] == "aigbmc":
47 task
.error("The aiger solver 'aigbmc' is only supported in bmc mode.")
48 solver_cmd
= " ".join([task
.exe_paths
["aigbmc"], str(task
.opt_depth
- 1)] + solver_args
[1:])
49 status_2
= "PASS" # aigbmc outputs status 2 when BMC passes
52 task
.error(f
"Invalid solver command {solver_args[0]}.")
56 f
"engine_{engine_idx}",
58 f
"cd {task.workdir}; {solver_cmd} model/design_aiger.aig",
59 logfile
=open(f
"{task.workdir}/engine_{engine_idx}/logfile.txt", "w")
65 aiw_file
= open(f
"{task.workdir}/engine_{engine_idx}/trace.aiw", "w")
67 def output_callback(line
):
72 if proc_status
is not None:
73 if not end_of_cex
and not produced_cex
and line
.isdigit():
76 print(line
, file=aiw_file
)
81 if line
.startswith("u"):
82 return f
"No CEX up to depth {int(line[1:])-1}."
84 if line
in ["0", "1", "2"]:
85 print(line
, file=aiw_file
)
86 if line
== "0": proc_status
= "PASS"
87 if line
== "1": proc_status
= "FAIL"
88 if line
== "2": proc_status
= status_2
92 def exit_callback(retcode
):
93 if solver_args
[0] not in ["avy"]:
95 assert proc_status
is not None
99 task
.update_status(proc_status
)
100 task
.log(f
"engine_{engine_idx}: Status returned by engine: {proc_status}")
101 task
.summary
.append(f
"""engine_{engine_idx} ({" ".join(engine)}) returned {proc_status}""")
105 if proc_status
== "FAIL" and task
.opt_aigsmt
!= "none":
110 f
"engine_{engine_idx}",
112 ("cd {}; {} -g -s {}{} --noprogress --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " +
113 "--dump-smtc engine_{i}/trace.smtc --aig model/design_aiger.aim:engine_{i}/trace.aiw model/design_smt2.smt2").format
114 (task
.workdir
, task
.exe_paths
["smtbmc"], task
.opt_aigsmt
,
115 "" if task
.opt_tbtop
is None else f
" --vlogtb-top {task.opt_tbtop}",
117 logfile
=open(f
"{task.workdir}/engine_{engine_idx}/logfile2.txt", "w")
122 f
"engine_{engine_idx}",
124 ("cd {}; {} -s {}{} --noprogress --append {} --dump-vcd engine_{i}/trace.vcd --dump-vlogtb engine_{i}/trace_tb.v " +
125 "--dump-smtc engine_{i}/trace.smtc --aig model/design_aiger.aim:engine_{i}/trace.aiw model/design_smt2.smt2").format
126 (task
.workdir
, task
.exe_paths
["smtbmc"], task
.opt_aigsmt
,
127 "" if task
.opt_tbtop
is None else f
" --vlogtb-top {task.opt_tbtop}",
128 task
.opt_append
, i
=engine_idx
),
129 logfile
=open(f
"{task.workdir}/engine_{engine_idx}/logfile2.txt", "w")
134 def output_callback2(line
):
135 nonlocal proc2_status
137 match
= re
.match(r
"^## [0-9: ]+ Status: FAILED", line
)
138 if match
: proc2_status
= "FAIL"
140 match
= re
.match(r
"^## [0-9: ]+ Status: PASSED", line
)
141 if match
: proc2_status
= "PASS"
145 def exit_callback2(line
):
146 assert proc2_status
is not None
148 assert proc2_status
== "PASS"
150 assert proc2_status
== "FAIL"
152 if os
.path
.exists(f
"{task.workdir}/engine_{engine_idx}/trace.vcd"):
153 task
.summary
.append(f
"counterexample trace: {task.workdir}/engine_{engine_idx}/trace.vcd")
155 proc2
.output_callback
= output_callback2
156 proc2
.exit_callback
= exit_callback2
159 task
.log(f
"engine_{engine_idx}: Engine did not produce a counter example.")
161 proc
.output_callback
= output_callback
162 proc
.exit_callback
= exit_callback