1
0
mirror of https://github.com/YosysHQ/nextpnr.git synced 2026-02-14 03:54:22 +00:00

current progress

This commit is contained in:
Lofty
2025-06-21 15:26:41 +01:00
parent 1ac1b67f7f
commit fcecdb9397

View File

@@ -36,14 +36,10 @@ struct APassThroughCell
{
APassThroughCell(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper}
{
lower->cluster = name;
lower->constr_abs_z = false;
lower->params[id_INIT_L00] = Property(LUT_D0, 4); // IN1
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT
upper->cluster = name;
upper->constr_abs_z = false;
upper->params[id_INIT_L02] = Property(LUT_D0, 4); // IN5
upper->params[id_INIT_L03] = Property(LUT_ZERO, 4); // (unused)
upper->params[id_INIT_L11] = Property(LUT_D0, 4); // L02
@@ -62,8 +58,8 @@ struct APassThroughCell
PortRef a0() const { return PortRef{upper, id_IN1}; }
PortRef a1() const { return PortRef{lower, id_IN1}; }
CellInfo *upper;
CellInfo *lower;
CellInfo *upper;
};
// Propagate B0 through POUTY1 and B1 through COUTY1
@@ -72,6 +68,8 @@ struct APassThroughCell
// TODO: is it worth trying to unify this with APassThroughCell?
struct BPassThroughCell
{
BPassThroughCell() : lower{nullptr}, upper{nullptr} {}
BPassThroughCell(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper}
{
lower->cluster = name;
@@ -80,8 +78,6 @@ struct BPassThroughCell
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT
upper->cluster = name;
upper->constr_abs_z = false;
upper->params[id_INIT_L02] = Property(LUT_D0, 4); // IN5
upper->params[id_INIT_L03] = Property(LUT_ZERO, 4); // (unused)
upper->params[id_INIT_L11] = Property(LUT_D0, 4); // L02
@@ -97,25 +93,23 @@ struct BPassThroughCell
PortRef b0() const { return PortRef{upper, id_IN1}; }
PortRef b1() const { return PortRef{lower, id_IN1}; }
CellInfo *upper;
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) : lower{lower}, upper{upper}
{
// TODO: simplify AND with zero/OR with zero into something more sensical.
lower->cluster = name;
lower->constr_abs_z = false;
lower->params[id_INIT_L00] = Property(LUT_ZERO, 4); // (unused)
lower->params[id_INIT_L01] = Property(LUT_D1, 4); // CINX
lower->params[id_INIT_L10] = Property(is_even_x ? LUT_AND : LUT_OR, 4);
upper->cluster = name;
upper->constr_abs_z = false;
upper->params[id_INIT_L02] = Property(LUT_D1, 4); // PINY1
upper->params[id_INIT_L03] = Property(LUT_ZERO, 4); // (unused)
upper->params[id_INIT_L11] = Property(is_even_x ? LUT_AND : LUT_OR, 4);
@@ -134,25 +128,23 @@ struct CarryGenCell
upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1
}
CellInfo *upper;
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) : lower{lower}, upper{upper}
{
// TODO: perhaps C_I[1234] could be pips?
lower->cluster = name;
lower->constr_abs_z = false;
lower->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
lower->params[id_INIT_L01] = Property(is_even_x ? LUT_ZERO : LUT_D1, 4); // CINX
lower->params[id_INIT_L10] = Property(LUT_XOR, 4); // XOR
upper->cluster = name;
upper->constr_abs_z = false;
upper->params[id_INIT_L02] = Property(LUT_D1, 4); // PINY1
upper->params[id_INIT_L03] = Property(LUT_ZERO, 4); // (unused)
upper->params[id_INIT_L11] = Property(LUT_D0, 4); // L02
@@ -175,26 +167,24 @@ struct MultfabCell
upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1
}
CellInfo *upper;
CellInfo *lower;
CellInfo *upper;
};
// CITE: CPE_ges_f-routing-1.pdf
// TODO:
struct FRoutingCell
{
FRoutingCell() : lower{nullptr}, upper{nullptr} {}
FRoutingCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_even_x) : lower{lower}, upper{upper}
{
// TODO: simplify AND with zero/OR with zero into something more sensical.
lower->cluster = name;
lower->constr_abs_z = false;
lower->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
lower->params[id_INIT_L01] = Property(LUT_ONE, 4); // (unused)
lower->params[id_INIT_L10] = Property(LUT_AND, 4);
upper->cluster = name;
upper->constr_abs_z = false;
upper->params[id_INIT_L02] = Property(LUT_ZERO, 4); // (unused)
upper->params[id_INIT_L03] = Property(LUT_ONE, 4); // (unused)
upper->params[id_INIT_L11] = Property(LUT_AND, 4);
@@ -216,8 +206,8 @@ struct FRoutingCell
upper->params[id_C_O2] = Property(0b11, 2); // COMB2OUT -> OUT2
}
CellInfo *upper;
CellInfo *lower;
CellInfo *upper;
};
// Multiply two bits of A with two bits of B.
@@ -225,16 +215,14 @@ struct FRoutingCell
// CITE: CPE_MULT.pdf
struct MultCell
{
MultCell() : lower{nullptr}, upper{nullptr} {}
MultCell(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper}
{
lower->cluster = name;
lower->constr_abs_z = false;
lower->params[id_INIT_L00] = Property(LUT_AND, 4);
lower->params[id_INIT_L01] = Property(LUT_D1, 4); // CINX
lower->params[id_INIT_L10] = Property(LUT_XOR, 4);
upper->cluster = name;
upper->constr_abs_z = false;
upper->params[id_INIT_L02] = Property(LUT_AND, 4);
upper->params[id_INIT_L03] = Property(LUT_D1, 4); // PINX
upper->params[id_INIT_L11] = Property(LUT_XOR, 4);
@@ -254,16 +242,16 @@ struct MultCell
PortRef a1() const { return PortRef{upper, id_IN1}; }
PortRef a2() const { return PortRef{lower, id_IN1}; }
CellInfo *upper;
CellInfo *lower;
CellInfo *upper;
};
struct MultMsbCell
{
MultMsbCell() : lower{nullptr}, upper{nullptr} {}
MultMsbCell(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper}
{
lower->cluster = name;
lower->constr_abs_z = false;
lower->params[id_INIT_L00] = Property(LUT_AND, 4);
lower->params[id_INIT_L01] = Property(LUT_D1, 4); // CINX
lower->params[id_INIT_L10] = Property(LUT_XOR, 4);
@@ -286,17 +274,17 @@ struct MultMsbCell
upper->params[id_C_O1] = Property(0b10, 2); // CP_OUT1 -> OUT1
}
CellInfo *upper;
CellInfo *lower;
CellInfo *upper;
};
// CITE: CPE_ges_MSB-routing.pdf
struct MsbRoutingCell
{
MsbRoutingCell() : lower{nullptr}, upper{nullptr} {}
MsbRoutingCell(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper}
{
lower->cluster = name;
lower->constr_abs_z = false;
lower->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
lower->params[id_INIT_L10] = Property(LUT_OR, 4);
@@ -321,20 +309,18 @@ struct MsbRoutingCell
upper->params[id_C_O2] = Property(0b10, 2); // CP_OUT2 -> OUT2
}
CellInfo *upper;
CellInfo *lower;
CellInfo *upper;
};
struct MultiplierColumn
{
// the variables are in bottom-up physical order as a mnemonic.
BPassThroughCell b_passthru;
CarryGenCell carry;
MultfabCell multfab;
FRoutingCell f_route;
std::vector<MultCell> mults;
MultMsbCell multmsb;
MultMsbCell mult_msb;
MsbRoutingCell msb_route;
};
@@ -343,6 +329,15 @@ struct Multiplier
{
std::vector<APassThroughCell> a_passthrus;
std::vector<MultiplierColumn> cols;
size_t cpe_count() const
{
auto count = a_passthrus.size();
for (const auto &col : cols) {
count += 6 + col.mults.size();
}
return count;
}
};
void GateMatePacker::pack_mult()
@@ -350,18 +345,59 @@ void GateMatePacker::pack_mult()
// note to self: use constr_children for recursive constraints
// fpga_generic.pas in p_r might have useful info
auto create_mult_cell = [&](IdString name) {
// instantiate a full CPE by creating two halves.
auto *lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$lower", name.c_str(ctx)));
auto *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", name.c_str(ctx)));
return MultCell{lower, upper, name};
auto create_a_passthru = [&](IdString name) {
auto *a_passthru_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$a_passthru_lower", name.c_str(ctx)));
auto *a_passthru_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$a_passthru_upper", name.c_str(ctx)));
return APassThroughCell{a_passthru_lower, a_passthru_upper, name};
};
auto create_multfab_cell = [&](IdString name, bool is_multfa) {
// instantiate a full CPE by creating two halves.
auto *lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$lower", name.c_str(ctx)));
auto *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", name.c_str(ctx)));
return MultfabCell{lower, upper, name, is_multfa};
auto create_mult_col = [&](IdString name, int a_width, bool is_even_x) {
// Ideally this would be the MultiplierColumn constructor, but we need create_cell_ptr here.
auto col = MultiplierColumn{};
{
auto *b_passthru_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$b_passthru_lower", name.c_str(ctx)));
auto *b_passthru_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$b_passthru_upper", name.c_str(ctx)));
col.b_passthru = BPassThroughCell{b_passthru_lower, b_passthru_upper, name};
}
{
auto *carry_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$carry_lower", name.c_str(ctx)));
auto *carry_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$carry_upper", name.c_str(ctx)));
col.carry = CarryGenCell{carry_lower, carry_upper, name, is_even_x};
}
{
auto *multfab_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$multfab_lower", name.c_str(ctx)));
auto *multfab_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$multfab_upper", name.c_str(ctx)));
col.multfab = MultfabCell{multfab_lower, multfab_upper, name, is_even_x};
}
{
auto *f_route_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$f_route_lower", name.c_str(ctx)));
auto *f_route_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$f_route_upper", name.c_str(ctx)));
col.f_route = FRoutingCell{f_route_lower, f_route_upper, name, is_even_x};
}
for (int i = 0; i < a_width / 2; i++) {
auto *mult_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$row%d$mult_lower", name.c_str(ctx), i));
auto *mult_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$row%d$mult_upper", name.c_str(ctx), i));
col.mults.push_back(MultCell{mult_lower, mult_upper, name});
}
{
auto *mult_msb_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$mult_msb_lower", name.c_str(ctx)));
auto *mult_msb_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$mult_msb_upper", name.c_str(ctx)));
col.mult_msb = MultMsbCell{mult_msb_lower, mult_msb_upper, name};
}
{
auto *msb_route_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$msb_route_lower", name.c_str(ctx)));
auto *msb_route_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$msb_route_upper", name.c_str(ctx)));
col.msb_route = MsbRoutingCell{msb_route_lower, msb_route_upper, name};
}
return col;
};
log_info("Packing multipliers...\n");
@@ -378,13 +414,70 @@ void GateMatePacker::pack_mult()
auto a_width = int_or_default(mult->params, id_A_WIDTH);
auto b_width = int_or_default(mult->params, id_B_WIDTH);
auto p_width = int_or_default(mult->params, id_P_WIDTH);
log_info(" Configuring '%s' as a %d * %d = %d multiplier\n", mult->name.c_str(ctx), a_width, b_width,
p_width);
log_info(" Configuring '%s' as a %d-bit * %d-bit = %d-bit multiplier.\n", mult->name.c_str(ctx), a_width,
b_width, p_width);
NPNR_ASSERT(a_width <= 2);
NPNR_ASSERT(b_width <= 2);
auto m = Multiplier{};
auto [mult_l, mult_u] = create_mult_cell(mult->name);
// Step 1: instantiate all the CPEs.
for (int a = 0; a <= a_width / 2; a++)
m.a_passthrus.push_back(create_a_passthru(ctx->idf("%s$col0$row%d", mult->name.c_str(ctx), a)));
for (int b = 0; b <= b_width / 2; b++)
m.cols.push_back(create_mult_col(ctx->idf("%s$col%d", mult->name.c_str(ctx), b + 1), a_width, b % 2 == 0));
// Step 2: constrain them together.
// We define (0,0) to be the B passthrough cell of column 1.
auto *root = m.cols[0].b_passthru.upper;
auto constrain_cell = [&](CellInfo *cell, int x_offset, int y_offset) {
if (cell == root)
return;
root->constr_children.push_back(cell);
cell->cluster = root->name;
cell->constr_abs_z = false;
cell->constr_x = x_offset;
cell->constr_y = y_offset;
};
// Constrain A passthrough cells.
for (int a = 0; a <= a_width / 2; a++) {
auto &a_passthru = m.a_passthrus[a];
constrain_cell(a_passthru.lower, -1, 4 + a);
constrain_cell(a_passthru.upper, -1, 4 + a);
}
// Constrain multiplier columns.
for (int b = 0; b <= b_width / 2; b++) {
auto &col = m.cols[b];
constrain_cell(col.b_passthru.lower, b, b);
constrain_cell(col.b_passthru.upper, b, b);
constrain_cell(col.carry.lower, b + 1, b);
constrain_cell(col.carry.upper, b + 1, b);
constrain_cell(col.multfab.lower, b + 2, b);
constrain_cell(col.multfab.upper, b + 2, b);
constrain_cell(col.f_route.lower, b + 3, b);
constrain_cell(col.f_route.upper, b + 3, b);
for (size_t mult_idx = 0; mult_idx < col.mults.size(); mult_idx++) {
constrain_cell(col.mults[mult_idx].lower, b + 4 + mult_idx, b);
constrain_cell(col.mults[mult_idx].upper, b + 4 + mult_idx, b);
}
constrain_cell(col.mult_msb.lower, b + 4 + col.mults.size() + 1, b);
constrain_cell(col.mult_msb.upper, b + 4 + col.mults.size() + 1, b);
constrain_cell(col.msb_route.lower, b + 4 + col.mults.size() + 2, b);
constrain_cell(col.msb_route.upper, b + 4 + col.mults.size() + 2, b);
}
// Step 3: connect them.
// Connect A passthrough cells.
log_info(" Created %zu CPEs.\n", m.cpe_count());
}
}