2024-05-23 05:40:41 -05:00
|
|
|
/*
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
*
|
|
|
|
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2024-06-21 05:59:34 -05:00
|
|
|
#include "kernel/functionalir.h"
|
2024-06-26 13:56:29 -05:00
|
|
|
#include "kernel/yosys.h"
|
2024-07-11 05:30:23 -05:00
|
|
|
#include "kernel/sexpr.h"
|
2024-07-10 08:28:48 -05:00
|
|
|
#include <ctype.h>
|
2024-05-23 05:40:41 -05:00
|
|
|
|
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
PRIVATE_NAMESPACE_BEGIN
|
|
|
|
|
2024-07-11 05:30:23 -05:00
|
|
|
using SExprUtil::list;
|
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
const char *reserved_keywords[] = {
|
2024-07-12 04:45:31 -05:00
|
|
|
// reserved keywords from the smtlib spec
|
2024-07-10 08:28:48 -05:00
|
|
|
"BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par",
|
|
|
|
"assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes",
|
|
|
|
"declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort",
|
|
|
|
"exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model",
|
|
|
|
"get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value",
|
|
|
|
"pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option",
|
|
|
|
|
2024-07-12 04:45:31 -05:00
|
|
|
// reserved for our own purposes
|
2024-07-10 08:28:48 -05:00
|
|
|
"pair", "Pair", "first", "second",
|
|
|
|
"inputs", "state",
|
|
|
|
nullptr
|
2024-05-23 05:40:41 -05:00
|
|
|
};
|
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
struct SmtScope : public FunctionalTools::Scope<int> {
|
|
|
|
SmtScope() {
|
|
|
|
for(const char **p = reserved_keywords; *p != nullptr; p++)
|
|
|
|
reserve(*p);
|
|
|
|
}
|
|
|
|
bool is_character_legal(char c) override {
|
|
|
|
return isascii(c) && (isalnum(c) || strchr("~!@$%^&*_-+=<>.?/", c));
|
|
|
|
}
|
2024-05-23 05:40:41 -05:00
|
|
|
};
|
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
struct SmtSort {
|
|
|
|
FunctionalIR::Sort sort;
|
|
|
|
SmtSort(FunctionalIR::Sort sort) : sort(sort) {}
|
|
|
|
SExpr to_sexpr() const {
|
|
|
|
if(sort.is_memory()) {
|
2024-07-10 12:08:12 -05:00
|
|
|
return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width()));
|
2024-07-10 08:28:48 -05:00
|
|
|
} else if(sort.is_signal()) {
|
2024-07-10 12:08:12 -05:00
|
|
|
return list("_", "BitVec", sort.width());
|
2024-07-10 08:28:48 -05:00
|
|
|
} else {
|
|
|
|
log_error("unknown sort");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2024-07-07 14:01:38 -05:00
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
class SmtStruct {
|
|
|
|
struct Field {
|
|
|
|
SmtSort sort;
|
|
|
|
std::string accessor;
|
|
|
|
};
|
|
|
|
idict<IdString> field_names;
|
|
|
|
vector<Field> fields;
|
|
|
|
SmtScope &scope;
|
|
|
|
public:
|
|
|
|
std::string name;
|
|
|
|
SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {}
|
|
|
|
void insert(IdString field_name, SmtSort sort) {
|
|
|
|
field_names(field_name);
|
|
|
|
auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name));
|
|
|
|
fields.emplace_back(Field{sort, accessor});
|
|
|
|
}
|
|
|
|
void write_definition(SExprWriter &w) {
|
2024-07-10 12:08:12 -05:00
|
|
|
w.open(list("declare-datatype", name));
|
|
|
|
w.open(list());
|
|
|
|
w.open(list(name));
|
2024-07-10 08:28:48 -05:00
|
|
|
for(const auto &field : fields)
|
2024-07-10 12:08:12 -05:00
|
|
|
w << list(field.accessor, field.sort.to_sexpr());
|
2024-07-10 08:28:48 -05:00
|
|
|
w.close(3);
|
|
|
|
}
|
|
|
|
template<typename Fn> void write_value(SExprWriter &w, Fn fn) {
|
2024-07-12 04:45:31 -05:00
|
|
|
if(field_names.empty()) {
|
|
|
|
// Zero-argument constructors in SMTLIB must not be called as functions.
|
|
|
|
w << name;
|
|
|
|
} else {
|
|
|
|
w.open(list(name));
|
|
|
|
for(auto field_name : field_names) {
|
|
|
|
w << fn(field_name);
|
|
|
|
w.comment(RTLIL::unescape_id(field_name), true);
|
|
|
|
}
|
|
|
|
w.close();
|
2024-07-10 08:28:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
SExpr access(SExpr record, IdString name) {
|
|
|
|
size_t i = field_names.at(name);
|
2024-07-10 12:08:12 -05:00
|
|
|
return list(fields[i].accessor, std::move(record));
|
2024-07-10 08:28:48 -05:00
|
|
|
}
|
|
|
|
};
|
2024-07-07 14:01:38 -05:00
|
|
|
|
2024-07-24 11:37:17 -05:00
|
|
|
std::string smt_const(RTLIL::Const const &c) {
|
|
|
|
std::string s = "#b";
|
|
|
|
for(int i = c.size(); i-- > 0; )
|
|
|
|
s += c[i] == State::S1 ? '1' : '0';
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2024-07-17 05:53:42 -05:00
|
|
|
struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> {
|
2024-07-10 08:28:48 -05:00
|
|
|
using Node = FunctionalIR::Node;
|
|
|
|
std::function<SExpr(Node)> n;
|
|
|
|
SmtStruct &input_struct;
|
|
|
|
SmtStruct &state_struct;
|
|
|
|
|
|
|
|
SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {}
|
|
|
|
|
|
|
|
SExpr from_bool(SExpr &&arg) {
|
2024-07-10 12:08:12 -05:00
|
|
|
return list("ite", std::move(arg), "#b1", "#b0");
|
2024-07-10 08:28:48 -05:00
|
|
|
}
|
|
|
|
SExpr to_bool(SExpr &&arg) {
|
2024-07-10 12:08:12 -05:00
|
|
|
return list("=", std::move(arg), "#b1");
|
2024-07-10 08:28:48 -05:00
|
|
|
}
|
|
|
|
SExpr extract(SExpr &&arg, int offset, int out_width = 1) {
|
2024-07-10 12:08:12 -05:00
|
|
|
return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg));
|
2024-07-10 08:28:48 -05:00
|
|
|
}
|
|
|
|
|
2024-07-17 05:53:42 -05:00
|
|
|
SExpr buf(Node, Node a) override { return n(a); }
|
|
|
|
SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); }
|
|
|
|
SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); }
|
|
|
|
SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); }
|
|
|
|
SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); }
|
|
|
|
SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); }
|
|
|
|
SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); }
|
|
|
|
SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); }
|
|
|
|
SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); }
|
|
|
|
SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); }
|
|
|
|
SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); }
|
|
|
|
SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); }
|
|
|
|
SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); }
|
|
|
|
SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); }
|
|
|
|
SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); }
|
2024-07-24 11:37:17 -05:00
|
|
|
SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); }
|
|
|
|
SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); }
|
2024-07-17 05:53:42 -05:00
|
|
|
SExpr reduce_xor(Node, Node a) override {
|
2024-07-10 08:28:48 -05:00
|
|
|
vector<SExpr> s { "bvxor" };
|
|
|
|
for(int i = 0; i < a.width(); i++)
|
|
|
|
s.push_back(extract(n(a), i));
|
|
|
|
return s;
|
|
|
|
}
|
2024-07-17 05:53:42 -05:00
|
|
|
SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); }
|
|
|
|
SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); }
|
|
|
|
SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); }
|
|
|
|
SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); }
|
|
|
|
SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); }
|
|
|
|
SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); }
|
2024-07-10 08:28:48 -05:00
|
|
|
|
|
|
|
SExpr extend(SExpr &&a, int in_width, int out_width) {
|
|
|
|
if(in_width < out_width)
|
2024-07-10 12:08:12 -05:00
|
|
|
return list(list("_", "zero_extend", out_width - in_width), std::move(a));
|
2024-07-10 08:28:48 -05:00
|
|
|
else
|
|
|
|
return std::move(a);
|
|
|
|
}
|
2024-07-17 05:53:42 -05:00
|
|
|
SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); }
|
|
|
|
SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); }
|
|
|
|
SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); }
|
|
|
|
SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); }
|
2024-07-24 11:37:17 -05:00
|
|
|
SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); }
|
2024-07-17 05:53:42 -05:00
|
|
|
SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); }
|
|
|
|
SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); }
|
2024-07-10 08:28:48 -05:00
|
|
|
|
2024-07-17 05:53:42 -05:00
|
|
|
SExpr input(Node, IdString name) override { return input_struct.access("inputs", name); }
|
|
|
|
SExpr state(Node, IdString name) override { return state_struct.access("state", name); }
|
2024-07-10 08:28:48 -05:00
|
|
|
};
|
2024-07-07 14:01:38 -05:00
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
struct SmtModule {
|
|
|
|
FunctionalIR ir;
|
|
|
|
SmtScope scope;
|
|
|
|
std::string name;
|
|
|
|
|
|
|
|
SmtStruct input_struct;
|
|
|
|
SmtStruct output_struct;
|
|
|
|
SmtStruct state_struct;
|
|
|
|
|
|
|
|
SmtModule(Module *module)
|
|
|
|
: ir(FunctionalIR::from_module(module))
|
|
|
|
, scope()
|
|
|
|
, name(scope.unique_name(module->name))
|
|
|
|
, input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope)
|
|
|
|
, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope)
|
|
|
|
, state_struct(scope.unique_name(module->name.str() + "_State"), scope)
|
|
|
|
{
|
2024-07-24 11:37:17 -05:00
|
|
|
scope.reserve(name + "-initial");
|
2024-07-10 08:28:48 -05:00
|
|
|
for (const auto &input : ir.inputs())
|
|
|
|
input_struct.insert(input.first, input.second);
|
|
|
|
for (const auto &output : ir.outputs())
|
|
|
|
output_struct.insert(output.first, output.second);
|
|
|
|
for (const auto &state : ir.state())
|
|
|
|
state_struct.insert(state.first, state.second);
|
|
|
|
}
|
|
|
|
|
2024-07-24 11:37:17 -05:00
|
|
|
void write_eval(SExprWriter &w)
|
|
|
|
{
|
2024-07-10 08:28:48 -05:00
|
|
|
w.push();
|
2024-07-10 12:08:12 -05:00
|
|
|
w.open(list("define-fun", name,
|
|
|
|
list(list("inputs", input_struct.name),
|
|
|
|
list("state", state_struct.name)),
|
|
|
|
list("Pair", output_struct.name, state_struct.name)));
|
2024-07-10 08:28:48 -05:00
|
|
|
auto inlined = [&](FunctionalIR::Node n) {
|
2024-07-18 08:23:00 -05:00
|
|
|
return n.fn() == FunctionalIR::Fn::constant;
|
2024-07-10 08:28:48 -05:00
|
|
|
};
|
|
|
|
SmtPrintVisitor visitor(input_struct, state_struct);
|
|
|
|
auto node_to_sexpr = [&](FunctionalIR::Node n) -> SExpr {
|
|
|
|
if(inlined(n))
|
|
|
|
return n.visit(visitor);
|
|
|
|
else
|
|
|
|
return scope(n.id(), n.name());
|
|
|
|
};
|
|
|
|
visitor.n = node_to_sexpr;
|
|
|
|
for(auto n : ir)
|
|
|
|
if(!inlined(n)) {
|
2024-07-10 12:08:12 -05:00
|
|
|
w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false);
|
2024-07-10 08:28:48 -05:00
|
|
|
w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true);
|
|
|
|
}
|
2024-07-10 12:08:12 -05:00
|
|
|
w.open(list("pair"));
|
2024-07-10 08:28:48 -05:00
|
|
|
output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.get_output_node(name)); });
|
|
|
|
state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.get_state_next_node(name)); });
|
|
|
|
w.pop();
|
|
|
|
}
|
2024-07-24 11:37:17 -05:00
|
|
|
|
|
|
|
void write_initial(SExprWriter &w)
|
|
|
|
{
|
|
|
|
std::string initial = name + "-initial";
|
|
|
|
w << list("declare-const", initial, state_struct.name);
|
|
|
|
for (const auto &[name, sort] : ir.state()) {
|
|
|
|
if(sort.is_signal())
|
|
|
|
w << list("assert", list("=", state_struct.access(initial, name), smt_const(ir.get_initial_state_signal(name))));
|
|
|
|
else if(sort.is_memory()) {
|
|
|
|
auto contents = ir.get_initial_state_memory(name);
|
|
|
|
for(int i = 0; i < 1<<sort.addr_width(); i++) {
|
|
|
|
auto addr = smt_const(RTLIL::Const(i, sort.addr_width()));
|
|
|
|
w << list("assert", list("=", list("select", state_struct.access(initial, name), addr), smt_const(contents[i])));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(std::ostream &out)
|
|
|
|
{
|
|
|
|
SExprWriter w(out);
|
|
|
|
|
|
|
|
input_struct.write_definition(w);
|
|
|
|
output_struct.write_definition(w);
|
|
|
|
state_struct.write_definition(w);
|
|
|
|
|
|
|
|
w << list("declare-datatypes",
|
|
|
|
list(list("Pair", 2)),
|
|
|
|
list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y"))))));
|
|
|
|
|
|
|
|
write_eval(w);
|
|
|
|
write_initial(w);
|
|
|
|
}
|
2024-05-23 05:40:41 -05:00
|
|
|
};
|
|
|
|
|
2024-06-21 05:59:34 -05:00
|
|
|
struct FunctionalSmtBackend : public Backend {
|
2024-07-10 08:28:48 -05:00
|
|
|
FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {}
|
2024-05-23 05:40:41 -05:00
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
void help() override { log("\nFunctional SMT Backend.\n\n"); }
|
2024-05-23 05:40:41 -05:00
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
|
|
|
{
|
|
|
|
log_header(design, "Executing Functional SMT Backend.\n");
|
2024-06-21 05:59:34 -05:00
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
size_t argidx = 1;
|
|
|
|
extra_args(f, filename, args, argidx, design);
|
2024-05-23 05:40:41 -05:00
|
|
|
|
2024-07-10 08:28:48 -05:00
|
|
|
for (auto module : design->selected_modules()) {
|
|
|
|
log("Processing module `%s`.\n", module->name.c_str());
|
|
|
|
SmtModule smt(module);
|
|
|
|
smt.write(*f);
|
|
|
|
}
|
|
|
|
}
|
2024-06-21 05:59:34 -05:00
|
|
|
} FunctionalSmtBackend;
|
2024-05-23 05:40:41 -05:00
|
|
|
|
|
|
|
PRIVATE_NAMESPACE_END
|