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 pack.h
pll.cc pll.cc
route_clock.cc route_clock.cc
route_mult.cc
) )
set(TEST_SOURCES set(TEST_SOURCES

View File

@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include "gatemate.h" #include "gatemate.h"
#include "gatemate_util.h" #include "gatemate_util.h"
#include "uarch/gatemate/pack.h"
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" #define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
#include "himbaechel_constids.h" #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) { auto *net = ctx->cells.at(cell_name)->ports.at(id_OUT).net;
NPNR_ASSERT(mult != nullptr);
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 for (PortRef user : net->users) {
if (need_inversion(mult, id_IN8) != should_be_inverted) auto column_parity = user.cell->constr_x % 2;
log_warning("%s.IN8 has wrong inversion state\n", mult->name.c_str(ctx)); 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 NPNR_ASSERT(!(all_correct && all_inverted) && "net doesn't drive any ports?");
if (need_inversion(mult, id_IN5) != should_be_inverted)
log_warning("%s.IN5 has wrong inversion state\n", mult->name.c_str(ctx));
// IN1 if (!all_correct && !all_inverted) {
if (need_inversion(mult, id_IN1) != should_be_inverted) log_warning("multiplier net '%s' has inconsistent inversion\n", net->name.c_str(ctx));
log_warning("%s.IN1 has wrong inversion state\n", mult->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() void write_bitstream()
{ {
check_multipliers();
ChipConfig cc; ChipConfig cc;
cc.chip_name = device; cc.chip_name = device;
std::vector<std::array<int, 9>> bank(uarch->dies); std::vector<std::array<int, 9>> bank(uarch->dies);
@ -281,6 +311,7 @@ struct BitstreamBackend
dict<IdString, Property> params = cell.second->params; dict<IdString, Property> params = cell.second->params;
Loc l = ctx->getBelLocation(cell.second->bel); Loc l = ctx->getBelLocation(cell.second->bel);
params.erase(id_L2T4_UPPER); params.erase(id_L2T4_UPPER);
params.erase(id_MULT_INVERT);
int c_i1 = int_or_default(params, id_C_I1, 0); int c_i1 = int_or_default(params, id_C_I1, 0);
int c_i2 = int_or_default(params, id_C_I2, 0); int c_i2 = int_or_default(params, id_C_I2, 0);
int c_i3 = int_or_default(params, id_C_I3, 0); int c_i3 = int_or_default(params, id_C_I3, 0);
@ -332,6 +363,12 @@ struct BitstreamBackend
else else
update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_RES, params); 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; int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id;
for (auto &p : params) { for (auto &p : params) {
IdString name = p.first; IdString name = p.first;

View File

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

View File

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

View File

@ -72,7 +72,9 @@ struct GateMateImpl : HimbaechelAPI
dict<std::pair<IdString, int>, Loc> locations; dict<std::pair<IdString, int>, Loc> locations;
int dies; int dies;
int preferred_die; 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: private:
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
@ -84,6 +86,7 @@ struct GateMateImpl : HimbaechelAPI
void assign_cell_info(); void assign_cell_info();
void route_clock(); void route_clock();
void route_mult();
void repack(); void repack();
const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const; const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const;

View File

@ -397,7 +397,7 @@ void GateMateImpl::pack()
packer.pack_misc(); packer.pack_misc();
packer.pack_ram(); packer.pack_ram();
packer.pack_serdes(); packer.pack_serdes();
// packer.pack_mult(); packer.pack_mult();
packer.pack_addf(); packer.pack_addf();
packer.pack_cpe(); packer.pack_cpe();
packer.remove_constants(); 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_L00] = Property(LUT_D1, 4); // PINY1
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (overriden by CIN) 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_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 : LUT_ZERO, 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_FUNCTION] = Property(C_EN_CIN, 3);
lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02 lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02
lower->params[id_C_HORIZ] = Property(0, 1); // CINY1 for CIN_ for L03 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_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_SEL_C] = Property(1, 1); // COMP_OUT -> CY2_VAL
cplines->params[id_C_SELY2] = Property(0, 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, MultfabCell::MultfabCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name,
bool is_even_x, bool enable_cinx) bool is_even_x, bool enable_cinx)
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines} : 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_L00] = Property(LUT_D1, 4); // PINY1
// lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) // lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L02 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) if (enable_cinx)
upper->params[id_C_I2] = Property(1, 1); // CINX for L01 upper->params[id_C_I2] = Property(1, 1); // CINX for L01
lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02 lower->params[id_C_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_SELX] = Property(1, 1); // inverted CINY2 -> CX_VAL
cplines->params[id_C_SEL_C] = Property(1, 1); // inverted CINY2 -> CX_VAL; COMP_OUT -> CY1_VAL cplines->params[id_C_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_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1
cplines->params[id_C_SEL_P] = Property(0, 1); // OUT1 -> PY1_VAL 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 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, FRoutingCell::FRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name,
bool is_even_x) bool is_even_x)
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines} : 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_L00] = Property(LUT_ZERO, 4); // (unused)
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused) lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
lower->params[id_INIT_L10] = Property(LUT_ZERO, 4); lower->params[id_INIT_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_L01] = Property(LUT_ONE, 4); // (unused)
upper->params[id_INIT_L10] = Property(LUT_D0, 4); upper->params[id_INIT_L10] = Property(LUT_D0, 4);
upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00 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_SELX] = Property(1, 1);
cplines->params[id_C_SEL_C] = 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_CY2_I] = Property(1, 1);
cplines->params[id_C_PY1_I] = Property(1, 1); cplines->params[id_C_PY1_I] = Property(1, 1);
cplines->params[id_C_PY2_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} 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_L02] = Property(LUT_AND, 4);
lower->params[id_INIT_L03] = Property(LUT_D1, 4); // PINX lower->params[id_INIT_L03] = Property(LUT_D1, 4); // PINX
lower->params[id_INIT_L11] = Property(LUT_XOR, 4); 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); lower->params[id_C_FUNCTION] = Property(C_MULT, 3);
upper->params[id_INIT_L00] = Property(LUT_AND, 4); 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); lower->params[id_C_C_P] = Property(0, 1);
} }
// upper->params[id_C_O1] = Property(0b10, 2); // CP_OUT1 -> OUT1 // Must force these, even if outputs are not used, to preserve logic
// upper->params[id_C_O2] = Property(0b10, 2); // CP_OUT2 -> OUT2 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) MsbRoutingCell::MsbRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name)
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines} : 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) comp->params[id_INIT_L30] = Property(LUT_ONE, 4); // zero -> COMP_OUT (L30 is inverted)
upper->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1 upper->params[id_INIT_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_PX_I] = Property(1, 1); // PX_VAL -> POUTX
cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1 cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1
cplines->params[id_C_PY2_I] = Property(1, 1); // PY2_VAL -> POUTY2 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() void GateMatePacker::pack_mult()
@ -399,6 +379,9 @@ void GateMatePacker::pack_mult()
auto create_zero_driver = [&](IdString name) { 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_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))); 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}; 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_comp->connectPort(id_COMPOUT, comp_conn);
a_passthru_lines->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}; 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, auto create_mult_col = [&](IdString name, int a_width, bool is_even_x, bool enable_cinx) {
bool multfab_enable_cinx) {
// Ideally this would be the MultiplierColumn constructor, but we need create_cell_ptr here. // Ideally this would be the MultiplierColumn constructor, but we need create_cell_ptr here.
auto col = MultiplierColumn{}; 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_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))); 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))); NetInfo *comp_in = ctx->createNet(ctx->idf("%s$carry$comp_in", name.c_str(ctx)));
carry_lower->connectPort(id_OUT, comp_in); carry_lower->connectPort(id_OUT, comp_in);
carry_comp->connectPort(id_COMB1, 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_comp->connectPort(id_COMPOUT, comp_out);
carry_lines->connectPort(id_COMPOUT, comp_out); carry_lines->connectPort(id_COMPOUT, comp_out);
col.carry = CarryGenCell{carry_lower, carry_upper, carry_comp, carry_lines, col.carry = CarryGenCell{carry_lower, carry_upper, carry_comp, carry_lines, name, !is_even_x, enable_cinx};
name, !is_even_x, carry_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')); 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_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); 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')); 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); multfab_lines->connectPort(id_COMPOUT, comp_out);
col.multfab = MultfabCell{multfab_lower, multfab_upper, multfab_comp, multfab_lines, 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))); 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_upper->connectPort(id_OUT, comb2_conn);
f_route_lines->connectPort(id_OUT2, 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); f_route_comp->connectPort(id_COMB2, comb2_conn);
}
NetInfo *comp_out = ctx->createNet(ctx->idf("%s$f_route$comp_out", name.c_str(ctx))); NetInfo *comp_out = ctx->createNet(ctx->idf("%s$f_route$comp_out", name.c_str(ctx)));
f_route_comp->connectPort(id_COMPOUT, comp_out); f_route_comp->connectPort(id_COMPOUT, comp_out);
@ -508,9 +500,9 @@ void GateMatePacker::pack_mult()
for (int i = 0; i < (a_width / 2); i++) { 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_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)); 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)}); 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_comp->connectPort(id_COMPOUT, comp_conn);
msb_route_lines->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}; 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 a_width = int_or_default(mult->params, id_A_WIDTH);
auto b_width = int_or_default(mult->params, id_B_WIDTH); auto b_width = int_or_default(mult->params, id_B_WIDTH);
auto p_width = int_or_default(mult->params, id_P_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. // 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)); mult->copyPortTo(ctx->idf("A[%d]", a_width - 1), mult, ctx->idf("A[%d]", a_width));
a_width += 1; a_width += 1;
} }
// Sign-extend odd B_WIDTH to even, because we're working with 2x2 multiplier cells. // 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)); mult->copyPortTo(ctx->idf("B[%d]", b_width - 1), mult, ctx->idf("B[%d]", b_width));
b_width += 1; b_width += 1;
} }
@ -566,8 +567,8 @@ void GateMatePacker::pack_mult()
for (int a = 0; a < a_width / 2; a++) 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))); 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++) 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, m.cols.push_back(
b == 2 /* ??? */, b > 0 /* ??? */)); 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. // Step 2: constrain them together.
// We define (0, 0) to be the B passthrough cell of column 1. // 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; auto &b_passthru = m.cols.at(b).b_passthru;
// Connect B input passthrough cell. // 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), b_passthru.lower, id_IN1);
mult->movePortTo(ctx->idf("B[%d]", 2 * b + 1), b_passthru.lower, id_IN1); mult->movePortTo(ctx->idf("B[%d]", 2 * b + 1), b_passthru.upper, id_IN1);
} }
// Intermediate multiplier connections. // 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