aboutsummaryrefslogtreecommitdiffstats
path: root/backends/smt2
diff options
context:
space:
mode:
Diffstat (limited to 'backends/smt2')
-rw-r--r--backends/smt2/Makefile.inc20
-rw-r--r--backends/smt2/smt2.cc283
-rw-r--r--backends/smt2/smtbmc.py467
-rw-r--r--backends/smt2/smtio.py365
4 files changed, 955 insertions, 180 deletions
diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc
index eacda2734..68394a909 100644
--- a/backends/smt2/Makefile.inc
+++ b/backends/smt2/Makefile.inc
@@ -3,14 +3,30 @@ OBJS += backends/smt2/smt2.o
ifneq ($(CONFIG),mxe)
ifneq ($(CONFIG),emcc)
+
+# MSYS targets support yosys-smtbmc, but require a launcher script
+ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64))
+TARGETS += yosys-smtbmc.exe yosys-smtbmc-script.py
+# Needed to find the Python interpreter for yosys-smtbmc scripts.
+# Override if necessary, it is only used for msys2 targets.
+PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3)
+
+yosys-smtbmc-script.py: backends/smt2/smtbmc.py
+ $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \
+ -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
+
+yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py
+ $(P) $(CXX) -DGUI=0 -O -s -o $@ $<
+# Other targets
+else
TARGETS += yosys-smtbmc
yosys-smtbmc: backends/smt2/smtbmc.py
- $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
+ $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
$(Q) chmod +x $@.new
$(Q) mv $@.new $@
+endif
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
endif
endif
-
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index dce7c25de..081dcda99 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -32,15 +32,18 @@ struct Smt2Worker
CellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
- bool bvmode, memmode, wiresmode, verbose, statebv, statedt;
+ bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode;
dict<IdString, int> &mod_stbv_width;
- int idcounter, statebv_width;
+ int idcounter = 0, statebv_width = 0;
std::vector<std::string> decls, trans, hier, dtmembers;
std::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver;
std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue;
pool<Cell*> recursive_cells, registers;
+ pool<SigBit> clock_posedge, clock_negedge;
+ vector<string> ex_state_eq, ex_input_eq;
+
std::map<RTLIL::SigBit, std::pair<int, int>> fcache;
std::map<Cell*, int> memarrays;
std::map<int, int> bvsizes;
@@ -104,16 +107,24 @@ struct Smt2Worker
decls.push_back(decl_str + "\n");
}
- Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, dict<IdString, int> &mod_stbv_width) :
+ Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, bool forallmode,
+ dict<IdString, int> &mod_stbv_width, dict<IdString, dict<IdString, pair<bool, bool>>> &mod_clk_cache) :
ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode),
- verbose(verbose), statebv(statebv), statedt(statedt), mod_stbv_width(mod_stbv_width), idcounter(0), statebv_width(0)
+ verbose(verbose), statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width)
{
+ pool<SigBit> noclock;
+
makebits(stringf("%s_is", get_id(module)));
for (auto cell : module->cells())
- for (auto &conn : cell->connections()) {
+ for (auto &conn : cell->connections())
+ {
+ if (GetSize(conn.second) == 0)
+ continue;
+
bool is_input = ct.cell_input(cell->type, conn.first);
bool is_output = ct.cell_output(cell->type, conn.first);
+
if (is_output && !is_input)
for (auto bit : sigmap(conn.second)) {
if (bit_driver.count(bit))
@@ -123,6 +134,66 @@ struct Smt2Worker
else if (is_output || !is_input)
log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n",
log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type));
+
+ if (cell->type.in("$mem") && conn.first.in("\\RD_CLK", "\\WR_CLK"))
+ {
+ SigSpec clk = sigmap(conn.second);
+ for (int i = 0; i < GetSize(clk); i++)
+ {
+ if (clk[i].wire == nullptr)
+ continue;
+
+ if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_ENABLE" : "\\WR_CLK_ENABLE")[i] != State::S1)
+ continue;
+
+ if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_POLARITY" : "\\WR_CLK_POLARITY")[i] == State::S1)
+ clock_posedge.insert(clk[i]);
+ else
+ clock_negedge.insert(clk[i]);
+ }
+ }
+ else
+ if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_") && conn.first.in("\\CLK", "\\C"))
+ {
+ bool posedge = (cell->type == "$_DFF_N_") || (cell->type == "$dff" && cell->getParam("\\CLK_POLARITY").as_bool());
+ for (auto bit : sigmap(conn.second)) {
+ if (posedge)
+ clock_posedge.insert(bit);
+ else
+ clock_negedge.insert(bit);
+ }
+ }
+ else
+ if (mod_clk_cache.count(cell->type) && mod_clk_cache.at(cell->type).count(conn.first))
+ {
+ for (auto bit : sigmap(conn.second)) {
+ if (mod_clk_cache.at(cell->type).at(conn.first).first)
+ clock_posedge.insert(bit);
+ if (mod_clk_cache.at(cell->type).at(conn.first).second)
+ clock_negedge.insert(bit);
+ }
+ }
+ else
+ {
+ for (auto bit : sigmap(conn.second))
+ noclock.insert(bit);
+ }
+ }
+
+ for (auto bit : noclock) {
+ clock_posedge.erase(bit);
+ clock_negedge.erase(bit);
+ }
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->port_input || GetSize(wire) != 1)
+ continue;
+ SigBit bit = sigmap(wire);
+ if (clock_posedge.count(bit))
+ mod_clk_cache[module->name][wire->name].first = true;
+ if (clock_negedge.count(bit))
+ mod_clk_cache[module->name][wire->name].second = true;
}
}
@@ -326,7 +397,8 @@ struct Smt2Worker
if (type == 's' || type == 'd' || type == 'b') {
width = max(width, GetSize(cell->getPort("\\A")));
- width = max(width, GetSize(cell->getPort("\\B")));
+ if (cell->hasPort("\\B"))
+ width = max(width, GetSize(cell->getPort("\\B")));
}
if (cell->hasPort("\\A")) {
@@ -344,6 +416,7 @@ struct Smt2Worker
for (char ch : expr) {
if (ch == 'A') processed_expr += get_bv(sig_a);
else if (ch == 'B') processed_expr += get_bv(sig_b);
+ else if (ch == 'P') processed_expr += get_bv(cell->getPort("\\B"));
else if (ch == 'L') processed_expr += is_signed ? "a" : "l";
else if (ch == 'U') processed_expr += is_signed ? "s" : "u";
else processed_expr += ch;
@@ -437,6 +510,7 @@ struct Smt2Worker
if (cell->type == "$_ANDNOT_") return export_gate(cell, "(and A (not B))");
if (cell->type == "$_ORNOT_") return export_gate(cell, "(or A (not B))");
if (cell->type == "$_MUX_") return export_gate(cell, "(ite S B A)");
+ if (cell->type == "$_NMUX_") return export_gate(cell, "(not (ite S B A))");
if (cell->type == "$_AOI3_") return export_gate(cell, "(not (or (and A B) C))");
if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))");
if (cell->type == "$_AOI4_") return export_gate(cell, "(not (or (and A B) (and C D)))");
@@ -455,14 +529,16 @@ struct Smt2Worker
return;
}
- if (cell->type.in("$anyconst", "$anyseq"))
+ if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq"))
{
registers.insert(cell);
string infostr = cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell);
if (cell->attributes.count("\\reg"))
infostr += " " + cell->attributes.at("\\reg").decode_string();
- decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, infostr.c_str()));
+ decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort("\\Y")), infostr.c_str()));
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y")));
+ if (cell->type == "$anyseq")
+ ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter));
register_bv(cell->getPort("\\Y"), idcounter++);
recursive_cells.erase(cell);
return;
@@ -480,7 +556,9 @@ struct Smt2Worker
if (cell->type.in("$shift", "$shiftx")) {
if (cell->getParam("\\B_SIGNED").as_bool()) {
- /* FIXME */
+ return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) "
+ "(bvlshr A B) (bvlshr A (bvneg B)))",
+ GetSize(cell->getPort("\\B")), 0), 's');
} else {
return export_bvop(cell, "(bvlshr A B)", 's');
}
@@ -506,6 +584,13 @@ struct Smt2Worker
if (cell->type == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd');
if (cell->type == "$mod") return export_bvop(cell, "(bvUrem A B)", 'd');
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool") &&
+ 2*GetSize(cell->getPort("\\A").chunks()) < GetSize(cell->getPort("\\A"))) {
+ bool is_and = cell->type == "$reduce_and";
+ string bits(GetSize(cell->getPort("\\A")), is_and ? '1' : '0');
+ return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b');
+ }
+
if (cell->type == "$reduce_and") return export_reduce(cell, "(and A)", true);
if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false);
if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false);
@@ -516,7 +601,7 @@ struct Smt2Worker
if (cell->type == "$logic_and") return export_reduce(cell, "(and (or A) (or B))", false);
if (cell->type == "$logic_or") return export_reduce(cell, "(or A B)", false);
- if (cell->type == "$mux" || cell->type == "$pmux")
+ if (cell->type.in("$mux", "$pmux"))
{
int width = GetSize(cell->getPort("\\Y"));
std::string processed_expr = get_bv(cell->getPort("\\A"));
@@ -554,17 +639,30 @@ struct Smt2Worker
int rd_ports = cell->getParam("\\RD_PORTS").as_int();
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
- decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d\n", get_id(cell), abits, width, rd_ports, wr_ports));
+ bool async_read = false;
+ if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) {
+ if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_zero())
+ log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module));
+ async_read = true;
+ }
+
+ decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(cell), abits, width, rd_ports, wr_ports, async_read ? "async" : "sync"));
+
+ string memstate;
+ if (async_read) {
+ memstate = stringf("%s#%d#final", get_id(module), arrayid);
+ } else {
+ memstate = stringf("%s#%d#0", get_id(module), arrayid);
+ }
if (statebv)
{
int mem_size = cell->getParam("\\SIZE").as_int();
int mem_offset = cell->getParam("\\OFFSET").as_int();
- makebits(stringf("%s#%d#0", get_id(module), arrayid), width*mem_size, get_id(cell));
-
- decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d#0| state))\n",
- get_id(module), get_id(cell), get_id(module), width*mem_size, get_id(module), arrayid));
+ makebits(memstate, width*mem_size, get_id(cell));
+ decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n",
+ get_id(module), get_id(cell), get_id(module), width*mem_size, memstate.c_str()));
for (int i = 0; i < rd_ports; i++)
{
@@ -584,9 +682,9 @@ struct Smt2Worker
read_expr += "0";
for (int k = 0; k < mem_size; k++)
- read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s#%d#0| state))\n %s)",
+ read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)",
get_id(module), i, get_id(cell), Const(k+mem_offset, abits).as_string().c_str(),
- width*(k+1)-1, width*k, get_id(module), arrayid, read_expr.c_str());
+ width*(k+1)-1, width*k, memstate.c_str(), read_expr.c_str());
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n",
get_id(module), idcounter, get_id(module), width, read_expr.c_str(), log_signal(data_sig)));
@@ -600,14 +698,14 @@ struct Smt2Worker
else
{
if (statedt)
- dtmembers.push_back(stringf(" (|%s#%d#0| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
- get_id(module), arrayid, abits, width, get_id(cell)));
+ dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ memstate.c_str(), abits, width, get_id(cell)));
else
- decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
- get_id(module), arrayid, get_id(module), abits, width, get_id(cell)));
+ decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ memstate.c_str(), get_id(module), abits, width, get_id(cell)));
- decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n",
- get_id(module), get_id(cell), get_id(module), abits, width, get_id(module), arrayid));
+ decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n",
+ get_id(module), get_id(cell), get_id(module), abits, width, memstate.c_str()));
for (int i = 0; i < rd_ports; i++)
{
@@ -622,8 +720,8 @@ struct Smt2Worker
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
- decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) (|%s_m:R%dA %s| state))) ; %s\n",
- get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, get_id(module), i, get_id(cell), log_signal(data_sig)));
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n",
+ get_id(module), idcounter, get_id(module), width, memstate.c_str(), get_id(module), i, get_id(cell), log_signal(data_sig)));
decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n",
get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter));
@@ -646,6 +744,9 @@ struct Smt2Worker
for (auto &conn : cell->connections())
{
+ if (GetSize(conn.second) == 0)
+ continue;
+
Wire *w = m->wire(conn.first);
SigSpec sig = sigmap(conn.second);
@@ -669,7 +770,7 @@ struct Smt2Worker
if (statebv)
makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type));
- if (statedt)
+ else if (statedt)
dtmembers.push_back(stringf(" (|%s_h %s| |%s_s|)\n",
get_id(module), get_id(cell->name), get_id(cell->type)));
else
@@ -713,17 +814,30 @@ struct Smt2Worker
decls.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\'))
decls.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width));
+ if (GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig)))
+ decls.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire),
+ clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : ""));
if (bvmode && GetSize(sig) > 1) {
decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n",
get_id(module), get_id(wire), get_id(module), GetSize(sig), get_bv(sig).c_str()));
+ if (wire->port_input)
+ ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))",
+ get_id(module), get_id(wire), get_id(module), get_id(wire)));
} else {
for (int i = 0; i < GetSize(sig); i++)
- if (GetSize(sig) > 1)
+ if (GetSize(sig) > 1) {
decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n",
get_id(module), get_id(wire), i, get_id(module), get_bool(sig[i]).c_str()));
- else
+ if (wire->port_input)
+ ex_input_eq.push_back(stringf(" (= (|%s_n %s %d| state) (|%s_n %s %d| other_state))",
+ get_id(module), get_id(wire), i, get_id(module), get_id(wire), i));
+ } else {
decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n",
get_id(module), get_id(wire), get_id(module), get_bool(sig[i]).c_str()));
+ if (wire->port_input)
+ ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))",
+ get_id(module), get_id(wire), get_id(module), get_id(wire)));
+ }
}
}
}
@@ -775,8 +889,8 @@ struct Smt2Worker
string name_a = get_bool(cell->getPort("\\A"));
string name_en = get_bool(cell->getPort("\\EN"));
- decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id,
- cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
+ string infostr = (cell->name[0] == '$' && cell->attributes.count("\\src")) ? cell->attributes.at("\\src").decode_string() : get_id(cell);
+ decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, infostr.c_str()));
if (cell->type == "$cover")
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
@@ -811,8 +925,14 @@ struct Smt2Worker
Module *m = module->design->module(cell->type);
log_assert(m != nullptr);
+ hier.push_back(stringf(" (= (|%s_is| state) (|%s_is| %s))\n",
+ get_id(module), get_id(cell->type), cell_state.c_str()));
+
for (auto &conn : cell->connections())
{
+ if (GetSize(conn.second) == 0)
+ continue;
+
Wire *w = m->wire(conn.first);
SigSpec sig = sigmap(conn.second);
@@ -842,6 +962,7 @@ struct Smt2Worker
std::string expr_d = get_bool(cell->getPort("\\D"));
std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q"))));
+ ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort("\\Q")).c_str(), get_bool(cell->getPort("\\Q"), "other_state").c_str()));
}
if (cell->type.in("$ff", "$dff"))
@@ -849,13 +970,16 @@ struct Smt2Worker
std::string expr_d = get_bv(cell->getPort("\\D"));
std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q"))));
+ ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Q")).c_str(), get_bv(cell->getPort("\\Q"), "other_state").c_str()));
}
- if (cell->type == "$anyconst")
+ if (cell->type.in("$anyconst", "$allconst"))
{
std::string expr_d = get_bv(cell->getPort("\\Y"));
std::string expr_q = get_bv(cell->getPort("\\Y"), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Y"))));
+ if (cell->type == "$anyconst")
+ ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Y")).c_str(), get_bv(cell->getPort("\\Y"), "other_state").c_str()));
}
if (cell->type == "$mem")
@@ -866,11 +990,25 @@ struct Smt2Worker
int width = cell->getParam("\\WIDTH").as_int();
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
+ bool async_read = false;
+ string initial_memstate, final_memstate;
+
+ if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) {
+ log_assert(cell->getParam("\\WR_CLK_ENABLE").is_fully_zero());
+ async_read = true;
+ initial_memstate = stringf("%s#%d#0", get_id(module), arrayid);
+ final_memstate = stringf("%s#%d#final", get_id(module), arrayid);
+ }
+
if (statebv)
{
int mem_size = cell->getParam("\\SIZE").as_int();
int mem_offset = cell->getParam("\\OFFSET").as_int();
+ if (async_read) {
+ makebits(final_memstate, width*mem_size, get_id(cell));
+ }
+
for (int i = 0; i < wr_ports; i++)
{
SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits);
@@ -909,6 +1047,15 @@ struct Smt2Worker
}
else
{
+ if (async_read) {
+ if (statedt)
+ dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ initial_memstate.c_str(), abits, width, get_id(cell)));
+ else
+ decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ initial_memstate.c_str(), get_id(module), abits, width, get_id(cell)));
+ }
+
for (int i = 0; i < wr_ports; i++)
{
SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits);
@@ -944,6 +1091,10 @@ struct Smt2Worker
std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, wr_ports);
std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid);
trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell)));
+ ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid));
+
+ if (async_read)
+ hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(cell)));
Const init_data = cell->getParam("\\INIT");
int memsize = cell->getParam("\\SIZE").as_int();
@@ -954,20 +1105,27 @@ struct Smt2Worker
break;
Const initword = init_data.extract(i*width, width, State::Sx);
+ Const initmask = initword;
bool gen_init_constr = false;
- for (auto bit : initword.bits)
- if (bit == State::S0 || bit == State::S1)
+ for (int k = 0; k < GetSize(initword); k++) {
+ if (initword[k] == State::S0 || initword[k] == State::S1) {
gen_init_constr = true;
+ initmask[k] = State::S1;
+ } else {
+ initmask[k] = State::S0;
+ initword[k] = State::S0;
+ }
+ }
if (gen_init_constr)
{
if (statebv)
/* FIXME */;
else
- init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]",
+ init_list.push_back(stringf("(= (bvand (select (|%s#%d#0| state) #b%s) #b%s) #b%s) ; %s[%d]",
get_id(module), arrayid, Const(i, abits).as_string().c_str(),
- initword.as_string().c_str(), get_id(cell), i));
+ initmask.as_string().c_str(), initword.as_string().c_str(), get_id(cell), i));
}
}
}
@@ -983,6 +1141,37 @@ struct Smt2Worker
hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name)));
trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n",
get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name)));
+ ex_state_eq.push_back(stringf("(|%s_ex_state_eq| (|%s_h %s| state) (|%s_h %s| other_state))\n",
+ get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name)));
+ }
+
+ if (forallmode)
+ {
+ string expr = ex_state_eq.empty() ? "true" : "(and";
+ if (!ex_state_eq.empty()) {
+ if (GetSize(ex_state_eq) == 1) {
+ expr = "\n " + ex_state_eq.front() + "\n";
+ } else {
+ for (auto &str : ex_state_eq)
+ expr += stringf("\n %s", str.c_str());
+ expr += "\n)";
+ }
+ }
+ decls.push_back(stringf("(define-fun |%s_ex_state_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(module), get_id(module), expr.c_str()));
+
+ expr = ex_input_eq.empty() ? "true" : "(and";
+ if (!ex_input_eq.empty()) {
+ if (GetSize(ex_input_eq) == 1) {
+ expr = "\n " + ex_input_eq.front() + "\n";
+ } else {
+ for (auto &str : ex_input_eq)
+ expr += stringf("\n %s", str.c_str());
+ expr += "\n)";
+ }
+ }
+ decls.push_back(stringf("(define-fun |%s_ex_input_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(module), get_id(module), expr.c_str()));
}
string assert_expr = assert_list.empty() ? "true" : "(and";
@@ -1073,7 +1262,7 @@ struct Smt2Worker
struct Smt2Backend : public Backend {
Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1229,10 +1418,11 @@ struct Smt2Backend : public Backend {
log("from non-zero to zero in the test design.\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::ifstream template_f;
bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false;
+ bool forallmode = false;
log_header(design, "Executing SMT2 backend.\n");
@@ -1286,7 +1476,7 @@ struct Smt2Backend : public Backend {
int indent = 0;
while (indent < GetSize(line) && (line[indent] == ' ' || line[indent] == '\t'))
indent++;
- if (line.substr(indent, 2) == "%%")
+ if (line.compare(indent, 2, "%%") == 0)
break;
*f << line << std::endl;
}
@@ -1336,17 +1526,30 @@ struct Smt2Backend : public Backend {
}
dict<IdString, int> mod_stbv_width;
+ dict<IdString, dict<IdString, pair<bool, bool>>> mod_clk_cache;
Module *topmod = design->top_module();
std::string topmod_id;
for (auto module : sorted_modules)
+ for (auto cell : module->cells())
+ if (cell->type.in("$allconst", "$allseq"))
+ goto found_forall;
+ if (0) {
+ found_forall:
+ forallmode = true;
+ *f << stringf("; yosys-smt2-forall\n");
+ if (!statebv && !statedt)
+ log_error("Forall-exists problems are only supported in -stbv or -stdt mode.\n");
+ }
+
+ for (auto module : sorted_modules)
{
- if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn())
+ if (module->get_blackbox_attribute() || module->has_memories_warn() || module->has_processes_warn())
continue;
log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
- Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, mod_stbv_width);
+ Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode, mod_stbv_width, mod_clk_cache);
worker.run();
worker.write(*f);
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index c8151c266..3d6d3e1b3 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -32,6 +32,7 @@ cexfile = None
aimfile = None
aiwfile = None
aigheader = True
+btorwitfile = None
vlogtbfile = None
vlogtbtop = None
inconstr = list()
@@ -86,12 +87,15 @@ yosys-smtbmc [options] <yosys_smt2_output>
--aig <aim_filename>:<aiw_filename>
like above, but for map files and witness files that do not
- share a filename prefix (or use differen file extensions).
+ share a filename prefix (or use different file extensions).
--aig-noheader
the AIGER witness file does not include the status and
properties lines.
+ --btorwit <btor_witness_filename>
+ read a BTOR witness.
+
--noinfo
only run the core proof, do not collect and print any
additional information (e.g. which assert failed)
@@ -99,8 +103,8 @@ yosys-smtbmc [options] <yosys_smt2_output>
--presat
check if the design with assumptions but without assertions
is SAT before checking if assertions are UNSAT. This will
- detect if there are contradicting assumtions. In some cases
- this will also help to "warmup" the solver, potentially
+ detect if there are contradicting assumptions. In some cases
+ this will also help to "warm up" the solver, potentially
yielding a speedup.
--final-only
@@ -145,14 +149,14 @@ yosys-smtbmc [options] <yosys_smt2_output>
--append <num_steps>
add <num_steps> time steps at the end of the trace
when creating a counter example (this additional time
- steps will still be constrained by assumtions)
+ steps will still be constrained by assumptions)
""" + so.helpmsg())
sys.exit(1)
try:
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts +
- ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "presat",
+ ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat",
"dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
"smtc-init", "smtc-top=", "noinit"])
except:
@@ -189,6 +193,8 @@ for o, a in opts:
aiwfile = a + ".aiw"
elif o == "--aig-noheader":
aigheader = False
+ elif o == "--btorwit":
+ btorwitfile = a
elif o == "--dump-vcd":
vcdfile = a
elif o == "--dump-vlogtb":
@@ -338,7 +344,7 @@ def get_constr_expr(db, state, final=False, getvalues=False):
if state not in db:
return ([], [], []) if getvalues else "true"
- netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*)\](?=[ )]|$)')
+ netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*|\S*)\](?=[ )]|$)')
def replace_netref(match):
state_sel = match.group(2)
@@ -575,6 +581,103 @@ if aimfile is not None:
num_steps = max(num_steps, step+1)
step += 1
+if btorwitfile is not None:
+ with open(btorwitfile, "r") as f:
+ step = None
+ suffix = None
+ altsuffix = None
+ header_okay = False
+
+ for line in f:
+ line = line.strip()
+
+ if line == "sat":
+ header_okay = True
+ continue
+
+ if not header_okay:
+ continue
+
+ if line == "" or line[0] == "b" or line[0] == "j":
+ continue
+
+ if line == ".":
+ break
+
+ if line[0] == '#' or line[0] == '@':
+ step = int(line[1:])
+ suffix = line
+ altsuffix = suffix
+ if suffix[0] == "@":
+ altsuffix = "#" + suffix[1:]
+ else:
+ altsuffix = "@" + suffix[1:]
+ continue
+
+ line = line.split()
+
+ if len(line) == 0:
+ continue
+
+ if line[-1].endswith(suffix):
+ line[-1] = line[-1][0:len(line[-1]) - len(suffix)]
+
+ if line[-1].endswith(altsuffix):
+ line[-1] = line[-1][0:len(line[-1]) - len(altsuffix)]
+
+ if line[-1][0] == "$":
+ continue
+
+ # BV assignments
+ if len(line) == 3 and line[1][0] != "[":
+ value = line[1]
+ name = line[2]
+
+ path = smt.get_path(topmod, name)
+
+ if not smt.net_exists(topmod, path):
+ continue
+
+ width = smt.net_width(topmod, path)
+
+ if width == 1:
+ assert value in ["0", "1"]
+ value = "true" if value == "1" else "false"
+ else:
+ value = "#b" + value
+
+ smtexpr = "(= [%s] %s)" % (name, value)
+ constr_assumes[step].append((btorwitfile, smtexpr))
+
+ # Array assignments
+ if len(line) == 4 and line[1][0] == "[":
+ index = line[1]
+ value = line[2]
+ name = line[3]
+
+ path = smt.get_path(topmod, name)
+
+ if not smt.mem_exists(topmod, path):
+ continue
+
+ meminfo = smt.mem_info(topmod, path)
+
+ if meminfo[1] == 1:
+ assert value in ["0", "1"]
+ value = "true" if value == "1" else "false"
+ else:
+ value = "#b" + value
+
+ assert index[0] == "["
+ assert index[-1] == "]"
+ index = "#b" + index[1:-1]
+
+ smtexpr = "(= (select [%s] %s) %s)" % (name, index, value)
+ constr_assumes[step].append((btorwitfile, smtexpr))
+
+ skip_steps = step
+ num_steps = step+1
+
def write_vcd_trace(steps_start, steps_stop, index):
filename = vcdfile.replace("%", index)
print_msg("Writing trace to VCD file: %s" % (filename))
@@ -589,12 +692,16 @@ def write_vcd_trace(steps_start, steps_stop, index):
if n.startswith("$"):
hidden_net = True
if not hidden_net:
- vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath))
+ edge = smt.net_clock(topmod, netpath)
+ if edge is None:
+ vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath))
+ else:
+ vcd.add_clock([topmod] + netpath, edge)
path_list.append(netpath)
mem_trace_data = dict()
for mempath in sorted(smt.hiermems(topmod)):
- abits, width, rports, wports = smt.mem_info(topmod, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath)
expr_id = list()
expr_list = list()
@@ -644,6 +751,9 @@ def write_vcd_trace(steps_start, steps_stop, index):
data = ["x"] * width
gotread = False
+ if len(wdata) == 0 and len(rdata) != 0:
+ wdata = [[]] * len(rdata)
+
assert len(rdata) == len(wdata)
for i in range(len(wdata)):
@@ -663,7 +773,8 @@ def write_vcd_trace(steps_start, steps_stop, index):
else:
buf[k] = tdata[i][k]
- tdata.append(data[:])
+ if not asyncwr:
+ tdata.append(data[:])
for j_data in wdata[i]:
if j_data["A"] != addr:
@@ -676,6 +787,9 @@ def write_vcd_trace(steps_start, steps_stop, index):
if M[k] == "1":
data[k] = D[k]
+ if asyncwr:
+ tdata.append(data[:])
+
assert len(tdata) == len(rdata)
netpath = mempath[:]
@@ -708,9 +822,12 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
if vlogtbtop is not None:
for item in vlogtbtop.split("."):
- assert item in smt.modinfo[vlogtb_topmod].cells
- vlogtb_state = "(|%s_h %s| %s)" % (vlogtb_topmod, item, vlogtb_state)
- vlogtb_topmod = smt.modinfo[vlogtb_topmod].cells[item]
+ if item in smt.modinfo[vlogtb_topmod].cells:
+ vlogtb_state = "(|%s_h %s| %s)" % (vlogtb_topmod, item, vlogtb_state)
+ vlogtb_topmod = smt.modinfo[vlogtb_topmod].cells[item]
+ else:
+ print_msg("Vlog top module '%s' not found: no cell '%s' in module '%s'" % (vlogtbtop, item, vlogtb_topmod))
+ break
with open(filename, "w") as f:
print("`ifndef VERILATOR", file=f)
@@ -782,7 +899,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
mems = sorted(smt.hiermems(vlogtb_topmod))
for mempath in mems:
- abits, width, rports, wports = smt.mem_info(vlogtb_topmod, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(vlogtb_topmod, mempath)
addr_expr_list = list()
data_expr_list = list()
@@ -885,7 +1002,7 @@ def write_constr_trace(steps_start, steps_stop, index):
mems = sorted(smt.hiermems(constr_topmod))
for mempath in mems:
- abits, width, rports, wports = smt.mem_info(constr_topmod, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(constr_topmod, mempath)
addr_expr_list = list()
data_expr_list = list()
@@ -972,7 +1089,7 @@ def print_anyconsts_worker(mod, state, path):
for fun, info in smt.modinfo[mod].anyconsts.items():
if info[1] is None:
- print_msg("Value for anyconst in %s (%s): %d" % (path, info, smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
+ print_msg("Value for anyconst in %s (%s): %d" % (path, info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
else:
print_msg("Value for anyconst %s.%s (%s): %d" % (path, info[1], info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
@@ -999,24 +1116,166 @@ def get_cover_list(mod, base):
return cover_expr, cover_desc
+states = list()
+asserts_antecedent_cache = [list()]
+asserts_consequent_cache = [list()]
+asserts_cache_dirty = False
+
+def smt_state(step):
+ smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
+ states.append("s%d" % step)
+
+def smt_assert(expr):
+ if expr == "true":
+ return
+
+ smt.write("(assert %s)" % expr)
+
+def smt_assert_antecedent(expr):
+ if expr == "true":
+ return
+
+ smt.write("(assert %s)" % expr)
+
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_antecedent_cache[-1].append(expr)
+
+def smt_assert_consequent(expr):
+ if expr == "true":
+ return
+
+ smt.write("(assert %s)" % expr)
+
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_consequent_cache[-1].append(expr)
+
+def smt_forall_assert():
+ if not smt.forall:
+ return
+
+ global asserts_cache_dirty
+ asserts_cache_dirty = False
+
+ def make_assert_expr(asserts_cache):
+ expr = list()
+ for lst in asserts_cache:
+ expr += lst
+
+ assert len(expr) != 0
+
+ if len(expr) == 1:
+ expr = expr[0]
+ else:
+ expr = "(and %s)" % (" ".join(expr))
+ return expr
+
+ antecedent_expr = make_assert_expr(asserts_antecedent_cache)
+ consequent_expr = make_assert_expr(asserts_consequent_cache)
+
+ states_db = set(states)
+ used_states_db = set()
+ new_antecedent_expr = list()
+ new_consequent_expr = list()
+ assert_expr = list()
+
+ def make_new_expr(new_expr, expr):
+ cursor = 0
+ while cursor < len(expr):
+ l = 1
+ if expr[cursor] in '|"':
+ while cursor+l+1 < len(expr) and expr[cursor] != expr[cursor+l]:
+ l += 1
+ l += 1
+ elif expr[cursor] not in '() ':
+ while cursor+l < len(expr) and expr[cursor+l] not in '|"() ':
+ l += 1
+
+ word = expr[cursor:cursor+l]
+ if word in states_db:
+ used_states_db.add(word)
+ word += "_"
+
+ new_expr.append(word)
+ cursor += l
+
+ make_new_expr(new_antecedent_expr, antecedent_expr)
+ make_new_expr(new_consequent_expr, consequent_expr)
+
+ new_antecedent_expr = ["".join(new_antecedent_expr)]
+ new_consequent_expr = ["".join(new_consequent_expr)]
+
+ if states[0] in used_states_db:
+ new_antecedent_expr.append("(|%s_ex_state_eq| %s %s_)" % (topmod, states[0], states[0]))
+ for s in states:
+ if s in used_states_db:
+ new_antecedent_expr.append("(|%s_ex_input_eq| %s %s_)" % (topmod, s, s))
+
+ if len(new_antecedent_expr) == 0:
+ new_antecedent_expr = "true"
+ elif len(new_antecedent_expr) == 1:
+ new_antecedent_expr = new_antecedent_expr[0]
+ else:
+ new_antecedent_expr = "(and %s)" % (" ".join(new_antecedent_expr))
+
+ if len(new_consequent_expr) == 0:
+ new_consequent_expr = "true"
+ elif len(new_consequent_expr) == 1:
+ new_consequent_expr = new_consequent_expr[0]
+ else:
+ new_consequent_expr = "(and %s)" % (" ".join(new_consequent_expr))
+
+ assert_expr.append("(assert (forall (")
+ first_state = True
+ for s in states:
+ if s in used_states_db:
+ assert_expr.append("%s(%s_ |%s_s|)" % ("" if first_state else " ", s, topmod))
+ first_state = False
+ assert_expr.append(") (=> %s %s)))" % (new_antecedent_expr, new_consequent_expr))
+
+ smt.write("".join(assert_expr))
+
+def smt_push():
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_antecedent_cache.append(list())
+ asserts_consequent_cache.append(list())
+ smt.write("(push 1)")
+
+def smt_pop():
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_antecedent_cache.pop()
+ asserts_consequent_cache.pop()
+ smt.write("(pop 1)")
+
+def smt_check_sat():
+ if asserts_cache_dirty:
+ smt_forall_assert()
+ return smt.check_sat()
if tempind:
- retstatus = False
+ retstatus = "FAILED"
skip_counter = step_size
for step in range(num_steps, -1, -1):
- smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+ if smt.forall:
+ print_msg("Temporal induction not supported for exists-forall problems.")
+ break
+
+ smt_state(step)
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step))
if step == num_steps:
- smt.write("(assert (not (and (|%s_a| s%d) %s)))" % (topmod, step, get_constr_expr(constr_asserts, step)))
+ smt_assert("(not (and (|%s_a| s%d) %s))" % (topmod, step, get_constr_expr(constr_asserts, step)))
else:
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step, step+1))
- smt.write("(assert (|%s_a| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step, step+1))
+ smt_assert("(|%s_a| s%d)" % (topmod, step))
+ smt_assert(get_constr_expr(constr_asserts, step))
if step > num_steps-skip_steps:
print_msg("Skipping induction in step %d.." % (step))
@@ -1030,9 +1289,9 @@ if tempind:
skip_counter = 0
print_msg("Trying induction in step %d.." % (step))
- if smt.check_sat() == "sat":
+ if smt_check_sat() == "sat":
if step == 0:
- print("%s Temporal induction failed!" % smt.timestamp())
+ print_msg("Temporal induction failed!")
print_anyconsts(num_steps)
print_failed_asserts(num_steps)
write_trace(step, num_steps+1, '%')
@@ -1043,8 +1302,8 @@ if tempind:
write_trace(step, num_steps+1, "%d" % step)
else:
- print("%s Temporal induction successful." % smt.timestamp())
- retstatus = True
+ print_msg("Temporal induction successful.")
+ retstatus = "PASSED"
break
elif covermode:
@@ -1062,48 +1321,52 @@ elif covermode:
smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr))
step = 0
- retstatus = False
+ retstatus = "FAILED"
found_failed_assert = False
assert step_size == 1
while step < num_steps:
- smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+ smt_state(step)
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step))
if step == 0:
if noinit:
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
else:
- smt.write("(assert (|%s_i| s0))" % (topmod))
- smt.write("(assert (|%s_is| s0))" % (topmod))
+ smt_assert_antecedent("(|%s_i| s0)" % (topmod))
+ smt_assert_antecedent("(|%s_is| s0)" % (topmod))
else:
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
while "1" in cover_mask:
print_msg("Checking cover reachability in step %d.." % (step))
- smt.write("(push 1)")
- smt.write("(assert (distinct (covers_%d s%d) #b%s))" % (coveridx, step, "0" * len(cover_desc)))
+ smt_push()
+ smt_assert("(distinct (covers_%d s%d) #b%s)" % (coveridx, step, "0" * len(cover_desc)))
- if smt.check_sat() == "unsat":
- smt.write("(pop 1)")
+ if smt_check_sat() == "unsat":
+ smt_pop()
break
if append_steps > 0:
for i in range(step+1, step+1+append_steps):
print_msg("Appending additional step %d." % i)
- smt.write("(declare-fun s%d () |%s_s|)" % (i, topmod))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, i))
- smt.write("(assert (|%s_u| s%d))" % (topmod, i))
- smt.write("(assert (|%s_h| s%d))" % (topmod, i))
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, i-1, i))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, i))
+ smt_state(i)
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
+ smt_assert_consequent(get_constr_expr(constr_assumes, i))
print_msg("Re-solving with appended steps..")
- assert smt.check_sat() == "sat"
+ if smt_check_sat() == "unsat":
+ print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
+ found_failed_assert = True
+ retstatus = "FAILED"
+ break
reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step)))
assert len(reached_covers) == len(cover_desc)
@@ -1130,14 +1393,14 @@ elif covermode:
break
coveridx += 1
- smt.write("(pop 1)")
+ smt_pop()
smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask))
if found_failed_assert:
break
if "1" not in cover_mask:
- retstatus = True
+ retstatus = "PASSED"
break
step += 1
@@ -1149,29 +1412,29 @@ elif covermode:
else: # not tempind, covermode
step = 0
- retstatus = True
+ retstatus = "PASSED"
while step < num_steps:
- smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+ smt_state(step)
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step))
if step == 0:
if noinit:
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
else:
- smt.write("(assert (|%s_i| s0))" % (topmod))
- smt.write("(assert (|%s_is| s0))" % (topmod))
+ smt_assert_antecedent("(|%s_i| s0)" % (topmod))
+ smt_assert_antecedent("(|%s_is| s0)" % (topmod))
else:
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
if step < skip_steps:
if assume_skipped is not None and step >= assume_skipped:
print_msg("Skipping step %d (and assuming pass).." % (step))
- smt.write("(assert (|%s_a| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
+ smt_assert("(|%s_a| s%d)" % (topmod, step))
+ smt_assert(get_constr_expr(constr_asserts, step))
else:
print_msg("Skipping step %d.." % (step))
step += 1
@@ -1180,12 +1443,12 @@ else: # not tempind, covermode
last_check_step = step
for i in range(1, step_size):
if step+i < num_steps:
- smt.write("(declare-fun s%d () |%s_s|)" % (step+i, topmod))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step+i))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step+i))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step+i))
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step+i-1, step+i))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step+i))
+ smt_state(step+i)
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step+i))
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step+i))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step+i))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step+i-1, step+i))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step+i))
last_check_step = step+i
if not gentrace:
@@ -1195,9 +1458,9 @@ else: # not tempind, covermode
else:
print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step))
- if smt.check_sat() == "unsat":
- print("%s Warmup failed!" % smt.timestamp())
- retstatus = False
+ if smt_check_sat() == "unsat":
+ print("%s Assumptions are unsatisfiable!" % smt.timestamp())
+ retstatus = "PREUNSAT"
break
if not final_only:
@@ -1205,36 +1468,40 @@ else: # not tempind, covermode
print_msg("Checking assertions in step %d.." % (step))
else:
print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step))
- smt.write("(push 1)")
+ smt_push()
- smt.write("(assert (not (and %s)))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] +
+ smt_assert("(not (and %s))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] +
[get_constr_expr(constr_asserts, i) for i in range(step, last_check_step+1)]))
- if smt.check_sat() == "sat":
+ if smt_check_sat() == "sat":
print("%s BMC failed!" % smt.timestamp())
if append_steps > 0:
for i in range(last_check_step+1, last_check_step+1+append_steps):
print_msg("Appending additional step %d." % i)
- smt.write("(declare-fun s%d () |%s_s|)" % (i, topmod))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, i))
- smt.write("(assert (|%s_u| s%d))" % (topmod, i))
- smt.write("(assert (|%s_h| s%d))" % (topmod, i))
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, i-1, i))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, i))
- assert smt.check_sat() == "sat"
+ smt_state(i)
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
+ smt_assert_consequent(get_constr_expr(constr_assumes, i))
+ print_msg("Re-solving with appended steps..")
+ if smt_check_sat() == "unsat":
+ print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
+ retstatus = "FAILED"
+ break
print_anyconsts(step)
for i in range(step, last_check_step+1):
print_failed_asserts(i)
write_trace(0, last_check_step+1+append_steps, '%')
- retstatus = False
+ retstatus = "FAILED"
break
- smt.write("(pop 1)")
+ smt_pop()
if (constr_final_start is not None) or (last_check_step+1 != num_steps):
for i in range(step, last_check_step+1):
- smt.write("(assert (|%s_a| s%d))" % (topmod, i))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
+ smt_assert("(|%s_a| s%d)" % (topmod, i))
+ smt_assert(get_constr_expr(constr_asserts, i))
if constr_final_start is not None:
for i in range(step, last_check_step+1):
@@ -1242,32 +1509,32 @@ else: # not tempind, covermode
continue
print_msg("Checking final constraints in step %d.." % (i))
- smt.write("(push 1)")
+ smt_push()
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, i, final=True))
- smt.write("(assert (not %s))" % get_constr_expr(constr_asserts, i, final=True))
+ smt_assert_consequent(get_constr_expr(constr_assumes, i, final=True))
+ smt_assert("(not %s)" % get_constr_expr(constr_asserts, i, final=True))
- if smt.check_sat() == "sat":
+ if smt_check_sat() == "sat":
print("%s BMC failed!" % smt.timestamp())
print_anyconsts(i)
print_failed_asserts(i, final=True)
write_trace(0, i+1, '%')
- retstatus = False
+ retstatus = "FAILED"
break
- smt.write("(pop 1)")
+ smt_pop()
if not retstatus:
break
else: # gentrace
for i in range(step, last_check_step+1):
- smt.write("(assert (|%s_a| s%d))" % (topmod, i))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
+ smt_assert("(|%s_a| s%d)" % (topmod, i))
+ smt_assert(get_constr_expr(constr_asserts, i))
print_msg("Solving for step %d.." % (last_check_step))
- if smt.check_sat() != "sat":
+ if smt_check_sat() != "sat":
print("%s No solution found!" % smt.timestamp())
- retstatus = False
+ retstatus = "FAILED"
break
elif dumpall:
@@ -1276,7 +1543,7 @@ else: # not tempind, covermode
step += step_size
- if gentrace:
+ if gentrace and retstatus:
print_anyconsts(0)
write_trace(0, num_steps, '%')
@@ -1284,5 +1551,5 @@ else: # not tempind, covermode
smt.write("(exit)")
smt.wait()
-print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)"))
-sys.exit(0 if retstatus else 1)
+print_msg("Status: %s" % retstatus)
+sys.exit(0 if retstatus == "PASSED" else 1)
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index abf8e812d..34bf7ef38 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -16,10 +16,67 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
-import sys, subprocess, re, os
+import sys, re, os, signal
+import subprocess
+if os.name == "posix":
+ import resource
from copy import deepcopy
from select import select
from time import time
+from queue import Queue, Empty
+from threading import Thread
+
+
+# This is needed so that the recursive SMT2 S-expression parser
+# does not run out of stack frames when parsing large expressions
+if os.name == "posix":
+ smtio_reclimit = 64 * 1024
+ if sys.getrecursionlimit() < smtio_reclimit:
+ sys.setrecursionlimit(smtio_reclimit)
+
+ current_rlimit_stack = resource.getrlimit(resource.RLIMIT_STACK)
+ if current_rlimit_stack[0] != resource.RLIM_INFINITY:
+ smtio_stacksize = 128 * 1024 * 1024
+ if os.uname().sysname == "Darwin":
+ # MacOS has rather conservative stack limits
+ smtio_stacksize = 16 * 1024 * 1024
+ if current_rlimit_stack[1] != resource.RLIM_INFINITY:
+ smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1])
+ if current_rlimit_stack[0] < smtio_stacksize:
+ try:
+ resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1]))
+ except ValueError:
+ # couldn't get more stack, just run with what we have
+ pass
+
+
+# currently running solvers (so we can kill them)
+running_solvers = dict()
+forced_shutdown = False
+solvers_index = 0
+
+def force_shutdown(signum, frame):
+ global forced_shutdown
+ if not forced_shutdown:
+ forced_shutdown = True
+ if signum is not None:
+ print("<%s>" % signal.Signals(signum).name)
+ for p in running_solvers.values():
+ # os.killpg(os.getpgid(p.pid), signal.SIGTERM)
+ os.kill(p.pid, signal.SIGTERM)
+ sys.exit(1)
+
+if os.name == "posix":
+ signal.signal(signal.SIGHUP, force_shutdown)
+signal.signal(signal.SIGINT, force_shutdown)
+signal.signal(signal.SIGTERM, force_shutdown)
+
+def except_hook(exctype, value, traceback):
+ if not forced_shutdown:
+ sys.__excepthook__(exctype, value, traceback)
+ force_shutdown(None, None)
+
+sys.excepthook = except_hook
hex_dict = {
@@ -40,24 +97,33 @@ class SmtModInfo:
self.memories = dict()
self.wires = set()
self.wsize = dict()
+ self.clocks = dict()
self.cells = dict()
self.asserts = dict()
self.covers = dict()
self.anyconsts = dict()
self.anyseqs = dict()
+ self.allconsts = dict()
+ self.allseqs = dict()
+ self.asize = dict()
class SmtIo:
def __init__(self, opts=None):
+ global solvers_index
+
self.logic = None
self.logic_qf = True
self.logic_ax = True
self.logic_uf = True
self.logic_bv = True
self.logic_dt = False
+ self.forall = False
self.produce_models = True
self.smt2cache = [list()]
self.p = None
+ self.p_index = solvers_index
+ solvers_index += 1
if opts is not None:
self.logic = opts.logic
@@ -91,23 +157,41 @@ class SmtIo:
self.topmod = None
self.setup_done = False
+ def __del__(self):
+ if self.p is not None and not forced_shutdown:
+ os.killpg(os.getpgid(self.p.pid), signal.SIGTERM)
+ if running_solvers is not None:
+ del running_solvers[self.p_index]
+
def setup(self):
assert not self.setup_done
+ if self.forall:
+ self.unroll = False
+
if self.solver == "yices":
- self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['yices-smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
if self.solver == "z3":
self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
if self.solver == "cvc4":
- self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
if self.solver == "mathsat":
self.popen_vargs = ['mathsat'] + self.solver_opts
if self.solver == "boolector":
- self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
self.unroll = True
if self.solver == "abc":
@@ -126,9 +210,10 @@ class SmtIo:
if self.dummy_file is not None:
self.dummy_fd = open(self.dummy_file, "w")
if not self.noincr:
- self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ self.p_open()
if self.unroll:
+ assert not self.forall
self.logic_uf = False
self.unroll_idcnt = 0
self.unroll_buffer = ""
@@ -148,17 +233,17 @@ class SmtIo:
self.setup_done = True
+ for stmt in self.info_stmts:
+ self.write(stmt)
+
if self.produce_models:
self.write("(set-option :produce-models true)")
self.write("(set-logic %s)" % self.logic)
- for stmt in self.info_stmts:
- self.write(stmt)
-
def timestamp(self):
secs = int(time() - self.start_time)
- return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
+ return "## %3d:%02d:%02d " % (secs // (60*60), (secs // 60) % 60, secs % 60)
def replace_in_stmt(self, stmt, pat, repl):
if stmt == pat:
@@ -209,6 +294,65 @@ class SmtIo:
return stmt
+ def p_thread_main(self):
+ while True:
+ data = self.p.stdout.readline().decode("ascii")
+ if data == "": break
+ self.p_queue.put(data)
+ self.p_queue.put("")
+ self.p_running = False
+
+ def p_open(self):
+ assert self.p is None
+ try:
+ self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ except FileNotFoundError:
+ print("%s SMT Solver '%s' not found in path." % (self.timestamp(), self.popen_vargs[0]), flush=True)
+ sys.exit(1)
+ running_solvers[self.p_index] = self.p
+ self.p_running = True
+ self.p_next = None
+ self.p_queue = Queue()
+ self.p_thread = Thread(target=self.p_thread_main)
+ self.p_thread.start()
+
+ def p_write(self, data, flush):
+ assert self.p is not None
+ self.p.stdin.write(bytes(data, "ascii"))
+ if flush: self.p.stdin.flush()
+
+ def p_read(self):
+ assert self.p is not None
+ if self.p_next is not None:
+ data = self.p_next
+ self.p_next = None
+ return data
+ if not self.p_running:
+ return ""
+ return self.p_queue.get()
+
+ def p_poll(self, timeout=0.1):
+ assert self.p is not None
+ assert self.p_running
+ if self.p_next is not None:
+ return False
+ try:
+ self.p_next = self.p_queue.get(True, timeout)
+ return False
+ except Empty:
+ return True
+
+ def p_close(self):
+ assert self.p is not None
+ self.p.stdin.close()
+ self.p_thread.join()
+ assert not self.p_running
+ del running_solvers[self.p_index]
+ self.p = None
+ self.p_next = None
+ self.p_queue = None
+ self.p_thread = None
+
def write(self, stmt, unroll=True):
if stmt.startswith(";"):
self.info(stmt)
@@ -281,20 +425,17 @@ class SmtIo:
if self.solver != "dummy":
if self.noincr:
if self.p is not None and not stmt.startswith("(get-"):
- self.p.stdin.close()
- self.p = None
+ self.p_close()
if stmt == "(push 1)":
self.smt2cache.append(list())
elif stmt == "(pop 1)":
self.smt2cache.pop()
else:
if self.p is not None:
- self.p.stdin.write(bytes(stmt + "\n", "ascii"))
- self.p.stdin.flush()
+ self.p_write(stmt + "\n", True)
self.smt2cache[-1].append(stmt)
else:
- self.p.stdin.write(bytes(stmt + "\n", "ascii"))
- self.p.stdin.flush()
+ self.p_write(stmt + "\n", True)
def info(self, stmt):
if not stmt.startswith("; yosys-smt2-"):
@@ -314,6 +455,11 @@ class SmtIo:
if self.logic is None:
self.logic_dt = True
+ if fields[1] == "yosys-smt2-forall":
+ if self.logic is None:
+ self.logic_qf = False
+ self.forall = True
+
if fields[1] == "yosys-smt2-module":
self.curmod = fields[2]
self.modinfo[self.curmod] = SmtModInfo()
@@ -337,12 +483,19 @@ class SmtIo:
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
if fields[1] == "yosys-smt2-memory":
- self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6]))
+ self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6]), fields[7] == "async")
if fields[1] == "yosys-smt2-wire":
self.modinfo[self.curmod].wires.add(fields[2])
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
+ if fields[1] == "yosys-smt2-clock":
+ for edge in fields[3:]:
+ if fields[2] not in self.modinfo[self.curmod].clocks:
+ self.modinfo[self.curmod].clocks[fields[2]] = edge
+ elif self.modinfo[self.curmod].clocks[fields[2]] != edge:
+ self.modinfo[self.curmod].clocks[fields[2]] = "event"
+
if fields[1] == "yosys-smt2-assert":
self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3]
@@ -350,10 +503,20 @@ class SmtIo:
self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3]
if fields[1] == "yosys-smt2-anyconst":
- self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[3], None if len(fields) <= 4 else fields[4])
+ self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
if fields[1] == "yosys-smt2-anyseq":
- self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[3], None if len(fields) <= 4 else fields[4])
+ self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-allconst":
+ self.modinfo[self.curmod].allconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-allseq":
+ self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
def hiernets(self, top, regs_only=False):
def hiernets_worker(nets, mod, cursor):
@@ -370,7 +533,8 @@ class SmtIo:
def hieranyconsts(self, top):
def worker(results, mod, cursor):
for name, value in sorted(self.modinfo[mod].anyconsts.items()):
- results.append((cursor, name, value[0], value[1]))
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
worker(results, celltype, cursor + [cellname])
@@ -381,7 +545,32 @@ class SmtIo:
def hieranyseqs(self, top):
def worker(results, mod, cursor):
for name, value in sorted(self.modinfo[mod].anyseqs.items()):
- results.append((cursor, name, value[0], value[1]))
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
+ for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
+ worker(results, celltype, cursor + [cellname])
+
+ results = list()
+ worker(results, top, [])
+ return results
+
+ def hierallconsts(self, top):
+ def worker(results, mod, cursor):
+ for name, value in sorted(self.modinfo[mod].allconsts.items()):
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
+ for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
+ worker(results, celltype, cursor + [cellname])
+
+ results = list()
+ worker(results, top, [])
+ return results
+
+ def hierallseqs(self, top):
+ def worker(results, mod, cursor):
+ for name, value in sorted(self.modinfo[mod].allseqs.items()):
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
worker(results, celltype, cursor + [cellname])
@@ -408,7 +597,7 @@ class SmtIo:
if self.solver == "dummy":
line = self.dummy_fd.readline().strip()
else:
- line = self.p.stdout.readline().decode("ascii").strip()
+ line = self.p_read().strip()
if self.dummy_file is not None:
self.dummy_fd.write(line + "\n")
@@ -421,12 +610,14 @@ class SmtIo:
if count_brackets == 0:
break
if self.solver != "dummy" and self.p.poll():
- print("SMT Solver terminated unexpectedly: %s" % "".join(stmt))
+ print("%s Solver terminated unexpectedly: %s" % (self.timestamp(), "".join(stmt)), flush=True)
sys.exit(1)
stmt = "".join(stmt)
if stmt.startswith("(error"):
- print("SMT Solver Error: %s" % stmt, file=sys.stderr)
+ print("%s Solver Error: %s" % (self.timestamp(), stmt), flush=True)
+ if self.solver != "dummy":
+ self.p_close()
sys.exit(1)
return stmt
@@ -441,15 +632,13 @@ class SmtIo:
if self.solver != "dummy":
if self.noincr:
if self.p is not None:
- self.p.stdin.close()
- self.p = None
- self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ self.p_close()
+ self.p_open()
for cache_ctx in self.smt2cache:
for cache_stmt in cache_ctx:
- self.p.stdin.write(bytes(cache_stmt + "\n", "ascii"))
+ self.p_write(cache_stmt + "\n", False)
- self.p.stdin.write(bytes("(check-sat)\n", "ascii"))
- self.p.stdin.flush()
+ self.p_write("(check-sat)\n", True)
if self.timeinfo:
i = 0
@@ -457,7 +646,7 @@ class SmtIo:
count = 0
num_bs = 0
- while select([self.p.stdout], [], [], 0.1) == ([], [], []):
+ while self.p_poll():
count += 1
if count < 25:
@@ -486,11 +675,43 @@ class SmtIo:
print("\b \b" * num_bs, end="", file=sys.stderr)
sys.stderr.flush()
+ else:
+ count = 0
+ while self.p_poll(60):
+ count += 1
+ msg = None
+
+ if count == 1:
+ msg = "1 minute"
+
+ elif count in [5, 10, 15, 30]:
+ msg = "%d minutes" % count
+
+ elif count == 60:
+ msg = "1 hour"
+
+ elif count % 60 == 0:
+ msg = "%d hours" % (count // 60)
+
+ if msg is not None:
+ print("%s waiting for solver (%s)" % (self.timestamp(), msg), flush=True)
+
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()
+
+ if result not in ["sat", "unsat"]:
+ if result == "":
+ print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)
+ else:
+ print("%s Unexpected response from solver: %s" % (self.timestamp(), result), flush=True)
+ if self.solver != "dummy":
+ self.p_close()
+ sys.exit(1)
+
return result
def parse(self, stmt):
@@ -545,6 +766,9 @@ class SmtIo:
return h
def bv2bin(self, v):
+ if type(v) is list and len(v) == 3 and v[0] == "_" and v[1].startswith("bv"):
+ x, n = int(v[1][2:]), int(v[2])
+ return "".join("1" if (x & (1 << i)) else "0" for i in range(n-1, -1, -1))
if v == "true": return "1"
if v == "false": return "0"
if v.startswith("#b"):
@@ -568,7 +792,7 @@ class SmtIo:
def get_path(self, mod, path):
assert mod in self.modinfo
- path = path.split(".")
+ path = path.replace("\\", "/").split(".")
for i in range(len(path)-1):
first = ".".join(path[0:i+1])
@@ -613,6 +837,17 @@ class SmtIo:
assert net_path[-1] in self.modinfo[mod].wsize
return self.modinfo[mod].wsize[net_path[-1]]
+ def net_clock(self, mod, net_path):
+ for i in range(len(net_path)-1):
+ assert mod in self.modinfo
+ assert net_path[i] in self.modinfo[mod].cells
+ mod = self.modinfo[mod].cells[net_path[i]]
+
+ assert mod in self.modinfo
+ if net_path[-1] not in self.modinfo[mod].clocks:
+ return None
+ return self.modinfo[mod].clocks[net_path[-1]]
+
def net_exists(self, mod, net_path):
for i in range(len(net_path)-1):
if mod not in self.modinfo: return False
@@ -672,6 +907,7 @@ class SmtIo:
def wait(self):
if self.p is not None:
self.p.wait()
+ self.p_close()
class SmtOpts:
@@ -763,6 +999,7 @@ class MkVcd:
self.f = f
self.t = -1
self.nets = dict()
+ self.clocks = dict()
def add_net(self, path, width):
path = tuple(path)
@@ -770,34 +1007,86 @@ class MkVcd:
key = "n%d" % len(self.nets)
self.nets[path] = (key, width)
+ def add_clock(self, path, edge):
+ path = tuple(path)
+ assert self.t == -1
+ key = "n%d" % len(self.nets)
+ self.nets[path] = (key, 1)
+ self.clocks[path] = (key, edge)
+
def set_net(self, path, bits):
path = tuple(path)
assert self.t >= 0
assert path in self.nets
- print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
+ if path not in self.clocks:
+ print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
+
+ def escape_name(self, name):
+ name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name)
+ if re.match("[\[\]]", name) and name[0] != "\\":
+ name = "\\" + name
+ return name
def set_time(self, t):
assert t >= self.t
if t != self.t:
if self.t == -1:
+ print("$version Generated by Yosys-SMTBMC $end", file=self.f)
+ print("$timescale 1ns $end", file=self.f)
print("$var integer 32 t smt_step $end", file=self.f)
print("$var event 1 ! smt_clock $end", file=self.f)
+
+ def vcdescape(n):
+ if n.startswith("$") or ":" in n:
+ return "\\" + n
+ return n
+
scope = []
for path in sorted(self.nets):
- while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]):
+ key, width = self.nets[path]
+
+ uipath = list(path)
+ if "." in uipath[-1] and not uipath[-1].startswith("$"):
+ uipath = uipath[0:-1] + uipath[-1].split(".")
+ for i in range(len(uipath)):
+ uipath[i] = re.sub(r"\[([^\]]*)\]", r"<\1>", uipath[i])
+
+ while uipath[:len(scope)] != scope:
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)
+
+ while uipath[:-1] != scope:
+ scopename = uipath[len(scope)]
+ print("$scope module %s $end" % vcdescape(scopename), file=self.f)
+ scope.append(uipath[len(scope)])
+
+ if path in self.clocks and self.clocks[path][1] == "event":
+ print("$var event 1 %s %s $end" % (key, vcdescape(uipath[-1])), file=self.f)
+ else:
+ print("$var wire %d %s %s $end" % (width, key, vcdescape(uipath[-1])), file=self.f)
+
for i in range(len(scope)):
print("$upscope $end", file=self.f)
+
print("$enddefinitions $end", file=self.f)
+
self.t = t
assert self.t >= 0
+
+ if self.t > 0:
+ print("#%d" % (10 * self.t - 5), file=self.f)
+ for path in sorted(self.clocks.keys()):
+ if self.clocks[path][1] == "posedge":
+ print("b0 %s" % self.nets[path][0], file=self.f)
+ elif self.clocks[path][1] == "negedge":
+ print("b1 %s" % self.nets[path][0], file=self.f)
+
print("#%d" % (10 * self.t), file=self.f)
print("1!", file=self.f)
print("b%s t" % format(self.t, "032b"), file=self.f)
+ for path in sorted(self.clocks.keys()):
+ if self.clocks[path][1] == "negedge":
+ print("b0 %s" % self.nets[path][0], file=self.f)
+ else:
+ print("b1 %s" % self.nets[path][0], file=self.f)