aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2018-11-02 18:59:04 +0000
committerDavid Shah <dave@ds0.me>2018-11-12 14:03:58 +0000
commitcba9b528e8427e84bf1f6c6b8c34dc2bbe2d6bdf (patch)
tree4e4344c2205a4c9df5e2bb4fe5fb6cc9ae09b7bb /common
parent143abc603482b2429d481d445333ebfab698498a (diff)
downloadnextpnr-cba9b528e8427e84bf1f6c6b8c34dc2bbe2d6bdf.tar.gz
nextpnr-cba9b528e8427e84bf1f6c6b8c34dc2bbe2d6bdf.tar.bz2
nextpnr-cba9b528e8427e84bf1f6c6b8c34dc2bbe2d6bdf.zip
timing: Improve Fmax output and print cross-clock paths
Signed-off-by: David Shah <dave@ds0.me>
Diffstat (limited to 'common')
-rw-r--r--common/router1.cc2
-rw-r--r--common/timing.cc182
-rw-r--r--common/timing.h2
3 files changed, 132 insertions, 54 deletions
diff --git a/common/router1.cc b/common/router1.cc
index c4708de7..08c9d701 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -951,7 +951,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
#ifndef NDEBUG
ctx->check();
#endif
- timing_analysis(ctx, true /* slack_histogram */, true /* print_path */);
+ timing_analysis(ctx, true /* slack_histogram */, true /* print_fmax */, true /* print_path */);
ctx->unlock();
return true;
} catch (log_execution_error_exception) {
diff --git a/common/timing.cc b/common/timing.cc
index 3969a2ac..73e48871 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -406,7 +406,6 @@ struct Timing
crit_ipin = &port.second;
}
}
-
}
if (!crit_ipin)
@@ -483,17 +482,28 @@ void assign_budget(Context *ctx, bool quiet)
log_info("Checksum: 0x%08x\n", ctx->checksum());
}
-void timing_analysis(Context *ctx, bool print_histogram, bool print_path)
+void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path)
{
+ auto format_event = [ctx](const ClockEvent &e, int field_width = 0) {
+ std::string value;
+ if (e.clock == ctx->id("$async$"))
+ value = std::string("<async>");
+ else
+ value = (e.edge == FALLING_EDGE ? std::string("negedge ") : std::string("posedge ")) + e.clock.str(ctx);
+ if (int(value.length()) < field_width)
+ value.insert(value.length(), field_width - int(value.length()), ' ');
+ return value;
+ };
+
CriticalPathMap crit_paths;
DelayFrequency slack_histogram;
- Timing timing(ctx, true /* net_delays */, false /* update */, print_path ? &crit_paths : nullptr,
+ Timing timing(ctx, true /* net_delays */, false /* update */, (print_path || print_fmax) ? &crit_paths : nullptr,
print_histogram ? &slack_histogram : nullptr);
auto min_slack = timing.walk_paths();
-
- if (print_path) {
- std::map<IdString, std::pair<ClockPair, CriticalPath>> clock_reports;
+ std::map<IdString, std::pair<ClockPair, CriticalPath>> clock_reports;
+ std::vector<ClockPair> xclock_paths;
+ if (print_path || print_fmax) {
for (auto path : crit_paths) {
const ClockEvent &a = path.first.start;
const ClockEvent &b = path.first.end;
@@ -505,66 +515,134 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path)
clock_reports[a.clock] = path;
}
}
+
+ for (auto &path : crit_paths) {
+ const ClockEvent &a = path.first.start;
+ const ClockEvent &b = path.first.end;
+ if (a.clock == b.clock && a.clock != ctx->id("$async$"))
+ continue;
+ xclock_paths.push_back(path.first);
+ }
+
if (clock_reports.empty()) {
log_warning("No clocks found in design");
- } else {
+ }
+
+ std::sort(xclock_paths.begin(), xclock_paths.end(), [ctx](const ClockPair &a, const ClockPair &b) {
+ if (a.start.clock.str(ctx) < b.start.clock.str(ctx))
+ return true;
+ if (a.start.clock.str(ctx) > b.start.clock.str(ctx))
+ return false;
+ if (a.start.edge < b.start.edge)
+ return true;
+ if (a.start.edge > b.start.edge)
+ return false;
+ if (a.end.clock.str(ctx) < b.end.clock.str(ctx))
+ return true;
+ if (a.end.clock.str(ctx) > b.end.clock.str(ctx))
+ return false;
+ if (a.end.edge < b.end.edge)
+ return true;
+ return false;
+ });
+ }
+
+ if (print_path) {
+ auto print_path_report = [ctx] (ClockPair &clocks, PortRefVector &crit_path) {
delay_t total = 0;
- log_break();
- for (auto &clock : clock_reports) {
- log_info("Critical path report for clock '%s':\n", clock.first.c_str(ctx));
- log_info("curr total\n");
- auto &crit_path = clock.second.second.ports;
- auto &front = crit_path.front();
- auto &front_port = front->cell->ports.at(front->port);
- auto &front_driver = front_port.net->driver;
-
- int port_clocks;
- ctx->getPortTimingClass(front_driver.cell, front_driver.port, port_clocks);
+ auto &front = crit_path.front();
+ auto &front_port = front->cell->ports.at(front->port);
+ auto &front_driver = front_port.net->driver;
+
+ int port_clocks;
+ auto portClass = ctx->getPortTimingClass(front_driver.cell, front_driver.port, port_clocks);
+ IdString last_port = front_driver.port;
+ if (portClass == TMG_REGISTER_OUTPUT) {
for (int i = 0; i < port_clocks; i++) {
TimingClockingInfo clockInfo = ctx->getPortClockingInfo(front_driver.cell, front_driver.port, i);
const NetInfo *clknet = get_net_or_empty(front_driver.cell, clockInfo.clock_port);
- if (clknet != nullptr && clknet->name == clock.first &&
- clockInfo.edge == clock.second.first.start.edge) {
- IdString last_port = clockInfo.clock_port;
-
- for (auto sink : crit_path) {
- auto sink_cell = sink->cell;
- auto &port = sink_cell->ports.at(sink->port);
- auto net = port.net;
- auto &driver = net->driver;
- auto driver_cell = driver.cell;
- DelayInfo comb_delay;
- ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
- total += comb_delay.maxDelay();
- log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(comb_delay.maxDelay()),
- ctx->getDelayNS(total), driver_cell->name.c_str(ctx), driver.port.c_str(ctx));
- auto net_delay = ctx->getNetinfoRouteDelay(net, *sink);
- total += net_delay;
- auto driver_loc = ctx->getBelLocation(driver_cell->bel);
- auto sink_loc = ctx->getBelLocation(sink_cell->bel);
- log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n",
- ctx->getDelayNS(net_delay), ctx->getDelayNS(total), net->name.c_str(ctx),
- ctx->getDelayNS(sink->budget), driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y);
- log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx));
- last_port = sink->port;
- }
+ if (clknet != nullptr && clknet->name == clocks.start.clock &&
+ clockInfo.edge == clocks.start.edge) {
}
}
- log_break();
}
- log_break();
- for (auto &clock : clock_reports) {
- double Fmax;
- if (clock.second.first.start.edge == clock.second.first.end.edge)
- Fmax = 1000 / ctx->getDelayNS(clock.second.second.path_delay);
- else
- Fmax = 500 / ctx->getDelayNS(clock.second.second.path_delay);
- log_info("Max frequency for clock '%s': %.02f MHz\n", clock.first.c_str(ctx), Fmax);
+ for (auto sink : crit_path) {
+ auto sink_cell = sink->cell;
+ auto &port = sink_cell->ports.at(sink->port);
+ auto net = port.net;
+ auto &driver = net->driver;
+ auto driver_cell = driver.cell;
+ DelayInfo comb_delay;
+ if (last_port == driver.port) {
+ // Case where we start with a STARTPOINT etc
+ comb_delay.delay = 0;
+ } else {
+ ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
+ }
+ total += comb_delay.maxDelay();
+ log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(comb_delay.maxDelay()),
+ ctx->getDelayNS(total), driver_cell->name.c_str(ctx), driver.port.c_str(ctx));
+ auto net_delay = ctx->getNetinfoRouteDelay(net, *sink);
+ total += net_delay;
+ auto driver_loc = ctx->getBelLocation(driver_cell->bel);
+ auto sink_loc = ctx->getBelLocation(sink_cell->bel);
+ log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay),
+ ctx->getDelayNS(total), net->name.c_str(ctx), ctx->getDelayNS(sink->budget),
+ driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y);
+ log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx));
+ last_port = sink->port;
}
+
+ };
+
+ for (auto &clock : clock_reports) {
+ log_break();
+ log_info("Critical path report for clock '%s':\n", clock.first.c_str(ctx));
+ log_info("curr total\n");
+ auto &crit_path = clock.second.second.ports;
+ print_path_report(clock.second.first, crit_path);
+ }
+
+
+ for (auto &xclock : xclock_paths) {
log_break();
+ std::string start = format_event(xclock.start);
+ std::string end = format_event(xclock.end);
+ log_info("Critical path report for cross-domain path '%s' -> '%s':\n", start.c_str(), end.c_str());
+ log_info("curr total\n");
+ auto &crit_path = crit_paths.at(xclock).ports;
+ print_path_report(xclock, crit_path);
}
}
+ if (print_fmax) {
+ log_break();
+ for (auto &clock : clock_reports) {
+
+ double Fmax;
+ if (clock.second.first.start.edge == clock.second.first.end.edge)
+ Fmax = 1000 / ctx->getDelayNS(clock.second.second.path_delay);
+ else
+ Fmax = 500 / ctx->getDelayNS(clock.second.second.path_delay);
+ log_info("Max frequency for clock '%s': %.02f MHz\n", clock.first.c_str(ctx), Fmax);
+ }
+ log_break();
+
+ int start_field_width = 0, end_field_width = 0;
+ for (auto &xclock : xclock_paths) {
+ start_field_width = std::max((int)format_event(xclock.start).length(), start_field_width);
+ end_field_width = std::max((int)format_event(xclock.end).length(), end_field_width);
+ }
+
+ for (auto &xclock : xclock_paths) {
+ const ClockEvent &a = xclock.start;
+ const ClockEvent &b = xclock.end;
+ auto &path = crit_paths.at(xclock);
+ auto ev_a = format_event(a, start_field_width), ev_b = format_event(b, end_field_width);
+ log_info("Max delay %s -> %s: %0.02f ns\n", ev_a.c_str(), ev_b.c_str(), ctx->getDelayNS(path.path_delay));
+ }
+ log_break();
+ }
if (print_histogram && slack_histogram.size() > 0) {
unsigned num_bins = 20;
diff --git a/common/timing.h b/common/timing.h
index cfb71ae0..1fd76310 100644
--- a/common/timing.h
+++ b/common/timing.h
@@ -29,7 +29,7 @@ void assign_budget(Context *ctx, bool quiet = false);
// Perform timing analysis and print out the fmax, and optionally the
// critical path
-void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_path = false);
+void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false);
NEXTPNR_NAMESPACE_END