nextpnr/common/nextpnr_types.cc
gatecat 86699b42f6 Switch to potentially-sparse net users array
This uses a new data structure for net.users that allows gaps, so
removing a port from a net is no longer an O(n) operation on the number
of users the net has.

Signed-off-by: gatecat <gatecat@ds0.me>
2022-02-27 13:47:05 +00:00

181 lines
5.9 KiB
C++

/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
*
* 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 "nextpnr_types.h"
#include "context.h"
#include "log.h"
#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
void CellInfo::addInput(IdString name)
{
ports[name].name = name;
ports[name].type = PORT_IN;
}
void CellInfo::addOutput(IdString name)
{
ports[name].name = name;
ports[name].type = PORT_OUT;
}
void CellInfo::addInout(IdString name)
{
ports[name].name = name;
ports[name].type = PORT_INOUT;
}
void CellInfo::setParam(IdString name, Property value) { params[name] = value; }
void CellInfo::unsetParam(IdString name) { params.erase(name); }
void CellInfo::setAttr(IdString name, Property value) { attrs[name] = value; }
void CellInfo::unsetAttr(IdString name) { attrs.erase(name); }
bool CellInfo::testRegion(BelId bel) const
{
return region == nullptr || !region->constr_bels || region->bels.count(bel);
}
void CellInfo::connectPort(IdString port_name, NetInfo *net)
{
if (net == nullptr)
return;
PortInfo &port = ports.at(port_name);
NPNR_ASSERT(port.net == nullptr);
port.net = net;
if (port.type == PORT_OUT) {
NPNR_ASSERT(net->driver.cell == nullptr);
net->driver.cell = this;
net->driver.port = port_name;
} else if (port.type == PORT_IN || port.type == PORT_INOUT) {
PortRef user;
user.cell = this;
user.port = port_name;
port.user_idx = net->users.add(user);
} else {
NPNR_ASSERT_FALSE("invalid port type for connect_port");
}
}
void CellInfo::disconnectPort(IdString port_name)
{
if (!ports.count(port_name))
return;
PortInfo &port = ports.at(port_name);
if (port.net != nullptr) {
if (port.user_idx)
port.net->users.remove(port.user_idx);
if (port.net->driver.cell == this && port.net->driver.port == port_name)
port.net->driver.cell = nullptr;
port.net = nullptr;
}
}
void CellInfo::connectPorts(IdString port, CellInfo *other, IdString other_port)
{
PortInfo &port1 = ports.at(port);
if (port1.net == nullptr) {
// No net on port1; need to create one
NetInfo *p1net = ctx->createNet(ctx->id(name.str(ctx) + "$conn$" + port.str(ctx)));
connectPort(port, p1net);
}
other->connectPort(other_port, port1.net);
}
void CellInfo::movePortTo(IdString port, CellInfo *other, IdString other_port)
{
if (!ports.count(port))
return;
PortInfo &old = ports.at(port);
// Create port on the replacement cell if it doesn't already exist
if (!other->ports.count(other_port)) {
other->ports[other_port].name = other_port;
other->ports[other_port].type = old.type;
}
PortInfo &rep = other->ports.at(other_port);
NPNR_ASSERT(old.type == rep.type);
rep.net = old.net;
rep.user_idx = old.user_idx;
old.net = nullptr;
old.user_idx = store_index<PortRef>{};
if (rep.type == PORT_OUT) {
if (rep.net != nullptr) {
rep.net->driver.cell = other;
rep.net->driver.port = other_port;
}
} else if (rep.type == PORT_IN) {
if (rep.net != nullptr) {
auto &load = rep.net->users.at(rep.user_idx);
load.cell = other;
load.port = other_port;
}
} else {
NPNR_ASSERT(false);
}
}
void CellInfo::renamePort(IdString old_name, IdString new_name)
{
if (!ports.count(old_name))
return;
PortInfo pi = ports.at(old_name);
if (pi.net != nullptr) {
if (pi.net->driver.cell == this && pi.net->driver.port == old_name)
pi.net->driver.port = new_name;
if (pi.user_idx)
pi.net->users.at(pi.user_idx).port = new_name;
}
ports.erase(old_name);
pi.name = new_name;
ports[new_name] = pi;
}
void CellInfo::movePortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell,
IdString new_name, int new_offset, bool new_brackets, int width)
{
for (int i = 0; i < width; i++) {
IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset));
IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset));
movePortTo(old_port, new_cell, new_port);
}
}
void CellInfo::copyPortTo(IdString port, CellInfo *other, IdString other_port)
{
if (!ports.count(port))
return;
other->ports[other_port].name = other_port;
other->ports[other_port].type = ports.at(port).type;
other->connectPort(other_port, ports.at(port).net);
}
void CellInfo::copyPortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell,
IdString new_name, int new_offset, bool new_brackets, int width)
{
for (int i = 0; i < width; i++) {
IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset));
IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset));
copyPortTo(old_port, new_cell, new_port);
}
}
NEXTPNR_NAMESPACE_END