From d8e748bc5864f7937cf087cf08d7497bff0d4f6d Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 16 Nov 2020 13:04:43 +0000 Subject: [PATCH] nexus: Refactor DSP macro splitting to make it more generic Signed-off-by: David Shah --- common/design_utils.cc | 10 +++ common/design_utils.h | 4 ++ nexus/constids.inc | 10 +++ nexus/pack.cc | 137 ++++++++++++++++++++++++++++++++--------- nexus/pins.cc | 19 ++++++ 5 files changed, 152 insertions(+), 28 deletions(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index 4d1c9e53..16cc2710 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -183,4 +183,14 @@ void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *ne connect_port(ctx, old_cell->ports.at(old_name).net, new_cell, new_name); } +void copy_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, + CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width) +{ + for (int i = 0; i < width; i++) { + IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); + IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + copy_port(ctx, old_cell, old_port, new_cell, new_port); + } +} + NEXTPNR_NAMESPACE_END diff --git a/common/design_utils.h b/common/design_utils.h index c93fe009..6f52eb0c 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -110,6 +110,10 @@ void print_utilisation(const Context *ctx); void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); +// Copy a bus of nets (if connected) from old, and connect it to the new ports +void copy_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, + CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); + // Copy a port from one cell to another void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *new_cell, IdString new_name); diff --git a/nexus/constids.inc b/nexus/constids.inc index 90416594..de3d27bc 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -312,3 +312,13 @@ X(CEOUT) X(REGINPUTA) X(REGINPUTB) X(REGOUTPUT) + +X(MULT18X18) +X(ROUNDBIT) +X(ROUNDHALFUP) +X(ROUNDRTZI) +X(SFTEN) + +X(MULT18X36) +X(MULT36X36H) +X(MULT36X36) diff --git a/nexus/pack.cc b/nexus/pack.cc index 0125378c..92035079 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1435,6 +1435,22 @@ struct NexusPacker cell->params[id_SHIFTA] = std::string("DISABLED"); cell->params[id_SIGNEDSTATIC_EN] = std::string("DISABLED"); cell->params[id_SR_18BITSHIFT_EN] = std::string("DISABLED"); + } else if (type == id_MULT18_CORE) { + cell->params[id_MULT18X18] = std::string("ENABLED"); + cell->params[id_ROUNDBIT] = std::string("ROUND_TO_BIT0"); + cell->params[id_ROUNDHALFUP] = std::string("DISABLED"); + cell->params[id_ROUNDRTZI] = std::string("ROUND_TO_ZERO"); + cell->params[id_SFTEN] = std::string("DISABLED"); + } else if (type == id_MULT18X36_CORE) { + cell->params[id_SFTEN] = std::string("DISABLED"); + cell->params[id_MULT18X36] = std::string("ENABLED"); + cell->params[id_MULT36] = std::string("DISABLED"); + cell->params[id_MULT36X36H] = std::string("USED_AS_LOWER_BIT_GENERATION"); + cell->params[id_ROUNDHALFUP] = std::string("DISABLED"); + cell->params[id_ROUNDRTZI] = std::string("ROUND_TO_ZERO"); + cell->params[id_ROUNDBIT] = std::string("ROUND_TO_BIT0"); + } else if (type == id_MULT36_CORE) { + cell->params[id_MULT36X36] = std::string("ENABLED"); } else if (type == id_REG18_CORE) { cell->params[id_GSR] = std::string("DISABLED"); cell->params[id_REGBYPS] = std::string("BYPASS"); @@ -1460,6 +1476,26 @@ struct NexusPacker dst->params[dst_name] = orig->params[orig_name]; } + struct DSPMacroType + { + int a_width; // width of 'A' input + int b_width; // width of 'B' input + int c_width; // width of 'C' input + int z_width; // width of 'Z' output + int N9x9; // number of 9x9 mult+preadds + int N18x18; // number of 18x18 mult + int N18x36; // number of 18x36 mult + bool has_preadd; // preadder is used + bool has_addsub; // post-multiply ALU addsub is used + }; + + const std::unordered_map dsp_types = { + {id_MULT9X9, {9, 9, 0, 18, 1, 0, 0, false, false}}, + {id_MULT18X18, {18, 18, 0, 36, 2, 1, 0, false, false}}, + {id_MULT18X36, {18, 36, 0, 54, 4, 2, 1, false, false}}, + {id_MULT36X36, {36, 36, 0, 72, 8, 4, 2, false, false}}, + }; + void pack_dsps() { log_info("Packing DSPs...\n"); @@ -1467,36 +1503,81 @@ struct NexusPacker for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; - if (ci->type == id_MULT9X9) { - // MULT9X9: PREADD9 -> MULT9 -> REG18 - CellInfo *preadd9_0 = create_dsp_cell(ci->name, id_PREADD9_CORE, nullptr, 0, 0); - CellInfo *mult9_0 = create_dsp_cell(ci->name, id_MULT9_CORE, preadd9_0, 0, 2); - CellInfo *reg18_0 = create_dsp_cell(ci->name, id_REG18_CORE, preadd9_0, 2, 0); - replace_bus(ctx, ci, id_B, 0, true, preadd9_0, id_B, 0, false, 9); - replace_bus(ctx, ci, id_A, 0, true, mult9_0, id_A, 0, false, 9); - replace_bus(ctx, ci, id_Z, 0, true, reg18_0, id_PP, 0, false, 18); - replace_port(ci, id_SIGNEDA, mult9_0, id_ASIGNED); - replace_port(ci, id_SIGNEDB, preadd9_0, id_BSIGNED); + if (!dsp_types.count(ci->type)) + continue; + auto &mt = dsp_types.at(ci->type); + int Nreg18 = mt.z_width / 18; - copy_port(ctx, ci, id_CLK, preadd9_0, id_CLK); - copy_port(ctx, ci, id_CLK, mult9_0, id_CLK); - copy_port(ctx, ci, id_CLK, reg18_0, id_CLK); - - replace_port(ci, id_CEA, mult9_0, id_CEA); - replace_port(ci, id_RSTA, mult9_0, id_RSTA); - replace_port(ci, id_CEB, preadd9_0, id_CEB); - replace_port(ci, id_RSTB, preadd9_0, id_RSTB); - replace_port(ci, id_CEOUT, reg18_0, id_CEP); - replace_port(ci, id_RSTOUT, reg18_0, id_RSTP); - - copy_param(ci, id_REGINPUTA, mult9_0, id_REGBYPSA1); - copy_param(ci, id_REGINPUTB, preadd9_0, id_REGBYPSBR0); - copy_param(ci, id_REGOUTPUT, reg18_0, id_REGBYPS); - - copy_global_dsp_params(ci, preadd9_0); - auto_cascade_group(preadd9_0); - to_remove.push_back(ci); + // Create consituent cells + std::vector preadd9(mt.N9x9), mult9(mt.N9x9), mult18(mt.N18x18), mult18x36(mt.N18x36), + reg18(Nreg18); + for (int i = 0; i < mt.N9x9; i++) { + preadd9[i] = create_dsp_cell(ci->name, id_PREADD9_CORE, preadd9[0], (i / 4) * 4 + (i / 2) % 2, (i % 2)); + mult9[i] = create_dsp_cell(ci->name, id_MULT9_CORE, preadd9[0], (i / 4) * 4 + (i / 2) % 2, (i % 2) + 2); } + for (int i = 0; i < mt.N18x18; i++) + mult18[i] = create_dsp_cell(ci->name, id_MULT18_CORE, preadd9[0], (i / 2) * 4 + i % 2, 4); + for (int i = 0; i < mt.N18x36; i++) + mult18x36[i] = create_dsp_cell(ci->name, id_MULT18X36_CORE, preadd9[0], (i * 4) + 2, 4); + for (int i = 0; i < Nreg18; i++) { + reg18[i] = create_dsp_cell(ci->name, id_REG18_CORE, preadd9[0], (i / 4) * 4 + 2, i % 4); + } + + // Configure the 9x9 preadd+multiply blocks + for (int i = 0; i < mt.N9x9; i++) { + // B input split across pre-adders + int b_start = (9 * i) % mt.b_width; + copy_bus(ctx, ci, id_B, b_start, true, preadd9[i], id_B, 0, false, 9); + // A input split across MULT9s + int a_start = 9 * (i % 2) + 18 * (i / 4); + copy_bus(ctx, ci, id_A, a_start, true, mult9[i], id_A, 0, false, 9); + // Connect control set signals + copy_port(ctx, ci, id_CLK, mult9[i], id_CLK); + copy_port(ctx, ci, id_CEA, mult9[i], id_CEA); + copy_port(ctx, ci, id_RSTA, mult9[i], id_RSTA); + copy_port(ctx, ci, id_CLK, preadd9[i], id_CLK); + copy_port(ctx, ci, id_CEB, preadd9[i], id_CEB); + copy_port(ctx, ci, id_RSTB, preadd9[i], id_RSTB); + // Copy register configuration + copy_param(ci, id_REGINPUTA, mult9[i], id_REGBYPSA1); + copy_param(ci, id_REGINPUTB, preadd9[i], id_REGBYPSBR0); + + // Connect up signedness for the most significant nonet + if ((b_start + 9) == mt.b_width) + copy_port(ctx, ci, id_BSIGNED, preadd9[i], id_SIGNEDB); + if ((a_start + 9) == mt.a_width) + copy_port(ctx, ci, id_ASIGNED, mult9[i], id_SIGNEDA); + } + + bool mult36_used = (mt.a_width >= 36) && (mt.b_width >= 36); + // Configure mult18x36s + for (int i = 0; i < mt.N18x36; i++) { + mult18x36[i]->params[id_MULT36] = mult36_used ? std::string("ENABLED") : std::string("DISABLED"); + mult18x36[i]->params[id_MULT36X36H] = (i == 1) ? std::string("USED_AS_HIGHER_BIT_GENERATION") + : std::string("USED_AS_LOWER_BIT_GENERATION"); + } + // Create final mult36 if needed + CellInfo *mult36 = nullptr; + if (mult36_used) { + mult36 = create_dsp_cell(ci->name, id_MULT36_CORE, preadd9[0], 6, 6); + } + + // Configure output registers + for (int i = 0; i < Nreg18; i++) { + // Output split across reg18s + replace_bus(ctx, ci, id_Z, i * 18, true, reg18[i], id_PP, 0, false, 18); + // Connect control set signals + copy_port(ctx, ci, id_CLK, reg18[i], id_CLK); + copy_port(ctx, ci, id_CEOUT, reg18[i], id_CEP); + copy_port(ctx, ci, id_RSTOUT, reg18[i], id_RSTP); + // Copy register configuration + copy_param(ci, id_REGOUTPUT, reg18[i], id_REGBYPS); + } + + // Misc finalisation + copy_global_dsp_params(ci, preadd9[0]); + auto_cascade_group(preadd9[0]); + to_remove.push_back(ci); } for (auto cell : to_remove) { diff --git a/nexus/pins.cc b/nexus/pins.cc index 134565ad..0cf5c3e2 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -124,6 +124,25 @@ static const std::unordered_map base_cell_pin_data {id_CLK, PINSTYLE_CLK}, {id_RSTP, PINSTYLE_LSR}, {id_CEP, PINSTYLE_CE}, + {{}, PINSTYLE_DEDI}, + }}, + {id_MULT18_CORE, + { + {id_SFTCTRL0, PINSTYLE_CIB}, + {id_SFTCTRL1, PINSTYLE_CIB}, + {id_SFTCTRL2, PINSTYLE_CIB}, + {id_SFTCTRL3, PINSTYLE_CIB}, + {id_ROUNDEN, PINSTYLE_CIB}, + {{}, PINSTYLE_DEDI}, + }}, + {id_MULT18X36_CORE, + { + {id_SFTCTRL0, PINSTYLE_CIB}, + {id_SFTCTRL1, PINSTYLE_CIB}, + {id_SFTCTRL2, PINSTYLE_CIB}, + {id_SFTCTRL3, PINSTYLE_CIB}, + {id_ROUNDEN, PINSTYLE_CIB}, + {{}, PINSTYLE_DEDI}, }}}; } // namespace