From 370db33a4c347049cb8f66c181d39fd2f2ce5e3a Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 15 Mar 2019 20:18:38 +0100 Subject: Add fmcombine pass Signed-off-by: Clifford Wolf --- kernel/celltypes.h | 45 +++++--- kernel/rtlil.cc | 4 +- passes/sat/Makefile.inc | 1 + passes/sat/fmcombine.cc | 292 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+), 17 deletions(-) create mode 100644 passes/sat/fmcombine.cc diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 8b8a56111..ae88f4aaf 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -81,6 +81,27 @@ struct CellTypes } void setup_internals() + { + setup_internals_eval(); + + IdString A = "\\A", B = "\\B", EN = "\\EN", Y = "\\Y"; + + setup_type("$tribuf", {A, EN}, {Y}, true); + + setup_type("$assert", {A, EN}, pool(), true); + setup_type("$assume", {A, EN}, pool(), true); + setup_type("$live", {A, EN}, pool(), true); + setup_type("$fair", {A, EN}, pool(), true); + setup_type("$cover", {A, EN}, pool(), true); + setup_type("$initstate", pool(), {Y}, true); + setup_type("$anyconst", pool(), {Y}, true); + setup_type("$anyseq", pool(), {Y}, true); + setup_type("$allconst", pool(), {Y}, true); + setup_type("$allseq", pool(), {Y}, true); + setup_type("$equiv", {A, B}, {Y}, true); + } + + void setup_internals_eval() { std::vector unary_ops = { "$not", "$pos", "$neg", @@ -111,20 +132,6 @@ struct CellTypes setup_type("$lcu", {P, G, CI}, {CO}, true); setup_type("$alu", {A, B, CI, BI}, {X, Y, CO}, true); setup_type("$fa", {A, B, C}, {X, Y}, true); - - setup_type("$tribuf", {A, EN}, {Y}, true); - - setup_type("$assert", {A, EN}, pool(), true); - setup_type("$assume", {A, EN}, pool(), true); - setup_type("$live", {A, EN}, pool(), true); - setup_type("$fair", {A, EN}, pool(), true); - setup_type("$cover", {A, EN}, pool(), true); - setup_type("$initstate", pool(), {Y}, true); - setup_type("$anyconst", pool(), {Y}, true); - setup_type("$anyseq", pool(), {Y}, true); - setup_type("$allconst", pool(), {Y}, true); - setup_type("$allseq", pool(), {Y}, true); - setup_type("$equiv", {A, B}, {Y}, true); } void setup_internals_mem() @@ -153,6 +160,15 @@ struct CellTypes } void setup_stdcells() + { + setup_stdcells_eval(); + + IdString A = "\\A", E = "\\E", Y = "\\Y"; + + setup_type("$_TBUF_", {A, E}, {Y}, true); + } + + void setup_stdcells_eval() { IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D"; IdString E = "\\E", F = "\\F", G = "\\G", H = "\\H"; @@ -179,7 +195,6 @@ struct CellTypes setup_type("$_OAI3_", {A, B, C}, {Y}, true); setup_type("$_AOI4_", {A, B, C, D}, {Y}, true); setup_type("$_OAI4_", {A, B, C, D}, {Y}, true); - setup_type("$_TBUF_", {A, E}, {Y}, true); } void setup_stdcells_mem() diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 7f1816190..d0fa88890 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -760,7 +760,7 @@ namespace { void check() { - if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" || + if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" || cell->type.substr(0,10) == "$fmcombine" || cell->type.substr(0, 9) == "$verific$" || cell->type.substr(0, 7) == "$array:" || cell->type.substr(0, 8) == "$extern:") return; @@ -2360,7 +2360,7 @@ void RTLIL::Cell::check() void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) { - if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" || + if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" || type.substr(0,10) == "$fmcombine" || type.substr(0, 9) == "$verific$" || type.substr(0, 7) == "$array:" || type.substr(0, 8) == "$extern:") return; diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index e91df1eb0..4eced2ff1 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -9,5 +9,6 @@ OBJS += passes/sat/assertpmux.o OBJS += passes/sat/clk2fflogic.o OBJS += passes/sat/async2sync.o OBJS += passes/sat/supercover.o +OBJS += passes/sat/fmcombine.o OBJS += passes/sat/mutate.o diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc new file mode 100644 index 000000000..b8a0e94c8 --- /dev/null +++ b/passes/sat/fmcombine.cc @@ -0,0 +1,292 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct FmcombineWorker +{ + Design *design; + Module *original = nullptr; + Module *module = nullptr; + IdString orig_type, combined_type; + + FmcombineWorker(Design *design, IdString orig_type) : + design(design), original(design->module(orig_type)), + orig_type(orig_type), combined_type("$fmcombine" + orig_type.str()) + { + } + + SigSpec import_sig(SigSpec sig, const string &suffix) + { + SigSpec newsig; + for (auto chunk : sig.chunks()) { + if (chunk.wire != nullptr) + chunk.wire = module->wire(chunk.wire->name.str() + suffix); + newsig.append(chunk); + } + return newsig; + } + + void import_prim_cell(Cell *cell, const string &suffix) + { + Cell *c = module->addCell(cell->name.str() + suffix, cell->type); + c->parameters = cell->parameters; + c->attributes = cell->attributes; + + for (auto &conn : cell->connections()) + c->setPort(conn.first, import_sig(conn.second, suffix)); + } + + void import_hier_cell(Cell *cell) + { + if (!cell->parameters.empty()) + log_cmd_error("Cell %s.%s has unresolved instance parameters.\n", log_id(original), log_id(cell)); + + FmcombineWorker sub_worker(design, cell->type); + sub_worker.generate(); + + Cell *c = module->addCell(cell->name.str() + "_combined", sub_worker.combined_type); + // c->parameters = cell->parameters; + c->attributes = cell->attributes; + + for (auto &conn : cell->connections()) { + c->setPort(conn.first.str() + "_gold", import_sig(conn.second, "_gold")); + c->setPort(conn.first.str() + "_gate", import_sig(conn.second, "_gate")); + } + } + + void generate() + { + if (design->module(combined_type)) { + // log("Combined module %s already exists.\n", log_id(combined_type)); + return; + } + + log("Generating combined module %s from module %s.\n", log_id(combined_type), log_id(orig_type)); + module = design->addModule(combined_type); + + for (auto wire : original->wires()) { + module->addWire(wire->name.str() + "_gold", wire); + module->addWire(wire->name.str() + "_gate", wire); + } + module->fixup_ports(); + + for (auto cell : original->cells()) { + if (design->module(cell->type) == nullptr) { + import_prim_cell(cell, "_gold"); + import_prim_cell(cell, "_gate"); + } else { + import_hier_cell(cell); + } + } + + for (auto &conn : original->connections()) { + module->connect(import_sig(conn.first, "_gold"), import_sig(conn.second, "_gold")); + module->connect(import_sig(conn.first, "_gate"), import_sig(conn.second, "_gate")); + } + + CellTypes ct; + ct.setup_internals_eval(); + ct.setup_stdcells_eval(); + + SigMap sigmap(module); + + dict data_bit_to_eq_net; + dict cell_to_eq_nets; + dict reduce_db; + dict invert_db; + + for (auto cell : original->cells()) + { + if (!ct.cell_known(cell->type)) + continue; + + for (auto &conn : cell->connections()) + { + if (!cell->output(conn.first)) + continue; + + SigSpec A = import_sig(conn.second, "_gold"); + SigSpec B = import_sig(conn.second, "_gate"); + SigBit EQ = module->Eq(NEW_ID, A, B); + + for (auto bit : sigmap({A, B})) + data_bit_to_eq_net[bit] = EQ; + + cell_to_eq_nets[cell].append(EQ); + } + } + + for (auto cell : original->cells()) + { + if (!ct.cell_known(cell->type)) + continue; + + bool skip_cell = !cell_to_eq_nets.count(cell); + pool src_eq_bits; + + for (auto &conn : cell->connections()) + { + if (skip_cell) + break; + + if (cell->output(conn.first)) + continue; + + SigSpec A = import_sig(conn.second, "_gold"); + SigSpec B = import_sig(conn.second, "_gate"); + + for (auto bit : sigmap({A, B})) { + if (data_bit_to_eq_net.count(bit)) + src_eq_bits.insert(data_bit_to_eq_net.at(bit)); + else + skip_cell = true; + } + } + + if (!skip_cell) { + SigSpec antecedent = SigSpec(src_eq_bits); + antecedent.sort_and_unify(); + + if (GetSize(antecedent) > 1) { + if (reduce_db.count(antecedent) == 0) + reduce_db[antecedent] = module->ReduceAnd(NEW_ID, antecedent); + antecedent = reduce_db.at(antecedent); + } + + SigSpec consequent = cell_to_eq_nets.at(cell); + consequent.sort_and_unify(); + + if (GetSize(consequent) > 1) { + if (reduce_db.count(consequent) == 0) + reduce_db[consequent] = module->ReduceAnd(NEW_ID, consequent); + consequent = reduce_db.at(consequent); + } + + module->addAssume(NEW_ID, consequent, antecedent); + + if (invert_db.count(antecedent) == 0) + invert_db[antecedent] = module->Not(NEW_ID, antecedent); + + if (invert_db.count(consequent) == 0) + invert_db[consequent] = module->Not(NEW_ID, consequent); + + module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent)); + } + } + } +}; + +struct FmcombinePass : public Pass { + FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" fmcombine [options] module_name gold_cell gate_cell\n"); + // log(" fmcombine [options] @gold_cell @gate_cell\n"); + log("\n"); + log("This pass takes two cells, which are instances of the same module, and replaces\n"); + log("them with one instance of a special 'combined' module, that effectively\n"); + log("contains two copies of the original module, plus some formal properties.\n"); + log("\n"); + log("This is useful for formal test benches that check what differences in behavior\n"); + log("a slight difference in input causes in a module.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + Module *module = nullptr; + Cell *gold_cell = nullptr; + Cell *gate_cell = nullptr; + + log_header(design, "Executing FMCOMBINE pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-o" && argidx+1 < args.size()) { + // filename = args[++argidx]; + // continue; + // } + break; + } + if (argidx+2 == args.size()) + { + string gold_name = args[argidx++]; + string gate_name = args[argidx++]; + log_cmd_error("fmcombine @gold_cell @gate_cell call style is not implemented yet."); + } + else if (argidx+3 == args.size()) + { + IdString module_name = RTLIL::escape_id(args[argidx++]); + IdString gold_name = RTLIL::escape_id(args[argidx++]); + IdString gate_name = RTLIL::escape_id(args[argidx++]); + + module = design->module(module_name); + if (module == nullptr) + log_cmd_error("Module %s not found.\n", log_id(module_name)); + + gold_cell = module->cell(gold_name); + if (gold_cell == nullptr) + log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gold_name), log_id(module)); + + gate_cell = module->cell(gate_name); + if (gate_cell == nullptr) + log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module)); + } + else + { + log_cmd_error("Invalid number of arguments.\n"); + } + // extra_args(args, argidx, design); + + if (gold_cell->type != gate_cell->type) + log_cmd_error("Types of gold and gate cells do not match.\n"); + if (!gold_cell->parameters.empty()) + log_cmd_error("Gold cell has unresolved instance parameters.\n"); + if (!gate_cell->parameters.empty()) + log_cmd_error("Gold cell has unresolved instance parameters.\n"); + + FmcombineWorker worker(design, gold_cell->type); + worker.generate(); + IdString combined_cell_name = module->uniquify(stringf("\\%s_%s", log_id(gold_cell), log_id(gate_cell))); + + Cell *cell = module->addCell(combined_cell_name, worker.combined_type); + cell->attributes = gold_cell->attributes; + cell->add_strpool_attribute("\\src", gate_cell->get_strpool_attribute("\\src")); + + log("Combining cells %s and %s in module %s into new cell %s.\n", log_id(gold_cell), log_id(gate_cell), log_id(module), log_id(cell)); + + for (auto &conn : gold_cell->connections()) + cell->setPort(conn.first.str() + "_gold", conn.second); + module->remove(gold_cell); + + for (auto &conn : gate_cell->connections()) + cell->setPort(conn.first.str() + "_gate", conn.second); + module->remove(gate_cell); + } +} FmcombinePass; + +PRIVATE_NAMESPACE_END -- cgit v1.2.3 From dacaebae35c81b4f4970af3ef8bfdb73691fa215 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 15 Mar 2019 21:45:37 +0100 Subject: Add "fmcombine -fwd -bwd -nop" Signed-off-by: Clifford Wolf --- passes/sat/fmcombine.cc | 69 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc index b8a0e94c8..cd75ca860 100644 --- a/passes/sat/fmcombine.cc +++ b/passes/sat/fmcombine.cc @@ -24,15 +24,23 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct opts_t +{ + bool fwd = false; + bool bwd = false; + bool nop = false; +}; + struct FmcombineWorker { + const opts_t &opts; Design *design; Module *original = nullptr; Module *module = nullptr; IdString orig_type, combined_type; - FmcombineWorker(Design *design, IdString orig_type) : - design(design), original(design->module(orig_type)), + FmcombineWorker(Design *design, IdString orig_type, const opts_t &opts) : + opts(opts), design(design), original(design->module(orig_type)), orig_type(orig_type), combined_type("$fmcombine" + orig_type.str()) { } @@ -63,7 +71,7 @@ struct FmcombineWorker if (!cell->parameters.empty()) log_cmd_error("Cell %s.%s has unresolved instance parameters.\n", log_id(original), log_id(cell)); - FmcombineWorker sub_worker(design, cell->type); + FmcombineWorker sub_worker(design, cell->type, opts); sub_worker.generate(); Cell *c = module->addCell(cell->name.str() + "_combined", sub_worker.combined_type); @@ -106,6 +114,9 @@ struct FmcombineWorker module->connect(import_sig(conn.first, "_gate"), import_sig(conn.second, "_gate")); } + if (opts.nop) + return; + CellTypes ct; ct.setup_internals_eval(); ct.setup_stdcells_eval(); @@ -184,15 +195,19 @@ struct FmcombineWorker consequent = reduce_db.at(consequent); } - module->addAssume(NEW_ID, consequent, antecedent); + if (opts.fwd) + module->addAssume(NEW_ID, consequent, antecedent); - if (invert_db.count(antecedent) == 0) - invert_db[antecedent] = module->Not(NEW_ID, antecedent); + if (opts.bwd) + { + if (invert_db.count(antecedent) == 0) + invert_db[antecedent] = module->Not(NEW_ID, antecedent); - if (invert_db.count(consequent) == 0) - invert_db[consequent] = module->Not(NEW_ID, consequent); + if (invert_db.count(consequent) == 0) + invert_db[consequent] = module->Not(NEW_ID, consequent); - module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent)); + module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent)); + } } } } @@ -214,9 +229,25 @@ struct FmcombinePass : public Pass { log("This is useful for formal test benches that check what differences in behavior\n"); log("a slight difference in input causes in a module.\n"); log("\n"); + log(" -fwd\n"); + log(" Insert forward hint assumptions into the combined module.\n"); + log("\n"); + log(" -bwd\n"); + log(" Insert backward hint assumptions into the combined module.\n"); + log(" (Backward hints are logically equivalend to fordward hits, but\n"); + log(" some solvers are faster with bwd hints, or even both -bwd and -fwd.)\n"); + log("\n"); + log(" -nop\n"); + log(" Don't insert hint assumptions into the combined module.\n"); + log(" (This should not provide any speedup over the original design, but\n"); + log(" strangely sometimes it does.)\n"); + log("\n"); + log("If none of -fwd, -bwd, and -nop is given, then -fwd is used as default.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { + opts_t opts; Module *module = nullptr; Cell *gold_cell = nullptr; Cell *gate_cell = nullptr; @@ -230,6 +261,18 @@ struct FmcombinePass : public Pass { // filename = args[++argidx]; // continue; // } + if (args[argidx] == "-fwd") { + opts.fwd = true; + continue; + } + if (args[argidx] == "-bwd") { + opts.bwd = true; + continue; + } + if (args[argidx] == "-nop") { + opts.nop = true; + continue; + } break; } if (argidx+2 == args.size()) @@ -262,6 +305,12 @@ struct FmcombinePass : public Pass { } // extra_args(args, argidx, design); + if (opts.nop && (opts.fwd || opts.bwd)) + log_cmd_error("Option -nop can not be combined with -fwd and/or -bwd.\n"); + + if (!opts.nop && !opts.fwd && !opts.bwd) + opts.fwd = true; + if (gold_cell->type != gate_cell->type) log_cmd_error("Types of gold and gate cells do not match.\n"); if (!gold_cell->parameters.empty()) @@ -269,7 +318,7 @@ struct FmcombinePass : public Pass { if (!gate_cell->parameters.empty()) log_cmd_error("Gold cell has unresolved instance parameters.\n"); - FmcombineWorker worker(design, gold_cell->type); + FmcombineWorker worker(design, gold_cell->type, opts); worker.generate(); IdString combined_cell_name = module->uniquify(stringf("\\%s_%s", log_id(gold_cell), log_id(gate_cell))); -- cgit v1.2.3