Add support for bidirectional IOs

This commit is contained in:
Miodrag Milanovic 2024-05-09 13:10:10 +02:00
parent 30858569bc
commit 3ccf72139d
3 changed files with 67 additions and 47 deletions

View File

@ -145,7 +145,7 @@ void NgUltraImpl::parse_csv(const std::string &filename)
std::vector<CellInfo *> dest = get_cells(arguments.at(0)); std::vector<CellInfo *> dest = get_cells(arguments.at(0));
for (auto c : dest) { 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("iobname")] = arguments.at(0);
c->params[ctx->id("standard")] = arguments.at(2); c->params[ctx->id("standard")] = arguments.at(2);
c->params[ctx->id("drive")] = arguments.at(3); c->params[ctx->id("drive")] = arguments.at(3);

View File

@ -149,25 +149,19 @@ void NgUltraImpl::postRoute()
{ {
Loc loc = ctx->getBelLocation(bel); Loc loc = ctx->getBelLocation(bel);
cell->setParam(ctx->id("type"), Property("BFR")); 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")) { if (boost::ends_with(bel_name, "CD")) {
loc.z -= 3; 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")) { } else if (boost::ends_with(bel_name, "OD")) {
loc.z -= 2; 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 { } else {
loc.z -= 1; 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)); CellInfo *iob = ctx->getBoundBelCell(ctx->getBelByLocation(loc));
if (iob) { if (!iob || iob->params.count(ctx->id("iobname"))==0)
cell->setParam(ctx->id("iobname"), iob->params[ctx->id("iobname")]); 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; break;
default: default:

View File

@ -302,48 +302,74 @@ void NgUltraPacker::pack_iobs(void)
{ {
log_info("Pack IOBs...\n"); log_info("Pack IOBs...\n");
// Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis
const pool<CellTypePort> top_ports{ for (auto &port : ctx->ports) {
CellTypePort(id_NX_IOB_I, id_IO), if (!ctx->cells.count(port.first))
CellTypePort(id_NX_IOB_O, id_IO), 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();
std::vector<IdString> to_remove;
for (auto &cell : ctx->cells) { PortRef top_port;
auto &ci = *cell.second; top_port.cell = nullptr;
if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf"))) bool is_npnr_iob = false;
continue;
NetInfo *i = ci.getPort(id_I); if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
if (i && i->driver.cell) { // Might have an input buffer connected to it
if (!top_ports.count(CellTypePort(i->driver))) is_npnr_iob = true;
log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), NetInfo *o = ci->getPort(id_O);
ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port)); if (o == nullptr)
for (auto &attrs : ci.attrs) ;
i->driver.cell->attrs[attrs.first] = attrs.second; else if (o->users.entries() > 1)
for (auto &params : ci.params) log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first));
i->driver.cell->params[params.first] = params.second; else if (o->users.entries() == 1)
top_port = *o->users.begin();
} }
NetInfo *o = ci.getPort(id_O); if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
if (o) { // Might have an output buffer connected to it
for (auto &usr : o->users) { is_npnr_iob = true;
if (!top_ports.count(CellTypePort(usr))) NetInfo *i = ci->getPort(id_I);
log_error("Top-level port '%s' driving illegal port %s.%s\n", ctx->nameOf(&ci), if (i != nullptr && i->driver.cell != nullptr) {
ctx->nameOf(usr.cell), ctx->nameOf(usr.port)); if (top_port.cell != nullptr)
for (auto &attrs : ci.attrs) log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first));
usr.cell->attrs[attrs.first] = attrs.second; top_port = i->driver;
for (auto &params : ci.params) }
usr.cell->params[params.first] = params.second; // 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); if (!is_npnr_iob)
ci.disconnectPort(id_O); log_error("Port '%s' doesn't seem to have a corresponding top level IO (internal cell type mismatch)\n",
to_remove.push_back(ci.name); ctx->nameOf(port.first));
}
for (IdString cell_name : to_remove)
ctx->cells.erase(cell_name);
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 &params : 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<CellInfo*> to_update; std::vector<CellInfo*> to_update;
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second; 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; continue;
if (ci.params.count(id_location) && ci.attrs.count(id_LOC)) { if (ci.params.count(id_location) && ci.attrs.count(id_LOC)) {
if (ci.params[id_location].as_string() != ci.attrs[id_LOC].as_string()) if (ci.params[id_location].as_string() != ci.attrs[id_LOC].as_string())