Merge pull request #1524 from YosysHQ/lofty/gatemate-mult-router

gatemate: multiplier router
This commit is contained in:
Lofty 2025-08-05 11:20:25 +01:00 committed by GitHub
commit 8938c73fc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 947 additions and 58 deletions

View File

@ -20,6 +20,7 @@ set(SOURCES
pack.h
pll.cc
route_clock.cc
route_mult.cc
)
set(TEST_SOURCES

View File

@ -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<IdString, Property> &params)
{
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<std::array<int, 9>> bank(uarch->dies);
@ -281,6 +311,7 @@ struct BitstreamBackend
dict<IdString, Property> 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;

View File

@ -2272,3 +2272,4 @@ X(CPE_LATCH)
X(L2T4_UPPER)
X(CPE_MX8)
X(CPE_BRIDGE)
X(MULT_INVERT)

View File

@ -186,6 +186,7 @@ void GateMateImpl::postPlace() { repack(); }
void GateMateImpl::preRoute()
{
ctx->assignArchInfo();
route_mult();
route_clock();
}

View File

@ -72,7 +72,9 @@ struct GateMateImpl : HimbaechelAPI
dict<std::pair<IdString, int>, Loc> locations;
int dies;
int preferred_die;
std::vector<CellInfo *> multipliers;
pool<IdString> multiplier_a_passthru_lowers;
pool<IdString> multiplier_a_passthru_uppers;
pool<IdString> 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;

View File

@ -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();

View File

@ -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,6 +461,9 @@ 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'));
@ -478,7 +471,7 @@ void GateMatePacker::pack_mult()
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};
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.

View File

@ -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