From 5a9ddc067588c895a483dbb314f3ff72c72fabe2 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 28 Mar 2022 14:06:09 +0100 Subject: [PATCH] ice40: Merge driving LUT<=2s into carry-only LCs Signed-off-by: gatecat --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- ice40/arch.cc | 29 ++++++++++++++-- ice40/archdefs.h | 1 + ice40/pack.cc | 63 ++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index d1e7b47b..6561ed59 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -29,7 +29,7 @@ RUN set -e -x ;\ cd /usr/local/src ;\ git clone --recursive https://github.com/YosysHQ/icestorm.git ;\ cd icestorm ;\ - git reset --hard 4bc68c9620e6be20f8fe10d20f84681d80beac23 ;\ + git reset --hard 9f66f9ce16941c6417813cb87653c735a78b53ae ;\ make -j $(nproc) ;\ make install 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 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 263903b0..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 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) { @@ -1642,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);