From d97f93ee8864f8cba14f62bd49dd2baf51b7b21c Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 19 Nov 2021 09:15:59 +0100 Subject: [PATCH 1/5] Added clustering free LUTs and FFs Signed-off-by: Maciej Kurc --- nexus/pack.cc | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/nexus/pack.cc b/nexus/pack.cc index 281a7cf2..ba6033f9 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2304,6 +2304,78 @@ struct NexusPacker } } + void pack_lutffs () { + log_info("Inferring LUT+FF pairs...\n"); + + size_t num_comb = 0; + size_t num_ff = 0; + size_t num_pair = 0; + + for (auto &cell : ctx->cells) { + CellInfo *ff = cell.second.get(); + if (ff->type != id_OXIDE_FF) { + continue; + } + + num_ff++; + + // Get input net + NetInfo *di = get_net_or_empty(ff, id_M); // At the packing stage all inputs go to M + if (di == nullptr || di->driver.cell == nullptr) { + continue; + } + + // Skip if there are multiple sinks + if (di->users.size() != 1) { + continue; + } + + // Check if the driver is a LUT and the direct connection is from F + CellInfo* lut = di->driver.cell; + if (lut->type != id_OXIDE_COMB || di->driver.port != id_F) { + continue; + } + + // The LUT must be in LOGIC mode + if (str_or_default(lut->params, id_MODE, "LOGIC") != "LOGIC") { + continue; + } + + // Skip clusters + // FIXME: In case of carry chain make the LUTFF part of the chain + if (lut->cluster != ClusterId() || ff->cluster != ClusterId()) { + continue; + } + + // Make a cluster + lut->cluster = lut->name; + lut->constr_children.push_back(ff); + + ff->cluster = lut->name; + ff->constr_x = 0; + ff->constr_y = 0; + ff->constr_z = 2; + ff->constr_abs_z = false; + + num_pair++; + } + + // Count OXIDE_COMB, OXIDE_FF are already counted + for (auto &cell : ctx->cells) { + CellInfo *ff = cell.second.get(); + if (ff->type == id_OXIDE_COMB) { + num_comb++; + } + } + + // Print statistics + log_info(" Created %zu LUT+FF pairs from %zu FFs and %zu LUTs\n", + num_pair, + num_ff, + num_comb + ); + } + explicit NexusPacker(Context *ctx) : ctx(ctx) {} void operator()() @@ -2323,6 +2395,7 @@ struct NexusPacker pack_luts(); pack_ip(); handle_iologic(); + pack_lutffs(); promote_globals(); place_globals(); generate_constraints(); From 086bcf06153ea820b27f84b70b36fba77a975404 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 19 Nov 2021 09:28:19 +0100 Subject: [PATCH 2/5] Added an option to control LUT and FF packing Signed-off-by: Maciej Kurc --- nexus/main.cc | 3 +++ nexus/pack.cc | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/nexus/main.cc b/nexus/main.cc index 6ffc9a20..83bb02d8 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -51,6 +51,7 @@ po::options_description NexusCommandHandler::getArchOptions() specific.add_options()("fasm", po::value(), "fasm file to write"); specific.add_options()("pdc", po::value(), "physical constraints file"); specific.add_options()("no-post-place-opt", "disable post-place repacking (debugging use only)"); + specific.add_options()("no-pack-lutff", "disable packing (clustering) LUTs and FFs together"); return specific; } @@ -76,6 +77,8 @@ std::unique_ptr NexusCommandHandler::createContext(dict(new Context(chipArgs)); if (vm.count("no-post-place-opt")) ctx->settings[ctx->id("no_post_place_opt")] = Property::State::S1; + if (vm.count("no-pack-lutff")) + ctx->settings[ctx->id("no_pack_lutff")] = Property::State::S1; return ctx; } diff --git a/nexus/pack.cc b/nexus/pack.cc index ba6033f9..a3dc7266 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2395,7 +2395,11 @@ struct NexusPacker pack_luts(); pack_ip(); handle_iologic(); - pack_lutffs(); + + if (!bool_or_default(ctx->settings, ctx->id("no_pack_lutff"))) { + pack_lutffs(); + } + promote_globals(); place_globals(); generate_constraints(); From 5bc97c94ae91af045eb522ae770576c478275d7f Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 19 Nov 2021 12:09:55 +0100 Subject: [PATCH 3/5] Added appending FFs to other existing LUT cluster types (carry, widefn) Signed-off-by: Maciej Kurc --- nexus/pack.cc | 85 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index a3dc7266..be298ef0 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2310,6 +2310,7 @@ struct NexusPacker size_t num_comb = 0; size_t num_ff = 0; size_t num_pair = 0; + size_t num_glue = 0; for (auto &cell : ctx->cells) { CellInfo *ff = cell.second.get(); @@ -2320,44 +2321,91 @@ struct NexusPacker num_ff++; // Get input net - NetInfo *di = get_net_or_empty(ff, id_M); // At the packing stage all inputs go to M + // At the packing stage all inputs go to M + NetInfo *di = get_net_or_empty(ff, id_M); if (di == nullptr || di->driver.cell == nullptr) { continue; } - // Skip if there are multiple sinks + // Skip if there are multiple sinks on that net if (di->users.size() != 1) { continue; } // Check if the driver is a LUT and the direct connection is from F CellInfo* lut = di->driver.cell; - if (lut->type != id_OXIDE_COMB || di->driver.port != id_F) { + if (lut->type != id_OXIDE_COMB) { + continue; + } + if (di->driver.port != id_F && + di->driver.port != id_F1 && + di->driver.port != id_OFX) + { continue; } - // The LUT must be in LOGIC mode - if (str_or_default(lut->params, id_MODE, "LOGIC") != "LOGIC") { + // The FF must not use M and DI at the same time + if (get_net_or_empty(ff, id_DI)) { continue; } - // Skip clusters - // FIXME: In case of carry chain make the LUTFF part of the chain - if (lut->cluster != ClusterId() || ff->cluster != ClusterId()) { + // The LUT must be in LOGIC/CARRY mode + if (str_or_default(lut->params, id_MODE, "LOGIC") != "LOGIC" && + str_or_default(lut->params, id_MODE, "LOGIC") != "CCU2") { continue; } - // Make a cluster - lut->cluster = lut->name; - lut->constr_children.push_back(ff); + // The FF cannot be in another cluster + if (ff->cluster != ClusterId()) { + continue; + } - ff->cluster = lut->name; - ff->constr_x = 0; - ff->constr_y = 0; - ff->constr_z = 2; - ff->constr_abs_z = false; + // A free LUT, create a new cluster + if (lut->cluster == ClusterId()) { - num_pair++; + lut->cluster = lut->name; + lut->constr_children.push_back(ff); + + ff->cluster = lut->name; + ff->constr_x = 0; + ff->constr_y = 0; + ff->constr_z = 2; + ff->constr_abs_z = false; + + num_pair++; + } + // Attach the FF to the existing cluster of the LUT + else { + + // Find the cluster root + CellInfo* root = nullptr; + if (!lut->constr_children.empty()) { + root = lut; + } + else { + for (auto &it : ctx->cells) { + if (it.second->cluster == lut->cluster && !it.second->constr_children.empty()) { + root = it.second.get(); + break; + } + } + } + NPNR_ASSERT(root != nullptr); + + // Constrain the FF relative to the LUT + ff->cluster = root->cluster; + ff->constr_x = lut->constr_x; + ff->constr_y = lut->constr_y; + ff->constr_z = lut->constr_z + 2; + ff->constr_abs_z = false; + root->constr_children.push_back(ff); + + num_glue++; + } + + // Reconnect M to DI + rename_port(ctx, ff, id_M, id_DI); + ff->params[id_SEL] = std::string("DL"); } // Count OXIDE_COMB, OXIDE_FF are already counted @@ -2369,8 +2417,9 @@ struct NexusPacker } // Print statistics - log_info(" Created %zu LUT+FF pairs from %zu FFs and %zu LUTs\n", + log_info(" Created %zu LUT+FF pairs and extended %zu clusters using total %zu FFs and %zu LUTs\n", num_pair, + num_glue, num_ff, num_comb ); From 238da79e524ba47e28aac7b30df692f55be3f2d2 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Mon, 22 Nov 2021 13:13:28 +0100 Subject: [PATCH 4/5] Fixed potential issues with carry-chain cluster expansion, added a parameter controlling the ratio of FFs that got glued to carry-chain clusters. Signed-off-by: Maciej Kurc --- nexus/main.cc | 8 ++++++++ nexus/pack.cc | 29 +++++++++++++++-------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/nexus/main.cc b/nexus/main.cc index 83bb02d8..667b6d80 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -52,6 +52,7 @@ po::options_description NexusCommandHandler::getArchOptions() specific.add_options()("pdc", po::value(), "physical constraints file"); specific.add_options()("no-post-place-opt", "disable post-place repacking (debugging use only)"); specific.add_options()("no-pack-lutff", "disable packing (clustering) LUTs and FFs together"); + specific.add_options()("carry-lutff-ratio", po::value(), "ratio of FFs to be added to carry-chain LUT clusters"); return specific; } @@ -79,6 +80,13 @@ std::unique_ptr NexusCommandHandler::createContext(dictsettings[ctx->id("no_post_place_opt")] = Property::State::S1; if (vm.count("no-pack-lutff")) ctx->settings[ctx->id("no_pack_lutff")] = Property::State::S1; + if (vm.count("carry-lutff-ratio")) { + float ratio = vm["carry-lutff-ratio"].as(); + if (ratio < 0.0f || ratio > 1.0f) { + log_error("Carry LUT+FF packing ration must be between 0.0 and 1.0"); + } + ctx->settings[ctx->id("carry_lutff_ratio")] = ratio; + } return ctx; } diff --git a/nexus/pack.cc b/nexus/pack.cc index be298ef0..8200f845 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2307,6 +2307,11 @@ struct NexusPacker void pack_lutffs () { log_info("Inferring LUT+FF pairs...\n"); + float carry_ratio = 1.0f; + if (ctx->settings.find(ctx->id("carry_lutff_ratio")) != ctx->settings.end()) { + carry_ratio = ctx->setting("carry_lutff_ratio"); + } + size_t num_comb = 0; size_t num_ff = 0; size_t num_pair = 0; @@ -2338,7 +2343,6 @@ struct NexusPacker continue; } if (di->driver.port != id_F && - di->driver.port != id_F1 && di->driver.port != id_OFX) { continue; @@ -2377,27 +2381,24 @@ struct NexusPacker // Attach the FF to the existing cluster of the LUT else { - // Find the cluster root - CellInfo* root = nullptr; - if (!lut->constr_children.empty()) { - root = lut; - } - else { - for (auto &it : ctx->cells) { - if (it.second->cluster == lut->cluster && !it.second->constr_children.empty()) { - root = it.second.get(); - break; - } + // No order not to make too large carry clusters pack only the + // given fraction of FFs there. + if(str_or_default(lut->params, id_MODE, "LOGIC") == "CCU2") { + float r = (float)(ctx->rng() % 1000) * 1e-3f; + if (r > carry_ratio) { + continue; } } - NPNR_ASSERT(root != nullptr); + + // Get the cluster root + CellInfo* root = ctx->cells.at(lut->cluster).get(); // Constrain the FF relative to the LUT ff->cluster = root->cluster; ff->constr_x = lut->constr_x; ff->constr_y = lut->constr_y; ff->constr_z = lut->constr_z + 2; - ff->constr_abs_z = false; + ff->constr_abs_z = lut->constr_abs_z; root->constr_children.push_back(ff); num_glue++; From 41accf84ceb3ca3f516d78e8c706cf18b035319b Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 23 Nov 2021 15:16:26 +0100 Subject: [PATCH 5/5] Added checking if all FFs added to an existing cluster have matching configuration Signed-off-by: Maciej Kurc --- nexus/pack.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/nexus/pack.cc b/nexus/pack.cc index 8200f845..0c771e82 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2304,6 +2304,23 @@ struct NexusPacker } } + FFControlSet gather_ff_settings(CellInfo* cell) { + NPNR_ASSERT(cell->type == id_OXIDE_FF); + + FFControlSet ctrlset; + ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC"; + ctrlset.regddr_en = is_enabled(cell, id_REGDDR); + ctrlset.gsr_en = is_enabled(cell, id_GSR); + ctrlset.clkmux = ctx->id(str_or_default(cell->params, id_CLKMUX, "CLK")).index; + ctrlset.cemux = ctx->id(str_or_default(cell->params, id_CEMUX, "CE")).index; + ctrlset.lsrmux = ctx->id(str_or_default(cell->params, id_LSRMUX, "LSR")).index; + ctrlset.clk = get_net_or_empty(cell, id_CLK); + ctrlset.ce = get_net_or_empty(cell, id_CE); + ctrlset.lsr = get_net_or_empty(cell, id_LSR); + + return ctrlset; + } + void pack_lutffs () { log_info("Inferring LUT+FF pairs...\n"); @@ -2312,6 +2329,12 @@ struct NexusPacker carry_ratio = ctx->setting("carry_lutff_ratio"); } + // FF control settings/signals are slice-wide. The dict below is used + // to track settings of FFs glued to clusters which may span more than + // one slice (eg. carry-chains). For now it is assumed that all FFs + // in one cluster share the same settings and control signals. + dict cluster_ffinfo; + size_t num_comb = 0; size_t num_ff = 0; size_t num_pair = 0; @@ -2364,6 +2387,9 @@ struct NexusPacker continue; } + // Get FF settings + auto ffinfo = gather_ff_settings(ff); + // A free LUT, create a new cluster if (lut->cluster == ClusterId()) { @@ -2381,6 +2407,19 @@ struct NexusPacker // Attach the FF to the existing cluster of the LUT else { + // Check if the FF settings match those of others in this + // cluster. If not then reject this FF. + // + // This is a greedy approach - the first attached FF will + // enforce its settings on all following candidates. A better + // approach would be to first form groups of matching FFs for + // a cluster and then attach only the largest group to it. + if (cluster_ffinfo.count(lut->cluster)) { + if (ffinfo != cluster_ffinfo.at(lut->cluster)) { + continue; + } + } + // No order not to make too large carry clusters pack only the // given fraction of FFs there. if(str_or_default(lut->params, id_MODE, "LOGIC") == "CCU2") { @@ -2407,6 +2446,11 @@ struct NexusPacker // Reconnect M to DI rename_port(ctx, ff, id_M, id_DI); ff->params[id_SEL] = std::string("DL"); + + // Store FF settings of the cluster + if (!cluster_ffinfo.count(lut->cluster)) { + cluster_ffinfo.emplace(lut->cluster, ffinfo); + } } // Count OXIDE_COMB, OXIDE_FF are already counted