ecp5: Helper functions and bitstream for DQS

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2019-02-12 13:13:06 +00:00 committed by David Shah
parent eb45956d0e
commit 4402361246
2 changed files with 63 additions and 0 deletions

View File

@ -1191,6 +1191,36 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
cc.tiles[tile].add_enum(clkdiv + ".DIV", str_or_default(ci->params, ctx->id("DIV"), "2.0"));
cc.tiles[tile].add_enum(clkdiv + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED"));
} else if (ci->type == id_TRELLIS_ECLKBUF) {
} else if (ci->type == id_DQSBUFM) {
Loc loc = ctx->getBelLocation(ci->bel);
bool l = loc.y < 10;
std::string pic = l ? "PICL" : "PICR";
TileGroup tg;
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y - 2, loc.x, pic + "1_DQS0"));
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y - 1, loc.x, pic + "2_DQS1"));
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, pic + "0_DQS2"));
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x, pic + "1_DQS3"));
tg.config.add_enum("DQS.MODE", "DQSBUFM");
tg.config.add_enum("DQS.DQS_LI_DEL_ADJ", str_or_default(ci->params, ctx->id("DQS_LI_DEL_ADJ"), "PLUS"));
tg.config.add_enum("DQS.DQS_LO_DEL_ADJ", str_or_default(ci->params, ctx->id("DQS_LO_DEL_ADJ"), "PLUS"));
int li_del_value = int_or_default(ci->params, ctx->id("DQS_LI_DEL_VAL"), 0);
if (str_or_default(ci->params, ctx->id("DQS_LI_DEL_ADJ"), "PLUS") == "MINUS")
li_del_value = (256 - li_del_value) & 0xFF;
int lo_del_value = int_or_default(ci->params, ctx->id("DQS_LO_DEL_VAL"), 0);
if (str_or_default(ci->params, ctx->id("DQS_LO_DEL_ADJ"), "PLUS") == "MINUS")
lo_del_value = (256 - lo_del_value) & 0xFF;
tg.config.add_word("DQS.DQS_LI_DEL_VAL", int_to_bitvector(li_del_value, 8));
tg.config.add_word("DQS.DQS_LO_DEL_VAL", int_to_bitvector(lo_del_value, 8));
tg.config.add_enum("DQS.WRLOADN_USED", get_net_or_empty(ci, id_WRLOADN) != nullptr ? "YES" : "NO");
tg.config.add_enum("DQS.RDLOADN_USED", get_net_or_empty(ci, id_RDLOADN) != nullptr ? "YES" : "NO");
tg.config.add_enum("DQS.PAUSE_USED", get_net_or_empty(ci, id_PAUSE) != nullptr ? "YES" : "NO");
tg.config.add_enum("DQS.READ_USED",
(get_net_or_empty(ci, id_READ0) != nullptr || get_net_or_empty(ci, id_READ1) != nullptr)
? "YES"
: "NO");
tg.config.add_enum("DQS.DDRDEL", get_net_or_empty(ci, id_DDRDEL) != nullptr ? "DDRDEL" : "0");
tg.config.add_enum("DQS.GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED"));
cc.tilegroups.push_back(tg);
} else {
NPNR_ASSERT_FALSE("unsupported cell type");
}

View File

@ -1673,6 +1673,39 @@ class Ecp5Packer
return iol_ptr;
};
auto process_dqs_port = [&](CellInfo *prim, CellInfo *pio, CellInfo *iol, IdString port) {
NetInfo *sig = nullptr;
if (prim->ports.count(port))
sig = prim->ports[port].net;
if (sig == nullptr || sig->driver.cell == nullptr)
log_error("Port %s of cell '%s' cannot be disconnected, it must be driven by a DQSBUFM\n",
port.c_str(ctx), prim->name.c_str(ctx));
if (iol->ports.at(port).net != nullptr) {
if (iol->ports.at(port).net != sig) {
log_error("IOLOGIC '%s' has conflicting %s signals '%s' and '%s'\n", iol->name.c_str(ctx),
port.c_str(ctx), iol->ports[port].net->name.c_str(ctx), sig->name.c_str(ctx));
}
} else {
bool dqsr;
int dqsgroup;
bool has_dqs = ctx->getPIODQSGroup(get_pio_bel(pio, prim), dqsr, dqsgroup);
if (!has_dqs)
log_error("Primitive '%s' cannot be connected to top level port '%s' as the associated pin is not "
"in any DQS group",
prim->name.c_str(ctx), pio->name.c_str(ctx));
if (sig->driver.cell->type != id_DQSBUFM || sig->driver.port != port)
log_error("Port %s of cell '%s' must be driven by port %s of a DQSBUFM", port.c_str(ctx),
prim->name.c_str(ctx), port.c_str(ctx));
auto &driver_group = dqsbuf_dqsg.at(sig->driver.cell->name);
if (driver_group.first != dqsr || driver_group.second != dqsgroup)
log_error("DQS group mismatch, port %s of '%s' in group %cDQ%d is driven by DQSBUFM '%s' in group "
"%cDQ%d\n",
port.c_str(ctx), prim->name.c_str(ctx), dqsr ? 'R' : 'L', dqsgroup,
sig->driver.cell->name.c_str(ctx), driver_group.first ? 'R' : 'L', driver_group.second);
replace_port(prim, port, iol, port);
}
};
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("IDDRX1F")) {