From 24fc33c01452cba93376ec6d0da006f64f0a4cc3 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 27 Sep 2024 17:09:15 +0200 Subject: [PATCH] xilinx: Basic I/ODDR support Signed-off-by: gatecat --- himbaechel/uarch/xilinx/fasm.cc | 6 +- himbaechel/uarch/xilinx/pack.cc | 2 +- himbaechel/uarch/xilinx/pack.h | 10 +- himbaechel/uarch/xilinx/pack_io.cc | 180 +++++++++++++++++++++++++++++ himbaechel/uarch/xilinx/xilinx.cc | 10 ++ himbaechel/uarch/xilinx/xilinx.h | 1 + 6 files changed, 199 insertions(+), 10 deletions(-) diff --git a/himbaechel/uarch/xilinx/fasm.cc b/himbaechel/uarch/xilinx/fasm.cc index ecd0930b..9ca5e074 100644 --- a/himbaechel/uarch/xilinx/fasm.cc +++ b/himbaechel/uarch/xilinx/fasm.cc @@ -333,19 +333,17 @@ struct FasmBackend out << src_name << std::endl; if (boost::contains(tile_name, "IOI") && boost::starts_with(dst_name, "IOI_OCLK_")) { -#if 0 dst_name.insert(dst_name.find("OCLK") + 4, 1, 'M'); orig_dst_name.insert(dst_name.find("OCLK") + 4, 1, 'M'); - WireId w = ctx->getWireByNameStr(tile_name + "/" + orig_dst_name); + WireId w = uarch->lookup_wire(pip.tile, ctx->id(orig_dst_name)); + NPNR_ASSERT(w != WireId()); if (ctx->getBoundWireNet(w) == nullptr) { out << tile_name << "."; out << dst_name << "."; out << src_name << std::endl; } -#endif - NPNR_ASSERT_FALSE("unimplemented!"); } last_was_blank = false; diff --git a/himbaechel/uarch/xilinx/pack.cc b/himbaechel/uarch/xilinx/pack.cc index a219fbf4..1644e188 100644 --- a/himbaechel/uarch/xilinx/pack.cc +++ b/himbaechel/uarch/xilinx/pack.cc @@ -733,7 +733,7 @@ void XilinxImpl::pack() packer.pack_io(); packer.prepare_clocking(); packer.pack_constants(); - // packer.pack_iologic(); + packer.pack_iologic(); // packer.pack_idelayctrl(); packer.pack_clocking(); packer.pack_muxfs(); diff --git a/himbaechel/uarch/xilinx/pack.h b/himbaechel/uarch/xilinx/pack.h index 30420329..008c758f 100644 --- a/himbaechel/uarch/xilinx/pack.h +++ b/himbaechel/uarch/xilinx/pack.h @@ -186,11 +186,11 @@ struct XC7Packer : public XilinxPacker // IOLOGIC dict hp_iol_rules, hd_iol_rules, ioctrl_rules; void fold_inverter(CellInfo *cell, std::string port); - std::string get_ologic_site(const std::string &io_bel); - std::string get_ilogic_site(const std::string &io_bel); - std::string get_ioctrl_site(const std::string &io_bel); - std::string get_odelay_site(const std::string &io_bel); - std::string get_idelay_site(const std::string &io_bel); + SiteIndex get_ologic_site(BelId io_bel); + SiteIndex get_ilogic_site(BelId io_bel); + SiteIndex get_ioctrl_site(BelId io_bel); + SiteIndex get_odelay_site(BelId io_bel); + SiteIndex get_idelay_site(BelId io_bel); // Call before packing constants void prepare_iologic(); diff --git a/himbaechel/uarch/xilinx/pack_io.cc b/himbaechel/uarch/xilinx/pack_io.cc index 5bebbb9c..2419822b 100644 --- a/himbaechel/uarch/xilinx/pack_io.cc +++ b/himbaechel/uarch/xilinx/pack_io.cc @@ -494,4 +494,184 @@ void XC7Packer::check_valid_pad(CellInfo *ci, std::string type) log_error("unsupported DRIVE strength property %s for port %s", drive_attr->second.c_str(), ci->name.c_str(ctx)); } +SiteIndex XC7Packer::get_ologic_site(BelId io_bel) +{ + BelId ibc_bel; + if (boost::contains(uarch->bel_name_in_site(io_bel).str(ctx), "IOB18")) + ibc_bel = uarch->get_site_bel(uarch->get_bel_site(io_bel), ctx->id("IOB18.OUTBUF_DCIEN")); + else + ibc_bel = uarch->get_site_bel(uarch->get_bel_site(io_bel), ctx->id("IOB33.OUTBUF")); + NPNR_ASSERT(ibc_bel != BelId()); + + std::queue visit; + visit.push(ctx->getBelPinWire(ibc_bel, id_IN)); + + while (!visit.empty()) { + WireId cursor = visit.front(); + visit.pop(); + for (auto bp : ctx->getWireBelPins(cursor)) { + auto site = uarch->get_bel_site(bp.bel); + if (boost::starts_with(uarch->get_site_name(site).str(ctx), "OLOGIC")) + return site; + } + for (auto pip : ctx->getPipsUphill(cursor)) + visit.push(ctx->getPipSrcWire(pip)); + } + NPNR_ASSERT_FALSE("failed to find OLOGIC"); +} + +SiteIndex XC7Packer::get_ilogic_site(BelId io_bel) +{ + BelId ibc_bel; + if (boost::contains(uarch->bel_name_in_site(io_bel).str(ctx), "IOB18")) + ibc_bel = uarch->get_site_bel(uarch->get_bel_site(io_bel), ctx->id("IOB18.INBUF_DCIEN")); + else + ibc_bel = uarch->get_site_bel(uarch->get_bel_site(io_bel), ctx->id("IOB33.INBUF_EN")); + + NPNR_ASSERT(ibc_bel != BelId()); + + std::queue visit; + visit.push(ctx->getBelPinWire(ibc_bel, id_OUT)); + + while (!visit.empty()) { + WireId cursor = visit.front(); + visit.pop(); + for (auto bp : ctx->getWireBelPins(cursor)) { + auto site = uarch->get_bel_site(bp.bel); + if (boost::starts_with(uarch->get_site_name(site).str(ctx), "ILOGIC")) + return site; + } + for (auto pip : ctx->getPipsDownhill(cursor)) + visit.push(ctx->getPipDstWire(pip)); + } + NPNR_ASSERT_FALSE("failed to find ILOGIC"); +} + + +void XC7Packer::fold_inverter(CellInfo *cell, std::string port) +{ + IdString p = ctx->id(port); + NetInfo *net = cell->getPort(p); + if (!net) + return; + CellInfo *drv = net->driver.cell; + if (!drv) + return; + if (drv->type == id_LUT1 && int_or_default(drv->params, id_INIT, 0) == 1) { + cell->disconnectPort(p); + NetInfo *preinv = drv->getPort(id_I0); + cell->connectPort(p, preinv); + cell->params[ctx->idf("IS_%s_INVERTED", port.c_str())] = 1; + if (net->users.empty()) + packed_cells.insert(drv->name); + } else if (drv->type == id_INV) { + cell->disconnectPort(p); + NetInfo *preinv = drv->getPort(id_I); + cell->connectPort(p, preinv); + cell->params[ctx->idf("IS_%s_INVERTED", port.c_str())] = 1; + if (net->users.empty()) + packed_cells.insert(drv->name); + } +} + +void XC7Packer::pack_iologic() +{ + log_info("Packing IOLOGIC...\n"); + dict iodelay_to_io; + dict iologic_rules; + + // IDDR + iologic_rules[id_IDDR].new_type = id_ILOGICE3_IFF; + iologic_rules[id_IDDR].port_multixform[id_C] = {id_CK, id_CKB}; + iologic_rules[id_IDDR].port_xform[id_S] = id_SR; + iologic_rules[id_IDDR].port_xform[id_R] = id_SR; + + // Handles pseudo-diff output buffers without finding multiple sinks + auto find_p_outbuf = [&](NetInfo *net) { + CellInfo *outbuf = nullptr; + for (auto &usr : net->users) { + IdString type = usr.cell->type; + if (type.in(id_IOB33_OUTBUF, id_IOB33M_OUTBUF, id_IOB18_OUTBUF_DCIEN, id_IOB18M_OUTBUF_DCIEN)) { + if (outbuf != nullptr) + return (CellInfo *)nullptr; // drives multiple outputs + outbuf = usr.cell; + } else if (type == id_ODELAYE2) { + auto dataout = usr.cell->ports.find(id_DATAOUT); + if (dataout != usr.cell->ports.end()) { + for (auto &user : dataout->second.net->users) { + IdString dataout_type = user.cell->type; + if (dataout_type.in(id_IOB18_OUTBUF_DCIEN, id_IOB18M_OUTBUF_DCIEN)) { + if (outbuf != nullptr) + return (CellInfo *)nullptr; // drives multiple outputs + outbuf = user.cell; + } + } + } else { + if (outbuf != nullptr) + return (CellInfo *)nullptr; // drives multiple outputs + } + } + } + return outbuf; + }; + + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type == id_ODDR) { + NetInfo *q = ci->getPort(id_Q); + if (q == nullptr || q->users.empty()) + log_error("%s '%s' has disconnected Q output\n", ci->type.c_str(ctx), ctx->nameOf(ci)); + BelId io_bel; + CellInfo *ob = find_p_outbuf(q); + if (ob != nullptr) + io_bel = ob->bel; + else + log_error("%s '%s' has illegal fanout on Q output\n", ci->type.c_str(ctx), ctx->nameOf(ci)); + SiteIndex ol_site = get_ologic_site(io_bel); + + PortRef dest_port = *q->users.begin(); + auto is_tristate = dest_port.port == id_TRI; + + dict oddr_rules; + if (boost::contains(uarch->get_site_name(ol_site).str(ctx), "IOB18")) + oddr_rules[id_ODDR].new_type = is_tristate ? id_OLOGICE2_TFF : id_OLOGICE2_OUTFF; + else + oddr_rules[id_ODDR].new_type = is_tristate ? id_OLOGICE3_TFF : id_OLOGICE3_OUTFF; + oddr_rules[id_ODDR].port_xform[id_C] = id_CK; + oddr_rules[id_ODDR].port_xform[id_S] = id_SR; + oddr_rules[id_ODDR].port_xform[id_R] = id_SR; + xform_cell(oddr_rules, ci); + + BelId oddr_bel = uarch->get_site_bel(ol_site, is_tristate ? ctx->id("TFF") : ctx->id("OUTFF")); + NPNR_ASSERT(oddr_bel != BelId()); + log_info(" binding output DDR cell '%s' to bel '%s'\n", ctx->nameOf(ci), ctx->nameOfBel(oddr_bel)); + ctx->bindBel(oddr_bel, ci, STRENGTH_LOCKED); + } else if (ci->type == id_IDDR) { + fold_inverter(ci, "C"); + + BelId io_bel; + NetInfo *d = ci->getPort(id_D); + if (!d || !d->driver.cell) + log_error("%s '%s' has disconnected D input\n", ci->type.c_str(ctx), ctx->nameOf(ci)); + CellInfo *drv = d->driver.cell; + if (boost::contains(drv->type.str(ctx), "INBUF_EN") || boost::contains(drv->type.str(ctx), "INBUF_DCIEN")) + io_bel = drv->bel; + else + log_error("%s '%s' has D input connected to illegal cell type %s\n", ci->type.c_str(ctx), + ctx->nameOf(ci), drv->type.c_str(ctx)); + + SiteIndex il_site = get_ilogic_site(io_bel); + + BelId iddr_bel = uarch->get_site_bel(il_site, ctx->id("IFF")); + NPNR_ASSERT(iddr_bel != BelId()); + log_info(" binding input DDR cell '%s' to bel '%s'\n", ctx->nameOf(ci), ctx->nameOfBel(iddr_bel)); + ctx->bindBel(iddr_bel, ci, STRENGTH_LOCKED); + } + } + + flush_cells(); + generic_xform(iologic_rules, false); + flush_cells(); +} + NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/xilinx/xilinx.cc b/himbaechel/uarch/xilinx/xilinx.cc index aed4d4b7..229c3e37 100644 --- a/himbaechel/uarch/xilinx/xilinx.cc +++ b/himbaechel/uarch/xilinx/xilinx.cc @@ -107,6 +107,16 @@ IdStringList XilinxImpl::get_site_bel_name(BelId bel) const return IdStringList::concat(get_site_name(get_bel_site(bel)), bel_name_in_site(bel)); } +WireId XilinxImpl::lookup_wire(int tile, IdString wire_name) const +{ + const auto &tdata = chip_tile_info(ctx->chip_info, tile); + for (int wire = 0; wire < tdata.wires.ssize(); wire++) { + if (IdString(tdata.wires[wire].name) == wire_name) + return ctx->normalise_wire(tile, wire); + } + return WireId(); +} + void XilinxImpl::notifyBelChange(BelId bel, CellInfo *cell) { auto &ts = tile_status.at(bel.tile); diff --git a/himbaechel/uarch/xilinx/xilinx.h b/himbaechel/uarch/xilinx/xilinx.h index a7d3e686..ac127ab9 100644 --- a/himbaechel/uarch/xilinx/xilinx.h +++ b/himbaechel/uarch/xilinx/xilinx.h @@ -149,6 +149,7 @@ struct XilinxImpl : HimbaechelAPI IdString bel_name_in_site(BelId bel) const; IdStringList get_site_bel_name(BelId bel) const; BelId get_site_bel(SiteIndex site, IdString bel_name) const; + WireId lookup_wire(int tile, IdString wire_name) const; int hclk_for_iob(BelId pad) const; int hclk_for_ioi(int tile) const;