nexus: IO pre-packing
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
69b449c875
commit
518ead2e2d
@ -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)
|
||||
{
|
||||
|
@ -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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user