aboutsummaryrefslogtreecommitdiffstats
path: root/passes/techmap
diff options
context:
space:
mode:
Diffstat (limited to 'passes/techmap')
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/abc9.cc149
-rw-r--r--passes/techmap/attrmap.cc187
-rw-r--r--passes/techmap/clkbufmap.cc298
-rw-r--r--passes/techmap/dff2dffe.cc2
-rw-r--r--passes/techmap/iopadmap.cc58
-rw-r--r--passes/techmap/shregmap.cc181
-rw-r--r--passes/techmap/techmap.cc10
8 files changed, 559 insertions, 327 deletions
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 56f05eca4..631a80aa5 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -16,6 +16,7 @@ endif
ifneq ($(SMALL),1)
OBJS += passes/techmap/iopadmap.o
+OBJS += passes/techmap/clkbufmap.o
OBJS += passes/techmap/hilomap.o
OBJS += passes/techmap/extract.o
OBJS += passes/techmap/extract_fa.o
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index c1d4fff2d..6fdf987f0 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -76,12 +76,11 @@ inline std::string remap_name(RTLIL::IdString abc_name)
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
}
-void handle_loops(RTLIL::Design *design)
+void handle_loops(RTLIL::Design *design,
+ const dict<IdString,pool<IdString>> &scc_break_inputs)
{
Pass::call(design, "scc -set_attr abc_scc_id {}");
- dict<IdString, vector<IdString>> abc_scc_break;
-
// For every unique SCC found, (arbitrarily) find the first
// cell in the component, and select (and mark) all its output
// wires
@@ -116,44 +115,29 @@ void handle_loops(RTLIL::Design *design)
cell->attributes.erase(it);
}
- auto jt = abc_scc_break.find(cell->type);
- if (jt == abc_scc_break.end()) {
- std::vector<IdString> ports;
- RTLIL::Module* box_module = design->module(cell->type);
- if (box_module) {
- auto ports_csv = box_module->attributes.at(ID(abc_scc_break), RTLIL::Const::from_string("")).decode_string();
- for (const auto &port_name : split_tokens(ports_csv, ",")) {
- auto port_id = RTLIL::escape_id(port_name);
- auto kt = cell->connections_.find(port_id);
- if (kt == cell->connections_.end())
- log_error("abc_scc_break attribute value '%s' does not exist as port on module '%s'\n", port_name.c_str(), log_id(box_module));
- ports.push_back(port_id);
- }
- }
- jt = abc_scc_break.insert(std::make_pair(cell->type, std::move(ports))).first;
- }
-
- for (auto port_name : jt->second) {
- RTLIL::SigSpec sig;
- auto &rhs = cell->connections_.at(port_name);
- for (auto b : rhs) {
- Wire *w = b.wire;
- if (!w) continue;
- w->port_output = true;
- w->set_bool_attribute(ID(abc_scc_break));
- w = module->wire(stringf("%s.abci", w->name.c_str()));
- if (!w) {
- w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
- w->port_input = true;
- }
- else {
- log_assert(b.offset < GetSize(w));
- log_assert(w->port_input);
+ auto jt = scc_break_inputs.find(cell->type);
+ if (jt != scc_break_inputs.end())
+ for (auto port_name : jt->second) {
+ RTLIL::SigSpec sig;
+ auto &rhs = cell->connections_.at(port_name);
+ for (auto b : rhs) {
+ Wire *w = b.wire;
+ if (!w) continue;
+ w->port_output = true;
+ w->set_bool_attribute(ID(abc_scc_break));
+ w = module->wire(stringf("%s.abci", w->name.c_str()));
+ if (!w) {
+ w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
+ w->port_input = true;
+ }
+ else {
+ log_assert(b.offset < GetSize(w));
+ log_assert(w->port_input);
+ }
+ sig.append(RTLIL::SigBit(w, b.offset));
}
- sig.append(RTLIL::SigBit(w, b.offset));
+ rhs = sig;
}
- rhs = sig;
- }
}
module->fixup_ports();
@@ -288,7 +272,9 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file,
- std::string wire_delay, const dict<int,IdString> &box_lookup)
+ std::string wire_delay, const dict<int,IdString> &box_lookup,
+ const dict<IdString,pool<IdString>> &scc_break_inputs
+)
{
module = current_module;
map_autoidx = autoidx++;
@@ -427,7 +413,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
RTLIL::Selection& sel = design->selection_stack.back();
sel.select(module);
- handle_loops(design);
+ handle_loops(design, scc_break_inputs);
Pass::call(design, "aigmap");
@@ -708,30 +694,27 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
int in_wires = 0, out_wires = 0;
// Stitch in mapped_mod's inputs/outputs into module
- for (auto &it : mapped_mod->wires_) {
- RTLIL::Wire *w = it.second;
- if (!w->port_input && !w->port_output)
- continue;
- RTLIL::Wire *wire = module->wire(w->name);
+ for (auto port : mapped_mod->ports) {
+ RTLIL::Wire *w = mapped_mod->wire(port);
+ RTLIL::Wire *wire = module->wire(port);
log_assert(wire);
- RTLIL::Wire *remap_wire = module->wire(remap_name(w->name));
+ RTLIL::Wire *remap_wire = module->wire(remap_name(port));
RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));
log_assert(GetSize(signal) >= GetSize(remap_wire));
- log_assert(w->port_input || w->port_output);
RTLIL::SigSig conn;
- if (w->port_input) {
- conn.first = remap_wire;
- conn.second = signal;
- in_wires++;
- module->connect(conn);
- }
if (w->port_output) {
conn.first = signal;
conn.second = remap_wire;
out_wires++;
module->connect(conn);
}
+ else if (w->port_input) {
+ conn.first = remap_wire;
+ conn.second = signal;
+ in_wires++;
+ module->connect(conn);
+ }
}
for (auto &it : bit_users)
@@ -1081,6 +1064,7 @@ struct Abc9Pass : public Pass {
extra_args(args, argidx, design);
dict<int,IdString> box_lookup;
+ dict<IdString,pool<IdString>> scc_break_inputs;
for (auto m : design->modules()) {
auto it = m->attributes.find(ID(abc_box_id));
if (it == m->attributes.end())
@@ -1093,6 +1077,56 @@ struct Abc9Pass : public Pass {
log_error("Module '%s' has the same abc_box_id = %d value as '%s'.\n",
log_id(m), id, log_id(r.first->second));
log_assert(r.second);
+
+ RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
+ for (auto p : m->ports) {
+ auto w = m->wire(p);
+ log_assert(w);
+ if (w->port_input) {
+ if (w->attributes.count(ID(abc_scc_break)))
+ scc_break_inputs[m->name].insert(p);
+ if (w->attributes.count(ID(abc_carry))) {
+ if (carry_in)
+ log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
+ carry_in = w;
+ }
+ }
+ if (w->port_output) {
+ if (w->attributes.count(ID(abc_carry))) {
+ if (carry_out)
+ log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
+ carry_out = w;
+ }
+ }
+ }
+ if (carry_in || carry_out) {
+ if (carry_in && !carry_out)
+ log_error("Module '%s' contains an 'abc_carry' input port but no output port.\n", log_id(m));
+ if (!carry_in && carry_out)
+ log_error("Module '%s' contains an 'abc_carry' output port but no input port.\n", log_id(m));
+ // Make carry_in the last PI, and carry_out the last PO
+ // since ABC requires it this way
+ auto &ports = m->ports;
+ for (auto it = ports.begin(); it != ports.end(); ) {
+ RTLIL::Wire* w = m->wire(*it);
+ log_assert(w);
+ if (w == carry_in || w == carry_out) {
+ it = ports.erase(it);
+ continue;
+ }
+ if (w->port_id > carry_in->port_id)
+ --w->port_id;
+ if (w->port_id > carry_out->port_id)
+ --w->port_id;
+ log_assert(w->port_input || w->port_output);
+ log_assert(ports[w->port_id-1] == w->name);
+ ++it;
+ }
+ ports.push_back(carry_in->name);
+ carry_in->port_id = ports.size();
+ ports.push_back(carry_out->name);
+ carry_out->port_id = ports.size();
+ }
}
for (auto mod : design->selected_modules())
@@ -1110,7 +1144,7 @@ struct Abc9Pass : public Pass {
if (!dff_mode || !clk_str.empty()) {
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup);
+ box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
continue;
}
@@ -1256,16 +1290,13 @@ struct Abc9Pass : public Pass {
en_sig = assign_map(std::get<3>(it.first));
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup);
+ box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
assign_map.set(mod);
}
}
assign_map.clear();
- // The "clean" pass also contains a design->check() call
- Pass::call(design, "clean");
-
log_pop();
}
} Abc9Pass;
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
index a38638e0b..5f30817d4 100644
--- a/passes/techmap/attrmap.cc
+++ b/passes/techmap/attrmap.cc
@@ -143,6 +143,82 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio
attributes.swap(new_attributes);
}
+void log_attrmap_paramap_options()
+{
+ log(" -tocase <name>\n");
+ log(" Match attribute names case-insensitively and set it to the specified\n");
+ log(" name.\n");
+ log("\n");
+ log(" -rename <old_name> <new_name>\n");
+ log(" Rename attributes as specified\n");
+ log("\n");
+ log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Map key/value pairs as indicated.\n");
+ log("\n");
+ log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Like -map, but use case-insensitive match for <old_value> when\n");
+ log(" it is a string value.\n");
+ log("\n");
+ log(" -remove <name>=<value>\n");
+ log(" Remove attributes matching this pattern.\n");
+}
+
+bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions)
+{
+ std::string arg = args[argidx];
+ if (arg == "-tocase" && argidx+1 < args.size()) {
+ auto action = new AttrmapTocase;
+ action->name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-rename" && argidx+2 < args.size()) {
+ auto action = new AttrmapRename;
+ action->old_name = args[++argidx];
+ action->new_name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
+ string arg1 = args[++argidx];
+ string arg2 = args[++argidx];
+ string val1, val2;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ p = arg2.find("=");
+ if (p != string::npos) {
+ val2 = arg2.substr(p+1);
+ arg2 = arg2.substr(0, p);
+ }
+ auto action = new AttrmapMap;
+ action->imap = (arg == "-map");
+ action->old_name = arg1;
+ action->new_name = arg2;
+ action->old_value = val1;
+ action->new_value = val2;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-remove" && argidx+1 < args.size()) {
+ string arg1 = args[++argidx], val1;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ auto action = new AttrmapRemove;
+ action->name = arg1;
+ action->has_value = (p != string::npos);
+ action->value = val1;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ return false;
+}
+
struct AttrmapPass : public Pass {
AttrmapPass() : Pass("attrmap", "renaming attributes") { }
void help() YS_OVERRIDE
@@ -151,25 +227,10 @@ struct AttrmapPass : public Pass {
log("\n");
log(" attrmap [options] [selection]\n");
log("\n");
- log("This command renames attributes and/or mapps key/value pairs to\n");
+ log("This command renames attributes and/or maps key/value pairs to\n");
log("other key/value pairs.\n");
log("\n");
- log(" -tocase <name>\n");
- log(" Match attribute names case-insensitively and set it to the specified\n");
- log(" name.\n");
- log("\n");
- log(" -rename <old_name> <new_name>\n");
- log(" Rename attributes as specified\n");
- log("\n");
- log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Map key/value pairs as indicated.\n");
- log("\n");
- log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Like -map, but use case-insensitive match for <old_value> when\n");
- log(" it is a string value.\n");
- log("\n");
- log(" -remove <name>=<value>\n");
- log(" Remove attributes matching this pattern.\n");
+ log_attrmap_paramap_options();
log("\n");
log(" -modattr\n");
log(" Operate on module attributes instead of attributes on wires and cells.\n");
@@ -190,58 +251,9 @@ struct AttrmapPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- std::string arg = args[argidx];
- if (arg == "-tocase" && argidx+1 < args.size()) {
- auto action = new AttrmapTocase;
- action->name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-rename" && argidx+2 < args.size()) {
- auto action = new AttrmapRename;
- action->old_name = args[++argidx];
- action->new_name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ if (parse_attrmap_paramap_options(argidx, args, actions))
continue;
- }
- if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
- string arg1 = args[++argidx];
- string arg2 = args[++argidx];
- string val1, val2;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- p = arg2.find("=");
- if (p != string::npos) {
- val2 = arg2.substr(p+1);
- arg2 = arg2.substr(0, p);
- }
- auto action = new AttrmapMap;
- action->imap = (arg == "-map");
- action->old_name = arg1;
- action->new_name = arg2;
- action->old_value = val1;
- action->new_value = val2;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-remove" && argidx+1 < args.size()) {
- string arg1 = args[++argidx], val1;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- auto action = new AttrmapRemove;
- action->name = arg1;
- action->has_value = (p != string::npos);
- action->value = val1;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-modattr") {
+ if (args[argidx] == "-modattr") {
modattr_mode = true;
continue;
}
@@ -287,4 +299,43 @@ struct AttrmapPass : public Pass {
}
} AttrmapPass;
+struct ParamapPass : public Pass {
+ ParamapPass() : Pass("paramap", "renaming cell parameters") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" paramap [options] [selection]\n");
+ log("\n");
+ log("This command renames cell parameters and/or maps key/value pairs to\n");
+ log("other key/value pairs.\n");
+ log("\n");
+ log_attrmap_paramap_options();
+ log("\n");
+ log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n");
+ log("\n");
+ log(" paramap -tocase INIT t:LUT4\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n");
+
+ vector<std::unique_ptr<AttrmapAction>> actions;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (parse_attrmap_paramap_options(argidx, args, actions))
+ continue;
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells())
+ attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters);
+ }
+} ParamapPass;
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc
new file mode 100644
index 000000000..246932d81
--- /dev/null
+++ b/passes/techmap/clkbufmap.cc
@@ -0,0 +1,298 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 Marcin Koƛcielnicki <mwk@0x04.net>
+ *
+ * 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
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+ size_t pos = port1.find_first_of(':');
+ if (pos != std::string::npos) {
+ port2 = port1.substr(pos+1);
+ port1 = port1.substr(0, pos);
+ }
+}
+
+struct ClkbufmapPass : public Pass {
+ ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" clkbufmap [options] [selection]\n");
+ log("\n");
+ log("Inserts global buffers between nets connected to clock inputs and their drivers.\n");
+ log("\n");
+ log("In the absence of any selection, all wires without the 'clkbuf_inhibit'\n");
+ log("attribute will be considered for global buffer insertion.\n");
+ log("Alternatively, to consider all wires without the 'buffer_type' attribute set to\n");
+ log("'none' or 'bufr' one would specify:\n");
+ log(" 'w:* a:buffer_type=none a:buffer_type=bufr %%u %%d'\n");
+ log("as the selection.\n");
+ log("\n");
+ log(" -buf <celltype> <portname_out>:<portname_in>\n");
+ log(" Specifies the cell type to use for the global buffers\n");
+ log(" and its port names. The first port will be connected to\n");
+ log(" the clock network sinks, and the second will be connected\n");
+ log(" to the actual clock source. This option is required.\n");
+ log("\n");
+ log(" -inpad <celltype> <portname_out>:<portname_in>\n");
+ log(" If specified, a PAD cell of the given type is inserted on\n");
+ log(" clock nets that are also top module's inputs (in addition\n");
+ log(" to the global buffer).\n");
+ log("\n");
+ }
+
+ void module_queue(Design *design, Module *module, std::vector<Module *> &modules_sorted, pool<Module *> &modules_processed) {
+ if (modules_processed.count(module))
+ return;
+ for (auto cell : module->cells()) {
+ Module *submodule = design->module(cell->type);
+ if (!submodule)
+ continue;
+ module_queue(design, submodule, modules_sorted, modules_processed);
+ }
+ modules_sorted.push_back(module);
+ modules_processed.insert(module);
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n");
+
+ std::string buf_celltype, buf_portname, buf_portname2;
+ std::string inpad_celltype, inpad_portname, inpad_portname2;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-buf" && argidx+2 < args.size()) {
+ buf_celltype = args[++argidx];
+ buf_portname = args[++argidx];
+ split_portname_pair(buf_portname, buf_portname2);
+ continue;
+ }
+ if (arg == "-inpad" && argidx+2 < args.size()) {
+ inpad_celltype = args[++argidx];
+ inpad_portname = args[++argidx];
+ split_portname_pair(inpad_portname, inpad_portname2);
+ continue;
+ }
+ break;
+ }
+
+ bool select = false;
+ if (argidx < args.size()) {
+ if (args[argidx].compare(0, 1, "-") != 0)
+ select = true;
+ extra_args(args, argidx, design);
+ }
+
+ if (buf_celltype.empty())
+ log_error("The -buf option is required.\n");
+
+ // Cell type, port name, bit index.
+ pool<pair<IdString, pair<IdString, int>>> sink_ports;
+ pool<pair<IdString, pair<IdString, int>>> buf_ports;
+
+ // Process submodules before module using them.
+ std::vector<Module *> modules_sorted;
+ pool<Module *> modules_processed;
+ for (auto module : design->selected_modules())
+ module_queue(design, module, modules_sorted, modules_processed);
+
+ for (auto module : modules_sorted)
+ {
+ if (module->get_blackbox_attribute()) {
+ for (auto port : module->ports) {
+ auto wire = module->wire(port);
+ if (wire->get_bool_attribute("\\clkbuf_driver"))
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ if (wire->get_bool_attribute("\\clkbuf_sink"))
+ for (int i = 0; i < GetSize(wire); i++)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ continue;
+ }
+ pool<SigBit> sink_wire_bits;
+ pool<SigBit> buf_wire_bits;
+ pool<SigBit> driven_wire_bits;
+ SigMap sigmap(module);
+ // bit -> (buffer, buffer's input)
+ dict<SigBit, pair<Cell *, Wire *>> buffered_bits;
+
+ // First, collect nets that could use a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (sink_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ sink_wire_bits.insert(sigmap(port.second[i]));
+
+ // Second, collect ones that already have a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ buf_wire_bits.insert(sigmap(port.second[i]));
+
+ // Collect all driven bits.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ if (cell->output(port.first))
+ for (int i = 0; i < port.second.size(); i++)
+ driven_wire_bits.insert(port.second[i]);
+
+ // Insert buffers.
+ std::vector<pair<Wire *, Wire *>> input_queue;
+ // Copy current wire list, as we will be adding new ones during iteration.
+ std::vector<Wire *> wires(module->wires());
+ for (auto wire : wires)
+ {
+ // Should not happen.
+ if (wire->port_input && wire->port_output)
+ continue;
+ bool process_wire = module->selected(wire);
+ if (!select && wire->get_bool_attribute("\\clkbuf_inhibit"))
+ process_wire = false;
+ if (!process_wire) {
+ // This wire is supposed to be bypassed, so make sure we don't buffer it in
+ // some buffer higher up in the hierarchy.
+ if (wire->port_output)
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ continue;
+ }
+
+ pool<int> input_bits;
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buf_wire_bits.count(mapped_wire_bit)) {
+ // Already buffered downstream. If this is an output, mark it.
+ if (wire->port_output)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ } else if (!sink_wire_bits.count(mapped_wire_bit)) {
+ // Nothing to do.
+ } else if (driven_wire_bits.count(wire_bit) || (wire->port_input && module->get_bool_attribute("\\top"))) {
+ // Clock network not yet buffered, driven by one of
+ // our cells or a top-level input -- buffer it.
+
+ log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype));
+ Wire *iwire = module->addWire(NEW_ID);
+ cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit);
+ cell->setPort(RTLIL::escape_id(buf_portname2), iwire);
+ if (wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute("\\top")) {
+ log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype));
+ cell2->setPort(RTLIL::escape_id(inpad_portname), iwire);
+ iwire = module->addWire(NEW_ID);
+ cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire);
+ }
+ buffered_bits[mapped_wire_bit] = make_pair(cell, iwire);
+
+ if (wire->port_input) {
+ input_bits.insert(i);
+ }
+ } else if (wire->port_input) {
+ // A clock input in a submodule -- mark it, let higher level
+ // worry about it.
+ if (wire->port_input)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+ if (!input_bits.empty()) {
+ // This is an input port and some buffers were inserted -- we need
+ // to create a new input wire and transfer attributes.
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+
+ for (int i = 0; i < wire->width; i++) {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ auto it = buffered_bits.find(mapped_wire_bit);
+ if (it != buffered_bits.end()) {
+
+ module->connect(it->second.second, SigSpec(new_wire, i));
+ } else {
+ module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ }
+ }
+ input_queue.push_back(make_pair(wire, new_wire));
+ }
+ }
+
+ // Mark any newly-buffered output ports as such.
+ for (auto wire : module->selected_wires()) {
+ if (wire->port_input || !wire->port_output)
+ continue;
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buffered_bits.count(mapped_wire_bit))
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+
+ // Reconnect the drivers to buffer inputs.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections()) {
+ if (!cell->output(port.first))
+ continue;
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig) {
+ const auto it = buffered_bits.find(sigmap(bit));
+ if (it == buffered_bits.end())
+ continue;
+ // Avoid substituting buffer's own output pin.
+ if (cell == it->second.first)
+ continue;
+ bit = it->second.second;
+ newsig = true;
+ }
+ if (newsig)
+ cell->setPort(port.first, sig);
+ }
+
+ // This has to be done last, to avoid upsetting sigmap before the port reconnections.
+ for (auto &it : input_queue) {
+ Wire *wire = it.first;
+ Wire *new_wire = it.second;
+ module->swap_names(new_wire, wire);
+ wire->attributes.clear();
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ module->fixup_ports();
+ }
+ }
+} ClkbufmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
index e766f2cf6..0242256e5 100644
--- a/passes/techmap/dff2dffe.cc
+++ b/passes/techmap/dff2dffe.cc
@@ -265,7 +265,7 @@ struct Dff2dffePass : public Pass {
log("\n");
log(" -unmap\n");
log(" operate in the opposite direction: replace $dffe cells with combinations\n");
- log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n");
+ log(" of $dff and $mux cells. the options below are ignored in unmap mode.\n");
log("\n");
log(" -unmap-mince N\n");
log(" Same as -unmap but only unmap $dffe where the clock enable port\n");
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
index a2551316f..c868b9a87 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -64,6 +64,11 @@ struct IopadmapPass : public Pass {
log(" of the tristate driver and the 2nd portname is the internal output\n");
log(" buffering the external signal.\n");
log("\n");
+ log(" -ignore <celltype> <portname>[:<portname>]*\n");
+ log(" Skips mapping inputs/outputs that are already connected to given\n");
+ log(" ports of the given cell. Can be used multiple times. This is in\n");
+ log(" addition to the cells specified as mapping targets.\n");
+ log("\n");
log(" -widthparam <param_name>\n");
log(" Use the specified parameter name to set the port width.\n");
log("\n");
@@ -88,6 +93,7 @@ struct IopadmapPass : public Pass {
std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
std::string widthparam, nameparam;
+ pool<pair<IdString, IdString>> ignore;
bool flag_bits = false;
size_t argidx;
@@ -127,6 +133,18 @@ struct IopadmapPass : public Pass {
split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
continue;
}
+ if (arg == "-ignore" && argidx+2 < args.size()) {
+ std::string ignore_celltype = args[++argidx];
+ std::string ignore_portname = args[++argidx];
+ std::string ignore_portname2;
+ while (!ignore_portname.empty()) {
+ split_portname_pair(ignore_portname, ignore_portname2);
+ ignore.insert(make_pair(RTLIL::escape_id(ignore_celltype), RTLIL::escape_id(ignore_portname)));
+
+ ignore_portname = ignore_portname2;
+ }
+ continue;
+ }
if (arg == "-widthparam" && argidx+1 < args.size()) {
widthparam = args[++argidx];
continue;
@@ -143,6 +161,23 @@ struct IopadmapPass : public Pass {
}
extra_args(args, argidx, design);
+ if (!inpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2)));
+ if (!outpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2)));
+ if (!inoutpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2)));
+ if (!toutpad_portname3.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3)));
+ if (!tinoutpad_portname4.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4)));
+
+ for (auto module : design->modules())
+ if (module->get_blackbox_attribute())
+ for (auto wire : module->wires())
+ if (wire->get_bool_attribute("\\iopad_external_pin"))
+ ignore.insert(make_pair(module->name, wire->name));
+
for (auto module : design->selected_modules())
{
dict<IdString, pool<int>> skip_wires;
@@ -150,28 +185,11 @@ struct IopadmapPass : public Pass {
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))))
+ for (auto port : cell->connections())
+ if (ignore.count(make_pair(cell->type, port.first)))
+ for (auto bit : sigmap(port.second))
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())
{
dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index 5e298d8dd..be00e5030 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ShregmapTech
{
virtual ~ShregmapTech() { }
- virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
- virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
+ virtual bool analyze(vector<int> &taps) = 0;
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
@@ -56,7 +54,7 @@ struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
+ bool analyze(vector<int> &taps)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
@@ -93,155 +91,6 @@ struct ShregmapTechGreenpak4 : ShregmapTech
}
};
-struct ShregmapTechXilinx7 : ShregmapTech
-{
- dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
- const ShregmapOptions &opts;
-
- ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
-
- virtual void init(const Module* module, const SigMap &sigmap) override
- {
- for (const auto &i : module->cells_) {
- auto cell = i.second;
- if (cell->type == ID($shiftx)) {
- if (cell->getParam(ID(Y_WIDTH)) != 1) continue;
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
- log_assert(j == cell->getParam(ID(A_WIDTH)).as_int());
- }
- else if (cell->type == ID($mux)) {
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
- j = 0;
- for (auto bit : sigmap(cell->getPort(ID::B)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
- }
- }
- }
-
- virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
- {
- auto it = sigbit_to_shiftx_offset.find(bit);
- if (it == sigbit_to_shiftx_offset.end())
- return;
- if (cell) {
- if (cell->type == ID($shiftx) && port == ID::A)
- return;
- if (cell->type == ID($mux) && port.in(ID::A, ID::B))
- return;
- }
- sigbit_to_shiftx_offset.erase(it);
- }
-
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
- {
- if (GetSize(taps) == 1)
- return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
-
- if (taps.back() < opts.minlen-1)
- return false;
-
- Cell *shiftx = nullptr;
- int group = 0;
- for (int i = 0; i < GetSize(taps); ++i) {
- auto it = sigbit_to_shiftx_offset.find(qbits[i]);
- if (it == sigbit_to_shiftx_offset.end())
- return false;
-
- // Check taps are sequential
- if (i != taps[i])
- return false;
- // Check taps are not connected to a shift register,
- // or sequential to the same shift register
- if (i == 0) {
- int offset;
- std::tie(shiftx,offset,group) = it->second;
- if (offset != i)
- return false;
- }
- else {
- Cell *shiftx_ = std::get<0>(it->second);
- if (shiftx_ != shiftx)
- return false;
- int offset = std::get<1>(it->second);
- if (offset != i)
- return false;
- int group_ = std::get<2>(it->second);
- if (group_ != group)
- return false;
- }
- }
- log_assert(shiftx);
-
- // Only map if $shiftx exclusively covers the shift register
- if (shiftx->type == ID($shiftx)) {
- if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- // Due to padding the most significant bits of A may be 1'bx,
- // and if so, discount them
- if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) {
- const SigSpec A = shiftx->getPort(ID::A);
- const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int();
- for (int i = GetSize(taps); i < A_width; ++i)
- if (A[i] != RTLIL::Sx) return false;
- }
- else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- }
- else if (shiftx->type == ID($mux)) {
- if (GetSize(taps) != 2)
- return false;
- }
- else log_abort();
-
- return true;
- }
-
- virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
- {
- const auto &tap = *taps.begin();
- auto bit = tap.second;
-
- auto it = sigbit_to_shiftx_offset.find(bit);
- log_assert(it != sigbit_to_shiftx_offset.end());
-
- auto newcell = cell->module->addCell(NEW_ID, ID($__XILINX_SHREG_));
- newcell->set_src_attribute(cell->get_src_attribute());
- newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH)));
- newcell->setParam(ID(INIT), cell->getParam(ID(INIT)));
- newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL)));
- newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL)));
-
- newcell->setPort(ID(C), cell->getPort(ID(C)));
- newcell->setPort(ID(D), cell->getPort(ID(D)));
- if (cell->hasPort(ID(E)))
- newcell->setPort(ID(E), cell->getPort(ID(E)));
-
- Cell* shiftx = std::get<0>(it->second);
- RTLIL::SigSpec l_wire, q_wire;
- if (shiftx->type == ID($shiftx)) {
- l_wire = shiftx->getPort(ID::B);
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else if (shiftx->type == ID($mux)) {
- l_wire = shiftx->getPort(ID(S));
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else log_abort();
-
- newcell->setPort(ID(Q), q_wire);
- newcell->setPort(ID(L), l_wire);
-
- return false;
- }
-};
-
-
struct ShregmapWorker
{
Module *module;
@@ -264,10 +113,8 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
- for (auto bit : sigmap(wire)) {
+ for (auto bit : sigmap(wire))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
- }
}
if (wire->attributes.count(ID(init))) {
@@ -317,10 +164,8 @@ struct ShregmapWorker
for (auto conn : cell->connections())
if (cell->input(conn.first))
- for (auto bit : sigmap(conn.second)) {
+ for (auto bit : sigmap(conn.second))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
- }
}
}
@@ -346,7 +191,7 @@ struct ShregmapWorker
IdString q_port = opts.ffcells.at(c1->type).second;
auto c1_conn = c1->connections();
- auto c2_conn = c1->connections();
+ auto c2_conn = c2->connections();
c1_conn.erase(d_port);
c1_conn.erase(q_port);
@@ -425,7 +270,7 @@ struct ShregmapWorker
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps, qbits))
+ if (opts.tech->analyze(taps))
break;
taps.pop_back();
@@ -544,9 +389,6 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
- if (opts.tech)
- opts.tech->init(module, sigmap);
-
make_sigbit_chain_next_prev();
find_chain_start_cells();
@@ -617,11 +459,6 @@ struct ShregmapPass : public Pass {
log("\n");
log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\n");
- log(" this option also implies -clkpol pos -zinit\n");
- log("\n");
- log(" -tech xilinx\n");
- log(" map to xilinx dynamic-length shift registers.\n");
- log(" this option also implies -params -init\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -676,12 +513,6 @@ struct ShregmapPass : public Pass {
clkpol = "pos";
opts.zinit = true;
opts.tech = new ShregmapTechGreenpak4;
- }
- else if (tech == "xilinx") {
- opts.init = true;
- opts.params = true;
- enpol = "any_or_none";
- opts.tech = new ShregmapTechXilinx7(opts);
} else {
argidx--;
break;
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index b271c8781..c4496f76f 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -943,7 +943,8 @@ struct TechmapPass : public Pass {
log(" instead of inlining them.\n");
log("\n");
log(" -max_iter <number>\n");
- log(" only run the specified number of iterations.\n");
+ log(" only run the specified number of iterations on each module.\n");
+ log(" default: unlimited\n");
log("\n");
log(" -recursive\n");
log(" instead of the iterative breadth-first algorithm use a recursive\n");
@@ -1157,15 +1158,16 @@ struct TechmapPass : public Pass {
RTLIL::Module *module = *worker.module_queue.begin();
worker.module_queue.erase(module);
+ int module_max_iter = max_iter;
bool did_something = true;
std::set<RTLIL::Cell*> handled_cells;
while (did_something) {
did_something = false;
- if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
- did_something = true;
+ if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
+ did_something = true;
if (did_something)
module->check();
- if (max_iter > 0 && --max_iter == 0)
+ if (module_max_iter > 0 && --module_max_iter == 0)
break;
}
}