| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| */ |
| |
| #include "kernel/yosys.h" |
| #include "kernel/sigtools.h" |
| #include "kernel/celltypes.h" |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| struct EquivMiterWorker |
| { |
| CellTypes ct; |
| SigMap sigmap; |
| |
| bool mode_trigger; |
| bool mode_cmp; |
| bool mode_assert; |
| bool mode_undef; |
| |
| IdString miter_name; |
| Module *miter_module; |
| Module *source_module; |
| |
| dict<SigBit, Cell*> bit_to_driver; |
| pool<Cell*> seed_cells, miter_cells; |
| pool<Wire*> miter_wires; |
| |
| void follow_cone(pool<Cell*> &cone, pool<Cell*> &leaves, Cell *c, bool gold_mode) |
| { |
| if (cone.count(c)) |
| return; |
| |
| if (c->type == "$equiv" && !seed_cells.count(c)) { |
| leaves.insert(c); |
| return; |
| } |
| |
| cone.insert(c); |
| |
| for (auto &conn : c->connections()) { |
| if (!ct.cell_input(c->type, conn.first)) |
| continue; |
| if (c->type == "$equiv" && (conn.first == "\\A") != gold_mode) |
| continue; |
| for (auto bit : sigmap(conn.second)) |
| if (bit_to_driver.count(bit)) |
| follow_cone(cone, leaves, bit_to_driver.at(bit), gold_mode); |
| } |
| } |
| |
| void find_miter_cells_wires() |
| { |
| sigmap.set(source_module); |
| |
| // initialize bit_to_driver |
| |
| for (auto c : source_module->cells()) |
| for (auto &conn : c->connections()) |
| if (ct.cell_output(c->type, conn.first)) |
| for (auto bit : sigmap(conn.second)) |
| if (bit.wire) |
| bit_to_driver[bit] = c; |
| |
| // find seed cells |
| |
| for (auto c : source_module->selected_cells()) |
| if (c->type == "$equiv") { |
| log("Seed $equiv cell: %s\n", log_id(c)); |
| seed_cells.insert(c); |
| } |
| |
| // follow cone from seed cells to next $equiv |
| |
| while (1) |
| { |
| pool<Cell*> gold_cone, gold_leaves; |
| pool<Cell*> gate_cone, gate_leaves; |
| |
| for (auto c : seed_cells) { |
| follow_cone(gold_cone, gold_leaves, c, true); |
| follow_cone(gate_cone, gate_leaves, c, false); |
| } |
| |
| log("Gold cone: %d cells (%d leaves).\n", GetSize(gold_cone), GetSize(gold_leaves)); |
| log("Gate cone: %d cells (%d leaves).\n", GetSize(gate_cone), GetSize(gate_leaves)); |
| |
| // done if all leaves are shared leaves |
| |
| if (gold_leaves == gate_leaves) { |
| miter_cells = gold_cone; |
| miter_cells.insert(gate_cone.begin(), gate_cone.end()); |
| log("Selected %d miter cells.\n", GetSize(miter_cells)); |
| break; |
| } |
| |
| // remove shared leaves |
| |
| for (auto it = gold_leaves.begin(); it != gold_leaves.end(); ) { |
| auto it2 = gate_leaves.find(*it); |
| if (it2 != gate_leaves.end()) { |
| it = gold_leaves.erase(it); |
| gate_leaves.erase(it2); |
| } else |
| ++it; |
| } |
| |
| // add remaining leaves to seeds and re-run |
| |
| log("Adding %d gold and %d gate seed cells.\n", GetSize(gold_leaves), GetSize(gate_leaves)); |
| seed_cells.insert(gold_leaves.begin(), gold_leaves.end()); |
| seed_cells.insert(gate_leaves.begin(), gate_leaves.end()); |
| } |
| |
| for (auto c : miter_cells) |
| for (auto &conn : c->connections()) |
| for (auto bit : sigmap(conn.second)) |
| if (bit.wire) |
| miter_wires.insert(bit.wire); |
| log("Selected %d miter wires.\n", GetSize(miter_wires)); |
| } |
| |
| void copy_to_miter() |
| { |
| // copy wires and cells |
| |
| for (auto w : miter_wires) |
| miter_module->addWire(w->name, w->width); |
| for (auto c : miter_cells) { |
| miter_module->addCell(c->name, c); |
| auto mc = miter_module->cell(c->name); |
| for (auto &conn : mc->connections()) |
| mc->setPort(conn.first, sigmap(conn.second)); |
| } |
| |
| // fixup wire references in cells |
| |
| sigmap.clear(); |
| |
| struct RewriteSigSpecWorker { |
| RTLIL::Module * mod; |
| void operator()(SigSpec &sig) { |
| vector<SigChunk> chunks = sig.chunks(); |
| for (auto &c : chunks) |
| if (c.wire != NULL) |
| c.wire = mod->wires_.at(c.wire->name); |
| sig = chunks; |
| } |
| }; |
| |
| RewriteSigSpecWorker rewriteSigSpecWorker; |
| rewriteSigSpecWorker.mod = miter_module; |
| miter_module->rewrite_sigspecs(rewriteSigSpecWorker); |
| |
| // find undriven or unused wires |
| |
| pool<SigBit> driven_bits, used_bits; |
| |
| for (auto c : miter_module->cells()) |
| for (auto &conn : c->connections()) { |
| if (ct.cell_input(c->type, conn.first)) |
| for (auto bit : conn.second) |
| if (bit.wire) |
| used_bits.insert(bit); |
| if (ct.cell_output(c->type, conn.first)) |
| for (auto bit : conn.second) |
| if (bit.wire) |
| driven_bits.insert(bit); |
| } |
| |
| // create ports |
| |
| for (auto w : miter_module->wires()) { |
| for (auto bit : SigSpec(w)) { |
| if (driven_bits.count(bit) && !used_bits.count(bit)) |
| w->port_output = true; |
| if (!driven_bits.count(bit) && used_bits.count(bit)) |
| w->port_input = true; |
| } |
| if (w->port_output && w->port_input) |
| log("Created miter inout port %s.\n", log_id(w)); |
| else if (w->port_output) |
| log("Created miter output port %s.\n", log_id(w)); |
| else if (w->port_input) |
| log("Created miter input port %s.\n", log_id(w)); |
| } |
| |
| miter_module->fixup_ports(); |
| } |
| |
| void make_stuff() |
| { |
| if (!mode_trigger && !mode_cmp && !mode_assert) |
| return; |
| |
| SigSpec trigger_signals; |
| vector<Cell*> equiv_cells; |
| |
| for (auto c : miter_module->cells()) |
| if (c->type == "$equiv" && c->getPort("\\A") != c->getPort("\\B")) |
| equiv_cells.push_back(c); |
| |
| for (auto c : equiv_cells) |
| { |
| SigSpec cmp = mode_undef ? |
| miter_module->LogicOr(NEW_ID, miter_module->Eqx(NEW_ID, c->getPort("\\A"), State::Sx), |
| miter_module->Eqx(NEW_ID, c->getPort("\\A"), c->getPort("\\B"))) : |
| miter_module->Eq(NEW_ID, c->getPort("\\A"), c->getPort("\\B")); |
| |
| if (mode_cmp) { |
| string cmp_name = string("\\cmp") + log_signal(c->getPort("\\Y")); |
| for (int i = 1; i < GetSize(cmp_name); i++) |
| if (cmp_name[i] == '\\') |
| cmp_name[i] = '_'; |
| else if (cmp_name[i] == ' ') |
| cmp_name = cmp_name.substr(0, i) + cmp_name.substr(i+1); |
| auto w = miter_module->addWire(cmp_name); |
| w->port_output = true; |
| miter_module->connect(w, cmp); |
| } |
| |
| if (mode_assert) |
| miter_module->addAssert(NEW_ID, cmp, State::S1); |
| |
| trigger_signals.append(miter_module->Not(NEW_ID, cmp)); |
| } |
| |
| if (mode_trigger) { |
| auto w = miter_module->addWire("\\trigger"); |
| w->port_output = true; |
| miter_module->addReduceOr(NEW_ID, trigger_signals, w); |
| } |
| |
| miter_module->fixup_ports(); |
| } |
| |
| void run() |
| { |
| log("Creating miter %s from module %s.\n", log_id(miter_module), log_id(source_module)); |
| find_miter_cells_wires(); |
| copy_to_miter(); |
| make_stuff(); |
| } |
| }; |
| |
| struct EquivMiterPass : public Pass { |
| EquivMiterPass() : Pass("equiv_miter", "extract miter from equiv circuit") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" equiv_miter [options] miter_module [selection]\n"); |
| log("\n"); |
| log("This creates a miter module for further analysis of the selected $equiv cells.\n"); |
| log("\n"); |
| log(" -trigger\n"); |
| log(" Create a trigger output\n"); |
| log("\n"); |
| log(" -cmp\n"); |
| log(" Create cmp_* outputs for individual unproven $equiv cells\n"); |
| log("\n"); |
| log(" -assert\n"); |
| log(" Create a $assert cell for each unproven $equiv cell\n"); |
| log("\n"); |
| log(" -undef\n"); |
| log(" Create compare logic that handles undefs correctly\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| EquivMiterWorker worker; |
| worker.ct.setup(design); |
| worker.mode_trigger = false; |
| worker.mode_cmp = false; |
| worker.mode_assert = false; |
| worker.mode_undef = false; |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) |
| { |
| if (args[argidx] == "-trigger") { |
| worker.mode_trigger = true; |
| continue; |
| } |
| if (args[argidx] == "-cmp") { |
| worker.mode_cmp = true; |
| continue; |
| } |
| if (args[argidx] == "-assert") { |
| worker.mode_assert = true; |
| continue; |
| } |
| if (args[argidx] == "-undef") { |
| worker.mode_undef = true; |
| continue; |
| } |
| break; |
| } |
| |
| if (argidx >= args.size()) |
| log_cmd_error("Invalid number of arguments.\n"); |
| |
| worker.miter_name = RTLIL::escape_id(args[argidx++]); |
| extra_args(args, argidx, design); |
| |
| if (design->module(worker.miter_name)) |
| log_cmd_error("Miter module %s already exists.\n", log_id(worker.miter_name)); |
| |
| worker.source_module = nullptr; |
| for (auto m : design->selected_modules()) { |
| if (worker.source_module != nullptr) |
| goto found_two_modules; |
| worker.source_module = m; |
| } |
| |
| if (worker.source_module == nullptr) |
| found_two_modules: |
| log_cmd_error("Exactly one module must be selected for 'equiv_miter'!\n"); |
| |
| log_header(design, "Executing EQUIV_MITER pass.\n"); |
| |
| worker.miter_module = design->addModule(worker.miter_name); |
| worker.run(); |
| } |
| } EquivMiterPass; |
| |
| PRIVATE_NAMESPACE_END |