From 008ccae25b8043ef13ad8f903a73aa931a9e574f Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 3 Mar 2026 19:17:34 +1000 Subject: [PATCH] Gowin. DSP. Implement MULT27X36. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new multiplier is made from two 27x18 units by switching inputs and creating a cluster connected via CASO->CASI. A second pass was required to process the multipliers created on the fly—the processing of DSP cells was separated into a separate function, which resulted in a large diff, but in reality there were very few changes. An important point is that in the 5A series, there is a gap between adjacent DSPs in one row. There are still SIA/CASI wires, so the DSPs on either side of the gap are connected, but the distance between them is greater than usual. We take this fact into account based on the gap coordinates from the chip database. Signed-off-by: YRabbit --- himbaechel/uarch/gowin/constids.inc | 5 + himbaechel/uarch/gowin/gowin.h | 5 +- himbaechel/uarch/gowin/gowin_arch_gen.py | 18 +- himbaechel/uarch/gowin/gowin_utils.cc | 18 +- himbaechel/uarch/gowin/gowin_utils.h | 4 + himbaechel/uarch/gowin/pack_dsp.cc | 1638 ++++++++++++---------- 6 files changed, 920 insertions(+), 768 deletions(-) diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index d681b881..527521bc 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -974,6 +974,9 @@ X(ASEL1) X(BSEL) X(BSEL0) X(BSEL1) +X(CSEL) +X(PSEL) +X(PADDSUB) X(SOA_REG) X(DSIGN) X(ACCLOAD) @@ -1003,6 +1006,8 @@ X(RESET1) X(ACCSEL) X(ACCSEL0) X(ACCSEL1) +X(MULT27X36_MAIN) +X(MULT27X36_AUX) // IOB types X(IBUF) diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 305cc3be..5d79eb30 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -73,7 +73,8 @@ inline bool is_bsram(const CellInfo *cell) { return type_is_bsram(cell->type); } inline bool type_is_dsp(IdString cell_type) { return cell_type.in(id_PADD9, id_PADD18, id_MULT9X9, id_MULT18X18, id_MULT36X36, id_ALU54D, id_MULTALU18X18, - id_MULTALU36X18, id_MULTADDALU18X18, id_MULT12X12, id_MULTADDALU12X12, id_MULTALU27X18); + id_MULTALU36X18, id_MULTADDALU18X18, id_MULT12X12, id_MULTADDALU12X12, id_MULTALU27X18, + id_MULT27X36); } inline bool is_dsp(const CellInfo *cell) { return type_is_dsp(cell->type); } @@ -197,6 +198,8 @@ NPNR_PACKED_STRUCT(struct Extra_package_data_POD { RelSlice cst; NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { int32_t chip_flags; int32_t dcs_prefix; + int16_t center_row; + int16_t center_col; Bottom_io_POD bottom_io; RelSlice diff_io_types; RelSlice dqce_bels; diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index e7cd26ff..3c1f5aff 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -295,6 +295,8 @@ class SpineSelectWire(BBAStruct): class ChipExtraData(BBAStruct): strs: StringPool flags: int + center_row: int + center_col: int dcs_prefix: IdString = field(default = None) bottom_io: BottomIO = field(default = None) diff_io_types: list[IdString] = field(default_factory = list) @@ -383,6 +385,8 @@ class ChipExtraData(BBAStruct): def serialise(self, context: str, bba: BBAWriter): bba.u32(self.flags) bba.u32(self.dcs_prefix.index) + bba.u16(self.center_row) + bba.u16(self.center_col) self.bottom_io.serialise(f"{context}_bottom_io", bba) bba.slice(f"{context}_diff_io_types", len(self.diff_io_types)) bba.slice(f"{context}_dqce_bels", len(self.dqce_bels)) @@ -1550,7 +1554,19 @@ def create_packages(chip: Chip, db: chipdb): # Extra chip data def create_extra_data(chip: Chip, db: chipdb, chip_flags: int): - chip.extra_data = ChipExtraData(chip.strs, chip_flags) + # The coordinates of the chip center are useful when building a DSP chain + # because there is an area around this particular point that does not + # contain any DSP blocks, but there are cascade and shift wires, so the gap + # between adjacent DSPs is larger than usual at this point. The coordinates + # of this particular cell may be useful when working with 138k clock MUXs + # in the future. + center_row = 0 + center_col = 0 + if hasattr(db, 'center_row'): + center_row = db.center_row + center_col = db.center_col + + chip.extra_data = ChipExtraData(chip.strs, chip_flags, center_row, center_col) if hasattr(db, "dcs_prefix"): chip.extra_data.set_dcs_prefix(db.dcs_prefix) else: diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index b95d1c28..759f7c66 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -510,7 +510,11 @@ Loc GowinUtils::get_dsp_next_in_chain_5a(Loc from) const Loc res; res.y = from.y; // next DSP - res.x = from.x + 3; + int off = 3; + if (from.y == get_center_row() && (from.x + 5) == get_center_col()) { + off = 9; + } + res.x = from.x + off; res.z = from.z; return res; } @@ -741,4 +745,16 @@ void GowinUtils::find_connected_bels(const CellInfo *cell, IdString port, IdStri } } +// Get spec locations +int GowinUtils::get_center_row(void) const +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->center_row; +} +int GowinUtils::get_center_col(void) const +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->center_col; +} + NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index c642d1e8..d5bc473b 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -240,6 +240,10 @@ struct GowinUtils // Find a maximum matching in a bipartite graph, g std::vector kuhn_find_maximum_bipartite_matching(int n, int k, std::vector> &g); + + // Get spec locations + int get_center_row(void) const; + int get_center_col(void) const; }; NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gowin/pack_dsp.cc b/himbaechel/uarch/gowin/pack_dsp.cc index abe1e6fc..d7fe92be 100644 --- a/himbaechel/uarch/gowin/pack_dsp.cc +++ b/himbaechel/uarch/gowin/pack_dsp.cc @@ -38,167 +38,115 @@ void GowinPacker::pack_dsp(void) std::vector> new_cells; log_info("Pack DSP...\n"); + std::vector> new_dsps; std::vector dsp_heads; + std::vector cells_to_remove; - for (auto &cell : ctx->cells) { - auto ci = cell.second.get(); - if (is_dsp(ci)) { - if (ctx->verbose) { - log_info(" pack %s %s\n", ci->type.c_str(ctx), ctx->nameOf(ci)); + auto process_dsp_cell = [&](CellInfo *ci) { + if (ctx->verbose) { + log_info(" pack %s %s\n", ci->type.c_str(ctx), ctx->nameOf(ci)); + } + switch (ci->type.hash()) { + case ID_PADD9: { + pass_net_type(ci, id_ASEL); + for (int i = 0; i < 9; ++i) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); } - switch (ci->type.hash()) { - case ID_PADD9: { - pass_net_type(ci, id_ASEL); + for (int i = 0; i < 9; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + + // ADD_SUB wire + IdString add_sub_net = ctx->id("$PACKER_GND"); + if (ci->params.count(ctx->id("ADD_SUB"))) { + if (ci->params.at(ctx->id("ADD_SUB")).as_int64() == 1) { + add_sub_net = ctx->id("$PACKER_VCC"); + } + } + ci->addInput(ctx->id("ADDSUB")); + ci->connectPort(ctx->id("ADDSUB"), ctx->nets.at(add_sub_net).get()); + + // PADD does not have outputs to the outside of the DSP - + // it is always connected to the inputs of the multiplier; + // to emulate a separate PADD primitive, we use + // multiplication by input C, equal to 1. We can switch the + // multiplier to multiplication mode by C in gowin_pack, + // but we will have to generate the value 1 at input C + // here. + ci->addInput(ctx->id("C0")); + ci->connectPort(ctx->id("C0"), ctx->nets.at(ctx->id("$PACKER_VCC")).get()); + for (int i = 1; i < 9; ++i) { + ci->addInput(ctx->idf("C%d", i)); + ci->connectPort(ctx->idf("C%d", i), ctx->nets.at(ctx->id("$PACKER_GND")).get()); + } + // mark mult9x9 as used by making cluster + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_y = 0; + + IdString mult_name = gwu.create_aux_name(ci->name); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); + + mult_ci->cluster = ci->name; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = gwu.get_dsp_mult_from_padd(0); + + // DSP head? + if (gwu.dsp_bus_src(ci, "SI", 9) == nullptr && gwu.dsp_bus_dst(ci, "SBO", 9) == nullptr) { for (int i = 0; i < 9; ++i) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + ci->disconnectPort(ctx->idf("SI[%d]", i)); + ci->disconnectPort(ctx->idf("SBO[%d]", i)); } - for (int i = 0; i < 9; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); } + } + } break; + case ID_PADD18: { + pass_net_type(ci, id_ASEL); + for (int i = 0; i < 18; ++i) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + } + for (int i = 0; i < 18; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } - // ADD_SUB wire - IdString add_sub_net = ctx->id("$PACKER_GND"); - if (ci->params.count(ctx->id("ADD_SUB"))) { - if (ci->params.at(ctx->id("ADD_SUB")).as_int64() == 1) { - add_sub_net = ctx->id("$PACKER_VCC"); - } + // ADD_SUB wire + IdString add_sub_net = ctx->id("$PACKER_GND"); + if (ci->params.count(ctx->id("ADD_SUB"))) { + if (ci->params.at(ctx->id("ADD_SUB")).as_int64() == 1) { + add_sub_net = ctx->id("$PACKER_VCC"); } - ci->addInput(ctx->id("ADDSUB")); - ci->connectPort(ctx->id("ADDSUB"), ctx->nets.at(add_sub_net).get()); + } + ci->addInput(ctx->id("ADDSUB")); + ci->connectPort(ctx->id("ADDSUB"), ctx->nets.at(add_sub_net).get()); - // PADD does not have outputs to the outside of the DSP - - // it is always connected to the inputs of the multiplier; - // to emulate a separate PADD primitive, we use - // multiplication by input C, equal to 1. We can switch the - // multiplier to multiplication mode by C in gowin_pack, - // but we will have to generate the value 1 at input C - // here. - ci->addInput(ctx->id("C0")); - ci->connectPort(ctx->id("C0"), ctx->nets.at(ctx->id("$PACKER_VCC")).get()); - for (int i = 1; i < 9; ++i) { - ci->addInput(ctx->idf("C%d", i)); - ci->connectPort(ctx->idf("C%d", i), ctx->nets.at(ctx->id("$PACKER_GND")).get()); - } - // mark mult9x9 as used by making cluster - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_y = 0; + // XXX form C as 1 + ci->addInput(ctx->id("C0")); + ci->connectPort(ctx->id("C0"), ctx->nets.at(ctx->id("$PACKER_VCC")).get()); + for (int i = 1; i < 18; ++i) { + ci->addInput(ctx->idf("C%d", i)); + ci->connectPort(ctx->idf("C%d", i), ctx->nets.at(ctx->id("$PACKER_GND")).get()); + } + // + // add padd9s and mult9s as a children + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); - IdString mult_name = gwu.create_aux_name(ci->name); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); - - mult_ci->cluster = ci->name; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = gwu.get_dsp_mult_from_padd(0); - - // DSP head? - if (gwu.dsp_bus_src(ci, "SI", 9) == nullptr && gwu.dsp_bus_dst(ci, "SBO", 9) == nullptr) { - for (int i = 0; i < 9; ++i) { - ci->disconnectPort(ctx->idf("SI[%d]", i)); - ci->disconnectPort(ctx->idf("SBO[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_PADD18: { - pass_net_type(ci, id_ASEL); - for (int i = 0; i < 18; ++i) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); - } - for (int i = 0; i < 18; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - - // ADD_SUB wire - IdString add_sub_net = ctx->id("$PACKER_GND"); - if (ci->params.count(ctx->id("ADD_SUB"))) { - if (ci->params.at(ctx->id("ADD_SUB")).as_int64() == 1) { - add_sub_net = ctx->id("$PACKER_VCC"); - } - } - ci->addInput(ctx->id("ADDSUB")); - ci->connectPort(ctx->id("ADDSUB"), ctx->nets.at(add_sub_net).get()); - - // XXX form C as 1 - ci->addInput(ctx->id("C0")); - ci->connectPort(ctx->id("C0"), ctx->nets.at(ctx->id("$PACKER_VCC")).get()); - for (int i = 1; i < 18; ++i) { - ci->addInput(ctx->idf("C%d", i)); - ci->connectPort(ctx->idf("C%d", i), ctx->nets.at(ctx->id("$PACKER_GND")).get()); - } - // - // add padd9s and mult9s as a children - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); - - for (int i = 0; i < 2; ++i) { - IdString padd_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); - new_cells.push_back(std::move(padd_cell)); - CellInfo *padd_ci = new_cells.back().get(); - - padd_ci->cluster = ci->name; - padd_ci->constr_abs_z = false; - padd_ci->constr_x = 0; - padd_ci->constr_y = 0; - padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::PADD18_0_0_Z + i; - - IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); - - mult_ci->cluster = ci->name; - mult_ci->constr_abs_z = false; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::PADD18_0_0_Z + i; - } - // DSP head? - if (gwu.dsp_bus_src(ci, "SI", 18) == nullptr && gwu.dsp_bus_dst(ci, "SBO", 18) == nullptr) { - for (int i = 0; i < 18; ++i) { - ci->disconnectPort(ctx->idf("SI[%d]", i)); - ci->disconnectPort(ctx->idf("SBO[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_MULT9X9: { - pass_net_type(ci, id_ASEL); - pass_net_type(ci, id_BSEL); - for (int i = 0; i < 9; ++i) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); - } - for (int i = 0; i < 18; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - // add padd9 as a child - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); - - IdString padd_name = gwu.create_aux_name(ci->name); + for (int i = 0; i < 2; ++i) { + IdString padd_name = gwu.create_aux_name(ci->name, i * 2); std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); new_cells.push_back(std::move(padd_cell)); CellInfo *padd_ci = new_cells.back().get(); @@ -207,661 +155,821 @@ void GowinPacker::pack_dsp(void) padd_ci->constr_abs_z = false; padd_ci->constr_x = 0; padd_ci->constr_y = 0; - padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULT9X9_0_0_Z; + padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::PADD18_0_0_Z + i; - // DSP head? - if (gwu.dsp_bus_src(ci, "SIA", 9) == nullptr && gwu.dsp_bus_src(ci, "SIB", 9) == nullptr) { - for (int i = 0; i < 9; ++i) { - ci->disconnectPort(ctx->idf("SIA[%d]", i)); - ci->disconnectPort(ctx->idf("SIB[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_MULT12X12: { - for (int i = 0; i < 2; ++i) { - ci->renamePort(ctx->idf("CLK[%d]", i), ctx->idf("CLK%d", i)); - ci->renamePort(ctx->idf("CE[%d]", i), ctx->idf("CE%d", i)); - ci->renamePort(ctx->idf("RESET[%d]", i), ctx->idf("RESET%d", i)); - } - for (int i = 0; i < 12; ++i) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); - } - for (int i = 0; i < 24; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - } break; - case ID_MULT18X18: { - pass_net_type(ci, id_ASEL); - pass_net_type(ci, id_BSEL); + IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); + + mult_ci->cluster = ci->name; + mult_ci->constr_abs_z = false; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::PADD18_0_0_Z + i; + } + // DSP head? + if (gwu.dsp_bus_src(ci, "SI", 18) == nullptr && gwu.dsp_bus_dst(ci, "SBO", 18) == nullptr) { for (int i = 0; i < 18; ++i) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + ci->disconnectPort(ctx->idf("SI[%d]", i)); + ci->disconnectPort(ctx->idf("SBO[%d]", i)); } - for (int i = 0; i < 36; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); } - // add padd9s and mult9s as a children - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); + } + } break; + case ID_MULT9X9: { + pass_net_type(ci, id_ASEL); + pass_net_type(ci, id_BSEL); + for (int i = 0; i < 9; ++i) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + } + for (int i = 0; i < 18; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + // add padd9 as a child + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); - for (int i = 0; i < 2; ++i) { - IdString padd_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); - new_cells.push_back(std::move(padd_cell)); - CellInfo *padd_ci = new_cells.back().get(); + IdString padd_name = gwu.create_aux_name(ci->name); + std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); + new_cells.push_back(std::move(padd_cell)); + CellInfo *padd_ci = new_cells.back().get(); - padd_ci->cluster = ci->name; - padd_ci->constr_abs_z = false; - padd_ci->constr_x = 0; - padd_ci->constr_y = 0; - padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULT18X18_0_0_Z + i; + padd_ci->cluster = ci->name; + padd_ci->constr_abs_z = false; + padd_ci->constr_x = 0; + padd_ci->constr_y = 0; + padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULT9X9_0_0_Z; - IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); - - mult_ci->cluster = ci->name; - mult_ci->constr_abs_z = false; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULT18X18_0_0_Z + i; + // DSP head? + if (gwu.dsp_bus_src(ci, "SIA", 9) == nullptr && gwu.dsp_bus_src(ci, "SIB", 9) == nullptr) { + for (int i = 0; i < 9; ++i) { + ci->disconnectPort(ctx->idf("SIA[%d]", i)); + ci->disconnectPort(ctx->idf("SIB[%d]", i)); } - - // DSP head? - if (gwu.dsp_bus_src(ci, "SIA", 18) == nullptr && gwu.dsp_bus_src(ci, "SIB", 18) == nullptr) { - for (int i = 0; i < 18; ++i) { - ci->disconnectPort(ctx->idf("SIA[%d]", i)); - ci->disconnectPort(ctx->idf("SIB[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); } - } break; - case ID_ALU54D: { - pass_net_type(ci, id_ACCLOAD); - for (int i = 0; i < 54; ++i) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); - } - // ACCLOAD - It looks like these wires are always connected to each other. - ci->cell_bel_pins.at(id_ACCLOAD).clear(); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD0); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD1); + } + } break; + case ID_MULT12X12: { + for (int i = 0; i < 2; ++i) { + ci->renamePort(ctx->idf("CLK[%d]", i), ctx->idf("CLK%d", i)); + ci->renamePort(ctx->idf("CE[%d]", i), ctx->idf("CE%d", i)); + ci->renamePort(ctx->idf("RESET[%d]", i), ctx->idf("RESET%d", i)); + } + for (int i = 0; i < 12; ++i) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + } + for (int i = 0; i < 24; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + } break; + case ID_MULT18X18: { + pass_net_type(ci, id_ASEL); + pass_net_type(ci, id_BSEL); + for (int i = 0; i < 18; ++i) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + } + for (int i = 0; i < 36; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + // add padd9s and mult9s as a children + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); - for (int i = 0; i < 54; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - // add padd9s and mult9s as a children - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); + for (int i = 0; i < 2; ++i) { + IdString padd_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); + new_cells.push_back(std::move(padd_cell)); + CellInfo *padd_ci = new_cells.back().get(); - for (int i = 0; i < 4; ++i) { - IdString padd_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); - new_cells.push_back(std::move(padd_cell)); - CellInfo *padd_ci = new_cells.back().get(); + padd_ci->cluster = ci->name; + padd_ci->constr_abs_z = false; + padd_ci->constr_x = 0; + padd_ci->constr_y = 0; + padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULT18X18_0_0_Z + i; - padd_ci->cluster = ci->name; - padd_ci->constr_abs_z = false; - padd_ci->constr_x = 0; - padd_ci->constr_y = 0; - padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::ALU54D_0_Z + 4 * (i / 2) + (i % 2); + IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); - IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); + mult_ci->cluster = ci->name; + mult_ci->constr_abs_z = false; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULT18X18_0_0_Z + i; + } - mult_ci->cluster = ci->name; - mult_ci->constr_abs_z = false; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::ALU54D_0_Z + 4 * (i / 2) + (i % 2); - } - - // DSP head? - if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { - for (int i = 0; i < 55; ++i) { - ci->disconnectPort(ctx->idf("CASI[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_MULTALU18X18: { - // Ports C and D conflict so we need to know the operating mode here. - if (ci->params.count(id_MULTALU18X18_MODE) == 0) { - ci->setParam(id_MULTALU18X18_MODE, 0); - } - int multalu18x18_mode = ci->params.at(id_MULTALU18X18_MODE).as_int64(); - if (multalu18x18_mode < 0 || multalu18x18_mode > 2) { - log_error("%s MULTALU18X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci)); - } - NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); - - for (int i = 0; i < 54; ++i) { - if (i < 18) { - if (multalu18x18_mode != 2) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d1", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d1", i)); - } else { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d0", i)); - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d0", i)); - } - } - switch (multalu18x18_mode) { - case 0: - ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i)); - ci->disconnectPort(ctx->idf("D[%d]", i)); - break; - case 1: - ci->disconnectPort(ctx->idf("C[%d]", i)); - ci->disconnectPort(ctx->idf("D[%d]", i)); - break; - case 2: - ci->disconnectPort(ctx->idf("C[%d]", i)); - ci->renamePort(ctx->idf("D[%d]", i), ctx->idf("D%d", i)); - break; - default: - break; - } - } - if (multalu18x18_mode != 2) { - ci->renamePort(id_ASIGN, id_ASIGN1); - ci->renamePort(id_BSIGN, id_BSIGN1); - ci->addInput(id_ASIGN0); - ci->addInput(id_BSIGN0); - ci->connectPort(id_ASIGN0, vss_net); - ci->connectPort(id_BSIGN0, vss_net); - ci->disconnectPort(id_DSIGN); - } else { // BSIGN0 and DSIGN are the same wire - ci->renamePort(id_ASIGN, id_ASIGN0); - ci->addInput(id_ASIGN1); - ci->connectPort(id_ASIGN1, vss_net); - ci->renamePort(id_BSIGN, id_BSIGN0); - } - - // ACCLOAD - It looks like these wires are always connected to each other. - pass_net_type(ci, id_ACCLOAD); - ci->cell_bel_pins.at(id_ACCLOAD).clear(); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD0); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD1); - - for (int i = 0; i < 54; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - - // add padd9s and mult9s as a children - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); - - for (int i = 0; i < 2; ++i) { - IdString padd_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); - new_cells.push_back(std::move(padd_cell)); - CellInfo *padd_ci = new_cells.back().get(); - - padd_ci->cluster = ci->name; - padd_ci->constr_abs_z = false; - padd_ci->constr_x = 0; - padd_ci->constr_y = 0; - padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTALU18X18_0_Z + i; - - IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); - - mult_ci->cluster = ci->name; - mult_ci->constr_abs_z = false; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTALU18X18_0_Z + i; - } - - // DSP head? - if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { - for (int i = 0; i < 55; ++i) { - ci->disconnectPort(ctx->idf("CASI[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_MULTALU27X18: { - for (int i = 0; i < 2; ++i) { - ci->renamePort(ctx->idf("CLK[%d]", i), ctx->idf("CLK%d", i)); - ci->renamePort(ctx->idf("CE[%d]", i), ctx->idf("CE%d", i)); - ci->renamePort(ctx->idf("RESET[%d]", i), ctx->idf("RESET%d", i)); - ci->renamePort(ctx->idf("ADDSUB[%d]", i), ctx->idf("ADDSUB%d", i)); - } - for (int i = 0; i < 27; ++i) { - ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); - } - for (int i = 0; i < 26; ++i) { - ci->renamePort(ctx->idf("D[%d]", i), ctx->idf("D%d", i)); - } + // DSP head? + if (gwu.dsp_bus_src(ci, "SIA", 18) == nullptr && gwu.dsp_bus_src(ci, "SIB", 18) == nullptr) { for (int i = 0; i < 18; ++i) { - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + ci->disconnectPort(ctx->idf("SIA[%d]", i)); + ci->disconnectPort(ctx->idf("SIB[%d]", i)); } - for (int i = 0; i < 48; ++i) { + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); + } + } + } break; + case ID_ALU54D: { + pass_net_type(ci, id_ACCLOAD); + for (int i = 0; i < 54; ++i) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + } + // ACCLOAD - It looks like these wires are always connected to each other. + ci->cell_bel_pins.at(id_ACCLOAD).clear(); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD0); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD1); + + for (int i = 0; i < 54; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + // add padd9s and mult9s as a children + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); + + for (int i = 0; i < 4; ++i) { + IdString padd_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); + new_cells.push_back(std::move(padd_cell)); + CellInfo *padd_ci = new_cells.back().get(); + + padd_ci->cluster = ci->name; + padd_ci->constr_abs_z = false; + padd_ci->constr_x = 0; + padd_ci->constr_y = 0; + padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::ALU54D_0_Z + 4 * (i / 2) + (i % 2); + + IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); + + mult_ci->cluster = ci->name; + mult_ci->constr_abs_z = false; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::ALU54D_0_Z + 4 * (i / 2) + (i % 2); + } + + // DSP head? + if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { + for (int i = 0; i < 55; ++i) { + ci->disconnectPort(ctx->idf("CASI[%d]", i)); + } + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); + } + } + } break; + case ID_MULTALU18X18: { + // Ports C and D conflict so we need to know the operating mode here. + if (ci->params.count(id_MULTALU18X18_MODE) == 0) { + ci->setParam(id_MULTALU18X18_MODE, 0); + } + int multalu18x18_mode = ci->params.at(id_MULTALU18X18_MODE).as_int64(); + if (multalu18x18_mode < 0 || multalu18x18_mode > 2) { + log_error("%s MULTALU18X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci)); + } + NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); + + for (int i = 0; i < 54; ++i) { + if (i < 18) { + if (multalu18x18_mode != 2) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d1", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d1", i)); + } else { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d0", i)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d0", i)); + } + } + switch (multalu18x18_mode) { + case 0: ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i)); + ci->disconnectPort(ctx->idf("D[%d]", i)); + break; + case 1: + ci->disconnectPort(ctx->idf("C[%d]", i)); + ci->disconnectPort(ctx->idf("D[%d]", i)); + break; + case 2: + ci->disconnectPort(ctx->idf("C[%d]", i)); + ci->renamePort(ctx->idf("D[%d]", i), ctx->idf("D%d", i)); + break; + default: + break; } - pass_net_type(ci, id_ACCSEL); - ci->cell_bel_pins.at(id_ACCSEL).clear(); - ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL0); - ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL1); - - for (int i = 0; i < 48; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - - // mark 2 mult12x12 as parts of the cluster to prevent - // other multipliers from being placed there - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); - - for (int i = 0; i < 2; ++i) { - IdString mult12x12_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr mult12x12_cell = gwu.create_cell(mult12x12_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult12x12_cell)); - CellInfo *mult12x12_ci = new_cells.back().get(); - - mult12x12_ci->cluster = ci->name; - mult12x12_ci->constr_abs_z = false; - mult12x12_ci->constr_x = 0; - mult12x12_ci->constr_y = 0; - mult12x12_ci->constr_z = BelZ::MULT12X12_0_Z - BelZ::MULTALU27X18_Z + i; - } - - // DSP head? This primitive has the ability to form chains using both SO[AB] -> SI[AB] and - // CASO->CASI - bool cas_head = false; - if (gwu.dsp_bus_src(ci, "CASI", 48) == nullptr) { - for (int i = 0; i < 48; ++i) { - ci->disconnectPort(ctx->idf("CASI[%d]", i)); - } - cas_head = true; - } - bool so_head = false; - if (gwu.dsp_bus_src(ci, "SIA", 27) == nullptr) { - for (int i = 0; i < 27; ++i) { - ci->disconnectPort(ctx->idf("SIA[%d]", i)); - } - so_head = true; - } - if (cas_head && so_head) { - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_MULTALU36X18: { - if (ci->params.count(id_MULTALU18X18_MODE) == 0) { - ci->setParam(id_MULTALU18X18_MODE, 0); - } - int multalu36x18_mode = ci->params.at(id_MULTALU36X18_MODE).as_int64(); - if (multalu36x18_mode < 0 || multalu36x18_mode > 2) { - log_error("%s MULTALU36X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci)); - } - NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); - - for (int i = 0; i < 36; ++i) { - if (i < 18) { - ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).clear(); - ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d0", i)); - ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d1", i)); - } - ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); - } - for (int i = 0; i < 54; ++i) { - switch (multalu36x18_mode) { - case 0: - ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i)); - break; - case 1: /* fallthrough */ - case 2: - ci->disconnectPort(ctx->idf("C[%d]", i)); - break; - default: - break; - } - } - - // both A have sign bit - // only MSB part of B has sign bit - ci->cell_bel_pins.at(id_ASIGN).clear(); - ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN0); - ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN1); + } + if (multalu18x18_mode != 2) { + ci->renamePort(id_ASIGN, id_ASIGN1); ci->renamePort(id_BSIGN, id_BSIGN1); + ci->addInput(id_ASIGN0); ci->addInput(id_BSIGN0); + ci->connectPort(id_ASIGN0, vss_net); ci->connectPort(id_BSIGN0, vss_net); + ci->disconnectPort(id_DSIGN); + } else { // BSIGN0 and DSIGN are the same wire + ci->renamePort(id_ASIGN, id_ASIGN0); + ci->addInput(id_ASIGN1); + ci->connectPort(id_ASIGN1, vss_net); + ci->renamePort(id_BSIGN, id_BSIGN0); + } - pass_net_type(ci, id_ACCLOAD); - if (multalu36x18_mode == 1) { - if (ci->attrs.at(id_NET_ACCLOAD).as_string() == "GND" || - ci->attrs.at(id_NET_ACCLOAD).as_string() == "VCC") { - ci->disconnectPort(id_ACCLOAD); - } else { - ci->addInput(id_ALUSEL4); - ci->addInput(id_ALUSEL6); - ci->cell_bel_pins.at(id_ACCLOAD).clear(); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL4); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL6); - } - } else { - ci->disconnectPort(id_ACCLOAD); + // ACCLOAD - It looks like these wires are always connected to each other. + pass_net_type(ci, id_ACCLOAD); + ci->cell_bel_pins.at(id_ACCLOAD).clear(); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD0); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD1); + + for (int i = 0; i < 54; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + + // add padd9s and mult9s as a children + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); + + for (int i = 0; i < 2; ++i) { + IdString padd_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); + new_cells.push_back(std::move(padd_cell)); + CellInfo *padd_ci = new_cells.back().get(); + + padd_ci->cluster = ci->name; + padd_ci->constr_abs_z = false; + padd_ci->constr_x = 0; + padd_ci->constr_y = 0; + padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTALU18X18_0_Z + i; + + IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); + + mult_ci->cluster = ci->name; + mult_ci->constr_abs_z = false; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTALU18X18_0_Z + i; + } + + // DSP head? + if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { + for (int i = 0; i < 55; ++i) { + ci->disconnectPort(ctx->idf("CASI[%d]", i)); } - - for (int i = 0; i < 54; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); } + } + } break; + case ID_MULT27X36: { + // We assemble that primitive from two MultAlu27x18 connected via CASO->CASI. + IdString l_mult_name = gwu.create_aux_name(ci->name, 1); + std::unique_ptr l_mult_cell = gwu.create_cell(l_mult_name, id_MULTALU27X18); + new_dsps.push_back(std::move(l_mult_cell)); + CellInfo *l_mult = new_dsps.back().get(); - // add padd9s and mult9s as a children - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); + IdString h_mult_name = gwu.create_aux_name(ci->name, 2); + std::unique_ptr h_mult_cell = gwu.create_cell(h_mult_name, id_MULTALU27X18); + new_dsps.push_back(std::move(h_mult_cell)); + CellInfo *h_mult = new_dsps.back().get(); - for (int i = 0; i < 2; ++i) { - IdString padd_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); - new_cells.push_back(std::move(padd_cell)); - CellInfo *padd_ci = new_cells.back().get(); + // SIAs are disconnected + for (int i = 0; i < 27; ++i) { + l_mult->addInput(ctx->idf("SIA[%d]", i)); + h_mult->addInput(ctx->idf("SIA[%d]", i)); + l_mult->addOutput(ctx->idf("SOA[%d]", i)); + h_mult->addOutput(ctx->idf("SOA[%d]", i)); + } - padd_ci->cluster = ci->name; - padd_ci->constr_abs_z = false; - padd_ci->constr_x = 0; - padd_ci->constr_y = 0; - padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTALU36X18_0_Z + i; + // unused + l_mult->addInput(id_CSEL); + h_mult->addInput(id_CSEL); + l_mult->addInput(id_ASEL); + h_mult->addInput(id_ASEL); + l_mult->addInput(id_ACCSEL); + h_mult->addInput(id_ACCSEL); - IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); + for (int i = 0; i < 48; ++i) { + // C is disconnected + l_mult->addInput(ctx->idf("C[%d]", i)); + h_mult->addInput(ctx->idf("C[%d]", i)); + // low mult CASI is disconnected + l_mult->addInput(ctx->idf("CASI[%d]", i)); + // high mult CASO is disconnected + h_mult->addOutput(ctx->idf("CASO[%d]", i)); + // low CASO -> high CASI + l_mult->addOutput(ctx->idf("CASO[%d]", i)); + h_mult->addInput(ctx->idf("CASI[%d]", i)); + l_mult->connectPorts(ctx->idf("CASO[%d]", i), h_mult, ctx->idf("CASI[%d]", i)); + } - mult_ci->cluster = ci->name; - mult_ci->constr_abs_z = false; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTALU36X18_0_Z + i; + // input A is shared + ci->movePortBusTo(id_A, 0, 1, l_mult, id_A, 0, 1, 27); + l_mult->copyPortBusTo(id_A, 0, 1, h_mult, id_A, 0, 1, 27); + + // input D is shared + ci->movePortBusTo(id_D, 0, 1, l_mult, id_D, 0, 1, 26); + l_mult->copyPortBusTo(id_D, 0, 1, h_mult, id_D, 0, 1, 26); + + // input PSEL is shared + ci->movePortTo(id_PSEL, l_mult, id_PSEL); + l_mult->copyPortBusTo(id_PSEL, 0, 1, h_mult, id_PSEL, 0, 1, 26); + + // input PADDSUB is shared + ci->movePortTo(id_PADDSUB, l_mult, id_PADDSUB); + l_mult->copyPortTo(id_PADDSUB, h_mult, id_PADDSUB); + + // input B is divided + ci->movePortBusTo(id_B, 0, 1, l_mult, id_B, 0, 1, 18); + ci->movePortBusTo(id_B, 18, 1, h_mult, id_B, 0, 1, 18); + + // output DOUT is divided + ci->movePortBusTo(id_DOUT, 0, 1, l_mult, id_DOUT, 0, 1, 18); + ci->movePortBusTo(id_DOUT, 18, 1, h_mult, id_DOUT, 0, 1, 47); + + // Control inputs are shared + ci->movePortBusTo(id_CLK, 0, 1, l_mult, id_CLK, 0, 1, 2); + l_mult->copyPortBusTo(id_CLK, 0, 1, h_mult, id_CLK, 0, 1, 2); + ci->movePortBusTo(id_CE, 0, 1, l_mult, id_CE, 0, 1, 2); + l_mult->copyPortBusTo(id_CE, 0, 1, h_mult, id_CE, 0, 1, 2); + ci->movePortBusTo(id_RESET, 0, 1, l_mult, id_RESET, 0, 1, 2); + l_mult->copyPortBusTo(id_RESET, 0, 1, h_mult, id_RESET, 0, 1, 2); + + // copy params + for (auto param : ci->params) { + l_mult->setParam(param.first, param.second); + h_mult->setParam(param.first, param.second); + } + // mark as MULT27X36 + // we will catch these attributes during packing and add the missing parameters + l_mult->setAttr(id_MULT27X36_MAIN, 1); + h_mult->setAttr(id_MULT27X36_AUX, 1); + + // remove former Mult27x36 + cells_to_remove.push_back(ci->name); + } break; + case ID_MULTALU27X18: { + for (int i = 0; i < 2; ++i) { + ci->renamePort(ctx->idf("CLK[%d]", i), ctx->idf("CLK%d", i)); + ci->renamePort(ctx->idf("CE[%d]", i), ctx->idf("CE%d", i)); + ci->renamePort(ctx->idf("RESET[%d]", i), ctx->idf("RESET%d", i)); + ci->renamePort(ctx->idf("ADDSUB[%d]", i), ctx->idf("ADDSUB%d", i)); + } + for (int i = 0; i < 27; ++i) { + ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d", i)); + } + for (int i = 0; i < 26; ++i) { + ci->renamePort(ctx->idf("D[%d]", i), ctx->idf("D%d", i)); + } + for (int i = 0; i < 18; ++i) { + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); + } + for (int i = 0; i < 48; ++i) { + ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i)); + } + pass_net_type(ci, id_ACCSEL); + ci->cell_bel_pins[id_ACCSEL].clear(); + ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL0); + ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL1); + + for (int i = 0; i < 48; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + + // mark 2 mult12x12 as parts of the cluster to prevent + // other multipliers from being placed there + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); + + for (int i = 0; i < 2; ++i) { + IdString mult12x12_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr mult12x12_cell = gwu.create_cell(mult12x12_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult12x12_cell)); + CellInfo *mult12x12_ci = new_cells.back().get(); + + mult12x12_ci->cluster = ci->name; + mult12x12_ci->constr_abs_z = false; + mult12x12_ci->constr_x = 0; + mult12x12_ci->constr_y = 0; + mult12x12_ci->constr_z = BelZ::MULT12X12_0_Z - BelZ::MULTALU27X18_Z + i; + if (ctx->verbose) { + log_info(" mark %s multiplier as used by %s\n", mult12x12_name.c_str(ctx), ctx->nameOf(ci)); } + } - // DSP head? - if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { - for (int i = 0; i < 55; ++i) { - ci->disconnectPort(ctx->idf("CASI[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_MULTADDALU12X12: { - for (int i = 0; i < 2; ++i) { - ci->renamePort(ctx->idf("CLK[%d]", i), ctx->idf("CLK%d", i)); - ci->renamePort(ctx->idf("CE[%d]", i), ctx->idf("CE%d", i)); - ci->renamePort(ctx->idf("RESET[%d]", i), ctx->idf("RESET%d", i)); - ci->renamePort(ctx->idf("ADDSUB[%d]", i), ctx->idf("ADDSUB%d", i)); - } - for (int i = 0; i < 12; ++i) { - ci->renamePort(ctx->idf("A0[%d]", i), ctx->idf("A0%d", i)); - ci->renamePort(ctx->idf("B0[%d]", i), ctx->idf("B0%d", i)); - ci->renamePort(ctx->idf("A1[%d]", i), ctx->idf("A1%d", i)); - ci->renamePort(ctx->idf("B1[%d]", i), ctx->idf("B1%d", i)); - } - pass_net_type(ci, id_ACCSEL); - ci->cell_bel_pins.at(id_ACCSEL).clear(); - ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL0); - ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL1); - + // DSP head? This primitive has the ability to form chains using both SO[AB] -> SI[AB] and + // CASO->CASI + bool cas_head = false; + if (gwu.dsp_bus_src(ci, "CASI", 48) == nullptr) { for (int i = 0; i < 48; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + ci->disconnectPort(ctx->idf("CASI[%d]", i)); } - - // mark 2 mult12x12 as parts of the cluster to prevent - // other multipliers from being placed there - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); - - for (int i = 0; i < 2; ++i) { - IdString mult12x12_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr mult12x12_cell = gwu.create_cell(mult12x12_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult12x12_cell)); - CellInfo *mult12x12_ci = new_cells.back().get(); - - mult12x12_ci->cluster = ci->name; - mult12x12_ci->constr_abs_z = false; - mult12x12_ci->constr_x = 0; - mult12x12_ci->constr_y = 0; - mult12x12_ci->constr_z = BelZ::MULT12X12_0_Z - BelZ::MULTADDALU12X12_Z + i; + cas_head = true; + } + bool so_head = false; + if (gwu.dsp_bus_src(ci, "SIA", 27) == nullptr) { + for (int i = 0; i < 27; ++i) { + ci->disconnectPort(ctx->idf("SIA[%d]", i)); } + so_head = true; + } + if (cas_head && so_head) { + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); + } + } + } break; + case ID_MULTALU36X18: { + if (ci->params.count(id_MULTALU18X18_MODE) == 0) { + ci->setParam(id_MULTALU18X18_MODE, 0); + } + int multalu36x18_mode = ci->params.at(id_MULTALU36X18_MODE).as_int64(); + if (multalu36x18_mode < 0 || multalu36x18_mode > 2) { + log_error("%s MULTALU36X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci)); + } + NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); - // DSP head? - if (gwu.dsp_bus_src(ci, "CASI", 48) == nullptr) { - for (int i = 0; i < 48; ++i) { - ci->disconnectPort(ctx->idf("CASI[%d]", i)); - } - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - - } break; - case ID_MULTADDALU18X18: { - if (ci->params.count(id_MULTADDALU18X18_MODE) == 0) { - ci->setParam(id_MULTADDALU18X18_MODE, 0); - } - int multaddalu18x18_mode = ci->params.at(id_MULTADDALU18X18_MODE).as_int64(); - if (multaddalu18x18_mode < 0 || multaddalu18x18_mode > 2) { - log_error("%s MULTADDALU18X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci)); - } - for (int i = 0; i < 54; ++i) { - if (i < 18) { - ci->renamePort(ctx->idf("A0[%d]", i), ctx->idf("A%d0", i)); - ci->renamePort(ctx->idf("B0[%d]", i), ctx->idf("B%d0", i)); - ci->renamePort(ctx->idf("A1[%d]", i), ctx->idf("A%d1", i)); - ci->renamePort(ctx->idf("B1[%d]", i), ctx->idf("B%d1", i)); - } - if (multaddalu18x18_mode == 0) { - ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i)); - } else { - ci->disconnectPort(ctx->idf("C[%d]", i)); - } - } - for (int i = 0; i < 2; ++i) { - ci->renamePort(ctx->idf("ASIGN[%d]", i), ctx->idf("ASIGN%d", i)); - ci->renamePort(ctx->idf("BSIGN[%d]", i), ctx->idf("BSIGN%d", i)); - ci->renamePort(ctx->idf("ASEL[%d]", i), ctx->idf("ASEL%d", i)); - ci->renamePort(ctx->idf("BSEL[%d]", i), ctx->idf("BSEL%d", i)); - } - - pass_net_type(ci, id_ASEL0); - pass_net_type(ci, id_ASEL1); - pass_net_type(ci, id_BSEL0); - pass_net_type(ci, id_BSEL1); - pass_net_type(ci, id_ACCLOAD); - if (multaddalu18x18_mode == 1) { - if (ci->attrs.at(id_NET_ACCLOAD).as_string() == "GND" || - ci->attrs.at(id_NET_ACCLOAD).as_string() == "VCC") { - ci->disconnectPort(id_ACCLOAD); - } else { - ci->addInput(id_ALUSEL4); - ci->addInput(id_ALUSEL6); - ci->cell_bel_pins.at(id_ACCLOAD).clear(); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL4); - ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL6); - } - } else { - ci->disconnectPort(id_ACCLOAD); - } - - for (int i = 0; i < 54; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - - // add padd9s and mult9s as a children - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); - - for (int i = 0; i < 2; ++i) { - IdString padd_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); - new_cells.push_back(std::move(padd_cell)); - CellInfo *padd_ci = new_cells.back().get(); - - padd_ci->cluster = ci->name; - padd_ci->constr_abs_z = false; - padd_ci->constr_x = 0; - padd_ci->constr_y = 0; - padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTADDALU18X18_0_Z + i; - - IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); - - mult_ci->cluster = ci->name; - mult_ci->constr_abs_z = false; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTADDALU18X18_0_Z + i; - } - - // DSP head? This primitive has the ability to form chains using both SO[AB] -> SI[AB] and - // CASO->CASI - bool cas_head = false; - if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { - for (int i = 0; i < 55; ++i) { - ci->disconnectPort(ctx->idf("CASI[%d]", i)); - } - cas_head = true; - } - bool so_head = false; - if (gwu.dsp_bus_src(ci, "SIA", 18) == nullptr && gwu.dsp_bus_src(ci, "SIB", 18) == nullptr) { - for (int i = 0; i < 18; ++i) { - ci->disconnectPort(ctx->idf("SIA[%d]", i)); - ci->disconnectPort(ctx->idf("SIB[%d]", i)); - } - so_head = true; - } - if (cas_head && so_head) { - dsp_heads.push_back(ci); - if (ctx->verbose) { - log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); - } - } - } break; - case ID_MULT36X36: { - for (int i = 0; i < 36; ++i) { + for (int i = 0; i < 36; ++i) { + if (i < 18) { ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).clear(); ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d0", i)); ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d1", i)); - ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).clear(); - ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).push_back(ctx->idf("B%d0", i)); - ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).push_back(ctx->idf("B%d1", i)); } - // only MSB sign bits - ci->cell_bel_pins.at(id_ASIGN).clear(); - ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN0); - ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN1); - ci->cell_bel_pins.at(id_BSIGN).clear(); - ci->cell_bel_pins.at(id_BSIGN).push_back(id_BSIGN0); - ci->cell_bel_pins.at(id_BSIGN).push_back(id_BSIGN1); - - // LSB sign bits = 0 - NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); - ci->addInput(id_ZERO_SIGN); - ci->cell_bel_pins[id_ZERO_SIGN].push_back(id_ZERO_ASIGN0); - ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_BSIGN0); - ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_BSIGN1); - ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_ASIGN1); - ci->connectPort(id_ZERO_SIGN, vss_net); - - for (int i = 0; i < 72; ++i) { - ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); - } - - // add padd9s and mult9s as a children - ci->cluster = ci->name; - ci->constr_abs_z = false; - ci->constr_x = 0; - ci->constr_y = 0; - ci->constr_z = 0; - ci->constr_children.clear(); - - for (int i = 0; i < 8; ++i) { - IdString padd_name = gwu.create_aux_name(ci->name, i * 2); - std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); - new_cells.push_back(std::move(padd_cell)); - CellInfo *padd_ci = new_cells.back().get(); - - static int padd_z[] = {BelZ::PADD9_0_0_Z, BelZ::PADD9_0_2_Z, BelZ::PADD9_1_0_Z, BelZ::PADD9_1_2_Z}; - padd_ci->cluster = ci->name; - padd_ci->constr_abs_z = false; - padd_ci->constr_x = 0; - padd_ci->constr_y = 0; - padd_ci->constr_z = padd_z[i / 2] - BelZ::MULT36X36_Z + i % 2; - - IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); - std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); - new_cells.push_back(std::move(mult_cell)); - CellInfo *mult_ci = new_cells.back().get(); - - static int mult_z[] = {BelZ::MULT9X9_0_0_Z, BelZ::MULT9X9_0_2_Z, BelZ::MULT9X9_1_0_Z, - BelZ::MULT9X9_1_2_Z}; - mult_ci->cluster = ci->name; - mult_ci->constr_abs_z = false; - mult_ci->constr_x = 0; - mult_ci->constr_y = 0; - mult_ci->constr_z = mult_z[i / 2] - BelZ::MULT36X36_Z + i % 2; - } - } break; - default: - log_error("Unsupported DSP type '%s'\n", ci->type.c_str(ctx)); + ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d", i)); } + for (int i = 0; i < 54; ++i) { + switch (multalu36x18_mode) { + case 0: + ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i)); + break; + case 1: /* fallthrough */ + case 2: + ci->disconnectPort(ctx->idf("C[%d]", i)); + break; + default: + break; + } + } + + // both A have sign bit + // only MSB part of B has sign bit + ci->cell_bel_pins.at(id_ASIGN).clear(); + ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN0); + ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN1); + ci->renamePort(id_BSIGN, id_BSIGN1); + ci->addInput(id_BSIGN0); + ci->connectPort(id_BSIGN0, vss_net); + + pass_net_type(ci, id_ACCLOAD); + if (multalu36x18_mode == 1) { + if (ci->attrs.at(id_NET_ACCLOAD).as_string() == "GND" || + ci->attrs.at(id_NET_ACCLOAD).as_string() == "VCC") { + ci->disconnectPort(id_ACCLOAD); + } else { + ci->addInput(id_ALUSEL4); + ci->addInput(id_ALUSEL6); + ci->cell_bel_pins.at(id_ACCLOAD).clear(); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL4); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL6); + } + } else { + ci->disconnectPort(id_ACCLOAD); + } + + for (int i = 0; i < 54; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + + // add padd9s and mult9s as a children + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); + + for (int i = 0; i < 2; ++i) { + IdString padd_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); + new_cells.push_back(std::move(padd_cell)); + CellInfo *padd_ci = new_cells.back().get(); + + padd_ci->cluster = ci->name; + padd_ci->constr_abs_z = false; + padd_ci->constr_x = 0; + padd_ci->constr_y = 0; + padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTALU36X18_0_Z + i; + + IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); + + mult_ci->cluster = ci->name; + mult_ci->constr_abs_z = false; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTALU36X18_0_Z + i; + } + + // DSP head? + if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { + for (int i = 0; i < 55; ++i) { + ci->disconnectPort(ctx->idf("CASI[%d]", i)); + } + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); + } + } + } break; + case ID_MULTADDALU12X12: { + for (int i = 0; i < 2; ++i) { + ci->renamePort(ctx->idf("CLK[%d]", i), ctx->idf("CLK%d", i)); + ci->renamePort(ctx->idf("CE[%d]", i), ctx->idf("CE%d", i)); + ci->renamePort(ctx->idf("RESET[%d]", i), ctx->idf("RESET%d", i)); + ci->renamePort(ctx->idf("ADDSUB[%d]", i), ctx->idf("ADDSUB%d", i)); + } + for (int i = 0; i < 12; ++i) { + ci->renamePort(ctx->idf("A0[%d]", i), ctx->idf("A0%d", i)); + ci->renamePort(ctx->idf("B0[%d]", i), ctx->idf("B0%d", i)); + ci->renamePort(ctx->idf("A1[%d]", i), ctx->idf("A1%d", i)); + ci->renamePort(ctx->idf("B1[%d]", i), ctx->idf("B1%d", i)); + } + pass_net_type(ci, id_ACCSEL); + ci->cell_bel_pins.at(id_ACCSEL).clear(); + ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL0); + ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL1); + + for (int i = 0; i < 48; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + + // mark 2 mult12x12 as parts of the cluster to prevent + // other multipliers from being placed there + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); + + for (int i = 0; i < 2; ++i) { + IdString mult12x12_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr mult12x12_cell = gwu.create_cell(mult12x12_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult12x12_cell)); + CellInfo *mult12x12_ci = new_cells.back().get(); + + mult12x12_ci->cluster = ci->name; + mult12x12_ci->constr_abs_z = false; + mult12x12_ci->constr_x = 0; + mult12x12_ci->constr_y = 0; + mult12x12_ci->constr_z = BelZ::MULT12X12_0_Z - BelZ::MULTADDALU12X12_Z + i; + } + + // DSP head? + if (gwu.dsp_bus_src(ci, "CASI", 48) == nullptr) { + for (int i = 0; i < 48; ++i) { + ci->disconnectPort(ctx->idf("CASI[%d]", i)); + } + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); + } + } + + } break; + case ID_MULTADDALU18X18: { + if (ci->params.count(id_MULTADDALU18X18_MODE) == 0) { + ci->setParam(id_MULTADDALU18X18_MODE, 0); + } + int multaddalu18x18_mode = ci->params.at(id_MULTADDALU18X18_MODE).as_int64(); + if (multaddalu18x18_mode < 0 || multaddalu18x18_mode > 2) { + log_error("%s MULTADDALU18X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci)); + } + for (int i = 0; i < 54; ++i) { + if (i < 18) { + ci->renamePort(ctx->idf("A0[%d]", i), ctx->idf("A%d0", i)); + ci->renamePort(ctx->idf("B0[%d]", i), ctx->idf("B%d0", i)); + ci->renamePort(ctx->idf("A1[%d]", i), ctx->idf("A%d1", i)); + ci->renamePort(ctx->idf("B1[%d]", i), ctx->idf("B%d1", i)); + } + if (multaddalu18x18_mode == 0) { + ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i)); + } else { + ci->disconnectPort(ctx->idf("C[%d]", i)); + } + } + for (int i = 0; i < 2; ++i) { + ci->renamePort(ctx->idf("ASIGN[%d]", i), ctx->idf("ASIGN%d", i)); + ci->renamePort(ctx->idf("BSIGN[%d]", i), ctx->idf("BSIGN%d", i)); + ci->renamePort(ctx->idf("ASEL[%d]", i), ctx->idf("ASEL%d", i)); + ci->renamePort(ctx->idf("BSEL[%d]", i), ctx->idf("BSEL%d", i)); + } + + pass_net_type(ci, id_ASEL0); + pass_net_type(ci, id_ASEL1); + pass_net_type(ci, id_BSEL0); + pass_net_type(ci, id_BSEL1); + pass_net_type(ci, id_ACCLOAD); + if (multaddalu18x18_mode == 1) { + if (ci->attrs.at(id_NET_ACCLOAD).as_string() == "GND" || + ci->attrs.at(id_NET_ACCLOAD).as_string() == "VCC") { + ci->disconnectPort(id_ACCLOAD); + } else { + ci->addInput(id_ALUSEL4); + ci->addInput(id_ALUSEL6); + ci->cell_bel_pins.at(id_ACCLOAD).clear(); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL4); + ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL6); + } + } else { + ci->disconnectPort(id_ACCLOAD); + } + + for (int i = 0; i < 54; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + + // add padd9s and mult9s as a children + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); + + for (int i = 0; i < 2; ++i) { + IdString padd_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); + new_cells.push_back(std::move(padd_cell)); + CellInfo *padd_ci = new_cells.back().get(); + + padd_ci->cluster = ci->name; + padd_ci->constr_abs_z = false; + padd_ci->constr_x = 0; + padd_ci->constr_y = 0; + padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTADDALU18X18_0_Z + i; + + IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); + + mult_ci->cluster = ci->name; + mult_ci->constr_abs_z = false; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTADDALU18X18_0_Z + i; + } + + // DSP head? This primitive has the ability to form chains using both SO[AB] -> SI[AB] and + // CASO->CASI + bool cas_head = false; + if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) { + for (int i = 0; i < 55; ++i) { + ci->disconnectPort(ctx->idf("CASI[%d]", i)); + } + cas_head = true; + } + bool so_head = false; + if (gwu.dsp_bus_src(ci, "SIA", 18) == nullptr && gwu.dsp_bus_src(ci, "SIB", 18) == nullptr) { + for (int i = 0; i < 18; ++i) { + ci->disconnectPort(ctx->idf("SIA[%d]", i)); + ci->disconnectPort(ctx->idf("SIB[%d]", i)); + } + so_head = true; + } + if (cas_head && so_head) { + dsp_heads.push_back(ci); + if (ctx->verbose) { + log_info(" found a DSP head: %s\n", ctx->nameOf(ci)); + } + } + } break; + case ID_MULT36X36: { + for (int i = 0; i < 36; ++i) { + ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).clear(); + ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d0", i)); + ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d1", i)); + ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).clear(); + ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).push_back(ctx->idf("B%d0", i)); + ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).push_back(ctx->idf("B%d1", i)); + } + // only MSB sign bits + ci->cell_bel_pins.at(id_ASIGN).clear(); + ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN0); + ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN1); + ci->cell_bel_pins.at(id_BSIGN).clear(); + ci->cell_bel_pins.at(id_BSIGN).push_back(id_BSIGN0); + ci->cell_bel_pins.at(id_BSIGN).push_back(id_BSIGN1); + + // LSB sign bits = 0 + NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); + ci->addInput(id_ZERO_SIGN); + ci->cell_bel_pins[id_ZERO_SIGN].push_back(id_ZERO_ASIGN0); + ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_BSIGN0); + ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_BSIGN1); + ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_ASIGN1); + ci->connectPort(id_ZERO_SIGN, vss_net); + + for (int i = 0; i < 72; ++i) { + ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i)); + } + + // add padd9s and mult9s as a children + ci->cluster = ci->name; + ci->constr_abs_z = false; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_children.clear(); + + for (int i = 0; i < 8; ++i) { + IdString padd_name = gwu.create_aux_name(ci->name, i * 2); + std::unique_ptr padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL); + new_cells.push_back(std::move(padd_cell)); + CellInfo *padd_ci = new_cells.back().get(); + + static int padd_z[] = {BelZ::PADD9_0_0_Z, BelZ::PADD9_0_2_Z, BelZ::PADD9_1_0_Z, BelZ::PADD9_1_2_Z}; + padd_ci->cluster = ci->name; + padd_ci->constr_abs_z = false; + padd_ci->constr_x = 0; + padd_ci->constr_y = 0; + padd_ci->constr_z = padd_z[i / 2] - BelZ::MULT36X36_Z + i % 2; + + IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1); + std::unique_ptr mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL); + new_cells.push_back(std::move(mult_cell)); + CellInfo *mult_ci = new_cells.back().get(); + + static int mult_z[] = {BelZ::MULT9X9_0_0_Z, BelZ::MULT9X9_0_2_Z, BelZ::MULT9X9_1_0_Z, + BelZ::MULT9X9_1_2_Z}; + mult_ci->cluster = ci->name; + mult_ci->constr_abs_z = false; + mult_ci->constr_x = 0; + mult_ci->constr_y = 0; + mult_ci->constr_z = mult_z[i / 2] - BelZ::MULT36X36_Z + i % 2; + } + } break; + default: + log_error("Unsupported DSP type '%s'\n", ci->type.c_str(ctx)); } + }; + + for (auto &cell : ctx->cells) { + auto ci = cell.second.get(); + if (is_dsp(ci)) { + process_dsp_cell(ci); + } + } + + // Process new DSPs if any are generated + for (auto &cell : new_dsps) { + IdString name = cell->name; + ctx->cells[name] = std::move(cell); + auto ci = ctx->cells.at(name).get(); + process_dsp_cell(ci); + } + + for (auto cell : cells_to_remove) { + ctx->cells.erase(cell); } // add new cells