aboutsummaryrefslogtreecommitdiffstats
path: root/gowin
diff options
context:
space:
mode:
authorYRabbit <rabbit@yrabbit.cyou>2021-10-07 18:38:33 +1000
committerYRabbit <rabbit@yrabbit.cyou>2021-10-07 18:38:33 +1000
commitc72ea15472680ea155d7be4520cba8640d074b50 (patch)
tree0eb8e18242c664ac31e3937047353dda5ee0e3ff /gowin
parent4f17a1711abc6d74c367457a1956c779a789e0e9 (diff)
downloadnextpnr-c72ea15472680ea155d7be4520cba8640d074b50.tar.gz
nextpnr-c72ea15472680ea155d7be4520cba8640d074b50.tar.bz2
nextpnr-c72ea15472680ea155d7be4520cba8640d074b50.zip
gowin: add support for wide LUTs.
* A hardwired MUX within each logical cell is used. * The delay is equal 0. * No user placement constraints. * The output route contains dummy PIPs. They are ignored by gowin_pack, but it may be worth removing them. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
Diffstat (limited to 'gowin')
-rw-r--r--gowin/arch.cc108
-rw-r--r--gowin/arch.h3
-rw-r--r--gowin/archdefs.h1
-rw-r--r--gowin/cells.cc23
-rw-r--r--gowin/cells.h34
-rw-r--r--gowin/constids.inc13
-rw-r--r--gowin/pack.cc261
7 files changed, 434 insertions, 9 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index 022c93c3..2d637695 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -583,6 +583,87 @@ void Arch::read_cst(std::istream &in)
}
}
+// Add all MUXes for the cell
+void Arch::addMuxBels(const DatabasePOD *db, int row, int col)
+{
+ IdString belname, bel_id;
+ char buf[40];
+ int z;
+ // XXX do real delay
+ DelayQuad delay = DelayQuad(0);
+ // make all wide luts with these parameters
+ struct
+ {
+ char type; // MUX type 5,6,7,8
+ char bel_idx; // just bel name suffix
+ char in_prefix[2]; // input from F or OF
+ char in_idx[2]; // input from bel with idx
+ } const mux_names[] = {{'5', '0', "", {'0', '1'}}, {'6', '0', "O", {'2', '0'}}, {'5', '1', "", {'2', '3'}},
+ {'7', '0', "O", {'5', '1'}}, {'5', '2', "", {'4', '5'}}, {'6', '1', "O", {'6', '4'}},
+ {'5', '3', "", {'6', '7'}}, {'8', '0', "O", {'3', '3'}}};
+
+ // 4 MUX2_LUT5, 2 MUX2_LUT6, 1 MUX2_LUT7, 1 MUX2_LUT8
+ for (int j = 0; j < 8; ++j) {
+ z = j + mux_0_z;
+
+ int grow = row + 1;
+ int gcol = col + 1;
+
+ // no MUX2_LUT8 in the last column
+ if (j == 7 && col == getGridDimX() - 1) {
+ continue;
+ }
+
+ // bel
+ snprintf(buf, 40, "R%dC%d_MUX2_LUT%c%c", grow, gcol, mux_names[j].type, mux_names[j].bel_idx);
+ belname = id(buf);
+ snprintf(buf, 40, "GW_MUX2_LUT%c", mux_names[j].type);
+ bel_id = id(buf);
+ addBel(belname, bel_id, Loc(col, row, z), false);
+
+ // dummy wires
+ snprintf(buf, 40, "I0MUX%d", j);
+ IdString id_wire_i0 = id(buf);
+ IdString wire_i0_name = wireToGlobal(row, col, db, id_wire_i0);
+ addWire(wire_i0_name, id_wire_i0, col, row);
+
+ snprintf(buf, 40, "I1MUX%d", j);
+ IdString id_wire_i1 = id(buf);
+ IdString wire_i1_name = wireToGlobal(row, col, db, id_wire_i1);
+ addWire(wire_i1_name, id_wire_i1, col, row);
+
+ // dummy left pip
+ snprintf(buf, 40, "%sF%c", mux_names[j].in_prefix, mux_names[j].in_idx[0]);
+ IdString id_src_F = id(buf);
+ // LUT8's I0 is wired to the right cell
+ IdString src_F;
+ int src_col = col;
+ if (j == 7) {
+ ++src_col;
+ }
+ src_F = wireToGlobal(row, src_col, db, id_src_F);
+ snprintf(buf, 40, "R%dC%d_%s__%s", grow, gcol, id_src_F.c_str(this), id_wire_i0.c_str(this));
+ addPip(id(buf), id_wire_i0, src_F, wire_i0_name, delay, Loc(col, row, 0));
+
+ // dummy right pip
+ snprintf(buf, 40, "%sF%c", mux_names[j].in_prefix, mux_names[j].in_idx[1]);
+ id_src_F = id(buf);
+ src_F = wireToGlobal(row, col, db, id_src_F);
+ snprintf(buf, 40, "R%dC%d_%s__%s", grow, gcol, id_src_F.c_str(this), id_wire_i1.c_str(this));
+ addPip(id(buf), id_wire_i1, src_F, wire_i1_name, delay, Loc(col, row, 0));
+
+ // the MUX ports
+ snprintf(buf, 40, "R%dC%d_OF%d", grow, gcol, j);
+ addBelOutput(belname, id_OF, id(buf));
+ snprintf(buf, 40, "R%dC%d_SEL%d", grow, gcol, j);
+ addBelInput(belname, id_SEL, id(buf));
+ snprintf(buf, 40, "R%dC%d_I0MUX%d", grow, gcol, j);
+ addBelInput(belname, id_I0, id(buf));
+ snprintf(buf, 40, "R%dC%d_I1MUX%d", grow, gcol, j);
+ addBelInput(belname, id_I1, id(buf));
+ }
+}
+
Arch::Arch(ArchArgs args) : args(args)
{
family = args.family;
@@ -645,7 +726,9 @@ Arch::Arch(ArchArgs args) : args(args)
}
// setup db
char buf[32];
- for (int i = 0; i < db->rows * db->cols; i++) {
+ // The reverse order of the enumeration simplifies the creation
+ // of MUX2_LUT8s: they need the existence of the wire on the right.
+ for (int i = db->rows * db->cols - 1; i >= 0; --i) {
int row = i / db->cols;
int col = i % db->cols;
const TilePOD *tile = db->grid[i].get();
@@ -718,6 +801,9 @@ Arch::Arch(ArchArgs args) : args(args)
snprintf(buf, 32, "R%dC%d_Q%d", row + 1, col + 1, z);
addBelOutput(belname, id_Q, id(buf));
}
+ if (z == 0) {
+ addMuxBels(db, row, col);
+ }
break;
case ID_IOBJ:
z++; /* fall-through*/
@@ -1094,6 +1180,7 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
bool Arch::place()
{
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
+ bool retVal;
if (placer == "heap") {
bool have_iobuf_or_constr = false;
for (auto &cell : cells) {
@@ -1103,7 +1190,6 @@ bool Arch::place()
break;
}
}
- bool retVal;
if (!have_iobuf_or_constr) {
log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to "
"SA.\n");
@@ -1116,15 +1202,21 @@ bool Arch::place()
}
getCtx()->settings[getCtx()->id("place")] = 1;
archInfoToAttributes();
- return retVal;
} else if (placer == "sa") {
- bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
+ retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
getCtx()->settings[getCtx()->id("place")] = 1;
archInfoToAttributes();
return retVal;
} else {
log_error("Gowin architecture does not support placer '%s'\n", placer.c_str());
}
+ // debug placement
+ if (getCtx()->debug) {
+ for (auto &cell : getCtx()->cells) {
+ log_info("Placed: %s -> %s\n", cell.first.c_str(getCtx()), getCtx()->nameOfBel(cell.second->bel));
+ }
+ }
+ return retVal;
}
bool Arch::route()
@@ -1183,13 +1275,15 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
bool Arch::isBelLocationValid(BelId bel) const
{
- std::vector<const CellInfo *> cells;
Loc loc = getBelLocation(bel);
+
+ std::vector<const CellInfo *> cells;
for (auto tbel : getBelsByTile(loc.x, loc.y)) {
CellInfo *bound = getBoundBelCell(tbel);
if (bound != nullptr)
cells.push_back(bound);
}
+
return cellsCompatible(cells.data(), int(cells.size()));
}
@@ -1213,6 +1307,7 @@ void Arch::assignArchInfo()
for (auto &cell : getCtx()->cells) {
IdString cname = cell.first;
CellInfo *ci = cell.second.get();
+ ci->is_slice = false;
if (ci->type == id("SLICE")) {
ci->is_slice = true;
ci->ff_used = ci->params.at(id_FF_USED).as_bool();
@@ -1238,9 +1333,6 @@ void Arch::assignArchInfo()
DelayQuad delay = delayLookup(speed->lut.timings.get(), speed->lut.num_timings, port_delay[i]);
addCellTimingDelay(cname, ports[i], id_F, delay);
}
-
- } else {
- ci->is_slice = false;
}
}
}
diff --git a/gowin/arch.h b/gowin/arch.h
index 8774c303..602c3db5 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -330,6 +330,7 @@ struct Arch : BaseArch<ArchRanges>
IdString wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString &wire);
DelayQuad getWireTypeDelay(IdString wire);
void read_cst(std::istream &in);
+ void addMuxBels(const DatabasePOD *db, int row, int col);
// ---------------------------------------------------------------
// Common Arch API. Every arch must provide the following methods.
@@ -444,6 +445,8 @@ struct Arch : BaseArch<ArchRanges>
// Internal usage
void assignArchInfo() override;
bool cellsCompatible(const CellInfo **cells, int count) const;
+ // start Z for the MUX2LUT5 bels
+ int const mux_0_z = 10;
std::vector<IdString> cell_types;
diff --git a/gowin/archdefs.h b/gowin/archdefs.h
index e35649ef..c4d2ecdb 100644
--- a/gowin/archdefs.h
+++ b/gowin/archdefs.h
@@ -65,6 +65,7 @@ struct ArchCellInfo : BaseClusterInfo
IdString ff_type;
// Is a slice type primitive
bool is_slice;
+
// Only packing rule for slice type primitives is a single clock per tile
const NetInfo *slice_clk;
const NetInfo *slice_ce;
diff --git a/gowin/cells.cc b/gowin/cells.cc
index e4b9db3f..58e4fddd 100644
--- a/gowin/cells.cc
+++ b/gowin/cells.cc
@@ -58,6 +58,10 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
add_port(ctx, new_cell.get(), id_Q, PORT_OUT);
add_port(ctx, new_cell.get(), id_CE, PORT_IN);
add_port(ctx, new_cell.get(), id_LSR, PORT_IN);
+ } else if (type == id_GW_MUX2_LUT5 || type == id_GW_MUX2_LUT6 || type == id_GW_MUX2_LUT7 ||
+ type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT8) {
+ add_port(ctx, new_cell.get(), id_SEL, PORT_IN);
+ add_port(ctx, new_cell.get(), id_OF, PORT_OUT);
} else if (type == id_IOB) {
new_cell->params[id_INPUT_USED] = 0;
new_cell->params[id_OUTPUT_USED] = 0;
@@ -68,7 +72,7 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
add_port(ctx, new_cell.get(), id_EN, PORT_IN);
add_port(ctx, new_cell.get(), id_O, PORT_OUT);
} else {
- log_error("unable to create generic cell of type %s", type.c_str(ctx));
+ log_error("unable to create generic cell of type %s\n", type.c_str(ctx));
}
return new_cell;
}
@@ -76,6 +80,23 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
{
lc->params[id_INIT] = lut->params[id_INIT];
+ lc->cluster = lut->cluster;
+ lc->constr_x = lut->constr_x;
+ lc->constr_y = lut->constr_y;
+ lc->constr_z = lut->constr_z;
+
+ // add itself to the cluster root children list
+ if (lc->cluster != ClusterId()) {
+ CellInfo *cluster_root = ctx->cells.at(lc->cluster).get();
+ lc->constr_x += cluster_root->constr_x;
+ lc->constr_y += cluster_root->constr_y;
+ lc->constr_z += cluster_root->constr_z;
+ if (cluster_root->cluster != cluster_root->name) {
+ lc->cluster = cluster_root->cluster;
+ cluster_root = ctx->cells.at(cluster_root->cluster).get();
+ }
+ cluster_root->constr_children.push_back(lc);
+ }
IdString sim_names[4] = {id_I0, id_I1, id_I2, id_I3};
IdString wire_names[4] = {id_A, id_B, id_C, id_D};
diff --git a/gowin/cells.h b/gowin/cells.h
index dbd86106..cb1c7aba 100644
--- a/gowin/cells.h
+++ b/gowin/cells.h
@@ -43,6 +43,40 @@ inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell)
}
}
+// Return true if a cell is a wide LUT mux
+inline bool is_widelut(const BaseCtx *ctx, const CellInfo *cell)
+{
+ switch (cell->type.index) {
+ case ID_MUX2_LUT5:
+ case ID_MUX2_LUT6:
+ case ID_MUX2_LUT7:
+ case ID_MUX2_LUT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// is MUX2_LUT5
+inline bool is_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT5); }
+
+inline bool is_gw_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT5); }
+
+// is MUX2_LUT6
+inline bool is_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT6); }
+
+inline bool is_gw_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT6); }
+
+// is MUX2_LUT7
+inline bool is_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT7); }
+
+inline bool is_gw_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT7); }
+
+// is MUX2_LUT8
+inline bool is_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT8); }
+
+inline bool is_gw_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT8); }
+
// Return true if a cell is a flipflop
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
{
diff --git a/gowin/constids.inc b/gowin/constids.inc
index bf26e9ca..e2482e39 100644
--- a/gowin/constids.inc
+++ b/gowin/constids.inc
@@ -358,6 +358,16 @@ X(IOBH)
X(IOBI)
X(IOBJ)
+// Wide LUTs
+X(MUX2_LUT5)
+X(MUX2_LUT6)
+X(MUX2_LUT7)
+X(MUX2_LUT8)
+X(GW_MUX2_LUT5)
+X(GW_MUX2_LUT6)
+X(GW_MUX2_LUT7)
+X(GW_MUX2_LUT8)
+
// DFF types
X(DFF)
X(DFFE)
@@ -409,6 +419,9 @@ X(I1)
X(I2)
X(I3)
X(OEN)
+X(S0)
+X(SEL)
+X(OF)
// timing
X(X0)
diff --git a/gowin/pack.cc b/gowin/pack.cc
index 708f06da..00602c00 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -28,6 +28,266 @@
NEXTPNR_NAMESPACE_BEGIN
+// pack MUX2_LUT5
+static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
+ std::vector<std::unique_ptr<CellInfo>> &new_cells)
+{
+
+ if (bool_or_default(ci->attrs, ctx->id("SINGLE_INPUT_MUX"))) {
+ // find the muxed LUT
+ NetInfo *i1 = ci->ports.at(id_I1).net;
+
+ CellInfo *lut1 = net_driven_by(ctx, i1, is_lut, id_F);
+ if (lut1 == nullptr) {
+ log_error("MUX2_LUT5 '%s' port I1 isn't connected to the LUT\n", ci->name.c_str(ctx));
+ return;
+ }
+ if (ctx->verbose) {
+ log_info("found attached lut1 %s\n", ctx->nameOf(lut1));
+ }
+
+ // XXX enable the placement constraints
+ auto mux_bel = ci->attrs.find(ctx->id("BEL"));
+ auto lut1_bel = lut1->attrs.find(ctx->id("BEL"));
+ if (lut1_bel != lut1->attrs.end() || mux_bel != ci->attrs.end()) {
+ log_error("MUX2_LUT5 '%s' placement restrictions are not yet supported\n", ci->name.c_str(ctx));
+ return;
+ }
+
+ std::unique_ptr<CellInfo> packed = create_generic_cell(ctx, ctx->id("GW_MUX2_LUT5"), ci->name.str(ctx) + "_LC");
+ if (ctx->verbose) {
+ log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get()));
+ }
+ // mux is the cluster root
+ packed->cluster = packed->name;
+ lut1->cluster = packed->name;
+ lut1->constr_z = -ctx->mux_0_z + 1;
+ packed->constr_children.clear();
+
+ // reconnect MUX ports
+ replace_port(ci, id_O, packed.get(), id_OF);
+ replace_port(ci, id_I1, packed.get(), id_I1);
+
+ // remove cells
+ packed_cells.insert(ci->name);
+ // new MUX cell
+ new_cells.push_back(std::move(packed));
+ } else {
+ // find the muxed LUTs
+ NetInfo *i0 = ci->ports.at(id_I0).net;
+ NetInfo *i1 = ci->ports.at(id_I1).net;
+
+ CellInfo *lut0 = net_driven_by(ctx, i0, is_lut, id_F);
+ CellInfo *lut1 = net_driven_by(ctx, i1, is_lut, id_F);
+ if (lut0 == nullptr || lut1 == nullptr) {
+ log_error("MUX2_LUT5 '%s' port I0 or I1 isn't connected to the LUT\n", ci->name.c_str(ctx));
+ return;
+ }
+ if (ctx->verbose) {
+ log_info("found attached lut0 %s\n", ctx->nameOf(lut0));
+ log_info("found attached lut1 %s\n", ctx->nameOf(lut1));
+ }
+
+ // XXX enable the placement constraints
+ auto mux_bel = ci->attrs.find(ctx->id("BEL"));
+ auto lut0_bel = lut0->attrs.find(ctx->id("BEL"));
+ auto lut1_bel = lut1->attrs.find(ctx->id("BEL"));
+ if (lut0_bel != lut0->attrs.end() || lut1_bel != lut1->attrs.end() || mux_bel != ci->attrs.end()) {
+ log_error("MUX2_LUT5 '%s' placement restrictions are not yet supported\n", ci->name.c_str(ctx));
+ return;
+ }
+
+ std::unique_ptr<CellInfo> packed = create_generic_cell(ctx, ctx->id("GW_MUX2_LUT5"), ci->name.str(ctx) + "_LC");
+ if (ctx->verbose) {
+ log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get()));
+ }
+ // mux is the cluster root
+ packed->cluster = packed->name;
+ lut0->cluster = packed->name;
+ lut0->constr_z = -ctx->mux_0_z;
+ lut1->cluster = packed->name;
+ lut1->constr_z = -ctx->mux_0_z + 1;
+ packed->constr_children.clear();
+
+ // reconnect MUX ports
+ replace_port(ci, id_O, packed.get(), id_OF);
+ replace_port(ci, id_S0, packed.get(), id_SEL);
+ replace_port(ci, id_I0, packed.get(), id_I0);
+ replace_port(ci, id_I1, packed.get(), id_I1);
+
+ // remove cells
+ packed_cells.insert(ci->name);
+ // new MUX cell
+ new_cells.push_back(std::move(packed));
+ }
+}
+
+// Common MUX2 packing routine
+static void pack_mux2_lut(Context *ctx, CellInfo *ci, bool (*pred)(const BaseCtx *, const CellInfo *),
+ char const type_suffix, IdString const type_id, int const x[2], int const z[2],
+ pool<IdString> &packed_cells, pool<IdString> &delete_nets,
+ std::vector<std::unique_ptr<CellInfo>> &new_cells)
+{
+ // find the muxed LUTs
+ NetInfo *i0 = ci->ports.at(id_I0).net;
+ NetInfo *i1 = ci->ports.at(id_I1).net;
+
+ CellInfo *mux0 = net_driven_by(ctx, i0, pred, id_OF);
+ CellInfo *mux1 = net_driven_by(ctx, i1, pred, id_OF);
+ if (mux0 == nullptr || mux1 == nullptr) {
+ log_error("MUX2_LUT%c '%s' port I0 or I1 isn't connected to the MUX\n", type_suffix, ci->name.c_str(ctx));
+ return;
+ }
+ if (ctx->verbose) {
+ log_info("found attached mux0 %s\n", ctx->nameOf(mux0));
+ log_info("found attached mux1 %s\n", ctx->nameOf(mux1));
+ }
+
+ // XXX enable the placement constraints
+ auto mux_bel = ci->attrs.find(ctx->id("BEL"));
+ auto mux0_bel = mux0->attrs.find(ctx->id("BEL"));
+ auto mux1_bel = mux1->attrs.find(ctx->id("BEL"));
+ if (mux0_bel != mux0->attrs.end() || mux1_bel != mux1->attrs.end() || mux_bel != ci->attrs.end()) {
+ log_error("MUX2_LUT%c '%s' placement restrictions are not yet supported\n", type_suffix, ci->name.c_str(ctx));
+ return;
+ }
+
+ std::unique_ptr<CellInfo> packed = create_generic_cell(ctx, type_id, ci->name.str(ctx) + "_LC");
+ if (ctx->verbose) {
+ log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get()));
+ }
+ // mux is the cluster root
+ packed->cluster = packed->name;
+ mux0->cluster = packed->name;
+ mux0->constr_x = x[0];
+ mux0->constr_z = z[0];
+ for (auto &child : mux0->constr_children) {
+ child->cluster = packed->name;
+ child->constr_x += mux0->constr_x;
+ child->constr_z += mux0->constr_z;
+ packed->constr_children.push_back(child);
+ }
+ mux0->constr_children.clear();
+ mux1->cluster = packed->name;
+ mux1->constr_x = x[1];
+ mux1->constr_z = z[1];
+ for (auto &child : mux1->constr_children) {
+ child->cluster = packed->name;
+ child->constr_x += mux1->constr_x;
+ child->constr_z += mux1->constr_z;
+ packed->constr_children.push_back(child);
+ }
+ mux1->constr_children.clear();
+ packed->constr_children.push_back(mux0);
+ packed->constr_children.push_back(mux1);
+
+ // reconnect MUX ports
+ replace_port(ci, id_O, packed.get(), id_OF);
+ replace_port(ci, id_S0, packed.get(), id_SEL);
+ replace_port(ci, id_I0, packed.get(), id_I0);
+ replace_port(ci, id_I1, packed.get(), id_I1);
+
+ // remove cells
+ packed_cells.insert(ci->name);
+ // new MUX cell
+ new_cells.push_back(std::move(packed));
+}
+
+// pack MUX2_LUT6
+static void pack_mux2_lut6(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
+ std::vector<std::unique_ptr<CellInfo>> &new_cells)
+{
+ static int x[] = {0, 0};
+ static int z[] = {+1, -1};
+ pack_mux2_lut(ctx, ci, is_gw_mux2_lut5, '6', id_GW_MUX2_LUT6, x, z, packed_cells, delete_nets, new_cells);
+}
+
+// pack MUX2_LUT7
+static void pack_mux2_lut7(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
+ std::vector<std::unique_ptr<CellInfo>> &new_cells)
+{
+ static int x[] = {0, 0};
+ static int z[] = {+2, -2};
+ pack_mux2_lut(ctx, ci, is_gw_mux2_lut6, '7', id_GW_MUX2_LUT7, x, z, packed_cells, delete_nets, new_cells);
+}
+
+// pack MUX2_LUT8
+static void pack_mux2_lut8(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
+ std::vector<std::unique_ptr<CellInfo>> &new_cells)
+{
+ static int x[] = {1, 0};
+ static int z[] = {-4, -4};
+ pack_mux2_lut(ctx, ci, is_gw_mux2_lut7, '8', id_GW_MUX2_LUT8, x, z, packed_cells, delete_nets, new_cells);
+}
+
+// Pack wide LUTs
+static void pack_wideluts(Context *ctx)
+{
+ log_info("Packing wide LUTs..\n");
+
+ pool<IdString> packed_cells;
+ pool<IdString> delete_nets;
+ std::vector<std::unique_ptr<CellInfo>> new_cells;
+
+ pool<IdString> mux2lut6;
+ pool<IdString> mux2lut7;
+ pool<IdString> mux2lut8;
+
+ // do MUX2_LUT5 and collect LUT6/7/8
+ log_info("Packing LUT5s..\n");
+ for (auto &cell : ctx->cells) {
+ CellInfo *ci = cell.second.get();
+ if (ctx->verbose) {
+ log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx));
+ }
+ if (is_widelut(ctx, ci)) {
+ if (is_mux2_lut5(ctx, ci)) {
+ pack_mux2_lut5(ctx, ci, packed_cells, delete_nets, new_cells);
+ } else {
+ if (is_mux2_lut6(ctx, ci)) {
+ mux2lut6.insert(ci->name);
+ } else {
+ if (is_mux2_lut7(ctx, ci)) {
+ mux2lut7.insert(ci->name);
+ } else {
+ if (is_mux2_lut8(ctx, ci)) {
+ mux2lut8.insert(ci->name);
+ }
+ }
+ }
+ }
+ }
+ }
+ // do MUX_LUT6
+ log_info("Packing LUT6s..\n");
+ for (auto &cell_name : mux2lut6) {
+ pack_mux2_lut6(ctx, ctx->cells[cell_name].get(), packed_cells, delete_nets, new_cells);
+ }
+
+ // do MUX_LUT7
+ log_info("Packing LUT7s..\n");
+ for (auto &cell_name : mux2lut7) {
+ pack_mux2_lut7(ctx, ctx->cells[cell_name].get(), packed_cells, delete_nets, new_cells);
+ }
+
+ // do MUX_LUT8
+ log_info("Packing LUT8s..\n");
+ for (auto &cell_name : mux2lut8) {
+ pack_mux2_lut8(ctx, ctx->cells[cell_name].get(), packed_cells, delete_nets, new_cells);
+ }
+
+ // actual delete, erase and move cells/nets
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto dnet : delete_nets) {
+ ctx->nets.erase(dnet);
+ }
+ for (auto &ncell : new_cells) {
+ ctx->cells[ncell->name] = std::move(ncell);
+ }
+}
+
// Pack LUTs and LUT-FF pairs
static void pack_lut_lutffs(Context *ctx)
{
@@ -287,6 +547,7 @@ bool Arch::pack()
log_break();
pack_constants(ctx);
pack_io(ctx);
+ pack_wideluts(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
ctx->settings[ctx->id("pack")] = 1;