From 84d8e1abe767cfc93aa43e3dfb45dd2766cfd5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Milanovi=C4=87?= Date: Mon, 7 Jul 2025 10:14:48 +0200 Subject: [PATCH] Use improved CPE model (#1503) * CPE mapping improvements * Use CP_OUT for adders * Fixes * Small fixes * Cleanups * Cleanup * Cleanups * Fixes * Fixes * Optimize * Cleanup * clangformat * Cleanup * Cleanup * Bump required version of database * Cleanup * Resolve name conflicts * Fix signal routing * Make CPE_LATCH separate * Add more timings models, need updated values * Fixed warning * multiplier support from lofty/gatemate-mult * explicitly zero some params in B passthrough * comment the relevant CPE inputs in check_multipliers * Rename some of bels * remove _lower from name * refactor multiplier checking * Revert "remove _lower from name" This reverts commit daa1041bdf855b664c8f6462e1ed994459239b9c. * Fixe net name to be unique * Make sure we at least generate bitstream with all info * Simplify zero * Bounded cell type in gui * typo fix * Remove A passthrough inversion option * Clean up CarryGenCell config * Update a passthru to use new primitives * Cleanup for adders * Clean up MsbRoutingCell * Cleanup * Refactor A connection code * Make it more as in PR #1513 * Added cplines to bpassthru and fixed constant driver for A * Add parts * Added comp out connections * clangformat * clangformat * Clean up B passthrough connections * wire up a bunch of intermediate signals * Bit of cleanup * handing of C_EN_IN * C_EN_CIN fixes * connect f_route to its lines * fix cite for FRoutingCell * fixup, oops * connect multfab to its lines * Commented line * Connect CPOUTs * Handle C_I params * connect CINY1 for CarryGenCell * fix carry gen CINX * Update L2T4 model * Updates for ADDCIN * clangformat * fix some issues with multfab and f_route * look at C_I when doing inversion * Only set some C_I signals when used * Fix one more place * do not use cplines so we can merge in one cell * Cover cases that could be optimized out * clangformat * Cleanups * Disable multiplier usage for now --------- Co-authored-by: Lofty --- gui/designwidget.cc | 2 + himbaechel/uarch/gatemate/CMakeLists.txt | 1 + himbaechel/uarch/gatemate/bitstream.cc | 151 +++- himbaechel/uarch/gatemate/cells.cc | 60 +- himbaechel/uarch/gatemate/constids.inc | 144 +++- himbaechel/uarch/gatemate/extra_data.h | 13 + himbaechel/uarch/gatemate/gatemate.cc | 173 ++-- himbaechel/uarch/gatemate/gatemate.h | 3 +- himbaechel/uarch/gatemate/gen/arch_gen.py | 95 ++- himbaechel/uarch/gatemate/gfx.cc | 39 +- himbaechel/uarch/gatemate/pack.cc | 149 ++-- himbaechel/uarch/gatemate/pack.h | 37 +- himbaechel/uarch/gatemate/pack_clocking.cc | 11 +- himbaechel/uarch/gatemate/pack_cpe.cc | 232 ++--- himbaechel/uarch/gatemate/pack_io.cc | 19 +- himbaechel/uarch/gatemate/pack_mult.cc | 942 +++++++++++++++++++++ himbaechel/uarch/gatemate/route_clock.cc | 4 +- himbaechel/uarch/gatemate/tests/lut.cc | 91 ++ himbaechel/uarch/gatemate/tests/testing.cc | 1 + 19 files changed, 1868 insertions(+), 299 deletions(-) create mode 100644 himbaechel/uarch/gatemate/pack_mult.cc diff --git a/gui/designwidget.cc b/gui/designwidget.cc index b7c16d45..c14b7423 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -633,6 +633,8 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt addProperty(topItem, QVariant::String, "Type", ctx->getBelType(bel).c_str(ctx)); addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel)); addProperty(topItem, QVariant::String, "Bound Cell", ctx->nameOf(ctx->getBoundBelCell(bel)), ElementType::CELL); + addProperty(topItem, QVariant::String, "Bound Cell Type", + ctx->getBoundBelCell(bel) ? ctx->getBoundBelCell(bel)->type.c_str(ctx) : ""); addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->nameOf(ctx->getConflictingBelCell(bel)), ElementType::CELL); diff --git a/himbaechel/uarch/gatemate/CMakeLists.txt b/himbaechel/uarch/gatemate/CMakeLists.txt index 6040d498..85f706a1 100644 --- a/himbaechel/uarch/gatemate/CMakeLists.txt +++ b/himbaechel/uarch/gatemate/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES pack_clocking.cc pack_cpe.cc pack_io.cc + pack_mult.cc pack_serdes.cc pack.h pll.cc diff --git a/himbaechel/uarch/gatemate/bitstream.cc b/himbaechel/uarch/gatemate/bitstream.cc index 6a51c811..a6c70a5b 100644 --- a/himbaechel/uarch/gatemate/bitstream.cc +++ b/himbaechel/uarch/gatemate/bitstream.cc @@ -81,12 +81,12 @@ struct BitstreamBackend return invert; } - void update_cpe_lt(CellInfo *cell, IdString port, IdString init, dict ¶ms) + void update_cpe_lt(CellInfo *cell, IdString port, IdString init, dict ¶ms, bool even) { unsigned init_val = int_or_default(params, init); bool invert = need_inversion(cell, port); if (invert) { - if (port.in(id_IN1, id_IN3)) + if (even) init_val = (init_val & 0b1010) >> 1 | (init_val & 0b0101) << 1; else init_val = (init_val & 0b0011) << 2 | (init_val & 0b1100) >> 2; @@ -106,10 +106,8 @@ struct BitstreamBackend void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit, dict ¶ms) { // Mux inversion data is contained in other CPE half - Loc l = ctx->getBelLocation(cell->bel); - CellInfo *cell_u = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(l.x, l.y, 0))); unsigned init_val = int_or_default(params, param); - bool invert = need_inversion(cell_u, port); + bool invert = need_inversion(cell, port); if (invert) { int old = (init_val >> bit) & 1; int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit); @@ -178,10 +176,10 @@ struct BitstreamBackend Loc l; auto ti = *tile_extra_data(pip.tile); tile_xy(ctx->chip_info, pip.tile, l.x, l.y); - l.z = 0; + l.z = CPE_LT_U_Z; BelId cpe_bel = ctx->getBelByLocation(l); // Only if switchbox is inside core (same as sharing location with CPE) - if (cpe_bel != BelId() && ctx->getBelType(cpe_bel).in(id_CPE_HALF_L, id_CPE_HALF_U)) { + if (cpe_bel != BelId() && ctx->getBelType(cpe_bel) == id_CPE_LT_U) { // Bitstream data for certain SB_DRIVES is located in other tiles switch (word[14]) { case '3': @@ -219,8 +217,33 @@ struct BitstreamBackend } } + void check_multipliers() + { + for (auto *mult : uarch->multipliers) { + NPNR_ASSERT(mult != nullptr); + + auto should_be_inverted = mult->constr_x % 2 == 1; + + // TODO: these are errors, but downgraded to allow providing *some* output. + + // IN8 + if (need_inversion(mult, id_IN8) != should_be_inverted) + log_warning("%s.IN8 has wrong inversion state\n", mult->name.c_str(ctx)); + + // IN5 + if (need_inversion(mult, id_IN5) != should_be_inverted) + log_warning("%s.IN5 has wrong inversion state\n", mult->name.c_str(ctx)); + + // IN1 + if (need_inversion(mult, id_IN1) != should_be_inverted) + log_warning("%s.IN1 has wrong inversion state\n", mult->name.c_str(ctx)); + } + } + void write_bitstream() { + check_multipliers(); + ChipConfig cc; cc.chip_name = device; std::vector> bank(uarch->dies); @@ -241,30 +264,66 @@ struct BitstreamBackend cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits()); } break; - case id_CPE_HALF_U.index: - case id_CPE_HALF_L.index: { + case id_CPE_CPLINES.index: + case id_CPE_COMP.index: + case id_CPE_L2T4.index: + case id_CPE_ADDF.index: + case id_CPE_ADDF2.index: + case id_CPE_MULT.index: + case id_CPE_MX4.index: + case id_CPE_CONCAT.index: + case id_CPE_FF.index: + case id_CPE_LATCH.index: + case id_CPE_RAMI.index: + case id_CPE_RAMO.index: + case id_CPE_RAMIO.index: { // Update configuration bits based on signal inversion dict params = cell.second->params; - uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0); - if (cell.second->type.in(id_CPE_HALF_U) && func != C_MX4) { - update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params); - update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L00, params); - update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params); - update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L01, params); - } - if (cell.second->type.in(id_CPE_HALF_L)) { - update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L02, params); - update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L02, params); - update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L03, params); - update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L03, params); - if (func == C_MX4) { - update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0, params); - update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1, params); - update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2, params); - update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3, params); + Loc l = ctx->getBelLocation(cell.second->bel); + params.erase(id_L2T4_UPPER); + int c_i1 = int_or_default(params, id_C_I1, 0); + int c_i2 = int_or_default(params, id_C_I2, 0); + int c_i3 = int_or_default(params, id_C_I3, 0); + int c_i4 = int_or_default(params, id_C_I4, 0); + if (cell.second->type.in(id_CPE_L2T4, id_CPE_LT_L, id_CPE_LT_U)) { + if (l.z == CPE_LT_U_Z) { + update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params, true); + update_cpe_lt(cell.second.get(), c_i1 ? id_PINY1 : id_IN2, id_INIT_L00, params, false); + update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params, true); + update_cpe_lt(cell.second.get(), c_i2 ? id_CINX : id_IN4, id_INIT_L01, params, false); + } else { + // These will be renamed later + update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params, true); + update_cpe_lt(cell.second.get(), c_i3 ? id_PINY1 : id_IN2, id_INIT_L00, params, false); + update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params, true); + update_cpe_lt(cell.second.get(), c_i4 ? id_PINX : id_IN4, id_INIT_L01, params, false); } } - if (cell.second->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) { + if (l.z == CPE_LT_FULL_Z) { + if (!cell.second->type.in(id_CPE_MULT)) { + if (cell.second->type.in(id_CPE_MX4)) { + update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0, params); + update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1, params); + update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2, params); + update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3, params); + update_cpe_lt(cell.second.get(), id_IN5, id_INIT_L02, params, true); + update_cpe_lt(cell.second.get(), c_i3 ? id_PINY1 : id_IN6, id_INIT_L02, params, false); + update_cpe_lt(cell.second.get(), id_IN7, id_INIT_L03, params, true); + update_cpe_lt(cell.second.get(), c_i4 ? id_PINX : id_IN8, id_INIT_L03, params, false); + } else { + update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params, true); + update_cpe_lt(cell.second.get(), c_i1 ? id_PINY1 : id_IN2, id_INIT_L00, params, false); + update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params, true); + update_cpe_lt(cell.second.get(), c_i2 ? id_CINX : id_IN4, id_INIT_L01, params, false); + update_cpe_lt(cell.second.get(), id_IN5, id_INIT_L02, params, true); + update_cpe_lt(cell.second.get(), c_i3 ? id_PINY1 : id_IN6, id_INIT_L02, params, false); + update_cpe_lt(cell.second.get(), id_IN7, id_INIT_L03, params, true); + update_cpe_lt(cell.second.get(), c_i4 ? id_PINX : id_IN8, id_INIT_L03, params, false); + } + } + } + + if (cell.second->type.in(id_CPE_FF, id_CPE_LATCH)) { update_cpe_inv(cell.second.get(), id_CLK, id_C_CPE_CLK, params); update_cpe_inv(cell.second.get(), id_EN, id_C_CPE_EN, params); bool set = int_or_default(params, id_C_EN_SR, 0) == 1; @@ -275,7 +334,43 @@ struct BitstreamBackend } int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id; for (auto &p : params) { - cc.tiles[loc].add_word(stringf("CPE%d.%s", id, p.first.c_str(ctx)), p.second.as_bits()); + IdString name = p.first; + switch (l.z) { + case CPE_LT_L_Z: + switch (p.first.index) { + case id_INIT_L00.index: + name = id_INIT_L02; + break; + case id_INIT_L01.index: + name = id_INIT_L03; + break; + case id_INIT_L10.index: + name = id_INIT_L11; + break; + } + break; + case CPE_RAMIO_U_Z: + switch (p.first.index) { + case id_C_RAM_I.index: + name = id_C_RAM_I2; + break; + case id_C_RAM_O.index: + name = id_C_RAM_O2; + break; + } + break; + case CPE_RAMIO_L_Z: + switch (p.first.index) { + case id_C_RAM_I.index: + name = id_C_RAM_I1; + break; + case id_C_RAM_O.index: + name = id_C_RAM_O1; + break; + } + break; + } + cc.tiles[loc].add_word(stringf("CPE%d.%s", id, name.c_str(ctx)), p.second.as_bits()); } } break; case id_CLKIN.index: { diff --git a/himbaechel/uarch/gatemate/cells.cc b/himbaechel/uarch/gatemate/cells.cc index 9be8ea67..67288382 100644 --- a/himbaechel/uarch/gatemate/cells.cc +++ b/himbaechel/uarch/gatemate/cells.cc @@ -33,19 +33,30 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name) cell->ports[id].name = id; cell->ports[id].type = dir; }; - if (type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) { - add_port(id_I1, PORT_IN); - add_port(id_I2, PORT_IN); - add_port(id_I3, PORT_IN); - add_port(id_I4, PORT_IN); - add_port(id_RAM_I, PORT_IN); + if (type.in(id_CPE_LT, id_CPE_LT_U, id_CPE_LT_L, id_CPE_L2T4, id_CPE_DUMMY)) { + add_port(id_IN1, PORT_IN); + add_port(id_IN2, PORT_IN); + add_port(id_IN3, PORT_IN); + add_port(id_IN4, PORT_IN); add_port(id_OUT, PORT_OUT); - add_port(id_RAM_O, PORT_OUT); - add_port(id_EN, PORT_IN); - add_port(id_CLK, PORT_IN); - add_port(id_SR, PORT_IN); - if (type == id_CPE_HALF_L) { + add_port(id_CPOUT, PORT_OUT); + // These are used to propagate alternate inputs for first LUT2 levels + add_port(id_CINX, PORT_IN); + add_port(id_PINX, PORT_IN); + add_port(id_PINY1, PORT_IN); + // For EN_CIN input + add_port(id_CINY1, PORT_IN); + if (type.in(id_CPE_LT_L)) { + add_port(id_CINY2, PORT_IN); + add_port(id_PINY2, PORT_IN); + add_port(id_COMBIN, PORT_IN); + + add_port(id_COUTX, PORT_OUT); + add_port(id_POUTX, PORT_OUT); add_port(id_COUTY1, PORT_OUT); + add_port(id_POUTY1, PORT_OUT); + add_port(id_COUTY2, PORT_OUT); + add_port(id_POUTY2, PORT_OUT); } } else if (type.in(id_CLKIN)) { for (int i = 0; i < 4; i++) { @@ -68,6 +79,33 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name) } else if (type.in(id_CC_BUFG)) { add_port(id_I, PORT_IN); add_port(id_O, PORT_OUT); + } else if (type.in(id_CPE_RAMIO, id_CPE_RAMI, id_CPE_RAMO)) { + add_port(id_I, PORT_IN); + add_port(id_RAM_I, PORT_IN); + add_port(id_RAM_O, PORT_OUT); + add_port(id_OUT, PORT_OUT); + } else if (type.in(id_CPE_COMP)) { + add_port(id_COMB1, PORT_IN); + add_port(id_COMB2, PORT_IN); + add_port(id_COMPOUT, PORT_OUT); + } else if (type.in(id_CPE_CPLINES)) { + add_port(id_OUT1, PORT_IN); + add_port(id_OUT2, PORT_IN); + add_port(id_COMPOUT, PORT_IN); + + add_port(id_CINX, PORT_IN); + add_port(id_PINX, PORT_IN); + add_port(id_CINY1, PORT_IN); + add_port(id_PINY1, PORT_IN); + add_port(id_CINY2, PORT_IN); + add_port(id_PINY2, PORT_IN); + + add_port(id_COUTX, PORT_OUT); + add_port(id_POUTX, PORT_OUT); + add_port(id_COUTY1, PORT_OUT); + add_port(id_POUTY1, PORT_OUT); + add_port(id_COUTY2, PORT_OUT); + add_port(id_POUTY2, PORT_OUT); } else { log_error("Trying to create unknown cell type %s\n", type.c_str(ctx)); } diff --git a/himbaechel/uarch/gatemate/constids.inc b/himbaechel/uarch/gatemate/constids.inc index 9e5ba03f..d68a5196 100644 --- a/himbaechel/uarch/gatemate/constids.inc +++ b/himbaechel/uarch/gatemate/constids.inc @@ -903,37 +903,49 @@ X(VALID) X(CC_USR_RSTN) X(USR_RSTN) -// hardware primitive CPE_HALF_U -X(CPE_HALF_U) -// CPE_HALF_U pins -X(RAM_I) +// hardware primitive CPE_LT_U +X(CPE_LT_U) +// CPE_LT_U pins X(IN1) X(IN2) X(IN3) X(IN4) +X(OUT) +X(CPOUT) +X(PINY1) +X(CINX) + +// hardware primitive CPE_FF_U +X(CPE_FF_U) +// CPE_FF_U pins +X(DIN) //X(CLK) //X(EN) //X(SR) -X(OUT) +X(DOUT) + +// hardware primitive CPE_RAMIO_U +X(CPE_RAMIO_U) +// CPE_RAMIO_U pins +X(RAM_I) +//X(I) +//X(OUT) X(RAM_O) -// hardware primitive CPE_HALF_L -X(CPE_HALF_L) -// CPE_HALF_L pins -//X(RAM_I) +// hardware primitive CPE_LT_L +X(CPE_LT_L) +// CPE_LT_L pins //X(IN1) //X(IN2) //X(IN3) //X(IN4) -//X(CLK) -//X(EN) -//X(SR) +X(COMBIN) //X(OUT) -//X(RAM_O) -X(CINX) +//X(CPOUT) +//X(CINX) X(PINX) X(CINY1) -X(PINY1) +//X(PINY1) X(CINY2) X(PINY2) X(COUTX) @@ -943,13 +955,88 @@ X(POUTY1) X(COUTY2) X(POUTY2) +// hardware primitive CPE_FF_L +X(CPE_FF_L) +// CPE_FF_L pins +//X(DIN) +//X(CLK) +//X(EN) +//X(SR) +//X(DOUT) + +// hardware primitive CPE_RAMIO_L +X(CPE_RAMIO_L) +// CPE_RAMIO_L pins +//X(RAM_I) +//X(I) +//X(OUT) +//X(RAM_O) + +// hardware primitive CPE_LT_FULL +X(CPE_LT_FULL) +// CPE_LT_FULL pins +//X(IN1) +//X(IN2) +//X(IN3) +//X(IN4) +X(IN5) +X(IN6) +X(IN7) +X(IN8) +X(OUT1) +X(OUT2) +X(CPOUT1) +X(CPOUT2) +X(MUXOUT) +//X(CINX) +//X(PINX) +//X(CINY1) +//X(PINY1) +//X(CINY2) +//X(PINY2) +//X(COUTX) +//X(POUTX) +//X(COUTY1) +//X(POUTY1) +//X(COUTY2) +//X(POUTY2) +//X(CLK) +//X(EN) +//X(SR) + +// hardware primitive CPE_COMP +X(CPE_COMP) +// CPE_COMP pins +X(COMB1) +X(COMB2) +X(COMPOUT) + +// hardware primitive CPE_CPLINES +X(CPE_CPLINES) +// CPE_CPLINES pins +//X(OUT1) +//X(OUT2) +//X(COMPOUT) +//X(CINX) +//X(PINX) +//X(CINY1) +//X(PINY1) +//X(CINY2) +//X(PINY2) +//X(COUTX) +//X(POUTX) +//X(COUTY1) +//X(POUTY1) +//X(COUTY2) +//X(POUTY2) + // hardware primitive GPIO X(GPIO) // GPIO pins //X(IN1) //X(IN2) -X(OUT1) -X(OUT2) +//X(OUT1) +//X(OUT2) X(OUT3) X(OUT4) //X(DDR) @@ -2000,7 +2087,6 @@ X(C_PY2_I) X(C_C_P) X(C_2D_IN) X(C_SN) -X(C_O) X(C_O1) X(C_O2) X(C_BR) @@ -2017,8 +2103,6 @@ X(C_EN_SR) X(C_CLKSEL) X(C_ENSEL) X(FF_INIT) -// Timing model for CPE -X(CPE_DFF) // GPIO configuration parameters X(OPEN_DRAIN) @@ -2055,7 +2139,11 @@ X(LVDS_EN) X(LVDS_IE) //X(LVDS_RTERM) -X(CPE_HALF) +X(CPE_LT) +X(CPE_FF) +X(CPE_RAMIO) +X(CPE_RAMI) +X(CPE_RAMO) X(RAM_I1) X(RAM_I2) X(RAM_O1) @@ -2170,3 +2258,17 @@ X(CPE_LVDS_IBUF) X(CPE_LVDS_OBUF) X(CPE_LVDS_TOBUF) X(CPE_LVDS_IOBUF) + +X(CPE_L2T4) +X(CPE_ADDF) +X(CPE_ADDF2) +X(CPE_MULT) +X(CPE_MX4) +//X(CPE_EN_CIN) +X(CPE_CONCAT) +//X(CPE_ADDCIN) +X(CPE_DUMMY) +X(CPE_LATCH) +X(L2T4_UPPER) +X(CPE_MX8) +X(CPE_BRIDGE) diff --git a/himbaechel/uarch/gatemate/extra_data.h b/himbaechel/uarch/gatemate/extra_data.h index 163fe805..8ce126dc 100644 --- a/himbaechel/uarch/gatemate/extra_data.h +++ b/himbaechel/uarch/gatemate/extra_data.h @@ -85,6 +85,19 @@ enum CPEFunction C_ADDCIN = 7, }; +enum CPE_Z +{ + CPE_LT_U_Z = 0, + CPE_LT_L_Z = 1, + CPE_FF_U_Z = 2, + CPE_FF_L_Z = 3, + CPE_RAMIO_U_Z = 4, + CPE_RAMIO_L_Z = 5, + CPE_COMP_Z = 6, + CPE_CPLINES_Z = 7, + CPE_LT_FULL_Z = 8, +}; + enum ClusterPlacement { PLACE_DB_CONSTR = 32, diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index c5bdefa1..35f6e2d4 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -18,7 +18,6 @@ */ #include "gatemate.h" -#include "design_utils.h" #include "log.h" #include "placer_heap.h" @@ -91,9 +90,10 @@ bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const if (cell->belStrength != PlaceStrength::STRENGTH_FIXED && tile_extra_data(bel.tile)->die != preferred_die) return false; - if (ctx->getBelType(bel).in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U)) { + if (getBelBucketForCellType(ctx->getBelType(bel)) == id_CPE_FF) { Loc loc = ctx->getBelLocation(bel); - const CellInfo *adj_half = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z == 1 ? 0 : 1))); + const CellInfo *adj_half = ctx->getBoundBelCell( + ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z == CPE_FF_L_Z ? CPE_FF_U_Z : CPE_FF_L_Z))); if (adj_half) { const auto &half_data = fast_cell_info.at(cell->flat_index); if (half_data.dff_used) { @@ -127,7 +127,7 @@ Loc GateMateImpl::getRelativeConstraint(Loc &root_loc, IdString id) const child_loc.y = root_loc.y + p->constr_y; child_loc.z = p->constr_z; } else { - log_error("Constrain info not available for pin.\n"); + log_error("Constrain info not available for pin '%s'.\n", id.c_str(ctx)); } } else { log_error("Bel info not available for constraints.\n"); @@ -192,29 +192,95 @@ void GateMateImpl::prePlace() { assign_cell_info(); } void GateMateImpl::postPlace() { ctx->assignArchInfo(); + std::vector delete_cells; for (auto &cell : ctx->cells) { - if (cell.second->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) { + if (cell.second->type.in(id_CPE_L2T4)) { Loc l = ctx->getBelLocation(cell.second->bel); - if (l.z == 0) { // CPE_HALF_U - if (cell.second->params.count(id_C_O) && int_or_default(cell.second->params, id_C_O, 0) == 0) - cell.second->params[id_C_2D_IN] = Property(1, 1); - rename_param(cell.second.get(), id_C_O, id_C_O2, 2); - rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I2, 1); - rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O2, 1); - cell.second->type = id_CPE_HALF_U; - } else { // CPE_HALF_L + if (l.z == CPE_LT_L_Z) { if (!cell.second->params.count(id_INIT_L20)) cell.second->params[id_INIT_L20] = Property(0b1100, 4); - rename_param(cell.second.get(), id_C_O, id_C_O1, 2); - rename_param(cell.second.get(), id_INIT_L00, id_INIT_L02, 4); - rename_param(cell.second.get(), id_INIT_L01, id_INIT_L03, 4); - rename_param(cell.second.get(), id_INIT_L10, id_INIT_L11, 4); - rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I1, 1); - rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O1, 1); - cell.second->type = id_CPE_HALF_L; } + cell.second->params[id_L2T4_UPPER] = Property((l.z == CPE_LT_U_Z) ? 1 : 0, 1); + } else if (cell.second->type.in(id_CPE_LT_L)) { + BelId bel = cell.second->bel; + PlaceStrength strength = cell.second->belStrength; + uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0); + Loc loc = ctx->getBelLocation(bel); + loc.z = CPE_LT_FULL_Z; + ctx->unbindBel(bel); + ctx->bindBel(ctx->getBelByLocation(loc), cell.second.get(), strength); + cell.second->renamePort(id_IN1, id_IN5); + cell.second->renamePort(id_IN2, id_IN6); + cell.second->renamePort(id_IN3, id_IN7); + cell.second->renamePort(id_IN4, id_IN8); + cell.second->renamePort(id_OUT, id_OUT1); + cell.second->renamePort(id_CPOUT, id_CPOUT1); + if (!cell.second->params.count(id_INIT_L20)) + cell.second->params[id_INIT_L20] = Property(0b1100, 4); + rename_param(cell.second.get(), id_INIT_L00, id_INIT_L02, 4); + rename_param(cell.second.get(), id_INIT_L01, id_INIT_L03, 4); + rename_param(cell.second.get(), id_INIT_L10, id_INIT_L11, 4); + + switch (func) { + case C_ADDF: + cell.second->type = id_CPE_ADDF; + break; + case C_ADDF2: + cell.second->type = id_CPE_ADDF2; + break; + case C_MULT: + cell.second->type = id_CPE_MULT; + break; + case C_MX4: + cell.second->type = id_CPE_MX4; + break; + case C_EN_CIN: + log_error("EN_CIN should be using L2T4.\n"); + break; + case C_CONCAT: + cell.second->type = id_CPE_CONCAT; + break; + case C_ADDCIN: + log_error("ADDCIN should be using L2T4.\n"); + break; + default: + break; + } + + loc.z = CPE_LT_U_Z; + CellInfo *upper = ctx->getBoundBelCell(ctx->getBelByLocation(loc)); + if (upper->params.count(id_INIT_L00)) + cell.second->params[id_INIT_L00] = Property(int_or_default(upper->params, id_INIT_L00, 0), 4); + if (upper->params.count(id_INIT_L01)) + cell.second->params[id_INIT_L01] = Property(int_or_default(upper->params, id_INIT_L01, 0), 4); + if (upper->params.count(id_INIT_L10)) + cell.second->params[id_INIT_L10] = Property(int_or_default(upper->params, id_INIT_L10, 0), 4); + if (upper->params.count(id_C_I1)) + cell.second->params[id_C_I1] = Property(int_or_default(upper->params, id_C_I1, 0), 1); + if (upper->params.count(id_C_I2)) + cell.second->params[id_C_I2] = Property(int_or_default(upper->params, id_C_I2, 0), 1); + upper->movePortTo(id_IN1, cell.second.get(), id_IN1); + upper->movePortTo(id_IN2, cell.second.get(), id_IN2); + upper->movePortTo(id_IN3, cell.second.get(), id_IN3); + upper->movePortTo(id_IN4, cell.second.get(), id_IN4); + upper->movePortTo(id_OUT, cell.second.get(), id_OUT2); + upper->movePortTo(id_CPOUT, cell.second.get(), id_CPOUT2); + + } + // Mark for deletion + else if (cell.second->type.in(id_CPE_LT_U, id_CPE_DUMMY)) { + delete_cells.push_back(cell.second->name); } } + for (auto pcell : delete_cells) { + for (auto &port : ctx->cells[pcell]->ports) { + ctx->cells[pcell]->disconnectPort(port.first); + } + ctx->unbindBel(ctx->cells[pcell]->bel); + ctx->cells.erase(pcell); + } + delete_cells.clear(); + ctx->assignArchInfo(); } void GateMateImpl::preRoute() { route_clock(); } @@ -222,7 +288,6 @@ void GateMateImpl::preRoute() { route_clock(); } void GateMateImpl::postRoute() { ctx->assignArchInfo(); - print_utilisation(ctx); const ArchArgs &args = ctx->args; if (args.options.count("out")) { @@ -242,28 +307,25 @@ void GateMateImpl::assign_cell_info() for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); auto &fc = fast_cell_info.at(ci->flat_index); - if (ci->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) { - fc.signal_used = int_or_default(ci->params, id_C_O, -1); + if (getBelBucketForCellType(ci->type) == id_CPE_FF) { fc.ff_en = ci->getPort(id_EN); fc.ff_clk = ci->getPort(id_CLK); fc.ff_sr = ci->getPort(id_SR); fc.ff_config = 0; - if (fc.signal_used == 0) { - fc.ff_config |= int_or_default(ci->params, id_C_CPE_EN, 0); - fc.ff_config <<= 2; - fc.ff_config |= int_or_default(ci->params, id_C_CPE_CLK, 0); - fc.ff_config <<= 2; - fc.ff_config |= int_or_default(ci->params, id_C_CPE_RES, 0); - fc.ff_config <<= 2; - fc.ff_config |= int_or_default(ci->params, id_C_CPE_SET, 0); - fc.ff_config <<= 2; - fc.ff_config |= int_or_default(ci->params, id_C_EN_SR, 0); - fc.ff_config <<= 1; - fc.ff_config |= int_or_default(ci->params, id_C_L_D, 0); - fc.ff_config <<= 1; - fc.ff_config |= int_or_default(ci->params, id_FF_INIT, 0); - fc.dff_used = true; - } + fc.ff_config |= int_or_default(ci->params, id_C_CPE_EN, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_CPE_CLK, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_CPE_RES, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_CPE_SET, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_EN_SR, 0); + fc.ff_config <<= 1; + fc.ff_config |= int_or_default(ci->params, id_C_L_D, 0); + fc.ff_config <<= 1; + fc.ff_config |= int_or_default(ci->params, id_FF_INIT, 0); + fc.dff_used = true; } } } @@ -274,8 +336,12 @@ IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const if (cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF, id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF)) return id_GPIO; - else if (cell_type.in(id_CPE_HALF_U, id_CPE_HALF_L, id_CPE_HALF)) - return id_CPE_HALF; + else if (cell_type.in(id_CPE_LT_U, id_CPE_LT_L, id_CPE_LT, id_CPE_L2T4)) + return id_CPE_LT; + else if (cell_type.in(id_CPE_FF_U, id_CPE_FF_L, id_CPE_FF, id_CPE_LATCH)) + return id_CPE_FF; + else if (cell_type.in(id_CPE_RAMIO, id_CPE_RAMI, id_CPE_RAMO)) + return id_CPE_RAMIO; else return cell_type; } @@ -283,8 +349,12 @@ IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const BelBucketId GateMateImpl::getBelBucketForBel(BelId bel) const { IdString bel_type = ctx->getBelType(bel); - if (bel_type.in(id_CPE_HALF_U, id_CPE_HALF_L)) - return id_CPE_HALF; + if (bel_type.in(id_CPE_LT_U, id_CPE_LT_L)) + return id_CPE_LT; + else if (bel_type.in(id_CPE_FF_U, id_CPE_FF_L)) + return id_CPE_FF; + else if (bel_type.in(id_CPE_RAMIO_U, id_CPE_RAMIO_L)) + return id_CPE_RAMIO; return bel_type; } @@ -294,10 +364,16 @@ bool GateMateImpl::isValidBelForCellType(IdString cell_type, BelId bel) const if (bel_type == id_GPIO) return cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF, id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF); - else if (bel_type == id_CPE_HALF_U) - return cell_type.in(id_CPE_HALF_U, id_CPE_HALF); - else if (bel_type == id_CPE_HALF_L) - return cell_type.in(id_CPE_HALF_L, id_CPE_HALF); + else if (bel_type == id_CPE_LT_U) + return cell_type.in(id_CPE_LT_U, id_CPE_LT, id_CPE_L2T4, id_CPE_DUMMY); + else if (bel_type == id_CPE_LT_L) + return cell_type.in(id_CPE_LT_L, id_CPE_LT, id_CPE_L2T4, id_CPE_DUMMY); + else if (bel_type == id_CPE_FF_U) + return cell_type.in(id_CPE_FF_U, id_CPE_FF, id_CPE_LATCH); + else if (bel_type == id_CPE_FF_L) + return cell_type.in(id_CPE_FF_L, id_CPE_FF, id_CPE_LATCH); + else if (bel_type.in(id_CPE_RAMIO_U, id_CPE_RAMIO_L)) + return cell_type.in(id_CPE_RAMIO, id_CPE_RAMI, id_CPE_RAMO); else return (bel_type == cell_type); } @@ -321,7 +397,8 @@ struct GateMateArch : HimbaechelArch { return device.size() > 6 && device.substr(0, 6) == "CCGM1A"; } - std::unique_ptr create(const std::string &device, const dict &args) + std::unique_ptr create(const std::string &device, + const dict &args) override { return std::make_unique(); } diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index e4f23e24..f63b38c6 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -63,6 +63,7 @@ struct GateMateImpl : HimbaechelAPI bool isPipInverting(PipId pip) const override; const GateMateTileExtraDataPOD *tile_extra_data(int tile) const; + void rename_param(CellInfo *cell, IdString name, IdString new_name, int width); std::set available_pads; std::map bel_to_pad; @@ -70,6 +71,7 @@ struct GateMateImpl : HimbaechelAPI dict, Loc> locations; int dies; int preferred_die; + std::vector multipliers; private: bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, @@ -80,7 +82,6 @@ struct GateMateImpl : HimbaechelAPI void parse_ccf(const std::string &filename); void assign_cell_info(); - void rename_param(CellInfo *cell, IdString name, IdString new_name, int width); void route_clock(); const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const; diff --git a/himbaechel/uarch/gatemate/gen/arch_gen.py b/himbaechel/uarch/gatemate/gen/arch_gen.py index 8e3cf04d..5949610e 100644 --- a/himbaechel/uarch/gatemate/gen/arch_gen.py +++ b/himbaechel/uarch/gatemate/gen/arch_gen.py @@ -134,32 +134,101 @@ def set_timings(ch): speed = "DEFAULT" tmg = ch.set_speed_grades([speed]) - lut = ch.timing.add_cell_variant(speed, "CPE_HALF_L") + lut = ch.timing.add_cell_variant(speed, "CPE_LT_L") lut.add_comb_arc("IN1", "OUT", TimingValue(416, 418)) # IN5 to OUT1 lut.add_comb_arc("IN2", "OUT", TimingValue(413, 422)) # IN6 to OUT1 lut.add_comb_arc("IN3", "OUT", TimingValue(372, 374)) # IN7 to OUT1 lut.add_comb_arc("IN4", "OUT", TimingValue(275, 385)) # IN8 to OUT1 - lut = ch.timing.add_cell_variant(speed, "CPE_HALF_U") + lut = ch.timing.add_cell_variant(speed, "CPE_LT_U") lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2 lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2 lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2 lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2 - lut = ch.timing.add_cell_variant(speed, "CPE_HALF") + lut = ch.timing.add_cell_variant(speed, "CPE_LT") lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2 lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2 lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2 lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2 - dff = ch.timing.add_cell_variant(speed, "CPE_DFF") - dff.add_setup_hold("CLK", "IN1", ClockEdge.RISING, TimingValue(60), TimingValue(50)) - dff.add_setup_hold("CLK", "IN2", ClockEdge.RISING, TimingValue(60), TimingValue(50)) - dff.add_setup_hold("CLK", "IN3", ClockEdge.RISING, TimingValue(60), TimingValue(50)) - dff.add_setup_hold("CLK", "IN4", ClockEdge.RISING, TimingValue(60), TimingValue(50)) - dff.add_clock_out("CLK", "OUT", ClockEdge.RISING, TimingValue(60)) + lut = ch.timing.add_cell_variant(speed, "CPE_L2T4") + lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2 -EXPECTED_VERSION = 1.2 + lut = ch.timing.add_cell_variant(speed, "CPE_L2T5") + lut.add_comb_arc("IN1", "OUT1", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "OUT1", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "OUT1", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "OUT1", TimingValue(443, 453)) # to OUT2 + lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1 + lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1 + lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1 + lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1 + + lut = ch.timing.add_cell_variant(speed, "CPE_MX4") + lut.add_comb_arc("IN1", "OUT1", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "OUT1", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "OUT1", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "OUT1", TimingValue(443, 453)) # to OUT2 + lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1 + lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1 + lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1 + lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1 + + lut = ch.timing.add_cell_variant(speed, "CPE_CI") + lut.add_comb_arc("IN1", "COUTY1", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "COUTY1", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "COUTY1", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "COUTY1", TimingValue(443, 453)) # to OUT2 + + lut = ch.timing.add_cell_variant(speed, "CPE_EN_CIN") + lut.add_comb_arc("CINY1", "OUT1", TimingValue(479, 484)) # to OUT2 + + lut = ch.timing.add_cell_variant(speed, "CPE_ADDF") + lut.add_comb_arc("IN1", "OUT2", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "OUT2", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "OUT2", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "OUT2", TimingValue(443, 453)) # to OUT2 + lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1 + lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1 + lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1 + lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1 + lut.add_comb_arc("CINY1", "COUTY1", TimingValue(479, 484)) + + lut = ch.timing.add_cell_variant(speed, "CPE_ADDF2") + lut.add_comb_arc("IN1", "OUT2", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "OUT2", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "OUT2", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "OUT2", TimingValue(443, 453)) # to OUT2 + lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1 + lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1 + lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1 + lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1 + lut.add_comb_arc("CINY1", "COUTY1", TimingValue(479, 484)) + + dff = ch.timing.add_cell_variant(speed, "CPE_FF") + dff.add_setup_hold("CLK", "DIN", ClockEdge.RISING, TimingValue(60), TimingValue(50)) + dff.add_clock_out("CLK", "DOUT", ClockEdge.RISING, TimingValue(60)) + + dff = ch.timing.add_cell_variant(speed, "CPE_LATCH") + dff.add_setup_hold("CLK", "DIN", ClockEdge.RISING, TimingValue(60), TimingValue(50)) + dff.add_clock_out("CLK", "DOUT", ClockEdge.RISING, TimingValue(60)) + + lut = ch.timing.add_cell_variant(speed, "CPE_RAMI") + lut.add_comb_arc("RAM_I", "OUT", TimingValue(0, 0)) + + lut = ch.timing.add_cell_variant(speed, "CPE_RAMO") + lut.add_comb_arc("I", "RAM_O", TimingValue(0, 0)) + + lut = ch.timing.add_cell_variant(speed, "CPE_RAMIO") + #lut.add_comb_arc("I", "OUT", TimingValue(0, 0)) + lut.add_comb_arc("I", "RAM_O", TimingValue(0, 0)) + lut.add_comb_arc("RAM_I", "OUT", TimingValue(0, 0)) + +EXPECTED_VERSION = 1.3 def main(): # Range needs to be +1, but we are adding +2 more to coordinates, since @@ -199,9 +268,11 @@ def main(): tt.create_wire(wire.name, wire.type) for prim in sorted(die.get_primitives_for_type(type_name)): bel = tt.create_bel(prim.name, prim.type, prim.z) + if (prim.name in ["CPE_LT_FULL"]): + bel.flags |= BEL_FLAG_HIDDEN extra = BelExtraData() for constr in sorted(die.get_pins_constraint(type_name, prim.name, prim.type)): - extra.add_constraints(ch.strs.id(constr.name),constr.rel_x,constr.rel_y,0 if constr.pin_num==2 else 1) + extra.add_constraints(ch.strs.id(constr.name),constr.rel_x,constr.rel_y,4 if constr.pin_num==2 else 5) bel.extra_data = extra for pin in sorted(die.get_primitive_pins(prim.type)): tt.add_bel_pin(bel, pin.name, die.get_pin_connection_name(prim,pin), pin.dir) @@ -239,7 +310,7 @@ def main(): pkg = ch.create_package(package) for pad in sorted(dev.get_package_pads(package)): pp = pkg.create_pad(pad.name, f"X{pad.x+2}Y{pad.y+2}", pad.bel, pad.function, pad.bank, pad.flags) - pp.extra_data = PadExtraData(pad.ddr.x+2, pad.ddr.y+2, pad.ddr.z) + pp.extra_data = PadExtraData(pad.ddr.x+2, pad.ddr.y+2, 4 if pad.ddr.z==0 else 5) ch.write_bba(args.bba) diff --git a/himbaechel/uarch/gatemate/gfx.cc b/himbaechel/uarch/gatemate/gfx.cc index bdd5e068..db9b1e84 100644 --- a/himbaechel/uarch/gatemate/gfx.cc +++ b/himbaechel/uarch/gatemate/gfx.cc @@ -34,20 +34,55 @@ void GateMateImpl::drawBel(std::vector &g, GraphicElement::style el.type = GraphicElement::TYPE_BOX; el.style = style; switch (bel_type.index) { - case id_CPE_HALF_L.index: + case id_CPE_LT_FULL.index: + el.x1 = loc.x + 0.21; + el.x2 = el.x1 + 0.18; + el.y1 = loc.y + 0.25; + el.y2 = el.y1 + 0.50; + g.push_back(el); + break; + case id_CPE_LT_L.index: el.x1 = loc.x + 0.20; el.x2 = el.x1 + 0.20; el.y1 = loc.y + 0.25; el.y2 = el.y1 + 0.20; g.push_back(el); break; - case id_CPE_HALF_U.index: + case id_CPE_LT_U.index: el.x1 = loc.x + 0.20; el.x2 = el.x1 + 0.20; el.y1 = loc.y + 0.55; el.y2 = el.y1 + 0.20; g.push_back(el); break; + case id_CPE_FF_L.index: + el.x1 = loc.x + 0.45; + el.x2 = el.x1 + 0.10; + el.y1 = loc.y + 0.25; + el.y2 = el.y1 + 0.20; + g.push_back(el); + break; + case id_CPE_FF_U.index: + el.x1 = loc.x + 0.45; + el.x2 = el.x1 + 0.10; + el.y1 = loc.y + 0.55; + el.y2 = el.y1 + 0.20; + g.push_back(el); + break; + case id_CPE_RAMIO_L.index: + el.x1 = loc.x + 0.60; + el.x2 = el.x1 + 0.10; + el.y1 = loc.y + 0.25; + el.y2 = el.y1 + 0.20; + g.push_back(el); + break; + case id_CPE_RAMIO_U.index: + el.x1 = loc.x + 0.60; + el.x2 = el.x1 + 0.10; + el.y1 = loc.y + 0.55; + el.y2 = el.y1 + 0.20; + g.push_back(el); + break; case id_RAM.index: el.x1 = loc.x + 0.70; el.x2 = el.x1 + 0.20; diff --git a/himbaechel/uarch/gatemate/pack.cc b/himbaechel/uarch/gatemate/pack.cc index 053bf3a7..840e95fd 100644 --- a/himbaechel/uarch/gatemate/pack.cc +++ b/himbaechel/uarch/gatemate/pack.cc @@ -47,39 +47,69 @@ void GateMatePacker::disconnect_if_gnd(CellInfo *cell, IdString input) } } -CellInfo *GateMatePacker::move_ram_i(CellInfo *cell, IdString origPort, bool place) +std::pair GateMatePacker::move_ram_i(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc) { CellInfo *cpe_half = nullptr; + CellInfo *cpe_ramio = nullptr; NetInfo *net = cell->getPort(origPort); if (net) { - cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), origPort.c_str(ctx))); + cpe_ramio = create_cell_ptr(id_CPE_RAMI, ctx->idf("%s$%s_rami", cell->name.c_str(ctx), origPort.c_str(ctx))); if (place) { - cell->constr_children.push_back(cpe_half); + cell->constr_children.push_back(cpe_ramio); + cpe_ramio->cluster = cell->cluster; + cpe_ramio->constr_abs_z = false; + cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index; + } else { + BelId b = ctx->getBelByLocation(cpe_loc); + ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED); + } + CellInfo *cpe_half = + create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), origPort.c_str(ctx))); + if (place) { + cpe_ramio->constr_children.push_back(cpe_half); cpe_half->cluster = cell->cluster; cpe_half->constr_abs_z = false; - cpe_half->constr_z = PLACE_DB_CONSTR + origPort.index; + cpe_half->constr_z = -4; + } else { + BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4)); + ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED); } - cpe_half->params[id_C_RAM_I] = Property(1, 1); - NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx))); - cell->movePortTo(origPort, cpe_half, id_OUT); + cpe_ramio->params[id_C_RAM_I] = Property(1, 1); + + NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_ramio->name.c_str(ctx))); + cell->movePortTo(origPort, cpe_ramio, id_OUT); cell->connectPort(origPort, ram_i); - cpe_half->connectPort(id_RAM_I, ram_i); + cpe_ramio->connectPort(id_RAM_I, ram_i); } - return cpe_half; + return std::make_pair(cpe_half, cpe_ramio); } -CellInfo *GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool place) +std::pair GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc) { CellInfo *cpe_half = nullptr; + CellInfo *cpe_ramio = nullptr; NetInfo *net = cell->getPort(origPort); if (net) { - cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), origPort.c_str(ctx))); + cpe_ramio = create_cell_ptr(id_CPE_RAMO, ctx->idf("%s$%s_ramo", cell->name.c_str(ctx), origPort.c_str(ctx))); if (place) { - cell->constr_children.push_back(cpe_half); + cell->constr_children.push_back(cpe_ramio); + cpe_ramio->cluster = cell->cluster; + cpe_ramio->constr_abs_z = false; + cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index; + } else { + BelId b = ctx->getBelByLocation(cpe_loc); + ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED); + } + cpe_half = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), origPort.c_str(ctx))); + if (place) { + cpe_ramio->constr_children.push_back(cpe_half); cpe_half->cluster = cell->cluster; cpe_half->constr_abs_z = false; - cpe_half->constr_z = PLACE_DB_CONSTR + origPort.index; + cpe_half->constr_z = -4; + } else { + BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4)); + ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED); } if (net->name == ctx->id("$PACKER_GND")) { cpe_half->params[id_INIT_L00] = Property(0b0000, 4); @@ -92,50 +122,47 @@ CellInfo *GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool pla cell->movePortTo(origPort, cpe_half, id_IN1); } cpe_half->params[id_INIT_L10] = Property(0b1010, 4); - cpe_half->params[id_C_O] = Property(0b11, 2); - cpe_half->params[id_C_RAM_O] = Property(1, 1); + cpe_ramio->params[id_C_RAM_O] = Property(1, 1); NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx))); cell->connectPort(origPort, ram_o); - cpe_half->connectPort(id_RAM_O, ram_o); + cpe_ramio->connectPort(id_RAM_O, ram_o); + + NetInfo *out = ctx->createNet(ctx->idf("%s$out", cpe_half->name.c_str(ctx))); + cpe_half->connectPort(id_OUT, out); + cpe_ramio->connectPort(id_I, out); } - return cpe_half; + return std::make_pair(cpe_half, cpe_ramio); } -CellInfo *GateMatePacker::move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed) +std::pair GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, + bool place, Loc cpe_loc) { - CellInfo *cpe = move_ram_i(cell, origPort, false); - if (cpe) { - BelId b = ctx->getBelByLocation(uarch->getRelativeConstraint(fixed, origPort)); - ctx->bindBel(b, cpe, PlaceStrength::STRENGTH_FIXED); - } - return cpe; -} - -CellInfo *GateMatePacker::move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed) -{ - CellInfo *cpe = move_ram_o(cell, origPort, false); - if (cpe) { - BelId b = ctx->getBelByLocation(uarch->getRelativeConstraint(fixed, origPort)); - ctx->bindBel(b, cpe, PlaceStrength::STRENGTH_FIXED); - } - return cpe; -} - -CellInfo *GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place) -{ - CellInfo *cpe_half = nullptr; NetInfo *i_net = cell->getPort(iPort); NetInfo *o_net = cell->getPort(oPort); if (!i_net && !o_net) - return cpe_half; + return std::make_pair(nullptr, nullptr); - cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), oPort.c_str(ctx))); + CellInfo *cpe_ramio = + create_cell_ptr(id_CPE_RAMIO, ctx->idf("%s$%s_ramio", cell->name.c_str(ctx), oPort.c_str(ctx))); if (place) { - cell->constr_children.push_back(cpe_half); + cell->constr_children.push_back(cpe_ramio); + cpe_ramio->cluster = cell->cluster; + cpe_ramio->constr_abs_z = false; + cpe_ramio->constr_z = PLACE_DB_CONSTR + oPort.index; + } else { + BelId b = ctx->getBelByLocation(cpe_loc); + ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED); + } + CellInfo *cpe_half = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), oPort.c_str(ctx))); + if (place) { + cpe_ramio->constr_children.push_back(cpe_half); cpe_half->cluster = cell->cluster; cpe_half->constr_abs_z = false; - cpe_half->constr_z = PLACE_DB_CONSTR + oPort.index; + cpe_half->constr_z = -4; + } else { + BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4)); + ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED); } if (o_net) { @@ -150,24 +177,41 @@ CellInfo *GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString o cell->movePortTo(oPort, cpe_half, id_IN1); } cpe_half->params[id_INIT_L10] = Property(0b1010, 4); - cpe_half->params[id_C_O] = Property(0b11, 2); - cpe_half->params[id_C_RAM_O] = Property(1, 1); + cpe_ramio->params[id_C_RAM_O] = Property(1, 1); NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx))); cell->connectPort(oPort, ram_o); - cpe_half->connectPort(id_RAM_O, ram_o); + cpe_ramio->connectPort(id_RAM_O, ram_o); + + NetInfo *out = ctx->createNet(ctx->idf("%s$out", cpe_half->name.c_str(ctx))); + cpe_half->connectPort(id_OUT, out); + cpe_ramio->connectPort(id_I, out); } if (i_net) { - cpe_half->params[id_C_RAM_I] = Property(1, 1); + cpe_ramio->params[id_C_RAM_I] = Property(1, 1); NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx))); - cell->movePortTo(iPort, cpe_half, id_OUT); + cell->movePortTo(iPort, cpe_ramio, id_OUT); cell->connectPort(iPort, ram_i); - cpe_half->connectPort(id_RAM_I, ram_i); + cpe_ramio->connectPort(id_RAM_I, ram_i); } - // TODO: set proper timing model, without this it detects combinational loops - cpe_half->timing_index = ctx->get_cell_timing_idx(id_CPE_DFF); - return cpe_half; + return std::make_pair(cpe_half, cpe_ramio); +} + +std::pair GateMatePacker::move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed) +{ + return move_ram_i(cell, origPort, false, uarch->getRelativeConstraint(fixed, origPort)); +} + +std::pair GateMatePacker::move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed) +{ + return move_ram_o(cell, origPort, false, uarch->getRelativeConstraint(fixed, origPort)); +} + +std::pair GateMatePacker::move_ram_io_fixed(CellInfo *cell, IdString iPort, IdString oPort, + Loc fixed) +{ + return move_ram_io(cell, iPort, oPort, false, uarch->getRelativeConstraint(fixed, oPort)); } void GateMatePacker::pack_misc() @@ -258,6 +302,7 @@ void GateMateImpl::pack() packer.pack_misc(); packer.pack_ram(); packer.pack_serdes(); + //packer.pack_mult(); packer.pack_addf(); packer.pack_cpe(); packer.remove_constants(); diff --git a/himbaechel/uarch/gatemate/pack.h b/himbaechel/uarch/gatemate/pack.h index 85098568..0e870256 100644 --- a/himbaechel/uarch/gatemate/pack.h +++ b/himbaechel/uarch/gatemate/pack.h @@ -24,6 +24,26 @@ NEXTPNR_NAMESPACE_BEGIN +enum CPELut +{ + LUT_ZERO = 0b0000, + LUT_NOR = 0b0001, + LUT_AND_INV_D1 = 0b0010, + LUT_INV_D1 = 0b0011, + LUT_AND_INV_D0 = 0b0100, + LUT_INV_D0 = 0b0101, + LUT_XOR = 0b0110, + LUT_NAND = 0b0111, + LUT_AND = 0b1000, + LUT_XNOR = 0b1001, + LUT_D0 = 0b1010, + LUT_NAND_INV_D0 = 0b1011, + LUT_D1 = 0b1100, + LUT_NAND_INV_D1 = 0b1101, + LUT_OR = 0b1110, + LUT_ONE = 0b1111 +}; + struct GateMatePacker { GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); }; @@ -37,6 +57,7 @@ struct GateMatePacker void insert_pll_bufg(); void pack_pll(); void pack_misc(); + void pack_mult(); void pack_constants(); void pack_ram(); void pack_serdes(); @@ -46,18 +67,22 @@ struct GateMatePacker void remove_not_used(); private: - void dff_to_cpe(CellInfo *dff, CellInfo *cpe); + void dff_to_cpe(CellInfo *dff); void insert_bufg(CellInfo *cell, IdString port); void disconnect_if_gnd(CellInfo *cell, IdString input); void pll_out(CellInfo *cell, IdString origPort, Loc fixed); PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback); - CellInfo *move_ram_i(CellInfo *cell, IdString origPort, bool place = true); - CellInfo *move_ram_o(CellInfo *cell, IdString origPort, bool place = true); - CellInfo *move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed); - CellInfo *move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed); - CellInfo *move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place = true); + std::pair move_ram_i(CellInfo *cell, IdString origPort, bool place = true, + Loc cpe_loc = Loc()); + std::pair move_ram_o(CellInfo *cell, IdString origPort, bool place = true, + Loc cpe_loc = Loc()); + std::pair move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place = true, + Loc cpe_loc = Loc()); + std::pair move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed); + std::pair move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed); + std::pair move_ram_io_fixed(CellInfo *cell, IdString iPort, IdString oPort, Loc fixed); uint8_t ram_ctrl_signal(CellInfo *cell, IdString port, bool alt); uint8_t ram_clk_signal(CellInfo *cell, IdString port); bool is_gpio_valid_dff(CellInfo *dff); diff --git a/himbaechel/uarch/gatemate/pack_clocking.cc b/himbaechel/uarch/gatemate/pack_clocking.cc index a21d463d..8e6d8eae 100644 --- a/himbaechel/uarch/gatemate/pack_clocking.cc +++ b/himbaechel/uarch/gatemate/pack_clocking.cc @@ -199,17 +199,20 @@ void GateMatePacker::pack_bufg() if (user_glb) { ci.movePortTo(id_I, glbout[die], ctx->idf("USR_GLB%d", i)); move_ram_o_fixed(glbout[die], ctx->idf("USR_GLB%d", i), ctx->getBelLocation(glbout[die]->bel)); - glbout[die]->params[ctx->idf("USR_GLB%d", i)] = Property(Property::State::S1); + glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1); } } else { // SER_CLK clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3); clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0); + NetInfo *conn = ctx->createNet(ci.name); + clkin[die]->connectPort(ctx->idf("CLK_REF%d", i), conn); + glbout[die]->connectPort(ctx->idf("CLK_REF_OUT%d", i), conn); } ci.movePortTo(id_O, glbout[die], ctx->idf("GLB%d", i)); glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1); - glbout[die]->params[ctx->idf("GLB%d", i)] = Property(glb_mux, 3); + glbout[die]->params[ctx->idf("GLB%d_CFG", i)] = Property(glb_mux, 3); packed_cells.emplace(ci.name); } } @@ -222,10 +225,10 @@ void GateMatePacker::pack_bufg() if (!global_signals.count(feedback_net)) { pll[i]->movePortTo(id_CLK_FEEDBACK, glbout[die], ctx->idf("USR_FB%d", i)); move_ram_o_fixed(glbout[die], ctx->idf("USR_FB%d", i), ctx->getBelLocation(glbout[die]->bel)); - glbout[die]->params[ctx->idf("USR_FB%d", i)] = Property(Property::State::S1); + glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1); } else { int index = global_signals[feedback_net]; - glbout[die]->params[ctx->idf("FB%d", i)] = Property(index, 2); + glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2); pll[i]->disconnectPort(id_CLK_FEEDBACK); } NetInfo *conn = diff --git a/himbaechel/uarch/gatemate/pack_cpe.cc b/himbaechel/uarch/gatemate/pack_cpe.cc index 936f25e9..bd360f43 100644 --- a/himbaechel/uarch/gatemate/pack_cpe.cc +++ b/himbaechel/uarch/gatemate/pack_cpe.cc @@ -29,94 +29,102 @@ NEXTPNR_NAMESPACE_BEGIN // Return true if a cell is a flipflop inline bool is_dff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type.in(id_CC_DFF, id_CC_DLT); } -void GateMatePacker::dff_to_cpe(CellInfo *dff, CellInfo *cpe) +void GateMatePacker::dff_to_cpe(CellInfo *dff) { bool invert; bool is_latch = dff->type == id_CC_DLT; if (is_latch) { - NetInfo *g_net = cpe->getPort(id_G); + NetInfo *g_net = dff->getPort(id_G); invert = bool_or_default(dff->params, id_G_INV, 0); if (g_net) { if (g_net->name == ctx->id("$PACKER_GND")) { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); - cpe->disconnectPort(id_G); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + dff->disconnectPort(id_G); } else if (g_net->name == ctx->id("$PACKER_VCC")) { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2); - cpe->disconnectPort(id_G); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2); + dff->disconnectPort(id_G); } else { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2); } } else { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); } dff->unsetParam(id_G_INV); - cpe->renamePort(id_G, id_CLK); + dff->renamePort(id_G, id_CLK); - cpe->params[id_C_CPE_EN] = Property(0b11, 2); - cpe->params[id_C_L_D] = Property(0b1, 1); + dff->params[id_C_CPE_EN] = Property(0b11, 2); + dff->params[id_C_L_D] = Property(0b1, 1); } else { - NetInfo *en_net = cpe->getPort(id_EN); + NetInfo *en_net = dff->getPort(id_EN); bool invert = bool_or_default(dff->params, id_EN_INV, 0); if (en_net) { if (en_net->name == ctx->id("$PACKER_GND")) { - cpe->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2); - cpe->disconnectPort(id_EN); + dff->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2); + dff->disconnectPort(id_EN); } else if (en_net->name == ctx->id("$PACKER_VCC")) { - cpe->params[id_C_CPE_EN] = Property(invert ? 0b00 : 0b11, 2); - cpe->disconnectPort(id_EN); + dff->params[id_C_CPE_EN] = Property(invert ? 0b00 : 0b11, 2); + dff->disconnectPort(id_EN); } else { - cpe->params[id_C_CPE_EN] = Property(invert ? 0b01 : 0b10, 2); + dff->params[id_C_CPE_EN] = Property(invert ? 0b01 : 0b10, 2); } } else { - cpe->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2); + dff->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2); } dff->unsetParam(id_EN_INV); - NetInfo *clk_net = cpe->getPort(id_CLK); + NetInfo *clk_net = dff->getPort(id_CLK); invert = bool_or_default(dff->params, id_CLK_INV, 0); if (clk_net) { if (clk_net->name == ctx->id("$PACKER_GND")) { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); - cpe->disconnectPort(id_CLK); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + dff->disconnectPort(id_CLK); } else if (clk_net->name == ctx->id("$PACKER_VCC")) { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2); - cpe->disconnectPort(id_CLK); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2); + dff->disconnectPort(id_CLK); } else { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2); } } else { - cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); } dff->unsetParam(id_CLK_INV); } - NetInfo *sr_net = cpe->getPort(id_SR); + NetInfo *sr_net = dff->getPort(id_SR); invert = bool_or_default(dff->params, id_SR_INV, 0); bool sr_val = bool_or_default(dff->params, id_SR_VAL, 0); if (sr_net) { if (sr_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { bool sr_signal = sr_net->name == ctx->id("$PACKER_VCC"); - if (sr_signal ^ invert) - log_error("Currently unsupported DFF configuration for '%s'\n.", dff->name.c_str(ctx)); - cpe->params[id_C_CPE_RES] = Property(0b11, 2); - cpe->params[id_C_CPE_SET] = Property(0b11, 2); - cpe->disconnectPort(id_SR); + if (sr_signal ^ invert) { + if (sr_val) { + dff->params[id_C_CPE_RES] = Property(0b11, 2); + dff->params[id_C_CPE_SET] = Property(0b00, 2); + } else { + dff->params[id_C_CPE_RES] = Property(0b00, 2); + dff->params[id_C_CPE_SET] = Property(0b11, 2); + } + } else { + dff->params[id_C_CPE_RES] = Property(0b11, 2); + dff->params[id_C_CPE_SET] = Property(0b11, 2); + } + dff->disconnectPort(id_SR); } else { if (sr_val) { - cpe->params[id_C_CPE_RES] = Property(0b11, 2); - cpe->params[id_C_CPE_SET] = Property(invert ? 0b10 : 0b01, 2); + dff->params[id_C_CPE_RES] = Property(0b11, 2); + dff->params[id_C_CPE_SET] = Property(invert ? 0b10 : 0b01, 2); if (is_latch) - cpe->renamePort(id_SR, id_EN); + dff->renamePort(id_SR, id_EN); else - cpe->params[id_C_EN_SR] = Property(0b1, 1); + dff->params[id_C_EN_SR] = Property(0b1, 1); } else { - cpe->params[id_C_CPE_RES] = Property(invert ? 0b10 : 0b01, 2); - cpe->params[id_C_CPE_SET] = Property(0b11, 2); + dff->params[id_C_CPE_RES] = Property(invert ? 0b10 : 0b01, 2); + dff->params[id_C_CPE_SET] = Property(0b11, 2); } } } else { - cpe->params[id_C_CPE_RES] = Property(0b11, 2); - cpe->params[id_C_CPE_SET] = Property(0b11, 2); + dff->params[id_C_CPE_RES] = Property(0b11, 2); + dff->params[id_C_CPE_SET] = Property(0b11, 2); } dff->unsetParam(id_SR_VAL); dff->unsetParam(id_SR_INV); @@ -124,15 +132,13 @@ void GateMatePacker::dff_to_cpe(CellInfo *dff, CellInfo *cpe) if (dff->params.count(id_INIT) && dff->params[id_INIT].is_fully_def()) { bool init = bool_or_default(dff->params, id_INIT, 0); if (init) - cpe->params[id_FF_INIT] = Property(0b11, 2); + dff->params[id_FF_INIT] = Property(0b11, 2); else - cpe->params[id_FF_INIT] = Property(0b10, 2); + dff->params[id_FF_INIT] = Property(0b10, 2); dff->unsetParam(id_INIT); } else { dff->unsetParam(id_INIT); } - cpe->timing_index = ctx->get_cell_timing_idx(id_CPE_DFF); - cpe->params[id_C_O] = Property(0b00, 2); } void GateMatePacker::pack_cpe() @@ -151,11 +157,14 @@ void GateMatePacker::pack_cpe() ci.renamePort(id_I3, id_IN4); ci.renamePort(id_O, id_OUT); - - ci.params[id_C_O] = Property(0b11, 2); - ci.type = id_CPE_HALF_L; + uarch->rename_param(&ci, id_INIT_L02, id_INIT_L00, 4); + uarch->rename_param(&ci, id_INIT_L03, id_INIT_L01, 4); + uarch->rename_param(&ci, id_INIT_L11, id_INIT_L10, 4); + ci.cluster = ci.name; + ci.constr_abs_z = true; + ci.constr_z = CPE_LT_L_Z; + ci.type = id_CPE_L2T4; } else if (ci.type == id_CC_MX2) { - ci.params[id_C_O] = Property(0b11, 2); ci.renamePort(id_D1, id_IN1); NetInfo *sel = ci.getPort(id_S0); ci.renamePort(id_S0, id_IN2); @@ -168,14 +177,13 @@ void GateMatePacker::pack_cpe() ci.params[id_INIT_L01] = Property(0b0100, 4); // AND inv D0 ci.params[id_INIT_L10] = Property(0b1110, 4); // OR ci.renamePort(id_Y, id_OUT); - ci.type = id_CPE_HALF; + ci.type = id_CPE_L2T4; } else { ci.renamePort(id_I0, id_IN1); ci.renamePort(id_I1, id_IN2); ci.renamePort(id_I2, id_IN3); ci.renamePort(id_I3, id_IN4); ci.renamePort(id_O, id_OUT); - ci.params[id_C_O] = Property(0b11, 2); if (ci.type.in(id_CC_LUT1, id_CC_LUT2)) { uint8_t val = int_or_default(ci.params, id_INIT, 0); if (ci.type == id_CC_LUT1) @@ -184,38 +192,40 @@ void GateMatePacker::pack_cpe() ci.unsetParam(id_INIT); ci.params[id_INIT_L10] = Property(0b1010, 4); } - ci.type = id_CPE_HALF; + ci.type = id_CPE_L2T4; } NetInfo *o = ci.getPort(id_OUT); if (o) { CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true); if (dff) { - if (dff->type == id_CC_DLT) { - dff->movePortTo(id_G, &ci, id_G); - } else { - dff->movePortTo(id_EN, &ci, id_EN); - dff->movePortTo(id_CLK, &ci, id_CLK); - } - dff->movePortTo(id_SR, &ci, id_SR); - dff->disconnectPort(id_D); - ci.disconnectPort(id_OUT); - dff->movePortTo(id_Q, &ci, id_OUT); - dff_to_cpe(dff, &ci); - packed_cells.insert(dff->name); + dff->cluster = ci.name; + dff->constr_abs_z = false; + dff->constr_z = +2; + ci.cluster = ci.name; + ci.constr_children.push_back(dff); + dff->renamePort(id_D, id_DIN); + dff->renamePort(id_Q, id_DOUT); + dff_to_cpe(dff); + dff->type = (dff->type == id_CC_DLT) ? id_CPE_LATCH : id_CPE_FF; } } } for (auto ci : l2t5_list) { - CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", ci->name.c_str(ctx))); + CellInfo *upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$upper", ci->name.c_str(ctx))); upper->cluster = ci->name; - upper->constr_abs_z = false; - upper->constr_z = -1; - ci->cluster = ci->name; + upper->constr_abs_z = true; + upper->constr_z = CPE_LT_U_Z; ci->movePortTo(id_I4, upper, id_IN1); upper->params[id_INIT_L00] = Property(0b1010, 4); upper->params[id_INIT_L10] = Property(0b1010, 4); ci->constr_children.push_back(upper); + + NetInfo *ci_out_conn = ctx->createNet(ctx->idf("%s$combin", ci->name.c_str(ctx))); + upper->connectPort(id_OUT, ci_out_conn); + ci->ports[id_COMBIN].name = id_COMBIN; + ci->ports[id_COMBIN].type = PORT_IN; + ci->connectPort(id_COMBIN, ci_out_conn); } l2t5_list.clear(); @@ -255,10 +265,9 @@ void GateMatePacker::pack_cpe() ci.params[id_INIT_L03] = Property(0b1100, 4); // IN8 ci.params[id_INIT_L11] = Property(invert, 4); // Inversion bits // ci.params[id_INIT_L20] = Property(0b1100, 4); // Always D1 - ci.params[id_C_O] = Property(0b11, 2); - ci.type = id_CPE_HALF_L; + ci.type = id_CPE_LT_L; - CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", ci.name.c_str(ctx))); + CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", ci.name.c_str(ctx))); upper->cluster = ci.name; upper->constr_abs_z = false; upper->constr_z = -1; @@ -273,26 +282,43 @@ void GateMatePacker::pack_cpe() } mux_list.clear(); + std::vector dff_list; for (auto &cell : ctx->cells) { CellInfo &ci = *cell.second; if (!ci.type.in(id_CC_DFF, id_CC_DLT)) continue; - ci.renamePort(id_Q, id_OUT); + dff_list.push_back(&ci); + } + for (auto &cell : dff_list) { + CellInfo &ci = *cell; + CellInfo *lt = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lt", ci.name.c_str(ctx))); + lt->cluster = ci.name; + lt->constr_abs_z = false; + lt->constr_z = -2; + ci.cluster = ci.name; + ci.constr_children.push_back(lt); + ci.renamePort(id_Q, id_DOUT); NetInfo *d_net = ci.getPort(id_D); if (d_net->name == ctx->id("$PACKER_GND")) { - ci.params[id_INIT_L00] = Property(0b0000, 4); + lt->params[id_INIT_L00] = Property(0b0000, 4); ci.disconnectPort(id_D); } else if (d_net->name == ctx->id("$PACKER_VCC")) { - ci.params[id_INIT_L00] = Property(0b1111, 4); + lt->params[id_INIT_L00] = Property(0b1111, 4); ci.disconnectPort(id_D); } else { - ci.params[id_INIT_L00] = Property(0b1010, 4); + lt->params[id_INIT_L00] = Property(0b1010, 4); } - ci.params[id_INIT_L10] = Property(0b1010, 4); - ci.renamePort(id_D, id_IN1); - dff_to_cpe(&ci, &ci); - ci.type = id_CPE_HALF; + lt->params[id_INIT_L10] = Property(0b1010, 4); + ci.movePortTo(id_D, lt, id_IN1); + dff_to_cpe(&ci); + ci.type = (ci.type == id_CC_DLT) ? id_CPE_LATCH : id_CPE_FF; + NetInfo *conn = ctx->createNet(ctx->idf("%s$di", ci.name.c_str(ctx))); + lt->connectPort(id_OUT, conn); + ci.ports[id_DIN].name = id_DIN; + ci.ports[id_DIN].type = PORT_IN; + ci.connectPort(id_DIN, conn); } + dff_list.clear(); } static bool is_addf_ci(NetInfo *net) @@ -385,23 +411,33 @@ void GateMatePacker::pack_addf() CellInfo *root = grp.front(); root->cluster = root->name; - CellInfo *ci_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$ci_upper", root->name.c_str(ctx))); + CellInfo *ci_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$ci_upper", root->name.c_str(ctx))); root->constr_children.push_back(ci_upper); ci_upper->cluster = root->name; ci_upper->constr_abs_z = false; ci_upper->constr_z = -1; ci_upper->constr_y = -1; - CellInfo *ci_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$ci_lower", root->name.c_str(ctx))); + CellInfo *ci_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$ci", root->name.c_str(ctx))); root->constr_children.push_back(ci_lower); ci_lower->cluster = root->name; ci_lower->constr_abs_z = false; ci_lower->constr_y = -1; - ci_lower->params[id_C_O] = Property(0b11, 2); - ci_lower->params[id_C_SELY1] = Property(1, 1); - ci_lower->params[id_C_CY1_I] = Property(1, 1); + ci_lower->params[id_INIT_L00] = Property(0b0000, 4); // zero ci_lower->params[id_INIT_L10] = Property(0b1010, 4); // D0 + CellInfo *ci_cplines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$ci_cplines", root->name.c_str(ctx))); + ci_cplines->params[id_C_SELY1] = Property(1, 1); + ci_cplines->params[id_C_CY1_I] = Property(1, 1); + root->constr_children.push_back(ci_cplines); + ci_cplines->cluster = root->name; + ci_cplines->constr_abs_z = true; + ci_cplines->constr_y = -1; + ci_cplines->constr_z = CPE_CPLINES_Z; + NetInfo *ci_out_conn = ctx->createNet(ctx->idf("%s$out", ci_lower->name.c_str(ctx))); + ci_lower->connectPort(id_OUT, ci_out_conn); + ci_cplines->connectPort(id_OUT1, ci_out_conn); + NetInfo *ci_net = root->getPort(id_CI); if (ci_net->name == ctx->id("$PACKER_GND")) { ci_lower->params[id_INIT_L00] = Property(0b0000, 4); @@ -414,8 +450,8 @@ void GateMatePacker::pack_addf() ci_lower->params[id_INIT_L00] = Property(0b1010, 4); // IN5 } - NetInfo *ci_conn = ctx->createNet(ctx->idf("%s$ci", root->name.c_str(ctx))); - ci_lower->connectPort(id_COUTY1, ci_conn); + NetInfo *ci_conn = ctx->createNet(ctx->idf("%s$ci_net", root->name.c_str(ctx))); + ci_cplines->connectPort(id_COUTY1, ci_conn); root->ports[id_CINY1].name = id_CINY1; root->ports[id_CINY1].type = PORT_IN; @@ -464,10 +500,9 @@ void GateMatePacker::pack_addf() cy->params[id_INIT_L20] = Property(0b0110, 4); // XOR } cy->params[id_C_FUNCTION] = Property(merged ? C_ADDF2 : C_ADDF, 3); - cy->params[id_C_O] = Property(0b11, 2); - cy->type = id_CPE_HALF_L; + cy->type = id_CPE_LT_L; - CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", cy->name.c_str(ctx))); + CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", cy->name.c_str(ctx))); upper->cluster = root->name; root->constr_children.push_back(upper); upper->constr_abs_z = false; @@ -475,7 +510,6 @@ void GateMatePacker::pack_addf() upper->constr_z = -1; if (merged) { cy->movePortTo(id_S, upper, id_OUT); - upper->params[id_C_O] = Property(0b11, 2); } else { cy->renamePort(id_S, id_OUT); } @@ -509,26 +543,23 @@ void GateMatePacker::pack_addf() if (i == grp.size() - 1) { if (!cy->getPort(id_CO)) break; - CellInfo *co_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$co_upper", cy->name.c_str(ctx))); + CellInfo *co_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$co_upper", cy->name.c_str(ctx))); co_upper->cluster = root->name; root->constr_children.push_back(co_upper); co_upper->constr_abs_z = false; co_upper->constr_z = -1; co_upper->constr_y = +i + 1; - CellInfo *co_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$co_lower", cy->name.c_str(ctx))); + CellInfo *co_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$co", cy->name.c_str(ctx))); co_lower->cluster = root->name; root->constr_children.push_back(co_lower); co_lower->constr_abs_z = false; co_lower->constr_y = +i + 1; - co_lower->params[id_C_O] = Property(0b11, 2); co_lower->params[id_C_FUNCTION] = Property(C_EN_CIN, 3); - co_lower->params[id_INIT_L11] = Property(0b1100, 4); + co_lower->params[id_INIT_L10] = Property(0b1100, 4); co_lower->params[id_INIT_L20] = Property(0b1100, 4); - NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co", cy->name.c_str(ctx))); + NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co_net", cy->name.c_str(ctx))); - co_lower->ports[id_CINY1].name = id_CINY1; - co_lower->ports[id_CINY1].type = PORT_IN; co_lower->connectPort(id_CINY1, co_conn); cy->ports[id_COUTY1].name = id_COUTY1; cy->ports[id_COUTY1].type = PORT_OUT; @@ -543,7 +574,7 @@ void GateMatePacker::pack_addf() for (auto &usr : co_net->users) { if (usr.cell->type == id_CC_ADDF || usr.port == id_CI) { usr.cell->disconnectPort(id_CI); - NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co", cy->name.c_str(ctx))); + NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co_net", cy->name.c_str(ctx))); cy->ports[id_COUTY1].name = id_COUTY1; cy->ports[id_COUTY1].type = PORT_OUT; cy->connectPort(id_COUTY1, co_conn); @@ -551,8 +582,7 @@ void GateMatePacker::pack_addf() break; } } - upper->params[id_C_O] = Property(0b10, 2); - cy->movePortTo(id_CO, upper, id_OUT); + cy->movePortTo(id_CO, upper, id_CPOUT); } } } @@ -563,10 +593,10 @@ void GateMatePacker::pack_constants() { log_info("Packing constants..\n"); // Replace constants with LUTs - const dict vcc_params = {{id_INIT_L10, Property(0b1111, 4)}, {id_C_O, Property(0b11, 2)}}; - const dict gnd_params = {{id_INIT_L10, Property(0b0000, 4)}, {id_C_O, Property(0b11, 2)}}; + const dict vcc_params = {{id_INIT_L10, Property(0b1111, 4)}}; + const dict gnd_params = {{id_INIT_L10, Property(0b0000, 4)}}; - h.replace_constants(CellTypePort(id_CPE_HALF, id_OUT), CellTypePort(id_CPE_HALF, id_OUT), vcc_params, gnd_params); + h.replace_constants(CellTypePort(id_CPE_L2T4, id_OUT), CellTypePort(id_CPE_L2T4, id_OUT), vcc_params, gnd_params); } void GateMatePacker::remove_constants() diff --git a/himbaechel/uarch/gatemate/pack_io.cc b/himbaechel/uarch/gatemate/pack_io.cc index 1ff219b4..8875614f 100644 --- a/himbaechel/uarch/gatemate/pack_io.cc +++ b/himbaechel/uarch/gatemate/pack_io.cc @@ -326,8 +326,8 @@ void GateMatePacker::pack_io_sel() cells.push_back(&ci); } - std::vector> ddr(uarch->dies); // for each bank - + std::vector, 9>> ddr( + uarch->dies, std::array, 9>{}); auto set_out_clk = [&](CellInfo *cell, CellInfo *target) -> bool { NetInfo *clk_net = cell->getPort(id_CLK); if (clk_net) { @@ -477,21 +477,18 @@ void GateMatePacker::pack_io_sel() oddr->movePortTo(id_D1, &ci, id_OUT1); const auto &pad = ctx->get_package_pin(ctx->id(loc)); int die = uarch->tile_extra_data(ci.bel.tile)->die; - CellInfo *cpe_half = ddr[die][pad->pad_bank]; + auto [cpe_half, cpe_ramio] = ddr[die][pad->pad_bank]; if (cpe_half) { if (cpe_half->getPort(id_IN1) != oddr->getPort(id_DDR)) log_error("DDR port use signal different than already occupied DDR source.\n"); ci.ports[id_DDR].name = id_DDR; ci.ports[id_DDR].type = PORT_IN; - ci.connectPort(id_DDR, cpe_half->getPort(id_RAM_O)); + ci.connectPort(id_DDR, cpe_ramio->getPort(id_RAM_O)); } else { - oddr->movePortTo(id_DDR, &ci, id_DDR); - cpe_half = move_ram_o(&ci, id_DDR, false); - uarch->ddr_nets.insert(cpe_half->getPort(id_IN1)->name); auto l = reinterpret_cast(pad->extra_data.get()); - ctx->bindBel(ctx->getBelByLocation(Loc(l->x, l->y, l->z)), cpe_half, - PlaceStrength::STRENGTH_FIXED); - ddr[die][pad->pad_bank] = cpe_half; + oddr->movePortTo(id_DDR, &ci, id_DDR); + ddr[die][pad->pad_bank] = move_ram_o(&ci, id_DDR, false, Loc(l->x, l->y, l->z)); + uarch->ddr_nets.insert(ddr[die][pad->pad_bank].first->getPort(id_IN1)->name); } use_custom_clock = set_out_clk(oddr, &ci); bool invert = bool_or_default(oddr->params, id_CLK_INV, 0); @@ -523,7 +520,7 @@ void GateMatePacker::pack_io_sel() Loc root_loc = ctx->getBelLocation(ci.bel); for (int i = 0; i < 4; i++) { - CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc); + CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc).first; if (cpe && i == 2) cpe->params[id_INIT_L10] = Property(0b0101, 4); // Invert CPE out for output enable (OUT3) } diff --git a/himbaechel/uarch/gatemate/pack_mult.cc b/himbaechel/uarch/gatemate/pack_mult.cc new file mode 100644 index 00000000..0195bafa --- /dev/null +++ b/himbaechel/uarch/gatemate/pack_mult.cc @@ -0,0 +1,942 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn Authors. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "idstring.h" +#include "log.h" +#include "nextpnr_assertions.h" +#include "nextpnr_namespaces.h" +#include "nextpnr_types.h" +#include "pack.h" +#include "property.h" +#include "uarch/gatemate/extra_data.h" +#include "uarch/gatemate/gatemate.h" +#include "util.h" + +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +// Constant zero. +struct ZeroDriver +{ + ZeroDriver() : lower{nullptr}, upper{nullptr} {} + ZeroDriver(CellInfo *lower, CellInfo *upper, IdString name); + + CellInfo *lower; + CellInfo *upper; +}; + +// Propagate A0 through OUT1 and A1 through OUT2; zero COUTX and POUTX. +struct APassThroughCell +{ + APassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name); + + void clean_up_cell(Context *ctx, CellInfo *cell); + + CellInfo *lower; + CellInfo *upper; + CellInfo *comp; + CellInfo *cplines; +}; + +// Propagate B0 through POUTY1 and B1 through COUTY1 +struct BPassThroughCell +{ + BPassThroughCell() : lower{nullptr}, upper{nullptr}, cplines{nullptr} {} + BPassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *cplines, IdString name); + + void clean_up_cell(Context *ctx, CellInfo *cell); + + CellInfo *lower; + CellInfo *upper; + CellInfo *cplines; +}; + +// TODO: Micko points out this is an L2T4 CPE_HALF +struct CarryGenCell +{ + CarryGenCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {} + CarryGenCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, bool is_odd_x, + bool enable_cinx); + + CellInfo *lower; + CellInfo *upper; + CellInfo *comp; + CellInfo *cplines; +}; + +// This prepares B bits for multiplication. +// CITE: CPE_MULTFab.pdf +struct MultfabCell +{ + MultfabCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {} + MultfabCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, bool is_even_x, + bool enable_cinx); + + CellInfo *lower; + CellInfo *upper; + CellInfo *comp; + CellInfo *cplines; +}; + +// CITE: CPE_ges_f-routing1.pdf for !is_even_x; CPE_ges_f-routing2 for is_even_x +struct FRoutingCell +{ + FRoutingCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {} + FRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, bool is_even_x); + + CellInfo *lower; + CellInfo *upper; + CellInfo *comp; + CellInfo *cplines; +}; + +// Multiply two bits of A with two bits of B. +// +// CITE: CPE_MULT.pdf +struct MultCell +{ + MultCell() : lower{nullptr}, upper{nullptr} {} + MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb); + + CellInfo *lower; + CellInfo *upper; +}; + +// CITE: CPE_ges_MSB-routing.pdf +struct MsbRoutingCell +{ + MsbRoutingCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {} + MsbRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name); + + CellInfo *lower; + CellInfo *upper; + CellInfo *comp; + CellInfo *cplines; +}; + +struct MultiplierColumn +{ + BPassThroughCell b_passthru; + CarryGenCell carry; + MultfabCell multfab; + FRoutingCell f_route; + std::vector mults; + MsbRoutingCell msb_route; +}; + +// A GateMate multiplier is made up of columns of 2x2 multipliers. +struct Multiplier +{ + ZeroDriver zero; + std::vector a_passthrus; + std::vector cols; + + size_t cpe_count() const + { + auto count = 1 /* (zero driver) */ + a_passthrus.size(); + for (const auto &col : cols) { + count += 4 /* (b_passthru, carry, multfab, f_route) */ + col.mults.size() + 1 /* (msb_route) */; + } + return count; + } +}; + +ZeroDriver::ZeroDriver(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper} +{ + upper->params[id_INIT_L00] = Property(LUT_ZERO, 4); // (unused) + upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + upper->params[id_INIT_L10] = Property(LUT_ZERO, 4); // (unused) +} + +APassThroughCell::APassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name) + : lower{lower}, upper{upper}, comp{comp}, cplines{cplines} +{ + lower->params[id_INIT_L00] = Property(LUT_D0, 4); // IN5 + lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L02 + + comp->params[id_INIT_L30] = Property(LUT_ONE, 4); // zero -> COMP_OUT (L30 is inverted) + + upper->params[id_INIT_L00] = Property(LUT_D0, 4); // IN1 + upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + upper->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT + + cplines->params[id_C_SEL_C] = Property(1, 1); // COMP_OUT -> CX_VAL + cplines->params[id_C_SEL_P] = Property(1, 1); // COMP_OUT -> PX_VAL + cplines->params[id_C_CX_I] = Property(1, 1); // CX_VAL -> COUTX + cplines->params[id_C_PX_I] = Property(1, 1); // PX_VAL -> POUTX +} + +void APassThroughCell::clean_up_cell(Context *ctx, CellInfo *cell) +{ + auto *net = cell->ports.at(id_IN1).net; + + NPNR_ASSERT(net != nullptr); + + bool net_is_gnd = net->name == ctx->idf("$PACKER_GND"); + bool net_is_vcc = net->name == ctx->idf("$PACKER_VCC"); + if (net_is_gnd || net_is_vcc) { + cell->params[id_INIT_L00] = Property(LUT_ZERO, 4); + cell->params[id_INIT_L10] = Property(net_is_vcc ? LUT_ONE : LUT_ZERO, 4); + cell->disconnectPort(id_IN1); + } +} + +// B0 -> POUTY1; B1 -> COUTY1 +BPassThroughCell::BPassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *cplines, IdString name) + : lower{lower}, upper{upper}, cplines{cplines} +{ + lower->params[id_INIT_L00] = Property(LUT_D0, 4); // IN5 + lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L02 + + upper->params[id_INIT_L00] = Property(LUT_D0, 4); // IN1 + upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + upper->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT + + cplines->params[id_C_SEL_C] = Property(0, 1); // COMB2OUT -> CY1_VAL + cplines->params[id_C_SEL_P] = Property(0, 1); // COMB1OUT -> PY1_VAL + cplines->params[id_C_SELY1] = Property(0, 1); // COMB1OUT -> PY1_VAL; COMB2OUT -> CY1_VAL + cplines->params[id_C_CY1_I] = Property(1, 1); // CY1_VAL -> COUTY1 + cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1 +} + +void BPassThroughCell::clean_up_cell(Context *ctx, CellInfo *cell) +{ + auto *net = cell->ports.at(id_IN1).net; + + NPNR_ASSERT(net != nullptr); + + bool net_is_gnd = net->name == ctx->idf("$PACKER_GND"); + bool net_is_vcc = net->name == ctx->idf("$PACKER_VCC"); + if (net_is_gnd || net_is_vcc) { + cell->params[id_INIT_L00] = Property(LUT_ZERO, 4); + cell->params[id_INIT_L10] = Property(net_is_vcc ? LUT_ONE : LUT_ZERO, 4); + cell->disconnectPort(id_IN1); + } +} + +CarryGenCell::CarryGenCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, + bool is_odd_x, bool enable_cinx) + : lower{lower}, upper{upper}, comp{comp}, cplines{cplines} +{ + lower->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1 + lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (overriden by CIN) + lower->params[id_INIT_L10] = Property(is_odd_x ? LUT_OR : LUT_ZERO, 4); + lower->params[id_INIT_L20] = Property(is_odd_x ? LUT_OR : LUT_ZERO, 4); + lower->params[id_C_FUNCTION] = Property(C_EN_CIN, 3); + lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02 + lower->params[id_C_HORIZ] = Property(0, 1); // CINY1 for CIN_ for L03 + + upper->params[id_INIT_L00] = Property(LUT_ZERO, 4); // (unused) + upper->params[id_INIT_L01] = Property(enable_cinx ? LUT_D1 : LUT_ZERO, 4); // CINX + upper->params[id_INIT_L10] = Property(LUT_D1, 4); + if (enable_cinx) + upper->params[id_C_I2] = Property(1, 1); // CINX for L01 + + comp->params[id_INIT_L30] = Property(LUT_INV_D0, 4); // OUT1 -> COMP_OUT + + cplines->params[id_C_PY1_I] = Property(0, 1); // PINY1 -> POUTY1 + cplines->params[id_C_CY1_I] = Property(0, 1); // CINY1 -> COUTY1 + cplines->params[id_C_CY2_I] = Property(1, 1); // CY2_VAL -> COUTY2 + cplines->params[id_C_SEL_C] = Property(1, 1); // COMP_OUT -> CY2_VAL + cplines->params[id_C_SELY2] = Property(0, 1); // COMP_OUT -> CY2_VAL + + // upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1 +} + +MultfabCell::MultfabCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, + bool is_even_x, bool enable_cinx) + : lower{lower}, upper{upper}, comp{comp}, cplines{cplines} +{ + // TODO: perhaps C_I[1234] could be pips? + + lower->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1 + // lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L02 + lower->params[id_INIT_L20] = Property(is_even_x ? LUT_AND_INV_D0 : LUT_OR, 4); // L10 AND L11 -> OUT1 + lower->params[id_C_FUNCTION] = Property(C_ADDCIN, 3); + lower->params[id_C_HORIZ] = Property(0, 1); // CINY1 for CIN_ for L20 + + comp->params[id_INIT_L30] = Property(LUT_INV_D1, 4); // L10 -> COMP_OUT + + upper->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1 + upper->params[id_INIT_L01] = Property(enable_cinx ? LUT_D1 : LUT_ZERO, 4); // CINX + upper->params[id_INIT_L10] = Property(LUT_XOR, 4); // XOR + + upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00 + if (enable_cinx) + upper->params[id_C_I2] = Property(1, 1); // CINX for L01 + lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02 + // upper->params[id_C_FUNCTION] = Property(C_ADDCIN, 3); + + cplines->params[id_C_SELX] = Property(1, 1); // inverted CINY2 -> CX_VAL + cplines->params[id_C_SEL_C] = Property(1, 1); // inverted CINY2 -> CX_VAL; COMP_OUT -> CY1_VAL + cplines->params[id_C_Y12] = Property(1, 1); // inverted CINY2 -> CX_VAL + cplines->params[id_C_CX_I] = Property(1, 1); // CX_VAL -> COUTX + cplines->params[id_C_CY1_I] = Property(1, 1); // CY1_VAL -> COUTY1 + cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1 + cplines->params[id_C_SEL_P] = Property(0, 1); // OUT1 -> PY1_VAL + cplines->params[id_C_SELY1] = Property(0, 1); // COMP_OUT -> CY1_VAL; OUT1 -> PY1_VAL + + // upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1 +} + +FRoutingCell::FRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, + bool is_even_x) + : lower{lower}, upper{upper}, comp{comp}, cplines{cplines} +{ + // TODO: simplify AND with zero/OR with zero into something more sensical. + + lower->params[id_INIT_L00] = Property(LUT_ZERO, 4); // (unused) + lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + lower->params[id_INIT_L10] = Property(LUT_ZERO, 4); + lower->params[id_INIT_L20] = Property(LUT_D1, 4); + lower->params[id_C_FUNCTION] = Property(C_ADDCIN, 3); + lower->params[id_C_HORIZ] = Property(0, 1); // CINY1 for CIN_ for L20 + + comp->params[id_INIT_L30] = Property(is_even_x ? LUT_ONE : LUT_INV_D1, 4); // L10 -> COMP_OUT + + upper->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1 + // upper->params[id_INIT_L01] = Property(LUT_ONE, 4); // (unused) + upper->params[id_INIT_L10] = Property(LUT_D0, 4); + upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00 + // upper->params[id_C_FUNCTION] = Property(C_ADDCIN, 3); + + cplines->params[id_C_SELX] = Property(1, 1); + cplines->params[id_C_SEL_C] = Property(1, 1); + cplines->params[id_C_Y12] = Property(1, 1); + cplines->params[id_C_CX_I] = Property(1, 1); + cplines->params[id_C_CY1_I] = Property(is_even_x, 1); + cplines->params[id_C_CY2_I] = Property(1, 1); + cplines->params[id_C_PY1_I] = Property(1, 1); + cplines->params[id_C_PY2_I] = Property(1, 1); + + // upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1 + // upper->params[id_C_O2] = Property(0b11, 2); // COMB2OUT -> OUT2 +} + +MultCell::MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb) : lower{lower}, upper{upper} +{ + lower->params[id_INIT_L02] = Property(LUT_AND, 4); + lower->params[id_INIT_L03] = Property(LUT_D1, 4); // PINX + lower->params[id_INIT_L11] = Property(LUT_XOR, 4); + // lower->params[id_INIT_L20] = Property(LUT_D1, 4); // L11 + lower->params[id_C_FUNCTION] = Property(C_MULT, 3); + + upper->params[id_INIT_L00] = Property(LUT_AND, 4); + upper->params[id_INIT_L01] = Property(LUT_D1, 4); // CINX + upper->params[id_INIT_L10] = Property(LUT_XOR, 4); + + upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00 + upper->params[id_C_I2] = Property(1, 1); // CINX for L01 + lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02 + lower->params[id_C_I4] = Property(1, 1); // PINX for L03 + upper->params[id_C_FUNCTION] = Property(C_MULT, 3); + + if (is_msb) { + lower->params[id_C_PY1_I] = Property(1, 1); + lower->params[id_C_C_P] = Property(1, 1); + } else { + lower->params[id_C_PY1_I] = Property(0, 1); + lower->params[id_C_C_P] = Property(0, 1); + } + + // upper->params[id_C_O1] = Property(0b10, 2); // CP_OUT1 -> OUT1 + // upper->params[id_C_O2] = Property(0b10, 2); // CP_OUT2 -> OUT2 +} + +MsbRoutingCell::MsbRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name) + : lower{lower}, upper{upper}, comp{comp}, cplines{cplines} +{ + // lower->params[id_INIT_L02] = Property(LUT_ZERO, 4); // (unused) + // lower->params[id_INIT_L03] = Property(LUT_ZERO, 4); // (unused) + // lower->params[id_INIT_L11] = Property(LUT_ZERO, 4); // (unused) + // lower->params[id_INIT_L20] = Property(LUT_ZERO, 4); // (unused) + + comp->params[id_INIT_L30] = Property(LUT_ONE, 4); // zero -> COMP_OUT (L30 is inverted) + + upper->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1 + upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) + upper->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT + upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00 + + cplines->params[id_C_SELX] = Property(1, 1); // COMB2OUT -> CX_VAL; PINY1 -> PX_VAL + cplines->params[id_C_SELY1] = Property(0, 1); // COMP_OUT -> PY1_VAL + cplines->params[id_C_SELY2] = Property(0, 1); // COMP_OUT -> PY2_VAL + cplines->params[id_C_SEL_P] = Property(1, 1); // PINY1 -> PX_VAL; COMP_OUT -> PY1_VAL; COMP_OUT -> PY2_VAL + cplines->params[id_C_CX_I] = Property(1, 1); // CX_VAL -> COUTX + cplines->params[id_C_PX_I] = Property(1, 1); // PX_VAL -> POUTX + cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1 + cplines->params[id_C_PY2_I] = Property(1, 1); // PY2_VAL -> POUTY2 + + // upper->params[id_C_O2] = Property(0b11, 2); // COMB2 -> OUT2 +} + +void GateMatePacker::pack_mult() +{ + // note to self: use constr_children for recursive constraints + // fpga_generic.pas in p_r might have useful info + + auto create_zero_driver = [&](IdString name) { + auto *zero_lower = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$zero_lower", name.c_str(ctx))); + auto *zero_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$zero", name.c_str(ctx))); + return ZeroDriver{zero_lower, zero_upper, name}; + }; + + auto create_a_passthru = [&](IdString name) { + auto *a_passthru_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$a_passthru_lower", name.c_str(ctx))); + auto *a_passthru_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$a_passthru_upper", name.c_str(ctx))); + auto *a_passthru_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$a_passthru_comp", name.c_str(ctx))); + auto *a_passthru_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$a_passthru_cplines", name.c_str(ctx))); + + NetInfo *comp_conn = ctx->createNet(ctx->idf("%s$a_passthru_comp$compout", name.c_str(ctx))); + a_passthru_comp->connectPort(id_COMPOUT, comp_conn); + a_passthru_lines->connectPort(id_COMPOUT, comp_conn); + + return APassThroughCell{a_passthru_lower, a_passthru_upper, a_passthru_comp, a_passthru_lines, name}; + }; + + auto create_mult_col = [&](IdString name, int a_width, bool is_even_x, bool carry_enable_cinx, + bool multfab_enable_cinx) { + // Ideally this would be the MultiplierColumn constructor, but we need create_cell_ptr here. + auto col = MultiplierColumn{}; + + { + auto *b_passthru_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$b_passthru_lower", name.c_str(ctx))); + auto *b_passthru_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$b_passthru_upper", name.c_str(ctx))); + auto *b_passthru_lines = + create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$b_passthru_cplines", name.c_str(ctx))); + + NetInfo *comb1_conn = ctx->createNet(ctx->idf("%s$b_passthru$comb1", name.c_str(ctx))); + b_passthru_lower->connectPort(id_OUT, comb1_conn); + b_passthru_lines->connectPort(id_OUT1, comb1_conn); + + NetInfo *comb2_conn = ctx->createNet(ctx->idf("%s$b_passthru$comb2", name.c_str(ctx))); + b_passthru_upper->connectPort(id_OUT, comb2_conn); + b_passthru_lines->connectPort(id_OUT2, comb2_conn); + + col.b_passthru = BPassThroughCell{b_passthru_lower, b_passthru_upper, b_passthru_lines, name}; + } + + { + auto *carry_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$carry_lower", name.c_str(ctx))); + auto *carry_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$carry_upper", name.c_str(ctx))); + auto *carry_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$carry_comp", name.c_str(ctx))); + auto *carry_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$carry_lines", name.c_str(ctx))); + + NetInfo *comp_in = ctx->createNet(ctx->idf("%s$carry$comp_in", name.c_str(ctx))); + carry_lower->connectPort(id_OUT, comp_in); + carry_comp->connectPort(id_COMB1, comp_in); + + NetInfo *comp_out = ctx->createNet(ctx->idf("%s$carry$comp_out", name.c_str(ctx))); + carry_comp->connectPort(id_COMPOUT, comp_out); + carry_lines->connectPort(id_COMPOUT, comp_out); + + col.carry = CarryGenCell{carry_lower, carry_upper, carry_comp, carry_lines, + name, !is_even_x, carry_enable_cinx}; + } + + { + auto *multfab_lower = + create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$multf%c_lower", name.c_str(ctx), is_even_x ? 'a' : 'b')); + auto *multfab_upper = + create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$multf%c_upper", name.c_str(ctx), is_even_x ? 'a' : 'b')); + auto *multfab_comp = + create_cell_ptr(id_CPE_COMP, ctx->idf("%s$multf%c_comp", name.c_str(ctx), is_even_x ? 'a' : 'b')); + auto *multfab_lines = create_cell_ptr( + id_CPE_CPLINES, ctx->idf("%s$multf%c_cplines", name.c_str(ctx), is_even_x ? 'a' : 'b')); + + NetInfo *comb1_conn = ctx->createNet(ctx->idf("%s$multf%c$comb1", name.c_str(ctx), is_even_x ? 'a' : 'b')); + multfab_lower->connectPort(id_OUT, comb1_conn); + multfab_lines->connectPort(id_OUT1, comb1_conn); + + NetInfo *comb2_conn = ctx->createNet(ctx->idf("%s$multf%c$comb2", name.c_str(ctx), is_even_x ? 'a' : 'b')); + multfab_upper->connectPort(id_OUT, comb2_conn); + multfab_comp->connectPort(id_COMB2, comb2_conn); + + NetInfo *comp_out = ctx->createNet(ctx->idf("%s$multf%c$comp_out", name.c_str(ctx), is_even_x ? 'a' : 'b')); + multfab_comp->connectPort(id_COMPOUT, comp_out); + multfab_lines->connectPort(id_COMPOUT, comp_out); + + col.multfab = MultfabCell{multfab_lower, multfab_upper, multfab_comp, multfab_lines, + name, is_even_x, multfab_enable_cinx}; + } + + { + auto *f_route_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$f_route_lower", name.c_str(ctx))); + auto *f_route_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$f_route_upper", name.c_str(ctx))); + auto *f_route_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$f_route_comp", name.c_str(ctx))); + auto *f_route_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$f_route_lines", name.c_str(ctx))); + + NetInfo *comb1_conn = ctx->createNet(ctx->idf("%s$f_route$comb1", name.c_str(ctx))); + f_route_lower->connectPort(id_OUT, comb1_conn); + f_route_lines->connectPort(id_OUT1, comb1_conn); + + NetInfo *comb2_conn = ctx->createNet(ctx->idf("%s$f_route$comb2", name.c_str(ctx))); + f_route_upper->connectPort(id_OUT, comb2_conn); + f_route_lines->connectPort(id_OUT2, comb2_conn); + if (!is_even_x) { + f_route_comp->connectPort(id_COMB2, comb2_conn); + } + + NetInfo *comp_out = ctx->createNet(ctx->idf("%s$f_route$comp_out", name.c_str(ctx))); + f_route_comp->connectPort(id_COMPOUT, comp_out); + f_route_lines->connectPort(id_COMPOUT, comp_out); + + col.f_route = FRoutingCell{f_route_lower, f_route_upper, f_route_comp, f_route_lines, name, is_even_x}; + } + + for (int i = 0; i < (a_width / 2); i++) { + auto *mult_lower = create_cell_ptr(id_CPE_LT_L, ctx->idf("%s$row%d$mult_lower", name.c_str(ctx), i)); + auto *mult_upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$row%d$mult_upper", name.c_str(ctx), i)); + + col.mults.push_back(MultCell{mult_lower, mult_upper, name, i == ((a_width / 2) - 1)}); + uarch->multipliers.push_back(mult_lower); + } + + { + auto *msb_route_lower = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$msb_route_lower", name.c_str(ctx))); + auto *msb_route_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$msb_route", name.c_str(ctx))); + auto *msb_route_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$msb_route_comp", name.c_str(ctx))); + auto *msb_route_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$msb_route_lines", name.c_str(ctx))); + + NetInfo *comp_conn = ctx->createNet(ctx->idf("%s$msb_route$compout", name.c_str(ctx))); + msb_route_comp->connectPort(id_COMPOUT, comp_conn); + msb_route_lines->connectPort(id_COMPOUT, comp_conn); + + col.msb_route = MsbRoutingCell{msb_route_lower, msb_route_upper, msb_route_comp, msb_route_lines, name}; + } + + return col; + }; + + log_info("Packing multipliers...\n"); + + auto mults = std::vector{}; + + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type == id_CC_MULT) + mults.push_back(ci); + } + + for (auto *mult : mults) { + auto a_width = int_or_default(mult->params, id_A_WIDTH); + auto b_width = int_or_default(mult->params, id_B_WIDTH); + auto p_width = int_or_default(mult->params, id_P_WIDTH); + + // Sign-extend odd A_WIDTH to even, because we're working with 2x2 multiplier cells. + while (a_width < p_width || a_width % 2 == 1) { + mult->copyPortTo(ctx->idf("A[%d]", a_width - 1), mult, ctx->idf("A[%d]", a_width)); + a_width += 1; + } + + // Sign-extend odd B_WIDTH to even, because we're working with 2x2 multiplier cells. + while (b_width < p_width || b_width % 2 == 1) { + mult->copyPortTo(ctx->idf("B[%d]", b_width - 1), mult, ctx->idf("B[%d]", b_width)); + b_width += 1; + } + + log_info(" Configuring '%s' as a %d-bit * %d-bit = %d-bit multiplier.\n", mult->name.c_str(ctx), a_width, + b_width, p_width); + + auto m = Multiplier{}; + + // Step 1: instantiate all the CPEs. + m.zero = create_zero_driver(ctx->idf("%s$col0", mult->name.c_str(ctx))); + for (int a = 0; a < a_width / 2; a++) + m.a_passthrus.push_back(create_a_passthru(ctx->idf("%s$col0$row%d", mult->name.c_str(ctx), a))); + for (int b = 0; b < b_width / 2; b++) + m.cols.push_back(create_mult_col(ctx->idf("%s$col%d", mult->name.c_str(ctx), b + 1), a_width, b % 2 == 0, + b == 2 /* ??? */, b > 0 /* ??? */)); + + // Step 2: constrain them together. + // We define (0, 0) to be the B passthrough cell of column 1. + // we also constrain it to proper Z location + auto *root = m.cols[0].b_passthru.upper; + root->cluster = root->name; + root->constr_abs_z = true; + root->constr_z = CPE_LT_U_Z; + + auto constrain_cell = [&](CellInfo *cell, int x_offset, int y_offset, int z_offset) { + if (cell == root) + return; + root->constr_children.push_back(cell); + cell->cluster = root->name; + cell->constr_abs_z = true; + cell->constr_x = x_offset; + cell->constr_y = y_offset; + cell->constr_z = z_offset; + }; + + // Constrain zero driver. + constrain_cell(m.zero.lower, -1, 3, CPE_LT_L_Z); + constrain_cell(m.zero.upper, -1, 3, CPE_LT_U_Z); + + // Constrain A passthrough cells. + for (int a = 0; a < a_width / 2; a++) { + auto &a_passthru = m.a_passthrus.at(a); + constrain_cell(a_passthru.lower, -1, 4 + a, CPE_LT_L_Z); + constrain_cell(a_passthru.upper, -1, 4 + a, CPE_LT_U_Z); + constrain_cell(a_passthru.comp, -1, 4 + a, CPE_COMP_Z); + constrain_cell(a_passthru.cplines, -1, 4 + a, CPE_CPLINES_Z); + } + + // Constrain multiplier columns. + for (int b = 0; b < b_width / 2; b++) { + auto &col = m.cols.at(b); + constrain_cell(col.b_passthru.lower, b, b, CPE_LT_L_Z); + constrain_cell(col.b_passthru.upper, b, b, CPE_LT_U_Z); + constrain_cell(col.b_passthru.cplines, b, b, CPE_CPLINES_Z); + + constrain_cell(col.carry.lower, b, b + 1, CPE_LT_L_Z); + constrain_cell(col.carry.upper, b, b + 1, CPE_LT_U_Z); + constrain_cell(col.carry.comp, b, b + 1, CPE_COMP_Z); + constrain_cell(col.carry.cplines, b, b + 1, CPE_CPLINES_Z); + + constrain_cell(col.multfab.lower, b, b + 2, CPE_LT_L_Z); + constrain_cell(col.multfab.upper, b, b + 2, CPE_LT_U_Z); + constrain_cell(col.multfab.comp, b, b + 2, CPE_COMP_Z); + constrain_cell(col.multfab.cplines, b, b + 2, CPE_CPLINES_Z); + + constrain_cell(col.f_route.lower, b, b + 3, CPE_LT_L_Z); + constrain_cell(col.f_route.upper, b, b + 3, CPE_LT_U_Z); + constrain_cell(col.f_route.comp, b, b + 3, CPE_COMP_Z); + constrain_cell(col.f_route.cplines, b, b + 3, CPE_CPLINES_Z); + + for (size_t mult_idx = 0; mult_idx < col.mults.size(); mult_idx++) { + constrain_cell(col.mults[mult_idx].lower, b, b + 4 + mult_idx, CPE_LT_L_Z); + constrain_cell(col.mults[mult_idx].upper, b, b + 4 + mult_idx, CPE_LT_U_Z); + } + + constrain_cell(col.msb_route.lower, b, b + 4 + col.mults.size(), CPE_LT_L_Z); + constrain_cell(col.msb_route.upper, b, b + 4 + col.mults.size(), CPE_LT_U_Z); + constrain_cell(col.msb_route.comp, b, b + 4 + col.mults.size(), CPE_COMP_Z); + constrain_cell(col.msb_route.cplines, b, b + 4 + col.mults.size(), CPE_CPLINES_Z); + } + + // Step 3: connect them. + + // Zero driver. + auto *zero_net = ctx->createNet(ctx->idf("%s$out", m.zero.upper->name.c_str(ctx))); + m.zero.upper->connectPort(id_OUT, zero_net); + + // A input. + for (size_t a = 0; a < m.a_passthrus.size(); a++) { + auto &a_passthru = m.a_passthrus.at(a); + + // Connect A input passthrough cell. + mult->movePortTo(ctx->idf("A[%d]", 2 * a), a_passthru.lower, id_IN1); + mult->movePortTo(ctx->idf("A[%d]", 2 * a + 1), a_passthru.upper, id_IN1); + + // Prepare A passthrough nets. + auto lower_name = a_passthru.lower->name; + auto upper_name = a_passthru.upper->name; + auto lower_net_name = a_passthru.lower->ports.at(id_IN1).net->name; + auto upper_net_name = a_passthru.upper->ports.at(id_IN1).net->name; + + auto *lower_net = ctx->createNet( + ctx->idf("%s$%s$a%d_passthru", lower_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * a)); + a_passthru.lower->connectPort(id_OUT, lower_net); + + auto *upper_net = ctx->createNet( + ctx->idf("%s$%s$a%d_passthru", upper_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * a + 1)); + a_passthru.upper->connectPort(id_OUT, upper_net); + + // Inputs may be GND/VCC; if so, clean them up. + a_passthru.clean_up_cell(ctx, a_passthru.lower); + a_passthru.clean_up_cell(ctx, a_passthru.upper); + + // Connect A passthrough outputs to multiplier inputs. + { + // Sum output connections. + auto &mult_row = m.cols.at(0).mults.at(a); + + auto *so1_net = ctx->createNet(ctx->idf("%s$so1", upper_name.c_str(ctx))); + a_passthru.cplines->connectPort(id_COUTX, so1_net); + mult_row.lower->connectPort(id_CINX, so1_net); + + auto *so2_net = ctx->createNet(ctx->idf("%s$so2", upper_name.c_str(ctx))); + a_passthru.cplines->connectPort(id_POUTX, so2_net); + mult_row.lower->connectPort(id_PINX, so2_net); + } + + for (size_t b = 0; b < m.cols.size(); b++) { + auto &mult_row = m.cols.at(b).mults.at(a); + mult_row.lower->connectPort(id_IN1, lower_net); + mult_row.upper->connectPort(id_IN1, upper_net); + + if (a == 0) { + mult_row.lower->connectPort(id_IN4, zero_net); + } else { + auto &mult_row_below = m.cols.at(b).mults.at(a - 1); + auto *a_net_below = mult_row_below.upper->ports.at(id_IN1).net; + mult_row.lower->connectPort(id_IN4, a_net_below); + } + } + } + + // B input. + for (size_t b = 0; b < m.cols.size(); b++) { + auto &b_passthru = m.cols.at(b).b_passthru; + + // Connect B input passthrough cell. + mult->movePortTo(ctx->idf("B[%d]", 2 * b), b_passthru.upper, id_IN1); + mult->movePortTo(ctx->idf("B[%d]", 2 * b + 1), b_passthru.lower, id_IN1); + } + + // Intermediate multiplier connections. + for (size_t b = 0; b < m.cols.size(); b++) { + auto &b_passthru = m.cols.at(b).b_passthru; + auto &b_carry = m.cols.at(b).carry; + auto &b_multfab = m.cols.at(b).multfab; + auto &b_f_route = m.cols.at(b).f_route; + auto &b_msb_route = m.cols.at(b).msb_route; + + auto lower_net_name = b_passthru.lower->ports.at(id_IN1).net->name; + auto upper_net_name = b_passthru.upper->ports.at(id_IN1).net->name; + + // B Passthrough (POUTY1, COUTY1) -> Carry Gen (PINY1, CINY1) + { + auto lines_name = b_passthru.cplines->name; + + auto *lower_net = ctx->createNet( + ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b)); + b_passthru.cplines->connectPort(id_POUTY1, lower_net); + b_carry.cplines->connectPort(id_PINY1, lower_net); + b_carry.lower->connectPort(id_PINY1, lower_net); + + auto *upper_net = ctx->createNet( + ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1)); + b_passthru.cplines->connectPort(id_COUTY1, upper_net); + b_carry.cplines->connectPort(id_CINY1, upper_net); + b_carry.lower->connectPort(id_CINY1, upper_net); + } + + // Carry Gen (POUTY1, COUTY1, COUTY2) -> MULTFab (PINY1, CINY1, CINY2) + { + auto lines_name = b_carry.cplines->name; + + auto *lower_net = ctx->createNet( + ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b)); + b_carry.cplines->connectPort(id_POUTY1, lower_net); + b_multfab.cplines->connectPort(id_PINY1, lower_net); + b_multfab.lower->connectPort(id_PINY1, lower_net); + b_multfab.upper->connectPort(id_PINY1, lower_net); + + auto *upper_net = ctx->createNet( + ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1)); + b_carry.cplines->connectPort(id_COUTY1, upper_net); + b_multfab.cplines->connectPort(id_CINY1, upper_net); + b_multfab.lower->connectPort(id_CINY1, upper_net); + + auto *ccs_net = ctx->createNet(ctx->idf("%s$ccs", lines_name.c_str(ctx))); + b_carry.cplines->connectPort(id_COUTY2, ccs_net); + b_multfab.cplines->connectPort(id_CINY2, ccs_net); + } + + // MULTFab (POUTY1, COUTY1, COUTY2) -> FRoute (PINY1, CINY1, CINY2) + { + auto lines_name = b_multfab.cplines->name; + + auto *lower_net = ctx->createNet( + ctx->idf("%s$%s$f%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b + 1)); + b_multfab.cplines->connectPort(id_POUTY1, lower_net); + b_f_route.cplines->connectPort(id_PINY1, lower_net); + b_f_route.upper->connectPort(id_PINY1, lower_net); + + auto *upper_net = + ctx->createNet(ctx->idf("%s$%s$f%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b)); + b_multfab.cplines->connectPort(id_COUTY1, upper_net); + b_f_route.cplines->connectPort(id_CINY1, upper_net); + b_f_route.lower->connectPort(id_CINY1, upper_net); + + auto *ccs_net = ctx->createNet(ctx->idf("%s$ccs", lines_name.c_str(ctx))); + b_multfab.cplines->connectPort(id_COUTY2, ccs_net); + b_f_route.cplines->connectPort(id_CINY2, ccs_net); + } + + // MULTFab (COUTX) -> Carry Gen (CINX) + if (b + 1 < m.cols.size()) { + auto &b_carry_right = m.cols.at(b + 1).carry; + + auto lines_name = b_multfab.cplines->name; + + auto *cco_net = ctx->createNet(ctx->idf("%s$cco", lines_name.c_str(ctx))); + b_multfab.cplines->connectPort(id_COUTX, cco_net); + b_carry_right.cplines->connectPort(id_CINX, cco_net); + b_carry_right.upper->connectPort(id_CINX, cco_net); + } + + // FRoute (POUTY1, POUTY2, COUTY1, COUTY2) -> C_MULT (PINY1, PINY2, CINY1, CINY2) + { + auto &b_mult = m.cols.at(b).mults.front(); + + auto lines_name = b_multfab.cplines->name; + + auto *f_p1_net = ctx->createNet( + ctx->idf("%s$%s$f%d_p1", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b)); + b_f_route.cplines->connectPort(id_POUTY1, f_p1_net); + b_mult.lower->connectPort(id_PINY1, f_p1_net); + + auto *f_p2_net = ctx->createNet( + ctx->idf("%s$%s$f%d_p2", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b + 1)); + b_f_route.cplines->connectPort(id_POUTY2, f_p2_net); + b_mult.lower->connectPort(id_PINY2, f_p2_net); + + auto *f_c1_net = ctx->createNet( + ctx->idf("%s$%s$f%d_c1", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b)); + b_f_route.cplines->connectPort(id_COUTY1, f_c1_net); + b_mult.lower->connectPort(id_CINY1, f_c1_net); + + auto *f_c2_net = ctx->createNet( + ctx->idf("%s$%s$f%d_c2", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1)); + b_f_route.cplines->connectPort(id_COUTY2, f_c2_net); + b_mult.lower->connectPort(id_CINY2, f_c2_net); + } + + // FRoute (COUTX) -> MULTFab (CINX) + if (b + 1 < m.cols.size()) { + auto &b_multfab_right = m.cols.at(b + 1).multfab; + + auto lines_name = b_f_route.cplines->name; + + auto *cco_net = ctx->createNet(ctx->idf("%s$cco", lines_name.c_str(ctx))); + b_f_route.cplines->connectPort(id_COUTX, cco_net); + b_multfab_right.cplines->connectPort(id_CINX, cco_net); + b_multfab_right.upper->connectPort(id_CINX, cco_net); + } + + // C_MULT (POUTY1, POUTY2, COUTY1, COUTY2) -> C_MULT (PINY1, PINY2, CINY1, CINY2) + for (size_t row = 0; row < m.cols.at(b).mults.size() - 1; row++) { + auto &b_mult = m.cols.at(b).mults.at(row); + auto &b_mult_up = m.cols.at(b).mults.at(row + 1); + + auto lines_name = b_mult.lower->name; + + auto *lower_b_net = + ctx->createNet(ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b)); + b_mult.lower->connectPort(id_POUTY1, lower_b_net); + b_mult_up.lower->connectPort(id_PINY1, lower_b_net); + + auto *upper_b_net = ctx->createNet( + ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1)); + b_mult.lower->connectPort(id_POUTY2, upper_b_net); + b_mult_up.lower->connectPort(id_PINY2, upper_b_net); + + auto *lower_co_net = + ctx->createNet(ctx->idf("%s$%s$co%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b)); + b_mult.lower->connectPort(id_COUTY1, lower_co_net); + b_mult_up.lower->connectPort(id_CINY1, lower_co_net); + + auto *upper_co_net = ctx->createNet( + ctx->idf("%s$%s$co%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1)); + b_mult.lower->connectPort(id_COUTY2, upper_co_net); + b_mult_up.lower->connectPort(id_CINY2, upper_co_net); + } + + // C_MULT (POUTX, COUTX) -> C_MULT (PINX, CINX) + if (b + 1 < m.cols.size()) { + for (size_t row = 1; row < m.cols.at(b).mults.size(); row++) { + auto &b_mult = m.cols.at(b).mults.at(row); + auto &b_mult_right = m.cols.at(b + 1).mults.at(row - 1); + + auto lines_name = b_mult.lower->name; + + auto *so1_net = ctx->createNet(ctx->idf("%s$so1", lines_name.c_str(ctx))); + b_mult.lower->connectPort(id_POUTX, so1_net); + b_mult_right.lower->connectPort(id_PINX, so1_net); + + auto *so2_net = ctx->createNet(ctx->idf("%s$so2", lines_name.c_str(ctx))); + b_mult.lower->connectPort(id_COUTX, so2_net); + b_mult_right.lower->connectPort(id_CINX, so2_net); + } + } + + // C_MULT (POUTY1, POUTY2) -> MsbRouting (PINY1, PINY2) + { + auto &b_mult = m.cols.at(b).mults.back(); + + auto lines_name = b_mult.lower->name; + + auto *lower_net = + ctx->createNet(ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b)); + b_mult.lower->connectPort(id_POUTY1, lower_net); + b_msb_route.cplines->connectPort(id_PINY1, lower_net); + b_msb_route.upper->connectPort(id_PINY1, lower_net); + + auto *upper_net = ctx->createNet( + ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b + 1)); + b_mult.lower->connectPort(id_POUTY2, upper_net); + b_msb_route.cplines->connectPort(id_PINY2, upper_net); + } + + // MsbRouting (POUTX, COUTX) -> C_MULT (PINX, CINX) + if (b + 1 < m.cols.size()) { + auto &b_mult_right = m.cols.at(b + 1).mults.back(); + + auto lines_name = b_msb_route.cplines->name; + + auto *so1_net = ctx->createNet(ctx->idf("%s$so1", lines_name.c_str(ctx))); + b_msb_route.cplines->connectPort(id_POUTX, so1_net); + b_mult_right.lower->connectPort(id_PINX, so1_net); + + auto *so2_net = ctx->createNet(ctx->idf("%s$so2", lines_name.c_str(ctx))); + b_msb_route.cplines->connectPort(id_COUTX, so2_net); + b_mult_right.lower->connectPort(id_CINX, so2_net); + } + } + + // P output. + auto diagonal_p_width = std::min(b_width, p_width); + auto vertical_p_width = std::max(p_width - b_width, 0); + + for (int p = 0; p < diagonal_p_width; p++) { + auto &mult_cell = m.cols[p / 2].mults[0]; + auto *cpe_half = (p % 2 == 1) ? mult_cell.upper : mult_cell.lower; + + mult->movePortTo(ctx->idf("P[%d]", p), cpe_half, id_CPOUT); + } + + for (int p = 0; p < vertical_p_width; p++) { + auto &mult_cell = m.cols.back().mults[1 + (p / 2)]; + auto *cpe_half = (p % 2 == 1) ? mult_cell.upper : mult_cell.lower; + + mult->movePortTo(ctx->idf("P[%d]", p + diagonal_p_width), cpe_half, id_CPOUT); + } + + // Clean up the multiplier. + for (size_t b = 0; b < m.cols.size(); b++) { + auto &b_passthru = m.cols.at(b).b_passthru; + + // This may be GND/VCC. + b_passthru.clean_up_cell(ctx, b_passthru.lower); + b_passthru.clean_up_cell(ctx, b_passthru.upper); + } + + ctx->cells.erase(mult->name); + + log_info(" Created %zu CPEs.\n", m.cpe_count()); + } +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/route_clock.cc b/himbaechel/uarch/gatemate/route_clock.cc index 52668592..db70e86e 100644 --- a/himbaechel/uarch/gatemate/route_clock.cc +++ b/himbaechel/uarch/gatemate/route_clock.cc @@ -48,8 +48,8 @@ void GateMateImpl::route_clock() auto clk_nets = std::vector{}; auto reserved_wires = dict{}; - auto feeds_clk_port = [](PortRef &port) { - return port.cell->type.in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U) && port.port.in(id_CLK); + auto feeds_clk_port = [&](PortRef &port) { + return (ctx->getBelBucketForCellType(port.cell->type) == id_CPE_FF) && port.port.in(id_CLK); }; auto feeds_ddr_port = [&](NetInfo *net, PortRef &port) { diff --git a/himbaechel/uarch/gatemate/tests/lut.cc b/himbaechel/uarch/gatemate/tests/lut.cc index 3c7b5f62..7f3c75a8 100644 --- a/himbaechel/uarch/gatemate/tests/lut.cc +++ b/himbaechel/uarch/gatemate/tests/lut.cc @@ -34,3 +34,94 @@ TEST_F(GateMateTest, pack_constants) packer.remove_constants(); ASSERT_EQ(ctx->cells.size(), 0LU); } + +// LUT[1:0] Function +// ============================== +// 00 Constant 0 +// 01 NOT A (inverts input) +// 10 A (passes input) +// 11 Constant 1 + +TEST_F(GateMateTest, remove_lut1_zero) +{ + CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut"); + lut1->params[id_INIT] = Property(0b00, 2); + + CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); + + direct_connect(lut1, id_O, obuf, id_A); + + ASSERT_EQ(ctx->cells.size(), 1LU); + ctx->uarch->pack(); + ASSERT_EQ(ctx->cells.size(), 1LU); +} + +TEST_F(GateMateTest, remove_lut1_one) +{ + CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut"); + lut1->params[id_INIT] = Property(0b11, 2); + + CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); + + direct_connect(lut1, id_O, obuf, id_A); + + ASSERT_EQ(ctx->cells.size(), 2LU); + ctx->uarch->pack(); + ASSERT_EQ(ctx->cells.size(), 1LU); +} + +TEST_F(GateMateTest, remove_lut1_pass) +{ + CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut"); + lut1->params[id_INIT] = Property(0b10, 2); + + CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); + CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf"); + + direct_connect(ibuf, id_Y, lut1, id_I0); + direct_connect(lut1, id_O, obuf, id_A); + + ASSERT_EQ(ctx->cells.size(), 3LU); + ctx->uarch->pack(); + // Expect IBUF -> CPE -> OBUF + // LUT removed, but CPE for driving OBUF added + ASSERT_EQ(ctx->cells.size(), 3LU); +} + +TEST_F(GateMateTest, remove_lut1_inv) +{ + CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut"); + lut1->params[id_INIT] = Property(0b01, 2); + + CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); + CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf"); + + direct_connect(ibuf, id_Y, lut1, id_I0); + direct_connect(lut1, id_O, obuf, id_A); + + ASSERT_EQ(ctx->cells.size(), 3LU); + ctx->uarch->pack(); + // Expect IBUF -> CPE -> OBUF + // LUT merged, but CPE for driving OBUF added + ASSERT_EQ(ctx->cells.size(), 3LU); +} + +TEST_F(GateMateTest, remove_lut1_not_driven) +{ + CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut"); + lut1->params[id_INIT] = Property(0b01, 2); + + CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); + CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf"); + + NetInfo *net_in = ctx->createNet(ctx->id("in")); + ibuf->connectPort(id_Y, net_in); + lut1->connectPort(id_I0, net_in); + obuf->connectPort(id_A, net_in); + + ASSERT_EQ(ctx->cells.size(), 3LU); + ctx->uarch->pack(); + // Expect IBUF -> CPE -> OBUF + // LUT1 removed as not used, but CPE for driving OBUF added + ASSERT_EQ(ctx->cells.size(), 3LU); +} diff --git a/himbaechel/uarch/gatemate/tests/testing.cc b/himbaechel/uarch/gatemate/tests/testing.cc index b66ffacd..4306e657 100644 --- a/himbaechel/uarch/gatemate/tests/testing.cc +++ b/himbaechel/uarch/gatemate/tests/testing.cc @@ -30,6 +30,7 @@ void GateMateTest::SetUp() { init_share_dirname(); chipArgs.device = "CCGM1A1"; + chipArgs.options.emplace("allow-unconstrained", ""); ctx = new Context(chipArgs); ctx->uarch->init(ctx); ctx->late_init();