mirror of https://github.com/YosysHQ/nextpnr.git
xilinx: Support cascaded IOSERDES and TMDS
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
4f27338b23
commit
4ace8952d3
|
|
@ -171,6 +171,12 @@ X(OLOGICE3_MISR)
|
|||
X(OSERDESE2)
|
||||
X(OSERDESE2_OSERDESE2)
|
||||
|
||||
X(SHIFTIN1)
|
||||
X(SHIFTIN2)
|
||||
X(SHIFTOUT1)
|
||||
X(SHIFTOUT2)
|
||||
X(TBYTEIN)
|
||||
|
||||
X(ILOGICE3)
|
||||
X(ILOGICE3_IFF)
|
||||
X(ILOGICE3_ZHOLD_DELAY)
|
||||
|
|
|
|||
|
|
@ -948,6 +948,17 @@ struct FasmBackend
|
|||
write_bit("SSTL12_SSTL135_SSTL15.IN");
|
||||
}
|
||||
|
||||
// IN_TERM.NONE and IN_ONLY for TMDS_33 output, e.g. HDMI signals
|
||||
if (is_output && is_diff) {
|
||||
if (is_tmds33 && yLoc == 1) {
|
||||
if (pad->attrs.count(id_IN_TERM))
|
||||
write_bit("IN_TERM." + pad->attrs.at(id_IN_TERM).as_string());
|
||||
else
|
||||
write_bit("IN_TERM.NONE");
|
||||
write_bit("LVCMOS12_LVCMOS15_LVCMOS18_LVCMOS25_LVCMOS33_LVDS_25_LVTTL_SSTL135_SSTL15_TMDS_33.IN_ONLY");
|
||||
}
|
||||
}
|
||||
|
||||
write_bit("PULLTYPE." + pulltype);
|
||||
pop(); // IOB_YN
|
||||
|
||||
|
|
@ -1064,7 +1075,12 @@ struct FasmBackend
|
|||
write_bit("ODDR.DDR_CLK_EDGE.SAME_EDGE");
|
||||
write_bit("ODDR.SRUSED");
|
||||
write_bit("ODDR_TDDR.IN_USE");
|
||||
write_bit("OQUSED", ci->getPort(id_OQ) != nullptr);
|
||||
|
||||
auto serdes_mode = str_or_default(ci->params, id_SERDES_MODE, "MASTER");
|
||||
bool is_cascaded = (serdes_mode == "SLAVE");
|
||||
|
||||
// For cascaded OSERDESE2, OQUSED must be set even though OQ is not connected
|
||||
write_bit("OQUSED", is_cascaded || ci->getPort(id_OQ));
|
||||
write_bit("ZINV_CLK", !bool_or_default(ci->params, id_IS_CLK_INVERTED, false));
|
||||
for (std::string t : {"T1", "T2", "T3", "T4"})
|
||||
write_bit("ZINV_" + t, (ci->getPort(ctx->id(t)) != nullptr || t == "T1") &&
|
||||
|
|
@ -1080,7 +1096,7 @@ struct FasmBackend
|
|||
push("OSERDES");
|
||||
write_bit("IN_USE");
|
||||
std::string type = str_or_default(ci->params, id_DATA_RATE_OQ, "BUF");
|
||||
write_bit(std::string("DATA_RATE_OQ.") + ((ci->getPort(id_OQ) != nullptr) ? type : "BUF"));
|
||||
write_bit(std::string("DATA_RATE_OQ.") + ((ci->getPort(id_OQ) != nullptr) ? type : "DDR"));
|
||||
write_bit(std::string("DATA_RATE_TQ.") +
|
||||
((ci->getPort(id_TQ) != nullptr) ? str_or_default(ci->params, id_DATA_RATE_TQ, "BUF") : "BUF"));
|
||||
int width = int_or_default(ci->params, id_DATA_WIDTH, 8);
|
||||
|
|
@ -1102,6 +1118,8 @@ struct FasmBackend
|
|||
#endif
|
||||
write_bit("SRTYPE.SYNC");
|
||||
write_bit("TSRTYPE.SYNC");
|
||||
if (is_cascaded)
|
||||
write_bit("SERDES_MODE.SLAVE");
|
||||
pop();
|
||||
} else if (ci->type == id_ISERDESE2_ISERDESE2) {
|
||||
std::string data_rate = str_or_default(ci->params, id_DATA_RATE);
|
||||
|
|
|
|||
|
|
@ -774,26 +774,88 @@ void XC7Packer::pack_iologic()
|
|||
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_OSERDESE2) {
|
||||
NetInfo *q = ci->getPort(id_OQ);
|
||||
NetInfo *ofb = ci->getPort(id_OFB);
|
||||
bool q_disconnected = !q || q->users.empty();
|
||||
bool ofb_disconnected = !ofb || ofb->users.empty();
|
||||
if (q_disconnected && ofb_disconnected) {
|
||||
log_error("%s '%s' has disconnected OQ/OFB output ports\n", ci->type.c_str(ctx), ctx->nameOf(ci));
|
||||
|
||||
NetInfo *shiftin1 = ci->getPort(id_SHIFTIN1), *shiftin2 = ci->getPort(id_SHIFTIN2),
|
||||
*tbytein = ci->getPort(id_TBYTEIN);
|
||||
|
||||
// If connected to ground (i.e. unused) this can't actually be routed so remove it
|
||||
if (shiftin1 && shiftin1->name == ctx->id("$PACKER_GND_NET")) {
|
||||
ci->disconnectPort(id_SHIFTIN1);
|
||||
shiftin1 = nullptr;
|
||||
}
|
||||
if (shiftin2 && shiftin2->name == ctx->id("$PACKER_GND_NET")) {
|
||||
ci->disconnectPort(id_SHIFTIN2);
|
||||
shiftin2 = nullptr;
|
||||
}
|
||||
if (tbytein && tbytein->name == ctx->id("$PACKER_GND_NET")) {
|
||||
ci->disconnectPort(id_TBYTEIN);
|
||||
tbytein = nullptr;
|
||||
}
|
||||
BelId io_bel;
|
||||
CellInfo *ob = !q_disconnected ? find_p_outbuf(q) : find_p_outbuf(ofb);
|
||||
if (ob != nullptr)
|
||||
io_bel = ob->bel;
|
||||
else
|
||||
log_error("%s '%s' has illegal fanout on OQ or OFB output\n", ci->type.c_str(ctx), ctx->nameOf(ci));
|
||||
|
||||
SiteIndex ol_site = get_ologic_site(io_bel);
|
||||
auto serdes_mode = str_or_default(ci->params, id_SERDES_MODE, "MASTER");
|
||||
|
||||
BelId oserdes_bel = uarch->get_site_bel(ol_site, id_OSERDESE2);
|
||||
NPNR_ASSERT(oserdes_bel != BelId());
|
||||
log_info(" binding output SERDES cell '%s' to bel '%s'\n", ctx->nameOf(ci), ctx->nameOfBel(oserdes_bel));
|
||||
ctx->bindBel(oserdes_bel, ci, STRENGTH_LOCKED);
|
||||
if (serdes_mode == "MASTER") {
|
||||
NetInfo *q = ci->getPort(id_OQ);
|
||||
NetInfo *ofb = ci->getPort(id_OFB);
|
||||
bool q_disconnected = !q || q->users.empty();
|
||||
bool ofb_disconnected = !ofb || ofb->users.empty();
|
||||
if (q_disconnected && ofb_disconnected) {
|
||||
log_error("%s '%s' has disconnected OQ/OFB output ports\n", ci->type.c_str(ctx), ctx->nameOf(ci));
|
||||
}
|
||||
BelId io_bel;
|
||||
CellInfo *ob = !q_disconnected ? find_p_outbuf(q) : find_p_outbuf(ofb);
|
||||
if (ob != nullptr)
|
||||
io_bel = ob->bel;
|
||||
else
|
||||
log_error("%s '%s' has illegal fanout on OQ or OFB output\n", ci->type.c_str(ctx), ctx->nameOf(ci));
|
||||
|
||||
SiteIndex ol_site = get_ologic_site(io_bel);
|
||||
|
||||
BelId oserdes_bel = uarch->get_site_bel(ol_site, id_OSERDESE2);
|
||||
NPNR_ASSERT(oserdes_bel != BelId());
|
||||
log_info(" binding output SERDES cell '%s' to bel '%s'\n", ctx->nameOf(ci),
|
||||
ctx->nameOfBel(oserdes_bel));
|
||||
ctx->bindBel(oserdes_bel, ci, STRENGTH_LOCKED);
|
||||
|
||||
if (shiftin1 != nullptr || shiftin2 != nullptr) {
|
||||
CellInfo *cascaded_cell = shiftin1 ? shiftin1->driver.cell : shiftin2->driver.cell;
|
||||
if (!cascaded_cell || !cascaded_cell->type.in(id_OSERDESE2))
|
||||
log_error("OSERDESE2 cell '%s' has SHIFTIN driven by illegal cell '%s'\n", ctx->nameOf(ci),
|
||||
cascaded_cell ? ctx->nameOf(cascaded_cell) : "");
|
||||
|
||||
// cascaded location is one below master, so place the cascaded cell there
|
||||
SiteIndex cascade_site = uarch->rel_site(ol_site, 0, -1);
|
||||
NPNR_ASSERT(cascade_site != SiteIndex());
|
||||
BelId cascade_bel = uarch->get_site_bel(cascade_site, id_OSERDESE2);
|
||||
NPNR_ASSERT(cascade_bel != BelId());
|
||||
log_info(" binding cascaded output SERDES cell '%s' to bel '%s'\n", ctx->nameOf(cascaded_cell),
|
||||
ctx->nameOfBel(cascade_bel));
|
||||
ctx->bindBel(cascade_bel, cascaded_cell, STRENGTH_LOCKED);
|
||||
}
|
||||
|
||||
} else if (serdes_mode == "SLAVE") {
|
||||
|
||||
NetInfo *shiftout1 = ci->getPort(id_SHIFTOUT1);
|
||||
NetInfo *shiftout2 = ci->getPort(id_SHIFTOUT2);
|
||||
if (!shiftout1 && !shiftout2)
|
||||
log_error("OSERDESE2 cell '%s' with SERDES_MODE SLAVE must have SHIFTOUT1/2 connected.\n",
|
||||
ctx->nameOf(ci));
|
||||
for (auto net : {shiftout1, shiftout2}) {
|
||||
if (!net)
|
||||
continue;
|
||||
if (net->users.entries() != 1)
|
||||
log_error(
|
||||
"OSERDESE2 cell '%s' with SERDES_MODE SLAVE has multiple fanout on SHIFTOUT net '%s'\n",
|
||||
ctx->nameOf(ci), ctx->nameOf(net));
|
||||
auto usr = *(net->users.begin());
|
||||
if (usr.cell->type != id_OSERDESE2 || !usr.port.in(id_SHIFTIN1, id_SHIFTIN2))
|
||||
log_error("OSERDESE2 cell '%s' has SHIFTOUT driving illegal cell port '%s.%s'\n",
|
||||
ctx->nameOf(ci), ctx->nameOf(usr.cell), ctx->nameOf(usr.port));
|
||||
}
|
||||
} else {
|
||||
log_error("OSERDESE2 cell '%s' has unsupported SERDES_MODE '%s'\n", ctx->nameOf(ci),
|
||||
serdes_mode.c_str());
|
||||
}
|
||||
|
||||
} else if (ci->type == id_IDDR) {
|
||||
fold_inverter(ci, "C");
|
||||
|
|
|
|||
|
|
@ -426,6 +426,18 @@ Loc XilinxImpl::rel_site_loc(SiteIndex site) const
|
|||
return Loc(site_data.rel_x, site_data.rel_y, 0);
|
||||
}
|
||||
|
||||
SiteIndex XilinxImpl::rel_site(SiteIndex site, int dx, int dy) const
|
||||
{
|
||||
const auto &base_site_data = tile_extra_data(site.tile)->sites[site.site];
|
||||
for (size_t i = 0; i < tile_extra_data(site.tile)->sites.size(); i++) {
|
||||
const auto &site_data = tile_extra_data(site.tile)->sites[i];
|
||||
if (site_data.name_prefix == base_site_data.name_prefix && site_data.rel_x == (base_site_data.rel_x + dx) &&
|
||||
site_data.rel_y == (base_site_data.rel_y + dy))
|
||||
return SiteIndex(site.tile, i);
|
||||
}
|
||||
return SiteIndex();
|
||||
}
|
||||
|
||||
int XilinxImpl::hclk_for_iob(BelId pad) const
|
||||
{
|
||||
std::string tile_type = bel_tile_type(pad).str(ctx);
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ struct XilinxImpl : HimbaechelAPI
|
|||
bool is_bram_tile(BelId bel) const;
|
||||
|
||||
SiteIndex get_bel_site(BelId bel) const;
|
||||
SiteIndex rel_site(SiteIndex site, int dx, int dy) const;
|
||||
Loc rel_site_loc(SiteIndex site) const;
|
||||
IdString get_site_name(SiteIndex site) const;
|
||||
IdString bel_name_in_site(BelId bel) const;
|
||||
|
|
|
|||
Loading…
Reference in New Issue