diff --git a/himbaechel/uarch/gatemate/pack_mult.cc b/himbaechel/uarch/gatemate/pack_mult.cc index 72069a26..f3ffadea 100644 --- a/himbaechel/uarch/gatemate/pack_mult.cc +++ b/himbaechel/uarch/gatemate/pack_mult.cc @@ -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 mults; - MultMsbCell multmsb; + MultMsbCell mult_msb; MsbRoutingCell msb_route; }; @@ -343,6 +329,15 @@ struct Multiplier { std::vector a_passthrus; std::vector 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()); } }