diff --git a/himbaechel/uarch/ng-ultra/csv.cc b/himbaechel/uarch/ng-ultra/csv.cc index 00d84499..25cc4112 100644 --- a/himbaechel/uarch/ng-ultra/csv.cc +++ b/himbaechel/uarch/ng-ultra/csv.cc @@ -145,7 +145,7 @@ void NgUltraImpl::parse_csv(const std::string &filename) std::vector dest = get_cells(arguments.at(0)); for (auto c : dest) { - c->attrs[id_LOC] = arguments.at(1); + c->params[ctx->id("location")] = arguments.at(1); c->params[ctx->id("iobname")] = arguments.at(0); c->params[ctx->id("standard")] = arguments.at(2); c->params[ctx->id("drive")] = arguments.at(3); diff --git a/himbaechel/uarch/ng-ultra/ng_ultra.cc b/himbaechel/uarch/ng-ultra/ng_ultra.cc index 49fb24a2..66a25f2e 100644 --- a/himbaechel/uarch/ng-ultra/ng_ultra.cc +++ b/himbaechel/uarch/ng-ultra/ng_ultra.cc @@ -149,25 +149,19 @@ void NgUltraImpl::postRoute() { Loc loc = ctx->getBelLocation(bel); cell->setParam(ctx->id("type"), Property("BFR")); + cell->setParam(ctx->id("mode"), Property(2, 2)); + cell->setParam(ctx->id("data_inv"), Property(0, 1)); if (boost::ends_with(bel_name, "CD")) { loc.z -= 3; - //cell->setParam(ctx->id("mode"), Property(0, 2)); - cell->setParam(ctx->id("path"), Property(2, 2)); } else if (boost::ends_with(bel_name, "OD")) { loc.z -= 2; - cell->setParam(ctx->id("mode"), Property(2, 2)); - cell->setParam(ctx->id("path"), Property(0, 2)); - cell->setParam(ctx->id("data_inv"), Property(0, 1)); } else { loc.z -= 1; - cell->setParam(ctx->id("mode"), Property(2, 2)); - cell->setParam(ctx->id("path"), Property(1, 2)); - cell->setParam(ctx->id("data_inv"), Property(0, 1)); } CellInfo *iob = ctx->getBoundBelCell(ctx->getBelByLocation(loc)); - if (iob) { - cell->setParam(ctx->id("iobname"), iob->params[ctx->id("iobname")]); - } + if (!iob || iob->params.count(ctx->id("iobname"))==0) + log_error("IOB for '%s' must have iobname defined.\n", cell->name.c_str(ctx)); + cell->setParam(ctx->id("iobname"), iob->params[ctx->id("iobname")]); } break; default: diff --git a/himbaechel/uarch/ng-ultra/pack.cc b/himbaechel/uarch/ng-ultra/pack.cc index 80db80fc..e6731e6a 100644 --- a/himbaechel/uarch/ng-ultra/pack.cc +++ b/himbaechel/uarch/ng-ultra/pack.cc @@ -302,48 +302,74 @@ void NgUltraPacker::pack_iobs(void) { log_info("Pack IOBs...\n"); // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis - const pool top_ports{ - CellTypePort(id_NX_IOB_I, id_IO), - CellTypePort(id_NX_IOB_O, id_IO), - }; - std::vector to_remove; - for (auto &cell : ctx->cells) { - auto &ci = *cell.second; - if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf"))) - continue; - NetInfo *i = ci.getPort(id_I); - if (i && i->driver.cell) { - if (!top_ports.count(CellTypePort(i->driver))) - log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), - ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port)); - for (auto &attrs : ci.attrs) - i->driver.cell->attrs[attrs.first] = attrs.second; - for (auto ¶ms : ci.params) - i->driver.cell->params[params.first] = params.second; + for (auto &port : 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 connected to it + is_npnr_iob = true; + NetInfo *o = ci->getPort(id_O); + if (o == nullptr) + ; + else if (o->users.entries() > 1) + log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first)); + else if (o->users.entries() == 1) + top_port = *o->users.begin(); } - NetInfo *o = ci.getPort(id_O); - if (o) { - for (auto &usr : o->users) { - if (!top_ports.count(CellTypePort(usr))) - log_error("Top-level port '%s' driving illegal port %s.%s\n", ctx->nameOf(&ci), - ctx->nameOf(usr.cell), ctx->nameOf(usr.port)); - for (auto &attrs : ci.attrs) - usr.cell->attrs[attrs.first] = attrs.second; - for (auto ¶ms : ci.params) - usr.cell->params[params.first] = params.second; + if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an output buffer connected to it + is_npnr_iob = true; + NetInfo *i = ci->getPort(id_I); + if (i != nullptr && i->driver.cell != nullptr) { + if (top_port.cell != nullptr) + log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); + top_port = i->driver; + } + // Edge case of a bidirectional buffer driving an output pin + if (i->users.entries() > 2) { + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + } else if (i->users.entries() == 2) { + if (top_port.cell != nullptr) + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + for (auto &usr : i->users) { + if (usr.cell->type == ctx->id("$nextpnr_obuf") || usr.cell->type == ctx->id("$nextpnr_iobuf")) + continue; + top_port = usr; + break; + } } } - ci.disconnectPort(id_I); - ci.disconnectPort(id_O); - to_remove.push_back(ci.name); - } - for (IdString cell_name : to_remove) - ctx->cells.erase(cell_name); + 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 + for (auto &attrs : ci->attrs) + top_port.cell->attrs[attrs.first] = attrs.second; + for (auto ¶ms : ci->params) + top_port.cell->params[params.first] = params.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 + ci->disconnectPort(id_I); + ci->disconnectPort(id_O); + ctx->cells.erase(port.first); + } std::vector to_update; for (auto &cell : ctx->cells) { CellInfo &ci = *cell.second; - if (!ci.type.in(id_NX_IOB_I, id_NX_IOB_O)) + if (!ci.type.in(id_NX_IOB_I, id_NX_IOB_O, id_NX_IOB)) continue; if (ci.params.count(id_location) && ci.attrs.count(id_LOC)) { if (ci.params[id_location].as_string() != ci.attrs[id_LOC].as_string())