/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * Copyright (C) 2019 Marcelina Koƛcielnicka * * 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 clock buffers on clock networks") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" clkbufmap [options] [selection]\n"); log("\n"); log("Inserts clock 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 clock 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 :\n"); log(" Specifies the cell type to use for the clock 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.\n"); log("\n"); log(" -inpad :\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 clock buffer, if any).\n"); log("\n"); log("At least one of -buf or -inpad should be specified.\n"); } void module_queue(Design *design, Module *module, std::vector &modules_sorted, pool &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 args, RTLIL::Design *design) override { log_header(design, "Executing CLKBUFMAP pass (inserting 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() && inpad_celltype.empty()) log_error("Either the -buf option or -inpad option is required.\n"); // Cell type, port name, bit index. pool>> sink_ports; pool>> buf_ports; dict>, pair> inv_ports_out; dict>, pair> inv_ports_in; // If true, use both ther -buf and -inpad cell for input ports that are clocks. bool buffer_inputs = true; Module *inpad_mod = design->module(RTLIL::escape_id(inpad_celltype)); if (inpad_mod) { Wire *buf_wire = inpad_mod->wire(RTLIL::escape_id(buf_portname)); if (buf_wire && buf_wire->get_bool_attribute(ID::clkbuf_driver)) buffer_inputs = false; } // Process submodules before module using them. std::vector modules_sorted; pool 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(ID::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(ID::clkbuf_sink)) for (int i = 0; i < GetSize(wire); i++) sink_ports.insert(make_pair(module->name, make_pair(wire->name, i))); auto it = wire->attributes.find(ID::clkbuf_inv); if (it != wire->attributes.end()) { IdString in_name = RTLIL::escape_id(it->second.decode_string()); for (int i = 0; i < GetSize(wire); i++) { inv_ports_out[make_pair(module->name, make_pair(wire->name, i))] = make_pair(in_name, i); inv_ports_in[make_pair(module->name, make_pair(in_name, i))] = make_pair(wire->name, i); } } } continue; } pool sink_wire_bits; pool buf_wire_bits; pool driven_wire_bits; SigMap sigmap(module); // bit -> (buffer, buffer's input) dict> 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])); // Third, propagate tags through inverters. bool retry = true; while (retry) { retry = false; for (auto cell : module->cells()) for (auto port : cell->connections()) for (int i = 0; i < port.second.size(); i++) { auto it = inv_ports_out.find(make_pair(cell->type, make_pair(port.first, i))); auto bit = sigmap(port.second[i]); // If output of an inverter is connected to a sink, mark it as buffered, // and request a buffer on the inverter's input instead. if (it != inv_ports_out.end() && !buf_wire_bits.count(bit) && sink_wire_bits.count(bit)) { buf_wire_bits.insert(bit); auto other_bit = sigmap(cell->getPort(it->second.first)[it->second.second]); sink_wire_bits.insert(other_bit); retry = true; } // If input of an inverter is marked as already-buffered, // mark its output already-buffered as well. auto it2 = inv_ports_in.find(make_pair(cell->type, make_pair(port.first, i))); if (it2 != inv_ports_in.end() && buf_wire_bits.count(bit)) { auto other_bit = sigmap(cell->getPort(it2->second.first)[it2->second.second]); if (!buf_wire_bits.count(other_bit)) { buf_wire_bits.insert(other_bit); retry = true; } } } }; // 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> input_queue; // Copy current wire list, as we will be adding new ones during iteration. std::vector 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(ID::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 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(ID::top))) { // Clock network not yet buffered, driven by one of // our cells or a top-level input -- buffer it. Wire *iwire = nullptr; RTLIL::Cell *cell = nullptr; bool is_input = wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute(ID::top); if (!buf_celltype.empty() && (!is_input || buffer_inputs)) { log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i); cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype)); iwire = module->addWire(NEW_ID); cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit); cell->setPort(RTLIL::escape_id(buf_portname2), iwire); } if (is_input) { 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)); if (iwire) { cell2->setPort(RTLIL::escape_id(inpad_portname), iwire); } else { cell2->setPort(RTLIL::escape_id(inpad_portname), mapped_wire_bit); cell = cell2; } iwire = module->addWire(NEW_ID); cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire); } if (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