aboutsummaryrefslogtreecommitdiffstats
path: root/passes/techmap
diff options
context:
space:
mode:
authorMarcin Koƛcielnicki <koriakin@0x04.net>2019-08-12 15:57:43 +0000
committerMarcin Koƛcielnicki <koriakin@0x04.net>2019-08-13 00:16:38 +0200
commitf4c62f33ac56bc5725c44ad822e75d2387f98061 (patch)
tree4a1659237670042c8709777d660b14be57cb004f /passes/techmap
parent78b30bbb1102047585d1a2eac89b1c7f5ca7344e (diff)
downloadyosys-f4c62f33ac56bc5725c44ad822e75d2387f98061.tar.gz
yosys-f4c62f33ac56bc5725c44ad822e75d2387f98061.tar.bz2
yosys-f4c62f33ac56bc5725c44ad822e75d2387f98061.zip
Add clock buffer insertion pass, improve iopadmap.
A few new attributes are defined for use in cell libraries: - iopad_external_pin: marks PAD cell's external-facing pin. Pad insertion will be skipped for ports that are already connected to such a pin. - clkbuf_sink: marks an input pin as a clock pin, requesting clock buffer insertion. - clkbuf_driver: marks an output pin as a clock buffer output pin. Clock buffer insertion will be skipped for nets that are already driven by such a pin. All three are module attributes that should be set to a comma-separeted list of pin names. Clock buffer insertion itself works as follows: 1. All cell ports, starting from bottom up, can be marked as clock sinks (requesting clock buffer insertion) or as clock buffer outputs. 2. If a wire in a given module is driven by a cell port that is a clock buffer output, it is in turn also considered a clock buffer output. 3. If an input port in a non-top module is connected to a clock sink in a contained cell, it is also in turn considered a clock sink. 4. If a wire in a module is driven by a non-clock-buffer cell, and is also connected to a clock sink port in a contained cell, a clock buffer is inserted in this module. 5. For the top module, a clock buffer is also inserted on input ports connected to clock sinks, optionally with a special kind of input PAD (such as IBUFG for Xilinx). 6. Clock buffer insertion on a given wire is skipped if the clkbuf_inhibit attribute is set on it.
Diffstat (limited to 'passes/techmap')
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/clkbufmap.cc299
-rw-r--r--passes/techmap/iopadmap.cc76
3 files changed, 356 insertions, 20 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/clkbufmap.cc b/passes/techmap/clkbufmap.cc
new file mode 100644
index 000000000..9ecc83071
--- /dev/null
+++ b/passes/techmap/clkbufmap.cc
@@ -0,0 +1,299 @@
+/*
+ * 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
+
+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);
+ }
+}
+
+std::vector<std::string> split(std::string text, const char *delim)
+{
+ std::vector<std::string> list;
+ char *p = strdup(text.c_str());
+ char *t = strtok(p, delim);
+ while (t != NULL) {
+ list.push_back(t);
+ t = strtok(NULL, delim);
+ }
+ free(p);
+ return list;
+}
+
+struct ClkbufmapPass : public Pass {
+ ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { }
+ void help() YS_OVERRIDE
+ {
+ log("\n");
+ log(" clkbufmap [options] [selection]\n");
+ log("\n");
+ log("Inserts global buffers between nets connected to clock inputs and their\n");
+ log("drivers.\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;
+ }
+ extra_args(args, argidx, design);
+
+ if (buf_celltype.empty())
+ log_error("The -buf option is required.");
+
+ // 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()) {
+ auto it = module->attributes.find("\\clkbuf_driver");
+ if (it != module->attributes.end()) {
+ auto value = it->second.decode_string();
+ for (auto name : split(value, ",")) {
+ auto wire = module->wire(RTLIL::escape_id(name));
+ if (!wire)
+ log_error("Module %s does not have port %s.\n", log_id(module), log_id(name));
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(RTLIL::escape_id(name), i)));
+ }
+ }
+ it = module->attributes.find("\\clkbuf_sink");
+ if (it != module->attributes.end()) {
+ auto value = it->second.decode_string();
+ for (auto name : split(value, ",")) {
+ auto wire = module->wire(RTLIL::escape_id(name));
+ if (!wire)
+ log_error("Module %s does not have port %s.\n", log_id(module), log_id(name));
+ for (int i = 0; i < GetSize(wire); i++)
+ sink_ports.insert(make_pair(module->name, make_pair(RTLIL::escape_id(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;
+ for (auto wire : module->selected_wires())
+ {
+ // Should not happen.
+ if (wire->port_input && wire->port_output)
+ continue;
+ if (wire->get_bool_attribute("\\clkbuf_inhibit"))
+ 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/iopadmap.cc b/passes/techmap/iopadmap.cc
index efcc082d5..e3d68ab0c 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -32,6 +32,19 @@ void split_portname_pair(std::string &port1, std::string &port2)
}
}
+std::vector<std::string> split(std::string text, const char *delim)
+{
+ std::vector<std::string> list;
+ char *p = strdup(text.c_str());
+ char *t = strtok(p, delim);
+ while (t != NULL) {
+ list.push_back(t);
+ t = strtok(NULL, delim);
+ }
+ free(p);
+ return list;
+}
+
struct IopadmapPass : public Pass {
IopadmapPass() : Pass("iopadmap", "technology mapping of i/o pads (or buffers)") { }
void help() YS_OVERRIDE
@@ -64,6 +77,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 +106,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 +146,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 +174,28 @@ 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())
+ {
+ auto it = module->attributes.find("\\iopad_external_pin");
+ if (it != module->attributes.end()) {
+ auto value = it->second.decode_string();
+ for (auto name : split(value, ",")) {
+ ignore.insert(make_pair(module->name, RTLIL::escape_id(name)));
+ }
+ }
+ }
+
for (auto module : design->selected_modules())
{
dict<IdString, pool<int>> skip_wires;
@@ -150,28 +203,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))))
- 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))))
+ 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(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;