mirror of
https://github.com/YosysHQ/nextpnr.git
synced 2026-01-13 15:27:37 +00:00
refactor inversion checker
This commit is contained in:
parent
fe7546fda5
commit
60f3c25cb0
@ -220,96 +220,61 @@ struct BitstreamBackend
|
||||
}
|
||||
}
|
||||
|
||||
void check_multipliers()
|
||||
void update_multiplier_input(IdString cell_name, dict<IdString, Property> ¶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<IdString> 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<std::array<int, 9>> 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;
|
||||
|
||||
@ -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<std::pair<IdString, int>, Loc> locations;
|
||||
int dies;
|
||||
int preferred_die;
|
||||
std::vector<CellInfo *> multipliers;
|
||||
std::vector<std::pair<CellInfo *, CellInfo *>> multiplier_a_passthrus;
|
||||
std::vector<CellInfo *> multiplier_zero_drivers;
|
||||
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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user