aboutsummaryrefslogtreecommitdiffstats
path: root/ecp5/bitstream.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ecp5/bitstream.cc')
-rw-r--r--ecp5/bitstream.cc238
1 files changed, 229 insertions, 9 deletions
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index df16946d..a9c82524 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -34,6 +34,18 @@
NEXTPNR_NAMESPACE_BEGIN
+namespace BaseConfigs {
+void config_empty_lfe5u_25f(ChipConfig &cc);
+void config_empty_lfe5u_45f(ChipConfig &cc);
+void config_empty_lfe5u_85f(ChipConfig &cc);
+void config_empty_lfe5um_25f(ChipConfig &cc);
+void config_empty_lfe5um_45f(ChipConfig &cc);
+void config_empty_lfe5um_85f(ChipConfig &cc);
+void config_empty_lfe5um5g_25f(ChipConfig &cc);
+void config_empty_lfe5um5g_45f(ChipConfig &cc);
+void config_empty_lfe5um5g_85f(ChipConfig &cc);
+} // namespace BaseConfigs
+
// Convert an absolute wire name to a relative Trellis one
static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire)
{
@@ -538,8 +550,37 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
config_file >> cc;
} else {
- cc.chip_name = ctx->getChipName();
- // TODO: .bit metadata
+ switch (ctx->args.type) {
+ case ArchArgs::LFE5U_25F:
+ BaseConfigs::config_empty_lfe5u_25f(cc);
+ break;
+ case ArchArgs::LFE5U_45F:
+ BaseConfigs::config_empty_lfe5u_45f(cc);
+ break;
+ case ArchArgs::LFE5U_85F:
+ BaseConfigs::config_empty_lfe5u_85f(cc);
+ break;
+ case ArchArgs::LFE5UM_25F:
+ BaseConfigs::config_empty_lfe5um_25f(cc);
+ break;
+ case ArchArgs::LFE5UM_45F:
+ BaseConfigs::config_empty_lfe5um_45f(cc);
+ break;
+ case ArchArgs::LFE5UM_85F:
+ BaseConfigs::config_empty_lfe5um_85f(cc);
+ break;
+ case ArchArgs::LFE5UM5G_25F:
+ BaseConfigs::config_empty_lfe5um5g_25f(cc);
+ break;
+ case ArchArgs::LFE5UM5G_45F:
+ BaseConfigs::config_empty_lfe5um5g_45f(cc);
+ break;
+ case ArchArgs::LFE5UM5G_85F:
+ BaseConfigs::config_empty_lfe5um5g_85f(cc);
+ break;
+ default:
+ NPNR_ASSERT_FALSE("Unsupported device type");
+ }
}
// Clear out DCU tieoffs in base config if DCU used
@@ -578,7 +619,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
// Find bank voltages
std::unordered_map<int, IOVoltage> bankVcc;
- std::unordered_map<int, bool> bankLvds;
+ std::unordered_map<int, bool> bankLvds, bankVref;
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
@@ -587,7 +628,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT");
std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33");
- if (dir != "INPUT") {
+ if (dir != "INPUT" || is_referenced(ioType_from_str(iotype))) {
IOVoltage vcc = get_vccio(ioType_from_str(iotype));
if (bankVcc.find(bank) != bankVcc.end()) {
// TODO: strong and weak constraints
@@ -603,6 +644,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
if (iotype == "LVDS")
bankLvds[bank] = true;
+ if ((dir == "INPUT" || dir == "BIDIR") && is_referenced(ioType_from_str(iotype)))
+ bankVref[bank] = true;
}
}
@@ -614,17 +657,67 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
std::string type = tile.second;
if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") {
int bank = std::stoi(type.substr(7));
- if (bankVcc.find(bank) != bankVcc.end())
- cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank]));
+ if (bankVcc.find(bank) != bankVcc.end()) {
+ if (bankVcc[bank] == IOVoltage::VCC_1V35)
+ cc.tiles[tile.first].add_enum("BANK.VCCIO", "1V2");
+ else
+ cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank]));
+ }
if (bankLvds[bank]) {
cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON");
cc.tiles[tile.first].add_enum("BANK.LVDSO", "ON");
}
+ if (bankVref[bank]) {
+ cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON");
+ cc.tiles[tile.first].add_enum("BANK.VREF", "ON");
+ }
}
}
}
}
+ // Create dummy outputs used as Vref input buffer for banks where Vref is used
+ for (auto bv : bankVref) {
+ if (!bv.second)
+ continue;
+ BelId vrefIO = ctx->getPioByFunctionName(fmt_str("VREF1_" << bv.first));
+ if (vrefIO == BelId())
+ log_error("unable to find VREF input for bank %d\n", bv.first);
+ if (!ctx->checkBelAvail(vrefIO)) {
+ CellInfo *bound = ctx->getBoundBelCell(vrefIO);
+ if (bound != nullptr)
+ log_error("VREF pin %s of bank %d is occupied by IO '%s'\n", ctx->getBelPackagePin(vrefIO).c_str(),
+ bv.first, bound->name.c_str(ctx));
+ else
+ log_error("VREF pin %s of bank %d is unavailable\n", ctx->getBelPackagePin(vrefIO).c_str(), bv.first);
+ }
+ log_info("Using pin %s as VREF for bank %d\n", ctx->getBelPackagePin(vrefIO).c_str(), bv.first);
+ std::string pio_tile = get_pio_tile(ctx, vrefIO);
+
+ std::string iotype;
+ switch (bankVcc[bv.first]) {
+ case IOVoltage::VCC_1V2:
+ iotype = "HSUL12";
+ break;
+ case IOVoltage::VCC_1V35:
+ iotype = "SSTL18_I";
+ break;
+ case IOVoltage::VCC_1V5:
+ iotype = "SSTL18_I";
+ break;
+ case IOVoltage::VCC_1V8:
+ iotype = "SSTL18_I";
+ break;
+ default:
+ log_error("Referenced inputs are not supported with bank VccIO of %s.\n",
+ iovoltage_to_str(bankVcc[bv.first]).c_str());
+ }
+
+ std::string pio = ctx->locInfo(vrefIO)->bel_data[vrefIO.index].name.get();
+ cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", "OUTPUT_" + iotype);
+ cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE");
+ }
+
// Configure slices
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
@@ -727,9 +820,13 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
// cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
cc.tiles[pio_tile].add_enum(other + ".PULLMODE", "NONE");
cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE");
+ } else if (is_referenced(ioType_from_str(iotype))) {
+ cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE");
}
if (dir != "INPUT" &&
- (ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) {
+ (ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr) &&
+ (ci->ports.find(ctx->id("IOLTO")) == ci->ports.end() ||
+ ci->ports.at(ctx->id("IOLTO")).net == nullptr)) {
// Tie tristate low if unconnected for outputs or bidir
std::string jpt = fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/JPADDT" << pio.back());
WireId jpt_wire = ctx->getWireByName(ctx->id(jpt));
@@ -740,11 +837,43 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get();
cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0");
}
- if (dir == "INPUT" && !is_differential(ioType_from_str(iotype))) {
+ if (dir == "INPUT" && !is_differential(ioType_from_str(iotype)) &&
+ !is_referenced(ioType_from_str(iotype))) {
cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON");
}
- if (ci->attrs.count(ctx->id("SLEWRATE")))
+ if (ci->attrs.count(ctx->id("SLEWRATE")) && !is_referenced(ioType_from_str(iotype)))
cc.tiles[pio_tile].add_enum(pio + ".SLEWRATE", str_or_default(ci->attrs, ctx->id("SLEWRATE"), "SLOW"));
+ if (ci->attrs.count(ctx->id("PULLMODE")))
+ cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", str_or_default(ci->attrs, ctx->id("PULLMODE"), "NONE"));
+ if (ci->attrs.count(ctx->id("DIFFRESISTOR")))
+ cc.tiles[pio_tile].add_enum(pio + ".DIFFRESISTOR",
+ str_or_default(ci->attrs, ctx->id("DIFFRESISTOR"), "OFF"));
+ if (ci->attrs.count(ctx->id("TERMINATION"))) {
+ auto vccio = get_vccio(ioType_from_str(iotype));
+ switch (vccio) {
+ case IOVoltage::VCC_1V8:
+ cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V8",
+ str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
+ break;
+ case IOVoltage::VCC_1V5:
+ cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V5",
+ str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
+ break;
+ case IOVoltage::VCC_1V35:
+ cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V35",
+ str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
+ break;
+ default:
+ log_error("TERMINATION is not supported with Vcc = %s (on PIO %s)\n",
+ iovoltage_to_str(vccio).c_str(), ci->name.c_str(ctx));
+ }
+ }
+ std::string datamux_oddr = str_or_default(ci->params, ctx->id("DATAMUX_ODDR"), "PADDO");
+ if (datamux_oddr != "PADDO")
+ cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_ODDR", datamux_oddr);
+ std::string datamux_mddr = str_or_default(ci->params, ctx->id("DATAMUX_MDDR"), "PADDO");
+ if (datamux_mddr != "PADDO")
+ cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_MDDR", datamux_mddr);
} else if (ci->type == ctx->id("DCCA")) {
// Nothing to do
} else if (ci->type == ctx->id("DP16KD")) {
@@ -1078,6 +1207,18 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_ENABLE_FILTEROPAMP"), 0), 1));
cc.tilegroups.push_back(tg);
+ } else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) {
+ Loc pio_loc = ctx->getBelLocation(ci->bel);
+ pio_loc.z -= ci->type == id_SIOLOGIC ? 2 : 4;
+ std::string pic_tile = get_pic_tile(ctx, ctx->getBelByLocation(pio_loc));
+ std::string prim = std::string("IOLOGIC") + "ABCD"[pio_loc.z];
+ for (auto &param : ci->params) {
+ if (param.first == ctx->id("DELAY.DEL_VALUE"))
+ cc.tiles[pic_tile].add_word(prim + "." + param.first.str(ctx),
+ int_to_bitvector(std::stoi(param.second), 7));
+ else
+ cc.tiles[pic_tile].add_enum(prim + "." + param.first.str(ctx), param.second);
+ }
} else if (ci->type == id_DCUA) {
TileGroup tg;
tg.tiles = get_dcu_tiles(ctx, ci->bel);
@@ -1100,6 +1241,85 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
std::string tname = ctx->getTileByTypeAndLocation(loc.y + 1, loc.x, "BMID_0H");
cc.tiles[tname].add_enum("PCSCLKDIV" + std::to_string(loc.z),
str_or_default(ci->params, ctx->id("GSR"), "ENABLED"));
+ } else if (ci->type == id_DTR) {
+ cc.tiles[ctx->getTileByType("DTR")].add_enum("DTR.MODE", "DTR");
+ } else if (ci->type == id_OSCG) {
+ int div = int_or_default(ci->params, ctx->id("DIV"), 128);
+ if (div == 128)
+ div = 127;
+ cc.tiles[ctx->getTileByType("EFB0_PICB0")].add_enum("OSC.DIV", std::to_string(div));
+ cc.tiles[ctx->getTileByType("EFB1_PICB1")].add_enum("OSC.DIV", std::to_string(div));
+ cc.tiles[ctx->getTileByType("EFB1_PICB1")].add_enum("OSC.MODE", "OSCG");
+ cc.tiles[ctx->getTileByType("EFB1_PICB1")].add_enum("CCLK.MODE", "_NONE_");
+ } else if (ci->type == id_USRMCLK) {
+ cc.tiles[ctx->getTileByType("EFB3_PICB1")].add_enum("CCLK.MODE", "USRMCLK");
+ } else if (ci->type == id_GSR) {
+ cc.tiles[ctx->getTileByType("EFB0_PICB0")].add_enum(
+ "GSR.GSRMODE", str_or_default(ci->params, ctx->id("MODE"), "ACTIVE_HIGH"));
+ cc.tiles[ctx->getTileByType("VIQ_BUF")].add_enum("GSR.SYNCMODE",
+ str_or_default(ci->params, ctx->id("SYNCMODE"), "ASYNC"));
+ } else if (ci->type == id_JTAGG) {
+ cc.tiles[ctx->getTileByType("EFB0_PICB0")].add_enum("JTAG.ER1",
+ str_or_default(ci->params, ctx->id("ER1"), "ENABLED"));
+ cc.tiles[ctx->getTileByType("EFB0_PICB0")].add_enum("JTAG.ER2",
+ str_or_default(ci->params, ctx->id("ER2"), "ENABLED"));
+ } else if (ci->type == id_CLKDIVF) {
+ Loc loc = ctx->getBelLocation(ci->bel);
+ bool r = loc.x > 5;
+ std::string clkdiv = std::string("CLKDIV_") + (r ? "R" : "L") + std::to_string(loc.z);
+ std::string tile = ctx->getTileByType(std::string("ECLK_") + (r ? "R" : "L"));
+ 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.x < 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 if (ci->type == id_ECLKSYNCB) {
+ Loc loc = ctx->getBelLocation(ci->bel);
+ bool r = loc.x > 5;
+ std::string eclksync = ctx->locInfo(bel)->bel_data[bel.index].name.get();
+ std::string tile = ctx->getTileByType(std::string("ECLK_") + (r ? "R" : "L"));
+ if (get_net_or_empty(ci, id_STOP) != nullptr)
+ cc.tiles[tile].add_enum(eclksync + ".MODE", "ECLKSYNCB");
+ } else if (ci->type == id_DDRDLL) {
+ Loc loc = ctx->getBelLocation(ci->bel);
+ bool u = loc.y<15, r = loc.x> 15;
+ std::string tiletype = fmt_str("DDRDLL_" << (u ? 'U' : 'L') << (r ? 'R' : 'L'));
+ if (ctx->args.type == ArchArgs::LFE5U_25F || ctx->args.type == ArchArgs::LFE5UM_25F ||
+ ctx->args.type == ArchArgs::LFE5UM5G_25F)
+ tiletype += "A";
+ std::string tile = ctx->getTileByType(tiletype);
+ cc.tiles[tile].add_enum("DDRDLL.MODE", "DDRDLLA");
+ cc.tiles[tile].add_enum("DDRDLL.GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED"));
+ cc.tiles[tile].add_enum("DDRDLL.FORCE_MAX_DELAY",
+ str_or_default(ci->params, ctx->id("FORCE_MAX_DELAY"), "NO"));
} else {
NPNR_ASSERT_FALSE("unsupported cell type");
}