diff options
author | Maciej Kurc <mkurc@antmicro.com> | 2021-09-09 15:50:03 +0200 |
---|---|---|
committer | Maciej Kurc <mkurc@antmicro.com> | 2021-09-28 17:42:51 +0200 |
commit | 99ae5ef38e5c7d92da56545701a4316e580c6f7b (patch) | |
tree | 62522b5301c4cd5f363c37388eb8f23500717de7 /common | |
parent | 9d8d3bdbc48133ff7758c9c5293e5904bc6e5ba7 (diff) | |
download | nextpnr-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>
Diffstat (limited to 'common')
-rw-r--r-- | common/command.cc | 8 | ||||
-rw-r--r-- | common/router1.cc | 2 | ||||
-rw-r--r-- | common/timing.cc | 83 | ||||
-rw-r--r-- | common/timing.h | 2 |
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 |