diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc index b5417c53..0904127c 100644 --- a/himbaechel/uarch/gowin/gowin.cc +++ b/himbaechel/uarch/gowin/gowin.cc @@ -1233,7 +1233,7 @@ bool GowinImpl::getClusterPlacement(ClusterId cluster, BelId root_bel, { CellInfo *root_ci = getClusterRootCell(cluster); if (!root_ci->type.in(id_PADD9, id_MULT9X9, id_PADD18, id_MULT18X18, id_MULTALU18X18, id_MULTALU36X18, - id_MULTADDALU18X18, id_ALU54D, id_MULTADDALU12X12)) { + id_MULTADDALU18X18, id_ALU54D, id_MULTADDALU12X12, id_MULTALU27X18)) { return HimbaechelAPI::getClusterPlacement(cluster, root_bel, placement); } diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 99db5d6b..305cc3be 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -73,7 +73,7 @@ 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_MULTALU36X18, id_MULTADDALU18X18, id_MULT12X12, id_MULTADDALU12X12, id_MULTALU27X18); } inline bool is_dsp(const CellInfo *cell) { return type_is_dsp(cell->type); } @@ -335,6 +335,7 @@ enum MULT12X12_0_Z = 640, MULT12X12_1_Z = 641, MULTADDALU12X12_Z = 642, + MULTALU27X18_Z = 643, }; } diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 7c53912b..e7cd26ff 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -140,6 +140,7 @@ CLKDIV_3_Z = 623 MULT12X12_0_Z = 640 MULT12X12_1_Z = 641 MULTADDALU12X12_Z = 642 +MULTALU27X18_Z = 643 # ======================================= # Chipdb additional info @@ -1196,6 +1197,28 @@ def create_dsp_5a_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, td for outp in range(24): add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT) + # create MultAlu27x18 + belname = f'MULTALU27X1800' + portmap = db[y, x].bels[belname].portmap + dsp = tt.create_bel(belname, "MULTALU27X18", MULTALU27X18_Z) + + for sfx, qnt in {('A', 27) , ('B', 18), ('C', 48), ('D', 26)}: + for inp in range(qnt): + add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT) + for inp in range(2): + add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, f"ADDSUB{inp}", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, f"ACCSEL{inp}", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, "CASISEL", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, "ASEL", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, "CSEL", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, "PSEL", "DSP_I", PinType.INPUT) + add_port_wire(tt, dsp, portmap, "PADDSUB", "DSP_I", PinType.INPUT) + for outp in range(48): + add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT) + # create MultAddAlu12x12 belname = f'MULTADDALU12X1200' portmap = db[y, x].bels[belname].portmap diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index 159c4f05..b95d1c28 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -523,7 +523,7 @@ Loc GowinUtils::get_dsp_next_in_chain(Loc from, IdString dsp_type) const if (dsp_type.in(id_ALU54D, id_MULTALU18X18, id_MULTALU36X18, id_MULTADDALU18X18)) { return get_dsp_next_macro_in_chain(from); } - if (dsp_type.in(id_MULTADDALU12X12)) { + if (dsp_type.in(id_MULTADDALU12X12, id_MULTALU27X18)) { return get_dsp_next_in_chain_5a(from); } NPNR_ASSERT_FALSE("Unknown DSP cell type."); diff --git a/himbaechel/uarch/gowin/pack_dsp.cc b/himbaechel/uarch/gowin/pack_dsp.cc index 8106d3f6..abe1e6fc 100644 --- a/himbaechel/uarch/gowin/pack_dsp.cc +++ b/himbaechel/uarch/gowin/pack_dsp.cc @@ -452,6 +452,79 @@ void GowinPacker::pack_dsp(void) } } } 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.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); @@ -803,6 +876,7 @@ void GowinPacker::pack_dsp(void) } } + // make CASO->CASI only chain from DSPs auto make_CAS_chain = [&](CellInfo *head, int wire_num) { CellInfo *cur_dsp = head; while (1) { @@ -837,6 +911,83 @@ void GowinPacker::pack_dsp(void) } }; + // make combined CASO->CASI and SOA->SIA chain + auto make_SIA_CAS_chain = [&](CellInfo *head, int cas_wire_num, int sia_wire_num, bool use_sib = true) { + CellInfo *cur_dsp = head; + while (1) { + bool end_of_cas_chain = false; + int wire_num = cas_wire_num; + CellInfo *next_dsp_a = gwu.dsp_bus_dst(cur_dsp, "CASO", wire_num); + if (next_dsp_a == nullptr) { + // End of CASO chain + for (int i = 0; i < wire_num; ++i) { + cur_dsp->disconnectPort(ctx->idf("CASO[%d]", i)); + } + end_of_cas_chain = true; + } else { + for (int i = 0; i < wire_num; ++i) { + cur_dsp->disconnectPort(ctx->idf("CASO[%d]", i)); + next_dsp_a->disconnectPort(ctx->idf("CASI[%d]", i)); + } + } + + bool end_of_so_chain = false; + wire_num = sia_wire_num; + CellInfo *next_so_dsp_a = gwu.dsp_bus_dst(cur_dsp, "SOA", wire_num); + CellInfo *next_so_dsp_b = use_sib ? gwu.dsp_bus_dst(cur_dsp, "SOB", wire_num) : nullptr; + if (next_so_dsp_a != nullptr && next_so_dsp_b != nullptr && next_so_dsp_a != next_so_dsp_b) { + log_error("%s is the source for two different DSPs (%s and %s) in the chain.", ctx->nameOf(cur_dsp), + ctx->nameOf(next_so_dsp_a), ctx->nameOf(next_so_dsp_b)); + } + if (next_so_dsp_a == nullptr && (!use_sib || (use_sib && next_so_dsp_b == nullptr))) { + // End of SO chain + for (int i = 0; i < wire_num; ++i) { + cur_dsp->disconnectPort(ctx->idf("SOA[%d]", i)); + if (use_sib) { + cur_dsp->disconnectPort(ctx->idf("SOB[%d]", i)); + } + } + end_of_so_chain = true; + } else { + next_so_dsp_a = next_so_dsp_a != nullptr ? next_so_dsp_a : next_so_dsp_b; + for (int i = 0; i < wire_num; ++i) { + cur_dsp->disconnectPort(ctx->idf("SOA[%d]", i)); + next_so_dsp_a->disconnectPort(ctx->idf("SIA[%d]", i)); + if (use_sib) { + cur_dsp->disconnectPort(ctx->idf("SOB[%d]", i)); + next_so_dsp_a->disconnectPort(ctx->idf("SIB[%d]", i)); + } + } + } + if (end_of_cas_chain && end_of_so_chain) { + break; + } + + // to the next + if (!end_of_cas_chain) { + cur_dsp->setAttr(id_USE_CASCADE_OUT, 1); + } + cur_dsp = next_dsp_a != nullptr ? next_dsp_a : next_so_dsp_a; + if (!end_of_cas_chain) { + cur_dsp->setAttr(id_USE_CASCADE_IN, 1); + } + if (ctx->verbose) { + log_info(" add %s to the chain. End of the SO chain:%d, end of the CAS chain:%d\n", + ctx->nameOf(cur_dsp), end_of_so_chain, end_of_cas_chain); + } + if (head->cluster == ClusterId()) { + head->cluster = head->name; + } + cur_dsp->cluster = head->name; + head->constr_children.push_back(cur_dsp); + for (auto child : cur_dsp->constr_children) { + child->cluster = head->name; + head->constr_children.push_back(child); + } + cur_dsp->constr_children.clear(); + } + }; + // DSP chains for (CellInfo *head : dsp_heads) { if (ctx->verbose) { @@ -946,77 +1097,13 @@ void GowinPacker::pack_dsp(void) case ID_MULTADDALU12X12: { make_CAS_chain(head, 48); } break; + case ID_MULTALU27X18: { + // This primitive has the ability to form chains using both SO[AB] -> SI[AB] and CASO->CASI + make_SIA_CAS_chain(head, 48, 27, false); + } break; case ID_MULTADDALU18X18: { // This primitive has the ability to form chains using both SO[AB] -> SI[AB] and CASO->CASI - CellInfo *cur_dsp = head; - while (1) { - bool end_of_cas_chain = false; - int wire_num = 55; - CellInfo *next_dsp_a = gwu.dsp_bus_dst(cur_dsp, "CASO", wire_num); - if (next_dsp_a == nullptr) { - // End of CASO chain - for (int i = 0; i < wire_num; ++i) { - cur_dsp->disconnectPort(ctx->idf("CASO[%d]", i)); - } - end_of_cas_chain = true; - } else { - for (int i = 0; i < wire_num; ++i) { - cur_dsp->disconnectPort(ctx->idf("CASO[%d]", i)); - next_dsp_a->disconnectPort(ctx->idf("CASI[%d]", i)); - } - } - - bool end_of_so_chain = false; - wire_num = 18; - CellInfo *next_so_dsp_a = gwu.dsp_bus_dst(cur_dsp, "SOA", wire_num); - CellInfo *next_so_dsp_b = gwu.dsp_bus_dst(cur_dsp, "SOB", wire_num); - if (next_so_dsp_a != nullptr && next_so_dsp_b != nullptr && next_so_dsp_a != next_so_dsp_b) { - log_error("%s is the source for two different DSPs (%s and %s) in the chain.", ctx->nameOf(cur_dsp), - ctx->nameOf(next_so_dsp_a), ctx->nameOf(next_so_dsp_b)); - } - if (next_so_dsp_a == nullptr && next_so_dsp_b == nullptr) { - // End of SO chain - for (int i = 0; i < wire_num; ++i) { - cur_dsp->disconnectPort(ctx->idf("SOA[%d]", i)); - cur_dsp->disconnectPort(ctx->idf("SOB[%d]", i)); - } - end_of_so_chain = true; - } else { - next_so_dsp_a = next_so_dsp_a != nullptr ? next_so_dsp_a : next_so_dsp_b; - for (int i = 0; i < wire_num; ++i) { - cur_dsp->disconnectPort(ctx->idf("SOA[%d]", i)); - cur_dsp->disconnectPort(ctx->idf("SOB[%d]", i)); - next_so_dsp_a->disconnectPort(ctx->idf("SIA[%d]", i)); - next_so_dsp_a->disconnectPort(ctx->idf("SIB[%d]", i)); - } - } - if (end_of_cas_chain && end_of_so_chain) { - break; - } - - // to next - if (!end_of_cas_chain) { - cur_dsp->setAttr(id_USE_CASCADE_OUT, 1); - } - cur_dsp = next_dsp_a != nullptr ? next_dsp_a : next_so_dsp_a; - if (!end_of_cas_chain) { - cur_dsp->setAttr(id_USE_CASCADE_IN, 1); - } - if (ctx->verbose) { - log_info(" add %s to the chain. End of the SO chain:%d, end of the CAS chain:%d\n", - ctx->nameOf(cur_dsp), end_of_so_chain, end_of_cas_chain); - } - if (head->cluster == ClusterId()) { - head->cluster = head->name; - } - cur_dsp->cluster = head->name; - head->constr_children.push_back(cur_dsp); - for (auto child : cur_dsp->constr_children) { - child->cluster = head->name; - head->constr_children.push_back(child); - } - cur_dsp->constr_children.clear(); - } + make_SIA_CAS_chain(head, 55, 18); } break; } }