diff --git a/himbaechel/uarch/gatemate/CMakeLists.txt b/himbaechel/uarch/gatemate/CMakeLists.txt index 85f706a1..b5f61388 100644 --- a/himbaechel/uarch/gatemate/CMakeLists.txt +++ b/himbaechel/uarch/gatemate/CMakeLists.txt @@ -20,6 +20,7 @@ set(SOURCES pack.h pll.cc route_clock.cc + route_mult.cc ) set(TEST_SOURCES diff --git a/himbaechel/uarch/gatemate/bitstream.cc b/himbaechel/uarch/gatemate/bitstream.cc index a6c70a5b..388216a1 100644 --- a/himbaechel/uarch/gatemate/bitstream.cc +++ b/himbaechel/uarch/gatemate/bitstream.cc @@ -23,6 +23,7 @@ #include "config.h" #include "gatemate.h" #include "gatemate_util.h" +#include "uarch/gatemate/pack.h" #define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" #include "himbaechel_constids.h" @@ -217,33 +218,62 @@ struct BitstreamBackend } } - void check_multipliers() + void update_multiplier_input(IdString cell_name, dict ¶ms) { - for (auto *mult : uarch->multipliers) { - NPNR_ASSERT(mult != nullptr); + auto *net = ctx->cells.at(cell_name)->ports.at(id_OUT).net; - auto should_be_inverted = mult->constr_x % 2 == 1; + int64_t driver_l10 = ctx->cells.at(cell_name)->params[id_INIT_L10].as_int64(); + bool driver_is_inverted = driver_l10 == LUT_INV_D0; - // TODO: these are errors, but downgraded to allow providing *some* output. + bool all_correct = true; + bool all_inverted = true; - // IN8 - if (need_inversion(mult, id_IN8) != should_be_inverted) - log_warning("%s.IN8 has wrong inversion state\n", mult->name.c_str(ctx)); + for (PortRef user : net->users) { + auto column_parity = user.cell->constr_x % 2; + auto should_be_inverted = driver_is_inverted ? column_parity == 0 : column_parity == 1; + auto inversion = need_inversion(user.cell, user.port); + all_correct &= (inversion == should_be_inverted); + all_inverted &= (inversion != should_be_inverted); + } - // IN5 - if (need_inversion(mult, id_IN5) != should_be_inverted) - log_warning("%s.IN5 has wrong inversion state\n", mult->name.c_str(ctx)); + NPNR_ASSERT(!(all_correct && all_inverted) && "net doesn't drive any ports?"); - // IN1 - if (need_inversion(mult, id_IN1) != should_be_inverted) - log_warning("%s.IN1 has wrong inversion state\n", mult->name.c_str(ctx)); + if (!all_correct && !all_inverted) { + log_warning("multiplier net '%s' has inconsistent inversion\n", net->name.c_str(ctx)); + + auto driver_loc = ctx->getBelLocation(net->driver.cell->bel); + log_warning("net is driven from (%d, %d)\n", driver_loc.x, driver_loc.y); + + log_warning(" these ports are not inverted:\n"); + for (PortRef user : net->users) { + auto loc = ctx->getBelLocation(user.cell->bel); + + auto should_be_inverted = user.cell->constr_x % 2 == 1; + auto inversion = need_inversion(user.cell, user.port); + if (inversion == should_be_inverted) + log_warning(" %s.%s at (%d, %d)\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), loc.x, + loc.y); + } + + log_warning(" these ports are inverted:\n"); + for (PortRef user : net->users) { + auto loc = ctx->getBelLocation(user.cell->bel); + + auto should_be_inverted = user.cell->constr_x % 2 == 1; + auto inversion = need_inversion(user.cell, user.port); + if (inversion != should_be_inverted) + log_warning(" %s.%s at (%d, %d)\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), loc.x, + loc.y); + } + } else if (all_inverted) { + params[id_INIT_L10] = Property(~driver_l10 & 0b1111, 4); + if (ctx->debug) + log_info("multiplier net '%s': fixed inversion\n", net->name.c_str(ctx)); } } void write_bitstream() { - check_multipliers(); - ChipConfig cc; cc.chip_name = device; std::vector> bank(uarch->dies); @@ -281,6 +311,7 @@ struct BitstreamBackend dict params = cell.second->params; Loc l = ctx->getBelLocation(cell.second->bel); params.erase(id_L2T4_UPPER); + params.erase(id_MULT_INVERT); 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); @@ -332,6 +363,12 @@ struct BitstreamBackend else update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_RES, params); } + + if (uarch->multiplier_a_passthru_lowers.count(cell.first) || + uarch->multiplier_a_passthru_uppers.count(cell.first) || + uarch->multiplier_zero_drivers.count(cell.first)) + update_multiplier_input(cell.first, params); + int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id; for (auto &p : params) { IdString name = p.first; diff --git a/himbaechel/uarch/gatemate/constids.inc b/himbaechel/uarch/gatemate/constids.inc index d68a5196..11140382 100644 --- a/himbaechel/uarch/gatemate/constids.inc +++ b/himbaechel/uarch/gatemate/constids.inc @@ -2272,3 +2272,4 @@ X(CPE_LATCH) X(L2T4_UPPER) X(CPE_MX8) X(CPE_BRIDGE) +X(MULT_INVERT) diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index b9a28ebf..b30b380b 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -186,6 +186,7 @@ void GateMateImpl::postPlace() { repack(); } void GateMateImpl::preRoute() { ctx->assignArchInfo(); + route_mult(); route_clock(); } diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index 43ecf246..ae280461 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -72,7 +72,9 @@ struct GateMateImpl : HimbaechelAPI dict, Loc> locations; int dies; int preferred_die; - std::vector multipliers; + pool multiplier_a_passthru_lowers; + pool multiplier_a_passthru_uppers; + pool multiplier_zero_drivers; private: bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, @@ -84,6 +86,7 @@ struct GateMateImpl : HimbaechelAPI void assign_cell_info(); void route_clock(); + void route_mult(); void repack(); const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const; diff --git a/himbaechel/uarch/gatemate/pack.cc b/himbaechel/uarch/gatemate/pack.cc index 393f56c7..f2caef1d 100644 --- a/himbaechel/uarch/gatemate/pack.cc +++ b/himbaechel/uarch/gatemate/pack.cc @@ -397,7 +397,7 @@ void GateMateImpl::pack() packer.pack_misc(); packer.pack_ram(); packer.pack_serdes(); - // packer.pack_mult(); + packer.pack_mult(); packer.pack_addf(); packer.pack_cpe(); packer.remove_constants(); diff --git a/himbaechel/uarch/gatemate/pack_mult.cc b/himbaechel/uarch/gatemate/pack_mult.cc index 0195bafa..7da4b028 100644 --- a/himbaechel/uarch/gatemate/pack_mult.cc +++ b/himbaechel/uarch/gatemate/pack_mult.cc @@ -240,8 +240,8 @@ CarryGenCell::CarryGenCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, Cel { 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_INIT_L10] = Property(is_odd_x ? LUT_OR : !enable_cinx ? LUT_ZERO : LUT_AND, 4); + lower->params[id_INIT_L20] = Property(is_odd_x ? LUT_OR : !enable_cinx ? LUT_ZERO : LUT_AND, 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 @@ -259,16 +259,12 @@ CarryGenCell::CarryGenCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, Cel 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 @@ -286,7 +282,6 @@ MultfabCell::MultfabCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellI 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 @@ -296,16 +291,12 @@ MultfabCell::MultfabCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellI 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); @@ -319,7 +310,6 @@ FRoutingCell::FRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, Cel // 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); @@ -329,9 +319,6 @@ FRoutingCell::FRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, Cel 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} @@ -339,7 +326,6 @@ MultCell::MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb) 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); @@ -360,18 +346,14 @@ MultCell::MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb) 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 + // Must force these, even if outputs are not used, to preserve logic + lower->params[id_C_O1] = Property(0b10, 2); // CP_OUT1 -> OUT1 + lower->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 @@ -387,8 +369,6 @@ MsbRoutingCell::MsbRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, 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() @@ -399,6 +379,9 @@ void GateMatePacker::pack_mult() 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))); + + uarch->multiplier_zero_drivers.insert(zero_upper->name); + return ZeroDriver{zero_lower, zero_upper, name}; }; @@ -412,11 +395,13 @@ void GateMatePacker::pack_mult() a_passthru_comp->connectPort(id_COMPOUT, comp_conn); a_passthru_lines->connectPort(id_COMPOUT, comp_conn); + uarch->multiplier_a_passthru_lowers.insert(a_passthru_lower->name); + uarch->multiplier_a_passthru_uppers.insert(a_passthru_upper->name); + 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) { + auto create_mult_col = [&](IdString name, int a_width, bool is_even_x, bool enable_cinx) { // Ideally this would be the MultiplierColumn constructor, but we need create_cell_ptr here. auto col = MultiplierColumn{}; @@ -443,6 +428,12 @@ void GateMatePacker::pack_mult() 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 *comb2_conn = ctx->createNet(ctx->idf("%s$carrycomb2", name.c_str(ctx))); + carry_upper->connectPort(id_OUT, comb2_conn); + carry_lower->ports[id_COMBIN].name = id_COMBIN; + carry_lower->ports[id_COMBIN].type = PORT_IN; + carry_lower->connectPort(id_COMBIN, comb2_conn); + 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); @@ -451,8 +442,7 @@ void GateMatePacker::pack_mult() 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}; + col.carry = CarryGenCell{carry_lower, carry_upper, carry_comp, carry_lines, name, !is_even_x, enable_cinx}; } { @@ -471,14 +461,17 @@ void GateMatePacker::pack_mult() 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_lower->ports[id_COMBIN].name = id_COMBIN; + multfab_lower->ports[id_COMBIN].type = PORT_IN; + multfab_lower->connectPort(id_COMBIN, 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}; + col.multfab = MultfabCell{multfab_lower, multfab_upper, multfab_comp, multfab_lines, + name, is_even_x, enable_cinx}; } { @@ -494,9 +487,8 @@ void GateMatePacker::pack_mult() 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) { + 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); @@ -508,9 +500,9 @@ void GateMatePacker::pack_mult() 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)); + mult_lower->params[id_MULT_INVERT] = Property(is_even_x ? Property::State::S0 : Property::State::S1); col.mults.push_back(MultCell{mult_lower, mult_upper, name, i == ((a_width / 2) - 1)}); - uarch->multipliers.push_back(mult_lower); } { @@ -523,6 +515,10 @@ void GateMatePacker::pack_mult() msb_route_comp->connectPort(id_COMPOUT, comp_conn); msb_route_lines->connectPort(id_COMPOUT, comp_conn); + NetInfo *out_conn = ctx->createNet(ctx->idf("%s$msb_route$out", name.c_str(ctx))); + msb_route_upper->connectPort(id_OUT, out_conn); + msb_route_lines->connectPort(id_OUT2, out_conn); + col.msb_route = MsbRoutingCell{msb_route_lower, msb_route_upper, msb_route_comp, msb_route_lines, name}; } @@ -543,15 +539,20 @@ void GateMatePacker::pack_mult() 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); + mult->renamePort(id_A, ctx->id("A[0]")); + mult->renamePort(id_B, ctx->id("B[0]")); + mult->renamePort(id_P, ctx->id("P[0]")); + int a_size = (((a_width + 1) / 2) + 1) * 2; + int b_size = (((b_width + 1) / 2) + 1) * 2; // Sign-extend odd A_WIDTH to even, because we're working with 2x2 multiplier cells. - while (a_width < p_width || a_width % 2 == 1) { + while (a_width < a_size) { 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) { + while (b_width < b_size) { mult->copyPortTo(ctx->idf("B[%d]", b_width - 1), mult, ctx->idf("B[%d]", b_width)); b_width += 1; } @@ -566,8 +567,8 @@ void GateMatePacker::pack_mult() 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 /* ??? */)); + 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 > 0)); // Step 2: constrain them together. // We define (0, 0) to be the B passthrough cell of column 1. @@ -700,8 +701,8 @@ void GateMatePacker::pack_mult() 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); + mult->movePortTo(ctx->idf("B[%d]", 2 * b), b_passthru.lower, id_IN1); + mult->movePortTo(ctx->idf("B[%d]", 2 * b + 1), b_passthru.upper, id_IN1); } // Intermediate multiplier connections. diff --git a/himbaechel/uarch/gatemate/route_mult.cc b/himbaechel/uarch/gatemate/route_mult.cc new file mode 100644 index 00000000..dc9ce38e --- /dev/null +++ b/himbaechel/uarch/gatemate/route_mult.cc @@ -0,0 +1,845 @@ +/* + * 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 "gatemate.h" +#include "log.h" + +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +namespace { +USING_NEXTPNR_NAMESPACE; + +void find_and_bind_downhill_pip(Context *ctx, WireId from, WireId to, NetInfo *net) +{ + NPNR_ASSERT(from != WireId()); + NPNR_ASSERT(to != WireId()); + for (auto pip : ctx->getPipsDownhill(from)) { + if (ctx->getPipDstWire(pip) == to) { + if (ctx->debug) + log_info(" pip %s: %s -> %s\n", ctx->nameOfPip(pip), ctx->nameOfWire(from), ctx->nameOfWire(to)); + + ctx->bindPip(pip, net, STRENGTH_LOCKED); + return; + } + } + log_error("Couldn't find pip from %s to %s\n", ctx->nameOfWire(from), ctx->nameOfWire(to)); +} + +void route_mult_diag(Context *ctx, NetInfo *net, Loc loc, WireId last_wire, int plane) +{ + auto hops = 0; + auto in_port = ctx->idf("IN%d", plane); + for (auto user : net->users) { + if (user.port == in_port) + hops++; + } + + if (ctx->debug) + log_info(" routing diagonal: %d hops\n", hops); + + for (int i = 0; i < hops; i++) { + auto cpe_in = ctx->getWireByName( + IdStringList::concat(ctx->idf("X%dY%d", loc.x + i, loc.y + i), ctx->idf("CPE.IN%d", plane))); + auto cpe_in_int = ctx->getWireByName( + IdStringList::concat(ctx->idf("X%dY%d", loc.x + i, loc.y + i), ctx->idf("CPE.IN%d_int", plane))); + + find_and_bind_downhill_pip(ctx, last_wire, cpe_in, net); // inverting + find_and_bind_downhill_pip(ctx, cpe_in, cpe_in_int, net); + + last_wire = cpe_in; + } +} + +void route_mult_x1y1_lower(Context *ctx, NetInfo *net, CellInfo *lower, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN5 using x1y1\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + + auto cpe_combout1 = ctx->getBelPinWire(lower->bel, id_OUT); + auto cpe_out1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT1_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P05.D0"))); + + ctx->bindWire(cpe_combout1, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout1, cpe_out1_int, net); + + if (is_fourgroup_a) { + auto sb_big = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P05.D0"))); + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_big, net); + find_and_bind_downhill_pip(ctx, sb_big, in_mux, net); // inverting + } else { + auto sb_sml_d0 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P05.D0"))); + auto sb_sml_y1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P05.Y1_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_sml_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_d0, sb_sml_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1_int, in_mux, net); // inverting + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 5); +} + +void route_mult_x1y1_upper_in1(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN1 using x1y1\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P01.D0"))); + + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + + if (is_fourgroup_a) { + auto sb_big = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P01.D0"))); + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_big, net); + find_and_bind_downhill_pip(ctx, sb_big, in_mux, net); // inverting + } else { + auto sb_sml_d0 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P01.D0"))); + auto sb_sml_y1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P01.Y1_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_sml_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_d0, sb_sml_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1_int, in_mux, net); // inverting + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 1); +} + +void route_mult_x1y1_upper_in8(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a, + bool bind_route_start = false) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN8 using x1y1\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y2 = ctx->idf("X%dY%d", loc.x + 1, loc.y + 1); + auto x4y2 = ctx->idf("X%dY%d", loc.x + 3, loc.y + 1); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + auto out_mux_d0 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("OM.P12.D0"))); + auto out_mux_y = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("OM.P12.Y"))); + auto in_mux_p12 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P12.D2"))); + + if (bind_route_start) { + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + find_and_bind_downhill_pip(ctx, cpe_out2_int, out_mux_d0, net); + } + + find_and_bind_downhill_pip(ctx, out_mux_d0, out_mux_y, net); // inverting + + if (is_fourgroup_a) { + auto sb_sml = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("SB_SML.P12.Y1_int"))); + auto sb_big_d2_1 = ctx->getWireByName(IdStringList::concat(x4y2, ctx->idf("SB_BIG.P12.D2_1"))); + auto sb_big_y1 = ctx->getWireByName(IdStringList::concat(x4y2, ctx->idf("SB_BIG.P12.Y1"))); + auto sb_big_ydiag = ctx->getWireByName(IdStringList::concat(x4y2, ctx->idf("SB_BIG.P12.YDIAG"))); + + find_and_bind_downhill_pip(ctx, out_mux_y, sb_sml, net); + find_and_bind_downhill_pip(ctx, sb_sml, sb_big_d2_1, net); // inverting + find_and_bind_downhill_pip(ctx, sb_big_d2_1, sb_big_y1, net); // inverting + find_and_bind_downhill_pip(ctx, sb_big_y1, sb_big_ydiag, net); // inverting + find_and_bind_downhill_pip(ctx, sb_big_ydiag, in_mux_p12, net); // inverting + } else { + auto sb_big = + ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("SB_BIG.P12.Y1"))); // aka x4y2/SB_SML.P12.D2_1 + auto sb_sml_y1_int = ctx->getWireByName(IdStringList::concat(x4y2, ctx->idf("SB_SML.P12.Y1_int"))); + auto sb_sml_ydiag_int = ctx->getWireByName(IdStringList::concat(x4y2, ctx->idf("SB_SML.P12.YDIAG_int"))); + auto sb_sml_y3_int = ctx->getWireByName(IdStringList::concat(x4y2, ctx->idf("SB_SML.P12.Y3_int"))); + + find_and_bind_downhill_pip(ctx, out_mux_y, sb_big, net); // inverting + find_and_bind_downhill_pip(ctx, sb_big, sb_sml_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1_int, sb_sml_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_ydiag_int, sb_sml_y3_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y3_int, in_mux_p12, net); // inverting + } + + auto in_mux_p04 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P04.D7"))); // aka IM.P12.Y + auto in_mux_p08 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P08.D6"))); // aka IM.P04.Y + + find_and_bind_downhill_pip(ctx, in_mux_p12, in_mux_p04, net); // inverting + find_and_bind_downhill_pip(ctx, in_mux_p04, in_mux_p08, net); // inverting + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y + 1, 0}, in_mux_p08, 8); +} + +void route_mult_x1y2_lower(Context *ctx, NetInfo *net, CellInfo *lower, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN5 using x1y2\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + auto x4y1 = ctx->idf("X%dY%d", loc.x + 3, loc.y); + + auto cpe_combout1 = ctx->getBelPinWire(lower->bel, id_OUT); + auto cpe_out1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT1_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P05.D2"))); + + ctx->bindWire(cpe_combout1, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout1, cpe_out1_int, net); + + if (is_fourgroup_a) { + auto sb_sml_p06_d0 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P06.D0"))); + auto sb_sml_p06_y1_int = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P06.Y1_int"))); + auto sb_sml_p06_ydiag_int = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P06.YDIAG_int"))); + auto sb_sml_p06_ydiag = + ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P06.YDIAG"))); // AKA SB_SML.P05.X23 + auto sb_sml_p05_ydiag_int = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P05.YDIAG_int"))); + auto sb_sml_p05_y1_int = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P05.Y1_int"))); + auto sb_sml_p05_y1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P05.Y1"))); + auto sb_big_y1 = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_BIG.P05.Y1"))); + auto sb_big_ydiag = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_BIG.P05.YDIAG"))); + // x2y1/IM.P05.D2 is x4y1/SB_BIG.P05.Y3 + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_sml_p06_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_p06_d0, sb_sml_p06_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p06_y1_int, sb_sml_p06_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p06_ydiag_int, sb_sml_p06_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p06_ydiag, sb_sml_p05_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p05_ydiag_int, sb_sml_p05_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p05_y1_int, sb_sml_p05_y1, net); + find_and_bind_downhill_pip(ctx, sb_sml_p05_y1, sb_big_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_y1, sb_big_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_ydiag, in_mux, net); + } else { + auto sb_big_p06_d0 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P06.D0"))); + auto sb_big_p06_y1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P06.Y1"))); + auto sb_big_p06_ydiag = + ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P06.YDIAG"))); // AKA SB_BIG.P05.X23 + auto sb_big_p05_ydiag = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P05.YDIAG"))); + auto sb_big_p05_y1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P05.Y1"))); + auto sb_sml_y1_int = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_SML.P05.Y1_int"))); + auto sb_sml_ydiag_int = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_SML.P05.YDIAG_int"))); + auto sb_sml_y3_int = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_SML.P05.Y3_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_big_p06_d0, net); + find_and_bind_downhill_pip(ctx, sb_big_p06_d0, sb_big_p06_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p06_y1, sb_big_p06_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p06_ydiag, sb_big_p05_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p05_ydiag, sb_big_p05_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p05_y1, sb_sml_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1_int, sb_sml_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_ydiag_int, sb_sml_y3_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y3_int, in_mux, net); + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 5); +} + +void route_mult_x1y2_upper_in1(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN1 using x1y2\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + auto x4y1 = ctx->idf("X%dY%d", loc.x + 3, loc.y); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P01.D2"))); + + if (is_fourgroup_a) { + auto sb_sml_p02_d0 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P02.D0"))); + auto sb_sml_p02_y1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P02.Y1_int"))); + auto sb_sml_p02_ydiag_int = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P02.YDIAG_int"))); + auto sb_sml_p02_ydiag = + ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P02.YDIAG"))); // AKA SB_SML.P01.X23 + auto sb_sml_p01_ydiag = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P01.YDIAG_int"))); + auto sb_sml_p01_y1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P01.Y1_int"))); + auto sb_big_d2_1 = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_BIG.P01.D2_1"))); + auto sb_big_y1 = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_BIG.P01.Y1"))); + auto sb_big_ydiag = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_BIG.P01.YDIAG"))); + // x2y1/IM.P01.D2 is x4y1/SB_BIG.P01.Y3 + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_sml_p02_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_d0, sb_sml_p02_y1, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_y1, sb_sml_p02_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_ydiag_int, sb_sml_p02_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_ydiag, sb_sml_p01_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p01_ydiag, sb_sml_p01_y1, net); + find_and_bind_downhill_pip(ctx, sb_sml_p01_y1, sb_big_d2_1, net); + find_and_bind_downhill_pip(ctx, sb_big_d2_1, sb_big_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_y1, sb_big_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_ydiag, in_mux, net); + } else { + auto sb_big_p02_d0 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P02.D0"))); + auto sb_big_p02_y1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P02.Y1"))); + auto sb_big_p02_ydiag = + ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P02.YDIAG"))); // AKA SB_BIG.P01.X23 + auto sb_big_p01_ydiag = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P01.YDIAG"))); + auto sb_big_p01_y1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_BIG.P01.Y1"))); + auto sb_sml_y1 = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_SML.P01.Y1_int"))); + auto sb_sml_ydiag = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_SML.P01.YDIAG_int"))); + auto sb_sml_y3 = ctx->getWireByName(IdStringList::concat(x4y1, ctx->idf("SB_SML.P01.Y3_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_big_p02_d0, net); + find_and_bind_downhill_pip(ctx, sb_big_p02_d0, sb_big_p02_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p02_y1, sb_big_p02_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p02_ydiag, sb_big_p01_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p01_ydiag, sb_big_p01_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p01_y1, sb_sml_y1, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1, sb_sml_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_ydiag, sb_sml_y3, net); + find_and_bind_downhill_pip(ctx, sb_sml_y3, in_mux, net); + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 1); +} + +void route_mult_x1y2_upper_in8(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a, + bool bind_route_start = false) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN8 using x1y2\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + auto x2y2 = ctx->idf("X%dY%d", loc.x + 1, loc.y + 1); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + auto out_mux_d1 = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("OM.P10.D1"))); + auto out_mux_y = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("OM.P10.Y"))); + auto in_mux_p10 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P10.D1"))); + + if (bind_route_start) { + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + find_and_bind_downhill_pip(ctx, cpe_out2_int, out_mux_d1, net); + } + + find_and_bind_downhill_pip(ctx, out_mux_d1, out_mux_y, net); // inverting + + if (is_fourgroup_a) { + auto sb_sml = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("SB_SML.P10.Y2_int"))); + + find_and_bind_downhill_pip(ctx, out_mux_y, sb_sml, net); + find_and_bind_downhill_pip(ctx, sb_sml, in_mux_p10, net); // inverting + } else { + // x2y1/OM.P10.Y is x2y1/SB_BIG.P10.D0 + // x2y2/IM.P10.D1 is x2y1/SB_BIG.P10.Y2 + + find_and_bind_downhill_pip(ctx, out_mux_y, in_mux_p10, net); // inverting + } + + auto in_mux_p12 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P12.D6"))); // aka IM.P10.Y + auto in_mux_p04 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P04.D7"))); // aka IM.P12.Y + auto in_mux_p08 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P08.D6"))); // aka IM.P04.Y + + find_and_bind_downhill_pip(ctx, in_mux_p10, in_mux_p12, net); // inverting + find_and_bind_downhill_pip(ctx, in_mux_p12, in_mux_p04, net); // inverting + find_and_bind_downhill_pip(ctx, in_mux_p04, in_mux_p08, net); // inverting + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y + 1, 0}, in_mux_p08, 8); +} + +void route_mult_x2y1_lower(Context *ctx, NetInfo *net, CellInfo *lower, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN5 using x2y1\n", net->name.c_str(ctx)); + + auto x0y1 = ctx->idf("X%dY%d", loc.x - 1, loc.y); + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + + auto cpe_combout1 = ctx->getBelPinWire(lower->bel, id_OUT); + auto cpe_out1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT1_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P05.D0"))); + + ctx->bindWire(cpe_combout1, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout1, cpe_out1_int, net); + + if (is_fourgroup_a) { + auto sb_big_p07_d0 = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P07.D0"))); + auto sb_big_p07_y1 = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P07.Y1"))); + auto sb_big_p07_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P07.YDIAG"))); // AKA SB_BIG.P06.X23 + auto sb_big_p06_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P06.YDIAG"))); // AKA SB_BIG.P05.X23 + auto sb_big_p05_ydiag = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P05.YDIAG"))); + // x2y1/IM.P05.D0 is x0y1/SB_BIG.P05.Y1 + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_big_p07_d0, net); + find_and_bind_downhill_pip(ctx, sb_big_p07_d0, sb_big_p07_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p07_y1, sb_big_p07_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p07_ydiag, sb_big_p06_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p06_ydiag, sb_big_p05_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p05_ydiag, in_mux, net); + } else { + auto sb_sml_p07_d0 = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P07.D0"))); + auto sb_sml_p07_y1_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P07.Y1_int"))); + auto sb_sml_p07_ydiag_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P07.YDIAG_int"))); + auto sb_sml_p07_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P07.YDIAG"))); // AKA SB_SML.P06.X23 + auto sb_sml_p06_ydiag_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P06.YDIAG_int"))); + auto sb_sml_p06_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P06.YDIAG"))); // AKA SB_SML.P05.X23 + auto sb_sml_p05_ydiag_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P05.YDIAG_int"))); + auto sb_sml_p05_y1_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P05.Y1_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_sml_p07_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_p07_d0, sb_sml_p07_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p07_y1_int, sb_sml_p07_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p07_ydiag_int, sb_sml_p07_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p07_ydiag, sb_sml_p06_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p06_ydiag_int, sb_sml_p06_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p06_ydiag, sb_sml_p05_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p05_ydiag_int, sb_sml_p05_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p05_y1_int, in_mux, net); + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 5); +} + +void route_mult_x2y1_upper_in1(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN1 using x2y1\n", net->name.c_str(ctx)); + + auto x0y1 = ctx->idf("X%dY%d", loc.x - 1, loc.y); + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P01.D0"))); + + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + + if (is_fourgroup_a) { + auto sb_big_p03_d0 = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P03.D0"))); + auto sb_big_p03_y1 = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P03.Y1"))); + auto sb_big_p03_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P03.YDIAG"))); // AKA SB_BIG.P02.X23 + auto sb_big_p02_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P02.YDIAG"))); // AKA SB_BIG.P01.X23 + auto sb_big_p01_ydiag = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_BIG.P01.YDIAG"))); + // x2y1/IM.P01.D0 is x0y1/SB_BIG.P01.Y1 + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_big_p03_d0, net); + find_and_bind_downhill_pip(ctx, sb_big_p03_d0, sb_big_p03_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p03_y1, sb_big_p03_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p03_ydiag, sb_big_p02_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p02_ydiag, sb_big_p01_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p01_ydiag, in_mux, net); + } else { + auto sb_sml_p03_d0 = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P03.D0"))); + auto sb_sml_p03_y1_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P03.Y1_int"))); + auto sb_sml_p03_ydiag_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P03.YDIAG_int"))); + auto sb_sml_p03_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P03.YDIAG"))); // AKA SB_SML.P02.X23 + auto sb_sml_p02_ydiag_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P02.YDIAG_int"))); + auto sb_sml_p02_ydiag = + ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P02.YDIAG"))); // AKA SB_SML.P01.X23 + auto sb_sml_p01_ydiag_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P01.YDIAG_int"))); + auto sb_sml_p01_y1_int = ctx->getWireByName(IdStringList::concat(x0y1, ctx->idf("SB_SML.P01.Y1_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_sml_p03_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_p03_d0, sb_sml_p03_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p03_y1_int, sb_sml_p03_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p03_ydiag_int, sb_sml_p03_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p03_ydiag, sb_sml_p02_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_ydiag_int, sb_sml_p02_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_ydiag, sb_sml_p01_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p01_ydiag_int, sb_sml_p01_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p01_y1_int, in_mux, net); + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 1); +} + +void route_mult_x2y1_upper_in8(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a, + bool bind_route_start = false) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN8 using x2y1\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x1y2 = ctx->idf("X%dY%d", loc.x, loc.y + 1); + auto x2y2 = ctx->idf("X%dY%d", loc.x + 1, loc.y + 1); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + auto out_mux_d2 = ctx->getWireByName(IdStringList::concat(x1y2, ctx->idf("OM.P09.D2"))); + auto out_mux_y = ctx->getWireByName(IdStringList::concat(x1y2, ctx->idf("OM.P09.Y"))); + auto in_mux_p09 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P09.D0"))); + + if (bind_route_start) { + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + find_and_bind_downhill_pip(ctx, cpe_out2_int, out_mux_d2, net); + } + + find_and_bind_downhill_pip(ctx, out_mux_d2, out_mux_y, net); // inverting + + if (is_fourgroup_a) { + auto sb_sml = ctx->getWireByName(IdStringList::concat(x1y2, ctx->idf("SB_SML.P09.Y1_int"))); + + find_and_bind_downhill_pip(ctx, out_mux_y, sb_sml, net); + find_and_bind_downhill_pip(ctx, sb_sml, in_mux_p09, net); // inverting + } else { + // x1y2/OM.P09.Y is x1y2/SB_BIG.P09.D0 + // x2y2/IM.P09.D0 is x2y1/SB_BIG.P09.Y1 + + find_and_bind_downhill_pip(ctx, out_mux_y, in_mux_p09, net); // inverting + } + + auto in_mux_p12 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P12.D7"))); // aka IM.P09.Y + auto in_mux_p04 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P04.D7"))); // aka IM.P12.Y + auto in_mux_p08 = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P08.D6"))); // aka IM.P04.Y + + find_and_bind_downhill_pip(ctx, in_mux_p09, in_mux_p12, net); // inverting + find_and_bind_downhill_pip(ctx, in_mux_p12, in_mux_p04, net); // inverting + find_and_bind_downhill_pip(ctx, in_mux_p04, in_mux_p08, net); // inverting + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y + 1, 0}, in_mux_p08, 8); +} + +void route_mult_x2y2_lower(Context *ctx, NetInfo *net, CellInfo *lower, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN5 using x2y2\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + + auto cpe_combout1 = ctx->getBelPinWire(lower->bel, id_OUT); + auto cpe_out1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT1_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P05.D0"))); + + ctx->bindWire(cpe_combout1, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout1, cpe_out1_int, net); + + if (is_fourgroup_a) { + auto sb_sml_p08_d0 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P08.D0"))); + auto sb_sml_p08_y1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P08.Y1_int"))); + auto sb_sml_p08_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P08.YDIAG_int"))); + auto sb_sml_p08_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P08.YDIAG"))); // AKA SB_SML.P07.X23 + auto sb_sml_p07_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P07.YDIAG_int"))); + auto sb_sml_p07_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P07.YDIAG"))); // AKA SB_SML.P06.X23 + auto sb_sml_p06_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P06.YDIAG_int"))); + auto sb_sml_p06_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P06.YDIAG"))); // AKA SB_SML.P05.X23 + auto sb_sml_p05_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P05.YDIAG_int"))); + auto sb_sml_p05_y1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P05.Y1_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_sml_p08_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_p08_d0, sb_sml_p08_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p08_y1_int, sb_sml_p08_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p08_ydiag_int, sb_sml_p08_ydiag, net); // inverting + find_and_bind_downhill_pip(ctx, sb_sml_p08_ydiag, sb_sml_p07_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p07_ydiag_int, sb_sml_p07_ydiag, net); // inverting + find_and_bind_downhill_pip(ctx, sb_sml_p07_ydiag, sb_sml_p06_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p06_ydiag_int, sb_sml_p06_ydiag, net); // inverting + find_and_bind_downhill_pip(ctx, sb_sml_p06_ydiag, sb_sml_p05_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p05_ydiag_int, sb_sml_p05_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p05_y1_int, in_mux, net); // inverting + } else { + auto sb_big_p08_d0 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P08.D0"))); + auto sb_big_p08_y1 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P08.Y1"))); + auto sb_big_p08_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P08.YDIAG"))); // AKA SB_BIG.P07.X23 + auto sb_big_p07_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P07.YDIAG"))); // AKA SB_BIG.P06.X23 + auto sb_big_p06_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P06.YDIAG"))); // AKA SB_BIG.P05.X23 + auto sb_big_p05_ydiag = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P05.YDIAG"))); + // x2y1/IM.P05.D0 is x1y1/SB_BIG.P05.Y1 + + find_and_bind_downhill_pip(ctx, cpe_out1_int, sb_big_p08_d0, net); + find_and_bind_downhill_pip(ctx, sb_big_p08_d0, sb_big_p08_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p08_y1, sb_big_p08_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p08_ydiag, sb_big_p07_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p07_ydiag, sb_big_p06_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p06_ydiag, sb_big_p05_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p05_ydiag, in_mux, net); + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 5); +} + +void route_mult_x2y2_upper_in1(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN1 using x2y2\n", net->name.c_str(ctx)); + + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y1 = ctx->idf("X%dY%d", loc.x + 1, loc.y); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y1, ctx->idf("IM.P01.D0"))); + + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + + if (is_fourgroup_a) { + auto sb_sml_p04_d0 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P04.D0"))); + auto sb_sml_p04_y1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P04.Y1_int"))); + auto sb_sml_p04_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P04.YDIAG_int"))); + auto sb_sml_p04_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P04.YDIAG"))); // AKA SB_SML.P03.X23 + auto sb_sml_p03_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P03.YDIAG_int"))); + auto sb_sml_p03_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P03.YDIAG"))); // AKA SB_SML.P02.X23 + auto sb_sml_p02_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P02.YDIAG_int"))); + auto sb_sml_p02_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P02.YDIAG"))); // AKA SB_SML.P01.X23 + auto sb_sml_p01_ydiag_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P01.YDIAG_int"))); + auto sb_sml_p01_y1_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_SML.P01.Y1_int"))); + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_sml_p04_d0, net); + find_and_bind_downhill_pip(ctx, sb_sml_p04_d0, sb_sml_p04_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p04_y1_int, sb_sml_p04_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p04_ydiag_int, sb_sml_p04_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p04_ydiag, sb_sml_p03_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p03_ydiag_int, sb_sml_p03_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p03_ydiag, sb_sml_p02_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_ydiag_int, sb_sml_p02_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_sml_p02_ydiag, sb_sml_p01_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p01_ydiag_int, sb_sml_p01_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_p01_y1_int, in_mux, net); + } else { + auto sb_big_p04_d0 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P04.D0"))); + auto sb_big_p04_y1 = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P04.Y1"))); + auto sb_big_p04_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P04.YDIAG"))); // AKA SB_BIG.P07.X23 + auto sb_big_p03_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P03.YDIAG"))); // AKA SB_BIG.P05.X23 + auto sb_big_p02_ydiag = + ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P02.YDIAG"))); // AKA SB_BIG.P05.X23 + auto sb_big_p01_ydiag = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("SB_BIG.P01.YDIAG"))); + // x2y1/IM.P05.D0 is x1y1/SB_BIG.P05.Y1 + + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_big_p04_d0, net); + find_and_bind_downhill_pip(ctx, sb_big_p04_d0, sb_big_p04_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_p04_y1, sb_big_p04_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p04_ydiag, sb_big_p03_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p03_ydiag, sb_big_p02_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p02_ydiag, sb_big_p01_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_p01_ydiag, in_mux, net); + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y, 0}, in_mux, 1); +} + +void route_mult_x2y2_upper_in8(Context *ctx, NetInfo *net, CellInfo *upper, Loc loc, bool is_fourgroup_a, + bool bind_route_start = false) +{ + if (ctx->debug) + log_info(" routing net '%s' -> IN8 using x2y2\n", net->name.c_str(ctx)); + + auto x0y0 = ctx->idf("X%dY%d", loc.x - 1, loc.y - 1); + auto x1y1 = ctx->idf("X%dY%d", loc.x, loc.y); + auto x2y0 = ctx->idf("X%dY%d", loc.x + 1, loc.y - 1); + auto x2y2 = ctx->idf("X%dY%d", loc.x + 1, loc.y + 1); + + auto cpe_combout2 = ctx->getBelPinWire(upper->bel, id_OUT); + auto cpe_out2_int = ctx->getWireByName(IdStringList::concat(x1y1, ctx->idf("CPE.OUT2_int"))); + auto in_mux = ctx->getWireByName(IdStringList::concat(x2y2, ctx->idf("IM.P08.D1"))); + + if (bind_route_start) { + ctx->bindWire(cpe_combout2, net, STRENGTH_LOCKED); + find_and_bind_downhill_pip(ctx, cpe_combout2, cpe_out2_int, net); + if (is_fourgroup_a) { + auto sb_big_d0 = ctx->getWireByName(IdStringList::concat(x0y0, ctx->idf("SB_BIG.P08.D0"))); + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_big_d0, net); + } else { + auto sb_sml_d0 = ctx->getWireByName(IdStringList::concat(x0y0, ctx->idf("SB_SML.P08.D0"))); + find_and_bind_downhill_pip(ctx, cpe_out2_int, sb_sml_d0, net); + } + } + + if (is_fourgroup_a) { + auto sb_big_d0 = ctx->getWireByName(IdStringList::concat(x0y0, ctx->idf("SB_BIG.P08.D0"))); + auto sb_big_y1 = ctx->getWireByName(IdStringList::concat(x0y0, ctx->idf("SB_BIG.P08.Y1"))); + auto sb_sml_y1_int = ctx->getWireByName(IdStringList::concat(x2y0, ctx->idf("SB_SML.P08.Y1_int"))); + auto sb_sml_ydiag_int = ctx->getWireByName(IdStringList::concat(x2y0, ctx->idf("SB_SML.P08.YDIAG_int"))); + auto sb_sml_y2_int = ctx->getWireByName(IdStringList::concat(x2y0, ctx->idf("SB_SML.P08.Y2_int"))); + + find_and_bind_downhill_pip(ctx, sb_big_d0, sb_big_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_y1, sb_sml_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1_int, sb_sml_ydiag_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_ydiag_int, sb_sml_y2_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y2_int, in_mux, net); + } else { + auto sb_sml_d0 = ctx->getWireByName(IdStringList::concat(x0y0, ctx->idf("SB_SML.P08.D0"))); + auto sb_sml_y1_int = ctx->getWireByName(IdStringList::concat(x0y0, ctx->idf("SB_SML.P08.Y1_int"))); + auto sb_sml_y1 = ctx->getWireByName(IdStringList::concat(x0y0, ctx->idf("SB_SML.P08.Y1"))); + auto sb_big_y1 = ctx->getWireByName(IdStringList::concat(x2y0, ctx->idf("SB_BIG.P08.Y1"))); + auto sb_big_ydiag = ctx->getWireByName(IdStringList::concat(x2y0, ctx->idf("SB_BIG.P08.YDIAG"))); + // x2y2/IM.P08.D0 is x2y0/SB_BIG.P08.Y2 + + find_and_bind_downhill_pip(ctx, sb_sml_d0, sb_sml_y1_int, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1_int, sb_sml_y1, net); + find_and_bind_downhill_pip(ctx, sb_sml_y1, sb_big_y1, net); + find_and_bind_downhill_pip(ctx, sb_big_y1, sb_big_ydiag, net); + find_and_bind_downhill_pip(ctx, sb_big_ydiag, in_mux, net); + } + + route_mult_diag(ctx, net, Loc{loc.x + 1, loc.y + 1, 0}, in_mux, 8); +} +} // namespace + +NEXTPNR_NAMESPACE_BEGIN + +void GateMateImpl::route_mult() +{ + log_info("Routing multipliers...\n"); + + // I am fully aware the nextpnr API is absolutely not designed around naming specific pips. + // Unfortunately, this is the easiest way to describe the specific routing required. + // Myrtle, please forgive me. + for (auto a_passthru_lower : this->multiplier_a_passthru_lowers) { + auto *lower = ctx->cells.at(a_passthru_lower).get(); + auto *lower_out = lower->ports.at(id_OUT).net; + + auto loc = ctx->getBelLocation(lower->bel); + + auto x_fourgroup = (loc.x - 3) % 4; + auto y_fourgroup = (loc.y - 3) % 4; + bool is_fourgroup_a = (x_fourgroup < 2 && y_fourgroup < 2) || (x_fourgroup >= 2 && y_fourgroup >= 2); + auto x_within_fourgroup = (loc.x - 3) % 2; + auto y_within_fourgroup = (loc.y - 3) % 2; + + if (ctx->debug) { + log_info(" A passthrough at (%d, %d) has 4-group %c\n", loc.x, loc.y, is_fourgroup_a ? 'A' : 'B'); + + log_info(" lower.OUT [OUT1] = %s\n", ctx->nameOfWire(ctx->getBelPinWire(lower->bel, id_OUT))); + for (auto sink_port : lower->ports.at(id_OUT).net->users) { + auto sink_loc = ctx->getBelLocation(sink_port.cell->bel); + log_info(" -> %s.%s at (%d, %d)\n", sink_port.cell->name.c_str(ctx), sink_port.port.c_str(ctx), + sink_loc.x, sink_loc.y); + } + } + + if (x_within_fourgroup == 0 && y_within_fourgroup == 0) { + route_mult_x1y1_lower(ctx, lower_out, lower, loc, is_fourgroup_a); + } else if (x_within_fourgroup == 0 && y_within_fourgroup == 1) { + route_mult_x1y2_lower(ctx, lower_out, lower, loc, is_fourgroup_a); + } else if (x_within_fourgroup == 1 && y_within_fourgroup == 0) { + route_mult_x2y1_lower(ctx, lower_out, lower, loc, is_fourgroup_a); + } else /* if (x_within_fourgroup == 1 && y_within_fourgroup == 1) */ { + route_mult_x2y2_lower(ctx, lower_out, lower, loc, is_fourgroup_a); + } + } + + for (auto a_passthru_upper : this->multiplier_a_passthru_uppers) { + auto *upper = ctx->cells.at(a_passthru_upper).get(); + auto *upper_out = upper->ports.at(id_OUT).net; + + auto loc = ctx->getBelLocation(upper->bel); + + auto x_fourgroup = (loc.x - 3) % 4; + auto y_fourgroup = (loc.y - 3) % 4; + bool is_fourgroup_a = (x_fourgroup < 2 && y_fourgroup < 2) || (x_fourgroup >= 2 && y_fourgroup >= 2); + auto x_within_fourgroup = (loc.x - 3) % 2; + auto y_within_fourgroup = (loc.y - 3) % 2; + + bool needs_in8_route = false; + + if (ctx->debug) { + log_info(" A passthrough at (%d, %d) has 4-group %c\n", loc.x, loc.y, is_fourgroup_a ? 'A' : 'B'); + + log_info(" upper.OUT [OUT2] = %s\n", ctx->nameOfWire(ctx->getBelPinWire(upper->bel, id_OUT))); + } + for (auto sink_port : upper->ports.at(id_OUT).net->users) { + if (sink_port.port == id_IN8) + needs_in8_route = true; + auto sink_loc = ctx->getBelLocation(sink_port.cell->bel); + if (ctx->debug) + log_info(" -> %s.%s at (%d, %d)\n", sink_port.cell->name.c_str(ctx), sink_port.port.c_str(ctx), + sink_loc.x, sink_loc.y); + } + + if (x_within_fourgroup == 0 && y_within_fourgroup == 0) { + route_mult_x1y1_upper_in1(ctx, upper_out, upper, loc, is_fourgroup_a); + if (needs_in8_route) + route_mult_x1y1_upper_in8(ctx, upper_out, upper, loc, is_fourgroup_a); + } else if (x_within_fourgroup == 0 && y_within_fourgroup == 1) { + route_mult_x1y2_upper_in1(ctx, upper_out, upper, loc, is_fourgroup_a); + if (needs_in8_route) + route_mult_x1y2_upper_in8(ctx, upper_out, upper, loc, is_fourgroup_a); + } else if (x_within_fourgroup == 1 && y_within_fourgroup == 0) { + route_mult_x2y1_upper_in1(ctx, upper_out, upper, loc, is_fourgroup_a); + if (needs_in8_route) + route_mult_x2y1_upper_in8(ctx, upper_out, upper, loc, is_fourgroup_a); + } else /* if (x_within_fourgroup == 1 && y_within_fourgroup == 1) */ { + route_mult_x2y2_upper_in1(ctx, upper_out, upper, loc, is_fourgroup_a); + if (needs_in8_route) + route_mult_x2y2_upper_in8(ctx, upper_out, upper, loc, is_fourgroup_a); + } + } + + for (auto zero_driver_name : this->multiplier_zero_drivers) { + auto *zero_driver = ctx->cells.at(zero_driver_name).get(); + auto *out = zero_driver->ports.at(id_OUT).net; + + auto loc = ctx->getBelLocation(zero_driver->bel); + + auto x_fourgroup = (loc.x - 3) % 4; + auto y_fourgroup = (loc.y - 3) % 4; + bool is_fourgroup_a = (x_fourgroup < 2 && y_fourgroup < 2) || (x_fourgroup >= 2 && y_fourgroup >= 2); + auto x_within_fourgroup = (loc.x - 3) % 2; + auto y_within_fourgroup = (loc.y - 3) % 2; + + if (ctx->debug) { + log_info(" Zero driver at (%d, %d) has 4-group %c\n", loc.x, loc.y, is_fourgroup_a ? 'A' : 'B'); + + log_info(" zero_driver.OUT [OUT2] = %s\n", + ctx->nameOfWire(ctx->getBelPinWire(zero_driver->bel, id_OUT))); + for (auto sink_port : zero_driver->ports.at(id_OUT).net->users) { + auto sink_loc = ctx->getBelLocation(sink_port.cell->bel); + log_info(" -> %s.%s at (%d, %d)\n", sink_port.cell->name.c_str(ctx), sink_port.port.c_str(ctx), + sink_loc.x, sink_loc.y); + } + } + + if (x_within_fourgroup == 0 && y_within_fourgroup == 0) { + route_mult_x1y1_upper_in8(ctx, out, zero_driver, loc, is_fourgroup_a, /*bind_route_start=*/true); + } else if (x_within_fourgroup == 0 && y_within_fourgroup == 1) { + route_mult_x1y2_upper_in8(ctx, out, zero_driver, loc, is_fourgroup_a, /*bind_route_start=*/true); + } else if (x_within_fourgroup == 1 && y_within_fourgroup == 0) { + route_mult_x2y1_upper_in8(ctx, out, zero_driver, loc, is_fourgroup_a, /*bind_route_start=*/true); + } else /* if (x_within_fourgroup == 1 && y_within_fourgroup == 1) */ { + route_mult_x2y2_upper_in8(ctx, out, zero_driver, loc, is_fourgroup_a, /*bind_route_start=*/true); + } + } +} + +NEXTPNR_NAMESPACE_END