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.
2019-08-12 10:57:43 -05:00
|
|
|
/*
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
2019-08-12 19:35:54 -05:00
|
|
|
* Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net>
|
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.
2019-08-12 10:57:43 -05:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
{
|
|
|
|
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()) {
|
2019-08-23 12:03:41 -05:00
|
|
|
for (auto port : module->ports) {
|
|
|
|
auto wire = module->wire(port);
|
2019-08-13 14:36:59 -05:00
|
|
|
if (wire->get_bool_attribute("\\clkbuf_driver"))
|
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.
2019-08-12 10:57:43 -05:00
|
|
|
for (int i = 0; i < GetSize(wire); i++)
|
2019-08-13 14:36:59 -05:00
|
|
|
buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
|
|
|
if (wire->get_bool_attribute("\\clkbuf_sink"))
|
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.
2019-08-12 10:57:43 -05:00
|
|
|
for (int i = 0; i < GetSize(wire); i++)
|
2019-08-13 14:36:59 -05:00
|
|
|
sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
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.
2019-08-12 10:57:43 -05:00
|
|
|
}
|
|
|
|
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
|