| /* | |
| * 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" | |
| USING_YOSYS_NAMESPACE | |
| PRIVATE_NAMESPACE_BEGIN | |
| struct EquivMarkWorker | |
| { | |
| Module *module; | |
| SigMap sigmap; | |
| // cache for traversing signal flow graph | |
| dict<SigBit, pool<IdString>> up_bit2cells; | |
| dict<IdString, pool<SigBit>> up_cell2bits; | |
| pool<IdString> edge_cells, equiv_cells; | |
| // graph traversal state | |
| pool<SigBit> queue, visited; | |
| // assigned regions | |
| dict<IdString, int> cell_regions; | |
| dict<SigBit, int> bit_regions; | |
| int next_region; | |
| // merge-find | |
| mfp<int> region_mf; | |
| EquivMarkWorker(Module *module) : module(module), sigmap(module) | |
| { | |
| for (auto cell : module->cells()) | |
| { | |
| if (cell->type == "$equiv") | |
| equiv_cells.insert(cell->name); | |
| for (auto &port : cell->connections()) | |
| { | |
| if (cell->input(port.first)) | |
| for (auto bit : sigmap(port.second)) | |
| up_cell2bits[cell->name].insert(bit); | |
| if (cell->output(port.first)) | |
| for (auto bit : sigmap(port.second)) | |
| up_bit2cells[bit].insert(cell->name); | |
| } | |
| } | |
| next_region = 0; | |
| } | |
| void mark() | |
| { | |
| while (!queue.empty()) | |
| { | |
| pool<IdString> cells; | |
| for (auto &bit : queue) | |
| { | |
| // log_assert(bit_regions.count(bit) == 0); | |
| bit_regions[bit] = next_region; | |
| visited.insert(bit); | |
| for (auto cell : up_bit2cells[bit]) | |
| if (edge_cells.count(cell) == 0) | |
| cells.insert(cell); | |
| } | |
| queue.clear(); | |
| for (auto cell : cells) | |
| { | |
| if (next_region == 0 && equiv_cells.count(cell)) | |
| continue; | |
| if (cell_regions.count(cell)) { | |
| if (cell_regions.at(cell) != 0) | |
| region_mf.merge(cell_regions.at(cell), next_region); | |
| continue; | |
| } | |
| cell_regions[cell] = next_region; | |
| for (auto bit : up_cell2bits[cell]) | |
| if (visited.count(bit) == 0) | |
| queue.insert(bit); | |
| } | |
| } | |
| next_region++; | |
| } | |
| void run() | |
| { | |
| log("Running equiv_mark on module %s:\n", log_id(module)); | |
| // marking region 0 | |
| for (auto wire : module->wires()) | |
| if (wire->port_id > 0) | |
| for (auto bit : sigmap(wire)) | |
| queue.insert(bit); | |
| for (auto cell_name : equiv_cells) | |
| { | |
| auto cell = module->cell(cell_name); | |
| SigSpec sig_a = sigmap(cell->getPort("\\A")); | |
| SigSpec sig_b = sigmap(cell->getPort("\\B")); | |
| if (sig_a == sig_b) { | |
| for (auto bit : sig_a) | |
| queue.insert(bit); | |
| edge_cells.insert(cell_name); | |
| cell_regions[cell_name] = 0; | |
| } | |
| } | |
| mark(); | |
| // marking unsolved regions | |
| for (auto cell : module->cells()) | |
| { | |
| if (cell_regions.count(cell->name) || cell->type != "$equiv") | |
| continue; | |
| SigSpec sig_a = sigmap(cell->getPort("\\A")); | |
| SigSpec sig_b = sigmap(cell->getPort("\\B")); | |
| log_assert(sig_a != sig_b); | |
| for (auto bit : sig_a) | |
| queue.insert(bit); | |
| for (auto bit : sig_b) | |
| queue.insert(bit); | |
| cell_regions[cell->name] = next_region; | |
| mark(); | |
| } | |
| // setting attributes | |
| dict<int, int> final_region_map; | |
| int next_final_region = 0; | |
| dict<int, int> region_cell_count; | |
| dict<int, int> region_wire_count; | |
| for (int i = 0; i < next_region; i++) { | |
| int r = region_mf.find(i); | |
| if (final_region_map.count(r) == 0) | |
| final_region_map[r] = next_final_region++; | |
| final_region_map[i] = final_region_map[r]; | |
| } | |
| for (auto cell : module->cells()) | |
| { | |
| if (cell_regions.count(cell->name)) { | |
| int r = final_region_map.at(cell_regions.at(cell->name)); | |
| cell->attributes["\\equiv_region"] = Const(r); | |
| region_cell_count[r]++; | |
| } else | |
| cell->attributes.erase("\\equiv_region"); | |
| } | |
| for (auto wire : module->wires()) | |
| { | |
| pool<int> regions; | |
| for (auto bit : sigmap(wire)) | |
| if (bit_regions.count(bit)) | |
| regions.insert(region_mf.find(bit_regions.at(bit))); | |
| if (GetSize(regions) == 1) { | |
| int r = final_region_map.at(*regions.begin()); | |
| wire->attributes["\\equiv_region"] = Const(r); | |
| region_wire_count[r]++; | |
| } else | |
| wire->attributes.erase("\\equiv_region"); | |
| } | |
| for (int i = 0; i < next_final_region; i++) | |
| log(" region %d: %d cells, %d wires\n", i, region_wire_count[i], region_cell_count[i]); | |
| } | |
| }; | |
| struct EquivMarkPass : public Pass { | |
| EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { } | |
| void help() YS_OVERRIDE | |
| { | |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | |
| log("\n"); | |
| log(" equiv_mark [options] [selection]\n"); | |
| log("\n"); | |
| log("This command marks the regions in an equivalence checking module. Region 0 is\n"); | |
| log("the proven part of the circuit. Regions with higher numbers are connected\n"); | |
| log("unproven subcricuits. The integer attribute 'equiv_region' is set on all\n"); | |
| log("wires and cells.\n"); | |
| log("\n"); | |
| } | |
| void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE | |
| { | |
| log_header(design, "Executing EQUIV_MARK pass.\n"); | |
| size_t argidx; | |
| for (argidx = 1; argidx < args.size(); argidx++) { | |
| // if (args[argidx] == "-foobar") { | |
| // continue; | |
| // } | |
| break; | |
| } | |
| extra_args(args, argidx, design); | |
| for (auto module : design->selected_whole_modules_warn()) { | |
| EquivMarkWorker worker(module); | |
| worker.run(); | |
| } | |
| } | |
| } EquivMarkPass; | |
| PRIVATE_NAMESPACE_END |