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