aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaciej Kurc <mkurc@antmicro.com>2021-09-09 15:50:03 +0200
committerMaciej Kurc <mkurc@antmicro.com>2021-09-28 17:42:51 +0200
commit99ae5ef38e5c7d92da56545701a4316e580c6f7b (patch)
tree62522b5301c4cd5f363c37388eb8f23500717de7
parent9d8d3bdbc48133ff7758c9c5293e5904bc6e5ba7 (diff)
downloadnextpnr-99ae5ef38e5c7d92da56545701a4316e580c6f7b.tar.gz
nextpnr-99ae5ef38e5c7d92da56545701a4316e580c6f7b.tar.bz2
nextpnr-99ae5ef38e5c7d92da56545701a4316e580c6f7b.zip
Added writing a CSV report with timing analysis of each net branch
Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
-rw-r--r--common/command.cc8
-rw-r--r--common/router1.cc2
-rw-r--r--common/timing.cc83
-rw-r--r--common/timing.h2
4 files changed, 89 insertions, 6 deletions
diff --git a/common/command.cc b/common/command.cc
index 954a3442..737a5b77 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -183,6 +183,9 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("placed-svg", po::value<std::string>(), "write render of placement to SVG file");
general.add_options()("routed-svg", po::value<std::string>(), "write render of routing to SVG file");
+ general.add_options()("timing-report", po::value<std::string>(),
+ "write timing analysis report in CSV format to file");
+
return general;
}
@@ -249,6 +252,11 @@ void CommandHandler::setupContext(Context *ctx)
ctx->settings[ctx->id("timing/allowFail")] = true;
}
+ if (vm.count("timing-report")) {
+ std::string filename = vm["timing-report"].as<std::string>();
+ ctx->settings[ctx->id("timing/reportFile")] = filename;
+ }
+
if (vm.count("placer")) {
std::string placer = vm["placer"].as<std::string>();
if (std::find(Arch::availablePlacers.begin(), Arch::availablePlacers.end(), placer) ==
diff --git a/common/router1.cc b/common/router1.cc
index 5645b898..d62e776a 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -874,7 +874,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
log_info("Checksum: 0x%08x\n", ctx->checksum());
timing_analysis(ctx, true /* slack_histogram */, true /* print_fmax */, true /* print_path */,
- true /* warn_on_failure */);
+ true /* warn_on_failure */, true /* write_report */);
return true;
} catch (log_execution_error_exception) {
diff --git a/common/timing.cc b/common/timing.cc
index ce293f34..fd14e2da 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -637,6 +637,16 @@ struct CriticalPath
typedef dict<ClockPair, CriticalPath> CriticalPathMap;
+struct NetSinkTiming
+{
+ ClockPair clock_pair;
+ PortRef sink;
+ delay_t delay;
+ delay_t budget;
+};
+
+typedef dict<const NetInfo*, std::vector<NetSinkTiming>, hash_ptr_ops> DetailedNetTimings;
+
struct Timing
{
Context *ctx;
@@ -645,6 +655,7 @@ struct Timing
delay_t min_slack;
CriticalPathMap *crit_path;
DelayFrequency *slack_histogram;
+ DetailedNetTimings *detailed_net_timings;
IdString async_clock;
struct TimingData
@@ -660,9 +671,11 @@ struct Timing
};
Timing(Context *ctx, bool net_delays, bool update, CriticalPathMap *crit_path = nullptr,
- DelayFrequency *slack_histogram = nullptr)
+ DelayFrequency *slack_histogram = nullptr,
+ DetailedNetTimings *detailed_net_timings = nullptr)
: ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->setting<float>("target_freq")),
- crit_path(crit_path), slack_histogram(slack_histogram), async_clock(ctx->id("$async$"))
+ crit_path(crit_path), slack_histogram(slack_histogram), detailed_net_timings(detailed_net_timings),
+ async_clock(ctx->id("$async$"))
{
}
@@ -948,6 +961,17 @@ struct Timing
ClockPair clockPair{startdomain.first, dest_ev};
nd.arrival_time[dest_ev] = std::max(nd.arrival_time[dest_ev], endpoint_arrival);
+ // Store the detailed timing for each net and user (a.k.a. sink)
+ if (detailed_net_timings) {
+ NetSinkTiming sink_timing;
+ sink_timing.clock_pair = clockPair;
+ sink_timing.sink = usr;
+ sink_timing.delay = endpoint_arrival;
+ sink_timing.budget = period;
+
+ (*detailed_net_timings)[net].push_back(sink_timing);
+ }
+
if (crit_path) {
if (!crit_nets.count(clockPair) || crit_nets.at(clockPair).first < endpoint_arrival) {
crit_nets[clockPair] = std::make_pair(endpoint_arrival, net);
@@ -1111,7 +1135,7 @@ 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_fmax, bool print_path, bool warn_on_failure)
+void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path, bool warn_on_failure, bool write_report)
{
auto format_event = [ctx](const ClockEvent &e, int field_width = 0) {
std::string value;
@@ -1126,9 +1150,10 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
CriticalPathMap crit_paths;
DelayFrequency slack_histogram;
+ DetailedNetTimings detailed_net_timings;
Timing timing(ctx, true /* net_delays */, false /* update */, (print_path || print_fmax) ? &crit_paths : nullptr,
- print_histogram ? &slack_histogram : nullptr);
+ print_histogram ? &slack_histogram : nullptr, write_report ? &detailed_net_timings : nullptr);
timing.walk_paths();
std::map<IdString, std::pair<ClockPair, CriticalPath>> clock_reports;
std::map<IdString, double> clock_fmax;
@@ -1411,6 +1436,56 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
std::string(bins[i] * bar_width / max_freq, '*').c_str(),
(bins[i] * bar_width) % max_freq > 0 ? '+' : ' ');
}
+
+ // Write detailed timing analysis report to file
+ std::string report_file;
+ if (write_report) {
+ if (ctx->settings.count(ctx->id("timing/reportFile"))) {
+ report_file = ctx->settings[ctx->id("timing/reportFile")].as_string();
+ }
+ }
+
+ if (!report_file.empty()) {
+ log_info("\nWriting timing analysis report...\n");
+
+ FILE* fp = fopen(report_file.c_str(), "w");
+ NPNR_ASSERT(fp != nullptr);
+
+ auto cellport = [&](const PortRef& port) {
+ std::string str;
+ if (port.cell != nullptr) {
+ str = std::string(port.cell->name.c_str(ctx));
+ } else {
+ str = "?";
+ }
+ return str + "." + std::string(port.port.c_str(ctx));
+ };
+
+ // Header
+ fprintf(fp, "net,source,sink,start,end,delay,budget\n");
+
+ // Content
+ for (const auto& it : detailed_net_timings) {
+ const NetInfo* net = it.first;
+ const std::string drv_port = cellport(net->driver);
+
+ for (const auto& sink_timing : it.second) {
+ fprintf(fp, "%s,%s,%s,%s %s,%s %s,%f,%f\n",
+ net->name.c_str(ctx),
+ drv_port.c_str(),
+ cellport(sink_timing.sink).c_str(),
+ edge_name(sink_timing.clock_pair.start.edge),
+ sink_timing.clock_pair.start.clock.c_str(ctx),
+ edge_name(sink_timing.clock_pair.end.edge),
+ sink_timing.clock_pair.end.clock.c_str(ctx),
+ ctx->getDelayNS(sink_timing.delay),
+ ctx->getDelayNS(sink_timing.budget)
+ );
+ }
+ }
+
+ fclose(fp);
+ }
}
NEXTPNR_NAMESPACE_END
diff --git a/common/timing.h b/common/timing.h
index 6548757b..4645077a 100644
--- a/common/timing.h
+++ b/common/timing.h
@@ -252,7 +252,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_fmax = true, bool print_path = false,
- bool warn_on_failure = false);
+ bool warn_on_failure = false, bool write_report = false);
NEXTPNR_NAMESPACE_END