2021-12-30 21:18:34 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
|
|
|
* Copyright (C) 2021 gatecat <gatecat@ds0.me>
|
|
|
|
*
|
|
|
|
* 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 "viaduct_helpers.h"
|
|
|
|
#include "design_utils.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "nextpnr.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
2022-05-02 16:56:36 +08:00
|
|
|
void ViaductHelpers::resize_ids(int x, int y, int z)
|
2021-12-30 21:18:34 +08:00
|
|
|
{
|
2022-05-02 16:56:36 +08:00
|
|
|
NPNR_ASSERT(x >= 0 && y >= 0 && x <= 20000 && y <= 20000 && z <= 1000);
|
2021-12-30 21:18:34 +08:00
|
|
|
while (int(x_ids.size()) <= x) {
|
2022-08-10 17:57:17 +08:00
|
|
|
IdString next = ctx->idf("X%d", int(x_ids.size()));
|
2021-12-30 21:18:34 +08:00
|
|
|
x_ids.push_back(next);
|
|
|
|
}
|
|
|
|
while (int(y_ids.size()) <= y) {
|
2022-08-10 17:57:17 +08:00
|
|
|
IdString next = ctx->idf("Y%d", int(y_ids.size()));
|
2021-12-30 21:18:34 +08:00
|
|
|
y_ids.push_back(next);
|
|
|
|
}
|
2022-05-02 16:56:36 +08:00
|
|
|
while (int(z_ids.size()) <= y) {
|
2022-08-10 17:57:17 +08:00
|
|
|
IdString next = ctx->idf("Z%d", int(z_ids.size()));
|
2022-05-02 16:56:36 +08:00
|
|
|
z_ids.push_back(next);
|
|
|
|
}
|
2021-12-30 21:18:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
IdStringList ViaductHelpers::xy_id(int x, int y, IdString base)
|
|
|
|
{
|
|
|
|
resize_ids(x, y);
|
|
|
|
std::array<IdString, 3> result{x_ids.at(x), y_ids.at(y), base};
|
|
|
|
return IdStringList(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
IdStringList ViaductHelpers::xy_id(int x, int y, IdStringList base)
|
|
|
|
{
|
|
|
|
resize_ids(x, y);
|
|
|
|
std::array<IdString, 2> prefix{x_ids.at(x), y_ids.at(y)};
|
|
|
|
return IdStringList::concat(IdStringList(prefix), base);
|
|
|
|
}
|
|
|
|
|
2022-05-02 16:56:36 +08:00
|
|
|
IdStringList ViaductHelpers::xyz_id(int x, int y, int z, IdString base)
|
|
|
|
{
|
|
|
|
resize_ids(x, y, z);
|
|
|
|
std::array<IdString, 4> result{x_ids.at(x), y_ids.at(y), z_ids.at(z), base};
|
|
|
|
return IdStringList(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
IdStringList ViaductHelpers::xyz_id(int x, int y, int z, IdStringList base)
|
|
|
|
{
|
|
|
|
resize_ids(x, y, z);
|
|
|
|
std::array<IdString, 3> prefix{x_ids.at(x), y_ids.at(y), z_ids.at(z)};
|
|
|
|
return IdStringList::concat(IdStringList(prefix), base);
|
|
|
|
}
|
|
|
|
|
2021-12-30 21:18:34 +08:00
|
|
|
void ViaductHelpers::remove_nextpnr_iobs(const pool<CellTypePort> &top_ports)
|
|
|
|
{
|
|
|
|
std::vector<IdString> to_remove;
|
|
|
|
for (auto &cell : ctx->cells) {
|
|
|
|
auto &ci = *cell.second;
|
|
|
|
if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf")))
|
|
|
|
continue;
|
2022-02-18 18:52:37 +08:00
|
|
|
NetInfo *i = ci.getPort(ctx->id("I"));
|
2021-12-30 21:18:34 +08:00
|
|
|
if (i && i->driver.cell) {
|
|
|
|
if (!top_ports.count(CellTypePort(i->driver)))
|
|
|
|
log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci),
|
|
|
|
ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port));
|
|
|
|
}
|
2022-02-18 18:52:37 +08:00
|
|
|
NetInfo *o = ci.getPort(ctx->id("O"));
|
2021-12-30 21:18:34 +08:00
|
|
|
if (o) {
|
|
|
|
for (auto &usr : o->users) {
|
|
|
|
if (!top_ports.count(CellTypePort(usr)))
|
|
|
|
log_error("Top-level port '%s' driving illegal port %s.%s\n", ctx->nameOf(&ci),
|
|
|
|
ctx->nameOf(usr.cell), ctx->nameOf(usr.port));
|
|
|
|
}
|
|
|
|
}
|
2022-02-18 18:52:37 +08:00
|
|
|
ci.disconnectPort(ctx->id("I"));
|
|
|
|
ci.disconnectPort(ctx->id("O"));
|
2021-12-30 21:18:34 +08:00
|
|
|
to_remove.push_back(ci.name);
|
|
|
|
}
|
|
|
|
for (IdString cell_name : to_remove)
|
|
|
|
ctx->cells.erase(cell_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ViaductHelpers::constrain_cell_pairs(const pool<CellTypePort> &src_ports, const pool<CellTypePort> &sink_ports,
|
2022-02-05 04:49:32 +08:00
|
|
|
int delta_z, bool allow_fanout)
|
2021-12-30 21:18:34 +08:00
|
|
|
{
|
|
|
|
int constrained = 0;
|
|
|
|
for (auto &cell : ctx->cells) {
|
|
|
|
auto &ci = *cell.second;
|
|
|
|
if (ci.cluster != ClusterId())
|
|
|
|
continue; // don't constrain already-constrained cells
|
|
|
|
bool done = false;
|
|
|
|
for (auto &port : ci.ports) {
|
|
|
|
// look for starting source ports
|
|
|
|
if (port.second.type != PORT_OUT || !port.second.net)
|
|
|
|
continue;
|
|
|
|
if (!src_ports.count(CellTypePort(ci.type, port.first)))
|
|
|
|
continue;
|
2022-02-26 23:17:46 +08:00
|
|
|
if (!allow_fanout && port.second.net->users.entries() > 1)
|
2022-02-05 04:49:32 +08:00
|
|
|
continue;
|
2021-12-30 21:18:34 +08:00
|
|
|
for (auto &usr : port.second.net->users) {
|
|
|
|
if (!sink_ports.count(CellTypePort(usr)))
|
|
|
|
continue;
|
|
|
|
if (usr.cell->cluster != ClusterId())
|
|
|
|
continue;
|
|
|
|
// Add the constraint
|
|
|
|
ci.cluster = ci.name;
|
|
|
|
ci.constr_abs_z = false;
|
|
|
|
ci.constr_children.push_back(usr.cell);
|
|
|
|
usr.cell->cluster = ci.name;
|
|
|
|
usr.cell->constr_x = 0;
|
|
|
|
usr.cell->constr_y = 0;
|
|
|
|
usr.cell->constr_z = delta_z;
|
|
|
|
usr.cell->constr_abs_z = false;
|
|
|
|
++constrained;
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return constrained;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViaductHelpers::replace_constants(CellTypePort vcc_driver, CellTypePort gnd_driver,
|
|
|
|
const dict<IdString, Property> &vcc_params,
|
|
|
|
const dict<IdString, Property> &gnd_params)
|
|
|
|
{
|
|
|
|
CellInfo *vcc_drv = ctx->createCell(ctx->id("$PACKER_VCC_DRV"), vcc_driver.cell_type);
|
|
|
|
vcc_drv->addOutput(vcc_driver.port);
|
|
|
|
for (auto &p : vcc_params)
|
|
|
|
vcc_drv->params[p.first] = p.second;
|
|
|
|
|
|
|
|
CellInfo *gnd_drv = ctx->createCell(ctx->id("$PACKER_GND_DRV"), gnd_driver.cell_type);
|
|
|
|
gnd_drv->addOutput(gnd_driver.port);
|
|
|
|
for (auto &p : gnd_params)
|
|
|
|
gnd_drv->params[p.first] = p.second;
|
|
|
|
|
|
|
|
NetInfo *vcc_net = ctx->createNet(ctx->id("$PACKER_VCC"));
|
|
|
|
NetInfo *gnd_net = ctx->createNet(ctx->id("$PACKER_GND"));
|
|
|
|
|
2022-12-06 17:04:59 +08:00
|
|
|
vcc_drv->connectPort(vcc_driver.port, vcc_net);
|
|
|
|
gnd_drv->connectPort(gnd_driver.port, gnd_net);
|
|
|
|
|
2021-12-30 21:18:34 +08:00
|
|
|
std::vector<IdString> trim_cells;
|
|
|
|
std::vector<IdString> trim_nets;
|
|
|
|
for (auto &net : ctx->nets) {
|
|
|
|
auto &ni = *net.second;
|
|
|
|
if (!ni.driver.cell)
|
|
|
|
continue;
|
|
|
|
if (ni.driver.cell->type != ctx->id("GND") && ni.driver.cell->type != ctx->id("VCC"))
|
|
|
|
continue;
|
|
|
|
NetInfo *replace = (ni.driver.cell->type == ctx->id("VCC")) ? vcc_net : gnd_net;
|
|
|
|
for (auto &usr : ni.users) {
|
|
|
|
usr.cell->ports.at(usr.port).net = replace;
|
2022-02-26 23:17:46 +08:00
|
|
|
usr.cell->ports.at(usr.port).user_idx = replace->users.add(usr);
|
2021-12-30 21:18:34 +08:00
|
|
|
}
|
|
|
|
trim_cells.push_back(ni.driver.cell->name);
|
|
|
|
trim_nets.push_back(ni.name);
|
|
|
|
}
|
|
|
|
for (IdString cell_name : trim_cells)
|
|
|
|
ctx->cells.erase(cell_name);
|
|
|
|
for (IdString net_name : trim_nets)
|
|
|
|
ctx->nets.erase(net_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_END
|