diff options
author | David Shah <dave@ds0.me> | 2020-10-12 11:41:26 +0100 |
---|---|---|
committer | David Shah <dave@ds0.me> | 2020-11-30 08:45:27 +0000 |
commit | 518ead2e2dd11ba3bcd19085c2c91178ba9e458c (patch) | |
tree | a72fa90ff7db0a19ed383349166a78d32a7c700e | |
parent | 69b449c8759efa15f52add21b1c7f6dc1ceada3a (diff) | |
download | nextpnr-518ead2e2dd11ba3bcd19085c2c91178ba9e458c.tar.gz nextpnr-518ead2e2dd11ba3bcd19085c2c91178ba9e458c.tar.bz2 nextpnr-518ead2e2dd11ba3bcd19085c2c91178ba9e458c.zip |
nexus: IO pre-packing
Signed-off-by: David Shah <dave@ds0.me>
-rw-r--r-- | common/util.h | 9 | ||||
-rw-r--r-- | nexus/pack.cc | 58 |
2 files changed, 67 insertions, 0 deletions
diff --git a/common/util.h b/common/util.h index 9512bd40..d82e984a 100644 --- a/common/util.h +++ b/common/util.h @@ -112,6 +112,15 @@ template <typename K, typename V> std::map<K, V *> sorted(const std::unordered_m return retVal; }; +// Wrap an unordered_map, and allow it to be iterated over sorted by key +template <typename K, typename V> std::map<K, V &> sorted_ref(std::unordered_map<K, V> &orig) +{ + std::map<K, V &> retVal; + for (auto &item : orig) + retVal.emplace(std::make_pair(item.first, std::ref(item.second))); + return retVal; +}; + // Wrap an unordered_set, and allow it to be iterated over sorted by key template <typename K> std::set<K> sorted(const std::unordered_set<K> &orig) { diff --git a/nexus/pack.cc b/nexus/pack.cc index 688d74ab..3b53ea55 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -473,6 +473,64 @@ struct NexusPacker } } + void prepare_io() + { + // Find the actual IO buffer corresponding to a port; and copy attributes across to it + // Note that this relies on Yosys to do IO buffer inference, to match vendor tooling behaviour + // In all cases the nextpnr-inserted IO buffers are removed as redundant. + for (auto &port : sorted_ref(ctx->ports)) { + if (!ctx->cells.count(port.first)) + log_error("Port '%s' doesn't seem to have a corresponding top level IO\n", ctx->nameOf(port.first)); + CellInfo *ci = ctx->cells.at(port.first).get(); + + PortRef top_port; + top_port.cell = nullptr; + bool is_npnr_iob = false; + + if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an input buffer (IB etc) connected to it + is_npnr_iob = true; + NetInfo *o = get_net_or_empty(ci, id_O); + if (o == nullptr) + ; + else if (o->users.size() > 1) + log_error("Top level '%s' has multiple input buffers\n", ctx->nameOf(port.first)); + else if (o->users.size() == 1) + top_port = o->users.at(0); + } + if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an output buffer (OB etc) connected to it + is_npnr_iob = true; + NetInfo *i = get_net_or_empty(ci, id_I); + if (i == nullptr && i->driver.cell != nullptr) { + if (top_port.cell != nullptr) + log_error("Top level '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); + top_port = i->driver; + } + } + if (!is_npnr_iob) + log_error("Port '%s' doesn't seem to have a corresponding top level IO (internal cell type mismatch)\n", + ctx->nameOf(port.first)); + + if (top_port.cell == nullptr) { + log_info("Trimming port '%s' as it is unused.\n", ctx->nameOf(port.first)); + } else { + // Copy attributes to real IO buffer + if (ctx->io_attr.count(port.first)) { + for (auto &kv : ctx->io_attr.at(port.first)) { + top_port.cell->attrs[kv.first] = kv.second; + } + } + // Make sure that top level net is set correctly + port.second.net = top_port.cell->ports.at(top_port.port).net; + } + // Now remove the nextpnr-inserted buffer + disconnect_port(ctx, ci, id_I); + disconnect_port(ctx, ci, id_O); + ctx->cells.erase(port.first); + } + } + void pack_io() { for (auto cell : sorted(ctx->cells)) { |