aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shah <davey1576@gmail.com>2018-10-17 11:14:27 +0100
committerGitHub <noreply@github.com>2018-10-17 11:14:27 +0100
commitb53a4862db99e41f16c06425f314a1a398a21985 (patch)
treedbe277f6d9487eab64715e4c14ce9be230820c38
parent529a595157a2eef24f8529b0de0c504a40ed503b (diff)
parent1cde2080902c25d2a59e8ebb5b0b23a90f693e8e (diff)
downloadnextpnr-b53a4862db99e41f16c06425f314a1a398a21985.tar.gz
nextpnr-b53a4862db99e41f16c06425f314a1a398a21985.tar.bz2
nextpnr-b53a4862db99e41f16c06425f314a1a398a21985.zip
Merge pull request #89 from YosysHQ/ecp5_bram
ECP5 BRAM support
-rw-r--r--common/place_common.cc6
-rw-r--r--ecp5/arch.cc62
-rw-r--r--ecp5/arch.h6
-rw-r--r--ecp5/bitstream.cc282
-rw-r--r--ecp5/config.cc46
-rw-r--r--ecp5/config.h10
-rw-r--r--ecp5/constids.inc118
-rw-r--r--ecp5/globals.cc15
-rw-r--r--ecp5/main.cc18
-rw-r--r--ecp5/pack.cc66
10 files changed, 619 insertions, 10 deletions
diff --git a/common/place_common.cc b/common/place_common.cc
index 120e5e00..da8ab37d 100644
--- a/common/place_common.cc
+++ b/common/place_common.cc
@@ -237,6 +237,12 @@ class ConstraintLegaliseWorker
return false;
}
}
+ // Don't place at tiles where any strongly bound Bels exist, as we might need to rip them up later
+ for (auto tilebel : ctx->getBelsByTile(loc.x, loc.y)) {
+ CellInfo *tcell = ctx->getBoundBelCell(tilebel);
+ if (tcell && tcell->belStrength >= STRENGTH_STRONG)
+ return false;
+ }
usedLocations.insert(loc);
for (auto child : cell->constr_children) {
IncreasingDiameterSearch xSearch, ySearch, zSearch;
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 9c059005..1be76c07 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -19,6 +19,7 @@
*/
#include <algorithm>
+#include <boost/range/adaptor/reversed.hpp>
#include <cmath>
#include <cstring>
#include "gfx.h"
@@ -76,11 +77,13 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unsupported ECP5 chip type.\n");
}
#else
- if (args.type == ArchArgs::LFE5U_25F) {
+ if (args.type == ArchArgs::LFE5U_25F || args.type == ArchArgs::LFE5UM_25F || args.type == ArchArgs::LFE5UM5G_25F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_25k));
- } else if (args.type == ArchArgs::LFE5U_45F) {
+ } else if (args.type == ArchArgs::LFE5U_45F || args.type == ArchArgs::LFE5UM_45F ||
+ args.type == ArchArgs::LFE5UM5G_45F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_45k));
- } else if (args.type == ArchArgs::LFE5U_85F) {
+ } else if (args.type == ArchArgs::LFE5U_85F || args.type == ArchArgs::LFE5UM_85F ||
+ args.type == ArchArgs::LFE5UM5G_85F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_85k));
} else {
log_error("Unsupported ECP5 chip type.\n");
@@ -110,6 +113,18 @@ std::string Arch::getChipName() const
return "LFE5U-45F";
} else if (args.type == ArchArgs::LFE5U_85F) {
return "LFE5U-85F";
+ } else if (args.type == ArchArgs::LFE5UM_25F) {
+ return "LFE5UM-25F";
+ } else if (args.type == ArchArgs::LFE5UM_45F) {
+ return "LFE5UM-45F";
+ } else if (args.type == ArchArgs::LFE5UM_85F) {
+ return "LFE5UM-85F";
+ } else if (args.type == ArchArgs::LFE5UM5G_25F) {
+ return "LFE5UM5G-25F";
+ } else if (args.type == ArchArgs::LFE5UM5G_45F) {
+ return "LFE5UM5G-45F";
+ } else if (args.type == ArchArgs::LFE5UM5G_85F) {
+ return "LFE5UM5G-85F";
} else {
log_error("Unknown chip\n");
}
@@ -125,6 +140,18 @@ IdString Arch::archArgsToId(ArchArgs args) const
return id("lfe5u_45f");
if (args.type == ArchArgs::LFE5U_85F)
return id("lfe5u_85f");
+ if (args.type == ArchArgs::LFE5UM_25F)
+ return id("lfe5um_25f");
+ if (args.type == ArchArgs::LFE5UM_45F)
+ return id("lfe5um_45f");
+ if (args.type == ArchArgs::LFE5UM_85F)
+ return id("lfe5um_85f");
+ if (args.type == ArchArgs::LFE5UM5G_25F)
+ return id("lfe5um5g_25f");
+ if (args.type == ArchArgs::LFE5UM5G_45F)
+ return id("lfe5um5g_45f");
+ if (args.type == ArchArgs::LFE5UM5G_85F)
+ return id("lfe5um5g_85f");
return IdString();
}
@@ -531,6 +558,19 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
return true;
}
return false;
+ } else if (cell->type == id_DP16KD) {
+ if (fromPort == id_CLKA) {
+ if (toPort.str(this).substr(0, 3) == "DOA") {
+ delay.delay = 4260;
+ return true;
+ }
+ } else if (fromPort == id_CLKB) {
+ if (toPort.str(this).substr(0, 3) == "DOB") {
+ delay.delay = 4280;
+ return true;
+ }
+ }
+ return false;
} else {
return false;
}
@@ -582,6 +622,22 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
if (port == id_CLKO)
return TMG_COMB_OUTPUT;
return TMG_IGNORE;
+ } else if (cell->type == id_DP16KD) {
+ if (port == id_CLKA || port == id_CLKB)
+ return TMG_CLOCK_INPUT;
+ std::string port_name = port.str(this);
+ for (auto c : boost::adaptors::reverse(port_name)) {
+ if (std::isdigit(c))
+ continue;
+ if (c == 'A')
+ clockPort = id_CLKA;
+ else if (c == 'B')
+ clockPort = id_CLKB;
+ else
+ NPNR_ASSERT_FALSE_STR("bad ram port");
+ return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
+ }
+ NPNR_ASSERT_FALSE_STR("no timing type for RAM port '" + port.str(this) + "'");
} else {
NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'");
}
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 9eac3c9f..35c8df19 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -392,6 +392,12 @@ struct ArchArgs
LFE5U_25F,
LFE5U_45F,
LFE5U_85F,
+ LFE5UM_25F,
+ LFE5UM_45F,
+ LFE5UM_85F,
+ LFE5UM5G_25F,
+ LFE5UM5G_45F,
+ LFE5UM5G_85F,
} type = NONE;
std::string package;
int speed = 6;
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index 296ea753..29b12c86 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -20,6 +20,8 @@
#include "bitstream.h"
#include <fstream>
+#include <iomanip>
+#include <regex>
#include <streambuf>
#include "config.h"
@@ -61,6 +63,101 @@ static std::vector<bool> int_to_bitvector(int val, int size)
return bv;
}
+static std::vector<bool> str_to_bitvector(std::string str, int size)
+{
+ std::vector<bool> bv;
+ bv.resize(size, 0);
+ if (str.substr(0, 2) != "0b")
+ log_error("error parsing value '%s', expected 0b prefix\n", str.c_str());
+ for (int i = 0; i < int(str.size()) - 2; i++) {
+ char c = str.at((str.size() - i) - 1);
+ NPNR_ASSERT(c == '0' || c == '1');
+ bv.at(i) = (c == '1');
+ }
+ return bv;
+}
+
+// Tie a wire using the CIB ties
+static void tie_cib_signal(Context *ctx, ChipConfig &cc, WireId wire, bool value)
+{
+ static const std::regex cib_re("J([A-D]|CE|LSR|CLK)[0-7]");
+ WireId cibsig = wire;
+ std::string basename = ctx->getWireBasename(wire).str(ctx);
+
+ while (!std::regex_match(basename, cib_re)) {
+ auto uphill = ctx->getPipsUphill(cibsig);
+ NPNR_ASSERT(uphill.begin() != uphill.end()); // At least one uphill pip
+ auto iter = uphill.begin();
+ cibsig = ctx->getPipSrcWire(*iter);
+ basename = ctx->getWireBasename(cibsig).str(ctx);
+ ++iter;
+ NPNR_ASSERT(!(iter != uphill.end())); // Exactly one uphill pip
+ }
+
+ bool out_value = value;
+ if (basename.substr(0, 3) == "JCE")
+ NPNR_ASSERT(value);
+ if (basename.substr(0, 4) == "JCLK" || basename.substr(0, 4) == "JLSR") {
+ NPNR_ASSERT(value);
+ out_value = 0;
+ }
+
+ for (const auto &tile : ctx->getTilesAtLocation(cibsig.location.y, cibsig.location.x)) {
+ if (tile.second.substr(0, 3) == "CIB" || tile.second.substr(0, 4) == "VCIB") {
+
+ cc.tiles[tile.first].add_enum("CIB." + basename + "MUX", out_value ? "1" : "0");
+ return;
+ }
+ }
+ NPNR_ASSERT_FALSE("CIB tile not found at location");
+}
+
+inline int chtohex(char c)
+{
+ static const std::string hex = "0123456789ABCDEF";
+ return hex.find(c);
+}
+
+std::vector<bool> parse_init_str(const std::string &str, int length)
+{
+ // Parse a string that may be binary or hex
+ std::vector<bool> result;
+ result.resize(length, false);
+ if (str.substr(0, 2) == "0x") {
+ // Lattice style hex string
+ if (int(str.length()) > (2 + ((length + 3) / 4)))
+ log_error("hex string value too long, expected up to %d chars and found %d.\n", (2 + ((length + 3) / 4)),
+ int(str.length()));
+ for (int i = 0; i < int(str.length()) - 2; i++) {
+ char c = str.at((str.size() - i) - 1);
+ int nibble = chtohex(c);
+ result.at(i * 4) = nibble & 0x1;
+ result.at(i * 4 + 1) = nibble & 0x2;
+ result.at(i * 4 + 2) = nibble & 0x4;
+ result.at(i * 4 + 3) = nibble & 0x8;
+ }
+ } else {
+ // Yosys style binary string
+ if (int(str.length()) > length)
+ log_error("hex string value too long, expected up to %d bits and found %d.\n", length, int(str.length()));
+ for (int i = 0; i < int(str.length()); i++) {
+ char c = str.at((str.size() - i) - 1);
+ NPNR_ASSERT(c == '0' || c == '1' || c == 'X' || c == 'x');
+ result.at(i) = (c == '1');
+ }
+ }
+ return result;
+}
+
+inline uint16_t bit_reverse(uint16_t x, int size)
+{
+ uint16_t y = 0;
+ for (int i = 0; i < size; i++)
+ if (x & (1 << i))
+ y |= (1 << ((size - 1) - i));
+ return y;
+}
+
// Get the PIO tile corresponding to a PIO bel
static std::string get_pio_tile(Context *ctx, BelId bel)
{
@@ -144,6 +241,77 @@ static std::string get_pic_tile(Context *ctx, BelId bel)
}
}
+// Get the list of tiles corresponding to a blockram
+std::vector<std::string> get_bram_tiles(Context *ctx, BelId bel)
+{
+ std::vector<std::string> tiles;
+ Loc loc = ctx->getBelLocation(bel);
+
+ static const std::set<std::string> ebr0 = {"MIB_EBR0", "EBR_CMUX_UR", "EBR_CMUX_LR", "EBR_CMUX_LR_25K"};
+ static const std::set<std::string> ebr8 = {"MIB_EBR8", "EBR_SPINE_UL1", "EBR_SPINE_UR1", "EBR_SPINE_LL1",
+ "EBR_CMUX_UL", "EBR_SPINE_LL0", "EBR_CMUX_LL", "EBR_SPINE_LR0",
+ "EBR_SPINE_LR1", "EBR_CMUX_LL_25K", "EBR_SPINE_UL2", "EBR_SPINE_UL0",
+ "EBR_SPINE_UR2", "EBR_SPINE_LL2", "EBR_SPINE_LR2", "EBR_SPINE_UR0"};
+
+ switch (loc.z) {
+ case 0:
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, ebr0));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR1"));
+ break;
+ case 1:
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "MIB_EBR2"));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR3"));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 2, "MIB_EBR4"));
+ break;
+ case 2:
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "MIB_EBR4"));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR5"));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 2, "MIB_EBR6"));
+ break;
+ case 3:
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "MIB_EBR6"));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR7"));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 2, ebr8));
+ break;
+ default:
+ NPNR_ASSERT_FALSE("bad EBR z loc");
+ }
+ return tiles;
+}
+
+void fix_tile_names(Context *ctx, ChipConfig &cc)
+{
+ // Remove the V prefix/suffix on certain tiles if device is a SERDES variant
+ if (ctx->args.type == ArchArgs::LFE5UM_25F || ctx->args.type == ArchArgs::LFE5UM_45F ||
+ ctx->args.type == ArchArgs::LFE5UM_85F || ctx->args.type == ArchArgs::LFE5UM5G_25F ||
+ ctx->args.type == ArchArgs::LFE5UM5G_45F || ctx->args.type == ArchArgs::LFE5UM5G_85F) {
+ std::map<std::string, std::string> tiletype_xform;
+ for (const auto &tile : cc.tiles) {
+ std::string newname = tile.first;
+ auto vcib = tile.first.find("VCIB");
+ if (vcib != std::string::npos) {
+ // Remove the V
+ newname.erase(vcib);
+ tiletype_xform[tile.first] = newname;
+ } else if (tile.first.back() == 'V') {
+ // BMID_0V or BMID_2V
+ if (tile.first.at(tile.first.size() - 2) == '0') {
+ newname.at(tile.first.size() - 1) = 'H';
+ tiletype_xform[tile.first] = newname;
+ } else if (tile.first.at(tile.first.size() - 2) == '2') {
+ newname.pop_back();
+ tiletype_xform[tile.first] = newname;
+ }
+ }
+ }
+ // Apply the name changes
+ for (auto xform : tiletype_xform) {
+ cc.tiles[xform.second] = cc.tiles.at(xform.first);
+ cc.tiles.erase(xform.first);
+ }
+ }
+}
+
void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file)
{
ChipConfig cc;
@@ -289,11 +457,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "DPRAM" && slice == "SLICEA") {
cc.tiles[tname].add_enum(slice + ".WREMUX", str_or_default(ci->params, ctx->id("WREMUX"), "WRE"));
- NetInfo *wcknet = nullptr;
std::string wckmux = str_or_default(ci->params, ctx->id("WCKMUX"), "WCK");
wckmux = (wckmux == "WCK") ? "CLK" : wckmux;
- if (ci->ports.find(ctx->id("WCK")) != ci->ports.end() && ci->ports.at(ctx->id("WCK")).net != nullptr)
- wcknet = ci->ports.at(ctx->id("WCK")).net;
cc.tiles[tname].add_enum("CLK1.CLKMUX", wckmux);
}
@@ -344,11 +509,120 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
} else if (ci->type == ctx->id("DCCA")) {
// Nothing to do
+ } else if (ci->type == ctx->id("DP16KD")) {
+ TileGroup tg;
+ Loc loc = ctx->getBelLocation(ci->bel);
+ tg.tiles = get_bram_tiles(ctx, ci->bel);
+ std::string ebr = "EBR" + std::to_string(loc.z);
+
+ tg.config.add_enum(ebr + ".MODE", "DP16KD");
+
+ auto csd_a = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_A"), "0b000"), 3),
+ csd_b = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_B"), "0b000"), 3);
+
+ tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A", str_or_default(ci->params, ctx->id("DATA_WIDTH_A"), "18"));
+ tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B", str_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "18"));
+
+ tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_A",
+ str_or_default(ci->params, ctx->id("WRITEMODE_A"), "NORMAL"));
+ tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_B",
+ str_or_default(ci->params, ctx->id("WRITEMODE_B"), "NORMAL"));
+
+ tg.config.add_enum(ebr + ".REGMODE_A", str_or_default(ci->params, ctx->id("REGMODE_A"), "NOREG"));
+ tg.config.add_enum(ebr + ".REGMODE_B", str_or_default(ci->params, ctx->id("REGMODE_B"), "NOREG"));
+
+ tg.config.add_enum(ebr + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC"));
+ tg.config.add_enum(ebr + ".ASYNC_RESET_RELEASE",
+ str_or_default(ci->params, ctx->id("ASYNC_RESET_RELEASE"), "SYNC"));
+ tg.config.add_enum(ebr + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED"));
+
+ tg.config.add_word(ebr + ".WID",
+ int_to_bitvector(bit_reverse(int_or_default(ci->attrs, ctx->id("WID"), 0), 9), 9));
+
+ // Tie signals as appropriate
+ for (auto port : ci->ports) {
+ if (port.second.net == nullptr && port.second.type == PORT_IN) {
+ if (port.first == id_CLKA || port.first == id_CLKB || port.first == id_WEA ||
+ port.first == id_WEB || port.first == id_RSTA || port.first == id_RSTB) {
+ // CIB clock or LSR. Tie to "1" (also 0 in prjtrellis db?) in CIB
+ // If MUX doesn't exist, set to INV to emulate default 0
+ tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true);
+ if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX")))
+ ci->params[ctx->id(port.first.str(ctx) + "MUX")] = "INV";
+ } else if (port.first == id_CEA || port.first == id_CEB || port.first == id_OCEA ||
+ port.first == id_OCEB) {
+ // CIB CE. Tie to "1" in CIB
+ // If MUX doesn't exist, set to passthru to emulate default 1
+ tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true);
+ if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX")))
+ ci->params[ctx->id(port.first.str(ctx) + "MUX")] = port.first.str(ctx);
+ } else if (port.first == id_CSA0 || port.first == id_CSA1 || port.first == id_CSA2 ||
+ port.first == id_CSB0 || port.first == id_CSB1 || port.first == id_CSB2) {
+ // CIB CE. Tie to "1" in CIB.
+ // If MUX doesn't exist, set to INV to emulate default 0
+ tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true);
+ if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX")))
+ ci->params[ctx->id(port.first.str(ctx) + "MUX")] = "INV";
+ } else {
+ // CIB ABCD signal
+ // Tie signals low unless explicit MUX param specified
+ bool value = bool_or_default(ci->params, ctx->id(port.first.str(ctx) + "MUX"), false);
+ tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), value);
+ }
+ }
+ }
+
+ // Invert CSDECODE bits to emulate inversion muxes on CSA/CSB signals
+ for (auto port : {std::make_pair("CSA", std::ref(csd_a)), std::make_pair("CSB", std::ref(csd_b))}) {
+ for (int bit = 0; bit < 3; bit++) {
+ std::string sig = port.first + std::to_string(bit);
+ if (str_or_default(ci->params, ctx->id(sig + "MUX"), sig) == "INV")
+ port.second.at(bit) = !port.second.at(bit);
+ }
+ }
+
+ tg.config.add_enum(ebr + ".CLKAMUX", str_or_default(ci->params, ctx->id("CLKAMUX"), "CLKA"));
+ tg.config.add_enum(ebr + ".CLKBMUX", str_or_default(ci->params, ctx->id("CLKBMUX"), "CLKB"));
+
+ tg.config.add_enum(ebr + ".RSTAMUX", str_or_default(ci->params, ctx->id("RSTAMUX"), "RSTA"));
+ tg.config.add_enum(ebr + ".RSTBMUX", str_or_default(ci->params, ctx->id("RSTBMUX"), "RSTB"));
+ tg.config.add_enum(ebr + ".WEAMUX", str_or_default(ci->params, ctx->id("WEAMUX"), "WEA"));
+ tg.config.add_enum(ebr + ".WEBMUX", str_or_default(ci->params, ctx->id("WEBMUX"), "WEB"));
+
+ tg.config.add_enum(ebr + ".CEAMUX", str_or_default(ci->params, ctx->id("CEAMUX"), "CEA"));
+ tg.config.add_enum(ebr + ".CEBMUX", str_or_default(ci->params, ctx->id("CEBMUX"), "CEB"));
+ tg.config.add_enum(ebr + ".OCEAMUX", str_or_default(ci->params, ctx->id("OCEAMUX"), "OCEA"));
+ tg.config.add_enum(ebr + ".OCEBMUX", str_or_default(ci->params, ctx->id("OCEBMUX"), "OCEB"));
+
+ tg.config.add_word(ebr + ".CSDECODE_A", csd_a);
+ tg.config.add_word(ebr + ".CSDECODE_B", csd_b);
+
+ std::vector<uint16_t> init_data;
+ init_data.resize(2048, 0x0);
+ // INIT_00 .. INIT_3F
+ for (int i = 0; i <= 0x3F; i++) {
+ IdString param = ctx->id("INITVAL_" +
+ fmt_str(std::hex << std::uppercase << std::setw(2) << std::setfill('0') << i));
+ auto value = parse_init_str(str_or_default(ci->params, param, "0"), 320);
+ for (int j = 0; j < 16; j++) {
+ // INIT parameter consists of 16 18-bit words with 2-bit padding
+ int ofs = 20 * j;
+ for (int k = 0; k < 18; k++) {
+ if (value.at(ofs + k))
+ init_data.at(i * 32 + j * 2 + (k / 9)) |= (1 << (k % 9));
+ }
+ }
+ }
+ int wid = int_or_default(ci->attrs, ctx->id("WID"), 0);
+ NPNR_ASSERT(!cc.bram_data.count(wid));
+ cc.bram_data[wid] = init_data;
+ cc.tilegroups.push_back(tg);
} else {
NPNR_ASSERT_FALSE("unsupported cell type");
}
}
-
+ // Fixup tile names
+ fix_tile_names(ctx, cc);
// Configure chip
if (!text_config_file.empty()) {
std::ofstream out_config(text_config_file);
diff --git a/ecp5/config.cc b/ecp5/config.cc
index 826c16a9..c8f94857 100644
--- a/ecp5/config.cc
+++ b/ecp5/config.cc
@@ -19,6 +19,7 @@
#include "config.h"
#include <boost/range/adaptor/reversed.hpp>
+#include <iomanip>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -274,6 +275,28 @@ std::ostream &operator<<(std::ostream &out, const ChipConfig &cc)
out << std::endl;
}
}
+ for (const auto &bram : cc.bram_data) {
+ out << ".bram_init " << bram.first << std::endl;
+ std::ios_base::fmtflags f(out.flags());
+ for (size_t i = 0; i < bram.second.size(); i++) {
+ out << std::setw(3) << std::setfill('0') << std::hex << bram.second.at(i);
+ if (i % 8 == 7)
+ out << std::endl;
+ else
+ out << " ";
+ }
+ out.flags(f);
+ out << std::endl;
+ }
+ for (const auto &tg : cc.tilegroups) {
+ out << ".tile_group";
+ for (const auto &tile : tg.tiles) {
+ out << " " << tile;
+ }
+ out << std::endl;
+ out << tg.config;
+ out << std::endl;
+ }
return out;
}
@@ -294,6 +317,29 @@ std::istream &operator>>(std::istream &in, ChipConfig &cc)
TileConfig tc;
in >> tc;
cc.tiles[tilename] = tc;
+ } else if (verb == ".tile_group") {
+ TileGroup tg;
+ std::string line;
+ getline(in, line);
+ std::stringstream ss2(line);
+
+ std::string tile;
+ while (ss2) {
+ ss2 >> tile;
+ tg.tiles.push_back(tile);
+ }
+ in >> tg.config;
+ cc.tilegroups.push_back(tg);
+ } else if (verb == ".bram_init") {
+ uint16_t bram;
+ in >> bram;
+ std::ios_base::fmtflags f(in.flags());
+ while (!skip_check_eor(in)) {
+ uint16_t value;
+ in >> std::hex >> value;
+ cc.bram_data[bram].push_back(value);
+ }
+ in.flags(f);
} else {
log_error("unrecognised config entry %s\n", verb.c_str());
}
diff --git a/ecp5/config.h b/ecp5/config.h
index 3d2ef971..8b38de5d 100644
--- a/ecp5/config.h
+++ b/ecp5/config.h
@@ -98,6 +98,14 @@ std::ostream &operator<<(std::ostream &out, const TileConfig &tc);
std::istream &operator>>(std::istream &in, TileConfig &ce);
+// A group of tiles to configure at once for a particular feature that is split across tiles
+// TileGroups are currently for non-routing configuration only
+struct TileGroup
+{
+ std::vector<std::string> tiles;
+ TileConfig config;
+};
+
// This represents the configuration of a chip at a high level
class ChipConfig
{
@@ -105,6 +113,8 @@ class ChipConfig
std::string chip_name;
std::vector<std::string> metadata;
std::map<std::string, TileConfig> tiles;
+ std::vector<TileGroup> tilegroups;
+ std::map<uint16_t, std::vector<uint16_t>> bram_data;
};
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc);
diff --git a/ecp5/constids.inc b/ecp5/constids.inc
index bd55fa90..7880126b 100644
--- a/ecp5/constids.inc
+++ b/ecp5/constids.inc
@@ -54,3 +54,121 @@ X(SRMODE)
X(CLKI)
X(CLKO)
+
+X(DP16KD)
+X(DIA0)
+X(DIA1)
+X(DIA2)
+X(DIA3)
+X(DIA4)
+X(DIA5)
+X(DIA6)
+X(DIA7)
+X(DIA8)
+X(DIA9)
+X(DIA10)
+X(DIA11)
+X(DIA12)
+X(DIA13)
+X(DIA14)
+X(DIA15)
+X(DIA16)
+X(DIA17)
+X(ADA0)
+X(ADA1)
+X(ADA2)
+X(ADA3)
+X(ADA4)
+X(ADA5)
+X(ADA6)
+X(ADA7)
+X(ADA8)
+X(ADA9)
+X(ADA10)
+X(ADA11)
+X(ADA12)
+X(ADA13)
+X(CEA)
+X(OCEA)
+X(CLKA)
+X(WEA)
+X(CSA2)
+X(CSA1)
+X(CSA0)
+X(RSTA)
+X(DIB0)
+X(DIB1)
+X(DIB2)
+X(DIB3)
+X(DIB4)
+X(DIB5)
+X(DIB6)
+X(DIB7)
+X(DIB8)
+X(DIB9)
+X(DIB10)
+X(DIB11)
+X(DIB12)
+X(DIB13)
+X(DIB14)
+X(DIB15)
+X(DIB16)
+X(DIB17)
+X(ADB0)
+X(ADB1)
+X(ADB2)
+X(ADB3)
+X(ADB4)
+X(ADB5)
+X(ADB6)
+X(ADB7)
+X(ADB8)
+X(ADB9)
+X(ADB10)
+X(ADB11)
+X(ADB12)
+X(ADB13)
+X(CEB)
+X(OCEB)
+X(CLKB)
+X(WEB)
+X(CSB2)
+X(CSB1)
+X(CSB0)
+X(RSTB)
+X(DOA0)
+X(DOA1)
+X(DOA2)
+X(DOA3)
+X(DOA4)
+X(DOA5)
+X(DOA6)
+X(DOA7)
+X(DOA8)
+X(DOA9)
+X(DOA10)
+X(DOA11)
+X(DOA12)
+X(DOA13)
+X(DOA14)
+X(DOA15)
+X(DOA16)
+X(DOA17)
+X(DOB0)
+X(DOB1)
+X(DOB2)
+X(DOB3)
+X(DOB4)
+X(DOB5)
+X(DOB6)
+X(DOB7)
+X(DOB8)
+X(DOB9)
+X(DOB10)
+X(DOB11)
+X(DOB12)
+X(DOB13)
+X(DOB14)
+X(DOB15)
+X(DOB16)
+X(DOB17) \ No newline at end of file
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index 364e4bca..5e5a2a01 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -24,7 +24,7 @@
#include "cells.h"
#include "log.h"
#include "nextpnr.h"
-
+#include "place_common.h"
#define fmt_str(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
NEXTPNR_NAMESPACE_BEGIN
@@ -260,15 +260,24 @@ class Ecp5GlobalRouter
// Attempt to place a DCC
void place_dcc(CellInfo *dcc)
{
+ BelId best_bel;
+ wirelen_t best_wirelen = 9999999;
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) {
if (ctx->isValidBelForCell(dcc, bel)) {
ctx->bindBel(bel, dcc, STRENGTH_LOCKED);
- return;
+ float tns;
+ wirelen_t wirelen = get_net_metric(ctx, dcc->ports.at(id_CLKI).net, MetricType::WIRELENGTH, tns);
+ if (wirelen < best_wirelen) {
+ best_bel = bel;
+ best_wirelen = wirelen;
+ }
+ ctx->unbindBel(bel);
}
}
}
- NPNR_ASSERT_FALSE("failed to place dcca");
+ NPNR_ASSERT(best_bel != BelId());
+ ctx->bindBel(best_bel, dcc, STRENGTH_LOCKED);
}
// Insert a DCC into a net to promote it to a global
diff --git a/ecp5/main.cc b/ecp5/main.cc
index 5ad5a9bf..e71b0983 100644
--- a/ecp5/main.cc
+++ b/ecp5/main.cc
@@ -50,6 +50,12 @@ po::options_description ECP5CommandHandler::getArchOptions()
specific.add_options()("25k", "set device type to LFE5U-25F");
specific.add_options()("45k", "set device type to LFE5U-45F");
specific.add_options()("85k", "set device type to LFE5U-85F");
+ specific.add_options()("um-25k", "set device type to LFE5UM-25F");
+ specific.add_options()("um-45k", "set device type to LFE5UM-45F");
+ specific.add_options()("um-85k", "set device type to LFE5UM-85F");
+ specific.add_options()("um5g-25k", "set device type to LFE5UM5G-25F");
+ specific.add_options()("um5g-45k", "set device type to LFE5UM5G-45F");
+ specific.add_options()("um5g-85k", "set device type to LFE5UM5G-85F");
specific.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)");
specific.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
@@ -84,6 +90,18 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
chipArgs.type = ArchArgs::LFE5U_45F;
if (vm.count("85k"))
chipArgs.type = ArchArgs::LFE5U_85F;
+ if (vm.count("um-25k"))
+ chipArgs.type = ArchArgs::LFE5UM_25F;
+ if (vm.count("um-45k"))
+ chipArgs.type = ArchArgs::LFE5UM_45F;
+ if (vm.count("um-85k"))
+ chipArgs.type = ArchArgs::LFE5UM_85F;
+ if (vm.count("um5g-25k"))
+ chipArgs.type = ArchArgs::LFE5UM5G_25F;
+ if (vm.count("um5g-45k"))
+ chipArgs.type = ArchArgs::LFE5UM5G_45F;
+ if (vm.count("um5g-85k"))
+ chipArgs.type = ArchArgs::LFE5UM5G_85F;
if (vm.count("package"))
chipArgs.package = vm["package"].as<std::string>();
else
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 0045617b..ef65fd27 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -844,6 +844,19 @@ class Ecp5Packer
((!constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "LSR") ||
(constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "INV"))) {
uc->ports[user.port].net = nullptr;
+ } else if (uc->type == id_DP16KD) {
+ if (user.port == id_CLKA || user.port == id_CLKB || user.port == id_RSTA || user.port == id_RSTB ||
+ user.port == id_WEA || user.port == id_WEB || user.port == id_CEA || user.port == id_CEB ||
+ user.port == id_OCEA || user.port == id_OCEB || user.port == id_CSA0 || user.port == id_CSA1 ||
+ user.port == id_CSA2 || user.port == id_CSB0 || user.port == id_CSB1 || user.port == id_CSB2) {
+ // Connect to CIB CLK, LSR or CE. Default state is 1
+ uc->params[ctx->id(user.port.str(ctx) + "MUX")] = constval ? user.port.str(ctx) : "INV";
+ } else {
+ // Connected to CIB ABCD. Default state is bitstream configurable
+ uc->params[ctx->id(user.port.str(ctx) + "MUX")] = constval ? "1" : "0";
+ }
+ uc->ports[user.port].net = nullptr;
+
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
@@ -909,10 +922,63 @@ class Ecp5Packer
}
}
+ void autocreate_empty_port(CellInfo *cell, IdString port)
+ {
+ if (!cell->ports.count(port)) {
+ cell->ports[port].name = port;
+ cell->ports[port].net = nullptr;
+ cell->ports[port].type = PORT_IN;
+ }
+ }
+
+ // Pack EBR
+ void pack_ebr()
+ {
+ // Autoincrement WID (starting from 3 seems to match vendor behaviour?)
+ int wid = 3;
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->type == id_DP16KD) {
+ // Add ports, even if disconnected, to ensure correct tie-offs
+ for (int i = 0; i < 14; i++) {
+ autocreate_empty_port(ci, ctx->id("ADA" + std::to_string(i)));
+ autocreate_empty_port(ci, ctx->id("ADB" + std::to_string(i)));
+ }
+ for (int i = 0; i < 18; i++) {
+ autocreate_empty_port(ci, ctx->id("DIA" + std::to_string(i)));
+ autocreate_empty_port(ci, ctx->id("DIB" + std::to_string(i)));
+ }
+ for (int i = 0; i < 3; i++) {
+ autocreate_empty_port(ci, ctx->id("CSA" + std::to_string(i)));
+ autocreate_empty_port(ci, ctx->id("CSB" + std::to_string(i)));
+ }
+ for (int i = 0; i < 3; i++) {
+ autocreate_empty_port(ci, ctx->id("CSA" + std::to_string(i)));
+ autocreate_empty_port(ci, ctx->id("CSB" + std::to_string(i)));
+ }
+
+ autocreate_empty_port(ci, id_CLKA);
+ autocreate_empty_port(ci, id_CEA);
+ autocreate_empty_port(ci, id_OCEA);
+ autocreate_empty_port(ci, id_WEA);
+ autocreate_empty_port(ci, id_RSTA);
+
+ autocreate_empty_port(ci, id_CLKB);
+ autocreate_empty_port(ci, id_CEB);
+ autocreate_empty_port(ci, id_OCEB);
+ autocreate_empty_port(ci, id_WEB);
+ autocreate_empty_port(ci, id_RSTB);
+
+ ci->attrs[ctx->id("WID")] = std::to_string(wid++);
+ }
+ }
+ }
+
public:
void pack()
{
pack_io();
+ pack_ebr();
pack_constants();
pack_dram();
pack_carries();