From 60f3c25cb05ab9c179294ae2acae6bd00e13a94a Mon Sep 17 00:00:00 2001 From: Lofty Date: Sat, 2 Aug 2025 15:23:56 +0100 Subject: [PATCH] refactor inversion checker --- himbaechel/uarch/gatemate/bitstream.cc | 111 +++++++++--------------- himbaechel/uarch/gatemate/gatemate.h | 7 +- himbaechel/uarch/gatemate/pack_mult.cc | 9 +- himbaechel/uarch/gatemate/route_mult.cc | 53 +++++++---- 4 files changed, 85 insertions(+), 95 deletions(-) diff --git a/himbaechel/uarch/gatemate/bitstream.cc b/himbaechel/uarch/gatemate/bitstream.cc index 30859430..492cb183 100644 --- a/himbaechel/uarch/gatemate/bitstream.cc +++ b/himbaechel/uarch/gatemate/bitstream.cc @@ -220,96 +220,61 @@ struct BitstreamBackend } } - void check_multipliers() + void update_multiplier_input(IdString cell_name, dict ¶ms) { - for (auto *mult : uarch->multipliers) { - NPNR_ASSERT(mult != nullptr); + auto *net = ctx->cells.at(cell_name)->ports.at(id_OUT).net; - auto should_be_inverted = mult->constr_x % 2 == 1; + int64_t driver_l10 = ctx->cells.at(cell_name)->params[id_INIT_L10].as_int64(); + bool driver_is_inverted = driver_l10 == LUT_ONE || 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)); - - // IN5 - if (need_inversion(mult, id_IN5) != should_be_inverted) - log_warning("%s.IN5 has wrong inversion state\n", mult->name.c_str(ctx)); - - // IN1 - if (need_inversion(mult, id_IN1) != should_be_inverted) - log_warning("%s.IN1 has wrong inversion state\n", mult->name.c_str(ctx)); - } - log_info("===\n"); - - pool multiplier_nets; - - for (auto *mult : uarch->multipliers) { - NPNR_ASSERT(mult != nullptr); - - multiplier_nets.insert(mult->ports.at(id_IN8).net->name); - multiplier_nets.insert(mult->ports.at(id_IN5).net->name); - multiplier_nets.insert(mult->ports.at(id_IN1).net->name); + 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); } - for (auto net_name : multiplier_nets) { - auto *net = ctx->nets.at(net_name).get(); + NPNR_ASSERT(!(all_correct && all_inverted) && "net doesn't drive any ports?"); - int64_t driver_l10 = net->driver.cell->params[id_INIT_L10].as_int64(); - bool driver_is_inverted = driver_l10 == LUT_ONE || driver_l10 == LUT_INV_D0; + if (!all_correct && !all_inverted) { + log_warning("multiplier net '%s' has inconsistent inversion\n", net->name.c_str(ctx)); - bool all_correct = true; - bool all_inverted = true; + 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 column_parity = user.cell->constr_x % 2; - auto should_be_inverted = driver_is_inverted ? column_parity == 0 : column_parity == 1; + 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); - all_correct &= (inversion == should_be_inverted); - all_inverted &= (inversion != should_be_inverted); + 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); } - NPNR_ASSERT(!(all_correct && all_inverted) && "net doesn't drive any ports?"); + log_warning(" these ports are inverted:\n"); + for (PortRef user : net->users) { + auto loc = ctx->getBelLocation(user.cell->bel); - 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) { - net->driver.cell->params[id_INIT_L10] = Property(~driver_l10 & 0b1111, 4); - log_info("multiplier net '%s': fixed inversion\n", net_name.c_str(ctx)); + 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); + log_info("multiplier net '%s': fixed inversion\n", net->name.c_str(ctx)); } } void write_bitstream() { - check_multipliers(); - ChipConfig cc; cc.chip_name = device; std::vector> bank(uarch->dies); @@ -399,6 +364,12 @@ struct BitstreamBackend else update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_RES, params); } + + if (uarch->multiplier_a_passthru_lowers.count(cell.first) || + uarch->multiplier_a_passthru_uppers.count(cell.first) || + uarch->multiplier_zero_drivers.count(cell.first)) + update_multiplier_input(cell.first, params); + int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id; for (auto &p : params) { IdString name = p.first; diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index a303e6dc..3d468b57 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -22,6 +22,7 @@ #include "extra_data.h" #include "himbaechel_api.h" +#include "idstring.h" #include "log.h" #include "nextpnr.h" #include "util.h" @@ -72,9 +73,9 @@ struct GateMateImpl : HimbaechelAPI dict, Loc> locations; int dies; int preferred_die; - std::vector multipliers; - std::vector> multiplier_a_passthrus; - std::vector multiplier_zero_drivers; + pool multiplier_a_passthru_lowers; + pool multiplier_a_passthru_uppers; + pool multiplier_zero_drivers; private: bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, diff --git a/himbaechel/uarch/gatemate/pack_mult.cc b/himbaechel/uarch/gatemate/pack_mult.cc index 9aff205f..9fe0683c 100644 --- a/himbaechel/uarch/gatemate/pack_mult.cc +++ b/himbaechel/uarch/gatemate/pack_mult.cc @@ -400,7 +400,7 @@ void GateMatePacker::pack_mult() 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.push_back(zero_upper); + uarch->multiplier_zero_drivers.insert(zero_upper->name); return ZeroDriver{zero_lower, zero_upper, name}; }; @@ -415,7 +415,8 @@ void GateMatePacker::pack_mult() a_passthru_comp->connectPort(id_COMPOUT, comp_conn); a_passthru_lines->connectPort(id_COMPOUT, comp_conn); - uarch->multiplier_a_passthrus.push_back({a_passthru_lower, a_passthru_upper}); + 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}; }; @@ -506,9 +507,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); @@ -523,7 +523,6 @@ void GateMatePacker::pack_mult() 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); } { diff --git a/himbaechel/uarch/gatemate/route_mult.cc b/himbaechel/uarch/gatemate/route_mult.cc index 6e2b2408..c27c989c 100644 --- a/himbaechel/uarch/gatemate/route_mult.cc +++ b/himbaechel/uarch/gatemate/route_mult.cc @@ -714,12 +714,9 @@ void GateMateImpl::route_mult() // 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 : this->multiplier_a_passthrus) { - auto *lower = a_passthru.first; - auto *upper = a_passthru.second; - + 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 *upper_out = upper->ports.at(id_OUT).net; auto loc = ctx->getBelLocation(lower->bel); @@ -729,17 +726,42 @@ void GateMateImpl::route_mult() auto x_within_fourgroup = (loc.x - 3) % 2; auto y_within_fourgroup = (loc.y - 3) % 2; + 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; 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); - } - 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) @@ -750,29 +772,26 @@ void GateMateImpl::route_mult() } if (x_within_fourgroup == 0 && y_within_fourgroup == 0) { - route_mult_x1y1_lower(ctx, lower_out, lower, loc, is_fourgroup_a); 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_lower(ctx, lower_out, lower, loc, is_fourgroup_a); 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_lower(ctx, lower_out, lower, loc, is_fourgroup_a); 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_lower(ctx, lower_out, lower, loc, is_fourgroup_a); 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 : this->multiplier_zero_drivers) { + 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);