ecp5: Trim IO connected to top level ports

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2018-11-09 15:17:26 +00:00
parent e9fe444dc7
commit 36178a5713

View File

@ -235,6 +235,43 @@ class Ecp5Packer
}
}
// Return true if an port is a top level port that provides its own IOBUF
bool is_top_port(PortRef &port) {
if (port.cell == nullptr)
return false;
if (port.cell->type == id_DCUA) {
return port.port == id_CH0_HDINP || port.port == id_CH0_HDINN || port.port == id_CH0_HDOUTP ||
port.port == id_CH0_HDOUTN ||
port.port == id_CH1_HDINP || port.port == id_CH1_HDINN || port.port == id_CH1_HDOUTP ||
port.port == id_CH1_HDOUTN;
} else if (port.cell->type == id_EXTREFB) {
return port.port == id_REFCLKP || port.port == id_REFCLKN;
} else {
return false;
}
}
// Return true if a net only drives a top port
bool drives_top_port(NetInfo *net, PortRef &tp) {
if (net == nullptr)
return false;
for (auto user : net->users) {
if (is_top_port(user)) {
if (net->users.size() > 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;
return true;
}
}
if (net->driver.cell != nullptr && is_top_port(net->driver)) {
if (net->users.size() > 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;
return true;
}
return false;
}
// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated
void pack_io()
{
@ -244,10 +281,14 @@ class Ecp5Packer
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
CellInfo *trio = nullptr;
NetInfo *ionet = nullptr;
PortRef tp;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
trio = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_trellis_io, ctx->id("B"), true, ci);
ionet = ci->ports.at(ctx->id("O")).net;
trio = net_only_drives(ctx, ionet, is_trellis_io, ctx->id("B"), true, ci);
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
ionet = ci->ports.at(ctx->id("I")).net;
trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci);
}
if (trio != nullptr) {
@ -266,6 +307,20 @@ class Ecp5Packer
ctx->nets.erase(net2->name);
}
}
} else if (drives_top_port(ionet, tp)) {
log_info("%s feeds %s %s.%s, removing %s %s.\n", ci->name.c_str(ctx), tp.cell->type.c_str(ctx), tp.cell->name.c_str(ctx),
tp.port.c_str(ctx),
ci->type.c_str(ctx), ci->name.c_str(ctx));
if (ionet != nullptr) {
ctx->nets.erase(ionet->name);
tp.cell->ports.at(tp.port).net = nullptr;
}
if (ci->type == ctx->id("$nextpnr_iobuf")) {
NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
if (net2 != nullptr) {
ctx->nets.erase(net2->name);
}
}
} else {
// Create a TRELLIS_IO buffer
std::unique_ptr<CellInfo> tr_cell =
@ -276,22 +331,25 @@ class Ecp5Packer
}
packed_cells.insert(ci->name);
for (const auto &attr : ci->attrs)
trio->attrs[attr.first] = attr.second;
if (trio != nullptr) {
for (const auto &attr : ci->attrs)
trio->attrs[attr.first] = attr.second;
auto loc_attr = trio->attrs.find(ctx->id("LOC"));
if (loc_attr != trio->attrs.end()) {
std::string pin = loc_attr->second;
BelId pinBel = ctx->getPackagePinBel(pin);
if (pinBel == BelId()) {
log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
trio->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
} else {
log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx),
ctx->getBelName(pinBel).c_str(ctx));
auto loc_attr = trio->attrs.find(ctx->id("LOC"));
if (loc_attr != trio->attrs.end()) {
std::string pin = loc_attr->second;
BelId pinBel = ctx->getPackagePinBel(pin);
if (pinBel == BelId()) {
log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
trio->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
} else {
log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx),
ctx->getBelName(pinBel).c_str(ctx));
}
trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
}
trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
}
}
}
flush_cells();