| /* | |
| * 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/consteval.h" | |
| USING_YOSYS_NAMESPACE | |
| PRIVATE_NAMESPACE_BEGIN | |
| struct ExtractFaConfig | |
| { | |
| bool enable_fa = false; | |
| bool enable_ha = false; | |
| bool verbose = false; | |
| int maxdepth = 20; | |
| int maxbreadth = 6; | |
| }; | |
| // http://svn.clifford.at/handicraft/2016/bindec/bindec.c | |
| int bindec(unsigned char v) | |
| { | |
| int r = v & 1; | |
| r += (~((v & 2) - 1)) & 10; | |
| r += (~((v & 4) - 1)) & 100; | |
| r += (~((v & 8) - 1)) & 1000; | |
| r += (~((v & 16) - 1)) & 10000; | |
| r += (~((v & 32) - 1)) & 100000; | |
| r += (~((v & 64) - 1)) & 1000000; | |
| r += (~((v & 128) - 1)) & 10000000; | |
| return r; | |
| } | |
| struct ExtractFaWorker | |
| { | |
| const ExtractFaConfig &config; | |
| Module *module; | |
| ConstEval ce; | |
| SigMap &sigmap; | |
| dict<SigBit, Cell*> driver; | |
| pool<SigBit> handled_bits; | |
| const int xor2_func = 0x6, xnor2_func = 0x9; | |
| const int xor3_func = 0x96, xnor3_func = 0x69; | |
| pool<tuple<SigBit, SigBit>> xorxnor2; | |
| pool<tuple<SigBit, SigBit, SigBit>> xorxnor3; | |
| dict<tuple<SigBit, SigBit>, dict<int, pool<SigBit>>> func2; | |
| dict<tuple<SigBit, SigBit, SigBit>, dict<int, pool<SigBit>>> func3; | |
| int count_func2; | |
| int count_func3; | |
| struct func2_and_info_t { | |
| bool inv_a, inv_b, inv_y; | |
| }; | |
| struct func3_maj_info_t { | |
| bool inv_a, inv_b, inv_c, inv_y; | |
| }; | |
| dict<int, func2_and_info_t> func2_and_info; | |
| dict<int, func3_maj_info_t> func3_maj_info; | |
| ExtractFaWorker(const ExtractFaConfig &config, Module *module) : | |
| config(config), module(module), ce(module), sigmap(ce.assign_map) | |
| { | |
| for (auto cell : module->selected_cells()) | |
| { | |
| if (cell->type.in( ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), | |
| ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_), | |
| ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) | |
| { | |
| SigBit y = sigmap(SigBit(cell->getPort(ID::Y))); | |
| log_assert(driver.count(y) == 0); | |
| driver[y] = cell; | |
| } | |
| } | |
| for (int ia = 0; ia < 2; ia++) | |
| for (int ib = 0; ib < 2; ib++) | |
| { | |
| func2_and_info_t f2i; | |
| f2i.inv_a = ia; | |
| f2i.inv_b = ib; | |
| f2i.inv_y = false; | |
| int func = 0; | |
| for (int i = 0; i < 4; i++) | |
| { | |
| bool a = (i & 1) ? !f2i.inv_a : f2i.inv_a; | |
| bool b = (i & 2) ? !f2i.inv_b : f2i.inv_b; | |
| if (a && b) func |= 1 << i; | |
| } | |
| log_assert(func2_and_info.count(func) == 0); | |
| func2_and_info[func] = f2i; | |
| f2i.inv_y = true; | |
| func ^= 15; | |
| log_assert(func2_and_info.count(func) == 0); | |
| func2_and_info[func] = f2i; | |
| } | |
| for (int ia = 0; ia < 2; ia++) | |
| for (int ib = 0; ib < 2; ib++) | |
| for (int ic = 0; ic < 2; ic++) | |
| { | |
| func3_maj_info_t f3i; | |
| f3i.inv_a = ia; | |
| f3i.inv_b = ib; | |
| f3i.inv_c = ic; | |
| f3i.inv_y = false; | |
| int func = 0; | |
| for (int i = 0; i < 8; i++) | |
| { | |
| bool a = (i & 1) ? !f3i.inv_a : f3i.inv_a; | |
| bool b = (i & 2) ? !f3i.inv_b : f3i.inv_b; | |
| bool c = (i & 4) ? !f3i.inv_c : f3i.inv_c; | |
| if ((a && b) || (a && c) || (b &&c)) func |= 1 << i; | |
| } | |
| log_assert(func3_maj_info.count(func) == 0); | |
| func3_maj_info[func] = f3i; | |
| // f3i.inv_y = true; | |
| // func ^= 255; | |
| // log_assert(func3_maj_info.count(func) == 0); | |
| // func3_maj_info[func] = f3i; | |
| } | |
| } | |
| void check_partition(SigBit root, pool<SigBit> &leaves) | |
| { | |
| if (config.enable_ha && GetSize(leaves) == 2) | |
| { | |
| leaves.sort(); | |
| SigBit A = SigSpec(leaves)[0]; | |
| SigBit B = SigSpec(leaves)[1]; | |
| int func = 0; | |
| for (int i = 0; i < 4; i++) | |
| { | |
| bool a_value = (i & 1) != 0; | |
| bool b_value = (i & 2) != 0; | |
| ce.push(); | |
| ce.set(A, a_value ? State::S1 : State::S0); | |
| ce.set(B, b_value ? State::S1 : State::S0); | |
| SigSpec sig = root; | |
| if (!ce.eval(sig)) { | |
| ce.pop(); | |
| return; | |
| } | |
| if (sig == State::S1) | |
| func |= 1 << i; | |
| ce.pop(); | |
| } | |
| // log("%04d %s %s -> %s\n", bindec(func), log_signal(A), log_signal(B), log_signal(root)); | |
| if (func == xor2_func || func == xnor2_func) | |
| xorxnor2.insert(tuple<SigBit, SigBit>(A, B)); | |
| count_func2++; | |
| func2[tuple<SigBit, SigBit>(A, B)][func].insert(root); | |
| } | |
| if (config.enable_fa && GetSize(leaves) == 3) | |
| { | |
| leaves.sort(); | |
| SigBit A = SigSpec(leaves)[0]; | |
| SigBit B = SigSpec(leaves)[1]; | |
| SigBit C = SigSpec(leaves)[2]; | |
| int func = 0; | |
| for (int i = 0; i < 8; i++) | |
| { | |
| bool a_value = (i & 1) != 0; | |
| bool b_value = (i & 2) != 0; | |
| bool c_value = (i & 4) != 0; | |
| ce.push(); | |
| ce.set(A, a_value ? State::S1 : State::S0); | |
| ce.set(B, b_value ? State::S1 : State::S0); | |
| ce.set(C, c_value ? State::S1 : State::S0); | |
| SigSpec sig = root; | |
| if (!ce.eval(sig)) { | |
| ce.pop(); | |
| return; | |
| } | |
| if (sig == State::S1) | |
| func |= 1 << i; | |
| ce.pop(); | |
| } | |
| // log("%08d %s %s %s -> %s\n", bindec(func), log_signal(A), log_signal(B), log_signal(C), log_signal(root)); | |
| if (func == xor3_func || func == xnor3_func) | |
| xorxnor3.insert(tuple<SigBit, SigBit, SigBit>(A, B, C)); | |
| count_func3++; | |
| func3[tuple<SigBit, SigBit, SigBit>(A, B, C)][func].insert(root); | |
| } | |
| } | |
| void find_partitions(SigBit root, pool<SigBit> &leaves, pool<pool<SigBit>> &cache, int maxdepth, int maxbreadth) | |
| { | |
| if (cache.count(leaves)) | |
| return; | |
| // log("%*s[%d] %s:", 20-maxdepth, "", maxdepth, log_signal(root)); | |
| // for (auto bit : leaves) | |
| // log(" %s", log_signal(bit)); | |
| // log("\n"); | |
| cache.insert(leaves); | |
| check_partition(root, leaves); | |
| if (maxdepth == 0) | |
| return; | |
| for (SigBit bit : leaves) | |
| { | |
| if (driver.count(bit) == 0) | |
| continue; | |
| Cell *cell = driver.at(bit); | |
| pool<SigBit> new_leaves = leaves; | |
| new_leaves.erase(bit); | |
| if (cell->hasPort(ID::A)) new_leaves.insert(sigmap(SigBit(cell->getPort(ID::A)))); | |
| if (cell->hasPort(ID::B)) new_leaves.insert(sigmap(SigBit(cell->getPort(ID::B)))); | |
| if (cell->hasPort(ID(C))) new_leaves.insert(sigmap(SigBit(cell->getPort(ID(C))))); | |
| if (cell->hasPort(ID(D))) new_leaves.insert(sigmap(SigBit(cell->getPort(ID(D))))); | |
| if (GetSize(new_leaves) > maxbreadth) | |
| continue; | |
| find_partitions(root, new_leaves, cache, maxdepth-1, maxbreadth); | |
| } | |
| } | |
| void assign_new_driver(SigBit bit, SigBit new_driver) | |
| { | |
| Cell *cell = driver.at(bit); | |
| if (sigmap(cell->getPort(ID::Y)) == bit) { | |
| cell->setPort(ID::Y, module->addWire(NEW_ID)); | |
| module->connect(bit, new_driver); | |
| } | |
| } | |
| void run() | |
| { | |
| log("Extracting full/half adders from %s:\n", log_id(module)); | |
| for (auto it : driver) | |
| { | |
| if (it.second->type.in(ID($_BUF_), ID($_NOT_))) | |
| continue; | |
| SigBit root = it.first; | |
| pool<SigBit> leaves = { root }; | |
| pool<pool<SigBit>> cache; | |
| if (config.verbose) | |
| log(" checking %s\n", log_signal(it.first)); | |
| count_func2 = 0; | |
| count_func3 = 0; | |
| find_partitions(root, leaves, cache, config.maxdepth, config.maxbreadth); | |
| if (config.verbose && count_func2 > 0) | |
| log(" extracted %d two-input functions\n", count_func2); | |
| if (config.verbose && count_func3 > 0) | |
| log(" extracted %d three-input functions\n", count_func3); | |
| } | |
| for (auto &key : xorxnor3) | |
| { | |
| SigBit A = get<0>(key); | |
| SigBit B = get<1>(key); | |
| SigBit C = get<2>(key); | |
| log(" 3-Input XOR/XNOR %s %s %s:\n", log_signal(A), log_signal(B), log_signal(C)); | |
| for (auto &it : func3.at(key)) | |
| { | |
| if (it.first != xor3_func && it.first != xnor3_func) | |
| continue; | |
| log(" %08d ->", bindec(it.first)); | |
| for (auto bit : it.second) | |
| log(" %s", log_signal(bit)); | |
| log("\n"); | |
| } | |
| dict<int, tuple<SigBit, SigBit, Cell*>> facache; | |
| for (auto &it : func3_maj_info) | |
| { | |
| int func = it.first; | |
| auto f3i = it.second; | |
| if (func3.at(key).count(func) == 0) | |
| continue; | |
| if (func3.at(key).count(xor3_func) == 0 && func3.at(key).count(xnor3_func) != 0) { | |
| f3i.inv_a = !f3i.inv_a; | |
| f3i.inv_b = !f3i.inv_b; | |
| f3i.inv_c = !f3i.inv_c; | |
| f3i.inv_y = !f3i.inv_y; | |
| } | |
| if (!f3i.inv_a && !f3i.inv_b && !f3i.inv_c && !f3i.inv_y) { | |
| log(" Majority without inversions:\n"); | |
| } else { | |
| log(" Majority with inverted"); | |
| if (f3i.inv_a) log(" A"); | |
| if (f3i.inv_b) log(" B"); | |
| if (f3i.inv_c) log(" C"); | |
| if (f3i.inv_y) log(" Y"); | |
| log(":\n"); | |
| } | |
| log(" %08d ->", bindec(func)); | |
| for (auto bit : func3.at(key).at(func)) | |
| log(" %s", log_signal(bit)); | |
| log("\n"); | |
| int fakey = 0; | |
| if (f3i.inv_a) fakey |= 1; | |
| if (f3i.inv_b) fakey |= 2; | |
| if (f3i.inv_c) fakey |= 4; | |
| int fakey_inv = fakey ^ 7; | |
| bool invert_xy = false; | |
| SigBit X, Y; | |
| if (facache.count(fakey)) | |
| { | |
| auto &fa = facache.at(fakey); | |
| X = get<0>(fa); | |
| Y = get<1>(fa); | |
| log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); | |
| } | |
| else | |
| if (facache.count(fakey_inv)) | |
| { | |
| auto &fa = facache.at(fakey_inv); | |
| invert_xy = true; | |
| X = get<0>(fa); | |
| Y = get<1>(fa); | |
| log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); | |
| } | |
| else | |
| { | |
| Cell *cell = module->addCell(NEW_ID, ID($fa)); | |
| cell->setParam(ID(WIDTH), 1); | |
| log(" Created $fa cell %s.\n", log_id(cell)); | |
| cell->setPort(ID::A, f3i.inv_a ? module->NotGate(NEW_ID, A) : A); | |
| cell->setPort(ID::B, f3i.inv_b ? module->NotGate(NEW_ID, B) : B); | |
| cell->setPort(ID(C), f3i.inv_c ? module->NotGate(NEW_ID, C) : C); | |
| X = module->addWire(NEW_ID); | |
| Y = module->addWire(NEW_ID); | |
| cell->setPort(ID(X), X); | |
| cell->setPort(ID::Y, Y); | |
| facache[fakey] = make_tuple(X, Y, cell); | |
| } | |
| if (func3.at(key).count(xor3_func)) { | |
| SigBit YY = invert_xy ? module->NotGate(NEW_ID, Y) : Y; | |
| for (auto bit : func3.at(key).at(xor3_func)) | |
| assign_new_driver(bit, YY); | |
| } | |
| if (func3.at(key).count(xnor3_func)) { | |
| SigBit YY = invert_xy ? Y : module->NotGate(NEW_ID, Y); | |
| for (auto bit : func3.at(key).at(xnor3_func)) | |
| assign_new_driver(bit, YY); | |
| } | |
| SigBit XX = invert_xy != f3i.inv_y ? module->NotGate(NEW_ID, X) : X; | |
| for (auto bit : func3.at(key).at(func)) | |
| assign_new_driver(bit, XX); | |
| } | |
| } | |
| for (auto &key : xorxnor2) | |
| { | |
| SigBit A = get<0>(key); | |
| SigBit B = get<1>(key); | |
| log(" 2-Input XOR/XNOR %s %s:\n", log_signal(A), log_signal(B)); | |
| for (auto &it : func2.at(key)) | |
| { | |
| if (it.first != xor2_func && it.first != xnor2_func) | |
| continue; | |
| log(" %04d ->", bindec(it.first)); | |
| for (auto bit : it.second) | |
| log(" %s", log_signal(bit)); | |
| log("\n"); | |
| } | |
| dict<int, tuple<SigBit, SigBit, Cell*>> facache; | |
| for (auto &it : func2_and_info) | |
| { | |
| int func = it.first; | |
| auto &f2i = it.second; | |
| if (func2.at(key).count(func) == 0) | |
| continue; | |
| if (!f2i.inv_a && !f2i.inv_b && !f2i.inv_y) { | |
| log(" AND without inversions:\n"); | |
| } else { | |
| log(" AND with inverted"); | |
| if (f2i.inv_a) log(" A"); | |
| if (f2i.inv_b) log(" B"); | |
| if (f2i.inv_y) log(" Y"); | |
| log(":\n"); | |
| } | |
| log(" %04d ->", bindec(func)); | |
| for (auto bit : func2.at(key).at(func)) | |
| log(" %s", log_signal(bit)); | |
| log("\n"); | |
| int fakey = 0; | |
| if (f2i.inv_a) fakey |= 1; | |
| if (f2i.inv_b) fakey |= 2; | |
| int fakey_inv = fakey ^ 3; | |
| bool invert_xy = false; | |
| SigBit X, Y; | |
| if (facache.count(fakey)) | |
| { | |
| auto &fa = facache.at(fakey); | |
| X = get<0>(fa); | |
| Y = get<1>(fa); | |
| log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); | |
| } | |
| else | |
| if (facache.count(fakey_inv)) | |
| { | |
| auto &fa = facache.at(fakey_inv); | |
| invert_xy = true; | |
| X = get<0>(fa); | |
| Y = get<1>(fa); | |
| log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); | |
| } | |
| else | |
| { | |
| Cell *cell = module->addCell(NEW_ID, ID($fa)); | |
| cell->setParam(ID(WIDTH), 1); | |
| log(" Created $fa cell %s.\n", log_id(cell)); | |
| cell->setPort(ID::A, f2i.inv_a ? module->NotGate(NEW_ID, A) : A); | |
| cell->setPort(ID::B, f2i.inv_b ? module->NotGate(NEW_ID, B) : B); | |
| cell->setPort(ID(C), State::S0); | |
| X = module->addWire(NEW_ID); | |
| Y = module->addWire(NEW_ID); | |
| cell->setPort(ID(X), X); | |
| cell->setPort(ID::Y, Y); | |
| } | |
| if (func2.at(key).count(xor2_func)) { | |
| SigBit YY = invert_xy || (f2i.inv_a && !f2i.inv_b) || (!f2i.inv_a && f2i.inv_b) ? module->NotGate(NEW_ID, Y) : Y; | |
| for (auto bit : func2.at(key).at(xor2_func)) | |
| assign_new_driver(bit, YY); | |
| } | |
| if (func2.at(key).count(xnor2_func)) { | |
| SigBit YY = invert_xy || (f2i.inv_a && !f2i.inv_b) || (!f2i.inv_a && f2i.inv_b) ? Y : module->NotGate(NEW_ID, Y); | |
| for (auto bit : func2.at(key).at(xnor2_func)) | |
| assign_new_driver(bit, YY); | |
| } | |
| SigBit XX = invert_xy != f2i.inv_y ? module->NotGate(NEW_ID, X) : X; | |
| for (auto bit : func2.at(key).at(func)) | |
| assign_new_driver(bit, XX); | |
| } | |
| } | |
| } | |
| }; | |
| struct ExtractFaPass : public Pass { | |
| ExtractFaPass() : Pass("extract_fa", "find and extract full/half adders") { } | |
| void help() YS_OVERRIDE | |
| { | |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | |
| log("\n"); | |
| log(" extract_fa [options] [selection]\n"); | |
| log("\n"); | |
| log("This pass extracts full/half adders from a gate-level design.\n"); | |
| log("\n"); | |
| log(" -fa, -ha\n"); | |
| log(" Enable cell types (fa=full adder, ha=half adder)\n"); | |
| log(" All types are enabled if none of this options is used\n"); | |
| log("\n"); | |
| log(" -d <int>\n"); | |
| log(" Set maximum depth for extracted logic cones (default=20)\n"); | |
| log("\n"); | |
| log(" -b <int>\n"); | |
| log(" Set maximum breadth for extracted logic cones (default=6)\n"); | |
| log("\n"); | |
| log(" -v\n"); | |
| log(" Verbose output\n"); | |
| log("\n"); | |
| } | |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | |
| { | |
| ExtractFaConfig config; | |
| log_header(design, "Executing EXTRACT_FA pass (find and extract full/half adders).\n"); | |
| log_push(); | |
| size_t argidx; | |
| for (argidx = 1; argidx < args.size(); argidx++) | |
| { | |
| if (args[argidx] == "-fa") { | |
| config.enable_fa = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-ha") { | |
| config.enable_ha = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-v") { | |
| config.verbose = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-d" && argidx+2 < args.size()) { | |
| config.maxdepth = atoi(args[++argidx].c_str()); | |
| continue; | |
| } | |
| if (args[argidx] == "-b" && argidx+2 < args.size()) { | |
| config.maxbreadth = atoi(args[++argidx].c_str()); | |
| continue; | |
| } | |
| break; | |
| } | |
| extra_args(args, argidx, design); | |
| if (!config.enable_fa && !config.enable_ha) { | |
| config.enable_fa = true; | |
| config.enable_ha = true; | |
| } | |
| for (auto module : design->selected_modules()) | |
| { | |
| ExtractFaWorker worker(config, module); | |
| worker.run(); | |
| } | |
| log_pop(); | |
| } | |
| } ExtractFaPass; | |
| PRIVATE_NAMESPACE_END |