aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2019-02-12 13:13:06 +0000
committerDavid Shah <davey1576@gmail.com>2019-02-24 10:28:25 +0100
commit44023612465703682d883e29e0de9fb900e6e531 (patch)
treea0048adc4bb0c3f72b3ec1c47d0fc705386c8b7b
parenteb45956d0ee7dffac4c1c0b242f15724a3e0a352 (diff)
downloadnextpnr-44023612465703682d883e29e0de9fb900e6e531.tar.gz
nextpnr-44023612465703682d883e29e0de9fb900e6e531.tar.bz2
nextpnr-44023612465703682d883e29e0de9fb900e6e531.zip
ecp5: Helper functions and bitstream for DQS
Signed-off-by: David Shah <dave@ds0.me>
-rw-r--r--ecp5/bitstream.cc30
-rw-r--r--ecp5/pack.cc33
2 files changed, 63 insertions, 0 deletions
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index e2d9888a..789957c6 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -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");
}
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 7abad40b..7538989c 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -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")) {