diff options
Diffstat (limited to 'ice40/pack.cc')
-rw-r--r-- | ice40/pack.cc | 102 |
1 files changed, 95 insertions, 7 deletions
diff --git a/ice40/pack.cc b/ice40/pack.cc index 2b5def46..92297e8e 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -266,6 +266,68 @@ static void pack_carries(Context *ctx) log_info(" %4d LCs used as CARRY only\n", carry_only); } +static void merge_carry_luts(Context *ctx) +{ + // Find carrys + log_info("Packing indirect carry+LUT pairs...\n"); + // Find cases where a less-than-LUT2 is driving a carry and pack them together + // +----+ +-----+ | + // A--|LUT2|----|CARRY| | + // B--| | C-| |-+ + // +----+ +-| | + // | +-----+ + // | + pool<IdString> packed_cells; + auto rewrite_init = [](unsigned lut_init) { + // I0 -> LUT I2 + // I1, I2 -> carry; don't care + // I3 -> LUT I3 + unsigned result = 0; + for (unsigned i = 0; i < 16; i++) { + unsigned j = 0; + if ((i & 1)) + j |= 4; + if ((i & 8)) + j |= 8; + if (lut_init & (1 << j)) + result |= (1 << i); + } + return result; + }; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_ICESTORM_LC || !bool_or_default(ci->params, id_CARRY_ENABLE)) + continue; // not a carry LC + if (ci->getPort(id_O)) + continue; // LUT output is already used + for (auto port : {id_I1, id_I2}) { // check carry inputs + NetInfo *i = ci->getPort(port); + if (!i) + continue; + CellInfo *drv = i->driver.cell; + if (i->driver.port != id_O) + continue; + if (!drv || drv->type != id_ICESTORM_LC || packed_cells.count(drv->name) || + bool_or_default(drv->params, id_CARRY_ENABLE) || bool_or_default(drv->params, id_DFF_ENABLE)) + continue; // not driven by a LUT, or driver already swallowed + // Check cardinality - must be LUT2 or less, noting top inputs used first + if (drv->getPort(id_I0) || drv->getPort(id_I1)) + continue; + // Pack into carry + drv->movePortTo(id_I2, ci, id_I0); + drv->movePortTo(id_I3, ci, id_I3); + drv->movePortTo(id_O, ci, id_O); + ci->params[id_LUT_INIT] = Property(rewrite_init(int_or_default(drv->params, id_LUT_INIT)), 16); + packed_cells.insert(drv->name); + break; + } + } + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + log_info(" %4d LUTs merged into carry LCs\n", int(packed_cells.size())); +} + // "Pack" RAMs static void pack_ram(Context *ctx) { @@ -962,31 +1024,56 @@ static void place_plls(Context *ctx) // Find a BEL for it BelId found_bel; + std::string conflict_str = ""; for (auto bel_pll : pll_all_bels) { - if (pll_used_bels.count(bel_pll.first)) + if (pll_used_bels.count(bel_pll.first)) { + conflict_str += + stringf(" PLL bel '%s' is already used by '%s'.\n", ctx->nameOfBel(bel_pll.first), + pll_used_bels.at(bel_pll.first)->name.c_str(ctx)); continue; + } BelPin pll_io_a, pll_io_b; BelId gb_a, gb_b; std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = bel_pll.second; if (bel2io.count(pll_io_a.bel)) { if (pll_io_a.bel == pad_bel) could_be_pad = !bel2io.count(pll_io_b.bel) || !is_sb_pll40_dual(ctx, ci); + auto conflict_pin = ctx->get_bel_package_pin(pll_io_a.bel); + conflict_str += + stringf(" PLL bel '%s' cannot be used as it conflicts with input '%s' on pin '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2io.at(pll_io_a.bel)->name.c_str(ctx), + conflict_pin.c_str()); continue; } - if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci)) + if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci)) { + auto conflict_pin = ctx->get_bel_package_pin(pll_io_b.bel); + conflict_str += + stringf(" PLL bel '%s' cannot be used as it conflicts with input '%s' on pin '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2io.at(pll_io_b.bel)->name.c_str(ctx), + conflict_pin.c_str()); continue; - if (gb_a_used && bel2gb.count(gb_a)) + } + if (gb_a_used && bel2gb.count(gb_a)) { + conflict_str += stringf( + " PLL bel '%s' cannot be used as it conflicts with global buffer '%s' at '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2gb.at(gb_a)->name.c_str(ctx), ctx->nameOfBel(gb_a)); continue; - if (gb_b_used && bel2gb.count(gb_b)) + } + if (gb_b_used && bel2gb.count(gb_b)) { + conflict_str += stringf( + " PLL bel '%s' cannot be used as it conflicts with global buffer '%s' at '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2gb.at(gb_b)->name.c_str(ctx), ctx->nameOfBel(gb_b)); continue; + } found_bel = bel_pll.first; break; } // Apply constrain & Inform user of result - if (found_bel == BelId()) - log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found.%s\n", ci->name.c_str(ctx), - could_be_pad ? " Did you mean to use a PAD PLL ?" : ""); + if (found_bel == BelId()) { + log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found.%s\n%s\n", ci->name.c_str(ctx), + could_be_pad ? " Did you mean to use a PAD PLL ?" : "", conflict_str.c_str()); + } log_info(" constrained PLL '%s' to %s\n", ci->name.c_str(ctx), ctx->nameOfBel(found_bel)); if (could_be_pad) @@ -1617,6 +1704,7 @@ bool Arch::pack() pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); pack_carries(ctx); + merge_carry_luts(ctx); pack_ram(ctx); place_plls(ctx); pack_special(ctx); |