2015-10-13 18:31:54 -05:00
|
|
|
#
|
|
|
|
# yosys -- Yosys Open SYnthesis Suite
|
|
|
|
#
|
|
|
|
# Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
|
|
|
#
|
|
|
|
# Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
|
|
# copyright notice and this permission notice appear in all copies.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
#
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-03 07:26:00 -05:00
|
|
|
import sys, subprocess, re
|
2016-09-07 13:57:56 -05:00
|
|
|
from copy import deepcopy
|
2015-10-13 10:17:23 -05:00
|
|
|
from select import select
|
|
|
|
from time import time
|
|
|
|
|
2016-09-02 05:09:09 -05:00
|
|
|
|
2016-09-02 06:46:56 -05:00
|
|
|
hex_dict = {
|
|
|
|
"0": "0000", "1": "0001", "2": "0010", "3": "0011",
|
|
|
|
"4": "0100", "5": "0101", "6": "0110", "7": "0111",
|
|
|
|
"8": "1000", "9": "1001", "A": "1010", "B": "1011",
|
|
|
|
"C": "1100", "D": "1101", "E": "1110", "F": "1111",
|
|
|
|
"a": "1010", "b": "1011", "c": "1100", "d": "1101",
|
|
|
|
"e": "1110", "f": "1111"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-02 05:09:09 -05:00
|
|
|
class SmtModInfo:
|
2016-07-11 04:49:05 -05:00
|
|
|
def __init__(self):
|
|
|
|
self.inputs = set()
|
|
|
|
self.outputs = set()
|
|
|
|
self.registers = set()
|
2016-08-20 11:42:32 -05:00
|
|
|
self.memories = dict()
|
2016-07-11 04:49:05 -05:00
|
|
|
self.wires = set()
|
|
|
|
self.wsize = dict()
|
|
|
|
self.cells = dict()
|
2016-08-17 13:10:02 -05:00
|
|
|
self.asserts = dict()
|
2016-08-30 12:27:42 -05:00
|
|
|
self.anyconsts = dict()
|
2016-07-11 04:49:05 -05:00
|
|
|
|
2016-09-02 05:09:09 -05:00
|
|
|
|
2016-09-02 05:01:31 -05:00
|
|
|
class SmtIo:
|
2016-09-19 13:43:28 -05:00
|
|
|
def __init__(self, opts=None):
|
2016-09-18 13:48:09 -05:00
|
|
|
self.logic = None
|
|
|
|
self.logic_qf = True
|
|
|
|
self.logic_ax = True
|
|
|
|
self.logic_uf = True
|
|
|
|
self.logic_bv = True
|
2016-09-24 13:40:22 -05:00
|
|
|
self.produce_models = True
|
2016-10-03 13:30:38 -05:00
|
|
|
self.smt2cache = [list()]
|
|
|
|
self.p = None
|
2016-09-18 13:48:09 -05:00
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
if opts is not None:
|
2016-09-18 13:48:09 -05:00
|
|
|
self.logic = opts.logic
|
2015-10-13 10:17:23 -05:00
|
|
|
self.solver = opts.solver
|
|
|
|
self.debug_print = opts.debug_print
|
|
|
|
self.debug_file = opts.debug_file
|
2016-09-19 13:43:28 -05:00
|
|
|
self.dummy_file = opts.dummy_file
|
2015-10-13 10:17:23 -05:00
|
|
|
self.timeinfo = opts.timeinfo
|
2016-09-07 13:57:56 -05:00
|
|
|
self.unroll = opts.unroll
|
2016-10-03 13:30:38 -05:00
|
|
|
self.noincr = opts.noincr
|
2016-09-24 13:40:22 -05:00
|
|
|
self.info_stmts = opts.info_stmts
|
|
|
|
self.nocomments = opts.nocomments
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
else:
|
|
|
|
self.solver = "z3"
|
|
|
|
self.debug_print = False
|
|
|
|
self.debug_file = None
|
2016-09-19 13:43:28 -05:00
|
|
|
self.dummy_file = None
|
2015-10-13 10:17:23 -05:00
|
|
|
self.timeinfo = True
|
2016-09-07 13:57:56 -05:00
|
|
|
self.unroll = False
|
2016-10-03 13:30:38 -05:00
|
|
|
self.noincr = False
|
2016-09-24 13:40:22 -05:00
|
|
|
self.info_stmts = list()
|
|
|
|
self.nocomments = False
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
if self.solver == "yices":
|
2016-10-03 13:30:38 -05:00
|
|
|
self.popen_vargs = ['yices-smt2', '--incremental']
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
if self.solver == "z3":
|
2016-10-03 13:30:38 -05:00
|
|
|
self.popen_vargs = ['z3', '-smt2', '-in']
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
if self.solver == "cvc4":
|
2016-10-03 13:30:38 -05:00
|
|
|
self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2']
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
if self.solver == "mathsat":
|
2016-10-03 13:30:38 -05:00
|
|
|
self.popen_vargs = ['mathsat']
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-03 07:26:00 -05:00
|
|
|
if self.solver == "boolector":
|
2016-10-03 13:30:38 -05:00
|
|
|
self.popen_vargs = ['boolector', '--smt2', '-i']
|
2016-09-07 13:57:56 -05:00
|
|
|
self.unroll = True
|
|
|
|
|
2016-10-01 06:54:21 -05:00
|
|
|
if self.solver == "abc":
|
2016-10-03 13:30:38 -05:00
|
|
|
self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000']
|
2016-10-03 13:43:38 -05:00
|
|
|
self.logic_ax = False
|
2016-10-01 06:54:21 -05:00
|
|
|
self.unroll = True
|
2016-10-03 13:30:38 -05:00
|
|
|
self.noincr = True
|
2016-10-01 06:54:21 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if self.solver == "dummy":
|
|
|
|
assert self.dummy_file is not None
|
|
|
|
self.dummy_fd = open(self.dummy_file, "r")
|
|
|
|
else:
|
|
|
|
if self.dummy_file is not None:
|
|
|
|
self.dummy_fd = open(self.dummy_file, "w")
|
2016-10-03 13:30:38 -05:00
|
|
|
if not self.noincr:
|
|
|
|
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
2016-09-19 13:43:28 -05:00
|
|
|
|
2016-09-07 13:57:56 -05:00
|
|
|
if self.unroll:
|
2016-09-18 13:48:09 -05:00
|
|
|
self.logic_uf = False
|
2016-09-07 13:57:56 -05:00
|
|
|
self.unroll_idcnt = 0
|
|
|
|
self.unroll_buffer = ""
|
|
|
|
self.unroll_sorts = set()
|
|
|
|
self.unroll_objs = set()
|
|
|
|
self.unroll_decls = dict()
|
|
|
|
self.unroll_cache = dict()
|
|
|
|
self.unroll_stack = list()
|
2016-09-03 07:26:00 -05:00
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
self.start_time = time()
|
|
|
|
|
2016-07-11 04:49:05 -05:00
|
|
|
self.modinfo = dict()
|
|
|
|
self.curmod = None
|
|
|
|
self.topmod = None
|
2016-09-18 13:48:09 -05:00
|
|
|
self.setup_done = False
|
|
|
|
|
|
|
|
def setup(self):
|
|
|
|
assert not self.setup_done
|
|
|
|
|
|
|
|
if self.logic is None:
|
|
|
|
self.logic = ""
|
|
|
|
if self.logic_qf: self.logic += "QF_"
|
|
|
|
if self.logic_ax: self.logic += "A"
|
|
|
|
if self.logic_uf: self.logic += "UF"
|
|
|
|
if self.logic_bv: self.logic += "BV"
|
2016-07-11 04:49:05 -05:00
|
|
|
|
2016-09-18 13:48:09 -05:00
|
|
|
self.setup_done = True
|
2016-09-24 13:40:22 -05:00
|
|
|
|
|
|
|
if self.produce_models:
|
|
|
|
self.write("(set-option :produce-models true)")
|
|
|
|
|
2016-09-18 13:48:09 -05:00
|
|
|
self.write("(set-logic %s)" % self.logic)
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-24 13:40:22 -05:00
|
|
|
for stmt in self.info_stmts:
|
|
|
|
self.write(stmt)
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
def timestamp(self):
|
|
|
|
secs = int(time() - self.start_time)
|
2015-10-13 18:27:55 -05:00
|
|
|
return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-07 13:57:56 -05:00
|
|
|
def replace_in_stmt(self, stmt, pat, repl):
|
|
|
|
if stmt == pat:
|
|
|
|
return repl
|
|
|
|
|
|
|
|
if isinstance(stmt, list):
|
|
|
|
return [self.replace_in_stmt(s, pat, repl) for s in stmt]
|
|
|
|
|
|
|
|
return stmt
|
|
|
|
|
|
|
|
def unroll_stmt(self, stmt):
|
|
|
|
if not isinstance(stmt, list):
|
|
|
|
return stmt
|
|
|
|
|
|
|
|
stmt = [self.unroll_stmt(s) for s in stmt]
|
|
|
|
|
|
|
|
if len(stmt) >= 2 and not isinstance(stmt[0], list) and stmt[0] in self.unroll_decls:
|
|
|
|
assert stmt[1] in self.unroll_objs
|
|
|
|
|
|
|
|
key = tuple(stmt)
|
|
|
|
if key not in self.unroll_cache:
|
|
|
|
decl = deepcopy(self.unroll_decls[key[0]])
|
|
|
|
|
|
|
|
self.unroll_cache[key] = "|UNROLL#%d|" % self.unroll_idcnt
|
|
|
|
decl[1] = self.unroll_cache[key]
|
|
|
|
self.unroll_idcnt += 1
|
|
|
|
|
|
|
|
if decl[0] == "declare-fun":
|
|
|
|
if isinstance(decl[3], list) or decl[3] not in self.unroll_sorts:
|
|
|
|
self.unroll_objs.add(decl[1])
|
|
|
|
decl[2] = list()
|
|
|
|
else:
|
|
|
|
self.unroll_objs.add(decl[1])
|
|
|
|
decl = list()
|
|
|
|
|
|
|
|
elif decl[0] == "define-fun":
|
|
|
|
arg_index = 1
|
|
|
|
for arg_name, arg_sort in decl[2]:
|
|
|
|
decl[4] = self.replace_in_stmt(decl[4], arg_name, key[arg_index])
|
|
|
|
arg_index += 1
|
|
|
|
decl[2] = list()
|
|
|
|
|
|
|
|
if len(decl) > 0:
|
|
|
|
decl = self.unroll_stmt(decl)
|
|
|
|
self.write(self.unparse(decl), unroll=False)
|
|
|
|
|
|
|
|
return self.unroll_cache[key]
|
|
|
|
|
|
|
|
return stmt
|
|
|
|
|
|
|
|
def write(self, stmt, unroll=True):
|
2016-09-18 13:48:09 -05:00
|
|
|
if stmt.startswith(";"):
|
|
|
|
self.info(stmt)
|
|
|
|
elif not self.setup_done:
|
|
|
|
self.setup()
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
stmt = stmt.strip()
|
2016-09-03 07:26:00 -05:00
|
|
|
|
2016-09-24 13:40:22 -05:00
|
|
|
if self.nocomments or self.unroll:
|
2016-09-07 13:57:56 -05:00
|
|
|
if stmt.startswith(";"):
|
2016-09-03 07:26:00 -05:00
|
|
|
return
|
2016-09-07 13:57:56 -05:00
|
|
|
stmt = re.sub(r" ;.*", "", stmt)
|
2016-09-24 13:40:22 -05:00
|
|
|
|
|
|
|
if unroll and self.unroll:
|
2016-09-07 13:57:56 -05:00
|
|
|
stmt = self.unroll_buffer + stmt
|
|
|
|
self.unroll_buffer = ""
|
|
|
|
|
|
|
|
s = re.sub(r"\|[^|]*\|", "", stmt)
|
|
|
|
if s.count("(") != s.count(")"):
|
|
|
|
self.unroll_buffer = stmt + " "
|
|
|
|
return
|
|
|
|
|
|
|
|
s = self.parse(stmt)
|
|
|
|
|
|
|
|
if self.debug_print:
|
|
|
|
print("-> %s" % s)
|
|
|
|
|
|
|
|
if len(s) == 3 and s[0] == "declare-sort" and s[2] == "0":
|
|
|
|
self.unroll_sorts.add(s[1])
|
|
|
|
return
|
|
|
|
|
|
|
|
elif len(s) == 4 and s[0] == "declare-fun" and s[2] == [] and s[3] in self.unroll_sorts:
|
|
|
|
self.unroll_objs.add(s[1])
|
|
|
|
return
|
|
|
|
|
|
|
|
elif len(s) >= 4 and s[0] == "declare-fun":
|
|
|
|
for arg_sort in s[2]:
|
|
|
|
if arg_sort in self.unroll_sorts:
|
|
|
|
self.unroll_decls[s[1]] = s
|
|
|
|
return
|
|
|
|
|
|
|
|
elif len(s) >= 4 and s[0] == "define-fun":
|
|
|
|
for arg_name, arg_sort in s[2]:
|
|
|
|
if arg_sort in self.unroll_sorts:
|
|
|
|
self.unroll_decls[s[1]] = s
|
|
|
|
return
|
|
|
|
|
|
|
|
stmt = self.unparse(self.unroll_stmt(s))
|
|
|
|
|
|
|
|
if stmt == "(push 1)":
|
|
|
|
self.unroll_stack.append((
|
|
|
|
deepcopy(self.unroll_sorts),
|
|
|
|
deepcopy(self.unroll_objs),
|
|
|
|
deepcopy(self.unroll_decls),
|
|
|
|
deepcopy(self.unroll_cache),
|
|
|
|
))
|
|
|
|
|
|
|
|
if stmt == "(pop 1)":
|
|
|
|
self.unroll_sorts, self.unroll_objs, self.unroll_decls, self.unroll_cache = self.unroll_stack.pop()
|
2016-09-03 07:26:00 -05:00
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
if self.debug_print:
|
|
|
|
print("> %s" % stmt)
|
2016-09-03 07:26:00 -05:00
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
if self.debug_file:
|
|
|
|
print(stmt, file=self.debug_file)
|
|
|
|
self.debug_file.flush()
|
2016-09-03 07:26:00 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if self.solver != "dummy":
|
2016-10-03 13:30:38 -05:00
|
|
|
if self.noincr:
|
2016-10-03 17:54:44 -05:00
|
|
|
if self.p is not None and not stmt.startswith("(get-"):
|
|
|
|
self.p.stdin.close()
|
|
|
|
self.p = None
|
2016-10-03 13:30:38 -05:00
|
|
|
if stmt == "(push 1)":
|
|
|
|
self.smt2cache.append(list())
|
|
|
|
elif stmt == "(pop 1)":
|
|
|
|
self.smt2cache.pop()
|
|
|
|
else:
|
2016-10-03 17:54:44 -05:00
|
|
|
if self.p is not None:
|
|
|
|
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
|
|
|
self.p.stdin.flush()
|
2016-10-03 13:30:38 -05:00
|
|
|
self.smt2cache[-1].append(stmt)
|
|
|
|
else:
|
|
|
|
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
|
|
|
self.p.stdin.flush()
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-07-11 05:49:33 -05:00
|
|
|
def info(self, stmt):
|
2016-07-11 04:49:05 -05:00
|
|
|
if not stmt.startswith("; yosys-smt2-"):
|
|
|
|
return
|
|
|
|
|
|
|
|
fields = stmt.split()
|
|
|
|
|
2016-09-18 13:48:09 -05:00
|
|
|
if fields[1] == "yosys-smt2-nomem":
|
|
|
|
if self.logic is None:
|
|
|
|
self.logic_ax = False
|
|
|
|
|
|
|
|
if fields[1] == "yosys-smt2-nobv":
|
|
|
|
if self.logic is None:
|
|
|
|
self.logic_bv = False
|
|
|
|
|
2016-07-11 04:49:05 -05:00
|
|
|
if fields[1] == "yosys-smt2-module":
|
|
|
|
self.curmod = fields[2]
|
2016-09-02 05:09:09 -05:00
|
|
|
self.modinfo[self.curmod] = SmtModInfo()
|
2016-07-11 04:49:05 -05:00
|
|
|
|
|
|
|
if fields[1] == "yosys-smt2-cell":
|
|
|
|
self.modinfo[self.curmod].cells[fields[3]] = fields[2]
|
|
|
|
|
|
|
|
if fields[1] == "yosys-smt2-topmod":
|
|
|
|
self.topmod = fields[2]
|
|
|
|
|
|
|
|
if fields[1] == "yosys-smt2-input":
|
|
|
|
self.modinfo[self.curmod].inputs.add(fields[2])
|
|
|
|
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
|
|
|
|
|
|
|
if fields[1] == "yosys-smt2-output":
|
|
|
|
self.modinfo[self.curmod].outputs.add(fields[2])
|
|
|
|
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
|
|
|
|
|
|
|
if fields[1] == "yosys-smt2-register":
|
|
|
|
self.modinfo[self.curmod].registers.add(fields[2])
|
|
|
|
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
|
|
|
|
2016-08-20 11:42:32 -05:00
|
|
|
if fields[1] == "yosys-smt2-memory":
|
2016-08-21 08:56:22 -05:00
|
|
|
self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]))
|
2016-08-20 11:42:32 -05:00
|
|
|
|
2016-07-11 04:49:05 -05:00
|
|
|
if fields[1] == "yosys-smt2-wire":
|
|
|
|
self.modinfo[self.curmod].wires.add(fields[2])
|
|
|
|
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
|
|
|
|
2016-08-17 13:10:02 -05:00
|
|
|
if fields[1] == "yosys-smt2-assert":
|
|
|
|
self.modinfo[self.curmod].asserts[fields[2]] = fields[3]
|
|
|
|
|
2016-08-30 12:27:42 -05:00
|
|
|
if fields[1] == "yosys-smt2-anyconst":
|
|
|
|
self.modinfo[self.curmod].anyconsts[fields[2]] = fields[3]
|
|
|
|
|
2016-08-20 11:42:32 -05:00
|
|
|
def hiernets(self, top, regs_only=False):
|
2016-07-11 05:49:33 -05:00
|
|
|
def hiernets_worker(nets, mod, cursor):
|
|
|
|
for netname in sorted(self.modinfo[mod].wsize.keys()):
|
2016-08-20 11:42:32 -05:00
|
|
|
if not regs_only or netname in self.modinfo[mod].registers:
|
|
|
|
nets.append(cursor + [netname])
|
2016-07-11 05:49:33 -05:00
|
|
|
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
|
|
|
hiernets_worker(nets, celltype, cursor + [cellname])
|
|
|
|
|
|
|
|
nets = list()
|
|
|
|
hiernets_worker(nets, top, [])
|
|
|
|
return nets
|
|
|
|
|
2016-08-20 11:42:32 -05:00
|
|
|
def hiermems(self, top):
|
|
|
|
def hiermems_worker(mems, mod, cursor):
|
|
|
|
for memname in sorted(self.modinfo[mod].memories.keys()):
|
|
|
|
mems.append(cursor + [memname])
|
|
|
|
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
|
|
|
hiermems_worker(mems, celltype, cursor + [cellname])
|
|
|
|
|
|
|
|
mems = list()
|
|
|
|
hiermems_worker(mems, top, [])
|
|
|
|
return mems
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
def read(self):
|
|
|
|
stmt = []
|
|
|
|
count_brackets = 0
|
|
|
|
|
|
|
|
while True:
|
2016-09-19 13:43:28 -05:00
|
|
|
if self.solver == "dummy":
|
|
|
|
line = self.dummy_fd.readline().strip()
|
|
|
|
else:
|
|
|
|
line = self.p.stdout.readline().decode("ascii").strip()
|
|
|
|
if self.dummy_file is not None:
|
|
|
|
self.dummy_fd.write(line + "\n")
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
count_brackets += line.count("(")
|
|
|
|
count_brackets -= line.count(")")
|
|
|
|
stmt.append(line)
|
2016-09-19 13:43:28 -05:00
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
if self.debug_print:
|
|
|
|
print("< %s" % line)
|
|
|
|
if count_brackets == 0:
|
|
|
|
break
|
2016-09-19 13:43:28 -05:00
|
|
|
if self.solver != "dummy" and self.p.poll():
|
2015-10-13 10:17:23 -05:00
|
|
|
print("SMT Solver terminated unexpectedly: %s" % "".join(stmt))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
stmt = "".join(stmt)
|
|
|
|
if stmt.startswith("(error"):
|
|
|
|
print("SMT Solver Error: %s" % stmt, file=sys.stderr)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
return stmt
|
|
|
|
|
|
|
|
def check_sat(self):
|
|
|
|
if self.debug_print:
|
|
|
|
print("> (check-sat)")
|
2016-09-24 13:40:22 -05:00
|
|
|
if self.debug_file and not self.nocomments:
|
2015-10-13 10:17:23 -05:00
|
|
|
print("; running check-sat..", file=self.debug_file)
|
|
|
|
self.debug_file.flush()
|
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if self.solver != "dummy":
|
2016-10-03 13:30:38 -05:00
|
|
|
if self.noincr:
|
|
|
|
if self.p is not None:
|
|
|
|
self.p.stdin.close()
|
2016-10-03 17:54:44 -05:00
|
|
|
self.p = None
|
2016-10-03 13:30:38 -05:00
|
|
|
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
|
|
for cache_ctx in self.smt2cache:
|
|
|
|
for cache_stmt in cache_ctx:
|
|
|
|
self.p.stdin.write(bytes(cache_stmt + "\n", "ascii"))
|
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
self.p.stdin.write(bytes("(check-sat)\n", "ascii"))
|
|
|
|
self.p.stdin.flush()
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if self.timeinfo:
|
|
|
|
i = 0
|
|
|
|
s = "/-\|"
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
count = 0
|
|
|
|
num_bs = 0
|
|
|
|
while select([self.p.stdout], [], [], 0.1) == ([], [], []):
|
|
|
|
count += 1
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if count < 25:
|
|
|
|
continue
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if count % 10 == 0 or count == 25:
|
|
|
|
secs = count // 10
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if secs < 60:
|
|
|
|
m = "(%d seconds)" % secs
|
|
|
|
elif secs < 60*60:
|
|
|
|
m = "(%d seconds -- %d:%02d)" % (secs, secs // 60, secs % 60)
|
|
|
|
else:
|
|
|
|
m = "(%d seconds -- %d:%02d:%02d)" % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
print("%s %s %c" % ("\b \b" * num_bs, m, s[i]), end="", file=sys.stderr)
|
|
|
|
num_bs = len(m) + 3
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
else:
|
|
|
|
print("\b" + s[i], end="", file=sys.stderr)
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
sys.stderr.flush()
|
|
|
|
i = (i + 1) % len(s)
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
if num_bs != 0:
|
|
|
|
print("\b \b" * num_bs, end="", file=sys.stderr)
|
|
|
|
sys.stderr.flush()
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
result = self.read()
|
|
|
|
if self.debug_file:
|
|
|
|
print("(set-info :status %s)" % result, file=self.debug_file)
|
|
|
|
print("(check-sat)", file=self.debug_file)
|
|
|
|
self.debug_file.flush()
|
|
|
|
return result
|
|
|
|
|
|
|
|
def parse(self, stmt):
|
|
|
|
def worker(stmt):
|
|
|
|
if stmt[0] == '(':
|
|
|
|
expr = []
|
|
|
|
cursor = 1
|
|
|
|
while stmt[cursor] != ')':
|
|
|
|
el, le = worker(stmt[cursor:])
|
|
|
|
expr.append(el)
|
|
|
|
cursor += le
|
|
|
|
return expr, cursor+1
|
|
|
|
|
|
|
|
if stmt[0] == '|':
|
|
|
|
expr = "|"
|
|
|
|
cursor = 1
|
|
|
|
while stmt[cursor] != '|':
|
|
|
|
expr += stmt[cursor]
|
|
|
|
cursor += 1
|
|
|
|
expr += "|"
|
|
|
|
return expr, cursor+1
|
|
|
|
|
|
|
|
if stmt[0] in [" ", "\t", "\r", "\n"]:
|
|
|
|
el, le = worker(stmt[1:])
|
|
|
|
return el, le+1
|
|
|
|
|
|
|
|
expr = ""
|
|
|
|
cursor = 0
|
|
|
|
while stmt[cursor] not in ["(", ")", "|", " ", "\t", "\r", "\n"]:
|
|
|
|
expr += stmt[cursor]
|
|
|
|
cursor += 1
|
|
|
|
return expr, cursor
|
|
|
|
return worker(stmt)[0]
|
|
|
|
|
2016-09-07 13:57:56 -05:00
|
|
|
def unparse(self, stmt):
|
|
|
|
if isinstance(stmt, list):
|
|
|
|
return "(" + " ".join([self.unparse(s) for s in stmt]) + ")"
|
|
|
|
return stmt
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
def bv2hex(self, v):
|
|
|
|
h = ""
|
2016-09-02 03:12:30 -05:00
|
|
|
v = self.bv2bin(v)
|
2015-10-14 16:23:25 -05:00
|
|
|
while len(v) > 0:
|
2015-10-13 10:17:23 -05:00
|
|
|
d = 0
|
|
|
|
if len(v) > 0 and v[-1] == "1": d += 1
|
|
|
|
if len(v) > 1 and v[-2] == "1": d += 2
|
|
|
|
if len(v) > 2 and v[-3] == "1": d += 4
|
|
|
|
if len(v) > 3 and v[-4] == "1": d += 8
|
|
|
|
h = hex(d)[2:] + h
|
|
|
|
if len(v) < 4: break
|
|
|
|
v = v[:-4]
|
|
|
|
return h
|
|
|
|
|
|
|
|
def bv2bin(self, v):
|
|
|
|
if v == "true": return "1"
|
|
|
|
if v == "false": return "0"
|
2015-10-14 16:23:25 -05:00
|
|
|
if v.startswith("#b"):
|
|
|
|
return v[2:]
|
|
|
|
if v.startswith("#x"):
|
2016-09-02 04:50:23 -05:00
|
|
|
return "".join(hex_dict.get(x) for x in v[2:])
|
2015-10-14 16:23:25 -05:00
|
|
|
assert False
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-08-21 08:56:22 -05:00
|
|
|
def bv2int(self, v):
|
|
|
|
return int(self.bv2bin(v), 2)
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
def get(self, expr):
|
|
|
|
self.write("(get-value (%s))" % (expr))
|
|
|
|
return self.parse(self.read())[0][1]
|
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
def get_list(self, expr_list):
|
2016-08-20 11:42:32 -05:00
|
|
|
if len(expr_list) == 0:
|
|
|
|
return []
|
|
|
|
self.write("(get-value (%s))" % " ".join(expr_list))
|
|
|
|
return [n[1] for n in self.parse(self.read())]
|
2016-08-18 04:17:45 -05:00
|
|
|
|
2016-08-22 10:27:43 -05:00
|
|
|
def get_path(self, mod, path):
|
|
|
|
assert mod in self.modinfo
|
|
|
|
path = path.split(".")
|
|
|
|
|
|
|
|
for i in range(len(path)-1):
|
|
|
|
first = ".".join(path[0:i+1])
|
|
|
|
second = ".".join(path[i+1:])
|
|
|
|
|
|
|
|
if first in self.modinfo[mod].cells:
|
|
|
|
nextmod = self.modinfo[mod].cells[first]
|
|
|
|
return [first] + self.get_path(nextmod, second)
|
|
|
|
|
|
|
|
return [".".join(path)]
|
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
def net_expr(self, mod, base, path):
|
|
|
|
if len(path) == 1:
|
|
|
|
assert mod in self.modinfo
|
2016-10-08 05:25:34 -05:00
|
|
|
if path[0] == "":
|
|
|
|
return base
|
2016-10-02 15:08:30 -05:00
|
|
|
if path[0] in self.modinfo[mod].cells:
|
|
|
|
return "(|%s_h %s| %s)" % (mod, path[0], base)
|
2016-08-22 10:27:43 -05:00
|
|
|
if path[0] in self.modinfo[mod].wsize:
|
|
|
|
return "(|%s_n %s| %s)" % (mod, path[0], base)
|
|
|
|
if path[0] in self.modinfo[mod].memories:
|
|
|
|
return "(|%s_m %s| %s)" % (mod, path[0], base)
|
|
|
|
assert 0
|
2016-08-18 04:17:45 -05:00
|
|
|
|
|
|
|
assert mod in self.modinfo
|
|
|
|
assert path[0] in self.modinfo[mod].cells
|
2016-07-11 05:49:33 -05:00
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
nextmod = self.modinfo[mod].cells[path[0]]
|
|
|
|
nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
|
|
|
|
return self.net_expr(nextmod, nextbase, path[1:])
|
|
|
|
|
|
|
|
def net_width(self, mod, net_path):
|
|
|
|
for i in range(len(net_path)-1):
|
2016-07-11 05:49:33 -05:00
|
|
|
assert mod in self.modinfo
|
2016-08-18 04:17:45 -05:00
|
|
|
assert net_path[i] in self.modinfo[mod].cells
|
|
|
|
mod = self.modinfo[mod].cells[net_path[i]]
|
2016-07-11 05:49:33 -05:00
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
assert mod in self.modinfo
|
|
|
|
assert net_path[-1] in self.modinfo[mod].wsize
|
|
|
|
return self.modinfo[mod].wsize[net_path[-1]]
|
2016-07-11 05:49:33 -05:00
|
|
|
|
2016-08-21 08:56:22 -05:00
|
|
|
def mem_expr(self, mod, base, path, portidx=None, infomode=False):
|
2016-08-20 11:42:32 -05:00
|
|
|
if len(path) == 1:
|
|
|
|
assert mod in self.modinfo
|
|
|
|
assert path[0] in self.modinfo[mod].memories
|
2016-08-21 08:56:22 -05:00
|
|
|
if infomode:
|
|
|
|
return self.modinfo[mod].memories[path[0]]
|
|
|
|
return "(|%s_m%s %s| %s)" % (mod, "" if portidx is None else ":%d" % portidx, path[0], base)
|
2016-08-20 11:42:32 -05:00
|
|
|
|
|
|
|
assert mod in self.modinfo
|
|
|
|
assert path[0] in self.modinfo[mod].cells
|
|
|
|
|
|
|
|
nextmod = self.modinfo[mod].cells[path[0]]
|
|
|
|
nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
|
2016-08-26 10:33:02 -05:00
|
|
|
return self.mem_expr(nextmod, nextbase, path[1:], portidx=portidx, infomode=infomode)
|
2016-08-20 11:42:32 -05:00
|
|
|
|
2016-08-21 08:56:22 -05:00
|
|
|
def mem_info(self, mod, base, path):
|
|
|
|
return self.mem_expr(mod, base, path, infomode=True)
|
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
def get_net(self, mod_name, net_path, state_name):
|
|
|
|
return self.get(self.net_expr(mod_name, state_name, net_path))
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
def get_net_list(self, mod_name, net_path_list, state_name):
|
|
|
|
return self.get_list([self.net_expr(mod_name, state_name, n) for n in net_path_list])
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-07-11 05:49:33 -05:00
|
|
|
def get_net_hex(self, mod_name, net_path, state_name):
|
|
|
|
return self.bv2hex(self.get_net(mod_name, net_path, state_name))
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
def get_net_hex_list(self, mod_name, net_path_list, state_name):
|
|
|
|
return [self.bv2hex(v) for v in self.get_net_list(mod_name, net_path_list, state_name)]
|
|
|
|
|
2016-07-11 05:49:33 -05:00
|
|
|
def get_net_bin(self, mod_name, net_path, state_name):
|
|
|
|
return self.bv2bin(self.get_net(mod_name, net_path, state_name))
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-08-18 04:17:45 -05:00
|
|
|
def get_net_bin_list(self, mod_name, net_path_list, state_name):
|
|
|
|
return [self.bv2bin(v) for v in self.get_net_list(mod_name, net_path_list, state_name)]
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
def wait(self):
|
2016-10-03 17:54:44 -05:00
|
|
|
if self.p is not None:
|
2016-09-19 13:43:28 -05:00
|
|
|
self.p.wait()
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
|
2016-09-02 05:01:31 -05:00
|
|
|
class SmtOpts:
|
2015-10-13 10:17:23 -05:00
|
|
|
def __init__(self):
|
2016-08-20 09:07:59 -05:00
|
|
|
self.shortopts = "s:v"
|
2016-10-03 13:30:38 -05:00
|
|
|
self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
|
2015-10-13 10:17:23 -05:00
|
|
|
self.solver = "z3"
|
|
|
|
self.debug_print = False
|
|
|
|
self.debug_file = None
|
2016-09-19 13:43:28 -05:00
|
|
|
self.dummy_file = None
|
2016-09-07 14:01:51 -05:00
|
|
|
self.unroll = False
|
2016-10-03 13:30:38 -05:00
|
|
|
self.noincr = False
|
2015-10-13 10:17:23 -05:00
|
|
|
self.timeinfo = True
|
2016-09-18 13:48:09 -05:00
|
|
|
self.logic = None
|
2016-09-24 13:40:22 -05:00
|
|
|
self.info_stmts = list()
|
|
|
|
self.nocomments = False
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
def handle(self, o, a):
|
|
|
|
if o == "-s":
|
|
|
|
self.solver = a
|
|
|
|
elif o == "-v":
|
|
|
|
self.debug_print = True
|
2016-09-07 13:57:56 -05:00
|
|
|
elif o == "--unroll":
|
|
|
|
self.unroll = True
|
2016-10-03 13:30:38 -05:00
|
|
|
elif o == "--noincr":
|
|
|
|
self.noincr = True
|
2016-09-24 13:40:22 -05:00
|
|
|
elif o == "--noprogress":
|
2017-01-04 05:03:04 -06:00
|
|
|
self.timeinfo = False
|
2016-08-20 09:07:59 -05:00
|
|
|
elif o == "--dump-smt2":
|
2015-10-13 10:17:23 -05:00
|
|
|
self.debug_file = open(a, "w")
|
2016-09-18 13:48:09 -05:00
|
|
|
elif o == "--logic":
|
|
|
|
self.logic = a
|
2016-09-19 13:43:28 -05:00
|
|
|
elif o == "--dummy":
|
|
|
|
self.dummy_file = a
|
2016-09-24 13:40:22 -05:00
|
|
|
elif o == "--info":
|
|
|
|
self.info_stmts.append(a)
|
|
|
|
elif o == "--nocomments":
|
|
|
|
self.nocomments = True
|
2015-10-13 10:17:23 -05:00
|
|
|
else:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def helpmsg(self):
|
|
|
|
return """
|
|
|
|
-s <solver>
|
2016-09-19 13:43:28 -05:00
|
|
|
set SMT solver: z3, cvc4, yices, mathsat, boolector, dummy
|
2015-10-13 10:17:23 -05:00
|
|
|
default: z3
|
|
|
|
|
2016-09-19 13:43:28 -05:00
|
|
|
--logic <smt2_logic>
|
|
|
|
use the specified SMT2 logic (e.g. QF_AUFBV)
|
|
|
|
|
|
|
|
--dummy <filename>
|
|
|
|
if solver is "dummy", read solver output from that file
|
|
|
|
otherwise: write solver output to that file
|
|
|
|
|
2015-10-13 10:17:23 -05:00
|
|
|
-v
|
|
|
|
enable debug output
|
|
|
|
|
2016-09-07 13:57:56 -05:00
|
|
|
--unroll
|
|
|
|
unroll uninterpreted functions
|
|
|
|
|
2016-10-03 13:30:38 -05:00
|
|
|
--noincr
|
|
|
|
don't use incremental solving, instead restart solver for
|
|
|
|
each (check-sat). This also avoids (push) and (pop).
|
|
|
|
|
2016-09-24 13:40:22 -05:00
|
|
|
--noprogress
|
2016-09-07 13:57:56 -05:00
|
|
|
disable timer display during solving
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-08-20 09:07:59 -05:00
|
|
|
--dump-smt2 <filename>
|
2015-10-13 10:17:23 -05:00
|
|
|
write smt2 statements to file
|
2016-09-24 13:40:22 -05:00
|
|
|
|
|
|
|
--info <smt2-info-stmt>
|
|
|
|
include the specified smt2 info statement in the smt2 output
|
|
|
|
|
|
|
|
--nocomments
|
|
|
|
strip all comments from the generated smt2 code
|
2015-10-13 10:17:23 -05:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
2016-09-02 05:01:31 -05:00
|
|
|
class MkVcd:
|
2015-10-13 10:17:23 -05:00
|
|
|
def __init__(self, f):
|
|
|
|
self.f = f
|
|
|
|
self.t = -1
|
|
|
|
self.nets = dict()
|
|
|
|
|
2016-07-11 05:49:33 -05:00
|
|
|
def add_net(self, path, width):
|
|
|
|
path = tuple(path)
|
2015-10-13 10:17:23 -05:00
|
|
|
assert self.t == -1
|
|
|
|
key = "n%d" % len(self.nets)
|
2016-07-11 05:49:33 -05:00
|
|
|
self.nets[path] = (key, width)
|
2015-10-13 10:17:23 -05:00
|
|
|
|
2016-07-11 05:49:33 -05:00
|
|
|
def set_net(self, path, bits):
|
|
|
|
path = tuple(path)
|
2015-10-13 10:17:23 -05:00
|
|
|
assert self.t >= 0
|
2016-07-11 05:49:33 -05:00
|
|
|
assert path in self.nets
|
|
|
|
print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
|
2015-10-13 10:17:23 -05:00
|
|
|
|
|
|
|
def set_time(self, t):
|
|
|
|
assert t >= self.t
|
|
|
|
if t != self.t:
|
|
|
|
if self.t == -1:
|
2016-09-10 09:24:08 -05:00
|
|
|
print("$var integer 32 t smt_step $end", file=self.f)
|
2015-10-15 08:08:41 -05:00
|
|
|
print("$var event 1 ! smt_clock $end", file=self.f)
|
2016-07-11 05:49:33 -05:00
|
|
|
scope = []
|
|
|
|
for path in sorted(self.nets):
|
|
|
|
while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]):
|
|
|
|
print("$upscope $end", file=self.f)
|
|
|
|
scope = scope[:-1]
|
|
|
|
while len(scope)+1 < len(path):
|
|
|
|
print("$scope module %s $end" % path[len(scope)], file=self.f)
|
|
|
|
scope.append(path[len(scope)-1])
|
|
|
|
key, width = self.nets[path]
|
|
|
|
print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f)
|
|
|
|
for i in range(len(scope)):
|
|
|
|
print("$upscope $end", file=self.f)
|
2015-10-13 10:17:23 -05:00
|
|
|
print("$enddefinitions $end", file=self.f)
|
|
|
|
self.t = t
|
|
|
|
assert self.t >= 0
|
2016-09-10 09:24:08 -05:00
|
|
|
print("#%d" % (10 * self.t), file=self.f)
|
2015-10-15 08:08:41 -05:00
|
|
|
print("1!", file=self.f)
|
2016-09-10 09:24:08 -05:00
|
|
|
print("b%s t" % format(self.t, "032b"), file=self.f)
|
2015-10-13 10:17:23 -05:00
|
|
|
|