diff options
Diffstat (limited to 'passes/techmap/clkbufmap.cc')
-rw-r--r-- | passes/techmap/clkbufmap.cc | 299 |
1 files changed, 299 insertions, 0 deletions
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 |