aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
Diffstat (limited to 'ice40')
-rw-r--r--ice40/arch.cc29
-rw-r--r--ice40/archdefs.h1
-rw-r--r--ice40/pack.cc102
3 files changed, 122 insertions, 10 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc
index b36c82d5..6746b302 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -923,9 +923,20 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const
{
- if (cell->type == id_ICESTORM_LC && cell->lcInfo.dffEnable) {
- if (toPort == id_O)
- return false;
+ if (cell->type == id_ICESTORM_LC) {
+ if (toPort == id_O) {
+ if (cell->lcInfo.dffEnable)
+ return false;
+ // "false paths"
+ if (fromPort == id_I0 && ((cell->lcInfo.lutInputMask & 0x1U) == 0))
+ return false;
+ if (fromPort == id_I1 && ((cell->lcInfo.lutInputMask & 0x2U) == 0))
+ return false;
+ if (fromPort == id_I2 && ((cell->lcInfo.lutInputMask & 0x4U) == 0))
+ return false;
+ if (fromPort == id_I3 && ((cell->lcInfo.lutInputMask & 0x8U) == 0))
+ return false;
+ }
} else if (cell->type == id_ICESTORM_RAM || cell->type == id_ICESTORM_SPRAM) {
return false;
}
@@ -1231,6 +1242,18 @@ void Arch::assignCellInfo(CellInfo *cell)
cell->lcInfo.inputCount++;
if (cell->getPort(id_I3))
cell->lcInfo.inputCount++;
+ // Find don't care LUT inputs to mask for timing analysis
+ cell->lcInfo.lutInputMask = 0x0;
+ unsigned init = int_or_default(cell->params, id_LUT_INIT);
+ for (unsigned k = 0; k < 4; k++) {
+ for (unsigned i = 0; i < 16; i++) {
+ // If toggling the LUT input makes a difference it's not a don't care
+ if (((init >> i) & 0x1U) != ((init >> (i ^ (1U << k))) & 0x1U)) {
+ cell->lcInfo.lutInputMask |= (1U << k);
+ break;
+ }
+ }
+ }
} else if (cell->type == id_SB_IO) {
cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT";
cell->ioInfo.global = bool_or_default(cell->attrs, id_GLOBAL);
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index 3d8ea282..07b209f1 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -137,6 +137,7 @@ struct ArchCellInfo : BaseClusterInfo
bool carryEnable;
bool negClk;
int inputCount;
+ unsigned lutInputMask;
const NetInfo *clk, *cen, *sr;
} lcInfo;
struct
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);