refactor multiplier checking

This commit is contained in:
Lofty 2025-06-30 12:26:51 +01:00 committed by Miodrag Milanovic
parent be652a481d
commit 69b06ed907
3 changed files with 130 additions and 134 deletions

View File

@ -219,33 +219,30 @@ struct BitstreamBackend
void check_multipliers()
{
for (const auto &multiplier : uarch->multipliers) {
for (const auto &col : multiplier.cols) {
for (const auto &mult : col.mults) {
NPNR_ASSERT(mult.lower != nullptr);
NPNR_ASSERT(mult.upper != nullptr);
for (auto *mult : uarch->multipliers) {
NPNR_ASSERT(mult != nullptr);
auto should_be_inverted = mult.lower->constr_x % 2 == 1;
auto should_be_inverted = mult->constr_x % 2 == 1;
// IN8
if (need_inversion(mult.lower, id_IN4) != should_be_inverted)
log_error("%s.IN4 has wrong inversion state\n", mult.lower->name.c_str(ctx));
// TODO: these are errors, but downgraded to allow providing *some* output.
// IN5
if (need_inversion(mult.lower, id_IN1) != should_be_inverted)
log_error("%s.IN1 has wrong inversion state\n", mult.lower->name.c_str(ctx));
// IN8
if (need_inversion(mult, id_IN8) != should_be_inverted)
log_warning("%s.IN8 has wrong inversion state\n", mult->name.c_str(ctx));
// IN1
if (need_inversion(mult.upper, id_IN1) != should_be_inverted)
log_error("%s.IN1 has wrong inversion state\n", mult.upper->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));
}
}
void write_bitstream()
{
//check_multipliers();
check_multipliers();
ChipConfig cc;
cc.chip_name = device;

View File

@ -30,119 +30,6 @@
NEXTPNR_NAMESPACE_BEGIN
// Constant zero.
struct ZeroDriver
{
ZeroDriver() : lower{nullptr}, upper{nullptr} {}
ZeroDriver(CellInfo *lower, CellInfo *upper, IdString name);
CellInfo *lower;
CellInfo *upper;
};
// Propagate A0 through OUT1 and A1 through OUT2; zero COUTX and POUTX.
struct APassThroughCell
{
APassThroughCell(CellInfo *lower, CellInfo *upper, IdString name, bool inverted);
void clean_up(Context *ctx);
CellInfo *lower;
CellInfo *upper;
bool inverted;
};
// Propagate B0 through POUTY1 and B1 through COUTY1
struct BPassThroughCell
{
BPassThroughCell() : lower{nullptr}, upper{nullptr} {}
BPassThroughCell(CellInfo *lower, CellInfo *upper, IdString name);
void clean_up(Context *ctx);
CellInfo *lower;
CellInfo *upper;
};
// TODO: Micko points out this is an L2T4 CPE_HALF
struct CarryGenCell
{
CarryGenCell() : lower{nullptr}, upper{nullptr} {}
CarryGenCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_even_x, bool enable_cinx);
CellInfo *lower;
CellInfo *upper;
};
// This prepares B bits for multiplication.
struct MultfabCell
{
MultfabCell() : lower{nullptr}, upper{nullptr} {}
MultfabCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_even_x, bool enable_cinx);
CellInfo *lower;
CellInfo *upper;
};
// CITE: CPE_ges_f-routing-1.pdf
struct FRoutingCell
{
FRoutingCell() : lower{nullptr}, upper{nullptr} {}
FRoutingCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_even_x);
CellInfo *lower;
CellInfo *upper;
};
// Multiply two bits of A with two bits of B.
//
// CITE: CPE_MULT.pdf
struct MultCell
{
MultCell() : lower{nullptr}, upper{nullptr} {}
MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb);
CellInfo *lower;
CellInfo *upper;
};
// CITE: CPE_ges_MSB-routing.pdf
struct MsbRoutingCell
{
MsbRoutingCell() : lower{nullptr}, upper{nullptr} {}
MsbRoutingCell(CellInfo *lower, CellInfo *upper, IdString name);
CellInfo *lower;
CellInfo *upper;
};
struct MultiplierColumn
{
BPassThroughCell b_passthru;
CarryGenCell carry;
MultfabCell multfab;
FRoutingCell f_route;
std::vector<MultCell> mults;
MsbRoutingCell msb_route;
};
// A GateMate multiplier is made up of columns of 2x2 multipliers.
struct Multiplier
{
ZeroDriver zero;
std::vector<APassThroughCell> a_passthrus;
std::vector<MultiplierColumn> cols;
size_t cpe_count() const
{
auto count = 1 /* (zero driver) */ + a_passthrus.size();
for (const auto &col : cols) {
count += 4 /* (b_passthru, carry, multfab, f_route) */ + col.mults.size() + 1 /* (msb_route) */;
}
return count;
}
};
struct GateMateImpl : HimbaechelAPI
{
~GateMateImpl();
@ -183,7 +70,7 @@ struct GateMateImpl : HimbaechelAPI
dict<std::pair<IdString, int>, Loc> locations;
int dies;
int preferred_die;
std::vector<Multiplier> multipliers;
std::vector<CellInfo *> multipliers;
private:
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,

View File

@ -33,6 +33,119 @@
NEXTPNR_NAMESPACE_BEGIN
// Constant zero.
struct ZeroDriver
{
ZeroDriver() : lower{nullptr}, upper{nullptr} {}
ZeroDriver(CellInfo *lower, CellInfo *upper, IdString name);
CellInfo *lower;
CellInfo *upper;
};
// Propagate A0 through OUT1 and A1 through OUT2; zero COUTX and POUTX.
struct APassThroughCell
{
APassThroughCell(CellInfo *lower, CellInfo *upper, IdString name, bool inverted);
void clean_up(Context *ctx);
CellInfo *lower;
CellInfo *upper;
bool inverted;
};
// Propagate B0 through POUTY1 and B1 through COUTY1
struct BPassThroughCell
{
BPassThroughCell() : lower{nullptr}, upper{nullptr} {}
BPassThroughCell(CellInfo *lower, CellInfo *upper, IdString name);
void clean_up(Context *ctx);
CellInfo *lower;
CellInfo *upper;
};
// TODO: Micko points out this is an L2T4 CPE_HALF
struct CarryGenCell
{
CarryGenCell() : lower{nullptr}, upper{nullptr} {}
CarryGenCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_even_x, bool enable_cinx);
CellInfo *lower;
CellInfo *upper;
};
// This prepares B bits for multiplication.
struct MultfabCell
{
MultfabCell() : lower{nullptr}, upper{nullptr} {}
MultfabCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_even_x, bool enable_cinx);
CellInfo *lower;
CellInfo *upper;
};
// CITE: CPE_ges_f-routing-1.pdf
struct FRoutingCell
{
FRoutingCell() : lower{nullptr}, upper{nullptr} {}
FRoutingCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_even_x);
CellInfo *lower;
CellInfo *upper;
};
// Multiply two bits of A with two bits of B.
//
// CITE: CPE_MULT.pdf
struct MultCell
{
MultCell() : lower{nullptr}, upper{nullptr} {}
MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb);
CellInfo *lower;
CellInfo *upper;
};
// CITE: CPE_ges_MSB-routing.pdf
struct MsbRoutingCell
{
MsbRoutingCell() : lower{nullptr}, upper{nullptr} {}
MsbRoutingCell(CellInfo *lower, CellInfo *upper, IdString name);
CellInfo *lower;
CellInfo *upper;
};
struct MultiplierColumn
{
BPassThroughCell b_passthru;
CarryGenCell carry;
MultfabCell multfab;
FRoutingCell f_route;
std::vector<MultCell> mults;
MsbRoutingCell msb_route;
};
// A GateMate multiplier is made up of columns of 2x2 multipliers.
struct Multiplier
{
ZeroDriver zero;
std::vector<APassThroughCell> a_passthrus;
std::vector<MultiplierColumn> cols;
size_t cpe_count() const
{
auto count = 1 /* (zero driver) */ + a_passthrus.size();
for (const auto &col : cols) {
count += 4 /* (b_passthru, carry, multfab, f_route) */ + col.mults.size() + 1 /* (msb_route) */;
}
return count;
}
};
ZeroDriver::ZeroDriver(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper}
{
lower->params[id_INIT_L02] = Property(LUT_ZERO, 4); // (unused)
@ -349,6 +462,7 @@ void GateMatePacker::pack_mult()
auto *mult_lower = create_cell_ptr(id_CPE_LT_L, ctx->idf("%s$row%d$mult", 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));
col.mults.push_back(MultCell{mult_lower, mult_upper, name, i == ((a_width / 2) - 1)});
uarch->multipliers.push_back(mult_lower);
}
{
@ -539,8 +653,6 @@ void GateMatePacker::pack_mult()
ctx->cells.erase(mult->name);
log_info(" Created %zu CPEs.\n", m.cpe_count());
uarch->multipliers.push_back(m);
}
}