mirror of https://github.com/YosysHQ/yosys.git
smtbmc: Add native json based witness format + smt2 backend support
This adds a native json based witness trace format. By having a common format that includes everything we support, and providing a conversion utility (yosys-witness) we no longer need to implement every format for every tool that deals with witness traces, avoiding a quadratic opportunity to introduce subtle bugs. Included: * smt2: New yosys-smt2-witness info lines containing full hierarchical paths without lossy escaping. * yosys-smtbmc --dump-yw trace.yw: Dump results in the new format. * yosys-smtbmc --yw trace.yw: Read new format as constraints. * yosys-witness: New tool to convert witness formats. Currently this can only display traces in a human-readable-only format and do a passthrough read/write of the new format. * ywio.py: Small python lib for reading and writing the new format. Used by yosys-smtbmc and yosys-witness to avoid duplication.
This commit is contained in:
parent
96a1173598
commit
f041e36c6e
|
@ -28,6 +28,9 @@ __pycache__
|
||||||
/yosys-smtbmc
|
/yosys-smtbmc
|
||||||
/yosys-smtbmc.exe
|
/yosys-smtbmc.exe
|
||||||
/yosys-smtbmc-script.py
|
/yosys-smtbmc-script.py
|
||||||
|
/yosys-witness
|
||||||
|
/yosys-witness.exe
|
||||||
|
/yosys-witness-script.py
|
||||||
/yosys-filterlib
|
/yosys-filterlib
|
||||||
/yosys-filterlib.exe
|
/yosys-filterlib.exe
|
||||||
/kernel/*.pyh
|
/kernel/*.pyh
|
||||||
|
|
|
@ -11,6 +11,10 @@ Yosys 0.20 .. Yosys 0.20-dev
|
||||||
* Formal Verification
|
* Formal Verification
|
||||||
- Added $anyinit cell to directly represent FFs with an unconstrained
|
- Added $anyinit cell to directly represent FFs with an unconstrained
|
||||||
initialization value. These can be generated by the new formalff pass.
|
initialization value. These can be generated by the new formalff pass.
|
||||||
|
- New JSON based yosys witness format for formal verification traces.
|
||||||
|
- yosys-smtbmc: Reading and writing of yosys witness traces.
|
||||||
|
- write_smt2: Emit inline metadata to support yosys witness trace.
|
||||||
|
- yosys-witness is a new tool to inspect and convert yosys witness traces.
|
||||||
|
|
||||||
Yosys 0.19 .. Yosys 0.20
|
Yosys 0.19 .. Yosys 0.20
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -7,6 +7,7 @@ ifneq ($(CONFIG),emcc)
|
||||||
# MSYS targets support yosys-smtbmc, but require a launcher script
|
# MSYS targets support yosys-smtbmc, but require a launcher script
|
||||||
ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64))
|
ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64))
|
||||||
TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc.exe $(PROGRAM_PREFIX)yosys-smtbmc-script.py
|
TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc.exe $(PROGRAM_PREFIX)yosys-smtbmc-script.py
|
||||||
|
TARGETS += $(PROGRAM_PREFIX)yosys-witness.exe $(PROGRAM_PREFIX)yosys-witness-script.py
|
||||||
# Needed to find the Python interpreter for yosys-smtbmc scripts.
|
# Needed to find the Python interpreter for yosys-smtbmc scripts.
|
||||||
# Override if necessary, it is only used for msys2 targets.
|
# Override if necessary, it is only used for msys2 targets.
|
||||||
PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3)
|
PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3)
|
||||||
|
@ -15,18 +16,31 @@ $(PROGRAM_PREFIX)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/$(PROGRAM_PREFIX)yosys/python3"]]|;' \
|
$(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \
|
||||||
-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
|
-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
|
||||||
|
|
||||||
|
$(PROGRAM_PREFIX)yosys-witness-script.py: backends/smt2/witness.py
|
||||||
|
$(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \
|
||||||
|
-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
|
||||||
|
|
||||||
$(PROGRAM_PREFIX)yosys-smtbmc.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-smtbmc-script.py
|
$(PROGRAM_PREFIX)yosys-smtbmc.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-smtbmc-script.py
|
||||||
$(P) $(CXX) -DGUI=0 -O -s -o $@ $<
|
$(P) $(CXX) -DGUI=0 -O -s -o $@ $<
|
||||||
|
|
||||||
|
$(PROGRAM_PREFIX)yosys-witness.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-witness-script.py
|
||||||
|
$(P) $(CXX) -DGUI=0 -O -s -o $@ $<
|
||||||
# Other targets
|
# Other targets
|
||||||
else
|
else
|
||||||
TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc
|
TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc $(PROGRAM_PREFIX)yosys-witness
|
||||||
|
|
||||||
$(PROGRAM_PREFIX)yosys-smtbmc: backends/smt2/smtbmc.py
|
$(PROGRAM_PREFIX)yosys-smtbmc: backends/smt2/smtbmc.py
|
||||||
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)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/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new
|
||||||
$(Q) chmod +x $@.new
|
$(Q) chmod +x $@.new
|
||||||
$(Q) mv $@.new $@
|
$(Q) mv $@.new $@
|
||||||
|
|
||||||
|
$(PROGRAM_PREFIX)yosys-witness: backends/smt2/witness.py
|
||||||
|
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new
|
||||||
|
$(Q) chmod +x $@.new
|
||||||
|
$(Q) mv $@.new $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
|
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
|
||||||
|
$(eval $(call add_share_file,share/python3,backends/smt2/ywio.py))
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "kernel/celltypes.h"
|
#include "kernel/celltypes.h"
|
||||||
#include "kernel/log.h"
|
#include "kernel/log.h"
|
||||||
#include "kernel/mem.h"
|
#include "kernel/mem.h"
|
||||||
|
#include "libs/json11/json11.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
|
@ -588,6 +589,9 @@ struct Smt2Worker
|
||||||
if (cell->type.in(ID($ff), ID($dff)))
|
if (cell->type.in(ID($ff), ID($dff)))
|
||||||
{
|
{
|
||||||
registers.insert(cell);
|
registers.insert(cell);
|
||||||
|
for (auto chunk : cell->getPort(ID::Q).chunks())
|
||||||
|
if (chunk.is_wire())
|
||||||
|
decls.push_back(witness_signal("reg", chunk.width, chunk.offset, "", idcounter, chunk.wire));
|
||||||
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(ID::Q)), log_signal(cell->getPort(ID::Q)));
|
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(ID::Q)), log_signal(cell->getPort(ID::Q)));
|
||||||
register_bv(cell->getPort(ID::Q), idcounter++);
|
register_bv(cell->getPort(ID::Q), idcounter++);
|
||||||
recursive_cells.erase(cell);
|
recursive_cells.erase(cell);
|
||||||
|
@ -610,6 +614,12 @@ struct Smt2Worker
|
||||||
decls.push_back(stringf("; yosys-smt2-minimize %s#%d\n", get_id(module), idcounter));
|
decls.push_back(stringf("; yosys-smt2-minimize %s#%d\n", get_id(module), idcounter));
|
||||||
log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str().c_str());
|
log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst));
|
||||||
|
for (auto chunk : cell->getPort(QY).chunks())
|
||||||
|
if (chunk.is_wire())
|
||||||
|
decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire));
|
||||||
|
|
||||||
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(QY)), log_signal(cell->getPort(QY)));
|
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(QY)), log_signal(cell->getPort(QY)));
|
||||||
if (cell->type == ID($anyseq))
|
if (cell->type == ID($anyseq))
|
||||||
ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter));
|
ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter));
|
||||||
|
@ -760,6 +770,7 @@ struct Smt2Worker
|
||||||
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));
|
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));
|
||||||
|
|
||||||
decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(mem->memid), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync"));
|
decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(mem->memid), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync"));
|
||||||
|
decls.push_back(witness_memory(get_id(mem->memid), cell, mem));
|
||||||
|
|
||||||
string memstate;
|
string memstate;
|
||||||
if (has_async_wr) {
|
if (has_async_wr) {
|
||||||
|
@ -852,6 +863,7 @@ struct Smt2Worker
|
||||||
if (m != nullptr)
|
if (m != nullptr)
|
||||||
{
|
{
|
||||||
decls.push_back(stringf("; yosys-smt2-cell %s %s\n", get_id(cell->type), get_id(cell->name)));
|
decls.push_back(stringf("; yosys-smt2-cell %s %s\n", get_id(cell->type), get_id(cell->name)));
|
||||||
|
decls.push_back(witness_cell(get_id(cell->name), cell));
|
||||||
string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
|
string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
|
||||||
|
|
||||||
for (auto &conn : cell->connections())
|
for (auto &conn : cell->connections())
|
||||||
|
@ -950,14 +962,19 @@ struct Smt2Worker
|
||||||
|
|
||||||
for (auto wire : module->wires()) {
|
for (auto wire : module->wires()) {
|
||||||
bool is_register = false;
|
bool is_register = false;
|
||||||
for (auto bit : SigSpec(wire))
|
bool contains_clock = false;
|
||||||
|
for (auto bit : SigSpec(wire)) {
|
||||||
if (reg_bits.count(bit))
|
if (reg_bits.count(bit))
|
||||||
is_register = true;
|
is_register = true;
|
||||||
|
auto sig_bit = sigmap(bit);
|
||||||
|
if (clock_posedge.count(sig_bit) || clock_negedge.count(sig_bit))
|
||||||
|
contains_clock = true;
|
||||||
|
}
|
||||||
bool is_smtlib2_comb_expr = wire->has_attribute(ID::smtlib2_comb_expr);
|
bool is_smtlib2_comb_expr = wire->has_attribute(ID::smtlib2_comb_expr);
|
||||||
if (is_smtlib2_comb_expr && !is_smtlib2_module)
|
if (is_smtlib2_comb_expr && !is_smtlib2_module)
|
||||||
log_error("smtlib2_comb_expr is only valid in a module with the smtlib2_module attribute: wire %s.%s", log_id(module),
|
log_error("smtlib2_comb_expr is only valid in a module with the smtlib2_module attribute: wire %s.%s", log_id(module),
|
||||||
log_id(wire));
|
log_id(wire));
|
||||||
if (wire->port_id || is_register || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) {
|
if (wire->port_id || is_register || contains_clock || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) {
|
||||||
RTLIL::SigSpec sig = sigmap(wire);
|
RTLIL::SigSpec sig = sigmap(wire);
|
||||||
std::vector<std::string> comments;
|
std::vector<std::string> comments;
|
||||||
if (wire->port_input)
|
if (wire->port_input)
|
||||||
|
@ -968,9 +985,20 @@ struct Smt2Worker
|
||||||
comments.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
|
comments.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
|
||||||
if (wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic()))
|
if (wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic()))
|
||||||
comments.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width));
|
comments.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)))
|
if (contains_clock && GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig)))
|
||||||
comments.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire),
|
comments.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire),
|
||||||
clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : ""));
|
clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : ""));
|
||||||
|
if (contains_clock) {
|
||||||
|
for (int i = 0; i < GetSize(sig); i++) {
|
||||||
|
bool is_posedge = clock_posedge.count(sig[i]);
|
||||||
|
bool is_negedge = clock_negedge.count(sig[i]);
|
||||||
|
if (is_posedge != is_negedge)
|
||||||
|
comments.push_back(witness_signal(
|
||||||
|
is_posedge ? "posedge" : "negedge", 1, i, get_id(wire), -1, wire));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wire->port_input)
|
||||||
|
comments.push_back(witness_signal("input", wire->width, 0, get_id(wire), -1, wire));
|
||||||
std::string smtlib2_comb_expr;
|
std::string smtlib2_comb_expr;
|
||||||
if (is_smtlib2_comb_expr) {
|
if (is_smtlib2_comb_expr) {
|
||||||
smtlib2_comb_expr =
|
smtlib2_comb_expr =
|
||||||
|
@ -980,6 +1008,8 @@ struct Smt2Worker
|
||||||
if (!bvmode && GetSize(sig) > 1)
|
if (!bvmode && GetSize(sig) > 1)
|
||||||
log_error("smtlib2_comb_expr is unsupported on multi-bit wires when -nobv is specified: wire %s.%s",
|
log_error("smtlib2_comb_expr is unsupported on multi-bit wires when -nobv is specified: wire %s.%s",
|
||||||
log_id(module), log_id(wire));
|
log_id(module), log_id(wire));
|
||||||
|
|
||||||
|
comments.push_back(witness_signal("blackbox", wire->width, 0, get_id(wire), -1, wire));
|
||||||
}
|
}
|
||||||
auto &out_decls = is_smtlib2_comb_expr ? smtlib2_decls : decls;
|
auto &out_decls = is_smtlib2_comb_expr ? smtlib2_decls : decls;
|
||||||
if (bvmode && GetSize(sig) > 1) {
|
if (bvmode && GetSize(sig) > 1) {
|
||||||
|
@ -1447,6 +1477,90 @@ struct Smt2Worker
|
||||||
f << "true)";
|
f << "true)";
|
||||||
f << stringf(" ; end of module %s\n", get_id(module));
|
f << stringf(" ; end of module %s\n", get_id(module));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T> static std::vector<std::string> witness_path(T *obj) {
|
||||||
|
std::vector<std::string> path;
|
||||||
|
if (obj->name.isPublic()) {
|
||||||
|
auto hdlname = obj->get_string_attribute(ID::hdlname);
|
||||||
|
for (auto token : split_tokens(hdlname))
|
||||||
|
path.push_back("\\" + token);
|
||||||
|
}
|
||||||
|
if (path.empty())
|
||||||
|
path.push_back(obj->name.str());
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string witness_signal(const char *type, int width, int offset, const std::string &smtname, int smtid, RTLIL::Wire *wire)
|
||||||
|
{
|
||||||
|
std::vector<std::string> hiername;
|
||||||
|
const char *wire_name = wire->name.c_str();
|
||||||
|
if (wire_name[0] == '\\') {
|
||||||
|
auto hdlname = wire->get_string_attribute(ID::hdlname);
|
||||||
|
for (auto token : split_tokens(hdlname))
|
||||||
|
hiername.push_back("\\" + token);
|
||||||
|
}
|
||||||
|
if (hiername.empty())
|
||||||
|
hiername.push_back(wire->name.str());
|
||||||
|
|
||||||
|
std::string line = "; yosys-smt2-witness ";
|
||||||
|
(json11::Json { json11::Json::object {
|
||||||
|
{ "type", type },
|
||||||
|
{ "offset", offset },
|
||||||
|
{ "width", width },
|
||||||
|
{ "smtname", smtname.empty() ? json11::Json(smtid) : json11::Json(smtname) },
|
||||||
|
{ "path", witness_path(wire) },
|
||||||
|
}}).dump(line);
|
||||||
|
line += "\n";
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string witness_cell(const char *smtname, RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
std::string line = "; yosys-smt2-witness ";
|
||||||
|
(json11::Json {json11::Json::object {
|
||||||
|
{ "type", "cell" },
|
||||||
|
{ "smtname", smtname },
|
||||||
|
{ "path", witness_path(cell) },
|
||||||
|
}}).dump(line);
|
||||||
|
line += "\n";
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string witness_memory(const char *smtname, RTLIL::Cell *cell, Mem *mem)
|
||||||
|
{
|
||||||
|
json11::Json::array uninitialized;
|
||||||
|
auto init_data = mem->get_init_data();
|
||||||
|
|
||||||
|
int cursor = 0;
|
||||||
|
|
||||||
|
while (cursor < init_data.size()) {
|
||||||
|
while (cursor < init_data.size() && init_data[cursor] != State::Sx)
|
||||||
|
cursor++;
|
||||||
|
int offset = cursor;
|
||||||
|
while (cursor < init_data.size() && init_data[cursor] == State::Sx)
|
||||||
|
cursor++;
|
||||||
|
int width = cursor - offset;
|
||||||
|
if (width)
|
||||||
|
uninitialized.push_back(json11::Json::object {
|
||||||
|
{"width", width},
|
||||||
|
{"offset", offset},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line = "; yosys-smt2-witness ";
|
||||||
|
(json11::Json { json11::Json::object {
|
||||||
|
{ "type", "mem" },
|
||||||
|
{ "width", mem->width },
|
||||||
|
{ "size", mem->size },
|
||||||
|
{ "rom", mem->wr_ports.empty() },
|
||||||
|
{ "statebv", statebv },
|
||||||
|
{ "smtname", smtname },
|
||||||
|
{ "uninitialized", uninitialized },
|
||||||
|
{ "path", witness_path(cell) },
|
||||||
|
}}).dump(line);
|
||||||
|
line += "\n";
|
||||||
|
return line;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Smt2Backend : public Backend {
|
struct Smt2Backend : public Backend {
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
import os, sys, getopt, re
|
import os, sys, getopt, re, bisect
|
||||||
##yosys-sys-path##
|
##yosys-sys-path##
|
||||||
from smtio import SmtIo, SmtOpts, MkVcd
|
from smtio import SmtIo, SmtOpts, MkVcd
|
||||||
|
from ywio import ReadWitness, WriteWitness, WitnessValues
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
got_topt = False
|
got_topt = False
|
||||||
|
@ -28,6 +29,8 @@ step_size = 1
|
||||||
num_steps = 20
|
num_steps = 20
|
||||||
append_steps = 0
|
append_steps = 0
|
||||||
vcdfile = None
|
vcdfile = None
|
||||||
|
inywfile = None
|
||||||
|
outywfile = None
|
||||||
cexfile = None
|
cexfile = None
|
||||||
aimfile = None
|
aimfile = None
|
||||||
aiwfile = None
|
aiwfile = None
|
||||||
|
@ -94,6 +97,9 @@ def usage():
|
||||||
the AIGER witness file does not include the status and
|
the AIGER witness file does not include the status and
|
||||||
properties lines.
|
properties lines.
|
||||||
|
|
||||||
|
--yw <yosys_witness_filename>
|
||||||
|
read a Yosys witness.
|
||||||
|
|
||||||
--btorwit <btor_witness_filename>
|
--btorwit <btor_witness_filename>
|
||||||
read a BTOR witness.
|
read a BTOR witness.
|
||||||
|
|
||||||
|
@ -121,6 +127,9 @@ def usage():
|
||||||
(hint: use 'write_smt2 -wires' for maximum
|
(hint: use 'write_smt2 -wires' for maximum
|
||||||
coverage of signals in generated VCD file)
|
coverage of signals in generated VCD file)
|
||||||
|
|
||||||
|
--dump-yw <yw_filename>
|
||||||
|
write trace as a Yosys witness trace
|
||||||
|
|
||||||
--dump-vlogtb <verilog_filename>
|
--dump-vlogtb <verilog_filename>
|
||||||
write trace as Verilog test bench
|
write trace as Verilog test bench
|
||||||
|
|
||||||
|
@ -167,8 +176,8 @@ def usage():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts +
|
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts +
|
||||||
["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat",
|
["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "yw=", "btorwit=", "presat",
|
||||||
"dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
|
"dump-vcd=", "dump-yw=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
|
||||||
"smtc-init", "smtc-top=", "noinit", "binary", "keep-going"])
|
"smtc-init", "smtc-top=", "noinit", "binary", "keep-going"])
|
||||||
except:
|
except:
|
||||||
usage()
|
usage()
|
||||||
|
@ -204,10 +213,14 @@ for o, a in opts:
|
||||||
aiwfile = a + ".aiw"
|
aiwfile = a + ".aiw"
|
||||||
elif o == "--aig-noheader":
|
elif o == "--aig-noheader":
|
||||||
aigheader = False
|
aigheader = False
|
||||||
|
elif o == "--yw":
|
||||||
|
inywfile = a
|
||||||
elif o == "--btorwit":
|
elif o == "--btorwit":
|
||||||
btorwitfile = a
|
btorwitfile = a
|
||||||
elif o == "--dump-vcd":
|
elif o == "--dump-vcd":
|
||||||
vcdfile = a
|
vcdfile = a
|
||||||
|
elif o == "--dump-yw":
|
||||||
|
outywfile = a
|
||||||
elif o == "--dump-vlogtb":
|
elif o == "--dump-vlogtb":
|
||||||
vlogtbfile = a
|
vlogtbfile = a
|
||||||
elif o == "--vlogtb-top":
|
elif o == "--vlogtb-top":
|
||||||
|
@ -602,6 +615,101 @@ if aimfile is not None:
|
||||||
num_steps = max(num_steps, step+2)
|
num_steps = max(num_steps, step+2)
|
||||||
step += 1
|
step += 1
|
||||||
|
|
||||||
|
if inywfile is not None:
|
||||||
|
with open(inywfile, "r") as f:
|
||||||
|
inyw = ReadWitness(f)
|
||||||
|
|
||||||
|
inits, seqs, clocks, mems = smt.hierwitness(topmod, allregs=True, blackbox=True)
|
||||||
|
|
||||||
|
smt_wires = defaultdict(list)
|
||||||
|
smt_mems = defaultdict(list)
|
||||||
|
|
||||||
|
for wire in inits + seqs:
|
||||||
|
smt_wires[wire["path"]].append(wire)
|
||||||
|
|
||||||
|
for mem in mems:
|
||||||
|
smt_mems[mem["path"]].append(mem)
|
||||||
|
|
||||||
|
addr_re = re.compile(r'\\\[[0-9]+\]$')
|
||||||
|
bits_re = re.compile(r'[01?]*$')
|
||||||
|
|
||||||
|
for t, step in inyw.steps():
|
||||||
|
present_signals, missing = step.present_signals(inyw.sigmap)
|
||||||
|
for sig in present_signals:
|
||||||
|
bits = step[sig]
|
||||||
|
if not bits_re.match(bits):
|
||||||
|
raise ValueError("unsupported bit value in Yosys witness file")
|
||||||
|
|
||||||
|
sig_end = sig.offset + len(bits)
|
||||||
|
if sig.path in smt_wires:
|
||||||
|
for wire in smt_wires[sig.path]:
|
||||||
|
width, offset = wire["width"], wire["offset"]
|
||||||
|
|
||||||
|
smt_bool = smt.net_width(topmod, wire["smtpath"]) == 1
|
||||||
|
|
||||||
|
offset = max(offset, 0)
|
||||||
|
|
||||||
|
end = width + offset
|
||||||
|
common_offset = max(sig.offset, offset)
|
||||||
|
common_end = min(sig_end, end)
|
||||||
|
if common_end <= common_offset:
|
||||||
|
continue
|
||||||
|
|
||||||
|
smt_expr = smt.net_expr(topmod, f"s{t}", wire["smtpath"])
|
||||||
|
|
||||||
|
if not smt_bool:
|
||||||
|
slice_high = common_end - offset - 1
|
||||||
|
slice_low = common_offset - offset
|
||||||
|
smt_expr = "((_ extract %d %d) %s)" % (slice_high, slice_low, smt_expr)
|
||||||
|
|
||||||
|
bit_slice = bits[len(bits) - (common_end - sig.offset):len(bits) - (common_offset - sig.offset)]
|
||||||
|
|
||||||
|
if bit_slice.count("?") == len(bit_slice):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if smt_bool:
|
||||||
|
assert width == 1
|
||||||
|
smt_constr = "(= %s %s)" % (smt_expr, "true" if bit_slice == "1" else "false")
|
||||||
|
else:
|
||||||
|
if "?" in bit_slice:
|
||||||
|
mask = bit_slice.replace("0", "1").replace("?", "0")
|
||||||
|
bit_slice = bit_slice.replace("?", "0")
|
||||||
|
smt_expr = "(bvand %s #b%s)" % (smt_expr, mask)
|
||||||
|
|
||||||
|
smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice)
|
||||||
|
|
||||||
|
constr_assumes[t].append((inywfile, smt_constr))
|
||||||
|
|
||||||
|
if sig.memory_path:
|
||||||
|
if sig.memory_path in smt_mems:
|
||||||
|
for mem in smt_mems[sig.memory_path]:
|
||||||
|
width, size, bv = mem["width"], mem["size"], mem["statebv"]
|
||||||
|
|
||||||
|
smt_expr = smt.net_expr(topmod, f"s{t}", mem["smtpath"])
|
||||||
|
|
||||||
|
if bv:
|
||||||
|
word_low = sig.memory_addr * width
|
||||||
|
word_high = word_low + width - 1
|
||||||
|
smt_expr = "((_ extract %d %d) %s)" % (word_high, word_low, smt_expr)
|
||||||
|
else:
|
||||||
|
addr_width = (size - 1).bit_length()
|
||||||
|
addr_bits = f"{sig.memory_addr:0{addr_width}b}"
|
||||||
|
smt_expr = "(select %s #b%s )" % (smt_expr, addr_bits)
|
||||||
|
|
||||||
|
if len(bits) < width:
|
||||||
|
slice_high = sig.offset + len(bits) - 1
|
||||||
|
smt_expr = "((_ extract %d %d) %s)" % (slice_high, sig.offset, smt_expr)
|
||||||
|
|
||||||
|
bit_slice = bits
|
||||||
|
|
||||||
|
if "?" in bit_slice:
|
||||||
|
mask = bit_slice.replace("0", "1").replace("?", "0")
|
||||||
|
bit_slice = bit_slice.replace("?", "0")
|
||||||
|
smt_expr = "(bvand %s #b%s)" % (smt_expr, mask)
|
||||||
|
|
||||||
|
smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice)
|
||||||
|
constr_assumes[t].append((inywfile, smt_constr))
|
||||||
|
|
||||||
if btorwitfile is not None:
|
if btorwitfile is not None:
|
||||||
with open(btorwitfile, "r") as f:
|
with open(btorwitfile, "r") as f:
|
||||||
step = None
|
step = None
|
||||||
|
@ -699,6 +807,115 @@ if btorwitfile is not None:
|
||||||
skip_steps = step
|
skip_steps = step
|
||||||
num_steps = step+1
|
num_steps = step+1
|
||||||
|
|
||||||
|
def collect_mem_trace_data(steps_start, steps_stop, vcd=None):
|
||||||
|
mem_trace_data = dict()
|
||||||
|
|
||||||
|
for mempath in sorted(smt.hiermems(topmod)):
|
||||||
|
abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath)
|
||||||
|
|
||||||
|
expr_id = list()
|
||||||
|
expr_list = list()
|
||||||
|
for i in range(steps_start, steps_stop):
|
||||||
|
for j in range(rports):
|
||||||
|
expr_id.append(('R', i-steps_start, j, 'A'))
|
||||||
|
expr_id.append(('R', i-steps_start, j, 'D'))
|
||||||
|
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j))
|
||||||
|
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j))
|
||||||
|
for j in range(wports):
|
||||||
|
expr_id.append(('W', i-steps_start, j, 'A'))
|
||||||
|
expr_id.append(('W', i-steps_start, j, 'D'))
|
||||||
|
expr_id.append(('W', i-steps_start, j, 'M'))
|
||||||
|
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j))
|
||||||
|
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j))
|
||||||
|
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j))
|
||||||
|
|
||||||
|
rdata = list()
|
||||||
|
wdata = list()
|
||||||
|
addrs = set()
|
||||||
|
|
||||||
|
for eid, edat in zip(expr_id, smt.get_list(expr_list)):
|
||||||
|
t, i, j, f = eid
|
||||||
|
|
||||||
|
if t == 'R':
|
||||||
|
c = rdata
|
||||||
|
elif t == 'W':
|
||||||
|
c = wdata
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
while len(c) <= i:
|
||||||
|
c.append(list())
|
||||||
|
c = c[i]
|
||||||
|
|
||||||
|
while len(c) <= j:
|
||||||
|
c.append(dict())
|
||||||
|
c = c[j]
|
||||||
|
|
||||||
|
c[f] = smt.bv2bin(edat)
|
||||||
|
|
||||||
|
if f == 'A':
|
||||||
|
addrs.add(c[f])
|
||||||
|
|
||||||
|
for addr in addrs:
|
||||||
|
tdata = list()
|
||||||
|
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)):
|
||||||
|
if not gotread:
|
||||||
|
for j_data in rdata[i]:
|
||||||
|
if j_data["A"] == addr:
|
||||||
|
data = list(j_data["D"])
|
||||||
|
gotread = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if gotread:
|
||||||
|
buf = data[:]
|
||||||
|
for ii in reversed(range(len(tdata))):
|
||||||
|
for k in range(width):
|
||||||
|
if tdata[ii][k] == "x":
|
||||||
|
tdata[ii][k] = buf[k]
|
||||||
|
else:
|
||||||
|
buf[k] = tdata[ii][k]
|
||||||
|
|
||||||
|
if not asyncwr:
|
||||||
|
tdata.append(data[:])
|
||||||
|
|
||||||
|
for j_data in wdata[i]:
|
||||||
|
if j_data["A"] != addr:
|
||||||
|
continue
|
||||||
|
|
||||||
|
D = j_data["D"]
|
||||||
|
M = j_data["M"]
|
||||||
|
|
||||||
|
for k in range(width):
|
||||||
|
if M[k] == "1":
|
||||||
|
data[k] = D[k]
|
||||||
|
|
||||||
|
if asyncwr:
|
||||||
|
tdata.append(data[:])
|
||||||
|
|
||||||
|
assert len(tdata) == len(rdata)
|
||||||
|
|
||||||
|
int_addr = int(addr, 2)
|
||||||
|
|
||||||
|
netpath = mempath[:]
|
||||||
|
if vcd:
|
||||||
|
netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int_addr)
|
||||||
|
vcd.add_net([topmod] + netpath, width)
|
||||||
|
|
||||||
|
for i in range(steps_start, steps_stop):
|
||||||
|
if i not in mem_trace_data:
|
||||||
|
mem_trace_data[i] = list()
|
||||||
|
mem_trace_data[i].append((netpath, int_addr, "".join(tdata[i-steps_start])))
|
||||||
|
|
||||||
|
return mem_trace_data
|
||||||
|
|
||||||
def write_vcd_trace(steps_start, steps_stop, index):
|
def write_vcd_trace(steps_start, steps_stop, index):
|
||||||
filename = vcdfile.replace("%", index)
|
filename = vcdfile.replace("%", index)
|
||||||
print_msg("Writing trace to VCD file: %s" % (filename))
|
print_msg("Writing trace to VCD file: %s" % (filename))
|
||||||
|
@ -720,107 +937,7 @@ def write_vcd_trace(steps_start, steps_stop, index):
|
||||||
vcd.add_clock([topmod] + netpath, edge)
|
vcd.add_clock([topmod] + netpath, edge)
|
||||||
path_list.append(netpath)
|
path_list.append(netpath)
|
||||||
|
|
||||||
mem_trace_data = dict()
|
mem_trace_data = collect_mem_trace_data(steps_start, steps_stop, vcd)
|
||||||
for mempath in sorted(smt.hiermems(topmod)):
|
|
||||||
abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath)
|
|
||||||
|
|
||||||
expr_id = list()
|
|
||||||
expr_list = list()
|
|
||||||
for i in range(steps_start, steps_stop):
|
|
||||||
for j in range(rports):
|
|
||||||
expr_id.append(('R', i-steps_start, j, 'A'))
|
|
||||||
expr_id.append(('R', i-steps_start, j, 'D'))
|
|
||||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j))
|
|
||||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j))
|
|
||||||
for j in range(wports):
|
|
||||||
expr_id.append(('W', i-steps_start, j, 'A'))
|
|
||||||
expr_id.append(('W', i-steps_start, j, 'D'))
|
|
||||||
expr_id.append(('W', i-steps_start, j, 'M'))
|
|
||||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j))
|
|
||||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j))
|
|
||||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j))
|
|
||||||
|
|
||||||
rdata = list()
|
|
||||||
wdata = list()
|
|
||||||
addrs = set()
|
|
||||||
|
|
||||||
for eid, edat in zip(expr_id, smt.get_list(expr_list)):
|
|
||||||
t, i, j, f = eid
|
|
||||||
|
|
||||||
if t == 'R':
|
|
||||||
c = rdata
|
|
||||||
elif t == 'W':
|
|
||||||
c = wdata
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
while len(c) <= i:
|
|
||||||
c.append(list())
|
|
||||||
c = c[i]
|
|
||||||
|
|
||||||
while len(c) <= j:
|
|
||||||
c.append(dict())
|
|
||||||
c = c[j]
|
|
||||||
|
|
||||||
c[f] = smt.bv2bin(edat)
|
|
||||||
|
|
||||||
if f == 'A':
|
|
||||||
addrs.add(c[f])
|
|
||||||
|
|
||||||
for addr in addrs:
|
|
||||||
tdata = list()
|
|
||||||
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)):
|
|
||||||
if not gotread:
|
|
||||||
for j_data in rdata[i]:
|
|
||||||
if j_data["A"] == addr:
|
|
||||||
data = list(j_data["D"])
|
|
||||||
gotread = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if gotread:
|
|
||||||
buf = data[:]
|
|
||||||
for ii in reversed(range(len(tdata))):
|
|
||||||
for k in range(width):
|
|
||||||
if tdata[ii][k] == "x":
|
|
||||||
tdata[ii][k] = buf[k]
|
|
||||||
else:
|
|
||||||
buf[k] = tdata[ii][k]
|
|
||||||
|
|
||||||
if not asyncwr:
|
|
||||||
tdata.append(data[:])
|
|
||||||
|
|
||||||
for j_data in wdata[i]:
|
|
||||||
if j_data["A"] != addr:
|
|
||||||
continue
|
|
||||||
|
|
||||||
D = j_data["D"]
|
|
||||||
M = j_data["M"]
|
|
||||||
|
|
||||||
for k in range(width):
|
|
||||||
if M[k] == "1":
|
|
||||||
data[k] = D[k]
|
|
||||||
|
|
||||||
if asyncwr:
|
|
||||||
tdata.append(data[:])
|
|
||||||
|
|
||||||
assert len(tdata) == len(rdata)
|
|
||||||
|
|
||||||
netpath = mempath[:]
|
|
||||||
netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int(addr, 2))
|
|
||||||
vcd.add_net([topmod] + netpath, width)
|
|
||||||
|
|
||||||
for i in range(steps_start, steps_stop):
|
|
||||||
if i not in mem_trace_data:
|
|
||||||
mem_trace_data[i] = list()
|
|
||||||
mem_trace_data[i].append((netpath, "".join(tdata[i-steps_start])))
|
|
||||||
|
|
||||||
for i in range(steps_start, steps_stop):
|
for i in range(steps_start, steps_stop):
|
||||||
vcd.set_time(i)
|
vcd.set_time(i)
|
||||||
|
@ -828,7 +945,7 @@ def write_vcd_trace(steps_start, steps_stop, index):
|
||||||
for path, value in zip(path_list, value_list):
|
for path, value in zip(path_list, value_list):
|
||||||
vcd.set_net([topmod] + path, value)
|
vcd.set_net([topmod] + path, value)
|
||||||
if i in mem_trace_data:
|
if i in mem_trace_data:
|
||||||
for path, value in mem_trace_data[i]:
|
for path, addr, value in mem_trace_data[i]:
|
||||||
vcd.set_net([topmod] + path, value)
|
vcd.set_net([topmod] + path, value)
|
||||||
|
|
||||||
vcd.set_time(steps_stop)
|
vcd.set_time(steps_stop)
|
||||||
|
@ -1072,8 +1189,72 @@ def write_constr_trace(steps_start, steps_stop, index):
|
||||||
for name, val in zip(pi_names, pi_values):
|
for name, val in zip(pi_names, pi_values):
|
||||||
print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f)
|
print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f)
|
||||||
|
|
||||||
|
def write_yw_trace(steps_start, steps_stop, index, allregs=False):
|
||||||
|
filename = outywfile.replace("%", index)
|
||||||
|
print_msg("Writing trace to Yosys witness file: %s" % (filename))
|
||||||
|
|
||||||
def write_trace(steps_start, steps_stop, index):
|
mem_trace_data = collect_mem_trace_data(steps_start, steps_stop)
|
||||||
|
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
inits, seqs, clocks, mems = smt.hierwitness(topmod, allregs)
|
||||||
|
|
||||||
|
yw = WriteWitness(f, "smtbmc")
|
||||||
|
|
||||||
|
for clock in clocks:
|
||||||
|
yw.add_clock(clock["path"], clock["offset"], clock["type"])
|
||||||
|
|
||||||
|
for seq in seqs:
|
||||||
|
seq["sig"] = yw.add_sig(seq["path"], seq["offset"], seq["width"])
|
||||||
|
|
||||||
|
for init in inits:
|
||||||
|
init["sig"] = yw.add_sig(init["path"], init["offset"], init["width"], True)
|
||||||
|
|
||||||
|
inits = seqs + inits
|
||||||
|
|
||||||
|
mem_dict = {tuple(mem["smtpath"]): mem for mem in mems}
|
||||||
|
mem_init_values = []
|
||||||
|
|
||||||
|
for path, addr, value in mem_trace_data.get(0, ()):
|
||||||
|
json_mem = mem_dict.get(tuple(path))
|
||||||
|
if not json_mem:
|
||||||
|
continue
|
||||||
|
|
||||||
|
bit_addr = addr * json_mem["width"]
|
||||||
|
uninit_chunks = [(chunk["width"] + chunk["offset"], chunk["offset"]) for chunk in json_mem["uninitialized"]]
|
||||||
|
first_chunk_nr = bisect.bisect_left(uninit_chunks, (bit_addr + 1,))
|
||||||
|
|
||||||
|
for uninit_end, uninit_offset in uninit_chunks[first_chunk_nr:]:
|
||||||
|
assert uninit_end > bit_addr
|
||||||
|
if uninit_offset > bit_addr + json_mem["width"]:
|
||||||
|
break
|
||||||
|
|
||||||
|
word_path = (*json_mem["path"], f"\\[{addr}]")
|
||||||
|
|
||||||
|
overlap_start = max(uninit_offset - bit_addr, 0)
|
||||||
|
overlap_end = min(uninit_end - bit_addr, json_mem["width"])
|
||||||
|
overlap_bits = value[len(value)-overlap_end:len(value)-overlap_start]
|
||||||
|
|
||||||
|
sig = yw.add_sig(word_path, overlap_start, overlap_end - overlap_start, True)
|
||||||
|
mem_init_values.append((sig, overlap_bits.replace("x", "?")))
|
||||||
|
|
||||||
|
for k in range(steps_start, steps_stop):
|
||||||
|
step_values = WitnessValues()
|
||||||
|
|
||||||
|
if k == steps_start:
|
||||||
|
for sig, value in mem_init_values:
|
||||||
|
step_values[sig] = value
|
||||||
|
sigs = inits + seqs
|
||||||
|
else:
|
||||||
|
sigs = seqs
|
||||||
|
|
||||||
|
for sig in sigs:
|
||||||
|
step_values[sig["sig"]] = smt.bv2bin(smt.get(smt.net_expr(topmod, f"s{k}", sig["smtpath"])))
|
||||||
|
yw.step(step_values)
|
||||||
|
|
||||||
|
yw.end_trace()
|
||||||
|
|
||||||
|
|
||||||
|
def write_trace(steps_start, steps_stop, index, allregs=False):
|
||||||
if vcdfile is not None:
|
if vcdfile is not None:
|
||||||
write_vcd_trace(steps_start, steps_stop, index)
|
write_vcd_trace(steps_start, steps_stop, index)
|
||||||
|
|
||||||
|
@ -1083,6 +1264,9 @@ def write_trace(steps_start, steps_stop, index):
|
||||||
if outconstr is not None:
|
if outconstr is not None:
|
||||||
write_constr_trace(steps_start, steps_stop, index)
|
write_constr_trace(steps_start, steps_stop, index)
|
||||||
|
|
||||||
|
if outywfile is not None:
|
||||||
|
write_yw_trace(steps_start, steps_stop, index, allregs)
|
||||||
|
|
||||||
|
|
||||||
def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()):
|
def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()):
|
||||||
assert mod in smt.modinfo
|
assert mod in smt.modinfo
|
||||||
|
@ -1392,12 +1576,12 @@ if tempind:
|
||||||
print_msg("Temporal induction failed!")
|
print_msg("Temporal induction failed!")
|
||||||
print_anyconsts(num_steps)
|
print_anyconsts(num_steps)
|
||||||
print_failed_asserts(num_steps)
|
print_failed_asserts(num_steps)
|
||||||
write_trace(step, num_steps+1, '%')
|
write_trace(step, num_steps+1, '%', allregs=True)
|
||||||
|
|
||||||
elif dumpall:
|
elif dumpall:
|
||||||
print_anyconsts(num_steps)
|
print_anyconsts(num_steps)
|
||||||
print_failed_asserts(num_steps)
|
print_failed_asserts(num_steps)
|
||||||
write_trace(step, num_steps+1, "%d" % step)
|
write_trace(step, num_steps+1, "%d" % step, allregs=True)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print_msg("Temporal induction successful.")
|
print_msg("Temporal induction successful.")
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys, re, os, signal
|
import sys, re, os, signal, json
|
||||||
import subprocess
|
import subprocess
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
import resource
|
import resource
|
||||||
|
@ -108,6 +108,7 @@ class SmtModInfo:
|
||||||
self.allconsts = dict()
|
self.allconsts = dict()
|
||||||
self.allseqs = dict()
|
self.allseqs = dict()
|
||||||
self.asize = dict()
|
self.asize = dict()
|
||||||
|
self.witness = []
|
||||||
|
|
||||||
|
|
||||||
class SmtIo:
|
class SmtIo:
|
||||||
|
@ -587,6 +588,11 @@ class SmtIo:
|
||||||
self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
|
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])
|
self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
|
||||||
|
|
||||||
|
if fields[1] == "yosys-smt2-witness":
|
||||||
|
data = json.loads(stmt.split(None, 2)[2])
|
||||||
|
if data.get("type") in ["cell", "mem", "posedge", "negedge", "input", "reg", "init", "seq", "blackbox"]:
|
||||||
|
self.modinfo[self.curmod].witness.append(data)
|
||||||
|
|
||||||
def hiernets(self, top, regs_only=False):
|
def hiernets(self, top, regs_only=False):
|
||||||
def hiernets_worker(nets, mod, cursor):
|
def hiernets_worker(nets, mod, cursor):
|
||||||
for netname in sorted(self.modinfo[mod].wsize.keys()):
|
for netname in sorted(self.modinfo[mod].wsize.keys()):
|
||||||
|
@ -658,6 +664,57 @@ class SmtIo:
|
||||||
hiermems_worker(mems, top, [])
|
hiermems_worker(mems, top, [])
|
||||||
return mems
|
return mems
|
||||||
|
|
||||||
|
def hierwitness(self, top, allregs=False, blackbox=True):
|
||||||
|
init_witnesses = []
|
||||||
|
seq_witnesses = []
|
||||||
|
clk_witnesses = []
|
||||||
|
mem_witnesses = []
|
||||||
|
|
||||||
|
def absolute(path, cursor, witness):
|
||||||
|
return {
|
||||||
|
**witness,
|
||||||
|
"path": path + tuple(witness["path"]),
|
||||||
|
"smtpath": cursor + [witness["smtname"]],
|
||||||
|
}
|
||||||
|
|
||||||
|
for witness in self.modinfo[top].witness:
|
||||||
|
if witness["type"] == "input":
|
||||||
|
seq_witnesses.append(absolute((), [], witness))
|
||||||
|
if witness["type"] in ("posedge", "negedge"):
|
||||||
|
clk_witnesses.append(absolute((), [], witness))
|
||||||
|
|
||||||
|
init_types = ["init"]
|
||||||
|
if allregs:
|
||||||
|
init_types.append("reg")
|
||||||
|
|
||||||
|
seq_types = ["seq"]
|
||||||
|
if blackbox:
|
||||||
|
seq_types.append("blackbox")
|
||||||
|
|
||||||
|
def worker(mod, path, cursor):
|
||||||
|
cell_paths = {}
|
||||||
|
for witness in self.modinfo[mod].witness:
|
||||||
|
if witness["type"] in init_types:
|
||||||
|
init_witnesses.append(absolute(path, cursor, witness))
|
||||||
|
if witness["type"] in seq_types:
|
||||||
|
seq_witnesses.append(absolute(path, cursor, witness))
|
||||||
|
if witness["type"] == "mem":
|
||||||
|
if allregs and not witness["rom"]:
|
||||||
|
width, size = witness["width"], witness["size"]
|
||||||
|
witness = {**witness, "uninitialized": {"width": width * size, "offset": 0}}
|
||||||
|
if not witness["uninitialized"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
mem_witnesses.append(absolute(path, cursor, witness))
|
||||||
|
if witness["type"] == "cell":
|
||||||
|
cell_paths[witness["smtname"]] = tuple(witness["path"])
|
||||||
|
|
||||||
|
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
||||||
|
worker(celltype, path + cell_paths.get(cellname, ("?" + cellname,)), cursor + [cellname])
|
||||||
|
|
||||||
|
worker(top, (), [])
|
||||||
|
return init_witnesses, seq_witnesses, clk_witnesses, mem_witnesses
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
stmt = []
|
stmt = []
|
||||||
count_brackets = 0
|
count_brackets = 0
|
||||||
|
@ -887,6 +944,8 @@ class SmtIo:
|
||||||
assert mod in self.modinfo
|
assert mod in self.modinfo
|
||||||
if path[0] == "":
|
if path[0] == "":
|
||||||
return base
|
return base
|
||||||
|
if isinstance(path[0], int):
|
||||||
|
return "(|%s#%d| %s)" % (mod, path[0], base)
|
||||||
if path[0] in self.modinfo[mod].cells:
|
if path[0] in self.modinfo[mod].cells:
|
||||||
return "(|%s_h %s| %s)" % (mod, path[0], base)
|
return "(|%s_h %s| %s)" % (mod, path[0], base)
|
||||||
if path[0] in self.modinfo[mod].wsize:
|
if path[0] in self.modinfo[mod].wsize:
|
||||||
|
@ -909,6 +968,8 @@ class SmtIo:
|
||||||
mod = self.modinfo[mod].cells[net_path[i]]
|
mod = self.modinfo[mod].cells[net_path[i]]
|
||||||
|
|
||||||
assert mod in self.modinfo
|
assert mod in self.modinfo
|
||||||
|
if isinstance(net_path[-1], int):
|
||||||
|
return None
|
||||||
assert net_path[-1] in self.modinfo[mod].wsize
|
assert net_path[-1] in self.modinfo[mod].wsize
|
||||||
return self.modinfo[mod].wsize[net_path[-1]]
|
return self.modinfo[mod].wsize[net_path[-1]]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# yosys -- Yosys Open SYnthesis Suite
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one>
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
##yosys-sys-path##
|
||||||
|
import click
|
||||||
|
|
||||||
|
from ywio import ReadWitness, WriteWitness, WitnessSig
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def cli():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(help="""
|
||||||
|
Display a Yosys witness trace in a human readable format.
|
||||||
|
""")
|
||||||
|
@click.argument("input", type=click.File("r"))
|
||||||
|
def display(input):
|
||||||
|
click.echo(f"Reading Yosys witness trace {input.name!r}...")
|
||||||
|
inyw = ReadWitness(input)
|
||||||
|
|
||||||
|
def output():
|
||||||
|
|
||||||
|
yield click.style("*** RTLIL bit-order below may differ from source level declarations ***", fg="red")
|
||||||
|
if inyw.clocks:
|
||||||
|
yield click.style("=== Clock Signals ===", fg="blue")
|
||||||
|
for clock in inyw.clocks:
|
||||||
|
yield f" {clock['edge']} {WitnessSig(clock['path'], clock['offset']).pretty()}"
|
||||||
|
|
||||||
|
for t, values in inyw.steps():
|
||||||
|
if t:
|
||||||
|
yield click.style(f"=== Step {t} ===", fg="blue")
|
||||||
|
else:
|
||||||
|
yield click.style("=== Initial State ===", fg="blue")
|
||||||
|
|
||||||
|
step_prefix = click.style(f"#{t}", fg="bright_black")
|
||||||
|
|
||||||
|
signals, missing = values.present_signals(inyw.sigmap)
|
||||||
|
|
||||||
|
assert not missing
|
||||||
|
|
||||||
|
for sig in signals:
|
||||||
|
display_bits = values[sig].replace("?", click.style("?", fg="bright_black"))
|
||||||
|
yield f" {step_prefix} {sig.pretty()} = {display_bits}"
|
||||||
|
click.echo_via_pager([line + "\n" for line in output()])
|
||||||
|
|
||||||
|
@cli.command(help="""
|
||||||
|
Transform a Yosys witness trace.
|
||||||
|
|
||||||
|
Currently no transformations are implemented, so it is only useful for testing.
|
||||||
|
""")
|
||||||
|
@click.argument("input", type=click.File("r"))
|
||||||
|
@click.argument("output", type=click.File("w"))
|
||||||
|
def yw2yw(input, output):
|
||||||
|
click.echo(f"Copying yosys witness trace from {input.name!r} to {output.name!r}...")
|
||||||
|
inyw = ReadWitness(input)
|
||||||
|
outyw = WriteWitness(output, "yosys-witness yw2yw")
|
||||||
|
|
||||||
|
for clock in inyw.clocks:
|
||||||
|
outyw.add_clock(clock["path"], clock["offset"], clock["edge"])
|
||||||
|
|
||||||
|
for sig in inyw.signals:
|
||||||
|
outyw.add_sig(sig.path, sig.offset, sig.width, sig.init_only)
|
||||||
|
|
||||||
|
for t, values in inyw.steps():
|
||||||
|
outyw.step(values)
|
||||||
|
|
||||||
|
outyw.end_trace()
|
||||||
|
|
||||||
|
click.echo(f"Copied {outyw.t + 1} time steps.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cli()
|
|
@ -0,0 +1,392 @@
|
||||||
|
#
|
||||||
|
# yosys -- Yosys Open SYnthesis Suite
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one>
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import json, re
|
||||||
|
|
||||||
|
from functools import total_ordering
|
||||||
|
|
||||||
|
|
||||||
|
class PrettyJson:
|
||||||
|
def __init__(self, f):
|
||||||
|
self.f = f
|
||||||
|
self.indent = 0
|
||||||
|
self.state = ["value"]
|
||||||
|
|
||||||
|
def line(self):
|
||||||
|
indent = len(self.state) - bool(self.state and self.state[-1] == "value")
|
||||||
|
print("\n", end=" " * (2 * indent), file=self.f)
|
||||||
|
|
||||||
|
def raw(self, str):
|
||||||
|
print(end=str, file=self.f)
|
||||||
|
|
||||||
|
def begin_object(self):
|
||||||
|
self.begin_value()
|
||||||
|
self.raw("{")
|
||||||
|
self.state.append("object_first")
|
||||||
|
|
||||||
|
def begin_array(self):
|
||||||
|
self.begin_value()
|
||||||
|
self.raw("[")
|
||||||
|
self.state.append("array_first")
|
||||||
|
|
||||||
|
def end_object(self):
|
||||||
|
state = self.state.pop()
|
||||||
|
if state == "object":
|
||||||
|
self.line()
|
||||||
|
else:
|
||||||
|
assert state == "object_first"
|
||||||
|
self.raw("}")
|
||||||
|
self.end_value()
|
||||||
|
|
||||||
|
def end_array(self):
|
||||||
|
state = self.state.pop()
|
||||||
|
if state == "array":
|
||||||
|
self.line()
|
||||||
|
else:
|
||||||
|
assert state == "array_first"
|
||||||
|
self.raw("]")
|
||||||
|
self.end_value()
|
||||||
|
|
||||||
|
def name(self, name):
|
||||||
|
if self.state[-1] == "object_first":
|
||||||
|
self.state[-1] = "object"
|
||||||
|
else:
|
||||||
|
self.raw(",")
|
||||||
|
self.line()
|
||||||
|
json.dump(str(name), self.f)
|
||||||
|
self.raw(": ")
|
||||||
|
self.state.append("value")
|
||||||
|
|
||||||
|
def begin_value(self):
|
||||||
|
if self.state[-1] == "array_first":
|
||||||
|
self.line()
|
||||||
|
self.state[-1] = "array"
|
||||||
|
elif self.state[-1] == "array":
|
||||||
|
self.raw(",")
|
||||||
|
self.line()
|
||||||
|
else:
|
||||||
|
assert self.state.pop() == "value"
|
||||||
|
|
||||||
|
def end_value(self):
|
||||||
|
if not self.state:
|
||||||
|
print(file=self.f, flush=True)
|
||||||
|
|
||||||
|
def value(self, value):
|
||||||
|
self.begin_value()
|
||||||
|
json.dump(value, self.f)
|
||||||
|
self.end_value()
|
||||||
|
|
||||||
|
def entry(self, name, value):
|
||||||
|
self.name(name)
|
||||||
|
self.value(value)
|
||||||
|
|
||||||
|
def object(self, entries=None):
|
||||||
|
if isinstance(entries, dict):
|
||||||
|
entries = dict.items()
|
||||||
|
self.begin_object()
|
||||||
|
for name, value in entries:
|
||||||
|
self.entry(name, value)
|
||||||
|
self.end_object()
|
||||||
|
|
||||||
|
def array(self, values=None):
|
||||||
|
self.begin_array()
|
||||||
|
for value in values:
|
||||||
|
self.value(value)
|
||||||
|
self.end_array()
|
||||||
|
|
||||||
|
|
||||||
|
addr_re = re.compile(r'\\\[[0-9]+\]$')
|
||||||
|
public_name_re = re.compile(r"\\([a-zA-Z_][a-zA-Z0-9_]*(\[[0-9]+\])?|\[[0-9]+\])$")
|
||||||
|
|
||||||
|
def pretty_name(id):
|
||||||
|
if public_name_re.match(id):
|
||||||
|
return id.lstrip("\\")
|
||||||
|
else:
|
||||||
|
return id
|
||||||
|
|
||||||
|
def pretty_path(path):
|
||||||
|
out = ""
|
||||||
|
for name in path:
|
||||||
|
name = pretty_name(name)
|
||||||
|
if name.startswith("["):
|
||||||
|
out += name
|
||||||
|
continue
|
||||||
|
if out:
|
||||||
|
out += "."
|
||||||
|
if name.startswith("\\") or name.startswith("$"):
|
||||||
|
out += name + " "
|
||||||
|
else:
|
||||||
|
out += name
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
@total_ordering
|
||||||
|
class WitnessSig:
|
||||||
|
def __init__(self, path, offset, width=1, init_only=False):
|
||||||
|
path = tuple(path)
|
||||||
|
self.path, self.width, self.offset, self.init_only = path, width, offset, init_only
|
||||||
|
|
||||||
|
self.memory_path = None
|
||||||
|
self.memory_addr = None
|
||||||
|
|
||||||
|
sort_path = path
|
||||||
|
sort_id = -1
|
||||||
|
if path and addr_re.match(path[-1]):
|
||||||
|
self.memory_path = sort_path = path[:-1]
|
||||||
|
self.memory_addr = sort_id = int(path[-1][2:-1])
|
||||||
|
|
||||||
|
self.sort_key = (init_only, sort_path, sort_id, offset, width)
|
||||||
|
|
||||||
|
def bits(self):
|
||||||
|
return ((self.path, i) for i in range(self.offset, self.offset + self.width))
|
||||||
|
|
||||||
|
def rev_bits(self):
|
||||||
|
return ((self.path, i) for i in reversed(range(self.offset, self.offset + self.width)))
|
||||||
|
|
||||||
|
def pretty(self):
|
||||||
|
if self.width > 1:
|
||||||
|
last_offset = self.offset + self.width - 1
|
||||||
|
return f"{pretty_path(self.path)}[{last_offset}:{self.offset}]"
|
||||||
|
else:
|
||||||
|
return f"{pretty_path(self.path)}[{self.offset}]"
|
||||||
|
|
||||||
|
def __eq__(self):
|
||||||
|
return self.sort_key
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.sort_key)
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.sort_key < other.sort_key
|
||||||
|
|
||||||
|
|
||||||
|
def coalesce_signals(signals):
|
||||||
|
bits = {}
|
||||||
|
for sig in signals:
|
||||||
|
for bit in sig.bits():
|
||||||
|
if sig.init_only:
|
||||||
|
bits.setdefault(bit, False)
|
||||||
|
else:
|
||||||
|
bits[bit] = True
|
||||||
|
|
||||||
|
active = None
|
||||||
|
|
||||||
|
out = []
|
||||||
|
|
||||||
|
for bit, not_init in sorted(bits.items()):
|
||||||
|
if active:
|
||||||
|
if active[0] == bit[0] and active[2] == bit[1] and active[3] == not_init:
|
||||||
|
active[2] += 1
|
||||||
|
else:
|
||||||
|
out.append(WitnessSig(active[0], active[1], active[2] - active[1], not active[3]))
|
||||||
|
active = None
|
||||||
|
|
||||||
|
if active is None:
|
||||||
|
active = [bit[0], bit[1], bit[1] + 1, not_init]
|
||||||
|
|
||||||
|
if active:
|
||||||
|
out.append(WitnessSig(active[0], active[1], active[2] - active[1], not active[3]))
|
||||||
|
|
||||||
|
return sorted(out)
|
||||||
|
|
||||||
|
|
||||||
|
class WitnessSigMap:
|
||||||
|
def __init__(self, signals=[]):
|
||||||
|
self.signals = []
|
||||||
|
|
||||||
|
self.id_to_bit = []
|
||||||
|
self.bit_to_id = {}
|
||||||
|
self.bit_to_sig = {}
|
||||||
|
|
||||||
|
for sig in signals:
|
||||||
|
self.add_signal(sig)
|
||||||
|
|
||||||
|
def add_signal(self, sig):
|
||||||
|
self.signals.append(sig)
|
||||||
|
for bit in sig.bits():
|
||||||
|
self.add_bit(bit)
|
||||||
|
self.bit_to_sig[bit] = sig
|
||||||
|
|
||||||
|
def add_bit(self, bit, id=None):
|
||||||
|
if id is None:
|
||||||
|
id = len(self.id_to_bit)
|
||||||
|
self.id_to_bit.append(bit)
|
||||||
|
else:
|
||||||
|
if len(self.id_to_bit) <= id:
|
||||||
|
self.id_to_bit += [None] * (id - len(self.id_to_bit) + 1)
|
||||||
|
self.id_to_bit[id] = bit
|
||||||
|
self.bit_to_id[bit] = id
|
||||||
|
|
||||||
|
|
||||||
|
class WitnessValues:
|
||||||
|
def __init__(self):
|
||||||
|
self.values = {}
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if isinstance(key, tuple) and len(key) == 2:
|
||||||
|
if value != "?":
|
||||||
|
assert isinstance(value, str)
|
||||||
|
assert len(value) == 1
|
||||||
|
self.values[key] = value
|
||||||
|
else:
|
||||||
|
assert isinstance(key, WitnessSig)
|
||||||
|
assert key.width == len(value)
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = reversed(value)
|
||||||
|
for bit, bit_value in zip(key.bits(), value):
|
||||||
|
if bit_value != "?":
|
||||||
|
self.values[bit] = bit_value
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
if isinstance(key, tuple) and len(key) == 2:
|
||||||
|
return self.values.get(key, "?")
|
||||||
|
else:
|
||||||
|
assert isinstance(key, WitnessSig)
|
||||||
|
return "".join([self.values.get(bit, "?") for bit in key.rev_bits()])
|
||||||
|
|
||||||
|
def pack_present(self, sigmap):
|
||||||
|
missing = []
|
||||||
|
|
||||||
|
max_id = max((sigmap.bit_to_id.get(bit, -1) for bit in self.values), default=-1)
|
||||||
|
|
||||||
|
vector = ["?"] * (max_id + 1)
|
||||||
|
for bit, bit_value in self.values.items():
|
||||||
|
id = sigmap.bit_to_id.get(bit, - 1)
|
||||||
|
if id < 0:
|
||||||
|
missing.append(bit)
|
||||||
|
else:
|
||||||
|
vector[max_id - sigmap.bit_to_id[bit]] = bit_value
|
||||||
|
|
||||||
|
return "".join(vector), missing
|
||||||
|
|
||||||
|
def pack(self, sigmap):
|
||||||
|
packed, missing = self.pack_present(sigmap)
|
||||||
|
if missing:
|
||||||
|
raise RuntimeError(f"Cannot pack bits {missing!r}")
|
||||||
|
return packed
|
||||||
|
|
||||||
|
def unpack(self, sigmap, bits):
|
||||||
|
for i, bit_value in enumerate(reversed(bits)):
|
||||||
|
if bit_value != "?":
|
||||||
|
self.values[sigmap.id_to_bit[i]] = bit_value
|
||||||
|
|
||||||
|
def present_signals(self, sigmap):
|
||||||
|
signals = set(sigmap.bit_to_sig.get(bit) for bit in self.values)
|
||||||
|
missing_signals = None in signals
|
||||||
|
if missing_signals:
|
||||||
|
signals.discard(None)
|
||||||
|
|
||||||
|
return sorted(signals), missing_signals
|
||||||
|
|
||||||
|
|
||||||
|
class WriteWitness:
|
||||||
|
def __init__(self, f, generator):
|
||||||
|
self.out = PrettyJson(f)
|
||||||
|
self.t = 0
|
||||||
|
self.header_written = False
|
||||||
|
self.clocks = []
|
||||||
|
self.signals = []
|
||||||
|
|
||||||
|
self.out.begin_object()
|
||||||
|
self.out.entry("format", "Yosys Witness Trace")
|
||||||
|
self.out.entry("generator", generator)
|
||||||
|
|
||||||
|
def add_clock(self, path, offset, edge):
|
||||||
|
assert not self.header_written
|
||||||
|
self.clocks.append({
|
||||||
|
"path": path,
|
||||||
|
"edge": edge,
|
||||||
|
"offset": offset,
|
||||||
|
})
|
||||||
|
|
||||||
|
def add_sig(self, path, offset, width=1, init_only=False):
|
||||||
|
assert not self.header_written
|
||||||
|
sig = WitnessSig(path, offset, width, init_only)
|
||||||
|
self.signals.append(sig)
|
||||||
|
return sig
|
||||||
|
|
||||||
|
def write_header(self):
|
||||||
|
assert not self.header_written
|
||||||
|
self.header_written = True
|
||||||
|
self.out.name("clocks")
|
||||||
|
self.out.array(self.clocks)
|
||||||
|
|
||||||
|
self.signals = coalesce_signals(self.signals)
|
||||||
|
self.sigmap = WitnessSigMap(self.signals)
|
||||||
|
|
||||||
|
self.out.name("signals")
|
||||||
|
self.out.array({
|
||||||
|
"path": sig.path,
|
||||||
|
"width": sig.width,
|
||||||
|
"offset": sig.offset,
|
||||||
|
"init_only": sig.init_only,
|
||||||
|
} for sig in self.signals)
|
||||||
|
|
||||||
|
self.out.name("steps")
|
||||||
|
self.out.begin_array()
|
||||||
|
|
||||||
|
def step(self, values):
|
||||||
|
if not self.header_written:
|
||||||
|
self.write_header()
|
||||||
|
|
||||||
|
self.out.value({"bits": values.pack(self.sigmap)})
|
||||||
|
|
||||||
|
self.t += 1
|
||||||
|
|
||||||
|
def end_trace(self):
|
||||||
|
if not self.header_written:
|
||||||
|
self.write_header()
|
||||||
|
self.out.end_array()
|
||||||
|
self.out.end_object()
|
||||||
|
|
||||||
|
|
||||||
|
class ReadWitness:
|
||||||
|
def __init__(self, f):
|
||||||
|
data = json.load(f)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
data_format = data.get("format", "Unknown Format")
|
||||||
|
|
||||||
|
if data_format != "Yosys Witness Trace":
|
||||||
|
raise ValueError(f"unsupported format {data_format!r}")
|
||||||
|
|
||||||
|
self.clocks = data["clocks"]
|
||||||
|
for clock in self.clocks:
|
||||||
|
clock["path"] = tuple(clock["path"])
|
||||||
|
|
||||||
|
self.signals = [
|
||||||
|
WitnessSig(sig["path"], sig["offset"], sig["width"], sig["init_only"])
|
||||||
|
for sig in data["signals"]
|
||||||
|
]
|
||||||
|
|
||||||
|
self.sigmap = WitnessSigMap(self.signals)
|
||||||
|
|
||||||
|
self.bits = [step["bits"] for step in data["steps"]]
|
||||||
|
|
||||||
|
def step(self, t):
|
||||||
|
values = WitnessValues()
|
||||||
|
values.unpack(self.sigmap, self.bits[t])
|
||||||
|
return values
|
||||||
|
|
||||||
|
def steps(self):
|
||||||
|
for i in range(len(self.bits)):
|
||||||
|
yield i, self.step(i)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.bits)
|
|
@ -4,11 +4,14 @@
|
||||||
(declare-fun |smtlib2_is| (|smtlib2_s|) Bool)
|
(declare-fun |smtlib2_is| (|smtlib2_s|) Bool)
|
||||||
(declare-fun |smtlib2#0| (|smtlib2_s|) (_ BitVec 8)) ; \a
|
(declare-fun |smtlib2#0| (|smtlib2_s|) (_ BitVec 8)) ; \a
|
||||||
; yosys-smt2-input a 8
|
; yosys-smt2-input a 8
|
||||||
|
; yosys-smt2-witness {"offset": 0, "path": ["\\a"], "smtname": "a", "type": "input", "width": 8}
|
||||||
(define-fun |smtlib2_n a| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#0| state))
|
(define-fun |smtlib2_n a| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#0| state))
|
||||||
(declare-fun |smtlib2#1| (|smtlib2_s|) (_ BitVec 8)) ; \b
|
(declare-fun |smtlib2#1| (|smtlib2_s|) (_ BitVec 8)) ; \b
|
||||||
; yosys-smt2-input b 8
|
; yosys-smt2-input b 8
|
||||||
|
; yosys-smt2-witness {"offset": 0, "path": ["\\b"], "smtname": "b", "type": "input", "width": 8}
|
||||||
(define-fun |smtlib2_n b| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#1| state))
|
(define-fun |smtlib2_n b| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#1| state))
|
||||||
; yosys-smt2-output add 8
|
; yosys-smt2-output add 8
|
||||||
|
; yosys-smt2-witness {"offset": 0, "path": ["\\add"], "smtname": "add", "type": "blackbox", "width": 8}
|
||||||
(define-fun |smtlib2_n add| ((state |smtlib2_s|)) (_ BitVec 8) (let (
|
(define-fun |smtlib2_n add| ((state |smtlib2_s|)) (_ BitVec 8) (let (
|
||||||
(|a| (|smtlib2_n a| state))
|
(|a| (|smtlib2_n a| state))
|
||||||
(|b| (|smtlib2_n b| state))
|
(|b| (|smtlib2_n b| state))
|
||||||
|
@ -16,6 +19,7 @@
|
||||||
(bvadd a b)
|
(bvadd a b)
|
||||||
))
|
))
|
||||||
; yosys-smt2-output eq 1
|
; yosys-smt2-output eq 1
|
||||||
|
; yosys-smt2-witness {"offset": 0, "path": ["\\eq"], "smtname": "eq", "type": "blackbox", "width": 1}
|
||||||
(define-fun |smtlib2_n eq| ((state |smtlib2_s|)) Bool (let (
|
(define-fun |smtlib2_n eq| ((state |smtlib2_s|)) Bool (let (
|
||||||
(|a| (|smtlib2_n a| state))
|
(|a| (|smtlib2_n a| state))
|
||||||
(|b| (|smtlib2_n b| state))
|
(|b| (|smtlib2_n b| state))
|
||||||
|
@ -23,6 +27,7 @@
|
||||||
(= a b)
|
(= a b)
|
||||||
))
|
))
|
||||||
; yosys-smt2-output sub 8
|
; yosys-smt2-output sub 8
|
||||||
|
; yosys-smt2-witness {"offset": 0, "path": ["\\sub"], "smtname": "sub", "type": "blackbox", "width": 8}
|
||||||
(define-fun |smtlib2_n sub| ((state |smtlib2_s|)) (_ BitVec 8) (let (
|
(define-fun |smtlib2_n sub| ((state |smtlib2_s|)) (_ BitVec 8) (let (
|
||||||
(|a| (|smtlib2_n a| state))
|
(|a| (|smtlib2_n a| state))
|
||||||
(|b| (|smtlib2_n b| state))
|
(|b| (|smtlib2_n b| state))
|
||||||
|
@ -38,13 +43,16 @@
|
||||||
(declare-sort |uut_s| 0)
|
(declare-sort |uut_s| 0)
|
||||||
(declare-fun |uut_is| (|uut_s|) Bool)
|
(declare-fun |uut_is| (|uut_s|) Bool)
|
||||||
; yosys-smt2-cell smtlib2 s
|
; yosys-smt2-cell smtlib2 s
|
||||||
|
; yosys-smt2-witness {"path": ["\\s"], "smtname": "s", "type": "cell"}
|
||||||
(declare-fun |uut#0| (|uut_s|) (_ BitVec 8)) ; \add
|
(declare-fun |uut#0| (|uut_s|) (_ BitVec 8)) ; \add
|
||||||
(declare-fun |uut#1| (|uut_s|) Bool) ; \eq
|
(declare-fun |uut#1| (|uut_s|) Bool) ; \eq
|
||||||
(declare-fun |uut#2| (|uut_s|) (_ BitVec 8)) ; \sub
|
(declare-fun |uut#2| (|uut_s|) (_ BitVec 8)) ; \sub
|
||||||
(declare-fun |uut_h s| (|uut_s|) |smtlib2_s|)
|
(declare-fun |uut_h s| (|uut_s|) |smtlib2_s|)
|
||||||
; yosys-smt2-anyconst uut#3 8 smtlib2_module.v:14.17-14.26
|
; yosys-smt2-anyconst uut#3 8 smtlib2_module.v:14.17-14.26
|
||||||
|
; yosys-smt2-witness {"offset": 0, "path": ["\\a"], "smtname": 3, "type": "init", "width": 8}
|
||||||
(declare-fun |uut#3| (|uut_s|) (_ BitVec 8)) ; \a
|
(declare-fun |uut#3| (|uut_s|) (_ BitVec 8)) ; \a
|
||||||
; yosys-smt2-anyconst uut#4 8 smtlib2_module.v:14.32-14.41
|
; yosys-smt2-anyconst uut#4 8 smtlib2_module.v:14.32-14.41
|
||||||
|
; yosys-smt2-witness {"offset": 0, "path": ["\\b"], "smtname": 4, "type": "init", "width": 8}
|
||||||
(declare-fun |uut#4| (|uut_s|) (_ BitVec 8)) ; \b
|
(declare-fun |uut#4| (|uut_s|) (_ BitVec 8)) ; \b
|
||||||
(define-fun |uut#5| ((state |uut_s|)) (_ BitVec 8) (bvadd (|uut#3| state) (|uut#4| state))) ; \add2
|
(define-fun |uut#5| ((state |uut_s|)) (_ BitVec 8) (bvadd (|uut#3| state) (|uut#4| state))) ; \add2
|
||||||
(define-fun |uut#6| ((state |uut_s|)) Bool (= (|uut#0| state) (|uut#5| state))) ; $0$formal$smtlib2_module.v:28$1_CHECK[0:0]$9
|
(define-fun |uut#6| ((state |uut_s|)) Bool (= (|uut#0| state) (|uut#5| state))) ; $0$formal$smtlib2_module.v:28$1_CHECK[0:0]$9
|
||||||
|
|
Loading…
Reference in New Issue