diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2018-08-30 20:46:20 +0200 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2018-08-30 20:46:20 +0200 |
commit | 5033b51947a6ef02cb785b5622e993335efa750a (patch) | |
tree | 7bed18c526bd94917fa2f08e3df12209863698a1 /passes | |
parent | fefe0fc0430f4f173a25e674708aa0f4f0854b31 (diff) |
New upstream version 0.7+20180830git0b7a184
Diffstat (limited to 'passes')
127 files changed, 6341 insertions, 675 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 01ada773..44a83b2b 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -25,4 +25,8 @@ OBJS += passes/cmds/plugin.o OBJS += passes/cmds/check.o OBJS += passes/cmds/qwp.o OBJS += passes/cmds/edgetypes.o +OBJS += passes/cmds/chformal.o +OBJS += passes/cmds/chtype.o +OBJS += passes/cmds/blackbox.o +OBJS += passes/cmds/ltp.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index e698926f..cfccca96 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -83,7 +83,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n struct AddPass : public Pass { AddPass() : Pass("add", "add objects to the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -106,7 +106,7 @@ struct AddPass : public Pass { log("selected modules.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string command; std::string arg_name; diff --git a/passes/cmds/blackbox.cc b/passes/cmds/blackbox.cc new file mode 100644 index 00000000..6094f8f1 --- /dev/null +++ b/passes/cmds/blackbox.cc @@ -0,0 +1,81 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct BlackboxPass : public Pass { + BlackboxPass() : Pass("blackbox", "change type of cells in the design") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" blackbox [options] [selection]\n"); + log("\n"); + log("Convert modules into blackbox modules (remove contents and set the blackbox\n"); + log("module attribute).\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-???") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) + { + pool<Cell*> remove_cells; + pool<Wire*> remove_wires; + + for (auto cell : module->cells()) + remove_cells.insert(cell); + + for (auto wire : module->wires()) + if (wire->port_id == 0) + remove_wires.insert(wire); + + for (auto it = module->memories.begin(); it != module->memories.end(); ++it) + delete it->second; + module->memories.clear(); + + for (auto it = module->processes.begin(); it != module->processes.end(); ++it) + delete it->second; + module->processes.clear(); + + module->new_connections(std::vector<RTLIL::SigSig>()); + + for (auto cell : remove_cells) + module->remove(cell); + + module->remove(remove_wires); + + module->set_bool_attribute("\\blackbox"); + } + } +} BlackboxPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index b3622cb1..64697c13 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct CheckPass : public Pass { CheckPass() : Pass("check", "check for obvious problems in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -44,14 +44,18 @@ struct CheckPass : public Pass { log("When called with -noinit then this command also checks for wires which have\n"); log("the 'init' attribute set.\n"); log("\n"); + log("When called with -initdrv then this command also checks for wires which have\n"); + log("the 'init' attribute set and aren't driven by a FF cell type.\n"); + log("\n"); log("When called with -assert then the command will produce an error if any\n"); log("problems are found in the current design.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { int counter = 0; bool noinit = false; + bool initdrv = false; bool assert_mode = false; size_t argidx; @@ -60,6 +64,10 @@ struct CheckPass : public Pass { noinit = true; continue; } + if (args[argidx] == "-initdrv") { + initdrv = true; + continue; + } if (args[argidx] == "-assert") { assert_mode = true; continue; @@ -70,6 +78,49 @@ struct CheckPass : public Pass { log_header(design, "Executing CHECK pass (checking for obvious problems).\n"); + pool<IdString> fftypes; + fftypes.insert("$sr"); + fftypes.insert("$ff"); + fftypes.insert("$dff"); + fftypes.insert("$dffe"); + fftypes.insert("$dffsr"); + fftypes.insert("$adff"); + fftypes.insert("$dlatch"); + fftypes.insert("$dlatchsr"); + fftypes.insert("$_DFFE_NN_"); + fftypes.insert("$_DFFE_NP_"); + fftypes.insert("$_DFFE_PN_"); + fftypes.insert("$_DFFE_PP_"); + fftypes.insert("$_DFFSR_NNN_"); + fftypes.insert("$_DFFSR_NNP_"); + fftypes.insert("$_DFFSR_NPN_"); + fftypes.insert("$_DFFSR_NPP_"); + fftypes.insert("$_DFFSR_PNN_"); + fftypes.insert("$_DFFSR_PNP_"); + fftypes.insert("$_DFFSR_PPN_"); + fftypes.insert("$_DFFSR_PPP_"); + fftypes.insert("$_DFF_NN0_"); + fftypes.insert("$_DFF_NN1_"); + fftypes.insert("$_DFF_NP0_"); + fftypes.insert("$_DFF_NP1_"); + fftypes.insert("$_DFF_N_"); + fftypes.insert("$_DFF_PN0_"); + fftypes.insert("$_DFF_PN1_"); + fftypes.insert("$_DFF_PP0_"); + fftypes.insert("$_DFF_PP1_"); + fftypes.insert("$_DFF_P_"); + fftypes.insert("$_DLATCHSR_NNN_"); + fftypes.insert("$_DLATCHSR_NNP_"); + fftypes.insert("$_DLATCHSR_NPN_"); + fftypes.insert("$_DLATCHSR_NPP_"); + fftypes.insert("$_DLATCHSR_PNN_"); + fftypes.insert("$_DLATCHSR_PNP_"); + fftypes.insert("$_DLATCHSR_PPN_"); + fftypes.insert("$_DLATCHSR_PPP_"); + fftypes.insert("$_DLATCH_N_"); + fftypes.insert("$_DLATCH_P_"); + fftypes.insert("$_FF_"); + for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) @@ -109,6 +160,8 @@ struct CheckPass : public Pass { if (bit.wire) wire_drivers_count[bit]++; } + pool<SigBit> init_bits; + for (auto wire : module->wires()) { if (wire->port_input) { SigSpec sig = sigmap(wire); @@ -121,9 +174,15 @@ struct CheckPass : public Pass { if (wire->port_input && !wire->port_output) for (auto bit : sigmap(wire)) if (bit.wire) wire_drivers_count[bit]++; - if (noinit && wire->attributes.count("\\init")) { - log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire)); - counter++; + if (wire->attributes.count("\\init")) { + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + init_bits.insert(sigmap(SigBit(wire, i))); + if (noinit) { + log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire)); + counter++; + } } } @@ -150,6 +209,26 @@ struct CheckPass : public Pass { log_warning("%s", message.c_str()); counter++; } + + if (initdrv) + { + for (auto cell : module->cells()) + { + if (fftypes.count(cell->type) == 0) + continue; + + for (auto bit : sigmap(cell->getPort("\\Q"))) + init_bits.erase(bit); + } + + SigSpec init_sig(init_bits); + init_sig.sort_and_unify(); + + for (auto chunk : init_sig.chunks()) { + log_warning("Wire %s.%s has 'init' attribute and is not driven by an FF cell.\n", log_id(module), log_signal(chunk)); + counter++; + } + } } log("found and reported %d problems.\n", counter); diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc new file mode 100644 index 00000000..522758ea --- /dev/null +++ b/passes/cmds/chformal.cc @@ -0,0 +1,282 @@ +/* + * 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 ChformalPass : public Pass { + ChformalPass() : Pass("chformal", "change formal constraints of the design") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" chformal [types] [mode] [options] [selection]\n"); + log("\n"); + log("Make changes to the formal constraints of the design. The [types] options\n"); + log("the type of constraint to operate on. If none of the folling options is given,\n"); + log("the command will operate on all constraint types:\n"); + log("\n"); + log(" -assert $assert cells, representing assert(...) constraints\n"); + log(" -assume $assume cells, representing assume(...) constraints\n"); + log(" -live $live cells, representing assert(s_eventually ...)\n"); + log(" -fair $fair cells, representing assume(s_eventually ...)\n"); + log(" -cover $cover cells, representing cover() statements\n"); + log("\n"); + log("Exactly one of the following modes must be specified:\n"); + log("\n"); + log(" -remove\n"); + log(" remove the cells and thus constraints from the design\n"); + log("\n"); + log(" -early\n"); + log(" bypass FFs that only delay the activation of a constraint\n"); + log("\n"); + log(" -delay <N>\n"); + log(" delay activation of the constraint by <N> clock cycles\n"); + log("\n"); + log(" -skip <N>\n"); + log(" ignore activation of the constraint in the first <N> clock cycles\n"); + log("\n"); + log(" -assert2assume\n"); + log(" -assume2assert\n"); + log(" -live2fair\n"); + log(" -fair2live\n"); + log(" change the roles of cells as indicated. this options can be combined\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + bool assert2assume = false; + bool assume2assert = false; + bool live2fair = false; + bool fair2live = false; + + pool<IdString> constr_types; + char mode = 0; + int mode_arg = 0; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-assert") { + constr_types.insert("$assert"); + continue; + } + if (args[argidx] == "-assume") { + constr_types.insert("$assume"); + continue; + } + if (args[argidx] == "-live") { + constr_types.insert("$live"); + continue; + } + if (args[argidx] == "-fair") { + constr_types.insert("$fair"); + continue; + } + if (args[argidx] == "-cover") { + constr_types.insert("$cover"); + continue; + } + if (mode == 0 && args[argidx] == "-remove") { + mode = 'r'; + continue; + } + if (mode == 0 && args[argidx] == "-early") { + mode = 'e'; + continue; + } + if (mode == 0 && args[argidx] == "-delay" && argidx+1 < args.size()) { + mode = 'd'; + mode_arg = atoi(args[++argidx].c_str()); + continue; + } + if (mode == 0 && args[argidx] == "-skip" && argidx+1 < args.size()) { + mode = 's'; + mode_arg = atoi(args[++argidx].c_str()); + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-assert2assume") { + assert2assume = true; + mode = 'c'; + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-assume2assert") { + assume2assert = true; + mode = 'c'; + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-live2fair") { + live2fair = true; + mode = 'c'; + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-fair2live") { + fair2live = true; + mode = 'c'; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (constr_types.empty()) { + constr_types.insert("$assert"); + constr_types.insert("$assume"); + constr_types.insert("$live"); + constr_types.insert("$fair"); + constr_types.insert("$cover"); + } + + if (mode == 0) + log_cmd_error("Mode option is missing.\n"); + + for (auto module : design->selected_modules()) + { + vector<Cell*> constr_cells; + + for (auto cell : module->selected_cells()) + if (constr_types.count(cell->type)) + constr_cells.push_back(cell); + + if (mode == 'r') + { + for (auto cell : constr_cells) + module->remove(cell); + } + else + if (mode == 'e') + { + SigMap sigmap(module); + dict<SigBit, pair<SigBit, pair<SigBit, bool>>> ffmap; + pool<SigBit> init_zero, init_one; + + for (auto wire : module->wires()) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec initsig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) { + if (initval[i] == State::S0) + init_zero.insert(initsig[i]); + if (initval[i] == State::S1) + init_one.insert(initsig[i]); + } + } + + for (auto cell : module->selected_cells()) + { + if (cell->type == "$ff") { + SigSpec D = sigmap(cell->getPort("\\D")); + SigSpec Q = sigmap(cell->getPort("\\Q")); + for (int i = 0; i < GetSize(D); i++) + ffmap[Q[i]] = make_pair(D[i], make_pair(State::Sm, false)); + } + if (cell->type == "$dff") { + SigSpec D = sigmap(cell->getPort("\\D")); + SigSpec Q = sigmap(cell->getPort("\\Q")); + SigSpec C = sigmap(cell->getPort("\\CLK")); + bool clockpol = cell->getParam("\\CLK_POLARITY").as_bool(); + for (int i = 0; i < GetSize(D); i++) + ffmap[Q[i]] = make_pair(D[i], make_pair(C, clockpol)); + } + } + + for (auto cell : constr_cells) + while (true) + { + SigSpec A = sigmap(cell->getPort("\\A")); + SigSpec EN = sigmap(cell->getPort("\\EN")); + + if (ffmap.count(A) == 0 || ffmap.count(EN) == 0) + break; + + if (!init_zero.count(EN)) { + if (cell->type == "$cover") break; + if (cell->type.in("$assert", "$assume") && !init_one.count(A)) break; + } + + const auto &A_map = ffmap.at(A); + const auto &EN_map = ffmap.at(EN); + + if (A_map.second != EN_map.second) + break; + + cell->setPort("\\A", A_map.first); + cell->setPort("\\EN", EN_map.first); + } + } + else + if (mode == 'd') + { + for (auto cell : constr_cells) + for (int i = 0; i < mode_arg; i++) + { + SigSpec orig_a = cell->getPort("\\A"); + SigSpec orig_en = cell->getPort("\\EN"); + + Wire *new_a = module->addWire(NEW_ID); + Wire *new_en = module->addWire(NEW_ID); + new_en->attributes["\\init"] = State::S0; + + module->addFf(NEW_ID, orig_a, new_a); + module->addFf(NEW_ID, orig_en, new_en); + + cell->setPort("\\A", new_a); + cell->setPort("\\EN", new_en); + } + } + else + if (mode == 's') + { + SigSpec en = State::S1; + + for (int i = 0; i < mode_arg; i++) { + Wire *w = module->addWire(NEW_ID); + w->attributes["\\init"] = State::S0; + module->addFf(NEW_ID, en, w); + en = w; + } + + for (auto cell : constr_cells) + cell->setPort("\\EN", module->LogicAnd(NEW_ID, en, cell->getPort("\\EN"))); + } + else + if (mode == 'c') + { + for (auto cell : constr_cells) + if (assert2assume && cell->type == "$assert") + cell->type = "$assume"; + else if (assume2assert && cell->type == "$assume") + cell->type = "$assert"; + else if (live2fair && cell->type == "$live") + cell->type = "$fair"; + else if (fair2live && cell->type == "$fair") + cell->type = "$live"; + } + } + } +} ChformalPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/chtype.cc b/passes/cmds/chtype.cc new file mode 100644 index 00000000..979aeadd --- /dev/null +++ b/passes/cmds/chtype.cc @@ -0,0 +1,83 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ChtypePass : public Pass { + ChtypePass() : Pass("chtype", "change type of cells in the design") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" chtype [options] [selection]\n"); + log("\n"); + log("Change the types of cells in the design.\n"); + log("\n"); + log(" -set <type>\n"); + log(" set the cell type to the given type\n"); + log("\n"); + log(" -map <old_type> <new_type>\n"); + log(" change cells types that match <old_type> to <new_type>\n"); + log("\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + IdString set_type; + dict<IdString, IdString> map_types; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (set_type == IdString() && args[argidx] == "-set" && argidx+1 < args.size()) { + set_type = RTLIL::escape_id(args[++argidx]); + continue; + } + if (args[argidx] == "-map" && argidx+2 < args.size()) { + IdString old_type = RTLIL::escape_id(args[++argidx]); + IdString new_type = RTLIL::escape_id(args[++argidx]); + map_types[old_type] = new_type; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + { + if (map_types.count(cell->type)) { + cell->type = map_types.at(cell->type); + continue; + } + + if (set_type != IdString()) { + cell->type = set_type; + continue; + } + } + } + } +} ChtypePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index 52611cf4..d480b79a 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -43,7 +43,7 @@ static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap & struct ConnectPass : public Pass { ConnectPass() : Pass("connect", "create or remove connections") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -75,7 +75,7 @@ struct ConnectPass : public Pass { log("This command does not operate on module with processes.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { RTLIL::Module *module = NULL; for (auto &it : design->modules_) { diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc index c9ab226d..5a15cbba 100644 --- a/passes/cmds/connwrappers.cc +++ b/passes/cmds/connwrappers.cc @@ -149,8 +149,8 @@ struct ConnwrappersWorker }; struct ConnwrappersPass : public Pass { - ConnwrappersPass() : Pass("connwrappers", "replace undef values with defined constants") { } - virtual void help() + ConnwrappersPass() : Pass("connwrappers", "match width of input-output port pairs") { } + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -172,7 +172,7 @@ struct ConnwrappersPass : public Pass { log("The options -signed, -unsigned, and -port can be specified multiple times.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { ConnwrappersWorker worker; diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc index fb863512..acd2dba5 100644 --- a/passes/cmds/copy.cc +++ b/passes/cmds/copy.cc @@ -26,7 +26,7 @@ PRIVATE_NAMESPACE_BEGIN struct CopyPass : public Pass { CopyPass() : Pass("copy", "copy modules in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -36,7 +36,7 @@ struct CopyPass : public Pass { log("by this command.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { if (args.size() != 3) log_cmd_error("Invalid number of arguments!\n"); diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 1475475c..0ec74767 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -35,7 +35,7 @@ PRIVATE_NAMESPACE_BEGIN struct CoverPass : public Pass { CoverPass() : Pass("cover", "print code coverage counters") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -83,7 +83,7 @@ struct CoverPass : public Pass { log("Coverage counters are only available in Yosys for Linux.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<FILE*> out_files; std::vector<std::string> patterns; @@ -128,7 +128,7 @@ struct CoverPass : public Pass { log("\n"); } -#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) +#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__)) for (auto &it : get_coverage_data()) { if (!patterns.empty()) { for (auto &p : patterns) diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc index 6d51d30e..f8d91ea4 100644 --- a/passes/cmds/delete.cc +++ b/passes/cmds/delete.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN struct DeletePass : public Pass { DeletePass() : Pass("delete", "delete objects in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -40,7 +40,7 @@ struct DeletePass : public Pass { log("selected wires, thus 'deleting' module ports.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool flag_input = false; bool flag_output = false; diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index e900e7b9..172addcc 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -17,10 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/celltypes.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "frontends/ast/ast.h" YOSYS_NAMESPACE_BEGIN @@ -29,7 +27,7 @@ std::vector<RTLIL::Design*> pushed_designs; struct DesignPass : public Pass { DesignPass() : Pass("design", "save, restore and reset current design") { } - virtual ~DesignPass() { + ~DesignPass() YS_OVERRIDE { for (auto &it : saved_designs) delete it.second; saved_designs.clear(); @@ -37,7 +35,7 @@ struct DesignPass : public Pass { delete it; pushed_designs.clear(); } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -82,13 +80,28 @@ struct DesignPass : public Pass { log("\n"); log("Copy modules from the current design into the specified one.\n"); log("\n"); + log("\n"); + log(" design -import <name> [-as <new_top_name>] [selection]\n"); + log("\n"); + log("Import the specified design into the current design. The source design must\n"); + log("either have a selected top module or the selection must contain exactly one\n"); + log("module that is then used as top module for this command.\n"); + log("\n"); + log("\n"); + log(" design -reset-vlog\n"); + log("\n"); + log("The Verilog front-end remembers defined macros and top-level declarations\n"); + log("between calls to 'read_verilog'. This command resets this memory.\n"); + log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool got_mode = false; bool reset_mode = false; + bool reset_vlog_mode = false; bool push_mode = false; bool pop_mode = false; + bool import_mode = false; RTLIL::Design *copy_from_design = NULL, *copy_to_design = NULL; std::string save_name, load_name, as_name; std::vector<RTLIL::Module*> copy_src_modules; @@ -102,6 +115,11 @@ struct DesignPass : public Pass { reset_mode = true; continue; } + if (!got_mode && args[argidx] == "-reset-vlog") { + got_mode = true; + reset_vlog_mode = true; + continue; + } if (!got_mode && args[argidx] == "-push") { got_mode = true; push_mode = true; @@ -146,8 +164,17 @@ struct DesignPass : public Pass { copy_from_design = design; continue; } - if (copy_from_design != NULL && args[argidx] == "-as" && argidx+1 < args.size()) { + if (!got_mode && args[argidx] == "-import" && argidx+1 < args.size()) { got_mode = true; + import_mode = true; + if (saved_designs.count(args[++argidx]) == 0) + log_cmd_error("No saved design '%s' found!\n", args[argidx].c_str()); + copy_from_design = saved_designs.at(args[argidx]); + copy_to_design = design; + as_name = args[argidx]; + continue; + } + if (copy_from_design != NULL && args[argidx] == "-as" && argidx+1 < args.size()) { as_name = args[++argidx]; continue; } @@ -156,10 +183,10 @@ struct DesignPass : public Pass { if (copy_from_design != NULL) { - if (copy_from_design != design && argidx == args.size()) + if (copy_from_design != design && argidx == args.size() && !import_mode) cmd_error(args, argidx, "Missing selection."); - RTLIL::Selection sel = design->selection_stack.back(); + RTLIL::Selection sel; if (argidx != args.size()) { handle_extra_select_args(this, args, argidx, args.size(), copy_from_design); sel = copy_from_design->selection_stack.back(); @@ -175,6 +202,17 @@ struct DesignPass : public Pass { if (sel.selected_module(it.first)) log_cmd_error("Module %s is only partly selected.\n", RTLIL::id2cstr(it.first)); } + + if (import_mode) { + for (auto module : copy_src_modules) + { + if (module->get_bool_attribute("\\top")) { + copy_src_modules.clear(); + copy_src_modules.push_back(module); + break; + } + } + } } extra_args(args, argidx, design, false); @@ -185,6 +223,68 @@ struct DesignPass : public Pass { if (pop_mode && pushed_designs.empty()) log_cmd_error("No pushed designs.\n"); + if (import_mode) + { + std::string prefix = RTLIL::escape_id(as_name); + + pool<Module*> queue; + dict<IdString, IdString> done; + + if (copy_to_design->modules_.count(prefix)) + delete copy_to_design->modules_.at(prefix); + + if (GetSize(copy_src_modules) != 1) + log_cmd_error("No top module found in source design.\n"); + + for (auto mod : copy_src_modules) + { + log("Importing %s as %s.\n", log_id(mod), log_id(prefix)); + + copy_to_design->modules_[prefix] = mod->clone(); + copy_to_design->modules_[prefix]->name = prefix; + copy_to_design->modules_[prefix]->design = copy_to_design; + copy_to_design->modules_[prefix]->attributes.erase("\\top"); + + queue.insert(copy_to_design->modules_[prefix]); + done[mod->name] = prefix; + } + + while (!queue.empty()) + { + pool<Module*> old_queue; + old_queue.swap(queue); + + for (auto mod : old_queue) + for (auto cell : mod->cells()) + { + Module *fmod = copy_from_design->module(cell->type); + + if (fmod == nullptr) + continue; + + if (done.count(cell->type) == 0) + { + std::string trg_name = prefix + "." + (cell->type.c_str() + (*cell->type.c_str() == '\\')); + + log("Importing %s as %s.\n", log_id(fmod), log_id(trg_name)); + + if (copy_to_design->modules_.count(trg_name)) + delete copy_to_design->modules_.at(trg_name); + + copy_to_design->modules_[trg_name] = fmod->clone(); + copy_to_design->modules_[trg_name]->name = trg_name; + copy_to_design->modules_[trg_name]->design = copy_to_design; + copy_to_design->modules_[trg_name]->attributes.erase("\\top"); + + queue.insert(copy_to_design->modules_[trg_name]); + done[cell->type] = trg_name; + } + + cell->type = done.at(cell->type); + } + } + } + else if (copy_to_design != NULL) { if (!as_name.empty() && copy_src_modules.size() > 1) @@ -196,6 +296,7 @@ struct DesignPass : public Pass { if (copy_to_design->modules_.count(trg_name)) delete copy_to_design->modules_.at(trg_name); + copy_to_design->modules_[trg_name] = mod->clone(); copy_to_design->modules_[trg_name]->name = trg_name; copy_to_design->modules_[trg_name]->design = copy_to_design; @@ -235,19 +336,34 @@ struct DesignPass : public Pass { design->selection_stack.push_back(RTLIL::Selection()); } + if (reset_mode || reset_vlog_mode || !load_name.empty() || push_mode || pop_mode) + { + for (auto node : design->verilog_packages) + delete node; + design->verilog_packages.clear(); + + for (auto node : design->verilog_globals) + delete node; + design->verilog_globals.clear(); + + design->verilog_defines.clear(); + } + if (!load_name.empty() || pop_mode) { RTLIL::Design *saved_design = pop_mode ? pushed_designs.back() : saved_designs.at(load_name); - if (pop_mode) - pushed_designs.pop_back(); - for (auto &it : saved_design->modules_) design->add(it.second->clone()); design->selection_stack = saved_design->selection_stack; design->selection_vars = saved_design->selection_vars; design->selected_active_module = saved_design->selected_active_module; + + if (pop_mode) { + delete saved_design; + pushed_designs.pop_back(); + } } } } DesignPass; diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc index 7b75a009..58ed6457 100644 --- a/passes/cmds/edgetypes.cc +++ b/passes/cmds/edgetypes.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct EdgetypePass : public Pass { EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -35,7 +35,7 @@ struct EdgetypePass : public Pass { log("is a 4-tuple of source and sink cell type and port name.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc index 85386f3d..522e1089 100644 --- a/passes/cmds/logcmd.cc +++ b/passes/cmds/logcmd.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct LogPass : public Pass { LogPass() : Pass("log", "print text and log files") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -52,7 +52,7 @@ struct LogPass : public Pass { log(" do not append a newline\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design*) + void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE { size_t argidx; bool to_stdout = false; diff --git a/passes/cmds/ltp.cc b/passes/cmds/ltp.cc new file mode 100644 index 00000000..05701710 --- /dev/null +++ b/passes/cmds/ltp.cc @@ -0,0 +1,185 @@ +/* + * 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/celltypes.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct LtpWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap sigmap; + + dict<SigBit, tuple<int, SigBit, Cell*>> bits; + dict<SigBit, dict<SigBit, Cell*>> bit2bits; + dict<SigBit, tuple<SigBit, Cell*>> bit2ff; + + int maxlvl; + SigBit maxbit; + pool<SigBit> busy; + + LtpWorker(RTLIL::Module *module, bool noff) : design(module->design), module(module), sigmap(module) + { + CellTypes ff_celltypes; + + if (noff) { + ff_celltypes.setup_internals_mem(); + ff_celltypes.setup_stdcells_mem(); + } + + for (auto wire : module->selected_wires()) + for (auto bit : sigmap(wire)) + bits[bit] = tuple<int, SigBit, Cell*>(-1, State::Sx, nullptr); + + for (auto cell : module->selected_cells()) + { + pool<SigBit> src_bits, dst_bits; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) { + if (cell->input(conn.first)) + src_bits.insert(bit); + if (cell->output(conn.first)) + dst_bits.insert(bit); + } + + if (noff && ff_celltypes.cell_known(cell->type)) { + for (auto s : src_bits) + for (auto d : dst_bits) { + bit2ff[s] = tuple<SigBit, Cell*>(d, cell); + break; + } + continue; + } + + for (auto s : src_bits) + for (auto d : dst_bits) + bit2bits[s][d] = cell; + } + + maxlvl = -1; + maxbit = State::Sx; + } + + void runner(SigBit bit, int level, SigBit from, Cell *via) + { + auto &bitinfo = bits.at(bit); + + if (get<0>(bitinfo) >= level) + return; + + if (busy.count(bit) > 0) { + log_warning("Detected loop at %s in %s\n", log_signal(bit), log_id(module)); + return; + } + + busy.insert(bit); + get<0>(bitinfo) = level; + get<1>(bitinfo) = from; + get<2>(bitinfo) = via; + + if (level > maxlvl) { + maxlvl = level; + maxbit = bit; + } + + if (bit2bits.count(bit)) { + for (auto &it : bit2bits.at(bit)) + runner(it.first, level+1, bit, it.second); + } + + busy.erase(bit); + } + + void printpath(SigBit bit) + { + auto &bitinfo = bits.at(bit); + if (get<2>(bitinfo)) { + printpath(get<1>(bitinfo)); + log("%5d: %s (via %s)\n", get<0>(bitinfo), log_signal(bit), log_id(get<2>(bitinfo))); + } else { + log("%5d: %s\n", get<0>(bitinfo), log_signal(bit)); + } + } + + void run() + { + for (auto &it : bits) + if (get<0>(it.second) < 0) + runner(it.first, 0, State::Sx, nullptr); + + log("\n"); + log("Longest topological path in %s (length=%d):\n", log_id(module), maxlvl); + + if (maxlvl >= 0) + printpath(maxbit); + + if (bit2ff.count(maxbit)) + log("%5s: %s (via %s)\n", "ff", log_signal(get<0>(bit2ff.at(maxbit))), log_id(get<1>(bit2ff.at(maxbit)))); + } +}; + +struct LtpPass : public Pass { + LtpPass() : Pass("ltp", "print longest topological path") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ltp [options] [selection]\n"); + log("\n"); + log("This command prints the longest topological path in the design. (Only considers\n"); + log("paths within a single module, so the design must be flattened.)\n"); + log("\n"); + log(" -noff\n"); + log(" automatically exclude FF cell types\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + bool noff = false; + + log_header(design, "Executing LTP pass (find longest path).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-noff") { + noff = true; + continue; + } + break; + } + + extra_args(args, argidx, design); + + for (Module *module : design->selected_modules()) + { + if (module->has_processes_warn()) + continue; + + LtpWorker worker(module, noff); + worker.run(); + } + } +} LtpPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index 828c671d..aa6d5b6c 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -58,7 +58,7 @@ void load_plugin(std::string, std::vector<std::string>) struct PluginPass : public Pass { PluginPass() : Pass("plugin", "load and list loaded plugins") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -76,7 +76,7 @@ struct PluginPass : public Pass { log(" List loaded plugins\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string plugin_filename; std::vector<std::string> plugin_aliases; diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc index 1b800b6d..1c64a7b7 100644 --- a/passes/cmds/qwp.cc +++ b/passes/cmds/qwp.cc @@ -778,7 +778,7 @@ struct QwpWorker struct QwpPass : public Pass { QwpPass() : Pass("qwp", "quadratic wirelength placer") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -808,7 +808,7 @@ struct QwpPass : public Pass { log("dense matrix operations. It is only a toy-placer for small circuits.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { QwpConfig config; xorshift32_state = 123456789; @@ -835,6 +835,7 @@ struct QwpPass : public Pass { } if (args[argidx] == "-dump" && argidx+1 < args.size()) { config.dump_file.open(args[++argidx], std::ofstream::trunc); + yosys_output_files.insert(args[argidx]); continue; } break; diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 6a002869..dce576fd 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -54,7 +54,7 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std:: struct RenamePass : public Pass { RenamePass() : Pass("rename", "rename object in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -81,7 +81,7 @@ struct RenamePass : public Pass { log("Rename top module.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string pattern_prefix = "_", pattern_suffix = "_"; bool flag_enumerate = false; diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index f083e1f6..7123ba9f 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct ScatterPass : public Pass { ScatterPass() : Pass("scatter", "add additional intermediate nets") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -41,7 +41,7 @@ struct ScatterPass : public Pass { log("Use the opt_clean command to get rid of the additional nets.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { CellTypes ct(design); extra_args(args, 1, design); diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index bb6d7447..99f4fbae 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -163,16 +163,8 @@ struct SccWorker } for (auto cell : workQueue) - cellToNextCell[cell] = sigToNextCells.find(cellToNextSig[cell]); - - labelCounter = 0; - cellLabels.clear(); - - while (workQueue.size() > 0) { - RTLIL::Cell *cell = *workQueue.begin(); - log_assert(cellStack.size() == 0); - cellDepth.clear(); + cellToNextCell[cell] = sigToNextCells.find(cellToNextSig[cell]); if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { log("Found an SCC:"); @@ -183,6 +175,16 @@ struct SccWorker sccList.push_back(scc); log("\n"); } + } + + labelCounter = 0; + cellLabels.clear(); + + while (!workQueue.empty()) + { + RTLIL::Cell *cell = *workQueue.begin(); + log_assert(cellStack.size() == 0); + cellDepth.clear(); run(cell, 0, maxDepth); } @@ -216,7 +218,7 @@ struct SccWorker struct SccPass : public Pass { SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -244,20 +246,18 @@ struct SccPass : public Pass { log(" are assumed to be bidirectional 'inout' ports.\n"); log("\n"); log(" -set_attr <name> <value>\n"); - log(" -set_cell_attr <name> <value>\n"); - log(" -set_wire_attr <name> <value>\n"); - log(" set the specified attribute on all cells and/or wires that are part of\n"); - log(" a logic loop. the special token {} in the value is replaced with a\n"); - log(" unique identifier for the logic loop.\n"); + log(" set the specified attribute on all cells that are part of a logic\n"); + log(" loop. the special token {} in the value is replaced with a unique\n"); + log(" identifier for the logic loop.\n"); log("\n"); log(" -select\n"); log(" replace the current selection with a selection of all cells and wires\n"); log(" that are part of a found logic loop\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - std::map<std::string, std::string> setCellAttr, setWireAttr; + std::map<std::string, std::string> setAttr; bool allCellTypes = false; bool selectMode = false; bool nofeedbackMode = false; @@ -285,18 +285,7 @@ struct SccPass : public Pass { continue; } if (args[argidx] == "-set_attr" && argidx+2 < args.size()) { - setCellAttr[args[argidx+1]] = args[argidx+2]; - setWireAttr[args[argidx+1]] = args[argidx+2]; - argidx += 2; - continue; - } - if (args[argidx] == "-set_cell_attr" && argidx+2 < args.size()) { - setCellAttr[args[argidx+1]] = args[argidx+2]; - argidx += 2; - continue; - } - if (args[argidx] == "-set_wire_attr" && argidx+2 < args.size()) { - setWireAttr[args[argidx+1]] = args[argidx+2]; + setAttr[args[argidx+1]] = args[argidx+2]; argidx += 2; continue; } @@ -309,9 +298,6 @@ struct SccPass : public Pass { int origSelectPos = design->selection_stack.size() - 1; extra_args(args, argidx, design); - if (setCellAttr.size() > 0 || setWireAttr.size() > 0) - log_cmd_error("The -set*_attr options are not implemented at the moment!\n"); - RTLIL::Selection newSelection(false); int scc_counter = 0; @@ -319,7 +305,33 @@ struct SccPass : public Pass { if (design->selected(mod_it.second)) { SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth); - scc_counter += GetSize(worker.sccList); + + if (!setAttr.empty()) + { + for (const auto &cells : worker.sccList) + { + for (auto attr : setAttr) + { + IdString attr_name(RTLIL::escape_id(attr.first)); + string attr_valstr = attr.second; + string index = stringf("%d", scc_counter); + + for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size()) + attr_valstr.replace(pos, 2, index); + + Const attr_value(attr_valstr); + + for (auto cell : cells) + cell->attributes[attr_name] = attr_value; + } + + scc_counter++; + } + } + else + { + scc_counter += GetSize(worker.sccList); + } if (selectMode) worker.select(newSelection); diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index d2e1a2e2..d97aa2b3 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -760,6 +760,9 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (!design->selected_active_module.empty()) { arg_mod = design->selected_active_module; arg_memb = arg; + } else + if (GetSize(arg) >= 2 && arg[0] >= 'a' && arg[0] <= 'z' && arg[1] == ':') { + arg_mod = "*", arg_memb = arg; } else { size_t pos = arg.find('/'); if (pos == std::string::npos) { @@ -947,7 +950,7 @@ PRIVATE_NAMESPACE_BEGIN struct SelectPass : public Pass { SelectPass() : Pass("select", "modify and view the list of selected objects") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -961,7 +964,7 @@ struct SelectPass : public Pass { log("list of selected objects.\n"); log("\n"); log("Note that many commands support an optional [selection] argument that can be\n"); - log("used to override the global selection for the command. The syntax of this\n"); + log("used to YS_OVERRIDE the global selection for the command. The syntax of this\n"); log("optional argument is identical to the syntax of the <selection> argument\n"); log("described here.\n"); log("\n"); @@ -1164,7 +1167,7 @@ struct SelectPass : public Pass { log(" select */t:SWITCH %%x:+[GATE] */t:SWITCH %%d\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool add_mode = false; bool del_mode = false; @@ -1263,6 +1266,7 @@ struct SelectPass : public Pass { log_cmd_error("Option -read can not be combined with a selection expression.\n"); std::ifstream f(read_file); + yosys_input_files.insert(read_file); if (f.fail()) log_error("Can't open '%s' for reading: %s\n", read_file.c_str(), strerror(errno)); @@ -1331,6 +1335,7 @@ struct SelectPass : public Pass { FILE *f = NULL; if (!write_file.empty()) { f = fopen(write_file.c_str(), "w"); + yosys_output_files.insert(write_file); if (f == NULL) log_error("Can't open '%s' for writing: %s\n", write_file.c_str(), strerror(errno)); } @@ -1465,7 +1470,7 @@ struct SelectPass : public Pass { struct CdPass : public Pass { CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1482,17 +1487,53 @@ struct CdPass : public Pass { log("\n"); log(" cd ..\n"); log("\n"); + log("Remove trailing substrings that start with '.' in current module name until\n"); + log("the name of a module in the current design is generated, then switch to that\n"); + log("module. Otherwise clear the current selection.\n"); + log("\n"); + log(" cd\n"); + log("\n"); log("This is just a shortcut for 'select -clear'.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - if (args.size() != 2) + if (args.size() != 1 && args.size() != 2) log_cmd_error("Invalid number of arguments.\n"); - if (args[1] == "..") { + if (args.size() == 1 || args[1] == "/") { + design->selection_stack.back() = RTLIL::Selection(true); + design->selected_active_module = std::string(); + return; + } + + if (args[1] == "..") + { + string modname = design->selected_active_module; + design->selection_stack.back() = RTLIL::Selection(true); design->selected_active_module = std::string(); + + while (1) + { + size_t pos = modname.rfind('.'); + + if (pos == string::npos) + break; + + modname = modname.substr(0, pos); + Module *mod = design->module(modname); + + if (mod == nullptr) + continue; + + design->selected_active_module = modname; + design->selection_stack.back() = RTLIL::Selection(); + select_filter_active_mod(design, design->selection_stack.back()); + design->selection_stack.back().optimize(design); + return; + } + return; } @@ -1537,7 +1578,7 @@ static void log_matches(const char *title, Module *module, T list) struct LsPass : public Pass { LsPass() : Pass("ls", "list modules or objects in modules") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1548,7 +1589,7 @@ struct LsPass : public Pass { log("When an active module is selected, this prints a list of objects in the module.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { size_t argidx = 1; extra_args(args, argidx, design); diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 689e3148..d38a6b3d 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -56,7 +56,7 @@ static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::v struct SetattrPass : public Pass { SetattrPass() : Pass("setattr", "set/unset attributes on objects") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -69,7 +69,7 @@ struct SetattrPass : public Pass { log("instead of objects within modules.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<setunset_t> setunset_list; bool flag_mod = false; @@ -130,7 +130,7 @@ struct SetattrPass : public Pass { struct SetparamPass : public Pass { SetparamPass() : Pass("setparam", "set/unset parameters on objects") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -142,7 +142,7 @@ struct SetparamPass : public Pass { log("The -type option can be used to change the cell type of the selected cells.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { vector<setunset_t> setunset_list; string new_cell_type; @@ -188,7 +188,7 @@ struct SetparamPass : public Pass { struct ChparamPass : public Pass { ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -203,7 +203,7 @@ struct ChparamPass : public Pass { log("List the available parameters of the selected modules.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<setunset_t> setunset_list; dict<RTLIL::IdString, RTLIL::Const> new_parameters; diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index 26b2eb87..62d940ce 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -23,33 +23,82 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +#define MODE_ZERO 0 +#define MODE_ONE 1 +#define MODE_UNDEF 2 +#define MODE_RANDOM 3 +#define MODE_ANYSEQ 4 +#define MODE_ANYCONST 5 + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +static RTLIL::Wire * add_wire(RTLIL::Module *module, std::string name, int width, bool flag_input, bool flag_output) +{ + RTLIL::Wire *wire = NULL; + name = RTLIL::escape_id(name); + + if (module->count_id(name) != 0) + { + log("Module %s already has such an object %s.\n", module->name.c_str(), name.c_str()); + name += "$"; + return add_wire(module, name, width, flag_input, flag_output); + } + else + { + wire = module->addWire(name, width); + wire->port_input = flag_input; + wire->port_output = flag_output; + + if (flag_input || flag_output) { + wire->port_id = module->wires_.size(); + module->fixup_ports(); + } + + log("Added wire %s to module %s.\n", name.c_str(), module->name.c_str()); + } + + return wire; +} + struct SetundefWorker { int next_bit_mode; uint32_t next_bit_state; + vector<SigSpec*> siglist; RTLIL::State next_bit() { - if (next_bit_mode == 0) + if (next_bit_mode == MODE_ZERO) return RTLIL::State::S0; - if (next_bit_mode == 1) + if (next_bit_mode == MODE_ONE) return RTLIL::State::S1; - // xorshift32 - next_bit_state ^= next_bit_state << 13; - next_bit_state ^= next_bit_state >> 17; - next_bit_state ^= next_bit_state << 5; - log_assert(next_bit_state != 0); + if (next_bit_mode == MODE_UNDEF) + return RTLIL::State::Sx; + + if (next_bit_mode == MODE_RANDOM) + { + // xorshift32 + next_bit_state ^= next_bit_state << 13; + next_bit_state ^= next_bit_state >> 17; + next_bit_state ^= next_bit_state << 5; + log_assert(next_bit_state != 0); - return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1; + return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1; + } + + log_abort(); } void operator()(RTLIL::SigSpec &sig) { + if (next_bit_mode == MODE_ANYSEQ || next_bit_mode == MODE_ANYCONST) { + siglist.push_back(&sig); + return; + } + for (auto &bit : sig) if (bit.wire == NULL && bit.data > RTLIL::State::S1) bit = next_bit(); @@ -58,23 +107,35 @@ struct SetundefWorker struct SetundefPass : public Pass { SetundefPass() : Pass("setundef", "replace undef values with defined constants") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" setundef [options] [selection]\n"); log("\n"); - log("This command replaced undef (x) constants with defined (0/1) constants.\n"); + log("This command replaces undef (x) constants with defined (0/1) constants.\n"); log("\n"); log(" -undriven\n"); log(" also set undriven nets to constant values\n"); log("\n"); + log(" -expose\n"); + log(" also expose undriven nets as inputs (use with -undriven)\n"); + log("\n"); log(" -zero\n"); log(" replace with bits cleared (0)\n"); log("\n"); log(" -one\n"); log(" replace with bits set (1)\n"); log("\n"); + log(" -undef\n"); + log(" replace with undef (x) bits, may be used with -undriven\n"); + log("\n"); + log(" -anyseq\n"); + log(" replace with $anyseq drivers (for formal)\n"); + log("\n"); + log(" -anyconst\n"); + log(" replace with $anyconst drivers (for formal)\n"); + log("\n"); log(" -random <seed>\n"); log(" replace with random bits using the specified integer als seed\n"); log(" value for the random number generator.\n"); @@ -83,13 +144,16 @@ struct SetundefPass : public Pass { log(" also create/update init values for flip-flops\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool got_value = false; bool undriven_mode = false; + bool expose_mode = false; bool init_mode = false; SetundefWorker worker; + log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -97,14 +161,39 @@ struct SetundefPass : public Pass { undriven_mode = true; continue; } + if (args[argidx] == "-expose") { + got_value = true; + expose_mode = true; + continue; + } if (args[argidx] == "-zero") { got_value = true; - worker.next_bit_mode = 0; + worker.next_bit_mode = MODE_ZERO; + worker.next_bit_state = 0; continue; } if (args[argidx] == "-one") { got_value = true; - worker.next_bit_mode = 1; + worker.next_bit_mode = MODE_ONE; + worker.next_bit_state = 0; + continue; + } + if (args[argidx] == "-anyseq") { + got_value = true; + worker.next_bit_mode = MODE_ANYSEQ; + worker.next_bit_state = 0; + continue; + } + if (args[argidx] == "-anyconst") { + got_value = true; + worker.next_bit_mode = MODE_ANYCONST; + worker.next_bit_state = 0; + continue; + } + if (args[argidx] == "-undef") { + got_value = true; + worker.next_bit_mode = MODE_UNDEF; + worker.next_bit_state = 0; continue; } if (args[argidx] == "-init") { @@ -113,7 +202,7 @@ struct SetundefPass : public Pass { } if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { got_value = true; - worker.next_bit_mode = 2; + worker.next_bit_mode = MODE_RANDOM; worker.next_bit_state = atoi(args[++argidx].c_str()) + 1; for (int i = 0; i < 10; i++) worker.next_bit(); @@ -123,8 +212,13 @@ struct SetundefPass : public Pass { } extra_args(args, argidx, design); + if (expose_mode && !undriven_mode) + log_cmd_error("Option -expose must be used with option -undriven.\n"); if (!got_value) - log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n"); + log_cmd_error("One of the options -zero, -one, -anyseq, -anyconst, or -random <seed> must be specified.\n"); + + if (init_mode && (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST)) + log_cmd_error("The options -init and -anyseq / -anyconst are exclusive.\n"); for (auto module : design->selected_modules()) { @@ -133,25 +227,103 @@ struct SetundefPass : public Pass { if (!module->processes.empty()) log_error("The 'setundef' command can't operate in -undriven mode on modules with processes. Run 'proc' first.\n"); - SigMap sigmap(module); - SigPool undriven_signals; + if (expose_mode) + { + SigMap sigmap(module); + dict<SigBit, bool> wire_drivers; + pool<SigBit> used_wires; + SigPool undriven_signals; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + SigSpec sig = sigmap(conn.second); + if (cell->input(conn.first)) + for (auto bit : sig) + if (bit.wire) + used_wires.insert(bit); + if (cell->output(conn.first)) + for (int i = 0; i < GetSize(sig); i++) + if (sig[i].wire) + wire_drivers[sig[i]] = true; + } + + for (auto wire : module->wires()) { + if (wire->port_input) { + SigSpec sig = sigmap(wire); + for (int i = 0; i < GetSize(sig); i++) + wire_drivers[sig[i]] = true; + } + if (wire->port_output) { + SigSpec sig = sigmap(wire); + for (auto bit : sig) + if (bit.wire) + used_wires.insert(bit); + } + } + + pool<RTLIL::Wire*> undriven_wires; + for (auto bit : used_wires) + if (!wire_drivers.count(bit)) + undriven_wires.insert(bit.wire); + + for (auto &it : undriven_wires) + undriven_signals.add(sigmap(it)); + + for (auto &it : undriven_wires) + if (it->port_input) + undriven_signals.del(sigmap(it)); + + CellTypes ct(design); + for (auto &it : module->cells_) + for (auto &conn : it.second->connections()) + if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) + undriven_signals.del(sigmap(conn.second)); + + RTLIL::SigSpec sig = undriven_signals.export_all(); + for (auto &c : sig.chunks()) { + RTLIL::Wire * wire; + if (c.wire->width == c.width) { + wire = c.wire; + wire->port_input = true; + } else { + string name = c.wire->name.str() + "$[" + std::to_string(c.width + c.offset) + ":" + std::to_string(c.offset) + "]"; + wire = add_wire(module, name, c.width, true, false); + module->connect(RTLIL::SigSig(c, wire)); + } + log("Exposing undriven wire %s as input.\n", wire->name.c_str()); + } + module->fixup_ports(); + } + else + { + SigMap sigmap(module); + SigPool undriven_signals; - for (auto &it : module->wires_) - if (!it.second->port_input) + for (auto &it : module->wires_) undriven_signals.add(sigmap(it.second)); - CellTypes ct(design); - for (auto &it : module->cells_) - for (auto &conn : it.second->connections()) - if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) - undriven_signals.del(sigmap(conn.second)); - - RTLIL::SigSpec sig = undriven_signals.export_all(); - for (auto &c : sig.chunks()) { - RTLIL::SigSpec bits; - for (int i = 0; i < c.width; i++) - bits.append(worker.next_bit()); - module->connect(RTLIL::SigSig(c, bits)); + for (auto &it : module->wires_) + if (it.second->port_input) + undriven_signals.del(sigmap(it.second)); + + CellTypes ct(design); + for (auto &it : module->cells_) + for (auto &conn : it.second->connections()) + if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) + undriven_signals.del(sigmap(conn.second)); + + RTLIL::SigSpec sig = undriven_signals.export_all(); + for (auto &c : sig.chunks()) { + RTLIL::SigSpec bits; + if (worker.next_bit_mode == MODE_ANYSEQ) + bits = module->Anyseq(NEW_ID, c.width); + else if (worker.next_bit_mode == MODE_ANYCONST) + bits = module->Anyconst(NEW_ID, c.width); + else + for (int i = 0; i < c.width; i++) + bits.append(worker.next_bit()); + module->connect(RTLIL::SigSig(c, bits)); + } } } @@ -236,6 +408,35 @@ struct SetundefPass : public Pass { } module->rewrite_sigspecs(worker); + + if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST) + { + vector<SigSpec*> siglist; + siglist.swap(worker.siglist); + + for (auto sigptr : siglist) + { + SigSpec &sig = *sigptr; + int cursor = 0; + + while (cursor < GetSize(sig)) + { + int width = 0; + while (cursor+width < GetSize(sig) && sig[cursor+width] == State::Sx) + width++; + + if (width > 0) { + if (worker.next_bit_mode == MODE_ANYSEQ) + sig.replace(cursor, module->Anyseq(NEW_ID, width)); + else + sig.replace(cursor, module->Anyconst(NEW_ID, width)); + cursor += width; + } else { + cursor++; + } + } + } + } } } } SetundefPass; diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 3a3939a8..a4887324 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -30,6 +30,10 @@ # include <readline/readline.h> #endif +#ifdef YOSYS_ENABLE_EDITLINE +# include <editline/readline.h> +#endif + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -569,7 +573,7 @@ struct ShowWorker struct ShowPass : public Pass { ShowPass() : Pass("show", "generate schematics using graphviz") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -580,6 +584,7 @@ struct ShowPass : public Pass { log("\n"); log(" -viewer <viewer>\n"); log(" Run the specified command with the graphics file as parameter.\n"); + log(" On Windows, this pauses yosys until the viewer exits.\n"); log("\n"); log(" -format <format>\n"); log(" Generate a graphics file in the specified format. Use 'dot' to just\n"); @@ -641,7 +646,7 @@ struct ShowPass : public Pass { log(" do not add the module name as graph title to the dot file\n"); log("\n"); log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n"); - log("specified, 'xdot' is used to display the schematic.\n"); + log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n"); log("\n"); log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n"); log("unless another prefix is specified using -prefix <prefix>.\n"); @@ -651,7 +656,7 @@ struct ShowPass : public Pass { log("the 'show' command is executed.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Generating Graphviz representation of design.\n"); log_push(); @@ -677,6 +682,7 @@ struct ShowPass : public Pass { bool flag_enum = false; bool flag_abbreviate = true; bool flag_notitle = false; + bool custom_prefix = false; RTLIL::IdString colorattr; size_t argidx; @@ -693,6 +699,7 @@ struct ShowPass : public Pass { } if (arg == "-prefix" && argidx+1 < args.size()) { prefix = args[++argidx]; + custom_prefix = true; continue; } if (arg == "-color" && argidx+2 < args.size()) { @@ -778,6 +785,7 @@ struct ShowPass : public Pass { for (auto filename : libfiles) { std::ifstream f; f.open(filename.c_str()); + yosys_input_files.insert(filename); if (f.fail()) log_error("Can't open lib file `%s'.\n", filename.c_str()); RTLIL::Design *lib = new RTLIL::Design; @@ -793,6 +801,8 @@ struct ShowPass : public Pass { log("Writing dot description to `%s'.\n", dot_file.c_str()); FILE *f = fopen(dot_file.c_str(), "w"); + if (custom_prefix) + yosys_output_files.insert(dot_file); if (f == NULL) { for (auto lib : libs) delete lib; @@ -808,14 +818,30 @@ struct ShowPass : public Pass { log_cmd_error("Nothing there to show.\n"); if (format != "dot" && !format.empty()) { - std::string cmd = stringf("dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'", format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str()); + #ifdef _WIN32 + // system()/cmd.exe does not understand single quotes on Windows. + #define DOT_CMD "dot -T%s \"%s\" > \"%s.new\" && move \"%s.new\" \"%s\"" + #else + #define DOT_CMD "dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'" + #endif + std::string cmd = stringf(DOT_CMD, format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str()); + #undef DOT_CMD log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); } if (!viewer_exe.empty()) { - std::string cmd = stringf("%s '%s' &", viewer_exe.c_str(), out_file.c_str()); + #ifdef _WIN32 + // system()/cmd.exe does not understand single quotes nor + // background tasks on Windows. So we have to pause yosys + // until the viewer exits. + #define VIEW_CMD "%s \"%s\"" + #else + #define VIEW_CMD "%s '%s' &" + #endif + std::string cmd = stringf(VIEW_CMD, viewer_exe.c_str(), out_file.c_str()); + #undef VIEW_CMD log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 7418ec4d..bafafca4 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -247,7 +247,7 @@ struct SpliceWorker struct SplicePass : public Pass { SplicePass() : Pass("splice", "create explicit splicing cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -288,7 +288,7 @@ struct SplicePass : public Pass { log("by selected wires are rewired.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool sel_by_cell = false; bool sel_by_wire = false; diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index 14eeb066..f5a1f17b 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -37,14 +37,20 @@ struct SplitnetsWorker new_wire_name += format.substr(0, 1); if (width > 1) { - new_wire_name += stringf("%d", offset+width-1); + if (wire->upto) + new_wire_name += stringf("%d", wire->start_offset+wire->width-(offset+width)-1); + else + new_wire_name += stringf("%d", wire->start_offset+offset+width-1); if (format.size() > 2) new_wire_name += format.substr(2, 1); else new_wire_name += ":"; } - new_wire_name += stringf("%d", offset); + if (wire->upto) + new_wire_name += stringf("%d", wire->start_offset+wire->width-offset-1); + else + new_wire_name += stringf("%d", wire->start_offset+offset); if (format.size() > 1) new_wire_name += format.substr(1, 1); @@ -81,7 +87,7 @@ struct SplitnetsWorker struct SplitnetsPass : public Pass { SplitnetsPass() : Pass("splitnets", "split up multi-bit nets") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -103,7 +109,7 @@ struct SplitnetsPass : public Pass { log(" and split nets so that no driver drives only part of a net.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool flag_ports = false; bool flag_driver = false; diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 362a0edf..54f4ea81 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -112,7 +112,7 @@ struct statdata_t "$lut", "$and", "$or", "$xor", "$xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", - "$add", "$sub", "$mul", "$div", "$mod", "$pow")) { + "$add", "$sub", "$mul", "$div", "$mod", "$pow", "$alu")) { int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0; int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0; int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0; @@ -142,7 +142,7 @@ struct statdata_t } } - void log_data() + void log_data(RTLIL::IdString mod_name, bool top_mod) { log(" Number of wires: %6d\n", num_wires); log(" Number of wire bits: %6d\n", num_wire_bits); @@ -163,7 +163,7 @@ struct statdata_t if (area != 0) { log("\n"); - log(" Chip area for this module: %f\n", area); + log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area); } } }; @@ -190,6 +190,7 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil { std::ifstream f; f.open(liberty_file.c_str()); + yosys_input_files.insert(liberty_file); if (f.fail()) log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); LibertyParser libparser(f); @@ -208,7 +209,7 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil struct StatPass : public Pass { StatPass() : Pass("stat", "print some statistics") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -230,7 +231,7 @@ struct StatPass : public Pass { log(" e.g. $add_8 for an 8 bit wide $add cell.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Printing statistics.\n"); @@ -274,7 +275,7 @@ struct StatPass : public Pass { log("\n"); log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); log("\n"); - data.log_data(); + data.log_data(mod->name, false); } if (top_mod != NULL && GetSize(mod_stat) > 1) @@ -287,7 +288,7 @@ struct StatPass : public Pass { statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0); log("\n"); - data.log_data(); + data.log_data(top_mod->name, true); } log("\n"); diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index 3db2dbf0..ff80f385 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct TeePass : public Pass { TeePass() : Pass("tee", "redirect command output to file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -49,7 +49,7 @@ struct TeePass : public Pass { log(" Add/subract INT from the -v setting for this command.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<FILE*> backup_log_files, files_to_close; int backup_log_verbose_level = log_verbose_level; @@ -65,6 +65,7 @@ struct TeePass : public Pass { if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) { const char *open_mode = args[argidx] == "-o" ? "w" : "a+"; FILE *f = fopen(args[++argidx].c_str(), open_mode); + yosys_input_files.insert(args[argidx]); if (f == NULL) { for (auto cf : files_to_close) fclose(cf); diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc index 56223610..3c0eac8d 100644 --- a/passes/cmds/torder.cc +++ b/passes/cmds/torder.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct TorderPass : public Pass { TorderPass() : Pass("torder", "print cells in topological order") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -43,7 +43,7 @@ struct TorderPass : public Pass { log(" are not used in topological sorting. this option deactivates that.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool noautostop = false; dict<IdString, pool<IdString>> stop_db; diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc index 1a5f873f..f5305cde 100644 --- a/passes/cmds/trace.cc +++ b/passes/cmds/trace.cc @@ -25,34 +25,34 @@ PRIVATE_NAMESPACE_BEGIN struct TraceMonitor : public RTLIL::Monitor { - virtual void notify_module_add(RTLIL::Module *module) YS_OVERRIDE + void notify_module_add(RTLIL::Module *module) YS_OVERRIDE { log("#TRACE# Module add: %s\n", log_id(module)); } - virtual void notify_module_del(RTLIL::Module *module) YS_OVERRIDE + void notify_module_del(RTLIL::Module *module) YS_OVERRIDE { log("#TRACE# Module delete: %s\n", log_id(module)); } - virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE + void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE { log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", log_id(cell->module), log_id(cell), log_id(port), log_signal(sig), log_signal(old_sig)); } - virtual void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_OVERRIDE + void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_OVERRIDE { log("#TRACE# Connection in module %s: %s = %s\n", log_id(module), log_signal(sigsig.first), log_signal(sigsig.second)); } - virtual void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_OVERRIDE + void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_OVERRIDE { log("#TRACE# New connections in module %s:\n", log_id(module)); for (auto &sigsig : sigsig_vec) log("## %s = %s\n", log_signal(sigsig.first), log_signal(sigsig.second)); } - virtual void notify_blackout(RTLIL::Module *module) YS_OVERRIDE + void notify_blackout(RTLIL::Module *module) YS_OVERRIDE { log("#TRACE# Blackout in module %s:\n", log_id(module)); } @@ -60,7 +60,7 @@ struct TraceMonitor : public RTLIL::Monitor struct TracePass : public Pass { TracePass() : Pass("trace", "redirect command output to file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -70,7 +70,7 @@ struct TracePass : public Pass { log("the design in real time.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -95,4 +95,3 @@ struct TracePass : public Pass { } TracePass; PRIVATE_NAMESPACE_END - diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index b7826593..9613b462 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct WriteFileFrontend : public Frontend { WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -44,7 +44,7 @@ struct WriteFileFrontend : public Frontend { log(" EOT\n"); log("\n"); } - virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*) + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE { bool append_mode = false; std::string output_filename; @@ -67,6 +67,7 @@ struct WriteFileFrontend : public Frontend { extra_args(f, filename, args, argidx); FILE *of = fopen(output_filename.c_str(), append_mode ? "a" : "w"); + yosys_output_files.insert(output_filename); char buffer[64 * 1024]; int bytes; diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc index 0494a724..71599f46 100644 --- a/passes/equiv/equiv_add.cc +++ b/passes/equiv/equiv_add.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct EquivAddPass : public Pass { EquivAddPass() : Pass("equiv_add", "add a $equiv cell") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -39,7 +39,7 @@ struct EquivAddPass : public Pass { log("This command adds $equiv cells for the ports of the specified cells.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { bool try_mode = false; diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index c958c3de..bcc68d6d 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -162,7 +162,7 @@ struct EquivInductWorker struct EquivInductPass : public Pass { EquivInductPass() : Pass("equiv_induct", "proving $equiv cells using temporal induction") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -192,7 +192,7 @@ struct EquivInductPass : public Pass { log("after reset.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { int success_counter = 0; bool model_undef = false; diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index 40ca4262..b1f88d55 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -260,11 +260,11 @@ struct EquivMakeWorker for (int i = 0; i < wire->width; i++) { if (undriven_bits.count(assign_map(SigBit(gold_wire, i)))) { - log(" Skipping signal bit %d: undriven on gold side.\n", i); + log(" Skipping signal bit %s [%d]: undriven on gold side.\n", id2cstr(gold_wire->name), i); continue; } if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) { - log(" Skipping signal bit %d: undriven on gate side.\n", i); + log(" Skipping signal bit %s [%d]: undriven on gate side.\n", id2cstr(gate_wire->name), i); continue; } equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); @@ -390,7 +390,7 @@ struct EquivMakeWorker struct EquivMakePass : public Pass { EquivMakePass() : Pass("equiv_make", "prepare a circuit for equivalence checking") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -415,7 +415,7 @@ struct EquivMakePass : public Pass { log("checking problem. Use 'miter -equiv' if you want to create a miter circuit.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { EquivMakeWorker worker; worker.ct.setup(design); diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc index 22c50176..135eaf14 100644 --- a/passes/equiv/equiv_mark.cc +++ b/passes/equiv/equiv_mark.cc @@ -204,7 +204,7 @@ struct EquivMarkWorker struct EquivMarkPass : public Pass { EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -216,7 +216,7 @@ struct EquivMarkPass : public Pass { log("wires and cells.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { log_header(design, "Executing EQUIV_MARK pass.\n"); diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index eb2e5a17..e06f9515 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -261,7 +261,7 @@ struct EquivMiterWorker struct EquivMiterPass : public Pass { EquivMiterPass() : Pass("equiv_miter", "extract miter from equiv circuit") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -282,7 +282,7 @@ struct EquivMiterPass : public Pass { log(" Create compare logic that handles undefs correctly\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { EquivMiterWorker worker; worker.ct.setup(design); diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc index 163b1009..18b3e7d3 100644 --- a/passes/equiv/equiv_purge.cc +++ b/passes/equiv/equiv_purge.cc @@ -80,7 +80,7 @@ struct EquivPurgeWorker Wire *wire = module->addWire(name, GetSize(sig)); wire->port_input = true; module->connect(sig, wire); - log(" Module input: %s\n", log_signal(wire)); + log(" Module input: %s (%s)\n", log_signal(wire), log_signal(sig)); return module->addWire(NEW_ID, GetSize(sig)); } } @@ -142,7 +142,7 @@ struct EquivPurgeWorker for (auto bit : queue) visited.insert(bit); - + for (auto bit : queue) { auto &cells = up_bit2cells[bit]; @@ -176,7 +176,7 @@ struct EquivPurgeWorker struct EquivPurgePass : public Pass { EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -187,7 +187,7 @@ struct EquivPurgePass : public Pass { log("ports as needed.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { log_header(design, "Executing EQUIV_PURGE pass.\n"); diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc index 770497a5..c5c28c7d 100644 --- a/passes/equiv/equiv_remove.cc +++ b/passes/equiv/equiv_remove.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN struct EquivRemovePass : public Pass { EquivRemovePass() : Pass("equiv_remove", "remove $equiv cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -40,7 +40,7 @@ struct EquivRemovePass : public Pass { log(" keep gate circuit\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { bool mode_gold = false; bool mode_gate = false; diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 49963ed6..c2fab26f 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -35,13 +35,14 @@ struct EquivSimpleWorker ezSatPtr ez; SatGen satgen; int max_seq; + bool short_cones; bool verbose; pool<pair<Cell*, int>> imported_cells_cache; - EquivSimpleWorker(const vector<Cell*> &equiv_cells, SigMap &sigmap, dict<SigBit, Cell*> &bit2driver, int max_seq, bool verbose, bool model_undef) : + EquivSimpleWorker(const vector<Cell*> &equiv_cells, SigMap &sigmap, dict<SigBit, Cell*> &bit2driver, int max_seq, bool short_cones, bool verbose, bool model_undef) : module(equiv_cells.front()->module), equiv_cells(equiv_cells), equiv_cell(nullptr), - sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), verbose(verbose) + sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), short_cones(short_cones), verbose(verbose) { satgen.model_undef = model_undef; } @@ -59,7 +60,7 @@ struct EquivSimpleWorker for (auto &conn : cell->connections()) if (yosys_celltypes.cell_input(cell->type, conn.first)) for (auto bit : sigmap(conn.second)) { - if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_")) { + if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_")) { if (!conn.first.in("\\CLK", "\\C")) next_seed.insert(bit); } else @@ -142,22 +143,44 @@ struct EquivSimpleWorker pool<SigBit> short_bits_cone_a, short_bits_cone_b; pool<SigBit> input_bits; - for (auto bit_a : seed_a) - find_input_cone(next_seed_a, short_cells_cone_a, short_bits_cone_a, full_cells_cone_b, full_bits_cone_b, &input_bits, bit_a); - next_seed_a.swap(seed_a); + if (short_cones) + { + for (auto bit_a : seed_a) + find_input_cone(next_seed_a, short_cells_cone_a, short_bits_cone_a, full_cells_cone_b, full_bits_cone_b, &input_bits, bit_a); + next_seed_a.swap(seed_a); - for (auto bit_b : seed_b) - find_input_cone(next_seed_b, short_cells_cone_b, short_bits_cone_b, full_cells_cone_a, full_bits_cone_a, &input_bits, bit_b); - next_seed_b.swap(seed_b); + for (auto bit_b : seed_b) + find_input_cone(next_seed_b, short_cells_cone_b, short_bits_cone_b, full_cells_cone_a, full_bits_cone_a, &input_bits, bit_b); + next_seed_b.swap(seed_b); + } + else + { + short_cells_cone_a = full_cells_cone_a; + short_bits_cone_a = full_bits_cone_a; + next_seed_a.swap(seed_a); + + short_cells_cone_b = full_cells_cone_b; + short_bits_cone_b = full_bits_cone_b; + next_seed_b.swap(seed_b); + } pool<Cell*> problem_cells; problem_cells.insert(short_cells_cone_a.begin(), short_cells_cone_a.end()); problem_cells.insert(short_cells_cone_b.begin(), short_cells_cone_b.end()); if (verbose) + { log(" Adding %d new cells to the problem (%d A, %d B, %d shared).\n", GetSize(problem_cells), GetSize(short_cells_cone_a), GetSize(short_cells_cone_b), (GetSize(short_cells_cone_a) + GetSize(short_cells_cone_b)) - GetSize(problem_cells)); + #if 0 + for (auto cell : short_cells_cone_a) + log(" A-side cell: %s\n", log_id(cell)); + + for (auto cell : short_cells_cone_b) + log(" B-side cell: %s\n", log_id(cell)); + #endif + } for (auto cell : problem_cells) { auto key = pair<Cell*, int>(cell, step+1); @@ -250,7 +273,7 @@ struct EquivSimpleWorker struct EquivSimplePass : public Pass { EquivSimplePass() : Pass("equiv_simple", "try proving simple $equiv instances") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -264,6 +287,10 @@ struct EquivSimplePass : public Pass { log(" -undef\n"); log(" enable modelling of undef states\n"); log("\n"); + log(" -short\n"); + log(" create shorter input cones that stop at shared nodes. This yields\n"); + log(" simpler SAT problems but sometimes fails to prove equivalence.\n"); + log("\n"); log(" -nogroup\n"); log(" disabling grouping of $equiv cells by output wire\n"); log("\n"); @@ -271,9 +298,9 @@ struct EquivSimplePass : public Pass { log(" the max. number of time steps to be considered (default = 1)\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { - bool verbose = false, model_undef = false, nogroup = false; + bool verbose = false, short_cones = false, model_undef = false, nogroup = false; int success_counter = 0; int max_seq = 1; @@ -285,6 +312,10 @@ struct EquivSimplePass : public Pass { verbose = true; continue; } + if (args[argidx] == "-short") { + short_cones = true; + continue; + } if (args[argidx] == "-undef") { model_undef = true; continue; @@ -329,7 +360,7 @@ struct EquivSimplePass : public Pass { unproven_cells_counter, GetSize(unproven_equiv_cells), log_id(module)); for (auto cell : module->cells()) { - if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_")) + if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_")) continue; for (auto &conn : cell->connections()) if (yosys_celltypes.cell_output(cell->type, conn.first)) @@ -346,7 +377,7 @@ struct EquivSimplePass : public Pass { for (auto it2 : it.second) cells.push_back(it2.second); - EquivSimpleWorker worker(cells, sigmap, bit2driver, max_seq, verbose, model_undef); + EquivSimpleWorker worker(cells, sigmap, bit2driver, max_seq, short_cones, verbose, model_undef); success_counter += worker.run(); } } diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc index 7b9230b3..b4a93ccf 100644 --- a/passes/equiv/equiv_status.cc +++ b/passes/equiv/equiv_status.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN struct EquivStatusPass : public Pass { EquivStatusPass() : Pass("equiv_status", "print status of equivalent checking module") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -36,7 +36,7 @@ struct EquivStatusPass : public Pass { log(" produce an error if any unproven $equiv cell is found\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { bool assert_mode = false; int unproven_count = 0; diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc index c4ced6a7..a7973fd0 100644 --- a/passes/equiv/equiv_struct.cc +++ b/passes/equiv/equiv_struct.cc @@ -283,7 +283,7 @@ struct EquivStructWorker struct EquivStructPass : public Pass { EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -314,7 +314,7 @@ struct EquivStructPass : public Pass { log(" maximum number of iterations to run before aborting\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { pool<IdString> fwonly_cells({ "$equiv" }); bool mode_icells = false; diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc index 997558b8..c5cb338a 100644 --- a/passes/fsm/fsm.cc +++ b/passes/fsm/fsm.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct FsmPass : public Pass { FsmPass() : Pass("fsm", "extract and optimize finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -68,7 +68,7 @@ struct FsmPass : public Pass { log(" passed through to fsm_recode pass\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool flag_nomap = false; bool flag_norecode = false; diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index 6a560f16..fc504e98 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -180,7 +180,7 @@ static void detect_fsm(RTLIL::Wire *wire) for (auto &port_it : cell->connections()) if (cell->output(port_it.first)) { SigSpec sig = assign_map(port_it.second); - Const val(set_output ? State::S1 : State::S0, GetSize(sig)); + Const val(set_output ? State::S1 : State::S0, GetSize(sig)); ce.set(sig, val); } } @@ -215,7 +215,7 @@ static void detect_fsm(RTLIL::Wire *wire) for (auto w : warnings) warnmsg += " " + w; log_warning("%s", warnmsg.c_str()); } else { - log("FSM state register %s.%s already has fsm_encoding attribute.\n", log_id(wire->module), log_id(wire)); + log("FSM state register %s.%s already has fsm_encoding attribute.\n", log_id(wire->module), log_id(wire)); } } else @@ -245,7 +245,7 @@ static void detect_fsm(RTLIL::Wire *wire) struct FsmDetectPass : public Pass { FsmDetectPass() : Pass("fsm_detect", "finding FSMs in design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -261,7 +261,7 @@ struct FsmDetectPass : public Pass { log("'fsm_encoding' attribute to \"none\".\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n"); extra_args(args, 1, design); diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index e7b9dcf9..c34d0c15 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -54,13 +54,27 @@ struct FsmExpand if (cell->getPort("\\A").size() < 2) return true; + int in_bits = 0; RTLIL::SigSpec new_signals; - if (cell->hasPort("\\A")) + + if (cell->hasPort("\\A")) { + in_bits += GetSize(cell->getPort("\\A")); new_signals.append(assign_map(cell->getPort("\\A"))); - if (cell->hasPort("\\B")) + } + + if (cell->hasPort("\\B")) { + in_bits += GetSize(cell->getPort("\\B")); new_signals.append(assign_map(cell->getPort("\\B"))); - if (cell->hasPort("\\S")) + } + + if (cell->hasPort("\\S")) { + in_bits += GetSize(cell->getPort("\\S")); new_signals.append(assign_map(cell->getPort("\\S"))); + } + + if (in_bits > 8) + return false; + if (cell->hasPort("\\Y")) new_signals.append(assign_map(cell->getPort("\\Y"))); @@ -173,6 +187,16 @@ struct FsmExpand new_ctrl_out.append(output_sig); fsm_cell->setPort("\\CTRL_OUT", new_ctrl_out); + if (GetSize(input_sig) > 10) + log_warning("Cell %s.%s (%s) has %d input bits, merging into FSM %s.%s might be problematic.\n", + log_id(cell->module), log_id(cell), log_id(cell->type), + GetSize(input_sig), log_id(fsm_cell->module), log_id(fsm_cell)); + + if (GetSize(fsm_data.transition_table) > 10000) + log_warning("Transition table for FSM %s.%s already has %d rows, merging more cells " + "into this FSM might be problematic.\n", log_id(fsm_cell->module), log_id(fsm_cell), + GetSize(fsm_data.transition_table)); + std::vector<FsmData::transition_t> new_transition_table; for (auto &tr : fsm_data.transition_table) { for (int i = 0; i < (1 << input_sig.size()); i++) { @@ -241,7 +265,7 @@ struct FsmExpand struct FsmExpandPass : public Pass { FsmExpandPass() : Pass("fsm_expand", "expand FSM cells by merging logic into it") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -255,7 +279,7 @@ struct FsmExpandPass : public Pass { log("word-wide cells. Call with -full to consider all cells for merging.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool full_mode = false; diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index 1cbfcfae..8eb1872f 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -120,7 +120,7 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::st */ struct FsmExportPass : public Pass { FsmExportPass() : Pass("fsm_export", "exporting FSMs to KISS2 files") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -143,7 +143,7 @@ struct FsmExportPass : public Pass { log(" use binary state encoding as state names instead of s0, s1, ...\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { dict<RTLIL::IdString, RTLIL::Const>::iterator attr_it; std::string arg; diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 8a4ee3f2..67551f67 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -401,7 +401,7 @@ static void extract_fsm(RTLIL::Wire *wire) struct FsmExtractPass : public Pass { FsmExtractPass() : Pass("fsm_extract", "extracting FSMs in design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -417,7 +417,7 @@ struct FsmExtractPass : public Pass { log("'opt_clean' pass to eliminate this signal.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n"); extra_args(args, 1, design); diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc index 2cc1a7d5..0548259e 100644 --- a/passes/fsm/fsm_info.cc +++ b/passes/fsm/fsm_info.cc @@ -30,7 +30,7 @@ PRIVATE_NAMESPACE_BEGIN struct FsmInfoPass : public Pass { FsmInfoPass() : Pass("fsm_info", "print information on finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -41,7 +41,7 @@ struct FsmInfoPass : public Pass { log("pass so that this information is included in the synthesis log file.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); extra_args(args, 1, design); diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc index c4230375..90c95891 100644 --- a/passes/fsm/fsm_map.cc +++ b/passes/fsm/fsm_map.cc @@ -274,9 +274,6 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) { RTLIL::SigSpec sig_a(RTLIL::State::Sx, next_state_wire->width); RTLIL::SigSpec sig_b, sig_s; - int reset_state = fsm_data.reset_state; - if (reset_state < 0) - reset_state = 0; for (size_t i = 0; i < fsm_data.state_table.size(); i++) { RTLIL::Const state = fsm_data.state_table[i]; @@ -325,7 +322,7 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) struct FsmMapPass : public Pass { FsmMapPass() : Pass("fsm_map", "mapping FSMs to basic logic") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -334,7 +331,7 @@ struct FsmMapPass : public Pass { log("This pass translates FSM cells to flip-flops and logic.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); extra_args(args, 1, design); diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index 5b1da44f..3a6ac274 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -323,7 +323,7 @@ PRIVATE_NAMESPACE_BEGIN struct FsmOptPass : public Pass { FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -334,7 +334,7 @@ struct FsmOptPass : public Pass { log("combination with the 'opt_clean' pass (see also 'help fsm').\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n"); extra_args(args, 1, design); diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index e1bde728..fa1ff48c 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -126,7 +126,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs struct FsmRecodePass : public Pass { FsmRecodePass() : Pass("fsm_recode", "recoding finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -151,7 +151,7 @@ struct FsmRecodePass : public Pass { log(" .map <old_bitpattern> <new_bitpattern>\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { FILE *fm_set_fsm_file = NULL; FILE *encfile = NULL; diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index 1fb669c1..b3f139b7 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -1,5 +1,5 @@ OBJS += passes/hierarchy/hierarchy.o -OBJS += passes/hierarchy/singleton.o +OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index e21a7a4e..5df69848 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "frontends/verific/verific.h" #include <stdlib.h> #include <stdio.h> #include <set> @@ -138,7 +139,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, } } -bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, std::vector<std::string> &libdirs) +bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs) { bool did_something = false; std::map<RTLIL::Cell*, std::pair<int, int>> array_cells; @@ -173,24 +174,24 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check for (auto &dir : libdirs) { - filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".v"; - if (check_file_exists(filename)) { - std::vector<std::string> args; - args.push_back(filename); - Frontend::frontend_call(design, NULL, filename, "verilog"); - goto loaded_module; - } - - filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".il"; - if (check_file_exists(filename)) { - std::vector<std::string> args; - args.push_back(filename); - Frontend::frontend_call(design, NULL, filename, "ilang"); - goto loaded_module; + static const vector<pair<string, string>> extensions_list = + { + {".v", "verilog"}, + {".sv", "verilog -sv"}, + {".il", "ilang"} + }; + + for (auto &ext : extensions_list) + { + filename = dir + "/" + RTLIL::unescape_id(cell->type) + ext.first; + if (check_file_exists(filename)) { + Frontend::frontend_call(design, NULL, filename, ext.second); + goto loaded_module; + } } } - if (flag_check && cell->type[0] != '$') + if ((flag_check || flag_simcheck) && cell->type[0] != '$') log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n", cell->type.c_str(), module->name.c_str(), cell->name.c_str()); continue; @@ -200,7 +201,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str()); did_something = true; } else - if (flag_check) + if (flag_check || flag_simcheck) { RTLIL::Module *mod = design->module(cell->type); for (auto &conn : cell->connections()) @@ -213,15 +214,19 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first)); for (auto ¶m : cell->parameters) - if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$') + if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL) log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(param.first)); } - if (cell->parameters.size() == 0) + if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) { + if (flag_simcheck) + log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox module.\n", + cell->type.c_str(), module->name.c_str(), cell->name.c_str()); continue; + } - if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) + if (cell->parameters.size() == 0) continue; RTLIL::Module *mod = design->modules_[cell->type]; @@ -254,7 +259,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check if (mod->wires_.count(portname) == 0) log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); int port_size = mod->wires_.at(portname)->width; - if (conn_size == port_size) + if (conn_size == port_size || conn_size == 0) continue; if (conn_size != port_size*num) log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); @@ -317,7 +322,7 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod) if (cache.count(mod) == 0) for (auto c : mod->cells()) { RTLIL::Module *m = mod->design->module(c->type); - if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume")) + if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume", "$live", "$fair", "$cover")) return cache[mod] = true; } return cache[mod]; @@ -338,7 +343,7 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -354,6 +359,10 @@ struct HierarchyPass : public Pass { log(" also check the design hierarchy. this generates an error when\n"); log(" an unknown module is used as cell type.\n"); log("\n"); + log(" -simcheck\n"); + log(" like -check, but also thow an error if blackbox modules are\n"); + log(" instantiated, and throw an error if the design has no top module\n"); + log("\n"); log(" -purge_lib\n"); log(" by default the hierarchy command will not remove library (blackbox)\n"); log(" modules. use this option to also remove unused blackbox modules.\n"); @@ -367,6 +376,11 @@ struct HierarchyPass : public Pass { log(" per default this pass also converts positional arguments in cells\n"); log(" to arguments using port names. this option disables this behavior.\n"); log("\n"); + log(" -keep_portwidths\n"); + log(" per default this pass adjusts the port width on cells that are\n"); + log(" module instances when the width does not match the module port. this\n"); + log(" option disables this behavior.\n"); + log("\n"); log(" -nokeep_asserts\n"); log(" per default this pass sets the \"keep\" attribute on all modules\n"); log(" that directly or indirectly contain one or more $assert cells. this\n"); @@ -400,18 +414,21 @@ struct HierarchyPass : public Pass { log("in the current design.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n"); bool flag_check = false; + bool flag_simcheck = false; bool purge_lib = false; RTLIL::Module *top_mod = NULL; + std::string load_top_mod; std::vector<std::string> libdirs; bool auto_top_mode = false; bool generate_mode = false; bool keep_positionals = false; + bool keep_portwidths = false; bool nokeep_asserts = false; std::vector<std::string> generate_cells; std::vector<generate_port_decl_t> generate_ports; @@ -419,7 +436,7 @@ struct HierarchyPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-generate" && !flag_check && !top_mod) { + if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !top_mod) { generate_mode = true; log("Entering generate mode.\n"); while (++argidx < args.size()) { @@ -462,6 +479,10 @@ struct HierarchyPass : public Pass { flag_check = true; continue; } + if (args[argidx] == "-simcheck") { + flag_simcheck = true; + continue; + } if (args[argidx] == "-purge_lib") { purge_lib = true; continue; @@ -470,6 +491,10 @@ struct HierarchyPass : public Pass { keep_positionals = true; continue; } + if (args[argidx] == "-keep_portwidths") { + keep_portwidths = true; + continue; + } if (args[argidx] == "-nokeep_asserts") { nokeep_asserts = true; continue; @@ -488,7 +513,7 @@ struct HierarchyPass : public Pass { top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL; } if (top_mod == NULL) - log_cmd_error("Module `%s' not found!\n", args[argidx].c_str()); + load_top_mod = args[argidx]; continue; } if (args[argidx] == "-auto-top") { @@ -499,6 +524,22 @@ struct HierarchyPass : public Pass { } extra_args(args, argidx, design, false); + if (!load_top_mod.empty()) { +#ifdef YOSYS_ENABLE_VERIFIC + if (verific_import_pending) { + verific_import(design, load_top_mod); + top_mod = design->module(RTLIL::escape_id(load_top_mod)); + } +#endif + if (top_mod == NULL) + log_cmd_error("Module `%s' not found!\n", load_top_mod.c_str()); + } else { +#ifdef YOSYS_ENABLE_VERIFIC + if (verific_import_pending) + verific_import(design); +#endif + } + if (generate_mode) { generate(design, generate_cells, generate_ports); return; @@ -524,6 +565,9 @@ struct HierarchyPass : public Pass { log("Automatically selected %s as design top module.\n", log_id(top_mod)); } + if (flag_simcheck && top_mod == nullptr) + log_error("Design has no top module.\n"); + bool did_something = true; while (did_something) { @@ -539,7 +583,7 @@ struct HierarchyPass : public Pass { } for (auto module : used_modules) { - if (expand_module(design, module, flag_check, libdirs)) + if (expand_module(design, module, flag_check, flag_simcheck, libdirs)) did_something = true; } } @@ -614,6 +658,72 @@ struct HierarchyPass : public Pass { } } + std::set<Module*> blackbox_derivatives; + std::vector<Module*> design_modules = design->modules(); + + for (auto module : design_modules) + for (auto cell : module->cells()) + { + Module *m = design->module(cell->type); + + if (m == nullptr) + continue; + + if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } + + for (auto &conn : cell->connections()) + { + Wire *w = m->wire(conn.first); + + if (w == nullptr || w->port_id == 0) + continue; + + if (GetSize(conn.second) == 0) + continue; + + SigSpec sig = conn.second; + + if (!keep_portwidths && GetSize(w) != GetSize(conn.second)) + { + if (GetSize(w) < GetSize(conn.second)) + { + int n = GetSize(conn.second) - GetSize(w); + if (!w->port_input && w->port_output) + module->connect(sig.extract(GetSize(w), n), Const(0, n)); + sig.remove(GetSize(w), n); + } + else + { + int n = GetSize(w) - GetSize(conn.second); + if (w->port_input && !w->port_output) + sig.append(Const(0, n)); + else + sig.append(module->addWire(NEW_ID, n)); + } + + if (!conn.second.is_fully_const() || !w->port_input || w->port_output) + log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), + log_id(conn.first), GetSize(conn.second), GetSize(sig)); + cell->setPort(conn.first, sig); + } + + if (w->port_output && !w->port_input && sig.has_const()) + log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", + log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig)); + } + } + + for (auto module : blackbox_derivatives) + design->remove(module); + log_pop(); } } HierarchyPass; diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index 9f312f82..ec242aa1 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -169,6 +169,7 @@ struct SubmodWorker } new_mod->fixup_ports(); + ct.setup_module(new_mod); for (RTLIL::Cell *cell : submod.cells) { RTLIL::Cell *new_cell = new_mod->addCell(cell->name, cell); @@ -268,7 +269,7 @@ struct SubmodWorker struct SubmodPass : public Pass { SubmodPass() : Pass("submod", "moving part of a module to a new submodule") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -296,7 +297,7 @@ struct SubmodPass : public Pass { log("with -copy to not modify the source module.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n"); log_push(); diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/uniquify.cc index 03c365fb..c88ecd82 100644 --- a/passes/hierarchy/singleton.cc +++ b/passes/hierarchy/uniquify.cc @@ -22,28 +22,28 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct SingletonPass : public Pass { - SingletonPass() : Pass("singleton", "create singleton modules") { } - virtual void help() +struct UniquifyPass : public Pass { + UniquifyPass() : Pass("uniquify", "create unique copies of modules") { } + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" singleton [selection]\n"); + log(" uniquify [selection]\n"); log("\n"); log("By default, a module that is instantiated by several other modules is only\n"); log("kept once in the design. This preserves the original modularity of the design\n"); log("and reduces the overall size of the design in memory. But it prevents certain\n"); - log("optimizations and other operations on the design. This pass creates singleton\n"); + log("optimizations and other operations on the design. This pass creates unique\n"); log("modules for all selected cells. The created modules are marked with the\n"); - log("'singleton' attribute.\n"); + log("'unique' attribute.\n"); log("\n"); - log("This commands only operates on modules that by themself have the 'singleton'\n"); - log("attribute set (the 'top' module is a singleton implicitly).\n"); + log("This commands only operates on modules that by themself have the 'unique'\n"); + log("attribute set (the 'top' module is unique implicitly).\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - log_header(design, "Executing SINGLETON pass (creating singleton modules).\n"); + log_header(design, "Executing UNIQUIFY pass (creating unique copies of modules).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -56,7 +56,7 @@ struct SingletonPass : public Pass { extra_args(args, argidx, design); bool did_something = true; - int singleton_cnt = 0; + int count = 0; while (did_something) { @@ -64,12 +64,13 @@ struct SingletonPass : public Pass { for (auto module : design->selected_modules()) { - if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top")) + if (!module->get_bool_attribute("\\unique") && !module->get_bool_attribute("\\top")) continue; for (auto cell : module->selected_cells()) { - auto tmod = design->module(cell->type); + Module *tmod = design->module(cell->type); + IdString newname = module->name.str() + "." + log_id(cell->name); if (tmod == nullptr) continue; @@ -77,25 +78,25 @@ struct SingletonPass : public Pass { if (tmod->get_bool_attribute("\\blackbox")) continue; - if (tmod->get_bool_attribute("\\singleton")) + if (tmod->get_bool_attribute("\\unique") && newname == tmod->name) continue; - cell->type = module->name.str() + "." + log_id(cell->name); - log("Creating singleton '%s'.\n", log_id(cell->type)); + log("Creating module %s from %s.\n", log_id(newname), log_id(tmod)); auto smod = tmod->clone(); - smod->name = cell->type; - smod->set_bool_attribute("\\singleton"); + smod->name = newname; + cell->type = newname; + smod->set_bool_attribute("\\unique"); design->add(smod); did_something = true; - singleton_cnt++; + count++; } } } - log("Created %d singleton modules.\n", singleton_cnt); + log("Created %d unique modules.\n", count); } -} SingletonPass; +} UniquifyPass; PRIVATE_NAMESPACE_END diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc index ad359c01..e468c3a0 100644 --- a/passes/memory/Makefile.inc +++ b/passes/memory/Makefile.inc @@ -7,4 +7,5 @@ OBJS += passes/memory/memory_unpack.o OBJS += passes/memory/memory_bram.o OBJS += passes/memory/memory_map.o OBJS += passes/memory/memory_memx.o +OBJS += passes/memory/memory_nordff.o diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index 947d598b..712bc253 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct MemoryPass : public Pass { MemoryPass() : Pass("memory", "translate memories to basic cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -48,7 +48,7 @@ struct MemoryPass : public Pass { log("or multiport memory blocks if called with the -nomap option.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool flag_nomap = false; bool flag_nordff = false; diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index a7f9cf38..e8552bbc 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -1120,7 +1120,7 @@ void handle_cell(Cell *cell, const rules_t &rules) struct MemoryBramPass : public Pass { MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1210,7 +1210,7 @@ struct MemoryBramPass : public Pass { log("the data bits to accommodate the enable pattern of port A.\n"); log("\n"); } - virtual void execute(vector<string> args, Design *design) + void execute(vector<string> args, Design *design) YS_OVERRIDE { rules_t rules; diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index ab66e3fb..70d98713 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -246,7 +246,7 @@ static void handle_module(Design *design, Module *module) struct MemoryCollectPass : public Pass { MemoryCollectPass() : Pass("memory_collect", "creating multi-port memory cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -256,7 +256,7 @@ struct MemoryCollectPass : public Pass { log("memory cells.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 40691d16..32df1917 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -33,8 +33,20 @@ struct MemoryDffWorker dict<SigBit, int> sigbit_users_count; dict<SigSpec, Cell*> mux_cells_a, mux_cells_b; pool<Cell*> forward_merged_dffs, candidate_dffs; + pool<SigBit> init_bits; - MemoryDffWorker(Module *module) : module(module), sigmap(module) { } + MemoryDffWorker(Module *module) : module(module), sigmap(module) + { + for (auto wire : module->wires()) { + if (wire->attributes.count("\\init") == 0) + continue; + SigSpec sig = sigmap(wire); + Const initval = wire->attributes.count("\\init"); + for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + init_bits.insert(sig[i]); + } + } bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) { @@ -45,6 +57,9 @@ struct MemoryDffWorker if (bit.wire == NULL) continue; + if (!after && init_bits.count(sigmap(bit))) + return false; + for (auto cell : dff_cells) { if (after && forward_merged_dffs.count(cell)) @@ -72,6 +87,9 @@ struct MemoryDffWorker if (d.size() != 1) continue; + if (after && init_bits.count(d)) + return false; + bit = d; clk = this_clk; clk_polarity = this_clk_polarity; @@ -265,7 +283,7 @@ struct MemoryDffWorker struct MemoryDffPass : public Pass { MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -279,7 +297,7 @@ struct MemoryDffPass : public Pass { log(" do not merge registers on read ports\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool flag_wr_only = false; diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index bffeec85..a0b808e5 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -352,7 +352,7 @@ struct MemoryMapWorker struct MemoryMapPass : public Pass { MemoryMapPass() : Pass("memory_map", "translate multiport memories to basic cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -362,7 +362,7 @@ struct MemoryMapPass : public Pass { log("pass to word-wide DFFs and address decoders.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); extra_args(args, 1, design); for (auto mod : design->selected_modules()) diff --git a/passes/memory/memory_memx.cc b/passes/memory/memory_memx.cc index 2b02e249..95837016 100644 --- a/passes/memory/memory_memx.cc +++ b/passes/memory/memory_memx.cc @@ -28,7 +28,7 @@ PRIVATE_NAMESPACE_BEGIN struct MemoryMemxPass : public Pass { MemoryMemxPass() : Pass("memory_memx", "emulate vlog sim behavior for mem ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -38,7 +38,7 @@ struct MemoryMemxPass : public Pass { log("behavior for out-of-bounds memory reads and writes.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n"); extra_args(args, 1, design); diff --git a/passes/memory/memory_nordff.cc b/passes/memory/memory_nordff.cc new file mode 100644 index 00000000..ba0361c0 --- /dev/null +++ b/passes/memory/memory_nordff.cc @@ -0,0 +1,121 @@ +/* + * 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 MemoryNordffPass : public Pass { + MemoryNordffPass() : Pass("memory_nordff", "extract read port FFs from memories") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" memory_nordff [options] [selection]\n"); + log("\n"); + log("This pass extracts FFs from memory read ports. This results in a netlist\n"); + log("similar to what one would get from calling memory_dff with -nordff.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing MEMORY_NORDFF pass (extracting $dff cells from $mem).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") { + // flag_wr_only = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : vector<Cell*>(module->selected_cells())) + { + if (cell->type != "$mem") + continue; + + int rd_ports = cell->getParam("\\RD_PORTS").as_int(); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + + SigSpec rd_addr = cell->getPort("\\RD_ADDR"); + SigSpec rd_data = cell->getPort("\\RD_DATA"); + SigSpec rd_clk = cell->getPort("\\RD_CLK"); + SigSpec rd_en = cell->getPort("\\RD_EN"); + Const rd_clk_enable = cell->getParam("\\RD_CLK_ENABLE"); + Const rd_clk_polarity = cell->getParam("\\RD_CLK_POLARITY"); + + for (int i = 0; i < rd_ports; i++) + { + bool clk_enable = rd_clk_enable[i] == State::S1; + + if (clk_enable) + { + bool clk_polarity = cell->getParam("\\RD_CLK_POLARITY")[i] == State::S1; + bool transparent = cell->getParam("\\RD_TRANSPARENT")[i] == State::S1; + + SigSpec clk = cell->getPort("\\RD_CLK")[i] ; + SigSpec en = cell->getPort("\\RD_EN")[i]; + Cell *c; + + if (transparent) + { + SigSpec sig_q = module->addWire(NEW_ID, abits); + SigSpec sig_d = rd_addr.extract(abits * i, abits); + rd_addr.replace(abits * i, sig_q); + if (en != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, en); + c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity); + } + else + { + SigSpec sig_d = module->addWire(NEW_ID, width); + SigSpec sig_q = rd_data.extract(width * i, width); + rd_data.replace(width *i, sig_d); + if (en != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, en); + c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity); + } + + log("Extracted %s FF from read port %d of %s.%s: %s\n", transparent ? "addr" : "data", + i, log_id(module), log_id(cell), log_id(c)); + } + + rd_en[i] = State::S1; + rd_clk[i] = State::S0; + rd_clk_enable[i] = State::S0; + rd_clk_polarity[i] = State::S1; + } + + cell->setPort("\\RD_ADDR", rd_addr); + cell->setPort("\\RD_DATA", rd_data); + cell->setPort("\\RD_CLK", rd_clk); + cell->setPort("\\RD_EN", rd_en); + cell->setParam("\\RD_CLK_ENABLE", rd_clk_enable); + cell->setParam("\\RD_CLK_POLARITY", rd_clk_polarity); + } + } +} MemoryNordffPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index ca09ac52..172afe0c 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -726,7 +726,7 @@ struct MemoryShareWorker struct MemorySharePass : public Pass { MemorySharePass() : Pass("memory_share", "consolidate memory ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -752,7 +752,7 @@ struct MemorySharePass : public Pass { log("optimizations) such as \"share\" and \"opt_merge\".\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).\n"); extra_args(args, 1, design); for (auto module : design->selected_modules()) diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index a0fc31b5..49ec6679 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -127,7 +127,7 @@ void handle_module(RTLIL::Design *design, RTLIL::Module *module) struct MemoryUnpackPass : public Pass { MemoryUnpackPass() : Pass("memory_unpack", "unpack multi-port memory cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -137,7 +137,7 @@ struct MemoryUnpackPass : public Pass { log("$memwr cells. It is the counterpart to the memory_collect pass.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index a8b1537b..0d01e9d3 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -10,5 +10,7 @@ OBJS += passes/opt/opt_expr.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o OBJS += passes/opt/wreduce.o +OBJS += passes/opt/opt_demorgan.o +OBJS += passes/opt/rmports.o endif diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 021c1a03..a4aca2fe 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct OptPass : public Pass { OptPass() : Pass("opt", "perform simple optimizations") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -63,7 +63,7 @@ struct OptPass : public Pass { log("\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string opt_clean_args; std::string opt_expr_args; diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 6600ffa2..c3b13aca 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -64,7 +64,7 @@ struct keep_cache_t bool query(Cell *cell) { - if (cell->type.in("$memwr", "$meminit", "$assert", "$assume")) + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover")) return true; if (cell->has_keep_attr()) @@ -91,9 +91,16 @@ void rmunused_module_cells(Module *module, bool verbose) Cell *cell = it.second; for (auto &it2 : cell->connections()) { if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first)) - for (auto bit : sigmap(it2.second)) + for (auto raw_bit : it2.second) { + if (raw_bit.wire == nullptr) + continue; + auto bit = sigmap(raw_bit); + if (bit.wire == nullptr) + log_warning("Driver-driver conflict for %s between cell %s.%s and constant %s in %s: Resolved using constant.\n", + log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)); if (bit.wire != nullptr) wire2driver[bit].insert(cell); + } } if (keep_cache.query(cell)) queue.insert(cell); @@ -320,19 +327,89 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos if (!used_signals.check_any(RTLIL::SigSpec(wire))) { if (check_public_name(wire->name) && verbose) { log(" removing unused non-port wire %s.\n", wire->name.c_str()); - del_wires_count++; } del_wires.insert(wire); + del_wires_count++; } module->remove(del_wires); - count_rm_wires += del_wires.size();; + count_rm_wires += del_wires.size(); - if (del_wires_count > 0) + if (verbose && del_wires_count > 0) log(" removed %d unused temporary wires.\n", del_wires_count); } -void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) +bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose) +{ + bool did_something = false; + CellTypes fftypes; + fftypes.setup_internals_mem(); + + SigMap sigmap(module); + dict<SigBit, State> qbits; + + for (auto cell : module->cells()) + if (fftypes.cell_known(cell->type) && cell->hasPort("\\Q")) + { + SigSpec sig = cell->getPort("\\Q"); + + for (int i = 0; i < GetSize(sig); i++) + { + SigBit bit = sig[i]; + + if (bit.wire == nullptr || bit.wire->attributes.count("\\init") == 0) + continue; + + Const init = bit.wire->attributes.at("\\init"); + + if (i >= GetSize(init) || init[i] == State::Sx || init[i] == State::Sz) + continue; + + sigmap.add(bit); + qbits[bit] = init[i]; + } + } + + for (auto wire : module->wires()) + { + if (!purge_mode && wire->name[0] == '\\') + continue; + + if (wire->attributes.count("\\init") == 0) + continue; + + Const init = wire->attributes.at("\\init"); + + for (int i = 0; i < GetSize(wire) && i < GetSize(init); i++) + { + if (init[i] == State::Sx || init[i] == State::Sz) + continue; + + SigBit wire_bit = SigBit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + + if (wire_bit == mapped_wire_bit) + goto next_wire; + + if (qbits.count(sigmap(SigBit(wire, i))) == 0) + goto next_wire; + + if (qbits.at(sigmap(SigBit(wire, i))) != init[i]) + goto next_wire; + } + + if (verbose) + log(" removing redundant init attribute on %s.\n", log_id(wire)); + + wire->attributes.erase("\\init"); + did_something = true; + next_wire:; + } + + return did_something; +} + +void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool rminit) { if (verbose) log("Finding unused cells or wires in module %s..\n", module->name.c_str()); @@ -358,11 +435,14 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) rmunused_module_cells(module, verbose); rmunused_module_signals(module, purge_mode, verbose); + + if (rminit && rmunused_module_init(module, purge_mode, verbose)) + rmunused_module_signals(module, purge_mode, verbose); } struct OptCleanPass : public Pass { OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -379,7 +459,7 @@ struct OptCleanPass : public Pass { log(" also remove internal nets if they have a public name\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool purge_mode = false; @@ -406,9 +486,12 @@ struct OptCleanPass : public Pass { for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) continue; - rmunused_module(module, purge_mode, true); + rmunused_module(module, purge_mode, true, true); } + if (count_rm_cells > 0 || count_rm_wires > 0) + log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires); + design->optimize(); design->sort(); design->check(); @@ -422,7 +505,7 @@ struct OptCleanPass : public Pass { struct CleanPass : public Pass { CleanPass() : Pass("clean", "remove unused cells and wires") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -437,7 +520,7 @@ struct CleanPass : public Pass { log("in -purge mode between the commands.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool purge_mode = false; @@ -465,7 +548,7 @@ struct CleanPass : public Pass { for (auto module : design->selected_whole_modules()) { if (module->has_processes()) continue; - rmunused_module(module, purge_mode, false); + rmunused_module(module, purge_mode, false, false); } if (count_rm_cells > 0 || count_rm_wires > 0) diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc new file mode 100644 index 00000000..1699a645 --- /dev/null +++ b/passes/opt/opt_demorgan.cc @@ -0,0 +1,202 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2017 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/modtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void demorgan_worker( + ModIndex& index, + Cell *cell, + unsigned int& cells_changed) +{ + SigMap& sigmap = index.sigmap; + auto m = cell->module; + + //TODO: Add support for reduce_xor + //DeMorgan of XOR is either XOR (if even number of inputs) or XNOR (if odd number) + + if( (cell->type != "$reduce_and") && (cell->type != "$reduce_or") ) + return; + + auto insig = sigmap(cell->getPort("\\A")); + log("Inspecting %s cell %s (%d inputs)\n", log_id(cell->type), log_id(cell->name), GetSize(insig)); + int num_inverted = 0; + for(int i=0; i<GetSize(insig); i++) + { + auto b = insig[i]; + + //See if this bit is driven by a $not cell + //TODO: do other stuff like nor/nand? + pool<ModIndex::PortInfo> ports = index.query_ports(b); + bool inverted = false; + for(auto x : ports) + { + if(x.port == "\\Y" && x.cell->type == "$_NOT_") + { + inverted = true; + break; + } + } + + if(inverted) + num_inverted ++; + } + + //Stop if less than half of the inputs are inverted + if(num_inverted*2 < GetSize(insig)) + { + log(" %d / %d inputs are inverted, not pushing\n", num_inverted, GetSize(insig)); + return; + } + + //More than half of the inputs are inverted! Push through + cells_changed ++; + log(" %d / %d inputs are inverted, pushing inverter through reduction\n", num_inverted, GetSize(insig)); + + //For each input, either add or remove the inverter as needed + //TODO: this duplicates the loop up above, can we refactor it? + for(int i=0; i<GetSize(insig); i++) + { + auto b = insig[i]; + + //See if this bit is driven by a $not cell + //TODO: do other stuff like nor/nand? + pool<ModIndex::PortInfo> ports = index.query_ports(b); + RTLIL::Cell* srcinv = NULL; + for(auto x : ports) + { + if(x.port == "\\Y" && x.cell->type == "$_NOT_") + { + srcinv = x.cell; + break; + } + } + + //We are NOT inverted! Add an inverter + if(!srcinv) + { + auto inverted_b = m->addWire(NEW_ID); + m->addNot(NEW_ID, RTLIL::SigSpec(b), RTLIL::SigSpec(inverted_b)); + insig[i] = inverted_b; + } + + //We ARE inverted - bypass it + //Don't automatically delete the inverter since other stuff might still use it + else + insig[i] = srcinv->getPort("\\A"); + } + + //Cosmetic fixup: If our input is just a scrambled version of one bus, rearrange it + //Reductions are all commutative, so there's no point in having them in a weird order + bool same_signal = true; + RTLIL::Wire* srcwire = insig[0].wire; + dict<int, int> seen_bits; + for(int i=0; i<GetSize(insig); i++) + seen_bits[i] = 0; + for(int i=0; i<GetSize(insig); i++) + { + seen_bits[insig[i].offset] ++; + if(insig[i].wire != srcwire) + { + same_signal = false; + break; + } + } + if(same_signal) + { + //Make sure we've seen every bit exactly once + bool every_bit_once = true; + for(int i=0; i<GetSize(insig); i++) + { + if(seen_bits[i] != 1) + { + every_bit_once = false; + break; + } + } + + //All good? Just use the whole wire as-is without any reordering + //We do have to swap MSB to LSB b/c that's the way the reduction cells seem to work? + //Unclear on why this isn't sorting properly + //TODO: can we do SigChunks instead of single bits if we have subsets of a bus? + if(every_bit_once && (GetSize(insig) == srcwire->width) ) + { + log("Rearranging bits\n"); + RTLIL::SigSpec newsig; + for(int i=0; i<GetSize(insig); i++) + newsig.append(RTLIL::SigBit(srcwire, GetSize(insig) - i - 1)); + insig = newsig; + insig.sort(); + } + } + + //Push the new input signal back to the reduction (after bypassing/adding inverters) + cell->setPort("\\A", insig); + + //Change the cell type + if(cell->type == "$reduce_and") + cell->type = "$reduce_or"; + else if(cell->type == "$reduce_or") + cell->type = "$reduce_and"; + //don't change XOR + + //Add an inverter to the output + auto inverted_output = cell->getPort("\\Y"); + auto uninverted_output = m->addWire(NEW_ID); + m->addNot(NEW_ID, RTLIL::SigSpec(uninverted_output), inverted_output); + cell->setPort("\\Y", uninverted_output); +} + +struct OptDemorganPass : public Pass { + OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_demorgan [selection]\n"); + log("\n"); + log("This pass pushes inverters through $reduce_* cells if this will reduce the\n"); + log("overall gate count of the circuit\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n"); + + int argidx = 0; + extra_args(args, argidx, design); + + unsigned int cells_changed = 0; + for (auto module : design->selected_modules()) + { + ModIndex index(module); + for (auto cell : module->selected_cells()) + demorgan_worker(index, cell, cells_changed); + } + + if(cells_changed) + log("Pushed inverters through %u reductions\n", cells_changed); + } +} OptDemorganPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index b62eae28..0ba233c6 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -259,6 +259,29 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative) return last_bit_one; } +// if the signal has only one bit set, return the index of that bit. +// otherwise return -1 +int get_onehot_bit_index(RTLIL::SigSpec signal) +{ + int bit_index = -1; + + for (int i = 0; i < GetSize(signal); i++) + { + if (signal[i] == RTLIL::State::S0) + continue; + + if (signal[i] != RTLIL::State::S1) + return -1; + + if (bit_index != -1) + return -1; + + bit_index = i; + } + + return bit_index; +} + void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv) { if (!design->selected(module)) @@ -348,19 +371,20 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type.in("$reduce_and", "$_AND_")) detect_const_and = true; - if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool()) detect_const_and = true; if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_")) detect_const_or = true; - if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool()) detect_const_or = true; if (detect_const_and || detect_const_or) { pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool(); - bool found_zero = false, found_one = false, found_inv = false; + bool found_zero = false, found_one = false, found_undef = false, found_inv = false, many_conconst = false; + SigBit non_const_input = State::Sm; if (cell->hasPort("\\B")) { vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector(); @@ -368,12 +392,20 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } for (auto bit : input_bits) { - if (bit == State::S0) - found_zero = true; - if (bit == State::S1) - found_one = true; - if (invert_map.count(bit) && input_bits.count(invert_map.at(bit))) - found_inv = true; + if (bit.wire) { + if (invert_map.count(bit) && input_bits.count(invert_map.at(bit))) + found_inv = true; + if (non_const_input != State::Sm) + many_conconst = true; + non_const_input = many_conconst ? State::Sm : bit; + } else { + if (bit == State::S0) + found_zero = true; + else if (bit == State::S1) + found_one = true; + else + found_undef = true; + } } if (detect_const_and && (found_zero || found_inv)) { @@ -387,6 +419,12 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1); goto next_cell; } + + if (non_const_input != State::Sm && !found_undef) { + cover("opt.opt_expr.and_or_buffer"); + replace_cell(assign_map, module, cell, "and_or_buffer", "\\Y", non_const_input); + goto next_cell; + } } if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") && @@ -411,6 +449,53 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (group_cell_inputs(module, cell, true, assign_map)) goto next_cell; + if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" || + cell->type == "$reduce_or" || cell->type == "$reduce_and" || cell->type == "$reduce_bool") + { + SigBit neutral_bit = cell->type == "$reduce_and" ? State::S1 : State::S0; + + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec new_sig_a; + + for (auto bit : sig_a) + if (bit != neutral_bit) new_sig_a.append(bit); + + if (GetSize(new_sig_a) == 0) + new_sig_a.append(neutral_bit); + + if (GetSize(new_sig_a) < GetSize(sig_a)) { + cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str()); + log("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a)); + cell->setPort("\\A", new_sig_a); + cell->parameters.at("\\A_WIDTH") = GetSize(new_sig_a); + did_something = true; + } + } + + if (cell->type == "$logic_and" || cell->type == "$logic_or") + { + SigBit neutral_bit = State::S0; + + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec new_sig_b; + + for (auto bit : sig_b) + if (bit != neutral_bit) new_sig_b.append(bit); + + if (GetSize(new_sig_b) == 0) + new_sig_b.append(neutral_bit); + + if (GetSize(new_sig_b) < GetSize(sig_b)) { + cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str()); + log("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b)); + cell->setPort("\\B", new_sig_b); + cell->parameters.at("\\B_WIDTH") = GetSize(new_sig_b); + did_something = true; + } + } + if (cell->type == "$reduce_and") { RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); @@ -633,6 +718,23 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if (cell->type == "$_TBUF_" || cell->type == "$tribuf") { + RTLIL::SigSpec input = cell->getPort(cell->type == "$_TBUF_" ? "\\E" : "\\EN"); + RTLIL::SigSpec a = cell->getPort("\\A"); + assign_map.apply(input); + assign_map.apply(a); + if (input == State::S1) + ACTION_DO("\\Y", cell->getPort("\\A")); + if (input == State::S0 && !a.is_fully_undef()) { + cover("opt.opt_expr.action_" S__LINE__); + log("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str()); + cell->setPort("\\A", SigSpec(State::Sx, GetSize(a))); + did_something = true; + goto next_cell; + } + } + if (cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex") { RTLIL::SigSpec a = cell->getPort("\\A"); @@ -1167,6 +1269,197 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + // remove redundant pairs of bits in ==, ===, !=, and !== + // replace cell with const driver if inputs can't be equal + if (do_fine && cell->type.in("$eq", "$ne", "$eqx", "$nex")) + { + pool<pair<SigBit, SigBit>> redundant_cache; + mfp<SigBit> contradiction_cache; + + contradiction_cache.promote(State::S0); + contradiction_cache.promote(State::S1); + + int a_width = cell->getParam("\\A_WIDTH").as_int(); + int b_width = cell->getParam("\\B_WIDTH").as_int(); + + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + int width = is_signed ? std::min(a_width, b_width) : std::max(a_width, b_width); + + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + + int redundant_bits = 0; + + for (int i = width-1; i >= 0; i--) + { + SigBit bit_a = i < a_width ? assign_map(sig_a[i]) : State::S0; + SigBit bit_b = i < b_width ? assign_map(sig_b[i]) : State::S0; + + if (bit_a != State::Sx && bit_a != State::Sz && + bit_b != State::Sx && bit_b != State::Sz) + contradiction_cache.merge(bit_a, bit_b); + + if (bit_b < bit_a) + std::swap(bit_a, bit_b); + + pair<SigBit, SigBit> key(bit_a, bit_b); + + if (redundant_cache.count(key)) { + if (i < a_width) sig_a.remove(i); + if (i < b_width) sig_b.remove(i); + redundant_bits++; + continue; + } + + redundant_cache.insert(key); + } + + if (contradiction_cache.find(State::S0) == contradiction_cache.find(State::S1)) + { + SigSpec y_sig = cell->getPort("\\Y"); + Const y_value(cell->type.in("$eq", "$eqx") ? 0 : 1, GetSize(y_sig)); + + log("Replacing cell `%s' in module `%s' with constant driver %s.\n", + log_id(cell), log_id(module), log_signal(y_value)); + + module->connect(y_sig, y_value); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + if (redundant_bits) + { + log("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n", + redundant_bits, log_id(cell->type), log_id(cell), log_id(module)); + + cell->setPort("\\A", sig_a); + cell->setPort("\\B", sig_b); + cell->setParam("\\A_WIDTH", GetSize(sig_a)); + cell->setParam("\\B_WIDTH", GetSize(sig_b)); + + did_something = true; + goto next_cell; + } + } + + // replace a<0 or a>=0 with the top bit of a + if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le")) + { + //used to decide whether the signal needs to be negated + bool is_lt = false; + + //references the variable signal in the comparison + RTLIL::SigSpec sigVar; + + //references the constant signal in the comparison + RTLIL::SigSpec sigConst; + + // note that this signal must be constant for the optimization + // to take place, but it is not checked beforehand. + // If new passes are added, this signal must be checked for const-ness + + //width of the variable port + int width; + int const_width; + + bool var_signed; + + if (cell->type == "$lt" || cell->type == "$ge") { + is_lt = cell->type == "$lt" ? 1 : 0; + sigVar = cell->getPort("\\A"); + sigConst = cell->getPort("\\B"); + width = cell->parameters["\\A_WIDTH"].as_int(); + const_width = cell->parameters["\\B_WIDTH"].as_int(); + var_signed = cell->parameters["\\A_SIGNED"].as_bool(); + } else + if (cell->type == "$gt" || cell->type == "$le") { + is_lt = cell->type == "$gt" ? 1 : 0; + sigVar = cell->getPort("\\B"); + sigConst = cell->getPort("\\A"); + width = cell->parameters["\\B_WIDTH"].as_int(); + const_width = cell->parameters["\\A_WIDTH"].as_int(); + var_signed = cell->parameters["\\B_SIGNED"].as_bool(); + } else + log_abort(); + + // replace a(signed) < 0 with the high bit of a + if (sigConst.is_fully_const() && sigConst.is_fully_zero() && var_signed == true) + { + RTLIL::SigSpec a_prime(RTLIL::State::S0, cell->parameters["\\Y_WIDTH"].as_int()); + a_prime[0] = sigVar[width - 1]; + if (is_lt) { + log("Replacing %s cell `%s' (implementing X<0) with X[%d]: %s\n", + log_id(cell->type), log_id(cell), width-1, log_signal(a_prime)); + module->connect(cell->getPort("\\Y"), a_prime); + module->remove(cell); + } else { + log("Replacing %s cell `%s' (implementing X>=0) with ~X[%d]: %s\n", + log_id(cell->type), log_id(cell), width-1, log_signal(a_prime)); + module->addNot(NEW_ID, a_prime, cell->getPort("\\Y")); + module->remove(cell); + } + did_something = true; + goto next_cell; + } else + if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false) + { + if (sigConst.is_fully_zero()) { + RTLIL::SigSpec a_prime(RTLIL::State::S0, 1); + if (is_lt) { + log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n", + log_id(cell->type), log_id(cell)); + a_prime[0] = RTLIL::State::S0; + } else { + log("Replacing %s cell `%s' (implementing unsigned X>=0) with constant true.\n", + log_id(cell->type), log_id(cell)); + a_prime[0] = RTLIL::State::S1; + } + module->connect(cell->getPort("\\Y"), a_prime); + module->remove(cell); + did_something = true; + goto next_cell; + } + + int const_bit_set = get_onehot_bit_index(sigConst); + if (const_bit_set >= 0 && const_bit_set < width) { + int bit_set = const_bit_set; + RTLIL::SigSpec a_prime(RTLIL::State::S0, width - bit_set); + for (int i = bit_set; i < width; i++) { + a_prime[i - bit_set] = sigVar[i]; + } + if (is_lt) { + log("Replacing %s cell `%s' (implementing unsigned X<%s) with !X[%d:%d]: %s.\n", + log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime)); + module->addLogicNot(NEW_ID, a_prime, cell->getPort("\\Y")); + } else { + log("Replacing %s cell `%s' (implementing unsigned X>=%s) with |X[%d:%d]: %s.\n", + log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime)); + module->addReduceOr(NEW_ID, a_prime, cell->getPort("\\Y")); + } + module->remove(cell); + did_something = true; + goto next_cell; + } + else if(const_bit_set >= width && const_bit_set >= 0){ + RTLIL::SigSpec a_prime(RTLIL::State::S0, 1); + if(is_lt){ + a_prime[0] = RTLIL::State::S1; + log("Replacing %s cell `%s' (implementing unsigned X[%d:0] < %s[%d:0]) with constant 0.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1); + } + else{ + log("Replacing %s cell `%s' (implementing unsigned X[%d:0]>= %s[%d:0]) with constant 1.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1); + } + module->connect(cell->getPort("\\Y"), a_prime); + module->remove(cell); + did_something = true; + goto next_cell; + + } + } + } + next_cell:; #undef ACTION_DO #undef ACTION_DO_Y @@ -1177,7 +1470,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons struct OptExprPass : public Pass { OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1211,7 +1504,7 @@ struct OptExprPass : public Pass { log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool mux_undef = false; bool mux_bool = false; diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 97989d27..eedf8890 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -275,13 +275,24 @@ struct OptMergeWorker ct.cell_types.erase("$pmux"); } + ct.cell_types.erase("$tribuf"); + ct.cell_types.erase("$_TBUF_"); + ct.cell_types.erase("$anyseq"); + ct.cell_types.erase("$anyconst"); + ct.cell_types.erase("$allseq"); + ct.cell_types.erase("$allconst"); + log("Finding identical cells in module `%s'.\n", module->name.c_str()); assign_map.set(module); dff_init_map.set(module); for (auto &it : module->wires_) - if (it.second->attributes.count("\\init") != 0) - dff_init_map.add(it.second, it.second->attributes.at("\\init")); + if (it.second->attributes.count("\\init") != 0) { + Const initval = it.second->attributes.at("\\init"); + for (int i = 0; i < GetSize(initval) && i < GetSize(it.second); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + dff_init_map.add(SigBit(it.second, i), initval[i]); + } bool did_something = true; while (did_something) @@ -330,7 +341,7 @@ struct OptMergeWorker struct OptMergePass : public Pass { OptMergePass() : Pass("opt_merge", "consolidate identical cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -346,7 +357,7 @@ struct OptMergePass : public Pass { log(" Operate on all cell types, not just built-in types.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n"); diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index f5ddc2af..87c7ce9b 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -449,7 +449,7 @@ struct OptMuxtreeWorker struct OptMuxtreePass : public Pass { OptMuxtreePass() : Pass("opt_muxtree", "eliminate dead trees in multiplexer trees") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -462,7 +462,7 @@ struct OptMuxtreePass : public Pass { log("This pass only operates on completely selected modules without processes.\n"); log("\n"); } - virtual void execute(vector<std::string> args, RTLIL::Design *design) + void execute(vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); extra_args(args, 1, design); diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index eb9d02ad..d99f1ca6 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -329,7 +329,7 @@ struct OptReduceWorker struct OptReducePass : public Pass { OptReducePass() : Pass("opt_reduce", "simplify large MUXes and AND/OR gates") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -350,7 +350,7 @@ struct OptReducePass : public Pass { log(" alias for -fine\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool do_fine = false; diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 922f086f..5880254c 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -39,11 +39,201 @@ void remove_init_attr(SigSpec sig) wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx; } +bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell) +{ + SigSpec sig_set, sig_clr; + State pol_set, pol_clr; + + if (cell->hasPort("\\S")) + sig_set = cell->getPort("\\S"); + + if (cell->hasPort("\\R")) + sig_clr = cell->getPort("\\R"); + + if (cell->hasPort("\\SET")) + sig_set = cell->getPort("\\SET"); + + if (cell->hasPort("\\CLR")) + sig_clr = cell->getPort("\\CLR"); + + log_assert(GetSize(sig_set) == GetSize(sig_clr)); + + if (cell->type.substr(0,8) == "$_DFFSR_") { + pol_set = cell->type[9] == 'P' ? State::S1 : State::S0; + pol_clr = cell->type[10] == 'P' ? State::S1 : State::S0; + } else + if (cell->type.substr(0,11) == "$_DLATCHSR_") { + pol_set = cell->type[12] == 'P' ? State::S1 : State::S0; + pol_clr = cell->type[13] == 'P' ? State::S1 : State::S0; + } else + if (cell->type == "$dffsr" || cell->type == "$dlatchsr") { + pol_set = cell->parameters["\\SET_POLARITY"].as_bool() ? State::S1 : State::S0; + pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool() ? State::S1 : State::S0; + } else + log_abort(); + + State npol_set = pol_set == State::S0 ? State::S1 : State::S0; + State npol_clr = pol_clr == State::S0 ? State::S1 : State::S0; + + SigSpec sig_d = cell->getPort("\\D"); + SigSpec sig_q = cell->getPort("\\Q"); + + bool did_something = false; + bool proper_sr = false; + bool used_pol_set = false; + bool used_pol_clr = false; + bool hasreset = false; + Const reset_val; + SigSpec sig_reset; + + for (int i = 0; i < GetSize(sig_set); i++) + { + SigBit s = sig_set[i], c = sig_clr[i]; + + if (s != npol_set || c != npol_clr) + hasreset = true; + + if (s == pol_set || c == pol_clr) + { + log("Constantly %s Q bit %s for SR cell %s (%s) from module %s.\n", + s == pol_set ? "set" : "cleared", log_signal(sig_q[i]), + log_id(cell), log_id(cell->type), log_id(mod)); + + remove_init_attr(sig_q[i]); + mod->connect(sig_q[i], s == pol_set ? State::S1 : State::S0); + sig_set.remove(i); + sig_clr.remove(i); + sig_d.remove(i); + sig_q.remove(i--); + did_something = true; + continue; + } + if (sig_reset.empty() && s.wire != nullptr) sig_reset = s; + if (sig_reset.empty() && c.wire != nullptr) sig_reset = c; + + if (s.wire != nullptr && s != sig_reset) proper_sr = true; + if (c.wire != nullptr && c != sig_reset) proper_sr = true; + + if ((s.wire == nullptr) != (c.wire == nullptr)) { + if (s.wire != nullptr) used_pol_set = true; + if (c.wire != nullptr) used_pol_clr = true; + reset_val.bits.push_back(c.wire == nullptr ? State::S1 : State::S0); + } else + proper_sr = true; + } + + if (!hasreset) + proper_sr = false; + + if (GetSize(sig_set) == 0) + { + log("Removing %s (%s) from module %s.\n", log_id(cell), log_id(cell->type), log_id(mod)); + mod->remove(cell); + return true; + } + + if (cell->type == "$dffsr" || cell->type == "$dlatchsr") + { + cell->setParam("\\WIDTH", GetSize(sig_d)); + cell->setPort("\\SET", sig_set); + cell->setPort("\\CLR", sig_clr); + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + } + else + { + cell->setPort("\\S", sig_set); + cell->setPort("\\R", sig_clr); + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + } + + if (proper_sr) + return did_something; + + if (used_pol_set && used_pol_clr && pol_set != pol_clr) + return did_something; + + if (cell->type == "$dlatchsr") + return did_something; + + State unified_pol = used_pol_set ? pol_set : pol_clr; + + if (cell->type == "$dffsr") + { + if (hasreset) + { + log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$adff", log_id(mod)); + + cell->type = "$adff"; + cell->setParam("\\ARST_POLARITY", unified_pol); + cell->setParam("\\ARST_VALUE", reset_val); + cell->setPort("\\ARST", sig_reset); + + cell->unsetParam("\\SET_POLARITY"); + cell->unsetParam("\\CLR_POLARITY"); + cell->unsetPort("\\SET"); + cell->unsetPort("\\CLR"); + + return true; + } + else + { + log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$dff", log_id(mod)); + + cell->type = "$dff"; + cell->unsetParam("\\SET_POLARITY"); + cell->unsetParam("\\CLR_POLARITY"); + cell->unsetPort("\\SET"); + cell->unsetPort("\\CLR"); + + return true; + } + } + else + { + IdString new_type; + + if (cell->type.substr(0,8) == "$_DFFSR_") + new_type = stringf("$_DFF_%c_", cell->type[8]); + else if (cell->type.substr(0,11) == "$_DLATCHSR_") + new_type = stringf("$_DLATCH_%c_", cell->type[11]); + else + log_abort(); + + log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), log_id(new_type), log_id(mod)); + + cell->type = new_type; + cell->unsetPort("\\S"); + cell->unsetPort("\\R"); + + return did_something; + } +} + bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) { - SigSpec sig_e = dlatch->getPort("\\EN"); + SigSpec sig_e; + State on_state, off_state; + + if (dlatch->type == "$dlatch") { + sig_e = assign_map(dlatch->getPort("\\EN")); + on_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S1 : State::S0; + off_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S0 : State::S1; + } else + if (dlatch->type == "$_DLATCH_P_") { + sig_e = assign_map(dlatch->getPort("\\E")); + on_state = State::S1; + off_state = State::S0; + } else + if (dlatch->type == "$_DLATCH_N_") { + sig_e = assign_map(dlatch->getPort("\\E")); + on_state = State::S0; + off_state = State::S1; + } else + log_abort(); - if (sig_e == State::S0) + if (sig_e == off_state) { RTLIL::Const val_init; for (auto bit : dff_init_map(dlatch->getPort("\\Q"))) @@ -52,7 +242,7 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) goto delete_dlatch; } - if (sig_e == State::S1) + if (sig_e == on_state) { mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D")); goto delete_dlatch; @@ -168,7 +358,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) goto delete_dff; } - if (sig_d == sig_q && (!sig_r.size() || !has_init || val_init == val_rv)) { + if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) { if (sig_r.size()) mod->connect(sig_q, val_rv); if (has_init) @@ -176,6 +366,28 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) goto delete_dff; } + if (!sig_r.empty() && sig_r.is_fully_const()) + { + if (sig_r == val_rp || sig_r.is_fully_undef()) { + mod->connect(sig_q, val_rv); + goto delete_dff; + } + + log("Removing unused reset from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod)); + + if (dff->type == "$adff") { + dff->type = "$dff"; + dff->unsetPort("\\ARST"); + dff->unsetParam("\\ARST_POLARITY"); + dff->unsetParam("\\ARST_VALUE"); + return true; + } + + log_assert(dff->type.substr(0,6) == "$_DFF_"); + dff->type = stringf("$_DFF_%c_", + dff->type[6]); + dff->unsetPort("\\R"); + } + return false; delete_dff: @@ -187,7 +399,7 @@ delete_dff: struct OptRmdffPass : public Pass { OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -197,7 +409,7 @@ struct OptRmdffPass : public Pass { log("a constant driver.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { int total_count = 0, total_initdrv = 0; log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n"); @@ -221,12 +433,16 @@ struct OptRmdffPass : public Pass { assign_map.set(module); dff_init_map.set(module); + mux_drivers.clear(); + init_attributes.clear(); for (auto wire : module->wires()) { if (wire->attributes.count("\\init") != 0) { Const initval = wire->attributes.at("\\init"); - dff_init_map.add(wire, initval); + for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + dff_init_map.add(SigBit(wire, i), initval[i]); for (int i = 0; i < GetSize(wire); i++) { SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit); if (mapped_bit.wire) { @@ -245,6 +461,7 @@ struct OptRmdffPass : public Pass { mux_drivers.clear(); std::vector<RTLIL::IdString> dff_list; + std::vector<RTLIL::IdString> dffsr_list; std::vector<RTLIL::IdString> dlatch_list; for (auto cell : module->cells()) { @@ -262,16 +479,28 @@ struct OptRmdffPass : public Pass { if (!design->selected(module, cell)) continue; + if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_", + "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_", "$dffsr", + "$_DLATCHSR_NNN_", "$_DLATCHSR_NNP_", "$_DLATCHSR_NPN_", "$_DLATCHSR_NPP_", + "$_DLATCHSR_PNN_", "$_DLATCHSR_PNP_", "$_DLATCHSR_PPN_", "$_DLATCHSR_PPP_", "$dlatchsr")) + dffsr_list.push_back(cell->name); + if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_", "$ff", "$dff", "$adff")) dff_list.push_back(cell->name); - if (cell->type == "$dlatch") + if (cell->type.in("$dlatch", "$_DLATCH_P_", "$_DLATCH_N_")) dlatch_list.push_back(cell->name); } + for (auto &id : dffsr_list) { + if (module->cell(id) != nullptr && + handle_dffsr(module, module->cells_[id])) + total_count++; + } + for (auto &id : dff_list) { if (module->cell(id) != nullptr && handle_dff(module, module->cells_[id])) @@ -310,6 +539,7 @@ struct OptRmdffPass : public Pass { assign_map.clear(); mux_drivers.clear(); + init_attributes.clear(); if (total_count || total_initdrv) design->scratchpad_set_bool("opt.did_something", true); diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc new file mode 100644 index 00000000..fc1596eb --- /dev/null +++ b/passes/opt/rmports.cc @@ -0,0 +1,187 @@ +/* + * 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/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct RmportsPassPass : public Pass { + RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" rmports [selection]\n"); + log("\n"); + log("This pass identifies ports in the selected modules which are not used or\n"); + log("driven and removes them.\n"); + log("\n"); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing RMPORTS pass (remove ports with no connections).\n"); + + size_t argidx = 1; + extra_args(args, argidx, design); + + // The set of ports we removed + dict<IdString, pool<IdString>> removed_ports; + + // Find all of the unused ports, and remove them from that module + auto modules = design->selected_modules(); + for(auto mod : modules) + ScanModule(mod, removed_ports); + + // Remove the unused ports from all instances of those modules + for(auto mod : modules) + CleanupModule(mod, removed_ports); + } + + void CleanupModule(Module *module, dict<IdString, pool<IdString>> &removed_ports) + { + log("Removing now-unused cell ports in module %s\n", module->name.c_str()); + + auto cells = module->cells(); + for(auto cell : cells) + { + if(removed_ports.find(cell->type) == removed_ports.end()) + { + // log(" Not touching instance \"%s\" because we didn't remove any ports from module \"%s\"\n", + // cell->name.c_str(), cell->type.c_str()); + continue; + } + + auto ports_to_remove = removed_ports[cell->type]; + for(auto p : ports_to_remove) + { + log(" Removing port \"%s\" from instance \"%s\"\n", + p.c_str(), cell->type.c_str()); + cell->unsetPort(p); + } + } + } + + void ScanModule(Module* module, dict<IdString, pool<IdString>> &removed_ports) + { + log("Finding unconnected ports in module %s\n", module->name.c_str()); + + pool<IdString> used_ports; + + // See what wires are used. + // Start by checking connections between named wires + auto &conns = module->connections(); + for(auto sigsig : conns) + { + auto s1 = sigsig.first; + auto s2 = sigsig.second; + + int len1 = s1.size(); + int len2 = s2.size(); + int len = len1; + if(len2 < len1) + len = len2; + + for(int i=0; i<len; i++) + { + auto w1 = s1[i].wire; + auto w2 = s2[i].wire; + if( (w1 == NULL) || (w2 == NULL) ) + continue; + + //log(" conn %s, %s\n", w1->name.c_str(), w2->name.c_str()); + + if( (w1->port_input || w1->port_output) && (used_ports.find(w1->name) == used_ports.end()) ) + used_ports.insert(w1->name); + + if( (w2->port_input || w2->port_output) && (used_ports.find(w2->name) == used_ports.end()) ) + used_ports.insert(w2->name); + } + } + + // Then check connections to cells + auto cells = module->cells(); + for(auto cell : cells) + { + auto &cconns = cell->connections(); + for(auto conn : cconns) + { + for(int i=0; i<conn.second.size(); i++) + { + auto sig = conn.second[i].wire; + if(sig == NULL) + continue; + + // log(" sig %s\n", sig->name.c_str()); + if( (sig->port_input || sig->port_output) && (used_ports.find(sig->name) == used_ports.end()) ) + used_ports.insert(sig->name); + } + } + } + + // Now that we know what IS used, get rid of anything that isn't in that list + pool<IdString> unused_ports; + for(auto port : module->ports) + { + if(used_ports.find(port) != used_ports.end()) + continue; + unused_ports.insert(port); + } + + // Print the ports out as we go through them + for(auto port : unused_ports) + { + log(" removing unused port %s\n", port.c_str()); + removed_ports[module->name].insert(port); + + // Remove from ports list + for(size_t i=0; i<module->ports.size(); i++) + { + if(module->ports[i] == port) + { + module->ports.erase(module->ports.begin() + i); + break; + } + } + + // Mark the wire as no longer a port + auto wire = module->wire(port); + wire->port_input = false; + wire->port_output = false; + wire->port_id = 0; + } + log("Removed %zu unused ports.\n", unused_ports.size()); + + // Re-number all of the wires that DO have ports still on them + for(size_t i=0; i<module->ports.size(); i++) + { + auto port = module->ports[i]; + auto wire = module->wire(port); + wire->port_id = i+1; + } + } + +} RmportsPassPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 22914eaa..b8028082 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1421,7 +1421,7 @@ struct ShareWorker struct SharePass : public Pass { SharePass() : Pass("share", "perform sat-based resource sharing") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1453,7 +1453,7 @@ struct SharePass : public Pass { log(" Only perform the first N merges, then stop. This is useful for debugging.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { ShareWorkerConfig config; diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 07503fbb..0164f58d 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -353,7 +353,7 @@ struct WreduceWorker struct WreducePass : public Pass { WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -373,7 +373,7 @@ struct WreducePass : public Pass { log(" flows that use the 'memory_memx' pass.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, Design *design) + void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { WreduceConfig config; bool opt_memx = false; diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index d5366f26..ef7cb0f7 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct ProcPass : public Pass { ProcPass() : Pass("proc", "translate processes to netlists") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -57,7 +57,7 @@ struct ProcPass : public Pass { log(" executed in -ifx mode.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string global_arst; bool ifxmode = false; diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 216b00dd..b69eba3f 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -203,7 +203,7 @@ restart_proc_arst: struct ProcArstPass : public Pass { ProcArstPass() : Pass("proc_arst", "detect asynchronous resets") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -221,7 +221,7 @@ struct ProcArstPass : public Pass { log(" in the 'init' attribute on the net.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string global_arst; bool global_arst_neg = false; diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 7dbabc21..b9e43d1d 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -143,7 +143,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) struct ProcCleanPass : public Pass { ProcCleanPass() : Pass("proc_clean", "remove empty parts of processes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -153,7 +153,7 @@ struct ProcCleanPass : public Pass { log("if it contains only empty structures.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { int total_count = 0; log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 98653dc6..519d35cd 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -322,6 +322,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) } } + SigSpec sig_q = sig; ce.assign_map.apply(insig); ce.assign_map.apply(rstval); ce.assign_map.apply(sig); @@ -350,13 +351,13 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) else if (!rstval.is_fully_const() && !ce.eval(rstval)) { log_warning("Async reset value `%s' is not constant!\n", log_signal(rstval)); - gen_dffsr(mod, insig, rstval, sig, + gen_dffsr(mod, insig, rstval, sig_q, sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, sync_edge->signal, sync_level->signal, proc); } else - gen_dff(mod, insig, rstval.as_const(), sig, + gen_dff(mod, insig, rstval.as_const(), sig_q, sync_edge && sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, sync_edge ? sync_edge->signal : SigSpec(), @@ -369,7 +370,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) struct ProcDffPass : public Pass { ProcDffPass() : Pass("proc_dff", "extract flip-flops from processes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -379,7 +380,7 @@ struct ProcDffPass : public Pass { log("d-type flip-flop cells.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n"); diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 6621afd3..d9d5dfbe 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -217,7 +217,7 @@ struct proc_dlatch_db_t return make_inner(children); } - SigBit make_hold(int n) + SigBit make_hold(int n, string &src) { if (n == true_node) return State::S1; @@ -235,20 +235,20 @@ struct proc_dlatch_db_t if (rule.match == State::S1) and_bits.append(rule.signal); else if (rule.match == State::S0) - and_bits.append(module->Not(NEW_ID, rule.signal)); + and_bits.append(module->Not(NEW_ID, rule.signal, false, src)); else - and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match)); + and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match, false, src)); } if (!rule.children.empty()) { SigSpec or_bits; for (int k : rule.children) - or_bits.append(make_hold(k)); - and_bits.append(module->ReduceOr(NEW_ID, or_bits)); + or_bits.append(make_hold(k, src)); + and_bits.append(module->ReduceOr(NEW_ID, or_bits, false, src)); } if (GetSize(and_bits) == 2) - and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]); + and_bits = module->And(NEW_ID, and_bits[0], and_bits[1], false, src); log_assert(GetSize(and_bits) == 1); rules_sig[n] = and_bits[0]; @@ -340,6 +340,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) RTLIL::SigSig latches_bits, nolatches_bits; dict<SigBit, SigBit> latches_out_in; dict<SigBit, int> latches_hold; + std::string src = proc->get_src_attribute(); for (auto sr : proc->syncs) { @@ -405,7 +406,8 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) SigSpec lhs = latches_bits.first.extract(offset, width); SigSpec rhs = latches_bits.second.extract(offset, width); - Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs); + Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n, src)), rhs, lhs); + cell->set_src_attribute(src); db.generated_dlatches.insert(cell); log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", @@ -420,7 +422,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) struct ProcDlatchPass : public Pass { ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -430,7 +432,7 @@ struct ProcDlatchPass : public Pass { log("d-type latches.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n"); diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index 0c8fb83d..e2dc07e5 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -102,7 +102,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) struct ProcInitPass : public Pass { ProcInitPass() : Pass("proc_init", "convert initial block to init attributes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -113,7 +113,7 @@ struct ProcInitPass : public Pass { log("respective wire.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_INIT pass (extract init attributes).\n"); diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 57e131ca..1329c1fe 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -382,7 +382,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) struct ProcMuxPass : public Pass { ProcMuxPass() : Pass("proc_mux", "convert decision trees to multiplexers") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -396,7 +396,7 @@ struct ProcMuxPass : public Pass { log(" 'case' expressions and 'if' conditions.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool ifxmode = false; log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index 5672fb47..7c334e66 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -65,7 +65,7 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) struct ProcRmdeadPass : public Pass { ProcRmdeadPass() : Pass("proc_rmdead", "eliminate dead trees in decision trees") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -74,7 +74,7 @@ struct ProcRmdeadPass : public Pass { log("This pass identifies unreachable branches in decision trees and removes them.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index 6785b750..8ab0280c 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -2,8 +2,10 @@ OBJS += passes/sat/sat.o OBJS += passes/sat/freduce.o OBJS += passes/sat/eval.o +OBJS += passes/sat/sim.o OBJS += passes/sat/miter.o OBJS += passes/sat/expose.o OBJS += passes/sat/assertpmux.o OBJS += passes/sat/clk2fflogic.o +OBJS += passes/sat/async2sync.o diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc index 63a90767..509cb0ba 100644 --- a/passes/sat/assertpmux.cc +++ b/passes/sat/assertpmux.cc @@ -181,7 +181,7 @@ struct AssertpmuxWorker struct AssertpmuxPass : public Pass { AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -199,7 +199,7 @@ struct AssertpmuxPass : public Pass { log(" additional constrained and check the $pmux condition always.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool flag_noinit = false; bool flag_always = false; diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc new file mode 100644 index 00000000..c92db711 --- /dev/null +++ b/passes/sat/async2sync.cc @@ -0,0 +1,147 @@ +/* + * 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 Async2syncPass : public Pass { + Async2syncPass() : Pass("async2sync", "convert async FF inputs to sync circuits") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" async2sync [options] [selection]\n"); + log("\n"); + log("This command replaces async FF inputs with sync circuits emulating the same\n"); + log("behavior for when the async signals are actually synchronized to the clock.\n"); + log("\n"); + log("This pass assumes negative hold time for the async FF inputs. For example when\n"); + log("a reset deasserts with the clock edge, then the FF output will still drive the\n"); + log("reset value in the next cycle regardless of the data-in value at the time of\n"); + log("the clock edge.\n"); + log("\n"); + log("Currently only $adff cells are supported by this pass.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + // bool flag_noinit = false; + + log_header(design, "Executing ASYNC2SYNC pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-noinit") { + // flag_noinit = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict<SigBit, State> initbits; + pool<SigBit> del_initbits; + + for (auto wire : module->wires()) + if (wire->attributes.count("\\init") > 0) + { + Const initval = wire->attributes.at("\\init"); + SigSpec initsig = sigmap(wire); + + for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + initbits[initsig[i]] = initval[i]; + } + + for (auto cell : vector<Cell*>(module->selected_cells())) + { + if (cell->type.in("$adff")) + { + // bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool(); + bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool(); + Const arst_val = cell->parameters["\\ARST_VALUE"]; + + SigSpec sig_clk = cell->getPort("\\CLK"); + SigSpec sig_arst = cell->getPort("\\ARST"); + SigSpec sig_d = cell->getPort("\\D"); + SigSpec sig_q = cell->getPort("\\Q"); + + log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(sig_arst), log_signal(sig_d), log_signal(sig_q)); + + Const init_val; + for (int i = 0; i < GetSize(sig_q); i++) { + SigBit bit = sigmap(sig_q[i]); + init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx); + del_initbits.insert(bit); + } + + Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d)); + Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q)); + new_q->attributes["\\init"] = init_val; + + if (arst_pol) { + module->addMux(NEW_ID, sig_d, arst_val, sig_arst, new_d); + module->addMux(NEW_ID, new_q, arst_val, sig_arst, sig_q); + } else { + module->addMux(NEW_ID, arst_val, sig_d, sig_arst, new_d); + module->addMux(NEW_ID, arst_val, new_q, sig_arst, sig_q); + } + + cell->setPort("\\D", new_d); + cell->setPort("\\Q", new_q); + cell->unsetPort("\\ARST"); + cell->unsetParam("\\ARST_POLARITY"); + cell->unsetParam("\\ARST_VALUE"); + cell->type = "$dff"; + continue; + } + } + + for (auto wire : module->wires()) + if (wire->attributes.count("\\init") > 0) + { + bool delete_initattr = true; + Const initval = wire->attributes.at("\\init"); + SigSpec initsig = sigmap(wire); + + for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++) + if (del_initbits.count(initsig[i]) > 0) + initval[i] = State::Sx; + else if (initval[i] != State::Sx) + delete_initattr = false; + + if (delete_initattr) + wire->attributes.erase("\\init"); + else + wire->attributes.at("\\init") = initval; + } + } + } +} Async2syncPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index ef6d5dd7..49ec795d 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct Clk2fflogicPass : public Pass { Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -36,7 +36,7 @@ struct Clk2fflogicPass : public Pass { log("multiple clocks.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { // bool flag_noinit = false; @@ -72,7 +72,88 @@ struct Clk2fflogicPass : public Pass { for (auto cell : vector<Cell*>(module->selected_cells())) { - if (cell->type.in("$dlatch")) + if (cell->type.in("$mem")) + { + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int rd_ports = cell->getParam("\\RD_PORTS").as_int(); + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); + + for (int i = 0; i < rd_ports; i++) { + if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool()) + log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! " + "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(cell), log_id(module)); + } + + Const wr_clk_en_param = cell->getParam("\\WR_CLK_ENABLE"); + Const wr_clk_pol_param = cell->getParam("\\WR_CLK_POLARITY"); + + SigSpec wr_clk_port = cell->getPort("\\WR_CLK"); + SigSpec wr_en_port = cell->getPort("\\WR_EN"); + SigSpec wr_addr_port = cell->getPort("\\WR_ADDR"); + SigSpec wr_data_port = cell->getPort("\\WR_DATA"); + + for (int wport = 0; wport < wr_ports; wport++) + { + bool clken = wr_clk_en_param[wport] == State::S1; + bool clkpol = wr_clk_pol_param[wport] == State::S1; + + if (!clken) + continue; + + SigBit clk = wr_clk_port[wport]; + SigSpec en = wr_en_port.extract(wport*width, width); + SigSpec addr = wr_addr_port.extract(wport*abits, abits); + SigSpec data = wr_data_port.extract(wport*width, width); + + log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n", + wport, log_id(module), log_id(cell), log_signal(clk), + log_signal(addr), log_signal(data)); + + Wire *past_clk = module->addWire(NEW_ID); + past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0; + module->addFf(NEW_ID, clk, past_clk); + + SigSpec clock_edge_pattern; + + if (clkpol) { + clock_edge_pattern.append_bit(State::S0); + clock_edge_pattern.append_bit(State::S1); + } else { + clock_edge_pattern.append_bit(State::S1); + clock_edge_pattern.append_bit(State::S0); + } + + SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern); + + SigSpec en_q = module->addWire(NEW_ID, GetSize(en)); + module->addFf(NEW_ID, en, en_q); + + SigSpec addr_q = module->addWire(NEW_ID, GetSize(addr)); + module->addFf(NEW_ID, addr, addr_q); + + SigSpec data_q = module->addWire(NEW_ID, GetSize(data)); + module->addFf(NEW_ID, data, data_q); + + wr_clk_port[wport] = State::S0; + wr_en_port.replace(wport*width, module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge)); + wr_addr_port.replace(wport*abits, addr_q); + wr_data_port.replace(wport*width, data_q); + + wr_clk_en_param[wport] = State::S0; + wr_clk_pol_param[wport] = State::S0; + } + + cell->setParam("\\WR_CLK_ENABLE", wr_clk_en_param); + cell->setParam("\\WR_CLK_POLARITY", wr_clk_pol_param); + + cell->setPort("\\WR_CLK", wr_clk_port); + cell->setPort("\\WR_EN", wr_en_port); + cell->setPort("\\WR_ADDR", wr_addr_port); + cell->setPort("\\WR_DATA", wr_data_port); + } + + if (cell->type.in("$dlatch", "$dlatchsr")) { bool enpol = cell->parameters["\\EN_POLARITY"].as_bool(); @@ -87,10 +168,31 @@ struct Clk2fflogicPass : public Pass { Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q)); module->addFf(NEW_ID, sig_q, past_q); - if (enpol) - module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q); + if (cell->type == "$dlatch") + { + if (enpol) + module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q); + else + module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q); + } else - module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q); + { + SigSpec t; + if (enpol) + t = module->Mux(NEW_ID, past_q, sig_d, sig_en); + else + t = module->Mux(NEW_ID, sig_d, past_q, sig_en); + + SigSpec s = cell->getPort("\\SET"); + if (!cell->parameters["\\SET_POLARITY"].as_bool()) + s = module->Not(NEW_ID, s); + t = module->Or(NEW_ID, t, s); + + SigSpec c = cell->getPort("\\CLR"); + if (cell->parameters["\\CLR_POLARITY"].as_bool()) + c = module->Not(NEW_ID, c); + module->addAnd(NEW_ID, t, c, sig_q); + } Const initval; bool assign_initval = false; diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 09f69cc5..008cd2df 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -360,7 +360,7 @@ struct VlogHammerReporter struct EvalPass : public Pass { EvalPass() : Pass("eval", "evaluate the circuit given an input") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -383,7 +383,7 @@ struct EvalPass : public Pass { log(" then all output ports of the current module are used.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<std::pair<std::string, std::string>> sets; std::vector<std::string> shows, tables; diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 9427547f..80934548 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -220,7 +220,7 @@ RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width struct ExposePass : public Pass { ExposePass() : Pass("expose", "convert internal signals to module ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -236,6 +236,10 @@ struct ExposePass : public Pass { log(" when exposing a wire, create an input/output pair and cut the internal\n"); log(" signal path at that wire.\n"); log("\n"); + log(" -input\n"); + log(" when exposing a wire, create an input port and disconnect the internal\n"); + log(" driver.\n"); + log("\n"); log(" -shared\n"); log(" only expose those signals that are shared among the selected modules.\n"); log(" this is useful for preparing modules for equivalence checking.\n"); @@ -253,12 +257,13 @@ struct ExposePass : public Pass { log(" designator for the exposed signal.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool flag_shared = false; bool flag_evert = false; bool flag_dff = false; bool flag_cut = false; + bool flag_input = false; bool flag_evert_dff = false; std::string sep = "."; @@ -279,10 +284,14 @@ struct ExposePass : public Pass { flag_dff = true; continue; } - if (args[argidx] == "-cut") { + if (args[argidx] == "-cut" && !flag_input) { flag_cut = true; continue; } + if (args[argidx] == "-input" && !flag_cut) { + flag_input = true; + continue; + } if (args[argidx] == "-evert-dff") { flag_evert_dff = true; continue; @@ -464,16 +473,42 @@ struct ExposePass : public Pass { continue; } - if (!it.second->port_output) { - it.second->port_output = true; - log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name)); + if (flag_input) + { + if (!it.second->port_input) { + it.second->port_input = true; + log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name)); + RTLIL::Wire *w = module->addWire(NEW_ID, GetSize(it.second)); + out_to_in_map.add(it.second, w); + } + } + else + { + if (!it.second->port_output) { + it.second->port_output = true; + log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name)); + } + + if (flag_cut) { + RTLIL::Wire *in_wire = add_new_wire(module, it.second->name.str() + sep + "i", it.second->width); + in_wire->port_input = true; + out_to_in_map.add(sigmap(it.second), in_wire); + } } + } - if (flag_cut) { - RTLIL::Wire *in_wire = add_new_wire(module, it.second->name.str() + sep + "i", it.second->width); - in_wire->port_input = true; - out_to_in_map.add(sigmap(it.second), in_wire); + if (flag_input) + { + for (auto &it : module->cells_) { + if (!ct.cell_known(it.second->type)) + continue; + for (auto &conn : it.second->connections_) + if (ct.cell_output(it.second->type, conn.first)) + conn.second = out_to_in_map(sigmap(conn.second)); } + + for (auto &conn : module->connections_) + conn.first = out_to_in_map(sigmap(conn.first)); } if (flag_cut) diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index 77263f6a..f2963163 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -687,7 +687,8 @@ struct FreduceWorker } std::map<RTLIL::SigBit, int> bitusage; - module->rewrite_sigspecs(CountBitUsage(sigmap, bitusage)); + CountBitUsage bitusage_worker(sigmap, bitusage); + module->rewrite_sigspecs(bitusage_worker); if (!dump_prefix.empty()) dump(); @@ -759,7 +760,7 @@ struct FreduceWorker struct FreducePass : public Pass { FreducePass() : Pass("freduce", "perform functional reduction") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -790,7 +791,7 @@ struct FreducePass : public Pass { log("circuit that is analyzed.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { reduce_counter = 0; reduce_stop_at = 0; diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 9e150b60..d37f1b12 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -358,7 +358,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL struct MiterPass : public Pass { MiterPass() : Pass("miter", "automatically create a miter circuit") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -402,7 +402,7 @@ struct MiterPass : public Pass { log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { if (args.size() > 1 && args[1] == "-equiv") { create_miter_equiv(this, args, design); diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index a6ac7afd..695a03e1 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -691,7 +691,6 @@ struct SatHelper // VCD has some limits on internal (non-display) identifier names, so make legal ones std::map<std::string, std::string> vcdnames; - fprintf(f, "$timescale 1ns\n"); // arbitrary time scale since actual clock period is unknown/unimportant fprintf(f, "$scope module %s $end\n", module->name.c_str()); for (auto &info : modelInfo) { @@ -891,7 +890,7 @@ void print_qed() struct SatPass : public Pass { SatPass() : Pass("sat", "solve a SAT problem in the circuit") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1058,7 +1057,7 @@ struct SatPass : public Pass { log(" Like -falsify but do not return an error for timeouts.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<std::pair<std::string, std::string>> sets, sets_init, prove, prove_x; std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at; diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc new file mode 100644 index 00000000..fadffcdb --- /dev/null +++ b/passes/sat/sim.cc @@ -0,0 +1,862 @@ +/* + * 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 SimShared +{ + bool debug = false; + bool hide_internal = true; + bool writeback = false; + bool zinit = false; + int rstlen = 1; +}; + +void zinit(State &v) +{ + if (v != State::S1) + v = State::S0; +} + +void zinit(Const &v) +{ + for (auto &bit : v.bits) + zinit(bit); +} + +struct SimInstance +{ + SimShared *shared; + + Module *module; + Cell *instance; + + SimInstance *parent; + dict<Cell*, SimInstance*> children; + + SigMap sigmap; + dict<SigBit, State> state_nets; + dict<SigBit, pool<Cell*>> upd_cells; + dict<SigBit, pool<Wire*>> upd_outports; + + pool<SigBit> dirty_bits; + pool<Cell*> dirty_cells; + pool<SimInstance*, hash_ptr_ops> dirty_children; + + struct ff_state_t + { + State past_clock; + Const past_d; + }; + + struct mem_state_t + { + Const past_wr_clk; + Const past_wr_en; + Const past_wr_addr; + Const past_wr_data; + Const data; + }; + + dict<Cell*, ff_state_t> ff_database; + dict<Cell*, mem_state_t> mem_database; + pool<Cell*> formal_database; + + dict<Wire*, pair<int, Const>> vcd_database; + + SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : + shared(shared), module(module), instance(instance), parent(parent), sigmap(module) + { + if (parent) { + log_assert(parent->children.count(instance) == 0); + parent->children[instance] = this; + } + + for (auto wire : module->wires()) + { + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(sig); i++) { + if (state_nets.count(sig[i]) == 0) + state_nets[sig[i]] = State::Sx; + if (wire->port_output) { + upd_outports[sig[i]].insert(wire); + dirty_bits.insert(sig[i]); + } + } + + if (wire->attributes.count("\\init")) { + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) { + state_nets[sig[i]] = initval[i]; + dirty_bits.insert(sig[i]); + } + } + } + + for (auto cell : module->cells()) + { + Module *mod = module->design->module(cell->type); + + if (mod != nullptr) { + dirty_children.insert(new SimInstance(shared, mod, cell, this)); + } + + for (auto &port : cell->connections()) { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + upd_cells[bit].insert(cell); + } + + if (cell->type.in("$dff")) { + ff_state_t ff; + ff.past_clock = State::Sx; + ff.past_d = Const(State::Sx, cell->getParam("\\WIDTH").as_int()); + ff_database[cell] = ff; + } + + if (cell->type == "$mem") + { + mem_state_t mem; + + mem.past_wr_clk = Const(State::Sx, GetSize(cell->getPort("\\WR_CLK"))); + mem.past_wr_en = Const(State::Sx, GetSize(cell->getPort("\\WR_EN"))); + mem.past_wr_addr = Const(State::Sx, GetSize(cell->getPort("\\WR_ADDR"))); + mem.past_wr_data = Const(State::Sx, GetSize(cell->getPort("\\WR_DATA"))); + + mem.data = cell->getParam("\\INIT"); + int sz = cell->getParam("\\SIZE").as_int() * cell->getParam("\\WIDTH").as_int(); + + if (GetSize(mem.data) > sz) + mem.data.bits.resize(sz); + + while (GetSize(mem.data) < sz) + mem.data.bits.push_back(State::Sx); + + mem_database[cell] = mem; + } + + if (cell->type.in("$assert", "$cover", "$assume")) { + formal_database.insert(cell); + } + } + + if (shared->zinit) + { + for (auto &it : ff_database) + { + Cell *cell = it.first; + ff_state_t &ff = it.second; + zinit(ff.past_d); + + SigSpec qsig = cell->getPort("\\Q"); + Const qdata = get_state(qsig); + zinit(qdata); + set_state(qsig, qdata); + } + + for (auto &it : mem_database) { + mem_state_t &mem = it.second; + zinit(mem.past_wr_en); + zinit(mem.data); + } + } + } + + ~SimInstance() + { + for (auto child : children) + delete child.second; + } + + IdString name() const + { + if (instance != nullptr) + return instance->name; + return module->name; + } + + std::string hiername() const + { + if (instance != nullptr) + return parent->hiername() + "." + log_id(instance->name); + + return log_id(module->name); + } + + Const get_state(SigSpec sig) + { + Const value; + + for (auto bit : sigmap(sig)) + if (bit.wire == nullptr) + value.bits.push_back(bit.data); + else if (state_nets.count(bit)) + value.bits.push_back(state_nets.at(bit)); + else + value.bits.push_back(State::Sz); + + if (shared->debug) + log("[%s] get %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value)); + return value; + } + + bool set_state(SigSpec sig, Const value) + { + bool did_something = false; + + sig = sigmap(sig); + log_assert(GetSize(sig) == GetSize(value)); + + for (int i = 0; i < GetSize(sig); i++) + if (state_nets.at(sig[i]) != value[i]) { + state_nets.at(sig[i]) = value[i]; + dirty_bits.insert(sig[i]); + did_something = true; + } + + if (shared->debug) + log("[%s] set %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value)); + return did_something; + } + + void update_cell(Cell *cell) + { + if (ff_database.count(cell)) + return; + + if (formal_database.count(cell)) + return; + + if (mem_database.count(cell)) + { + mem_state_t &mem = mem_database.at(cell); + + int num_rd_ports = cell->getParam("\\RD_PORTS").as_int(); + + int size = cell->getParam("\\SIZE").as_int(); + int offset = cell->getParam("\\OFFSET").as_int(); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + + if (cell->getParam("\\RD_CLK_ENABLE").as_bool()) + log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(cell)); + + SigSpec rd_addr_sig = cell->getPort("\\RD_ADDR"); + SigSpec rd_data_sig = cell->getPort("\\RD_DATA"); + + for (int port_idx = 0; port_idx < num_rd_ports; port_idx++) + { + Const addr = get_state(rd_addr_sig.extract(port_idx*abits, abits)); + Const data = Const(State::Sx, width); + + if (addr.is_fully_def()) { + int index = addr.as_int() - offset; + if (index >= 0 && index < size) + data = mem.data.extract(index*width, width); + } + + set_state(rd_data_sig.extract(port_idx*width, width), data); + } + + return; + } + + if (children.count(cell)) + { + auto child = children.at(cell); + for (auto &conn: cell->connections()) + if (cell->input(conn.first)) { + Const value = get_state(conn.second); + child->set_state(child->module->wire(conn.first), value); + } + dirty_children.insert(child); + return; + } + + if (yosys_celltypes.cell_evaluable(cell->type)) + { + RTLIL::SigSpec sig_a, sig_b, sig_c, sig_d, sig_s, sig_y; + bool has_a, has_b, has_c, has_d, has_s, has_y; + + has_a = cell->hasPort("\\A"); + has_b = cell->hasPort("\\B"); + has_c = cell->hasPort("\\C"); + has_d = cell->hasPort("\\D"); + has_s = cell->hasPort("\\S"); + has_y = cell->hasPort("\\Y"); + + if (has_a) sig_a = cell->getPort("\\A"); + if (has_b) sig_b = cell->getPort("\\B"); + if (has_c) sig_c = cell->getPort("\\C"); + if (has_d) sig_d = cell->getPort("\\D"); + if (has_s) sig_s = cell->getPort("\\S"); + if (has_y) sig_y = cell->getPort("\\Y"); + + if (shared->debug) + log("[%s] eval %s (%s)\n", hiername().c_str(), log_id(cell), log_id(cell->type)); + + // Simple (A -> Y) and (A,B -> Y) cells + if (has_a && !has_c && !has_d && !has_s && has_y) { + set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b))); + return; + } + + // (A,B,C -> Y) cells + if (has_a && has_b && has_c && !has_d && !has_s && has_y) { + set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c))); + return; + } + + // (A,B,S -> Y) cells + if (has_a && has_b && !has_c && !has_d && has_s && has_y) { + set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s))); + return; + } + + log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); + return; + } + + log_error("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); + } + + void update_ph1() + { + pool<Cell*> queue_cells; + pool<Wire*> queue_outports; + + queue_cells.swap(dirty_cells); + + while (1) + { + for (auto bit : dirty_bits) + { + if (upd_cells.count(bit)) + for (auto cell : upd_cells.at(bit)) + queue_cells.insert(cell); + + if (upd_outports.count(bit) && parent != nullptr) + for (auto wire : upd_outports.at(bit)) + queue_outports.insert(wire); + } + + dirty_bits.clear(); + + if (!queue_cells.empty()) + { + for (auto cell : queue_cells) + update_cell(cell); + + queue_cells.clear(); + continue; + } + + for (auto wire : queue_outports) + if (instance->hasPort(wire->name)) { + Const value = get_state(wire); + parent->set_state(instance->getPort(wire->name), value); + } + + queue_outports.clear(); + + for (auto child : dirty_children) + child->update_ph1(); + + dirty_children.clear(); + + if (dirty_bits.empty()) + break; + } + } + + bool update_ph2() + { + bool did_something = false; + + for (auto &it : ff_database) + { + Cell *cell = it.first; + ff_state_t &ff = it.second; + + if (cell->type.in("$dff")) + { + bool clkpol = cell->getParam("\\CLK_POLARITY").as_bool(); + State current_clock = get_state(cell->getPort("\\CLK"))[0]; + + if (clkpol ? (ff.past_clock == State::S1 || current_clock != State::S1) : + (ff.past_clock == State::S0 || current_clock != State::S0)) + continue; + + if (set_state(cell->getPort("\\Q"), ff.past_d)) + did_something = true; + } + } + + for (auto &it : mem_database) + { + Cell *cell = it.first; + mem_state_t &mem = it.second; + + int num_wr_ports = cell->getParam("\\WR_PORTS").as_int(); + + int size = cell->getParam("\\SIZE").as_int(); + int offset = cell->getParam("\\OFFSET").as_int(); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + + Const wr_clk_enable = cell->getParam("\\WR_CLK_ENABLE"); + Const wr_clk_polarity = cell->getParam("\\WR_CLK_POLARITY"); + Const current_wr_clk = get_state(cell->getPort("\\WR_CLK")); + + for (int port_idx = 0; port_idx < num_wr_ports; port_idx++) + { + Const addr, data, enable; + + if (wr_clk_enable[port_idx] == State::S0) + { + addr = get_state(cell->getPort("\\WR_ADDR").extract(port_idx*abits, abits)); + data = get_state(cell->getPort("\\WR_DATA").extract(port_idx*width, width)); + enable = get_state(cell->getPort("\\WR_EN").extract(port_idx*width, width)); + } + else + { + if (wr_clk_polarity[port_idx] == State::S1 ? + (mem.past_wr_clk[port_idx] == State::S1 || current_wr_clk[port_idx] != State::S1) : + (mem.past_wr_clk[port_idx] == State::S0 || current_wr_clk[port_idx] != State::S0)) + continue; + + addr = mem.past_wr_addr.extract(port_idx*abits, abits); + data = mem.past_wr_data.extract(port_idx*width, width); + enable = mem.past_wr_en.extract(port_idx*width, width); + } + + if (addr.is_fully_def()) + { + int index = addr.as_int() - offset; + if (index >= 0 && index < size) + for (int i = 0; i < width; i++) + if (enable[i] == State::S1 && mem.data.bits.at(index*width+i) != data[i]) { + mem.data.bits.at(index*width+i) = data[i]; + dirty_cells.insert(cell); + did_something = true; + } + } + } + } + + for (auto it : children) + if (it.second->update_ph2()) { + dirty_children.insert(it.second); + did_something = true; + } + + return did_something; + } + + void update_ph3() + { + for (auto &it : ff_database) + { + Cell *cell = it.first; + ff_state_t &ff = it.second; + + if (cell->type.in("$dff")) { + ff.past_clock = get_state(cell->getPort("\\CLK"))[0]; + ff.past_d = get_state(cell->getPort("\\D")); + } + } + + for (auto &it : mem_database) + { + Cell *cell = it.first; + mem_state_t &mem = it.second; + + mem.past_wr_clk = get_state(cell->getPort("\\WR_CLK")); + mem.past_wr_en = get_state(cell->getPort("\\WR_EN")); + mem.past_wr_addr = get_state(cell->getPort("\\WR_ADDR")); + mem.past_wr_data = get_state(cell->getPort("\\WR_DATA")); + } + + for (auto cell : formal_database) + { + string label = log_id(cell); + if (cell->attributes.count("\\src")) + label = cell->attributes.at("\\src").decode_string(); + + State a = get_state(cell->getPort("\\A"))[0]; + State en = get_state(cell->getPort("\\EN"))[0]; + + if (cell->type == "$cover" && en == State::S1 && a != State::S1) + log("Cover %s.%s (%s) reached.\n", hiername().c_str(), log_id(cell), label.c_str()); + + if (cell->type == "$assume" && en == State::S1 && a != State::S1) + log("Assumption %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str()); + + if (cell->type == "$assert" && en == State::S1 && a != State::S1) + log_warning("Assert %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str()); + } + + for (auto it : children) + it.second->update_ph3(); + } + + void writeback(pool<Module*> &wbmods) + { + if (wbmods.count(module)) + log_error("Instance %s of module %s is not unique: Writeback not possible. (Fix by running 'uniquify'.)\n", hiername().c_str(), log_id(module)); + + wbmods.insert(module); + + for (auto wire : module->wires()) + wire->attributes.erase("\\init"); + + for (auto &it : ff_database) + { + Cell *cell = it.first; + SigSpec sig_q = cell->getPort("\\Q"); + Const initval = get_state(sig_q); + + for (int i = 0; i < GetSize(sig_q); i++) + { + Wire *w = sig_q[i].wire; + + if (w->attributes.count("\\init") == 0) + w->attributes["\\init"] = Const(State::Sx, GetSize(w)); + + w->attributes["\\init"][sig_q[i].offset] = initval[i]; + } + } + + for (auto &it : mem_database) + { + Cell *cell = it.first; + mem_state_t &mem = it.second; + Const initval = mem.data; + + while (GetSize(initval) >= 2) { + if (initval[GetSize(initval)-1] != State::Sx) break; + if (initval[GetSize(initval)-2] != State::Sx) break; + initval.bits.pop_back(); + } + + cell->setParam("\\INIT", initval); + } + + for (auto it : children) + it.second->writeback(wbmods); + } + + void write_vcd_header(std::ofstream &f, int &id) + { + f << stringf("$scope module %s $end\n", log_id(name())); + + for (auto wire : module->wires()) + { + if (shared->hide_internal && wire->name[0] == '$') + continue; + + f << stringf("$var wire %d n%d %s%s $end\n", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire)); + vcd_database[wire] = make_pair(id++, Const()); + } + + for (auto child : children) + child.second->write_vcd_header(f, id); + + f << stringf("$upscope $end\n"); + } + + void write_vcd_step(std::ofstream &f) + { + for (auto &it : vcd_database) + { + Wire *wire = it.first; + Const value = get_state(wire); + int id = it.second.first; + + if (it.second.second == value) + continue; + + it.second.second = value; + + f << "b"; + for (int i = GetSize(value)-1; i >= 0; i--) { + switch (value[i]) { + case State::S0: f << "0"; break; + case State::S1: f << "1"; break; + case State::Sx: f << "x"; break; + default: f << "z"; + } + } + + f << stringf(" n%d\n", id); + } + + for (auto child : children) + child.second->write_vcd_step(f); + } +}; + +struct SimWorker : SimShared +{ + SimInstance *top = nullptr; + std::ofstream vcdfile; + pool<IdString> clock, clockn, reset, resetn; + + ~SimWorker() + { + delete top; + } + + void write_vcd_header() + { + if (!vcdfile.is_open()) + return; + + int id = 1; + top->write_vcd_header(vcdfile, id); + + vcdfile << stringf("$enddefinitions $end\n"); + } + + void write_vcd_step(int t) + { + if (!vcdfile.is_open()) + return; + + vcdfile << stringf("#%d\n", t); + top->write_vcd_step(vcdfile); + } + + void update() + { + while (1) + { + if (debug) + log("\n-- ph1 --\n"); + + top->update_ph1(); + + if (debug) + log("\n-- ph2 --\n"); + + if (!top->update_ph2()) + break; + } + + if (debug) + log("\n-- ph3 --\n"); + + top->update_ph3(); + } + + void set_inports(pool<IdString> ports, State value) + { + for (auto portname : ports) + { + Wire *w = top->module->wire(portname); + + if (w == nullptr) + log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module)); + + top->set_state(w, value); + } + } + + void run(Module *topmod, int numcycles) + { + log_assert(top == nullptr); + top = new SimInstance(this, topmod); + + if (debug) + log("\n===== 0 =====\n"); + else + log("Simulating cycle 0.\n"); + + set_inports(reset, State::S1); + set_inports(resetn, State::S0); + + set_inports(clock, State::Sx); + set_inports(clockn, State::Sx); + + update(); + + write_vcd_header(); + write_vcd_step(0); + + for (int cycle = 0; cycle < numcycles; cycle++) + { + if (debug) + log("\n===== %d =====\n", 10*cycle + 5); + + set_inports(clock, State::S0); + set_inports(clockn, State::S1); + + update(); + write_vcd_step(10*cycle + 5); + + if (debug) + log("\n===== %d =====\n", 10*cycle + 10); + else + log("Simulating cycle %d.\n", cycle+1); + + set_inports(clock, State::S1); + set_inports(clockn, State::S0); + + if (cycle+1 == rstlen) { + set_inports(reset, State::S0); + set_inports(resetn, State::S1); + } + + update(); + write_vcd_step(10*cycle + 10); + } + + write_vcd_step(10*numcycles + 2); + + if (writeback) { + pool<Module*> wbmods; + top->writeback(wbmods); + } + } +}; + +struct SimPass : public Pass { + SimPass() : Pass("sim", "simulate the circuit") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" sim [options] [top-level]\n"); + log("\n"); + log("This command simulates the circuit using the given top-level module.\n"); + log("\n"); + log(" -vcd <filename>\n"); + log(" write the simulation results to the given VCD file\n"); + log("\n"); + log(" -clock <portname>\n"); + log(" name of top-level clock input\n"); + log("\n"); + log(" -clockn <portname>\n"); + log(" name of top-level clock input (inverse polarity)\n"); + log("\n"); + log(" -reset <portname>\n"); + log(" name of top-level reset input (active high)\n"); + log("\n"); + log(" -resetn <portname>\n"); + log(" name of top-level inverted reset input (active low)\n"); + log("\n"); + log(" -rstlen <integer>\n"); + log(" number of cycles reset should stay active (default: 1)\n"); + log("\n"); + log(" -zinit\n"); + log(" zero-initialize all uninitialized regs and memories\n"); + log("\n"); + log(" -n <integer>\n"); + log(" number of cycles to simulate (default: 20)\n"); + log("\n"); + log(" -a\n"); + log(" include all nets in VCD output, nut just those with public names\n"); + log("\n"); + log(" -w\n"); + log(" writeback mode: use final simulation state as new init state\n"); + log("\n"); + log(" -d\n"); + log(" enable debug output\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + SimWorker worker; + int numcycles = 20; + + log_header(design, "Executing SIM pass (simulate the circuit).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-vcd" && argidx+1 < args.size()) { + worker.vcdfile.open(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-n" && argidx+1 < args.size()) { + numcycles = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-rstlen" && argidx+1 < args.size()) { + worker.rstlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-clock" && argidx+1 < args.size()) { + worker.clock.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-clockn" && argidx+1 < args.size()) { + worker.clockn.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-reset" && argidx+1 < args.size()) { + worker.reset.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-resetn" && argidx+1 < args.size()) { + worker.resetn.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-a") { + worker.hide_internal = false; + continue; + } + if (args[argidx] == "-d") { + worker.debug = true; + continue; + } + if (args[argidx] == "-w") { + worker.writeback = true; + continue; + } + if (args[argidx] == "-zinit") { + worker.zinit = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + Module *top_mod = nullptr; + + if (design->full_selection()) { + top_mod = design->top_module(); + } else { + auto mods = design->selected_whole_modules(); + if (GetSize(mods) != 1) + log_cmd_error("Only one top module must be selected.\n"); + top_mod = mods.front(); + } + + worker.run(top_mod, numcycles); + } +} SimPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 311a1af9..4faa0ab0 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -16,6 +16,9 @@ ifneq ($(SMALL),1) OBJS += passes/techmap/iopadmap.o OBJS += passes/techmap/hilomap.o OBJS += passes/techmap/extract.o +OBJS += passes/techmap/extract_fa.o +OBJS += passes/techmap/extract_counter.o +OBJS += passes/techmap/extract_reduce.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dff2dffe.o OBJS += passes/techmap/dffinit.o @@ -32,6 +35,7 @@ OBJS += passes/techmap/insbuf.o OBJS += passes/techmap/attrmvcp.o OBJS += passes/techmap/attrmap.o OBJS += passes/techmap/zinit.o +OBJS += passes/techmap/dff2dffs.o endif GENFILES += passes/techmap/techmap.inc @@ -54,4 +58,3 @@ yosys-filterlib$(EXE): passes/techmap/filterlib.o $(Q) mkdir -p $(dir $@) $(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) endif - diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index cc79296c..d2d15a4a 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -29,17 +29,17 @@ // Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025 // http://en.wikipedia.org/wiki/Topological_sorting -#define ABC_COMMAND_LIB "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}" -#define ABC_COMMAND_CTR "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_COMMAND_LUT "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs" -#define ABC_COMMAND_SOP "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; cover {I} {P}" -#define ABC_COMMAND_DFL "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map" - -#define ABC_FAST_COMMAND_LIB "retime -o {D}; map {D}" -#define ABC_FAST_COMMAND_CTR "retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_FAST_COMMAND_LUT "retime -o; if" -#define ABC_FAST_COMMAND_SOP "retime -o; cover -I {I} -P {P}" -#define ABC_FAST_COMMAND_DFL "retime -o; map" +#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put" +#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2" +#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put" + +#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}" +#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "strash; dretime; if" +#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "strash; dretime; map" #include "kernel/register.h" #include "kernel/sigtools.h" @@ -60,6 +60,10 @@ #include "frontends/blif/blifparse.h" +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -74,6 +78,8 @@ enum class gate_type_t { G_NOR, G_XOR, G_XNOR, + G_ANDNOT, + G_ORNOT, G_MUX, G_AOI3, G_OAI3, @@ -90,6 +96,7 @@ struct gate_t int in1, in2, in3, in4; bool is_port; RTLIL::SigBit bit; + RTLIL::State init; }; bool map_mux4; @@ -102,10 +109,13 @@ SigMap assign_map; RTLIL::Module *module; std::vector<gate_t> signal_list; std::map<RTLIL::SigBit, int> signal_map; +std::map<RTLIL::SigBit, RTLIL::State> signal_init; pool<std::string> enabled_gates; +bool recover_init; bool clk_polarity, en_polarity; RTLIL::SigSpec clk_sig, en_sig; +dict<int, std::string> pi_map, po_map; int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1) { @@ -121,6 +131,10 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, gate.in4 = -1; gate.is_port = false; gate.bit = bit; + if (signal_init.count(bit)) + gate.init = signal_init.at(bit); + else + gate.init = State::Sx; signal_list.push_back(gate); signal_map[bit] = gate.id; } @@ -207,7 +221,7 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) return; } - if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_b = cell->getPort("\\B"); @@ -232,6 +246,10 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) map_signal(sig_y, G(XOR), mapped_a, mapped_b); else if (cell->type == "$_XNOR_") map_signal(sig_y, G(XNOR), mapped_a, mapped_b); + else if (cell->type == "$_ANDNOT_") + map_signal(sig_y, G(ANDNOT), mapped_a, mapped_b); + else if (cell->type == "$_ORNOT_") + map_signal(sig_y, G(ORNOT), mapped_a, mapped_b); else log_abort(); @@ -532,11 +550,13 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho } std::string selfdir_name = proc_self_dirname(); - while (1) { - size_t pos = text.find(selfdir_name); - if (pos == std::string::npos) - break; - text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); + if (selfdir_name != "/") { + while (1) { + size_t pos = text.find(selfdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); + } } return text; @@ -588,6 +608,14 @@ struct abc_output_filter void next_line(const std::string &line) { + int pi, po; + if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { + log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", + pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", + po, po_map.count(po) ? po_map.at(po).c_str() : "???"); + return; + } + for (char ch : line) next_char(ch); } @@ -595,7 +623,7 @@ struct abc_output_filter void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, - bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, bool fast_mode, + bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode) { module = current_module; @@ -603,11 +631,12 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin signal_map.clear(); signal_list.clear(); + pi_map.clear(); + po_map.clear(); + recover_init = false; if (clk_str != "$") { - assign_map.set(module); - clk_polarity = true; clk_sig = RTLIL::SigSpec(); @@ -615,6 +644,30 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin en_sig = RTLIL::SigSpec(); } + if (!clk_str.empty() && clk_str != "$") + { + if (clk_str.find(',') != std::string::npos) { + int pos = clk_str.find(','); + std::string en_str = clk_str.substr(pos+1); + clk_str = clk_str.substr(0, pos); + if (en_str[0] == '!') { + en_polarity = false; + en_str = en_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) + en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); + } + if (clk_str[0] == '!') { + clk_polarity = false; + clk_str = clk_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) + clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); + } + + if (dff_mode && clk_sig.empty()) + log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); + std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; if (!cleanup) tempdir_name[0] = tempdir_name[4] = '_'; @@ -652,7 +705,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin all_luts_cost_same = false; abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; if (all_luts_cost_same && !fast_mode) - abc_script += "; lutpack"; + abc_script += "; lutpack {S}"; } else if (!liberty_file.empty()) abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); else if (sop_mode) @@ -660,6 +713,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin else abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; + if (script_file.empty() && !delay_target.empty()) + for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1)) + abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8); + for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); @@ -669,6 +726,9 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3); + for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos)) + abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3); + abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -680,30 +740,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "%s\n", abc_script.c_str()); fclose(f); - if (!clk_str.empty() && clk_str != "$") - { - if (clk_str.find(',') != std::string::npos) { - int pos = clk_str.find(','); - std::string en_str = clk_str.substr(pos+1); - clk_str = clk_str.substr(0, pos); - if (en_str[0] == '!') { - en_polarity = false; - en_str = en_str.substr(1); - } - if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) - en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); - } - if (clk_str[0] == '!') { - clk_polarity = false; - clk_str = clk_str.substr(1); - } - if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) - clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); - } - - if (dff_mode && clk_sig.empty()) - log_error("Clock domain %s not found.\n", clk_str.c_str()); - if (dff_mode || !clk_str.empty()) { if (clk_sig.size() == 0) @@ -749,7 +785,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!si.is_port || si.type != G(NONE)) continue; fprintf(f, " n%d", si.id); - count_input++; + pi_map[count_input++] = log_signal(si.bit); } if (count_input == 0) fprintf(f, " dummy_input\n"); @@ -761,7 +797,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!si.is_port || si.type == G(NONE)) continue; fprintf(f, " n%d", si.id); - count_output++; + po_map[count_output++] = log_signal(si.bit); } fprintf(f, "\n"); @@ -806,6 +842,13 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); fprintf(f, "00 1\n"); fprintf(f, "11 1\n"); + } else if (si.type == G(ANDNOT)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "10 1\n"); + } else if (si.type == G(ORNOT)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "1- 1\n"); + fprintf(f, "-0 1\n"); } else if (si.type == G(MUX)) { fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); fprintf(f, "1-0 1\n"); @@ -829,7 +872,11 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "00-- 1\n"); fprintf(f, "--00 1\n"); } else if (si.type == G(FF)) { - fprintf(f, ".latch n%d n%d\n", si.in1, si.id); + if (si.init == State::S0 || si.init == State::S1) { + fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0); + recover_init = true; + } else + fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id); } else if (si.type != G(NONE)) log_abort(); if (si.type != G(NONE)) @@ -851,38 +898,42 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin f = fopen(buffer.c_str(), "wt"); if (f == NULL) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); - fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); - fprintf(f, "GATE ONE 1 Y=CONST1;\n"); - fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_")); - fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_")); + fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); + fprintf(f, "GATE ONE 1 Y=CONST1;\n"); + fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_")); + fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_")); if (enabled_gates.empty() || enabled_gates.count("AND")) - fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); + fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); if (enabled_gates.empty() || enabled_gates.count("NAND")) - fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); + fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); if (enabled_gates.empty() || enabled_gates.count("OR")) - fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); + fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); if (enabled_gates.empty() || enabled_gates.count("NOR")) - fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); + fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); if (enabled_gates.empty() || enabled_gates.count("XOR")) - fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); + fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); if (enabled_gates.empty() || enabled_gates.count("XNOR")) - fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); + fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); + if (enabled_gates.empty() || enabled_gates.count("ANDNOT")) + fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ANDNOT_")); + if (enabled_gates.empty() || enabled_gates.count("ORNOT")) + fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ORNOT_")); if (enabled_gates.empty() || enabled_gates.count("AOI3")) - fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); + fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); if (enabled_gates.empty() || enabled_gates.count("OAI3")) - fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); + fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); if (enabled_gates.empty() || enabled_gates.count("AOI4")) - fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); + fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); if (enabled_gates.empty() || enabled_gates.count("OAI4")) - fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); + fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); if (enabled_gates.empty() || enabled_gates.count("MUX")) - fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); + fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); if (map_mux4) - fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_")); + fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_")); if (map_mux8) - fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_")); + fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_")); if (map_mux16) - fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_")); + fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_")); fclose(f); if (!lut_costs.empty()) { @@ -898,8 +949,24 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); +#ifndef YOSYS_LINK_ABC abc_output_filter filt(tempdir_name, show_tempdir); int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); +#else + // These needs to be mutable, supposedly due to getopt + char *abc_argv[5]; + string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); + abc_argv[0] = strdup(exe_file.c_str()); + abc_argv[1] = strdup("-s"); + abc_argv[2] = strdup("-f"); + abc_argv[3] = strdup(tmp_script_name.c_str()); + abc_argv[4] = 0; + int ret = Abc_RealMain(4, abc_argv); + free(abc_argv[0]); + free(abc_argv[1]); + free(abc_argv[2]); + free(abc_argv[3]); +#endif if (ret != 0) log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); @@ -954,7 +1021,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } - if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" || c->type == "\\XNOR") { + if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" || + c->type == "\\XNOR" || c->type == "\\ANDNOT" || c->type == "\\ORNOT") { RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); @@ -1130,6 +1198,15 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin module->connect(conn); } + if (recover_init) + for (auto wire : mapped_mod->wires()) { + if (wire->attributes.count("\\init")) { + Wire *w = module->wires_[remap_name(wire->name)]; + log_assert(w->attributes.count("\\init") == 0); + w->attributes["\\init"] = wire->attributes.at("\\init"); + } + } + for (auto &it : cell_stats) log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); int in_wires = 0, out_wires = 0; @@ -1171,7 +1248,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin struct AbcPass : public Pass { AbcPass() : Pass("abc", "use ABC for technology mapping") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1205,7 +1282,7 @@ struct AbcPass : public Pass { log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); log("\n"); log(" for -lut/-luts (only one LUT size):\n"); - log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack").c_str()); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack {S}").c_str()); log("\n"); log(" for -lut/-luts (different LUT sizes):\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); @@ -1253,6 +1330,8 @@ struct AbcPass : public Pass { log(" -D <picoseconds>\n"); log(" set delay target. the string {D} in the default scripts above is\n"); log(" replaced by this option when used, and an empty string otherwise.\n"); + log(" this also replaces 'dretime' with 'dretime; retime -o {D}' in the\n"); + log(" default scripts above.\n"); log("\n"); log(" -I <num>\n"); log(" maximum number of SOP inputs.\n"); @@ -1262,6 +1341,10 @@ struct AbcPass : public Pass { log(" maximum number of SOP products.\n"); log(" (replaces {P} in the default scripts above)\n"); log("\n"); + log(" -S <num>\n"); + log(" maximum number of LUT inputs shared.\n"); + log(" (replaces {S} in the default scripts above, default: -S 1)\n"); + log("\n"); log(" -lut <width>\n"); log(" generate netlist using luts of (max) the specified width.\n"); log("\n"); @@ -1283,10 +1366,21 @@ struct AbcPass : public Pass { // log(" (ignored when used with -liberty or -lut)\n"); // log("\n"); log(" -g type1,type2,...\n"); - log(" Map the the specified list of gate types. Supported gates types are:\n"); - log(" AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4.\n"); + log(" Map to the specified list of gate types. Supported gates types are:\n"); + log(" AND, NAND, OR, NOR, XOR, XNOR, ANDNOT, ORNOT, MUX, AOI3, OAI3, AOI4, OAI4.\n"); log(" (The NOT gate is always added to this list automatically.)\n"); log("\n"); + log(" The following aliases can be used to reference common sets of gate types:\n"); + log(" simple: AND OR XOR MUX\n"); + log(" cmos2: NAND NOR\n"); + log(" cmos3: NAND NOR AOI3 OAI3\n"); + log(" cmos4: NAND NOR AOI3 OAI3 AOI4 OAI4\n"); + log(" gates: AND NAND OR NOR XOR XNOR ANDNOT ORNOT\n"); + log(" aig: AND NAND OR NOR ANDNOT ORNOT\n"); + log("\n"); + log(" Prefix a gate type with a '-' to remove it from the list. For example\n"); + log(" the arguments 'AND,OR,XOR' and 'simple,-MUX' are equivalent.\n"); + log("\n"); log(" -dff\n"); log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); log(" clock domains are automatically partitioned in clock domains and each\n"); @@ -1316,24 +1410,35 @@ struct AbcPass : public Pass { log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n"); log("loaded into ABC before the ABC script is executed.\n"); log("\n"); - log("This pass does not operate on modules with unprocessed processes in it.\n"); - log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n"); + log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); + log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); + log("ABC on logic snippets extracted from your design. You will not get any useful\n"); + log("output when passing an ABC script that writes a file. Instead write your full\n"); + log("design as BLIF file with write_blif and the load that into ABC externally if\n"); + log("you want to use ABC to convert your design into another format.\n"); log("\n"); log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); + assign_map.clear(); + signal_list.clear(); + signal_map.clear(); + signal_init.clear(); + pi_map.clear(); + po_map.clear(); + #ifdef ABCEXTERNAL std::string exe_file = ABCEXTERNAL; #else std::string exe_file = proc_self_dirname() + "yosys-abc"; #endif std::string script_file, liberty_file, constr_file, clk_str; - std::string delay_target, sop_inputs, sop_products; + std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1"; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; bool show_tempdir = false, sop_mode = false; vector<int> lut_costs; @@ -1365,17 +1470,20 @@ struct AbcPass : public Pass { } if (arg == "-script" && argidx+1 < args.size()) { script_file = args[++argidx]; + rewrite_filename(script_file); if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') script_file = std::string(pwd) + "/" + script_file; continue; } if (arg == "-liberty" && argidx+1 < args.size()) { liberty_file = args[++argidx]; + rewrite_filename(liberty_file); if (!liberty_file.empty() && !is_absolute_path(liberty_file)) liberty_file = std::string(pwd) + "/" + liberty_file; continue; } if (arg == "-constr" && argidx+1 < args.size()) { + rewrite_filename(constr_file); constr_file = args[++argidx]; if (!constr_file.empty() && !is_absolute_path(constr_file)) constr_file = std::string(pwd) + "/" + constr_file; @@ -1393,6 +1501,10 @@ struct AbcPass : public Pass { sop_products = "-P " + args[++argidx]; continue; } + if (arg == "-S" && argidx+1 < args.size()) { + lutin_shared = "-S " + args[++argidx]; + continue; + } if (arg == "-lut" && argidx+1 < args.size()) { string arg = args[++argidx]; size_t pos = arg.find_first_of(':'); @@ -1445,20 +1557,83 @@ struct AbcPass : public Pass { } if (arg == "-g" && argidx+1 < args.size()) { for (auto g : split_tokens(args[++argidx], ",")) { + vector<string> gate_list; + bool remove_gates = false; + if (GetSize(g) > 0 && g[0] == '-') { + remove_gates = true; + g = g.substr(1); + } if (g == "AND") goto ok_gate; if (g == "NAND") goto ok_gate; if (g == "OR") goto ok_gate; if (g == "NOR") goto ok_gate; if (g == "XOR") goto ok_gate; if (g == "XNOR") goto ok_gate; + if (g == "ANDNOT") goto ok_gate; + if (g == "ORNOT") goto ok_gate; if (g == "MUX") goto ok_gate; if (g == "AOI3") goto ok_gate; if (g == "OAI3") goto ok_gate; if (g == "AOI4") goto ok_gate; if (g == "OAI4") goto ok_gate; + if (g == "simple") { + gate_list.push_back("AND"); + gate_list.push_back("OR"); + gate_list.push_back("XOR"); + gate_list.push_back("MUX"); + goto ok_alias; + } + if (g == "cmos2") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + goto ok_alias; + } + if (g == "cmos3") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + gate_list.push_back("AOI3"); + gate_list.push_back("OAI3"); + goto ok_alias; + } + if (g == "cmos4") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + gate_list.push_back("AOI3"); + gate_list.push_back("OAI3"); + gate_list.push_back("AOI4"); + gate_list.push_back("OAI4"); + goto ok_alias; + } + if (g == "gates") { + gate_list.push_back("AND"); + gate_list.push_back("NAND"); + gate_list.push_back("OR"); + gate_list.push_back("NOR"); + gate_list.push_back("XOR"); + gate_list.push_back("XNOR"); + gate_list.push_back("ANDNOT"); + gate_list.push_back("ORNOT"); + goto ok_alias; + } + if (g == "aig") { + gate_list.push_back("AND"); + gate_list.push_back("NAND"); + gate_list.push_back("OR"); + gate_list.push_back("NOR"); + gate_list.push_back("ANDNOT"); + gate_list.push_back("ORNOT"); + goto ok_alias; + } cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); ok_gate: - enabled_gates.insert(g); + gate_list.push_back(g); + ok_alias: + for (auto gate : gate_list) { + if (remove_gates) + enabled_gates.erase(gate); + else + enabled_gates.insert(gate); + } } continue; } @@ -1501,163 +1676,190 @@ struct AbcPass : public Pass { log_cmd_error("Got -constr but no -liberty!\n"); for (auto mod : design->selected_modules()) - if (mod->processes.size() > 0) + { + if (mod->processes.size() > 0) { log("Skipping module %s as it contains processes.\n", log_id(mod)); - else if (!dff_mode || !clk_str.empty()) - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, - delay_target, sop_inputs, sop_products, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); - else - { - assign_map.set(mod); - CellTypes ct(design); + continue; + } - std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); - std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); + assign_map.set(mod); + signal_init.clear(); + + for (Wire *wire : mod->wires()) + if (wire->attributes.count("\\init")) { + SigSpec initsig = assign_map(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + switch (initval[i]) { + case State::S0: + signal_init[initsig[i]] = State::S0; + break; + case State::S1: + signal_init[initsig[i]] = State::S0; + break; + default: + break; + } + } - std::set<RTLIL::Cell*> expand_queue, next_expand_queue; - std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; - std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; + if (!dff_mode || !clk_str.empty()) { + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); + continue; + } - typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; - std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; - std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; + CellTypes ct(design); - std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; - std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; + std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); + std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); - for (auto cell : all_cells) - { - clkdomain_t key; - - for (auto &conn : cell->connections()) - for (auto bit : conn.second) { - bit = assign_map(bit); - if (bit.wire != nullptr) { - cell_to_bit[cell].insert(bit); - bit_to_cell[bit].insert(cell); - if (ct.cell_input(cell->type, conn.first)) { - cell_to_bit_up[cell].insert(bit); - bit_to_cell_down[bit].insert(cell); - } - if (ct.cell_output(cell->type, conn.first)) { - cell_to_bit_down[cell].insert(bit); - bit_to_cell_up[bit].insert(cell); - } - } - } + std::set<RTLIL::Cell*> expand_queue, next_expand_queue; + std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; + std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; - if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") - { - key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec()); - } - else - if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") - { - bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"; - bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"; - key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E"))); - } - else - continue; + typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; + std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; - unassigned_cells.erase(cell); - expand_queue.insert(cell); - expand_queue_up.insert(cell); - expand_queue_down.insert(cell); + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; - assigned_cells[key].push_back(cell); - assigned_cells_reverse[cell] = key; + for (auto cell : all_cells) + { + clkdomain_t key; + + for (auto &conn : cell->connections()) + for (auto bit : conn.second) { + bit = assign_map(bit); + if (bit.wire != nullptr) { + cell_to_bit[cell].insert(bit); + bit_to_cell[bit].insert(cell); + if (ct.cell_input(cell->type, conn.first)) { + cell_to_bit_up[cell].insert(bit); + bit_to_cell_down[bit].insert(cell); + } + if (ct.cell_output(cell->type, conn.first)) { + cell_to_bit_down[cell].insert(bit); + bit_to_cell_up[bit].insert(cell); + } + } } - while (!expand_queue_up.empty() || !expand_queue_down.empty()) + if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") { - if (!expand_queue_up.empty()) - { - RTLIL::Cell *cell = *expand_queue_up.begin(); - clkdomain_t key = assigned_cells_reverse.at(cell); - expand_queue_up.erase(cell); - - for (auto bit : cell_to_bit_up[cell]) - for (auto c : bit_to_cell_up[bit]) - if (unassigned_cells.count(c)) { - unassigned_cells.erase(c); - next_expand_queue_up.insert(c); - assigned_cells[key].push_back(c); - assigned_cells_reverse[c] = key; - expand_queue.insert(c); - } - } + key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec()); + } + else + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") + { + bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"; + bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"; + key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E"))); + } + else + continue; - if (!expand_queue_down.empty()) - { - RTLIL::Cell *cell = *expand_queue_down.begin(); - clkdomain_t key = assigned_cells_reverse.at(cell); - expand_queue_down.erase(cell); - - for (auto bit : cell_to_bit_down[cell]) - for (auto c : bit_to_cell_down[bit]) - if (unassigned_cells.count(c)) { - unassigned_cells.erase(c); - next_expand_queue_up.insert(c); - assigned_cells[key].push_back(c); - assigned_cells_reverse[c] = key; - expand_queue.insert(c); - } - } + unassigned_cells.erase(cell); + expand_queue.insert(cell); + expand_queue_up.insert(cell); + expand_queue_down.insert(cell); - if (expand_queue_up.empty() && expand_queue_down.empty()) { - expand_queue_up.swap(next_expand_queue_up); - expand_queue_down.swap(next_expand_queue_down); - } - } + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } - while (!expand_queue.empty()) + while (!expand_queue_up.empty() || !expand_queue_down.empty()) + { + if (!expand_queue_up.empty()) { - RTLIL::Cell *cell = *expand_queue.begin(); + RTLIL::Cell *cell = *expand_queue_up.begin(); clkdomain_t key = assigned_cells_reverse.at(cell); - expand_queue.erase(cell); - - for (auto bit : cell_to_bit.at(cell)) { - for (auto c : bit_to_cell[bit]) - if (unassigned_cells.count(c)) { - unassigned_cells.erase(c); - next_expand_queue.insert(c); - assigned_cells[key].push_back(c); - assigned_cells_reverse[c] = key; - } - bit_to_cell[bit].clear(); - } + expand_queue_up.erase(cell); + + for (auto bit : cell_to_bit_up[cell]) + for (auto c : bit_to_cell_up[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } - if (expand_queue.empty()) - expand_queue.swap(next_expand_queue); + if (!expand_queue_down.empty()) + { + RTLIL::Cell *cell = *expand_queue_down.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_down.erase(cell); + + for (auto bit : cell_to_bit_down[cell]) + for (auto c : bit_to_cell_down[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } } - clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); - for (auto cell : unassigned_cells) { - assigned_cells[key].push_back(cell); - assigned_cells_reverse[cell] = key; + if (expand_queue_up.empty() && expand_queue_down.empty()) { + expand_queue_up.swap(next_expand_queue_up); + expand_queue_down.swap(next_expand_queue_down); } + } - log_header(design, "Summary of detected clock domains:\n"); - for (auto &it : assigned_cells) - log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), - std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), - std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); - - for (auto &it : assigned_cells) { - clk_polarity = std::get<0>(it.first); - clk_sig = assign_map(std::get<1>(it.first)); - en_polarity = std::get<2>(it.first); - en_sig = assign_map(std::get<3>(it.first)); - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", - keepff, delay_target, sop_inputs, sop_products, fast_mode, it.second, show_tempdir, sop_mode); - assign_map.set(mod); + while (!expand_queue.empty()) + { + RTLIL::Cell *cell = *expand_queue.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue.erase(cell); + + for (auto bit : cell_to_bit.at(cell)) { + for (auto c : bit_to_cell[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + } + bit_to_cell[bit].clear(); } + + if (expand_queue.empty()) + expand_queue.swap(next_expand_queue); + } + + clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); + for (auto cell : unassigned_cells) { + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; } + log_header(design, "Summary of detected clock domains:\n"); + for (auto &it : assigned_cells) + log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), + std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), + std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); + + for (auto &it : assigned_cells) { + clk_polarity = std::get<0>(it.first); + clk_sig = assign_map(std::get<1>(it.first)); + en_polarity = std::get<2>(it.first); + en_sig = assign_map(std::get<3>(it.first)); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode); + assign_map.set(mod); + } + } + assign_map.clear(); signal_list.clear(); signal_map.clear(); + signal_init.clear(); + pi_map.clear(); + po_map.clear(); log_pop(); } diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc index b9ac7ade..35df2ff7 100644 --- a/passes/techmap/aigmap.cc +++ b/passes/techmap/aigmap.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct AigmapPass : public Pass { AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" aigmap [options] [selection]\n"); @@ -37,7 +37,7 @@ struct AigmapPass : public Pass { log(" Enable creation of $_NAND_ cells\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool nand_mode = false; diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 9f6dd02d..dc7d416b 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -55,19 +55,19 @@ struct AlumaccWorker RTLIL::SigSpec get_gt() { if (GetSize(cached_gt) == 0) - cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq())); + cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute()); return cached_gt; } RTLIL::SigSpec get_eq() { if (GetSize(cached_eq) == 0) - cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort("\\X")); + cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort("\\X"), false, alu_cell->get_src_attribute()); return cached_eq; } RTLIL::SigSpec get_ne() { if (GetSize(cached_ne) == 0) - cached_ne = alu_cell->module->Not(NEW_ID, get_eq()); + cached_ne = alu_cell->module->Not(NEW_ID, get_eq(), false, alu_cell->get_src_attribute()); return cached_ne; } @@ -75,7 +75,7 @@ struct AlumaccWorker if (GetSize(cached_cf) == 0) { cached_cf = alu_cell->getPort("\\CO"); log_assert(GetSize(cached_cf) >= 1); - cached_cf = alu_cell->module->Not(NEW_ID, cached_cf[GetSize(cached_cf)-1]); + cached_cf = alu_cell->module->Not(NEW_ID, cached_cf[GetSize(cached_cf)-1], false, alu_cell->get_src_attribute()); } return cached_cf; } @@ -352,10 +352,13 @@ struct AlumaccWorker { auto n = it.second; auto cell = module->addCell(NEW_ID, "$macc"); + macc_counter++; log(" creating $macc cell for %s: %s\n", log_id(n->cell), log_id(cell)); + cell->set_src_attribute(n->cell->get_src_attribute()); + n->macc.optimize(GetSize(n->y)); n->macc.to_cell(cell); cell->setPort("\\Y", n->y); @@ -452,6 +455,7 @@ struct AlumaccWorker void replace_alu() { + std::string src(""); for (auto &it1 : sig_alu) for (auto n : it1.second) { @@ -475,6 +479,9 @@ struct AlumaccWorker log("%s%s", i ? ", ": "", log_id(n->cells[i])); log(": %s\n", log_id(n->alu_cell)); + if (n->cells.size() > 0) + n->alu_cell->set_src_attribute(n->cells[0]->get_src_attribute()); + n->alu_cell->setPort("\\A", n->a); n->alu_cell->setPort("\\B", n->b); n->alu_cell->setPort("\\CI", GetSize(n->c) ? n->c : RTLIL::S0); @@ -532,7 +539,7 @@ struct AlumaccWorker struct AlumaccPass : public Pass { AlumaccPass() : Pass("alumacc", "extract ALU and MACC cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -542,7 +549,7 @@ struct AlumaccPass : public Pass { log("and $macc cells.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n"); diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc index dec81d21..0b5576b0 100644 --- a/passes/techmap/attrmap.cc +++ b/passes/techmap/attrmap.cc @@ -81,7 +81,7 @@ struct AttrmapAction { struct AttrmapTocase : AttrmapAction { string name; - virtual bool apply(IdString &id, Const&) { + bool apply(IdString &id, Const&) YS_OVERRIDE { if (match_name(name, id, true)) id = RTLIL::escape_id(name); return true; @@ -90,7 +90,7 @@ struct AttrmapTocase : AttrmapAction { struct AttrmapRename : AttrmapAction { string old_name, new_name; - virtual bool apply(IdString &id, Const&) { + bool apply(IdString &id, Const&) YS_OVERRIDE { if (match_name(old_name, id)) id = RTLIL::escape_id(new_name); return true; @@ -101,7 +101,7 @@ struct AttrmapMap : AttrmapAction { bool imap; string old_name, new_name; string old_value, new_value; - virtual bool apply(IdString &id, Const &val) { + bool apply(IdString &id, Const &val) YS_OVERRIDE { if (match_name(old_name, id) && match_value(old_value, val, true)) { id = RTLIL::escape_id(new_name); val = make_value(new_value); @@ -112,7 +112,7 @@ struct AttrmapMap : AttrmapAction { struct AttrmapRemove : AttrmapAction { string name, value; - virtual bool apply(IdString &id, Const &val) { + bool apply(IdString &id, Const &val) YS_OVERRIDE { return !(match_name(name, id) && match_value(value, val)); } }; @@ -144,7 +144,7 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio struct AttrmapPass : public Pass { AttrmapPass() : Pass("attrmap", "renaming attributes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -179,7 +179,7 @@ struct AttrmapPass : public Pass { log(" -imap keep=\"false\" keep=0 -remove keep=0\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n"); diff --git a/passes/techmap/attrmvcp.cc b/passes/techmap/attrmvcp.cc index 1537def0..e59aa651 100644 --- a/passes/techmap/attrmvcp.cc +++ b/passes/techmap/attrmvcp.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct AttrmvcpPass : public Pass { AttrmvcpPass() : Pass("attrmvcp", "move or copy attributes from wires to driving cells") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" attrmvcp [options] [selection]\n"); @@ -53,7 +53,7 @@ struct AttrmvcpPass : public Pass { log(" multiple times.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ATTRMVCP pass (move or copy attributes).\n"); diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc index ed4e4576..9f0c7bf6 100644 --- a/passes/techmap/deminout.cc +++ b/passes/techmap/deminout.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct DeminoutPass : public Pass { DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" deminout [options] [selection]\n"); @@ -33,7 +33,7 @@ struct DeminoutPass : public Pass { log("\"Demote\" inout ports to input or output ports, if possible.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n"); @@ -57,7 +57,7 @@ struct DeminoutPass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); - pool<SigBit> bits_written, bits_used, bits_inout; + pool<SigBit> bits_written, bits_used, bits_inout, bits_tribuf; dict<SigBit, int> bits_numports; for (auto wire : module->wires()) @@ -82,6 +82,25 @@ struct DeminoutPass : public Pass { if (cellport_in) for (auto bit : sigmap(conn.second)) bits_used.insert(bit); + + if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_")) + { + bool tribuf = (cell->type == "$_TBUF_"); + + if (!tribuf) { + for (auto &c : cell->connections()) { + if (!c.first.in("\\A", "\\B")) + continue; + for (auto b : sigmap(c.second)) + if (b == State::Sz) + tribuf = true; + } + } + + if (tribuf) + for (auto bit : sigmap(conn.second)) + bits_tribuf.insert(bit); + } } for (auto wire : module->selected_wires()) @@ -95,10 +114,15 @@ struct DeminoutPass : public Pass { if (bits_numports[bit] > 1 || bits_inout.count(bit)) new_input = true, new_output = true; - if (bits_written.count(bit)) + if (bits_written.count(bit)) { new_output = true; - else if (bits_used.count(bit)) - new_input = true; + if (bits_tribuf.count(bit)) + goto tribuf_bit; + } else { + tribuf_bit: + if (bits_used.count(bit)) + new_input = true; + } } if (new_input != new_output) { diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc index 1b8920bb..3291f5a4 100644 --- a/passes/techmap/dff2dffe.cc +++ b/passes/techmap/dff2dffe.cc @@ -253,7 +253,7 @@ struct Dff2dffeWorker struct Dff2dffePass : public Pass { Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -279,11 +279,12 @@ struct Dff2dffePass : public Pass { log(" -direct-match <pattern>\n"); log(" like -direct for all DFF cell types matching the expression.\n"); log(" this will use $__DFFE_* as <external_gate_type> matching the\n"); - log(" internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is\n"); - log(" converted to $_DFFE_[NP]_.\n"); + log(" internal gate type $_DFF_*_, and $__DFFSE_* for those matching\n"); + log(" $_DFFS_*_, except for $_DFF_[NP]_, which is converted to \n"); + log(" $_DFFE_[NP]_.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); @@ -315,6 +316,15 @@ struct Dff2dffePass : public Pass { if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict["$_DFF_PN1_"] = "$__DFFE_PN1"; if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict["$_DFF_PP0_"] = "$__DFFE_PP0"; if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict["$_DFF_PP1_"] = "$__DFFE_PP1"; + + if (patmatch(pattern, "$__DFFS_NN0_")) found_match = true, direct_dict["$__DFFS_NN0_"] = "$__DFFSE_NN0"; + if (patmatch(pattern, "$__DFFS_NN1_")) found_match = true, direct_dict["$__DFFS_NN1_"] = "$__DFFSE_NN1"; + if (patmatch(pattern, "$__DFFS_NP0_")) found_match = true, direct_dict["$__DFFS_NP0_"] = "$__DFFSE_NP0"; + if (patmatch(pattern, "$__DFFS_NP1_")) found_match = true, direct_dict["$__DFFS_NP1_"] = "$__DFFSE_NP1"; + if (patmatch(pattern, "$__DFFS_PN0_")) found_match = true, direct_dict["$__DFFS_PN0_"] = "$__DFFSE_PN0"; + if (patmatch(pattern, "$__DFFS_PN1_")) found_match = true, direct_dict["$__DFFS_PN1_"] = "$__DFFSE_PN1"; + if (patmatch(pattern, "$__DFFS_PP0_")) found_match = true, direct_dict["$__DFFS_PP0_"] = "$__DFFSE_PP0"; + if (patmatch(pattern, "$__DFFS_PP1_")) found_match = true, direct_dict["$__DFFS_PP1_"] = "$__DFFSE_PP1"; if (!found_match) log_cmd_error("No cell types matched pattern '%s'.\n", pattern); continue; diff --git a/passes/techmap/dff2dffs.cc b/passes/techmap/dff2dffs.cc new file mode 100644 index 00000000..39a4f6ad --- /dev/null +++ b/passes/techmap/dff2dffs.cc @@ -0,0 +1,142 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2018 David Shah <dave@ds0.me> + * + * 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 Dff2dffsPass : public Pass { + Dff2dffsPass() : Pass("dff2dffs", "process sync set/reset with SR over CE priority") { } + void help() YS_OVERRIDE + { + log("\n"); + log(" dff2dffs [options] [selection]\n"); + log("\n"); + log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n"); + log("dff2dffe for SR over CE priority.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + pool<IdString> dff_types; + dff_types.insert("$_DFF_N_"); + dff_types.insert("$_DFF_P_"); + + for (auto module : design->selected_modules()) + { + log("Merging set/reset $_MUX_ cells into DFFs in %s.\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, Cell*> sr_muxes; + vector<Cell*> ff_cells; + + for (auto cell : module->selected_cells()) + { + if (dff_types.count(cell->type)) { + ff_cells.push_back(cell); + continue; + } + + if (cell->type != "$_MUX_") + continue; + + SigBit bit_a = sigmap(cell->getPort("\\A")); + SigBit bit_b = sigmap(cell->getPort("\\B")); + + if (bit_a.wire == nullptr || bit_b.wire == nullptr) + sr_muxes[sigmap(cell->getPort("\\Y"))] = cell; + } + + for (auto cell : ff_cells) + { + SigSpec sig_d = cell->getPort("\\D"); + + if (GetSize(sig_d) < 1) + continue; + + SigBit bit_d = sigmap(sig_d[0]); + + if (sr_muxes.count(bit_d) == 0) + continue; + + Cell *mux_cell = sr_muxes.at(bit_d); + SigBit bit_a = sigmap(mux_cell->getPort("\\A")); + SigBit bit_b = sigmap(mux_cell->getPort("\\B")); + SigBit bit_s = sigmap(mux_cell->getPort("\\S")); + + log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell), + log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type)); + + SigBit sr_val, sr_sig; + bool invert_sr; + sr_sig = bit_s; + if (bit_a.wire == nullptr) { + bit_d = bit_b; + sr_val = bit_a; + invert_sr = true; + } else { + log_assert(bit_b.wire == nullptr); + bit_d = bit_a; + sr_val = bit_b; + invert_sr = false; + } + + if (sr_val == State::S1) { + if (cell->type == "$_DFF_N_") { + if (invert_sr) cell->type = "$__DFFS_NN1_"; + else cell->type = "$__DFFS_NP1_"; + } else { + log_assert(cell->type == "$_DFF_P_"); + if (invert_sr) cell->type = "$__DFFS_PN1_"; + else cell->type = "$__DFFS_PP1_"; + } + } else { + if (cell->type == "$_DFF_N_") { + if (invert_sr) cell->type = "$__DFFS_NN0_"; + else cell->type = "$__DFFS_NP0_"; + } else { + log_assert(cell->type == "$_DFF_P_"); + if (invert_sr) cell->type = "$__DFFS_PN0_"; + else cell->type = "$__DFFS_PP0_"; + } + } + cell->setPort("\\R", sr_sig); + cell->setPort("\\D", bit_d); + } + } + } +} Dff2dffsPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index d737b342..a8eecc97 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct DffinitPass : public Pass { DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -38,15 +38,25 @@ struct DffinitPass : public Pass { log(" operate on the specified cell type. this option can be used\n"); log(" multiple times.\n"); log("\n"); + log(" -highlow\n"); + log(" use the string values \"high\" and \"low\" to represent a single-bit\n"); + log(" initial value of 1 or 0. (multi-bit values are not supported in this\n"); + log(" mode.)\n"); + log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n"); dict<IdString, dict<IdString, IdString>> ff_types; + bool highlow_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-highlow") { + highlow_mode = true; + continue; + } if (args[argidx] == "-ff" && argidx+3 < args.size()) { IdString cell_name = RTLIL::escape_id(args[++argidx]); IdString output_port = RTLIL::escape_id(args[++argidx]); @@ -106,6 +116,16 @@ struct DffinitPass : public Pass { cleanup_bits.insert(sig[i]); } + if (highlow_mode && GetSize(value) != 0) { + if (GetSize(value) != 1) + log_error("Multi-bit init value for %s.%s.%s is incompatible with -highlow mode.\n", + log_id(module), log_id(cell), log_id(it.second)); + if (value[0] == State::S1) + value = Const("high"); + else + value = Const("low"); + } + log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second), log_id(it.first), log_signal(sig), log_signal(value)); cell->setParam(it.second, value); diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index c8104fb7..416ed2bd 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -240,6 +240,10 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo if (cell->id != "cell" || cell->args.size() != 1) continue; + LibertyAst *dn = cell->find("dont_use"); + if (dn != NULL && dn->value == "true") + continue; + LibertyAst *ff = cell->find("ff"); if (ff == NULL) continue; @@ -478,11 +482,15 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare auto cell_type = cell->type; auto cell_name = cell->name; auto cell_connections = cell->connections(); + std::string src = cell->get_src_attribute(); + module->remove(cell); cell_mapping &cm = cell_mappings[cell_type]; RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : "\\" + cm.cell_name); + new_cell->set_src_attribute(src); + bool has_q = false, has_qn = false; for (auto &port : cm.ports) { if (port.second == 'Q') has_q = true; @@ -529,7 +537,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare struct DfflibmapPass : public Pass { DfflibmapPass() : Pass("dfflibmap", "technology mapping of flip-flops") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" dfflibmap [-prepare] -liberty <file> [selection]\n"); @@ -545,7 +553,7 @@ struct DfflibmapPass : public Pass { log("liberty file.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc index 0d4d5362..086a1d2f 100644 --- a/passes/techmap/dffsr2dff.cc +++ b/passes/techmap/dffsr2dff.cc @@ -176,7 +176,7 @@ void adff_worker(SigMap &sigmap, Module *module, Cell *cell) struct Dffsr2dffPass : public Pass { Dffsr2dffPass() : Pass("dffsr2dff", "convert DFFSR cells to simpler FF cell types") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -186,7 +186,7 @@ struct Dffsr2dffPass : public Pass { log("$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n"); diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index 71e29c60..fff90f13 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -352,7 +352,7 @@ bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right) struct ExtractPass : public Pass { ExtractPass() : Pass("extract", "find subcircuits and replace them with cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -440,7 +440,7 @@ struct ExtractPass : public Pass { log("See 'help techmap' for a pass that does the opposite thing.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n"); log_push(); diff --git a/passes/techmap/extract_counter.cc b/passes/techmap/extract_counter.cc new file mode 100644 index 00000000..a8d0bc83 --- /dev/null +++ b/passes/techmap/extract_counter.cc @@ -0,0 +1,651 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2017 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/modtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +//get the list of cells hooked up to at least one bit of a given net +pool<Cell*> get_other_cells(const RTLIL::SigSpec& port, ModIndex& index, Cell* src) +{ + pool<Cell*> rval; + for(auto b : port) + { + pool<ModIndex::PortInfo> ports = index.query_ports(b); + for(auto x : ports) + { + if(x.cell == src) + continue; + rval.insert(x.cell); + } + } + return rval; +} + +//return true if there is a full-width bus connection from cell a port ap to cell b port bp +//if other_conns_allowed is false, then we require a strict point to point connection (no other links) +bool is_full_bus( + const RTLIL::SigSpec& sig, + ModIndex& index, + Cell* a, + RTLIL::IdString ap, + Cell* b, + RTLIL::IdString bp, + bool other_conns_allowed = false) +{ + for(auto s : sig) + { + pool<ModIndex::PortInfo> ports = index.query_ports(s); + bool found_a = false; + bool found_b = false; + for(auto x : ports) + { + if( (x.cell == a) && (x.port == ap) ) + found_a = true; + else if( (x.cell == b) && (x.port == bp) ) + found_b = true; + else if(!other_conns_allowed) + return false; + } + + if( (!found_a) || (!found_b) ) + return false; + } + + return true; +} + +//return true if the signal connects to one port only (nothing on the other end) +bool is_unconnected(const RTLIL::SigSpec& port, ModIndex& index) +{ + for(auto b : port) + { + pool<ModIndex::PortInfo> ports = index.query_ports(b); + if(ports.size() > 1) + return false; + } + + return true; +} + +struct CounterExtraction +{ + int width; //counter width + RTLIL::Wire* rwire; //the register output + bool has_reset; //true if we have a reset + bool has_ce; //true if we have a clock enable + RTLIL::SigSpec rst; //reset pin + bool rst_inverted; //true if reset is active low + bool rst_to_max; //true if we reset to max instead of 0 + int count_value; //value we count from + RTLIL::SigSpec ce; //clock signal + RTLIL::SigSpec clk; //clock enable, if any + RTLIL::SigSpec outsig; //counter output signal + RTLIL::Cell* count_mux; //counter mux + RTLIL::Cell* count_reg; //counter register + RTLIL::Cell* underflow_inv; //inverter reduction for output-underflow detect + pool<ModIndex::PortInfo> pouts; //Ports that take a parallel output from us +}; + +//attempt to extract a counter centered on the given adder cell +//For now we only support DOWN counters. +//TODO: up/down support +int counter_tryextract( + ModIndex& index, + Cell *cell, + CounterExtraction& extract, + pool<RTLIL::IdString>& parallel_cells, + int maxwidth) +{ + SigMap& sigmap = index.sigmap; + + //A counter with less than 2 bits makes no sense + //TODO: configurable min threshold + int a_width = cell->getParam("\\A_WIDTH").as_int(); + extract.width = a_width; + if( (a_width < 2) || (a_width > maxwidth) ) + return 1; + + //Second input must be a single bit + int b_width = cell->getParam("\\B_WIDTH").as_int(); + if(b_width != 1) + return 2; + + //Both inputs must be unsigned, so don't extract anything with a signed input + bool a_sign = cell->getParam("\\A_SIGNED").as_bool(); + bool b_sign = cell->getParam("\\B_SIGNED").as_bool(); + if(a_sign || b_sign) + return 3; + + //To be a counter, one input of the ALU must be a constant 1 + //TODO: can A or B be swapped in synthesized RTL or is B always the 1? + const RTLIL::SigSpec b_port = sigmap(cell->getPort("\\B")); + if(!b_port.is_fully_const() || (b_port.as_int() != 1) ) + return 4; + + //BI and CI must be constant 1 as well + const RTLIL::SigSpec bi_port = sigmap(cell->getPort("\\BI")); + if(!bi_port.is_fully_const() || (bi_port.as_int() != 1) ) + return 5; + const RTLIL::SigSpec ci_port = sigmap(cell->getPort("\\CI")); + if(!ci_port.is_fully_const() || (ci_port.as_int() != 1) ) + return 6; + + //CO and X must be unconnected (exactly one connection to each port) + if(!is_unconnected(sigmap(cell->getPort("\\CO")), index)) + return 7; + if(!is_unconnected(sigmap(cell->getPort("\\X")), index)) + return 8; + + //Y must have exactly one connection, and it has to be a $mux cell. + //We must have a direct bus connection from our Y to their A. + const RTLIL::SigSpec aluy = sigmap(cell->getPort("\\Y")); + pool<Cell*> y_loads = get_other_cells(aluy, index, cell); + if(y_loads.size() != 1) + return 9; + Cell* count_mux = *y_loads.begin(); + extract.count_mux = count_mux; + if(count_mux->type != "$mux") + return 10; + if(!is_full_bus(aluy, index, cell, "\\Y", count_mux, "\\A")) + return 11; + + //B connection of the mux is our underflow value + const RTLIL::SigSpec underflow = sigmap(count_mux->getPort("\\B")); + if(!underflow.is_fully_const()) + return 12; + extract.count_value = underflow.as_int(); + + //S connection of the mux must come from an inverter (need not be the only load) + const RTLIL::SigSpec muxsel = sigmap(count_mux->getPort("\\S")); + extract.outsig = muxsel; + pool<Cell*> muxsel_conns = get_other_cells(muxsel, index, count_mux); + Cell* underflow_inv = NULL; + for(auto c : muxsel_conns) + { + if(c->type != "$logic_not") + continue; + if(!is_full_bus(muxsel, index, c, "\\Y", count_mux, "\\S", true)) + continue; + + underflow_inv = c; + break; + } + if(underflow_inv == NULL) + return 13; + extract.underflow_inv = underflow_inv; + + //Y connection of the mux must have exactly one load, the counter's internal register, if there's no clock enable + //If we have a clock enable, Y drives the B input of a mux. A of that mux must come from our register + const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y")); + pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux); + if(muxy_loads.size() != 1) + return 14; + Cell* muxload = *muxy_loads.begin(); + Cell* count_reg = muxload; + Cell* cemux = NULL; + RTLIL::SigSpec cey; + if(muxload->type == "$mux") + { + //This mux is probably a clock enable mux. + //Find our count register (should be our only load) + cemux = muxload; + cey = sigmap(cemux->getPort("\\Y")); + pool<Cell*> cey_loads = get_other_cells(cey, index, cemux); + if(cey_loads.size() != 1) + return 24; + count_reg = *cey_loads.begin(); + + //Mux should have A driven by count Q, and B by muxy + //TODO: if A and B are swapped, CE polarity is inverted + if(sigmap(cemux->getPort("\\B")) != muxy) + return 24; + if(sigmap(cemux->getPort("\\A")) != sigmap(count_reg->getPort("\\Q"))) + return 24; + if(sigmap(cemux->getPort("\\Y")) != sigmap(count_reg->getPort("\\D"))) + return 24; + + //Select of the mux is our clock enable + extract.has_ce = true; + extract.ce = sigmap(cemux->getPort("\\S")); + } + else + extract.has_ce = false; + + extract.count_reg = count_reg; + if(count_reg->type == "$dff") + extract.has_reset = false; + else if(count_reg->type == "$adff") + { + extract.has_reset = true; + + //Check polarity of reset - we may have to add an inverter later on! + extract.rst_inverted = (count_reg->getParam("\\ARST_POLARITY").as_int() != 1); + + //Verify ARST_VALUE is zero or full scale + int rst_value = count_reg->getParam("\\ARST_VALUE").as_int(); + if(rst_value == 0) + extract.rst_to_max = false; + else if(rst_value == extract.count_value) + extract.rst_to_max = true; + else + return 23; + + //Save the reset + extract.rst = sigmap(count_reg->getPort("\\ARST")); + } + //TODO: support synchronous reset + else + return 15; + + //Sanity check that we use the ALU output properly + if(extract.has_ce) + { + if(!is_full_bus(muxy, index, count_mux, "\\Y", cemux, "\\B")) + return 16; + if(!is_full_bus(cey, index, cemux, "\\Y", count_reg, "\\D")) + return 16; + } + else if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D")) + return 16; + + //TODO: Verify count_reg CLK_POLARITY is 1 + + //Register output must have exactly two loads, the inverter and ALU + //(unless we have a parallel output!) + //If we have a clock enable, 3 is OK + const RTLIL::SigSpec qport = count_reg->getPort("\\Q"); + const RTLIL::SigSpec cnout = sigmap(qport); + pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg); + unsigned int max_loads = 2; + if(extract.has_ce) + max_loads = 3; + if(cnout_loads.size() > max_loads) + { + for(auto c : cnout_loads) + { + if(c == underflow_inv) + continue; + if(c == cell) + continue; + if(c == muxload) + continue; + + //If we specified a limited set of cells for parallel output, check that we only drive them + if(!parallel_cells.empty()) + { + //Make sure we're in the whitelist + if( parallel_cells.find(c->type) == parallel_cells.end()) + return 17; + } + + //Figure out what port(s) are driven by it + //TODO: this can probably be done more efficiently w/o multiple iterations over our whole net? + for(auto b : qport) + { + pool<ModIndex::PortInfo> ports = index.query_ports(b); + for(auto x : ports) + { + if(x.cell != c) + continue; + extract.pouts.insert(ModIndex::PortInfo(c, x.port, 0)); + } + } + } + } + if(!is_full_bus(cnout, index, count_reg, "\\Q", underflow_inv, "\\A", true)) + return 18; + if(!is_full_bus(cnout, index, count_reg, "\\Q", cell, "\\A", true)) + return 19; + + //Look up the clock from the register + extract.clk = sigmap(count_reg->getPort("\\CLK")); + + //Register output net must have an INIT attribute equal to the count value + extract.rwire = cnout.as_wire(); + if(extract.rwire->attributes.find("\\init") == extract.rwire->attributes.end()) + return 20; + int rinit = extract.rwire->attributes["\\init"].as_int(); + if(rinit != extract.count_value) + return 21; + + return 0; +} + +void counter_worker( + ModIndex& index, + Cell *cell, + unsigned int& total_counters, + pool<Cell*>& cells_to_remove, + pool<pair<Cell*, string>>& cells_to_rename, + pool<RTLIL::IdString>& parallel_cells, + int maxwidth) +{ + SigMap& sigmap = index.sigmap; + + //Core of the counter must be an ALU + if (cell->type != "$alu") + return; + + //A input is the count value. Check if it has COUNT_EXTRACT set. + //If it's not a wire, don't even try + auto port = sigmap(cell->getPort("\\A")); + if(!port.is_wire()) + return; + RTLIL::Wire* a_wire = port.as_wire(); + bool force_extract = false; + bool never_extract = false; + string count_reg_src = a_wire->attributes["\\src"].decode_string().c_str(); + if(a_wire->attributes.find("\\COUNT_EXTRACT") != a_wire->attributes.end()) + { + pool<string> sa = a_wire->get_strpool_attribute("\\COUNT_EXTRACT"); + string extract_value; + if(sa.size() >= 1) + { + extract_value = *sa.begin(); + log(" Signal %s declared at %s has COUNT_EXTRACT = %s\n", + log_id(a_wire), + count_reg_src.c_str(), + extract_value.c_str()); + + if(extract_value == "FORCE") + force_extract = true; + else if(extract_value == "NO") + never_extract = true; + else if(extract_value == "AUTO") + {} //default + else + log_error(" Illegal COUNT_EXTRACT value %s (must be one of FORCE, NO, AUTO)\n", + extract_value.c_str()); + } + } + + //If we're explicitly told not to extract, don't infer a counter + if(never_extract) + return; + + //Attempt to extract a counter + CounterExtraction extract; + int reason = counter_tryextract(index, cell, extract, parallel_cells, maxwidth); + + //Nonzero code - we could not find a matchable counter. + //Do nothing, unless extraction was forced in which case give an error + if(reason != 0) + { + static const char* reasons[25]= + { + "no problem", //0 + "counter is too large/small", //1 + "counter does not count by one", //2 + "counter uses signed math", //3 + "counter does not count by one", //4 + "ALU is not a subtractor", //5 + "ALU is not a subtractor", //6 + "ALU ports used outside counter", //7 + "ALU ports used outside counter", //8 + "ALU output used outside counter", //9 + "ALU output is not a mux", //10 + "ALU output is not full bus", //11 + "Underflow value is not constant", //12 + "No underflow detector found", //13 + "Mux output is used outside counter", //14 + "Counter reg is not DFF/ADFF", //15 + "Counter input is not full bus", //16 + "Count register is used outside counter, but not by an allowed cell", //17 + "Register output is not full bus", //18 + "Register output is not full bus", //19 + "No init value found", //20 + "Underflow value is not equal to init value", //21 + "RESERVED, not implemented", //22, kept for compatibility but not used anymore + "Reset is not to zero or COUNT_TO", //23 + "Clock enable configuration is unsupported" //24 + }; + + if(force_extract) + { + log_error( + "Counter extraction is set to FORCE on register %s, but a counter could not be inferred (%s)\n", + log_id(a_wire), + reasons[reason]); + } + return; + } + + //Get new cell name + string countname = string("$COUNTx$") + log_id(extract.rwire->name.str()); + + //Wipe all of the old connections to the ALU + cell->unsetPort("\\A"); + cell->unsetPort("\\B"); + cell->unsetPort("\\BI"); + cell->unsetPort("\\CI"); + cell->unsetPort("\\CO"); + cell->unsetPort("\\X"); + cell->unsetPort("\\Y"); + cell->unsetParam("\\A_SIGNED"); + cell->unsetParam("\\A_WIDTH"); + cell->unsetParam("\\B_SIGNED"); + cell->unsetParam("\\B_WIDTH"); + cell->unsetParam("\\Y_WIDTH"); + + //Change the cell type + cell->type = "$__COUNT_"; + + //Hook up resets + if(extract.has_reset) + { + //TODO: support other kinds of reset + cell->setParam("\\RESET_MODE", RTLIL::Const("LEVEL")); + + //If the reset is active low, infer an inverter ($__COUNT_ cells always have active high reset) + if(extract.rst_inverted) + { + auto realreset = cell->module->addWire(NEW_ID); + cell->module->addNot(NEW_ID, extract.rst, RTLIL::SigSpec(realreset)); + cell->setPort("\\RST", realreset); + } + else + cell->setPort("\\RST", extract.rst); + } + else + { + cell->setParam("\\RESET_MODE", RTLIL::Const("RISING")); + cell->setPort("\\RST", RTLIL::SigSpec(false)); + } + + //Hook up other stuff + //cell->setParam("\\CLKIN_DIVIDE", RTLIL::Const(1)); + cell->setParam("\\COUNT_TO", RTLIL::Const(extract.count_value)); + cell->setParam("\\WIDTH", RTLIL::Const(extract.width)); + cell->setPort("\\CLK", extract.clk); + cell->setPort("\\OUT", extract.outsig); + + //Hook up clock enable + if(extract.has_ce) + { + cell->setParam("\\HAS_CE", RTLIL::Const(1)); + cell->setPort("\\CE", extract.ce); + } + else + cell->setParam("\\HAS_CE", RTLIL::Const(0)); + + //Hook up hard-wired ports (for now up/down are not supported), default to no parallel output + cell->setParam("\\HAS_POUT", RTLIL::Const(0)); + cell->setParam("\\RESET_TO_MAX", RTLIL::Const(0)); + cell->setParam("\\DIRECTION", RTLIL::Const("DOWN")); + cell->setPort("\\CE", RTLIL::Const(1)); + cell->setPort("\\UP", RTLIL::Const(0)); + + //Hook up any parallel outputs + for(auto load : extract.pouts) + { + log(" Counter has parallel output to cell %s port %s\n", log_id(load.cell->name), log_id(load.port)); + + //Find the wire hooked to the old port + auto sig = load.cell->getPort(load.port); + + //Connect it to our parallel output + //(this is OK to do more than once b/c they all go to the same place) + cell->setPort("\\POUT", sig); + cell->setParam("\\HAS_POUT", RTLIL::Const(1)); + } + + //Delete the cells we've replaced (let opt_clean handle deleting the now-redundant wires) + cells_to_remove.insert(extract.count_mux); + cells_to_remove.insert(extract.count_reg); + cells_to_remove.insert(extract.underflow_inv); + + //Log it + total_counters ++; + string reset_type = "non-resettable"; + if(extract.has_reset) + { + if(extract.rst_inverted) + reset_type = "negative"; + else + reset_type = "positive"; + + //TODO: support other kind of reset + reset_type += " async resettable"; + } + log(" Found %d-bit (%s) down counter %s (counting from %d) for register %s, declared at %s\n", + extract.width, + reset_type.c_str(), + countname.c_str(), + extract.count_value, + log_id(extract.rwire->name), + count_reg_src.c_str()); + + //Optimize the counter + //If we have no parallel output, and we have redundant bits, shrink us + if(extract.pouts.empty()) + { + //TODO: Need to update this when we add support for counters with nonzero reset values + //to make sure the reset value fits in our bit space too + + //Optimize it + int newbits = ceil(log2(extract.count_value)); + if(extract.width != newbits) + { + cell->setParam("\\WIDTH", RTLIL::Const(newbits)); + log(" Optimizing out %d unused high-order bits (new width is %d)\n", + extract.width - newbits, + newbits); + } + } + + //Finally, rename the cell + cells_to_rename.insert(pair<Cell*, string>(cell, countname)); +} + +struct ExtractCounterPass : public Pass { + ExtractCounterPass() : Pass("extract_counter", "Extract GreenPak4 counter cells") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" extract_counter [options] [selection]\n"); + log("\n"); + log("This pass converts non-resettable or async resettable down counters to\n"); + log("counter cells. Use a target-specific 'techmap' map file to convert those cells\n"); + log("to the actual target cells.\n"); + log("\n"); + log(" -maxwidth N\n"); + log(" Only extract counters up to N bits wide\n"); + log("\n"); + log(" -pout X,Y,...\n"); + log(" Only allow parallel output from the counter to the listed cell types\n"); + log(" (if not specified, parallel outputs are not restricted)\n"); + log("\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing EXTRACT_COUNTER pass (find counters in netlist).\n"); + + int maxwidth = 64; + size_t argidx; + pool<RTLIL::IdString> parallel_cells; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-pout") + { + if(argidx + 1 >= args.size()) + { + log_error("extract_counter -pout requires an argument\n"); + return; + } + + std::string pouts = args[++argidx]; + std::string tmp; + for(size_t i=0; i<pouts.length(); i++) + { + if(pouts[i] == ',') + { + parallel_cells.insert(RTLIL::escape_id(tmp)); + tmp = ""; + } + else + tmp += pouts[i]; + } + parallel_cells.insert(RTLIL::escape_id(tmp)); + continue; + } + + if (args[argidx] == "-maxwidth" && argidx+1 < args.size()) + { + maxwidth = atoi(args[++argidx].c_str()); + continue; + } + } + extra_args(args, argidx, design); + + //Extract all of the counters we could find + unsigned int total_counters = 0; + for (auto module : design->selected_modules()) + { + pool<Cell*> cells_to_remove; + pool<pair<Cell*, string>> cells_to_rename; + + ModIndex index(module); + for (auto cell : module->selected_cells()) + counter_worker(index, cell, total_counters, cells_to_remove, cells_to_rename, parallel_cells, maxwidth); + + for(auto cell : cells_to_remove) + { + //log("Removing cell %s\n", log_id(cell->name)); + module->remove(cell); + } + + for(auto cpair : cells_to_rename) + { + //log("Renaming cell %s to %s\n", log_id(cpair.first->name), cpair.second.c_str()); + module->rename(cpair.first, cpair.second); + } + } + + if(total_counters) + log("Extracted %u counters\n", total_counters); + } +} ExtractCounterPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/extract_fa.cc b/passes/techmap/extract_fa.cc new file mode 100644 index 00000000..9e6dc0d2 --- /dev/null +++ b/passes/techmap/extract_fa.cc @@ -0,0 +1,605 @@ +/* + * 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( "$_BUF_", "$_NOT_", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", + "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_", "$_MUX_", + "$_AOI3_", "$_OAI3_", "$_AOI4_", "$_OAI4_")) + { + SigBit y = sigmap(SigBit(cell->getPort("\\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)) + log_abort(); + + 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)) + log_abort(); + + 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("\\A")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\A")))); + if (cell->hasPort("\\B")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\B")))); + if (cell->hasPort("\\C")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\C")))); + if (cell->hasPort("\\D")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\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("\\Y")) == bit) { + cell->setPort("\\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("$_BUF_", "$_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, "$fa"); + cell->setParam("\\WIDTH", 1); + + log(" Created $fa cell %s.\n", log_id(cell)); + + cell->setPort("\\A", f3i.inv_a ? module->NotGate(NEW_ID, A) : A); + cell->setPort("\\B", f3i.inv_b ? module->NotGate(NEW_ID, B) : B); + cell->setPort("\\C", f3i.inv_c ? module->NotGate(NEW_ID, C) : C); + + X = module->addWire(NEW_ID); + Y = module->addWire(NEW_ID); + + cell->setPort("\\X", X); + cell->setPort("\\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, "$fa"); + cell->setParam("\\WIDTH", 1); + + log(" Created $fa cell %s.\n", log_id(cell)); + + cell->setPort("\\A", f2i.inv_a ? module->NotGate(NEW_ID, A) : A); + cell->setPort("\\B", f2i.inv_b ? module->NotGate(NEW_ID, B) : B); + cell->setPort("\\C", State::S0); + + X = module->addWire(NEW_ID); + Y = module->addWire(NEW_ID); + + cell->setPort("\\X", X); + cell->setPort("\\Y", Y); + } + + if (func2.at(key).count(xor2_func)) { + SigBit YY = invert_xy ? 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 ? 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 diff --git a/passes/techmap/extract_reduce.cc b/passes/techmap/extract_reduce.cc new file mode 100644 index 00000000..a77bbc0b --- /dev/null +++ b/passes/techmap/extract_reduce.cc @@ -0,0 +1,324 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2017 Robert Ou <rqou@robertou.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. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include <deque> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ExtractReducePass : public Pass +{ + enum GateType { + And, + Or, + Xor + }; + + ExtractReducePass() : Pass("extract_reduce", "converts gate chains into $reduce_* cells") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" extract_reduce [options] [selection]\n"); + log("\n"); + log("converts gate chains into $reduce_* cells\n"); + log("\n"); + log("This command finds chains of $_AND_, $_OR_, and $_XOR_ cells and replaces them\n"); + log("with their corresponding $reduce_* cells. Because this command only operates on\n"); + log("these cell types, it is recommended to map the design to only these cell types\n"); + log("using the `abc -g` command. Note that, in some cases, it may be more effective\n"); + log("to map the design to only $_AND_ cells, run extract_reduce, map the remaining\n"); + log("parts of the design to AND/OR/XOR cells, and run extract_reduce a second time.\n"); + log("\n"); + log(" -allow-off-chain\n"); + log(" Allows matching of cells that have loads outside the chain. These cells\n"); + log(" will be replicated and folded into the $reduce_* cell, but the original\n"); + log(" cell will remain, driving its original loads.\n"); + log("\n"); + } + + inline bool IsRightType(Cell* cell, GateType gt) + { + return (cell->type == "$_AND_" && gt == GateType::And) || + (cell->type == "$_OR_" && gt == GateType::Or) || + (cell->type == "$_XOR_" && gt == GateType::Xor); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing EXTRACT_REDUCE pass.\n"); + log_push(); + + size_t argidx; + bool allow_off_chain = false; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-allow-off-chain") + { + allow_off_chain = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + + // Index all of the nets in the module + dict<SigBit, Cell*> sig_to_driver; + dict<SigBit, pool<Cell*>> sig_to_sink; + for (auto cell : module->selected_cells()) + { + for (auto &conn : cell->connections()) + { + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + sig_to_driver[bit] = cell; + + if (cell->input(conn.first)) + { + for (auto bit : sigmap(conn.second)) + { + if (sig_to_sink.count(bit) == 0) + sig_to_sink[bit] = pool<Cell*>(); + sig_to_sink[bit].insert(cell); + } + } + } + } + + // Need to check if any wires connect to module ports + pool<SigBit> port_sigs; + for (auto wire : module->selected_wires()) + if (wire->port_input || wire->port_output) + for (auto bit : sigmap(wire)) + port_sigs.insert(bit); + + // Actual logic starts here + pool<Cell*> consumed_cells; + for (auto cell : module->selected_cells()) + { + if (consumed_cells.count(cell)) + continue; + + GateType gt; + + if (cell->type == "$_AND_") + gt = GateType::And; + else if (cell->type == "$_OR_") + gt = GateType::Or; + else if (cell->type == "$_XOR_") + gt = GateType::Xor; + else + continue; + + log("Working on cell %s...\n", cell->name.c_str()); + + // If looking for a single chain, follow linearly to the sink + pool<Cell*> sinks; + if(!allow_off_chain) + { + Cell* head_cell = cell; + Cell* x = cell; + while (true) + { + if(!IsRightType(x, gt)) + break; + + head_cell = x; + + auto y = sigmap(x->getPort("\\Y")); + log_assert(y.size() == 1); + + // Should only continue if there is one fanout back into a cell (not to a port) + if (sig_to_sink[y[0]].size() != 1) + break; + + x = *sig_to_sink[y[0]].begin(); + } + + sinks.insert(head_cell); + } + + //If off-chain loads are allowed, we have to do a wider traversal to see what the longest chain is + else + { + //BFS, following all chains until they hit a cell of a different type + //Pick the longest one + auto y = sigmap(cell->getPort("\\Y")); + pool<Cell*> current_loads = sig_to_sink[y]; + pool<Cell*> next_loads; + + while(!current_loads.empty()) + { + //Find each sink and see what they are + for(auto x : current_loads) + { + //Not one of our gates? Don't follow any further + //(but add the originating cell to the list of sinks) + if(!IsRightType(x, gt)) + { + sinks.insert(cell); + continue; + } + + //If this signal drives a port, add it to the sinks + //(even though it may not be the end of a chain) + if(port_sigs.count(x) && !consumed_cells.count(x)) + sinks.insert(x); + + //It's a match, search everything out from it + auto& next = sig_to_sink[x]; + for(auto z : next) + next_loads.insert(z); + } + + //If we couldn't find any downstream loads, stop. + //Create a reduction for each of the max-length chains we found + if(next_loads.empty()) + { + for(auto s : current_loads) + { + //Not one of our gates? Don't follow any further + if(!IsRightType(s, gt)) + continue; + + sinks.insert(s); + } + break; + } + + //Otherwise, continue down the chain + current_loads = next_loads; + next_loads.clear(); + } + } + + //We have our list, go act on it + for(auto head_cell : sinks) + { + log(" Head cell is %s\n", head_cell->name.c_str()); + + //Avoid duplication if we already were covered + if(consumed_cells.count(head_cell)) + continue; + + pool<Cell*> cur_supercell; + std::deque<Cell*> bfs_queue = {head_cell}; + while (bfs_queue.size()) + { + Cell* x = bfs_queue.front(); + bfs_queue.pop_front(); + + cur_supercell.insert(x); + + auto a = sigmap(x->getPort("\\A")); + log_assert(a.size() == 1); + + // Must have only one sink unless we're going off chain + // XXX: Check that it is indeed this node? + if( allow_off_chain || (sig_to_sink[a[0]].size() + port_sigs.count(a[0]) == 1) ) + { + Cell* cell_a = sig_to_driver[a[0]]; + if(cell_a && IsRightType(cell_a, gt)) + { + // The cell here is the correct type, and it's definitely driving + // this current cell. + bfs_queue.push_back(cell_a); + } + } + + auto b = sigmap(x->getPort("\\B")); + log_assert(b.size() == 1); + + // Must have only one sink + // XXX: Check that it is indeed this node? + if( allow_off_chain || (sig_to_sink[b[0]].size() + port_sigs.count(b[0]) == 1) ) + { + Cell* cell_b = sig_to_driver[b[0]]; + if(cell_b && IsRightType(cell_b, gt)) + { + // The cell here is the correct type, and it's definitely driving only + // this current cell. + bfs_queue.push_back(cell_b); + } + } + } + + log(" Cells:\n"); + for (auto x : cur_supercell) + log(" %s\n", x->name.c_str()); + + if (cur_supercell.size() > 1) + { + // Worth it to create reduce cell + log(" Creating $reduce_* cell!\n"); + + pool<SigBit> input_pool; + pool<SigBit> input_pool_intermed; + for (auto x : cur_supercell) + { + input_pool.insert(sigmap(x->getPort("\\A"))[0]); + input_pool.insert(sigmap(x->getPort("\\B"))[0]); + input_pool_intermed.insert(sigmap(x->getPort("\\Y"))[0]); + } + SigSpec input; + for (auto b : input_pool) + if (input_pool_intermed.count(b) == 0) + input.append_bit(b); + + SigBit output = sigmap(head_cell->getPort("\\Y")[0]); + + auto new_reduce_cell = module->addCell(NEW_ID, + gt == GateType::And ? "$reduce_and" : + gt == GateType::Or ? "$reduce_or" : + gt == GateType::Xor ? "$reduce_xor" : ""); + new_reduce_cell->setParam("\\A_SIGNED", 0); + new_reduce_cell->setParam("\\A_WIDTH", input.size()); + new_reduce_cell->setParam("\\Y_WIDTH", 1); + new_reduce_cell->setPort("\\A", input); + new_reduce_cell->setPort("\\Y", output); + + if(allow_off_chain) + consumed_cells.insert(head_cell); + else + { + for (auto x : cur_supercell) + consumed_cells.insert(x); + } + } + } + } + + // Remove all of the head cells, since we supplant them. + // Do not remove the upstream cells since some might still be in use ("clean" will get rid of unused ones) + for (auto cell : consumed_cells) + module->remove(cell); + } + + log_pop(); + } +} ExtractReducePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index 82cecac2..9ec651ae 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -55,7 +55,7 @@ void hilomap_worker(RTLIL::SigSpec &sig) struct HilomapPass : public Pass { HilomapPass() : Pass("hilomap", "technology mapping of constant hi- and/or lo-drivers") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" hilomap [options] [selection]\n"); @@ -74,7 +74,7 @@ struct HilomapPass : public Pass { log(" each constant bit.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n"); diff --git a/passes/techmap/insbuf.cc b/passes/techmap/insbuf.cc index aa81468d..2173049b 100644 --- a/passes/techmap/insbuf.cc +++ b/passes/techmap/insbuf.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct InsbufPass : public Pass { InsbufPass() : Pass("insbuf", "insert buffer cells for connected wires") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" insbuf [options] [selection]\n"); @@ -37,7 +37,7 @@ struct InsbufPass : public Pass { log(" call to \"clean\" will remove all $_BUF_ in the design.)\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing INSBUF pass (insert buffer cells for connected wires).\n"); diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 4acbf7c0..efcc082d 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -34,7 +34,7 @@ void split_portname_pair(std::string &port1, std::string &port2) struct IopadmapPass : public Pass { IopadmapPass() : Pass("iopadmap", "technology mapping of i/o pads (or buffers)") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" iopadmap [options] [selection]\n"); @@ -78,7 +78,7 @@ struct IopadmapPass : public Pass { log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); @@ -146,11 +146,37 @@ struct IopadmapPass : public Pass { for (auto module : design->selected_modules()) { dict<IdString, pool<int>> skip_wires; + pool<SigBit> skip_wire_bits; + SigMap sigmap(module); + + for (auto cell : module->cells()) + { + if (cell->type == RTLIL::escape_id(inpad_celltype) && cell->hasPort(RTLIL::escape_id(inpad_portname2))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inpad_portname2)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(outpad_celltype) && cell->hasPort(RTLIL::escape_id(outpad_portname2))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(outpad_portname2)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(inoutpad_celltype) && cell->hasPort(RTLIL::escape_id(inoutpad_portname2))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inoutpad_portname2)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(toutpad_celltype) && cell->hasPort(RTLIL::escape_id(toutpad_portname3))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(toutpad_portname3)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(tinoutpad_celltype) && cell->hasPort(RTLIL::escape_id(tinoutpad_portname4))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(tinoutpad_portname4)))) + skip_wire_bits.insert(bit); + } if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) { - SigMap sigmap(module); dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits; + pool<pair<IdString, IdString>> norewrites; + SigMap rewrites; for (auto cell : module->cells()) if (cell->type == "$_TBUF_") { @@ -177,6 +203,9 @@ struct IopadmapPass : public Pass { if (tbuf_bits.count(mapped_wire_bit) == 0) continue; + if (skip_wire_bits.count(mapped_wire_bit)) + continue; + auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit); Cell *tbuf_cell = module->cell(tbuf_cache.first); @@ -219,6 +248,9 @@ struct IopadmapPass : public Pass { module->remove(tbuf_cell); skip_wires[wire->name].insert(i); + + norewrites.insert(make_pair(cell->name, RTLIL::escape_id(tinoutpad_portname4))); + rewrites.add(sigmap(wire_bit), owire); continue; } @@ -256,6 +288,22 @@ struct IopadmapPass : public Pass { } } } + + if (GetSize(norewrites)) + { + for (auto cell : module->cells()) + for (auto port : cell->connections()) + { + if (norewrites.count(make_pair(cell->name, port.first))) + continue; + + SigSpec orig_sig = sigmap(port.second); + SigSpec new_sig = rewrites(orig_sig); + + if (orig_sig != new_sig) + cell->setPort(port.first, new_sig); + } + } } for (auto wire : module->selected_wires()) @@ -272,6 +320,13 @@ struct IopadmapPass : public Pass { skip_bit_indices = skip_wires.at(wire->name); } + for (int i = 0; i < GetSize(wire); i++) + if (skip_wire_bits.count(sigmap(SigBit(wire, i)))) + skip_bit_indices.insert(i); + + if (GetSize(wire) == GetSize(skip_bit_indices)) + continue; + if (wire->port_input && !wire->port_output) { if (inpad_celltype.empty()) { log("Don't map input port %s.%s: Missing option -inpad.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name)); diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index d5254c02..d3b1ff02 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -100,8 +100,15 @@ int LibertyParser::lexer(std::string &str) break; } f.unget(); - // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); - return 'v'; + if (str == "+" || str == "-") { + /* Single operator is not an identifier */ + // fprintf(stderr, "LEX: char >>%s<<\n", str.c_str()); + return str[0]; + } + else { + // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); + return 'v'; + } } if (c == '"') { @@ -191,6 +198,19 @@ LibertyAst *LibertyParser::parse() tok = lexer(ast->value); if (tok != 'v') error(); + tok = lexer(str); + while (tok == '+' || tok == '-' || tok == '*' || tok == '/') { + ast->value += tok; + tok = lexer(str); + if (tok != 'v') + error(); + ast->value += str; + tok = lexer(str); + } + if (tok == ';') + break; + else + error(); continue; } diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc index 2bb0bd8b..d32bbff1 100644 --- a/passes/techmap/lut2mux.cc +++ b/passes/techmap/lut2mux.cc @@ -56,7 +56,7 @@ int lut2mux(Cell *cell) struct Lut2muxPass : public Pass { Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -65,7 +65,7 @@ struct Lut2muxPass : public Pass { log("This pass converts $lut cells to $_MUX_ gates.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n"); diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 32569d07..3e8e59e6 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -365,7 +365,7 @@ PRIVATE_NAMESPACE_BEGIN struct MaccmapPass : public Pass { MaccmapPass() : Pass("maccmap", "mapping macc cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -375,7 +375,7 @@ struct MaccmapPass : public Pass { log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool unmap_mode = false; diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index 1dc64958..12da9ed0 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -561,7 +561,7 @@ struct MuxcoverWorker struct MuxcoverPass : public Pass { MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -579,7 +579,7 @@ struct MuxcoverPass : public Pass { log(" less efficient than the original circuit.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n"); diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc index 6fcdf82b..cc765d89 100644 --- a/passes/techmap/nlutmap.cc +++ b/passes/techmap/nlutmap.cc @@ -92,7 +92,7 @@ struct NlutmapWorker for (auto bit : sigmap(conn.second)) bit_lut_count[bit]++; } - + for (auto &cand : candidate_ratings) { for (auto &conn : cand.first->connections()) @@ -129,7 +129,7 @@ struct NlutmapWorker struct NlutmapPass : public Pass { NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -149,7 +149,7 @@ struct NlutmapPass : public Pass { log("to generic logic gates ($_AND_, etc.).\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { NlutmapConfig config; diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc index c626dbcc..b7a22dc3 100644 --- a/passes/techmap/pmuxtree.cc +++ b/passes/techmap/pmuxtree.cc @@ -67,7 +67,7 @@ static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data, struct PmuxtreePass : public Pass { PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -76,7 +76,7 @@ struct PmuxtreePass : public Pass { log("This pass transforms $pmux cells to a trees of $mux cells.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PMUXTREE pass.\n"); diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index 6936b499..f20863ba 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -391,7 +391,7 @@ struct ShregmapWorker struct ShregmapPass : public Pass { ShregmapPass() : Pass("shregmap", "map shift registers") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -449,7 +449,7 @@ struct ShregmapPass : public Pass { log(" map to greenpak4 shift registers.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { ShregmapOptions opts; string clkpol, enpol; diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index c6b932bd..660b6060 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -575,7 +575,7 @@ PRIVATE_NAMESPACE_BEGIN struct SimplemapPass : public Pass { SimplemapPass() : Pass("simplemap", "mapping simple coarse-grain cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -590,7 +590,7 @@ struct SimplemapPass : public Pass { log(" $sr, $ff, $dff, $dffsr, $adff, $dlatch\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); extra_args(args, 1, design); diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 6784f48c..d0e5e223 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -171,18 +171,15 @@ struct TechmapWorker } std::string orig_cell_name; - pool<string> extra_src_attrs; + pool<string> extra_src_attrs = cell->get_strpool_attribute("\\src"); - if (!flatten_mode) - { + if (!flatten_mode) { for (auto &it : tpl->cells_) if (it.first == "\\_TECHMAP_REPLACE_") { orig_cell_name = cell->name.str(); module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); break; } - - extra_src_attrs = cell->get_strpool_attribute("\\src"); } dict<IdString, IdString> memory_renames; @@ -247,6 +244,9 @@ struct TechmapWorker continue; } + if (GetSize(it.second) == 0) + continue; + RTLIL::Wire *w = tpl->wires_.at(portname); RTLIL::SigSig c, extra_connect; @@ -305,10 +305,15 @@ struct TechmapWorker // approach that yields nicer outputs: // replace internal wires that are connected to external wires - if (w->port_output) + if (w->port_output && !w->port_input) { port_signal_map.add(c.second, c.first); - else + } else + if (!w->port_output && w->port_input) { port_signal_map.add(c.first, c.second); + } else { + module->connect(c); + extra_connect = SigSig(); + } for (auto &attr : w->attributes) { if (attr.first == "\\src") @@ -322,8 +327,9 @@ struct TechmapWorker for (auto &it : tpl->cells_) { std::string c_name = it.second->name.str(); + bool techmap_replace_cell = (!flatten_mode) && (c_name == "\\_TECHMAP_REPLACE_"); - if (!flatten_mode && c_name == "\\_TECHMAP_REPLACE_") + if (techmap_replace_cell) c_name = orig_cell_name; else apply_prefix(cell->name.str(), c_name); @@ -353,6 +359,11 @@ struct TechmapWorker if (c->attributes.count("\\src")) c->add_strpool_attribute("\\src", extra_src_attrs); + + if (techmap_replace_cell) + for (auto attr : cell->attributes) + if (!c->attributes.count(attr.first)) + c->attributes[attr.first] = attr.second; } for (auto &it : tpl->connections()) { @@ -451,6 +462,7 @@ struct TechmapWorker bool mapped_cell = false; std::string cell_type = cell->type.str(); + if (in_recursion && cell_type.substr(0, 2) == "\\$") cell_type = cell_type.substr(1); @@ -498,6 +510,8 @@ struct TechmapWorker extmapper_module = extmapper_design->addModule(m_name); RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell); + extmapper_cell->set_src_attribute(cell->get_src_attribute()); + int port_counter = 1; for (auto &c : extmapper_cell->connections_) { RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second)); @@ -877,7 +891,7 @@ struct TechmapWorker struct TechmapPass : public Pass { TechmapPass() : Pass("techmap", "generic technology mapper") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -919,7 +933,7 @@ struct TechmapPass : public Pass { log(" -D <define>, -I <incdir>\n"); log(" this options are passed as-is to the Verilog frontend for loading the\n"); log(" map file. Note that the Verilog frontend is also called with the\n"); - log(" '-ignore_redef' option set.\n"); + log(" '-nooverwrite' option set.\n"); log("\n"); log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n"); log("match cells with a type that match the text value of this attribute. Otherwise\n"); @@ -1000,7 +1014,7 @@ struct TechmapPass : public Pass { log("constant value.\n"); log("\n"); log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n"); - log("of the cell that is being replaced.\n"); + log("and attributes of the cell that is being replaced.\n"); log("\n"); log("See 'help extract' for a pass that does the opposite thing.\n"); log("\n"); @@ -1008,7 +1022,7 @@ struct TechmapPass : public Pass { log("essentially techmap but using the design itself as map library).\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing TECHMAP pass (map to technology primitives).\n"); log_push(); @@ -1017,7 +1031,7 @@ struct TechmapPass : public Pass { simplemap_get_mappers(worker.simplemap_mappers); std::vector<std::string> map_files; - std::string verilog_frontend = "verilog -ignore_redef"; + std::string verilog_frontend = "verilog -nooverwrite"; int max_iter = -1; size_t argidx; @@ -1076,6 +1090,7 @@ struct TechmapPass : public Pass { std::ifstream f; rewrite_filename(fn); f.open(fn.c_str()); + yosys_input_files.insert(fn); if (f.fail()) log_cmd_error("Can't open map file `%s'\n", fn.c_str()); Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend); @@ -1126,7 +1141,7 @@ struct TechmapPass : public Pass { struct FlattenPass : public Pass { FlattenPass() : Pass("flatten", "flatten design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1140,7 +1155,7 @@ struct FlattenPass : public Pass { log("flattened by this command.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FLATTEN pass (flatten design).\n"); log_push(); diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc index 03629082..587cb903 100644 --- a/passes/techmap/tribuf.cc +++ b/passes/techmap/tribuf.cc @@ -139,7 +139,7 @@ struct TribufWorker { struct TribufPass : public Pass { TribufPass() : Pass("tribuf", "infer tri-state buffers") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -156,7 +156,7 @@ struct TribufPass : public Pass { log(" to non-tristate logic. this option implies -merge.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { TribufConfig config; diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc index a577e123..b46147fb 100644 --- a/passes/techmap/zinit.cc +++ b/passes/techmap/zinit.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct ZinitPass : public Pass { ZinitPass() : Pass("zinit", "add inverters so all FF are zero-initialized") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -37,7 +37,7 @@ struct ZinitPass : public Pass { log(" also add zero initialization to uninitialized FFs\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool all_mode = false; diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index 09cb4195..5d5466af 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -244,7 +244,7 @@ static void test_abcloop() struct TestAbcloopPass : public Pass { TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -259,7 +259,7 @@ struct TestAbcloopPass : public Pass { log(" use this value as rng seed value (default = unix time).\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design*) + void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE { int num_iter = 100; xorshift32_state = 0; diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index cb31056f..bfb1d664 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -324,7 +324,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int s struct TestAutotbBackend : public Backend { TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -349,7 +349,7 @@ struct TestAutotbBackend : public Backend { log(" number of iterations the test bench should run (default = 1000)\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { int num_iter = 1000; int seed = 0; diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 049c2053..e360b5ed 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -652,7 +652,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: struct TestCellPass : public Pass { TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -712,7 +712,7 @@ struct TestCellPass : public Pass { log(" create a Verilog test bench to test simlib and write_verilog\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design*) + void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE { int num_iter = 100; std::string techmap_cmd = "techmap -assert"; @@ -852,8 +852,6 @@ struct TestCellPass : public Pass { // cell_types["$slice"] = "A"; // cell_types["$concat"] = "A"; - // cell_types["$assert"] = "A"; - // cell_types["$assume"] = "A"; cell_types["$lut"] = "*"; cell_types["$sop"] = "*"; |