aboutsummaryrefslogtreecommitdiffstats
path: root/ecp5
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2022-03-01 16:38:48 +0000
committerGitHub <noreply@github.com>2022-03-01 16:38:48 +0000
commit0a70b9c992c06a7553725b3742052eb95abd5f20 (patch)
treed1d8436576bad3424031c5ce435d76717fef196e /ecp5
parentd8bea3ccfc7b6e925a9fd63c9172748ea0420e88 (diff)
parent86699b42f619960bfefd4d0b479dd44a90527ea4 (diff)
downloadnextpnr-0a70b9c992c06a7553725b3742052eb95abd5f20.tar.gz
nextpnr-0a70b9c992c06a7553725b3742052eb95abd5f20.tar.bz2
nextpnr-0a70b9c992c06a7553725b3742052eb95abd5f20.zip
Merge pull request #925 from YosysHQ/gatecat/netlist-iv
Switch to potentially-sparse net users array
Diffstat (limited to 'ecp5')
-rw-r--r--ecp5/cells.cc7
-rw-r--r--ecp5/globals.cc22
-rw-r--r--ecp5/pack.cc56
3 files changed, 39 insertions, 46 deletions
diff --git a/ecp5/cells.cc b/ecp5/cells.cc
index a5d484ff..2c5f96d3 100644
--- a/ecp5/cells.cc
+++ b/ecp5/cells.cc
@@ -212,10 +212,7 @@ static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellI
NPNR_ASSERT(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net);
NetInfo *ffnet = ff->ports.at(ff_port).net;
if (ffnet != nullptr)
- ffnet->users.erase(
- std::remove_if(ffnet->users.begin(), ffnet->users.end(),
- [ff, ff_port](PortRef port) { return port.cell == ff && port.port == ff_port; }),
- ffnet->users.end());
+ ffnet->users.remove(ff->ports.at(ff_port).user_idx);
} else {
ff->movePortTo(ff_port, lc, lc_port);
}
@@ -477,7 +474,7 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::u
inv_lut->connectPorts(id_Z, trio, id_T);
created_cells.push_back(std::move(inv_lut));
- if (donet->users.size() > 1) {
+ if (donet->users.entries() > 1) {
for (auto user : donet->users)
log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx));
log_error("unsupported tristate IO pattern for IO buffer '%s', "
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index 7b48e693..71188aa0 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -472,17 +472,15 @@ class Ecp5GlobalRouter
} else if (is_logic_port(user)) {
keep_users.push_back(user);
} else {
- glbptr->users.push_back(user);
user.cell->ports.at(user.port).net = glbptr;
+ user.cell->ports.at(user.port).user_idx = glbptr->users.add(user);
}
}
- net->users = keep_users;
+ net->users.clear();
+ for (auto &usr : keep_users)
+ usr.cell->ports.at(usr.port).user_idx = net->users.add(usr);
- dcc->ports[id_CLKI].net = net;
- PortRef clki_pr;
- clki_pr.port = id_CLKI;
- clki_pr.cell = dcc.get();
- net->users.push_back(clki_pr);
+ dcc->connectPort(id_CLKI, net);
if (net->clkconstr) {
glbptr->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint());
glbptr->clkconstr->low = net->clkconstr->low;
@@ -556,9 +554,13 @@ class Ecp5GlobalRouter
if (ci->type == id_DCCA || ci->type == id_DCSC) {
NetInfo *clock = ci->ports.at((ci->type == id_DCSC) ? id_DCSOUT : id_CLKO).net;
NPNR_ASSERT(clock != nullptr);
- bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(),
- [this](const PortRef &port) { return !is_clock_port(port); });
-
+ bool drives_fabric = false;
+ for (auto &usr : clock->users) {
+ if (!is_clock_port(usr)) {
+ drives_fabric = true;
+ break;
+ }
+ }
int glbid;
if (drives_fabric) {
if (fab_globals.empty())
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index e657e60c..02ce5aa1 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -326,14 +326,14 @@ class Ecp5Packer
}
// Pack LUTs feeding the same CCU2, RAM or DFF into a SLICE
- if (znet != nullptr && znet->users.size() < 10) {
+ if (znet != nullptr && znet->users.entries() < 10) {
for (auto user : znet->users) {
if (is_lc(ctx, user.cell) || user.cell->type == id_DP16KD || is_ff(ctx, user.cell)) {
for (auto port : user.cell->ports) {
if (port.second.type != PORT_IN || port.second.net == nullptr ||
port.second.net == znet)
continue;
- if (port.second.net->users.size() > 10)
+ if (port.second.net->users.entries() > 10)
continue;
CellInfo *drv = port.second.net->driver.cell;
if (drv == nullptr)
@@ -355,11 +355,11 @@ class Ecp5Packer
if (!ci->ports.count(ctx->id(inp)))
continue;
NetInfo *innet = ci->ports.at(ctx->id(inp)).net;
- if (innet != nullptr && innet->users.size() < 5 && innet->users.size() > 1)
+ if (innet != nullptr && innet->users.entries() < 5 && innet->users.entries() > 1)
inpnets.push_back(innet);
}
std::sort(inpnets.begin(), inpnets.end(),
- [&](const NetInfo *a, const NetInfo *b) { return a->users.size() < b->users.size(); });
+ [&](const NetInfo *a, const NetInfo *b) { return a->users.entries() < b->users.entries(); });
for (auto inet : inpnets) {
for (auto &user : inet->users) {
if (user.cell == nullptr || user.cell == ci || !is_lut(ctx, user.cell))
@@ -412,7 +412,7 @@ class Ecp5Packer
return false;
for (auto user : net->users) {
if (is_top_port(user)) {
- if (net->users.size() > 1)
+ if (net->users.entries() > 1)
log_error(" port %s.%s must be connected to (and only to) a top level pin\n",
user.cell->name.c_str(ctx), user.port.c_str(ctx));
tp = user;
@@ -420,7 +420,7 @@ class Ecp5Packer
}
}
if (net->driver.cell != nullptr && is_top_port(net->driver)) {
- if (net->users.size() > 1)
+ if (net->users.entries() > 1)
log_error(" port %s.%s must be connected to (and only to) a top level pin\n",
net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx));
tp = net->driver;
@@ -460,9 +460,9 @@ class Ecp5Packer
NetInfo *net = trio->ports.at(id_B).net;
if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) &&
- net->users.size() > 1) ||
+ net->users.entries() > 1) ||
(ci->type == ctx->id("$nextpnr_obuf") &&
- (net->users.size() > 2 || net->driver.cell != nullptr)) ||
+ (net->users.entries() > 2 || net->driver.cell != nullptr)) ||
(ci->type == ctx->id("$nextpnr_iobuf") && ci->ports.at(id_I).net != nullptr &&
ci->ports.at(id_I).net->driver.cell != nullptr))
log_error("Pin B of %s '%s' connected to more than a single top level IO.\n",
@@ -742,16 +742,14 @@ class Ecp5Packer
feedin->params[id_INJECT1_0] = std::string("NO");
feedin->params[id_INJECT1_1] = std::string("YES");
- carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(),
- [chain_in](const PortRef &user) {
- return user.port == chain_in.port && user.cell == chain_in.cell;
- }),
- carry->users.end());
+ carry->users.remove(chain_in.cell->ports.at(chain_in.port).user_idx);
feedin->connectPort(id_A0, carry);
NetInfo *new_carry = ctx->createNet(ctx->id(feedin->name.str(ctx) + "$COUT"));
feedin->connectPort(id_COUT, new_carry);
chain_in.cell->ports[chain_in.port].net = nullptr;
+ chain_in.cell->ports[chain_in.port].user_idx = {};
+
chain_in.cell->connectPort(chain_in.port, new_carry);
CellInfo *feedin_ptr = feedin.get();
@@ -782,12 +780,8 @@ class Ecp5Packer
if (chain_next) {
// Loop back into LUT4_1 for feedthrough
feedout->connectPort(id_A1, carry);
-
- carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(),
- [chain_next](const PortRef &user) {
- return user.port == chain_next->port && user.cell == chain_next->cell;
- }),
- carry->users.end());
+ if (chain_next->cell && chain_next->cell->ports.at(chain_next->port).user_idx)
+ carry->users.remove(chain_next->cell->ports.at(chain_next->port).user_idx);
NetInfo *new_cout = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$COUT"));
feedout->connectPort(id_COUT, new_cout);
@@ -833,7 +827,7 @@ class Ecp5Packer
} else {
NetInfo *carry_net = cell->ports.at(id_COUT).net;
bool at_end = (curr_cell == carryc.cells.end() - 1);
- if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) {
+ if (carry_net != nullptr && (carry_net->users.entries() > 1 || at_end)) {
boost::optional<PortRef> nextport;
if (!at_end) {
auto next_cell = *(curr_cell + 1);
@@ -1123,7 +1117,7 @@ class Ecp5Packer
if (pn == nullptr)
continue;
// Skip high-fanout nets that are unlikely to be relevant
- if (pn->users.size() > 25)
+ if (pn->users.entries() > 25)
continue;
// Add other ports on this net if not already visited
auto visit_port = [&](const PortRef &port) {
@@ -1304,11 +1298,11 @@ class Ecp5Packer
} else {
// Not allowed to change to a tie-high
uc->ports[user.port].net = constnet;
- constnet->users.push_back(user);
+ uc->ports[user.port].user_idx = constnet->users.add(user);
}
} else {
uc->ports[user.port].net = constnet;
- constnet->users.push_back(user);
+ uc->ports[user.port].user_idx = constnet->users.add(user);
}
} else if (is_ff(ctx, uc) && user.port == id_LSR &&
((!constval && str_or_default(uc->params, id_LSRMUX, "LSR") == "LSR") ||
@@ -1335,7 +1329,7 @@ class Ecp5Packer
user.port.str(ctx).substr(0, 6) == "SOURCE" || user.port.str(ctx).substr(0, 6) == "SIGNED" ||
user.port.str(ctx).substr(0, 2) == "OP") {
uc->ports[user.port].net = constnet;
- constnet->users.push_back(user);
+ uc->ports[user.port].user_idx = constnet->users.add(user);
} else {
// Connected to CIB ABCD. Default state is bitstream configurable
uc->params[ctx->id(user.port.str(ctx) + "MUX")] = std::string(constval ? "1" : "0");
@@ -1343,7 +1337,7 @@ class Ecp5Packer
}
} else {
uc->ports[user.port].net = constnet;
- constnet->users.push_back(user);
+ uc->ports[user.port].user_idx = constnet->users.add(user);
}
}
}
@@ -2037,7 +2031,7 @@ class Ecp5Packer
CellInfo *ci = cell.second.get();
if (ci->type == id_DQSBUFM) {
CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_DQSI).net, is_trellis_io, id_O);
- if (pio == nullptr || ci->ports.at(id_DQSI).net->users.size() > 1)
+ if (pio == nullptr || ci->ports.at(id_DQSI).net->users.entries() > 1)
log_error("DQSBUFM '%s' DQSI input must be connected only to a top level input\n",
ci->name.c_str(ctx));
if (!pio->attrs.count(id_BEL))
@@ -2273,7 +2267,7 @@ class Ecp5Packer
CellInfo *i_pio = net_driven_by(ctx, ci->ports.at(id_A).net, is_trellis_io, id_O);
CellInfo *o_pio = net_only_drives(ctx, ci->ports.at(id_Z).net, is_trellis_io, id_I, true);
CellInfo *iol = nullptr;
- if (i_pio != nullptr && ci->ports.at(id_A).net->users.size() == 1) {
+ if (i_pio != nullptr && ci->ports.at(id_A).net->users.entries() == 1) {
iol = create_pio_iologic(i_pio, ci);
set_iologic_mode(iol, "IREG_OREG");
bool drives_iologic = false;
@@ -2356,7 +2350,7 @@ class Ecp5Packer
CellInfo *ci = cell.second.get();
if (ci->type == id_IDDRX1F) {
CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O);
- if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1)
+ if (pio == nullptr || ci->ports.at(id_D).net->users.entries() > 1)
log_error("IDDRX1F '%s' D input must be connected only to a top level input\n",
ci->name.c_str(ctx));
CellInfo *iol;
@@ -2438,7 +2432,7 @@ class Ecp5Packer
packed_cells.insert(cell.first);
} else if (ci->type == id_IDDRX2F || ci->type == id_IDDR71B) {
CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O);
- if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1)
+ if (pio == nullptr || ci->ports.at(id_D).net->users.entries() > 1)
log_error("%s '%s' D input must be connected only to a top level input\n", ci->type.c_str(ctx),
ci->name.c_str(ctx));
CellInfo *iol;
@@ -2530,7 +2524,7 @@ class Ecp5Packer
packed_cells.insert(cell.first);
} else if (ci->type == id_IDDRX2DQA) {
CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O);
- if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1)
+ if (pio == nullptr || ci->ports.at(id_D).net->users.entries() > 1)
log_error("IDDRX2DQA '%s' D input must be connected only to a top level input\n",
ci->name.c_str(ctx));
CellInfo *iol;
@@ -2597,7 +2591,7 @@ class Ecp5Packer
// See if it can be packed as an input ff
NetInfo *d = ci->getPort(id_DI);
CellInfo *pio = net_driven_by(ctx, d, is_trellis_io, id_O);
- if (pio != nullptr && d->users.size() == 1) {
+ if (pio != nullptr && d->users.entries() == 1) {
// Input FF
CellInfo *iol;
if (pio_iologic.count(pio->name))