aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ecp5/cells.cc44
-rw-r--r--ecp5/cells.h2
-rw-r--r--ecp5/pack.cc25
3 files changed, 71 insertions, 0 deletions
diff --git a/ecp5/cells.cc b/ecp5/cells.cc
index 13025da9..4beaabd2 100644
--- a/ecp5/cells.cc
+++ b/ecp5/cells.cc
@@ -18,6 +18,7 @@
*/
#include "cells.h"
+#include <algorithm>
#include "design_utils.h"
#include "log.h"
#include "util.h"
@@ -121,4 +122,47 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
return new_cell;
}
+static void set_param_safe(bool has_ff, CellInfo *lc, IdString name, const std::string &value)
+{
+ NPNR_ASSERT(!has_ff || lc->params.at(name) == value);
+ lc->params[name] = value;
+}
+
+static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellInfo *lc, IdString lc_port)
+{
+ if (has_ff) {
+ assert(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net);
+ NetInfo *ffnet = ff->ports.at(ff_port).net;
+ if (ffnet != nullptr)
+ ffnet->users.erase(
+ std::remove_if(ffnet->users.begin(), ffnet->users.end(),
+ [ff, ff_port](PortRef port) { return port.cell == ff && port.port == ff_port; }),
+ ffnet->users.end());
+ } else {
+ replace_port(ff, ff_port, lc, lc_port);
+ }
+}
+
+void ff_to_lc(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut)
+{
+ bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr;
+ std::string reg = "REG" + std::to_string(index);
+ set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE"));
+ set_param_safe(has_ff, lc, ctx->id("GSR"), str_or_default(ff->params, ctx->id("GSR"), "DISABLED"));
+ set_param_safe(has_ff, lc, ctx->id("CEMUX"), str_or_default(ff->params, ctx->id("CEMUX"), "1"));
+ set_param_safe(has_ff, lc, ctx->id("LSRMUX"), str_or_default(ff->params, ctx->id("LSRMUX"), "LSR"));
+ lc->params[ctx->id(reg + "_SD")] = driven_by_lut ? "1" : "0";
+ lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET");
+ replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK"));
+ replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR"));
+ replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE"));
+
+ replace_port(ff, ctx->id("Q"), lc, ctx->id("Q" + std::to_string(index)));
+ if (driven_by_lut) {
+ replace_port(ff, ctx->id("DI"), lc, ctx->id("DI" + std::to_string(index)));
+ } else {
+ replace_port(ff, ctx->id("DI"), lc, ctx->id("M" + std::to_string(index)));
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/ecp5/cells.h b/ecp5/cells.h
index 2d11da20..d8d17061 100644
--- a/ecp5/cells.h
+++ b/ecp5/cells.h
@@ -46,6 +46,8 @@ inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty
inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("L6MUX21"); }
+void ff_to_lc(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut);
+
NEXTPNR_NAMESPACE_END
#endif
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index c44c0c4c..13b24872 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -52,6 +52,23 @@ class Ecp5Packer
new_cells.clear();
}
+ // Find FFs associated with LUTs, or LUT expansion muxes
+ void find_lutff_pairs()
+ {
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (is_lut(ctx, ci) || is_pfumx(ctx, ci) || is_l6mux(ctx, ci)) {
+ NetInfo *znet = ci->ports.at(ctx->id("Z")).net;
+ if (znet != nullptr) {
+ CellInfo *ff = net_only_drives(ctx, znet, is_ff, ctx->id("DI"), false);
+ if (ff != nullptr) {
+ lutffPairs[ci->name] = ff->name;
+ }
+ }
+ }
+ }
+ }
+
// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated
void pack_io()
{
@@ -126,6 +143,13 @@ class Ecp5Packer
replace_port(ci, ctx->id("Z"), packed.get(), ctx->id("OFX0"));
ctx->nets.erase(f0->name);
ctx->nets.erase(f1->name);
+
+ if (lutffPairs.find(ci->name) != lutffPairs.end()) {
+ CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get();
+ ff_to_lc(ctx, ff, packed.get(), 0, true);
+ packed_cells.insert(ff->name);
+ }
+
new_cells.push_back(std::move(packed));
packed_cells.insert(lc0->name);
packed_cells.insert(lc1->name);
@@ -157,6 +181,7 @@ class Ecp5Packer
};
std::unordered_map<IdString, SliceUsage> sliceUsage;
+ std::unordered_map<IdString, IdString> lutffPairs;
};
// Main pack function