aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/design_utils.cc10
-rw-r--r--common/design_utils.h4
-rw-r--r--nexus/constids.inc10
-rw-r--r--nexus/pack.cc139
-rw-r--r--nexus/pins.cc19
5 files changed, 153 insertions, 29 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<IdString, DSPMacroType> 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);
-
- 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);
+ if (!dsp_types.count(ci->type))
+ continue;
+ auto &mt = dsp_types.at(ci->type);
+ int Nreg18 = mt.z_width / 18;
+
+ // Create consituent cells
+ std::vector<CellInfo *> 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<IdString, Arch::CellPinsData> 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