From c72ea15472680ea155d7be4520cba8640d074b50 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 7 Oct 2021 18:38:33 +1000 Subject: [PATCH] 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 --- gowin/arch.cc | 108 +++++++++++++++++-- gowin/arch.h | 3 + gowin/archdefs.h | 1 + gowin/cells.cc | 23 +++- gowin/cells.h | 34 ++++++ gowin/constids.inc | 13 +++ gowin/pack.cc | 261 +++++++++++++++++++++++++++++++++++++++++++++ 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 cells; Loc loc = getBelLocation(bel); + + std::vector 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 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 // 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 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 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 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 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 &packed_cells, pool &delete_nets, + std::vector> &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 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 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 &packed_cells, pool &delete_nets, + std::vector> &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 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 &packed_cells, pool &delete_nets, + std::vector> &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 &packed_cells, pool &delete_nets, + std::vector> &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 &packed_cells, pool &delete_nets, + std::vector> &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 packed_cells; + pool delete_nets; + std::vector> new_cells; + + pool mux2lut6; + pool mux2lut7; + pool 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;