aboutsummaryrefslogtreecommitdiffstats
path: root/nexus/pack.cc
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2020-12-02 09:44:17 +0000
committerDavid Shah <dave@ds0.me>2020-12-02 09:44:17 +0000
commitb666c858246c50c231c04c2cf2ee069b22995c9a (patch)
tree02c8f081e3da6ea43b4d66df17c8d1d603d462c5 /nexus/pack.cc
parent5a291e4622b98b5648035a0bbc6b82f05ee63cdd (diff)
downloadnextpnr-b666c858246c50c231c04c2cf2ee069b22995c9a.tar.gz
nextpnr-b666c858246c50c231c04c2cf2ee069b22995c9a.tar.bz2
nextpnr-b666c858246c50c231c04c2cf2ee069b22995c9a.zip
nexus: Add support for deriving timing constraints in packer
Signed-off-by: David Shah <dave@ds0.me>
Diffstat (limited to 'nexus/pack.cc')
-rw-r--r--nexus/pack.cc96
1 files changed, 96 insertions, 0 deletions
diff --git a/nexus/pack.cc b/nexus/pack.cc
index ff5d1047..b4c1566b5 100644
--- a/nexus/pack.cc
+++ b/nexus/pack.cc
@@ -1709,6 +1709,101 @@ struct NexusPacker
}
}
+ void generate_constraints()
+ {
+ log_info("Generating derived timing constraints...\n");
+ auto MHz = [&](delay_t a) { return 1000.0 / ctx->getDelayNS(a); };
+
+ auto equals_epsilon = [](delay_t a, delay_t b) { return (std::abs(a - b) / std::max(double(b), 1.0)) < 1e-3; };
+
+ std::unordered_set<IdString> user_constrained, changed_nets;
+ for (auto &net : ctx->nets) {
+ if (net.second->clkconstr != nullptr)
+ user_constrained.insert(net.first);
+ changed_nets.insert(net.first);
+ }
+
+ auto set_period = [&](CellInfo *ci, IdString port, delay_t period) {
+ if (!ci->ports.count(port))
+ return;
+ NetInfo *to = ci->ports.at(port).net;
+ if (to == nullptr)
+ return;
+ if (to->clkconstr != nullptr) {
+ if (!equals_epsilon(to->clkconstr->period.min_delay, period) && user_constrained.count(to->name))
+ log_warning(
+ " Overriding derived constraint of %.1f MHz on net %s with user-specified constraint of "
+ "%.1f MHz.\n",
+ MHz(to->clkconstr->period.min_delay), to->name.c_str(ctx), MHz(period));
+ return;
+ }
+ to->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint());
+ to->clkconstr->low.min_delay = period / 2;
+ to->clkconstr->low.max_delay = period / 2;
+ to->clkconstr->high.min_delay = period / 2;
+ to->clkconstr->high.max_delay = period / 2;
+ to->clkconstr->period.min_delay = period;
+ to->clkconstr->period.max_delay = period;
+ log_info(" Derived frequency constraint of %.1f MHz for net %s\n", MHz(to->clkconstr->period.min_delay),
+ to->name.c_str(ctx));
+ changed_nets.insert(to->name);
+ };
+
+ auto copy_constraint = [&](CellInfo *ci, IdString fromPort, IdString toPort, double ratio = 1.0) {
+ if (!ci->ports.count(fromPort) || !ci->ports.count(toPort))
+ return;
+ NetInfo *from = ci->ports.at(fromPort).net, *to = ci->ports.at(toPort).net;
+ if (from == nullptr || from->clkconstr == nullptr || to == nullptr)
+ return;
+ if (to->clkconstr != nullptr) {
+ if (!equals_epsilon(to->clkconstr->period.min_delay,
+ delay_t(from->clkconstr->period.min_delay / ratio)) &&
+ user_constrained.count(to->name))
+ log_warning(
+ " Overriding derived constraint of %.1f MHz on net %s with user-specified constraint of "
+ "%.1f MHz.\n",
+ MHz(to->clkconstr->period.min_delay), to->name.c_str(ctx),
+ MHz(delay_t(from->clkconstr->period.min_delay / ratio)));
+ return;
+ }
+ to->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint());
+ to->clkconstr->low = ctx->getDelayFromNS(ctx->getDelayNS(from->clkconstr->low.min_delay) / ratio);
+ to->clkconstr->high = ctx->getDelayFromNS(ctx->getDelayNS(from->clkconstr->high.min_delay) / ratio);
+ to->clkconstr->period = ctx->getDelayFromNS(ctx->getDelayNS(from->clkconstr->period.min_delay) / ratio);
+ log_info(" Derived frequency constraint of %.1f MHz for net %s\n", MHz(to->clkconstr->period.min_delay),
+ to->name.c_str(ctx));
+ changed_nets.insert(to->name);
+ };
+
+ // Run in a loop while constraints are changing to deal with dependencies
+ // Iteration limit avoids hanging in crazy loopback situation (self-fed PLLs or dividers, etc)
+ int iter = 0;
+ const int itermax = 5000;
+ while (!changed_nets.empty() && iter < itermax) {
+ ++iter;
+ std::unordered_set<IdString> changed_cells;
+ for (auto net : changed_nets) {
+ for (auto &user : ctx->nets.at(net)->users)
+ if (user.port == id_CLKI)
+ changed_cells.insert(user.cell->name);
+ auto &drv = ctx->nets.at(net)->driver;
+ if (iter == 1 && drv.cell != nullptr && (drv.port == id_HFCLKOUT || drv.port == id_LFCLKOUT))
+ changed_cells.insert(drv.cell->name);
+ }
+ changed_nets.clear();
+ for (auto cell : sorted(changed_cells)) {
+ CellInfo *ci = ctx->cells.at(cell).get();
+ if (ci->type == id_DCC) {
+ copy_constraint(ci, id_CLKI, id_CLKO, 1);
+ } else if (ci->type == id_OSC_CORE) {
+ int div = int_or_default(ci->params, ctx->id("HF_CLK_DIV"), 128);
+ set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1)));
+ set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10)));
+ }
+ }
+ }
+ }
+
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
void operator()()
@@ -1725,6 +1820,7 @@ struct NexusPacker
pack_luts();
promote_globals();
place_globals();
+ generate_constraints();
}
};