Gowin.DSP. Add MULTALU27X18. (#1650)

This primitive occupies one DSP block entirely and can be connected into
complex chains both by arguments (shifting operands from SOA to SIA) and
by results (CASO->CASI cascades).

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2026-02-26 21:25:10 +10:00 committed by GitHub
parent e953c250a4
commit 6235ba21e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 183 additions and 72 deletions

View File

@ -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);
}

View File

@ -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,
};
}

View File

@ -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

View File

@ -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.");

View File

@ -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<CellInfo> 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;
}
}