From 99ae5ef38e5c7d92da56545701a4316e580c6f7b Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Thu, 9 Sep 2021 15:50:03 +0200 Subject: [PATCH 01/10] Added writing a CSV report with timing analysis of each net branch Signed-off-by: Maciej Kurc --- common/command.cc | 8 +++++ common/router1.cc | 2 +- common/timing.cc | 83 ++++++++++++++++++++++++++++++++++++++++++++--- 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(), "write render of placement to SVG file"); general.add_options()("routed-svg", po::value(), "write render of routing to SVG file"); + general.add_options()("timing-report", po::value(), + "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(); + ctx->settings[ctx->id("timing/reportFile")] = filename; + } + if (vm.count("placer")) { std::string placer = vm["placer"].as(); 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 CriticalPathMap; +struct NetSinkTiming +{ + ClockPair clock_pair; + PortRef sink; + delay_t delay; + delay_t budget; +}; + +typedef dict, 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("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> clock_reports; std::map 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 From 12adbb81b177a948c0c75592b209393559741158 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 10 Sep 2021 10:00:44 +0200 Subject: [PATCH 02/10] Switched to JSON format for timing analysis report Signed-off-by: Maciej Kurc --- common/timing.cc | 124 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 38 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index fd14e2da..d10ac181 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -26,9 +26,12 @@ #include #include "log.h" #include "util.h" +#include "json11.hpp" NEXTPNR_NAMESPACE_BEGIN +using namespace json11; + void TimingAnalyser::setup() { init_ports(); @@ -1135,6 +1138,12 @@ void assign_budget(Context *ctx, bool quiet) log_info("Checksum: 0x%08x\n", ctx->checksum()); } +void write_timing_report( + Context* ctx, + const std::string& file_name, + const DetailedNetTimings& detailed_net_timings +); + 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) { @@ -1447,45 +1456,84 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p 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); + write_timing_report(ctx, report_file, detailed_net_timings); } } +void write_timing_report( + Context* ctx, + const std::string& file_name, + const DetailedNetTimings& detailed_net_timings +) +{ + auto cellport_name = [ctx](const PortRef& port) { + std::string str; + if (port.cell != nullptr) { + str = std::string(port.cell->name.c_str(ctx)); + } else { + str = ""; // FIXME: When does that happen? + } + return str + "." + std::string(port.port.c_str(ctx)); + }; + + auto event_name = [ctx](const ClockEvent &e) { + std::string value; + if (e.clock == ctx->id("$async$")) + value = std::string(""); + else + value = (e.edge == FALLING_EDGE ? std::string("negedge ") : + std::string("posedge ")) + e.clock.str(ctx); + return value; + }; + + // Open the file + FILE* fp = fopen(file_name.c_str(), "w"); + NPNR_ASSERT(fp != nullptr); + + // Detailed net timing analysis + auto detailedNetTimingsJson = Json::array(); + for (const auto& it : detailed_net_timings) { + const NetInfo* net = it.first; + const std::string drv_port = cellport_name(net->driver); + + ClockEvent start = it.second[0].clock_pair.start; + + Json::array endpointsJson; + for (const auto& sink_timing : it.second) { + + // FIXME: Is it possible that there are multiple different start + // events for a single net? It has a single driver + NPNR_ASSERT(sink_timing.clock_pair.start == start); + + auto endpointJson = Json::object({ + {"sink", cellport_name(sink_timing.sink)}, + {"event", event_name(sink_timing.clock_pair.end)}, + {"delay", ctx->getDelayNS(sink_timing.delay)}, + {"budget", ctx->getDelayNS(sink_timing.budget)} + }); + endpointsJson.push_back(endpointJson); + } + + auto netTimingJson = Json::object({ + {"net", net->name.c_str(ctx)}, + {"driver", drv_port}, + {"event", event_name(start)}, + {"endpoints", endpointsJson} + }); + detailedNetTimingsJson.push_back(netTimingJson); + } + + auto analysisJson = Json::object({ + {"detailed_net_timings", Json(detailedNetTimingsJson)} + }); + + // Assemble and serialize the final Json + auto jsonRoot = Json(Json::object({ + {"timing_analysis", Json(analysisJson)} + })); + + fputs(jsonRoot.dump().c_str(), fp); + fclose(fp); +} + NEXTPNR_NAMESPACE_END From d8571b6c008fd89a3dc59122dccaf16054b7c592 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 10 Sep 2021 14:04:41 +0200 Subject: [PATCH 03/10] Decoupled critical path report generation from its printing Signed-off-by: Maciej Kurc --- common/timing.cc | 402 +++++++++++++++++++++++++++++++---------------- 1 file changed, 266 insertions(+), 136 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index d10ac181..596058eb 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -640,6 +640,30 @@ struct CriticalPath typedef dict CriticalPathMap; +struct CriticalPathSegment +{ + enum class Type { + LOGIC, + ROUTING + }; + + std::pair from; + std::pair to; + Type type; + const NetInfo* net; + delay_t delay; + delay_t budget; +}; +typedef std::vector CriticalPathSegments; + +struct CriticalPathReport +{ + ClockPair clock_pair; + CriticalPathSegments segments; + delay_t period; +}; +typedef dict CriticalPathReportMap; + struct NetSinkTiming { ClockPair clock_pair; @@ -1141,9 +1165,101 @@ void assign_budget(Context *ctx, bool quiet) void write_timing_report( Context* ctx, const std::string& file_name, + const std::map clock_reports, + const std::vector xclock_reports, const DetailedNetTimings& detailed_net_timings ); +CriticalPathReport build_critical_path_report(Context* ctx, ClockPair &clocks, const PortRefVector &crit_path) { + + CriticalPathReport report; + report.clock_pair = 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); + + const CellInfo* last_cell = front->cell; + IdString last_port = front_driver.port; + + int clock_start = -1; + 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 == clocks.start.clock && + clockInfo.edge == clocks.start.edge) { + last_port = clockInfo.clock_port; + clock_start = i; + break; + } + } + } + + 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; + DelayQuad comb_delay; + if (clock_start != -1) { + auto clockInfo = ctx->getPortClockingInfo(driver_cell, driver.port, clock_start); + comb_delay = clockInfo.clockToQ; + clock_start = -1; + } else if (last_port == driver.port) { + // Case where we start with a STARTPOINT etc + comb_delay = DelayQuad(0); + } else { + ctx->getCellDelay(driver_cell, last_port, driver.port, comb_delay); + } + + CriticalPathSegment seg_logic; + seg_logic.type = CriticalPathSegment::Type::LOGIC; + seg_logic.delay = comb_delay.maxDelay(); + seg_logic.budget = 0; + seg_logic.from = std::make_pair(const_cast(last_cell), last_port); + seg_logic.to = std::make_pair(driver_cell, driver.port); + seg_logic.net = nullptr; + report.segments.push_back(seg_logic); + + auto net_delay = ctx->getNetinfoRouteDelay(net, *sink); + + CriticalPathSegment seg_route; + seg_route.type = CriticalPathSegment::Type::ROUTING; + seg_route.delay = net_delay; + seg_route.budget = sink->budget; + seg_route.from = std::make_pair(driver_cell, driver.port); + seg_route.to = std::make_pair(sink_cell, sink->port); + seg_route.net = net; + report.segments.push_back(seg_route); + + last_cell = sink_cell; + last_port = sink->port; + } + + int clockCount = 0; + auto sinkClass = ctx->getPortTimingClass(crit_path.back()->cell, crit_path.back()->port, clockCount); + if (sinkClass == TMG_REGISTER_INPUT && clockCount > 0) { + auto sinkClockInfo = ctx->getPortClockingInfo(crit_path.back()->cell, crit_path.back()->port, 0); + delay_t setup = sinkClockInfo.setup.maxDelay(); + + CriticalPathSegment seg_logic; + seg_logic.type = CriticalPathSegment::Type::LOGIC; + seg_logic.delay = setup; + seg_logic.budget = 0; + seg_logic.from = std::make_pair(nullptr, IdString()); + seg_logic.to = std::make_pair(const_cast(last_cell), last_port); + seg_logic.net = nullptr; + report.segments.push_back(seg_logic); + } + + return report; +} + 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) { @@ -1164,11 +1280,17 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p Timing timing(ctx, true /* net_delays */, false /* update */, (print_path || print_fmax) ? &crit_paths : nullptr, print_histogram ? &slack_histogram : nullptr, write_report ? &detailed_net_timings : nullptr); timing.walk_paths(); - std::map> clock_reports; + + bool report_critical_paths = print_path || print_fmax || write_report; + + std::map clock_reports; + std::vector xclock_reports; + std::map clock_fmax; - std::vector xclock_paths; std::set empty_clocks; // set of clocks with no interior paths - if (print_path || print_fmax) { + + if (report_critical_paths) { + for (auto path : crit_paths) { const ClockEvent &a = path.first.start; const ClockEvent &b = path.first.end; @@ -1187,8 +1309,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p else Fmax = 500 / ctx->getDelayNS(path.second.path_delay); if (!clock_fmax.count(a.clock) || Fmax < clock_fmax.at(a.clock)) { - clock_reports[a.clock] = path; clock_fmax[a.clock] = Fmax; + clock_reports[a.clock] = build_critical_path_report(ctx, path.first, path.second.ports); + clock_reports[a.clock].period = path.second.path_period; } } @@ -1197,14 +1320,21 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p const ClockEvent &b = path.first.end; if (a.clock == b.clock && a.clock != ctx->id("$async$")) continue; - xclock_paths.push_back(path.first); + + auto &crit_path = crit_paths.at(path.first).ports; + xclock_reports.push_back(build_critical_path_report(ctx, path.first, crit_path)); + xclock_reports.back().period = path.second.path_period; } if (clock_reports.empty()) { log_info("No Fmax available; no interior timing paths found in design.\n"); } - std::sort(xclock_paths.begin(), xclock_paths.end(), [ctx](const ClockPair &a, const ClockPair &b) { + std::sort(xclock_reports.begin(), xclock_reports.end(), [ctx](const CriticalPathReport &ra, const CriticalPathReport &rb) { + + const auto& a = ra.clock_pair; + const auto& b = rb.clock_pair; + if (a.start.clock.str(ctx) < b.start.clock.str(ctx)) return true; if (a.start.clock.str(ctx) > b.start.clock.str(ctx)) @@ -1223,124 +1353,86 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p }); } + // Print critical paths if (print_path) { - static auto print_net_source = [](Context *ctx, NetInfo *net) { - // Check if this net is annotated with a source list - auto sources = net->attrs.find(ctx->id("src")); - if (sources == net->attrs.end()) { - // No sources for this net, can't print anything - return; - } - // Sources are separated by pipe characters. - // There is no guaranteed ordering on sources, so we just print all - auto sourcelist = sources->second.as_string(); - std::vector source_entries; - size_t current = 0, prev = 0; - while ((current = sourcelist.find("|", prev)) != std::string::npos) { - source_entries.emplace_back(sourcelist.substr(prev, current - prev)); - prev = current + 1; - } - // Ensure we emplace the final entry - source_entries.emplace_back(sourcelist.substr(prev, current - prev)); - - // Iterate and print our source list at the correct indentation level - log_info(" Defined in:\n"); - for (auto entry : source_entries) { - log_info(" %s\n", entry.c_str()); - } - }; - - auto print_path_report = [ctx](ClockPair &clocks, PortRefVector &crit_path) { + auto print_path_report = [ctx](const CriticalPathReport& report) { delay_t total = 0, logic_total = 0, route_total = 0; - 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; - int clock_start = -1; - 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 == clocks.start.clock && - clockInfo.edge == clocks.start.edge) { - last_port = clockInfo.clock_port; - clock_start = i; - break; - } - } - } log_info("curr total\n"); - 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; - DelayQuad comb_delay; - if (clock_start != -1) { - auto clockInfo = ctx->getPortClockingInfo(driver_cell, driver.port, clock_start); - comb_delay = clockInfo.clockToQ; - clock_start = -1; - } else if (last_port == driver.port) { - // Case where we start with a STARTPOINT etc - comb_delay = DelayQuad(0); - } else { - ctx->getCellDelay(driver_cell, last_port, driver.port, comb_delay); - } - total += comb_delay.maxDelay(); - logic_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; - route_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)); - if (ctx->verbose) { - auto driver_wire = ctx->getNetinfoSourceWire(net); - auto sink_wire = ctx->getNetinfoSinkWire(net, *sink, 0); - log_info(" prediction: %f ns estimate: %f ns\n", - ctx->getDelayNS(ctx->predictDelay(net, *sink)), - ctx->getDelayNS(ctx->estimateDelay(driver_wire, sink_wire))); - auto cursor = sink_wire; - delay_t delay; - while (driver_wire != cursor) { -#ifdef ARCH_ECP5 - if (net->is_global) - break; -#endif - auto it = net->wires.find(cursor); - assert(it != net->wires.end()); - auto pip = it->second.pip; - NPNR_ASSERT(pip != PipId()); - delay = ctx->getPipDelay(pip).maxDelay(); - log_info(" %1.3f %s\n", ctx->getDelayNS(delay), ctx->nameOfPip(pip)); - cursor = ctx->getPipSrcWire(pip); + for (const auto& segment : report.segments) { + + total += segment.delay; + + if (segment.type == CriticalPathSegment::Type::LOGIC) { + logic_total += segment.delay; + if (segment.from.first != nullptr) { + log_info("%4.1f %4.1f Source %s.%s\n", + ctx->getDelayNS(segment.delay), + ctx->getDelayNS(total), + segment.from.first->name.c_str(ctx), + segment.from.second.c_str(ctx) + ); + } + else { + log_info("%4.1f %4.1f Setup %s.%s\n", + ctx->getDelayNS(segment.delay), + ctx->getDelayNS(total), + segment.to.first->name.c_str(ctx), + segment.to.second.c_str(ctx) + ); } } - if (!ctx->disable_critical_path_source_print) { - print_net_source(ctx, net); + + if (segment.type == CriticalPathSegment::Type::ROUTING) { + route_total += segment.delay; + + auto driver_loc = ctx->getBelLocation(segment.from.first->bel); + auto sink_loc = ctx->getBelLocation(segment.to.first->bel); + + log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", + ctx->getDelayNS(segment.delay), + ctx->getDelayNS(total), + segment.net->name.c_str(ctx), + ctx->getDelayNS(segment.budget), + driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y + ); + log_info(" Sink %s.%s\n", + segment.to.first->name.c_str(ctx), + segment.to.second.c_str(ctx) + ); + + if (ctx->verbose) { + + PortRef sink; + sink.cell = segment.to.first; + sink.port = segment.to.second; + sink.budget = segment.budget; + + auto net = segment.net; + + auto driver_wire = ctx->getNetinfoSourceWire(net); + auto sink_wire = ctx->getNetinfoSinkWire(net, sink, 0); + log_info(" prediction: %f ns estimate: %f ns\n", + ctx->getDelayNS(ctx->predictDelay(net, sink)), + ctx->getDelayNS(ctx->estimateDelay(driver_wire, sink_wire))); + auto cursor = sink_wire; + delay_t delay; + while (driver_wire != cursor) { +#ifdef ARCH_ECP5 + if (net->is_global) + break; +#endif + auto it = net->wires.find(cursor); + assert(it != net->wires.end()); + auto pip = it->second.pip; + NPNR_ASSERT(pip != PipId()); + delay = ctx->getPipDelay(pip).maxDelay(); + log_info(" %1.3f %s\n", ctx->getDelayNS(delay), ctx->nameOfPip(pip)); + cursor = ctx->getPipSrcWire(pip); + } + } } - last_port = sink->port; - } - int clockCount = 0; - auto sinkClass = ctx->getPortTimingClass(crit_path.back()->cell, crit_path.back()->port, clockCount); - if (sinkClass == TMG_REGISTER_INPUT && clockCount > 0) { - auto sinkClockInfo = ctx->getPortClockingInfo(crit_path.back()->cell, crit_path.back()->port, 0); - delay_t setup = sinkClockInfo.setup.maxDelay(); - total += setup; - logic_total += setup; - log_info("%4.1f %4.1f Setup %s.%s\n", ctx->getDelayNS(setup), ctx->getDelayNS(total), - crit_path.back()->cell->name.c_str(ctx), crit_path.back()->port.c_str(ctx)); } log_info("%.1f ns logic, %.1f ns routing\n", ctx->getDelayNS(logic_total), ctx->getDelayNS(route_total)); }; @@ -1348,24 +1440,24 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p for (auto &clock : clock_reports) { log_break(); std::string start = - clock.second.first.start.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge"); + clock.second.clock_pair.start.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge"); std::string end = - clock.second.first.end.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge"); + clock.second.clock_pair.end.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge"); log_info("Critical path report for clock '%s' (%s -> %s):\n", clock.first.c_str(ctx), start.c_str(), end.c_str()); - auto &crit_path = clock.second.second.ports; - print_path_report(clock.second.first, crit_path); + auto &report = clock.second; + print_path_report(report); } - for (auto &xclock : xclock_paths) { + for (auto &report : xclock_reports) { log_break(); - std::string start = format_event(xclock.start); - std::string end = format_event(xclock.end); + std::string start = format_event(report.clock_pair.start); + std::string end = format_event(report.clock_pair.end); log_info("Critical path report for cross-domain path '%s' -> '%s':\n", start.c_str(), end.c_str()); - auto &crit_path = crit_paths.at(xclock).ports; - print_path_report(xclock, crit_path); + print_path_report(report); } } + if (print_fmax) { log_break(); unsigned max_width = 0; @@ -1401,17 +1493,20 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p 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 &report : xclock_reports) { + start_field_width = std::max((int)format_event(report.clock_pair.start).length(), start_field_width); + end_field_width = std::max((int)format_event(report.clock_pair.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); + for (auto &report : xclock_reports) { + const ClockEvent &a = report.clock_pair.start; + const ClockEvent &b = report.clock_pair.end; + delay_t path_delay = 0; + for (const auto& segment : report.segments) { + path_delay += segment.delay; + } 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_info("Max delay %s -> %s: %0.02f ns\n", ev_a.c_str(), ev_b.c_str(), ctx->getDelayNS(path_delay)); } log_break(); } @@ -1455,14 +1550,20 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p } if (!report_file.empty()) { - log_info("\nWriting timing analysis report...\n"); - write_timing_report(ctx, report_file, detailed_net_timings); + log_break(); + log_info("Writing timing analysis report...\n"); + write_timing_report(ctx, report_file, + clock_reports, + xclock_reports, + detailed_net_timings); } } void write_timing_report( Context* ctx, const std::string& file_name, + const std::map clock_reports, + const std::vector xclock_reports, const DetailedNetTimings& detailed_net_timings ) { @@ -1486,11 +1587,39 @@ void write_timing_report( return value; }; +// auto report_critical_path = [ctx](const ClockPair &clocks, const PortRefVector &path) { +// } + // Open the file FILE* fp = fopen(file_name.c_str(), "w"); NPNR_ASSERT(fp != nullptr); - // Detailed net timing analysis + // Critical paths + auto critPathsJson = Json::array(); + for (auto &report : clock_reports) { + + critPathsJson.push_back(Json::object({ + {"from", event_name(report.second.clock_pair.start)}, + {"to", event_name(report.second.clock_pair.end)}, + {"path", Json()} + })); + + //auto &crit_path = clock.second.second.ports; + //print_path_report(clock.second.first, crit_path); + } + + // Cross-domain paths + for (auto &report : xclock_reports) { + critPathsJson.push_back(Json::object({ + {"from", event_name(report.clock_pair.start)}, + {"to", event_name(report.clock_pair.end)}, + {"path", Json()} + })); +// auto &crit_path = crit_paths.at(xclock).ports; +// print_path_report(xclock, crit_path); + } + + // Detailed per-net timing analysis auto detailedNetTimingsJson = Json::array(); for (const auto& it : detailed_net_timings) { const NetInfo* net = it.first; @@ -1524,6 +1653,7 @@ void write_timing_report( } auto analysisJson = Json::object({ + {"critical_paths", Json(critPathsJson)}, {"detailed_net_timings", Json(detailedNetTimingsJson)} }); From c6dc1f535acd2e9d0893ac17c9fa204e8b2370a6 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 10 Sep 2021 15:44:46 +0200 Subject: [PATCH 04/10] Added reporting critical paths in JSON format Signed-off-by: Maciej Kurc --- common/timing.cc | 74 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 596058eb..8c4002d0 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -1251,8 +1251,8 @@ CriticalPathReport build_critical_path_report(Context* ctx, ClockPair &clocks, c seg_logic.type = CriticalPathSegment::Type::LOGIC; seg_logic.delay = setup; seg_logic.budget = 0; - seg_logic.from = std::make_pair(nullptr, IdString()); - seg_logic.to = std::make_pair(const_cast(last_cell), last_port); + seg_logic.from = std::make_pair(const_cast(last_cell), last_port); + seg_logic.to = seg_logic.from; seg_logic.net = nullptr; report.segments.push_back(seg_logic); } @@ -1366,7 +1366,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p if (segment.type == CriticalPathSegment::Type::LOGIC) { logic_total += segment.delay; - if (segment.from.first != nullptr) { + if (segment.from != segment.to) { log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(segment.delay), ctx->getDelayNS(total), @@ -1567,16 +1567,6 @@ void write_timing_report( const DetailedNetTimings& detailed_net_timings ) { - auto cellport_name = [ctx](const PortRef& port) { - std::string str; - if (port.cell != nullptr) { - str = std::string(port.cell->name.c_str(ctx)); - } else { - str = ""; // FIXME: When does that happen? - } - return str + "." + std::string(port.port.c_str(ctx)); - }; - auto event_name = [ctx](const ClockEvent &e) { std::string value; if (e.clock == ctx->id("$async$")) @@ -1587,8 +1577,46 @@ void write_timing_report( return value; }; -// auto report_critical_path = [ctx](const ClockPair &clocks, const PortRefVector &path) { -// } + auto report_critical_path = [ctx](const CriticalPathReport& report) { + Json::array pathJson; + + for (const auto& segment : report.segments) { + + auto fromLoc = ctx->getBelLocation(segment.from.first->bel); + auto toLoc = ctx->getBelLocation(segment.to.first->bel); + + auto fromJson = Json::object({ + {"cell", segment.from.first->name.c_str(ctx)}, + {"port", segment.from.second.c_str(ctx)}, + {"loc", Json::array({fromLoc.x, fromLoc.y})} + }); + + auto toJson = Json::object({ + {"cell", segment.to.first->name.c_str(ctx)}, + {"port", segment.to.second.c_str(ctx)}, + {"loc", Json::array({toLoc.x, toLoc.y})} + }); + + auto segmentJson = Json::object({ + {"delay", ctx->getDelayNS(segment.delay)}, + {"from", fromJson}, + {"to", toJson}, + }); + + if (segment.type == CriticalPathSegment::Type::LOGIC) { + segmentJson["type"] = "logic"; + } + else if (segment.type == CriticalPathSegment::Type::ROUTING) { + segmentJson["type"] = "routing"; + segmentJson["net"] = segment.net->name.c_str(ctx); + segmentJson["budget"] = ctx->getDelayNS(segment.budget); + } + + pathJson.push_back(segmentJson); + } + + return pathJson; + }; // Open the file FILE* fp = fopen(file_name.c_str(), "w"); @@ -1601,11 +1629,8 @@ void write_timing_report( critPathsJson.push_back(Json::object({ {"from", event_name(report.second.clock_pair.start)}, {"to", event_name(report.second.clock_pair.end)}, - {"path", Json()} + {"path", report_critical_path(report.second)} })); - - //auto &crit_path = clock.second.second.ports; - //print_path_report(clock.second.first, crit_path); } // Cross-domain paths @@ -1613,17 +1638,14 @@ void write_timing_report( critPathsJson.push_back(Json::object({ {"from", event_name(report.clock_pair.start)}, {"to", event_name(report.clock_pair.end)}, - {"path", Json()} + {"path", report_critical_path(report)} })); -// auto &crit_path = crit_paths.at(xclock).ports; -// print_path_report(xclock, crit_path); } // Detailed per-net timing analysis auto detailedNetTimingsJson = Json::array(); for (const auto& it : detailed_net_timings) { const NetInfo* net = it.first; - const std::string drv_port = cellport_name(net->driver); ClockEvent start = it.second[0].clock_pair.start; @@ -1635,7 +1657,8 @@ void write_timing_report( NPNR_ASSERT(sink_timing.clock_pair.start == start); auto endpointJson = Json::object({ - {"sink", cellport_name(sink_timing.sink)}, + {"cell", sink_timing.sink.cell->name.c_str(ctx)}, + {"port", sink_timing.sink.port.c_str(ctx)}, {"event", event_name(sink_timing.clock_pair.end)}, {"delay", ctx->getDelayNS(sink_timing.delay)}, {"budget", ctx->getDelayNS(sink_timing.budget)} @@ -1645,7 +1668,8 @@ void write_timing_report( auto netTimingJson = Json::object({ {"net", net->name.c_str(ctx)}, - {"driver", drv_port}, + {"driver", net->driver.cell->name.c_str(ctx)}, + {"port", net->driver.port.c_str(ctx)}, {"event", event_name(start)}, {"endpoints", endpointsJson} }); From 6deff56e836543fe6d3083d7402fd42edc00e4c7 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 28 Sep 2021 17:01:28 +0200 Subject: [PATCH 05/10] Moved timing result report storage to the context, added its writeout to the current utilization and fmax report Signed-off-by: Maciej Kurc --- common/command.cc | 8 - common/nextpnr_types.h | 71 +++++++++ common/report.cc | 123 ++++++++++++++ common/router1.cc | 2 +- common/timing.cc | 355 ++++++++++------------------------------- common/timing.h | 2 +- 6 files changed, 279 insertions(+), 282 deletions(-) diff --git a/common/command.cc b/common/command.cc index 737a5b77..954a3442 100644 --- a/common/command.cc +++ b/common/command.cc @@ -183,9 +183,6 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("placed-svg", po::value(), "write render of placement to SVG file"); general.add_options()("routed-svg", po::value(), "write render of routing to SVG file"); - general.add_options()("timing-report", po::value(), - "write timing analysis report in CSV format to file"); - return general; } @@ -252,11 +249,6 @@ void CommandHandler::setupContext(Context *ctx) ctx->settings[ctx->id("timing/allowFail")] = true; } - if (vm.count("timing-report")) { - std::string filename = vm["timing-report"].as(); - ctx->settings[ctx->id("timing/reportFile")] = filename; - } - if (vm.count("placer")) { std::string placer = vm["placer"].as(); if (std::find(Arch::availablePlacers.begin(), Arch::availablePlacers.end(), placer) == diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index 1cae3dbe..17261095 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -223,10 +223,81 @@ struct ClockFmax float constraint; }; +struct ClockEvent +{ + IdString clock; + ClockEdge edge; + + bool operator==(const ClockEvent &other) const { return clock == other.clock && edge == other.edge; } + unsigned int hash() const { return mkhash(clock.hash(), int(edge)); } +}; + +struct ClockPair +{ + ClockEvent start, end; + + bool operator==(const ClockPair &other) const { return start == other.start && end == other.end; } + unsigned int hash() const { return mkhash(start.hash(), end.hash()); } +}; + +struct CriticalPath +{ + struct Segment { + + // Segment type + enum class Type { + LOGIC, + ROUTING + }; + + // Type + Type type; + // Net name (routing only) + IdString net; + // From cell.port + std::pair from; + // To cell.port + std::pair to; + // Segment delay + delay_t delay; + // Segment budget (routing only) + delay_t budget; + }; + + // Clock pair + ClockPair clock_pair; + // Total path delay + delay_t delay; + // Period (max allowed delay) + delay_t period; + // Individual path segments + std::vector segments; +}; + +// Holds timing information of a single source to sink path of a net +struct NetSinkTiming +{ + // Clock event pair + ClockPair clock_pair; + // Cell and port (the sink) + std::pair cell_port; + // Delay + delay_t delay; + // Delay budget + delay_t budget; +}; + struct TimingResult { // Achieved and target Fmax for all clock domains dict clock_fmax; + // Single domain critical paths + dict clock_paths; + // Cross-domain critical paths + std::vector xclock_paths; + + // Detailed net timing data + dict> detailed_net_timings; }; // Represents the contents of a non-leaf cell in a design diff --git a/common/report.cc b/common/report.cc index 4b3b2418..a595777a 100644 --- a/common/report.cc +++ b/common/report.cc @@ -40,6 +40,127 @@ dict> get_utilization(const Context *ctx) return result; } } // namespace + +static std::string clock_event_name (const Context* ctx, const ClockEvent& e) { + std::string value; + if (e.clock == ctx->id("$async$")) + value = std::string(""); + else + value = (e.edge == FALLING_EDGE ? std::string("negedge ") : + std::string("posedge ")) + e.clock.str(ctx); + return value; +}; + +static Json::array report_critical_paths (const Context* ctx) { + + auto report_critical_path = [ctx](const CriticalPath& report) { + Json::array pathJson; + + for (const auto& segment : report.segments) { + + const auto& driver = ctx->cells.at(segment.from.first); + const auto& sink = ctx->cells.at(segment.to.first); + + auto fromLoc = ctx->getBelLocation(driver->bel); + auto toLoc = ctx->getBelLocation(sink->bel); + + auto fromJson = Json::object({ + {"cell", segment.from.first.c_str(ctx)}, + {"port", segment.from.second.c_str(ctx)}, + {"loc", Json::array({fromLoc.x, fromLoc.y})} + }); + + auto toJson = Json::object({ + {"cell", segment.to.first.c_str(ctx)}, + {"port", segment.to.second.c_str(ctx)}, + {"loc", Json::array({toLoc.x, toLoc.y})} + }); + + auto segmentJson = Json::object({ + {"delay", ctx->getDelayNS(segment.delay)}, + {"from", fromJson}, + {"to", toJson}, + }); + + if (segment.type == CriticalPath::Segment::Type::LOGIC) { + segmentJson["type"] = "logic"; + } + else if (segment.type == CriticalPath::Segment::Type::ROUTING) { + segmentJson["type"] = "routing"; + segmentJson["net"] = segment.net.c_str(ctx); + segmentJson["budget"] = ctx->getDelayNS(segment.budget); + } + + pathJson.push_back(segmentJson); + } + + return pathJson; + }; + + auto critPathsJson = Json::array(); + + // Critical paths + for (auto &report : ctx->timing_result.clock_paths) { + + critPathsJson.push_back(Json::object({ + {"from", clock_event_name(ctx, report.second.clock_pair.start)}, + {"to", clock_event_name(ctx, report.second.clock_pair.end)}, + {"path", report_critical_path(report.second)} + })); + } + + // Cross-domain paths + for (auto &report : ctx->timing_result.xclock_paths) { + critPathsJson.push_back(Json::object({ + {"from", clock_event_name(ctx, report.clock_pair.start)}, + {"to", clock_event_name(ctx, report.clock_pair.end)}, + {"path", report_critical_path(report)} + })); + } + + return critPathsJson; +} + +static Json::array report_detailed_net_timings (const Context* ctx) { + auto detailedNetTimingsJson = Json::array(); + + // Detailed per-net timing analysis + for (const auto& it : ctx->timing_result.detailed_net_timings) { + + const NetInfo* net = ctx->nets.at(it.first).get(); + ClockEvent start = it.second[0].clock_pair.start; + + Json::array endpointsJson; + for (const auto& sink_timing : it.second) { + + // FIXME: Is it possible that there are multiple different start + // events for a single net? It has a single driver + NPNR_ASSERT(sink_timing.clock_pair.start == start); + + auto endpointJson = Json::object({ + {"cell", sink_timing.cell_port.first.c_str(ctx)}, + {"port", sink_timing.cell_port.second.c_str(ctx)}, + {"event", clock_event_name(ctx, sink_timing.clock_pair.end)}, + {"delay", ctx->getDelayNS(sink_timing.delay)}, + {"budget", ctx->getDelayNS(sink_timing.budget)} + }); + endpointsJson.push_back(endpointJson); + } + + auto netTimingJson = Json::object({ + {"net", net->name.c_str(ctx)}, + {"driver", net->driver.cell->name.c_str(ctx)}, + {"port", net->driver.port.c_str(ctx)}, + {"event", clock_event_name(ctx, start)}, + {"endpoints", endpointsJson} + }); + + detailedNetTimingsJson.push_back(netTimingJson); + } + + return detailedNetTimingsJson; +} + void Context::writeReport(std::ostream &out) const { auto util = get_utilization(this); @@ -60,6 +181,8 @@ void Context::writeReport(std::ostream &out) const out << Json(Json::object{ {"utilization", util_json}, {"fmax", fmax_json}, + {"critical_paths", report_critical_paths(this)}, + {"detailed_net_timings", report_detailed_net_timings(this)} }) .dump() << std::endl; diff --git a/common/router1.cc b/common/router1.cc index d62e776a..c8585b0b 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 /* write_report */); + true /* warn_on_failure */, true /* update_results */); return true; } catch (log_execution_error_exception) { diff --git a/common/timing.cc b/common/timing.cc index 8c4002d0..369b6579 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -26,12 +26,9 @@ #include #include "log.h" #include "util.h" -#include "json11.hpp" NEXTPNR_NAMESPACE_BEGIN -using namespace json11; - void TimingAnalyser::setup() { init_ports(); @@ -609,70 +606,19 @@ PortInfo &TimingAnalyser::port_info(const CellPortKey &key) { return ctx->cells. /** LEGACY CODE BEGIN **/ -namespace { -struct ClockEvent -{ - IdString clock; - ClockEdge edge; - - bool operator==(const ClockEvent &other) const { return clock == other.clock && edge == other.edge; } - unsigned int hash() const { return mkhash(clock.hash(), int(edge)); } -}; - -struct ClockPair -{ - ClockEvent start, end; - - bool operator==(const ClockPair &other) const { return start == other.start && end == other.end; } - unsigned int hash() const { return mkhash(start.hash(), end.hash()); } -}; -} // namespace - typedef std::vector PortRefVector; typedef std::map DelayFrequency; -struct CriticalPath +struct CriticalPathData { PortRefVector ports; delay_t path_delay; delay_t path_period; }; -typedef dict CriticalPathMap; +typedef dict CriticalPathDataMap; -struct CriticalPathSegment -{ - enum class Type { - LOGIC, - ROUTING - }; - - std::pair from; - std::pair to; - Type type; - const NetInfo* net; - delay_t delay; - delay_t budget; -}; -typedef std::vector CriticalPathSegments; - -struct CriticalPathReport -{ - ClockPair clock_pair; - CriticalPathSegments segments; - delay_t period; -}; -typedef dict CriticalPathReportMap; - -struct NetSinkTiming -{ - ClockPair clock_pair; - PortRef sink; - delay_t delay; - delay_t budget; -}; - -typedef dict, hash_ptr_ops> DetailedNetTimings; +typedef dict> DetailedNetTimings; struct Timing { @@ -680,7 +626,7 @@ struct Timing bool net_delays; bool update; delay_t min_slack; - CriticalPathMap *crit_path; + CriticalPathDataMap *crit_path; DelayFrequency *slack_histogram; DetailedNetTimings *detailed_net_timings; IdString async_clock; @@ -697,7 +643,7 @@ struct Timing dict arrival_time; }; - Timing(Context *ctx, bool net_delays, bool update, CriticalPathMap *crit_path = nullptr, + Timing(Context *ctx, bool net_delays, bool update, CriticalPathDataMap *crit_path = nullptr, DelayFrequency *slack_histogram = nullptr, DetailedNetTimings *detailed_net_timings = nullptr) : ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->setting("target_freq")), @@ -992,11 +938,11 @@ struct Timing if (detailed_net_timings) { NetSinkTiming sink_timing; sink_timing.clock_pair = clockPair; - sink_timing.sink = usr; + sink_timing.cell_port = std::make_pair(usr.cell->name, usr.port); sink_timing.delay = endpoint_arrival; sink_timing.budget = period; - (*detailed_net_timings)[net].push_back(sink_timing); + (*detailed_net_timings)[net->name].push_back(sink_timing); } if (crit_path) { @@ -1162,17 +1108,9 @@ void assign_budget(Context *ctx, bool quiet) log_info("Checksum: 0x%08x\n", ctx->checksum()); } -void write_timing_report( - Context* ctx, - const std::string& file_name, - const std::map clock_reports, - const std::vector xclock_reports, - const DetailedNetTimings& detailed_net_timings -); +CriticalPath build_critical_path_report(Context* ctx, ClockPair &clocks, const PortRefVector &crit_path) { -CriticalPathReport build_critical_path_report(Context* ctx, ClockPair &clocks, const PortRefVector &crit_path) { - - CriticalPathReport report; + CriticalPath report; report.clock_pair = clocks; auto &front = crit_path.front(); @@ -1217,24 +1155,24 @@ CriticalPathReport build_critical_path_report(Context* ctx, ClockPair &clocks, c ctx->getCellDelay(driver_cell, last_port, driver.port, comb_delay); } - CriticalPathSegment seg_logic; - seg_logic.type = CriticalPathSegment::Type::LOGIC; + CriticalPath::Segment seg_logic; + seg_logic.type = CriticalPath::Segment::Type::LOGIC; seg_logic.delay = comb_delay.maxDelay(); seg_logic.budget = 0; - seg_logic.from = std::make_pair(const_cast(last_cell), last_port); - seg_logic.to = std::make_pair(driver_cell, driver.port); - seg_logic.net = nullptr; + seg_logic.from = std::make_pair(last_cell->name, last_port); + seg_logic.to = std::make_pair(driver_cell->name, driver.port); + seg_logic.net = IdString(); report.segments.push_back(seg_logic); auto net_delay = ctx->getNetinfoRouteDelay(net, *sink); - CriticalPathSegment seg_route; - seg_route.type = CriticalPathSegment::Type::ROUTING; + CriticalPath::Segment seg_route; + seg_route.type = CriticalPath::Segment::Type::ROUTING; seg_route.delay = net_delay; seg_route.budget = sink->budget; - seg_route.from = std::make_pair(driver_cell, driver.port); - seg_route.to = std::make_pair(sink_cell, sink->port); - seg_route.net = net; + seg_route.from = std::make_pair(driver_cell->name, driver.port); + seg_route.to = std::make_pair(sink_cell->name, sink->port); + seg_route.net = net->name; report.segments.push_back(seg_route); last_cell = sink_cell; @@ -1247,20 +1185,20 @@ CriticalPathReport build_critical_path_report(Context* ctx, ClockPair &clocks, c auto sinkClockInfo = ctx->getPortClockingInfo(crit_path.back()->cell, crit_path.back()->port, 0); delay_t setup = sinkClockInfo.setup.maxDelay(); - CriticalPathSegment seg_logic; - seg_logic.type = CriticalPathSegment::Type::LOGIC; + CriticalPath::Segment seg_logic; + seg_logic.type = CriticalPath::Segment::Type::LOGIC; seg_logic.delay = setup; seg_logic.budget = 0; - seg_logic.from = std::make_pair(const_cast(last_cell), last_port); + seg_logic.from = std::make_pair(last_cell->name, last_port); seg_logic.to = seg_logic.from; - seg_logic.net = nullptr; + seg_logic.net = IdString(); report.segments.push_back(seg_logic); } return report; } -void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path, bool warn_on_failure, bool write_report) +void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path, bool warn_on_failure, bool update_results) { auto format_event = [ctx](const ClockEvent &e, int field_width = 0) { std::string value; @@ -1273,20 +1211,19 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p return value; }; - CriticalPathMap crit_paths; + CriticalPathDataMap 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, write_report ? &detailed_net_timings : nullptr); + print_histogram ? &slack_histogram : nullptr, update_results ? &detailed_net_timings : nullptr); timing.walk_paths(); - bool report_critical_paths = print_path || print_fmax || write_report; + bool report_critical_paths = print_path || print_fmax || update_results; - std::map clock_reports; - std::vector xclock_reports; - - std::map clock_fmax; + dict clock_reports; + std::vector xclock_reports; + dict clock_fmax; std::set empty_clocks; // set of clocks with no interior paths if (report_critical_paths) { @@ -1308,8 +1245,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p Fmax = 1000 / ctx->getDelayNS(path.second.path_delay); else Fmax = 500 / ctx->getDelayNS(path.second.path_delay); - if (!clock_fmax.count(a.clock) || Fmax < clock_fmax.at(a.clock)) { - clock_fmax[a.clock] = Fmax; + if (!clock_fmax.count(a.clock) || Fmax < clock_fmax.at(a.clock).achieved) { + clock_fmax[a.clock].achieved = Fmax; + clock_fmax[a.clock].constraint = 0.0f; // Will be filled later clock_reports[a.clock] = build_critical_path_report(ctx, path.first, path.second.ports); clock_reports[a.clock].period = path.second.path_period; } @@ -1330,7 +1268,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p log_info("No Fmax available; no interior timing paths found in design.\n"); } - std::sort(xclock_reports.begin(), xclock_reports.end(), [ctx](const CriticalPathReport &ra, const CriticalPathReport &rb) { + std::sort(xclock_reports.begin(), xclock_reports.end(), [ctx](const CriticalPath &ra, const CriticalPath &rb) { const auto& a = ra.clock_pair; const auto& b = rb.clock_pair; @@ -1351,26 +1289,45 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p return true; return false; }); + + for (auto &clock : clock_reports) { + float target = ctx->setting("target_freq") / 1e6; + if (ctx->nets.at(clock.first)->clkconstr) + target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay()); + clock_fmax[clock.first].constraint = target; + } + } + + // Update timing results in the context + if (update_results) { + auto& results = ctx->timing_result; + + results.clock_fmax = std::move(clock_fmax); + results.clock_paths = std::move(clock_reports); + results.xclock_paths = std::move(xclock_reports); + + results.detailed_net_timings = std::move(detailed_net_timings); } // Print critical paths if (print_path) { - auto print_path_report = [ctx](const CriticalPathReport& report) { + // A helper function for reporting one critical path + auto print_path_report = [ctx](const CriticalPath& path) { delay_t total = 0, logic_total = 0, route_total = 0; log_info("curr total\n"); - for (const auto& segment : report.segments) { + for (const auto& segment : path.segments) { total += segment.delay; - if (segment.type == CriticalPathSegment::Type::LOGIC) { + if (segment.type == CriticalPath::Segment::Type::LOGIC) { logic_total += segment.delay; if (segment.from != segment.to) { log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(segment.delay), ctx->getDelayNS(total), - segment.from.first->name.c_str(ctx), + segment.from.first.c_str(ctx), segment.from.second.c_str(ctx) ); } @@ -1378,43 +1335,46 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p log_info("%4.1f %4.1f Setup %s.%s\n", ctx->getDelayNS(segment.delay), ctx->getDelayNS(total), - segment.to.first->name.c_str(ctx), + segment.to.first.c_str(ctx), segment.to.second.c_str(ctx) ); } } - if (segment.type == CriticalPathSegment::Type::ROUTING) { + if (segment.type == CriticalPath::Segment::Type::ROUTING) { route_total += segment.delay; - auto driver_loc = ctx->getBelLocation(segment.from.first->bel); - auto sink_loc = ctx->getBelLocation(segment.to.first->bel); + const auto& driver = ctx->cells.at(segment.from.first); + const auto& sink = ctx->cells.at(segment.to.first); + + auto driver_loc = ctx->getBelLocation(driver->bel); + auto sink_loc = ctx->getBelLocation(sink->bel); log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(segment.delay), ctx->getDelayNS(total), - segment.net->name.c_str(ctx), + segment.net.c_str(ctx), ctx->getDelayNS(segment.budget), driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y ); log_info(" Sink %s.%s\n", - segment.to.first->name.c_str(ctx), + segment.to.first.c_str(ctx), segment.to.second.c_str(ctx) ); if (ctx->verbose) { - PortRef sink; - sink.cell = segment.to.first; - sink.port = segment.to.second; - sink.budget = segment.budget; + PortRef sink_ref; + sink_ref.cell = sink.get(); + sink_ref.port = segment.to.second; + sink_ref.budget = segment.budget; - auto net = segment.net; + const NetInfo* net = ctx->nets.at(segment.net).get(); auto driver_wire = ctx->getNetinfoSourceWire(net); - auto sink_wire = ctx->getNetinfoSinkWire(net, sink, 0); + auto sink_wire = ctx->getNetinfoSinkWire(net, sink_ref, 0); log_info(" prediction: %f ns estimate: %f ns\n", - ctx->getDelayNS(ctx->predictDelay(net, sink)), + ctx->getDelayNS(ctx->predictDelay(net, sink_ref)), ctx->getDelayNS(ctx->estimateDelay(driver_wire, sink_wire))); auto cursor = sink_wire; delay_t delay; @@ -1437,6 +1397,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p log_info("%.1f ns logic, %.1f ns routing\n", ctx->getDelayNS(logic_total), ctx->getDelayNS(route_total)); }; + // Single domain paths for (auto &clock : clock_reports) { log_break(); std::string start = @@ -1449,6 +1410,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p print_path_report(report); } + // Cross-domain paths for (auto &report : xclock_reports) { log_break(); std::string start = format_event(report.clock_pair.start); @@ -1460,31 +1422,28 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p if (print_fmax) { log_break(); + unsigned max_width = 0; - auto &result = ctx->timing_result; - result.clock_fmax.clear(); for (auto &clock : clock_reports) max_width = std::max(max_width, clock.first.str(ctx).size()); + for (auto &clock : clock_reports) { const auto &clock_name = clock.first.str(ctx); const int width = max_width - clock_name.size(); - float target = ctx->setting("target_freq") / 1e6; - if (ctx->nets.at(clock.first)->clkconstr) - target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay()); - result.clock_fmax[clock.first].achieved = clock_fmax[clock.first]; - result.clock_fmax[clock.first].constraint = target; + float fmax = clock_fmax[clock.first].achieved; + float target = clock_fmax[clock.first].constraint; + bool passed = target < fmax; - bool passed = target < clock_fmax[clock.first]; if (!warn_on_failure || passed) log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "", - clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target); + clock_name.c_str(), fmax, passed ? "PASS" : "FAIL", target); else if (bool_or_default(ctx->settings, ctx->id("timing/allowFail"), false)) log_warning("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "", - clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target); + clock_name.c_str(), fmax, passed ? "PASS" : "FAIL", target); else log_nonfatal_error("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "", - clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target); + clock_name.c_str(), fmax, passed ? "PASS" : "FAIL", target); } for (auto &eclock : empty_clocks) { if (eclock != ctx->id("$async$")) @@ -1540,154 +1499,6 @@ 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_break(); - log_info("Writing timing analysis report...\n"); - write_timing_report(ctx, report_file, - clock_reports, - xclock_reports, - detailed_net_timings); - } -} - -void write_timing_report( - Context* ctx, - const std::string& file_name, - const std::map clock_reports, - const std::vector xclock_reports, - const DetailedNetTimings& detailed_net_timings -) -{ - auto event_name = [ctx](const ClockEvent &e) { - std::string value; - if (e.clock == ctx->id("$async$")) - value = std::string(""); - else - value = (e.edge == FALLING_EDGE ? std::string("negedge ") : - std::string("posedge ")) + e.clock.str(ctx); - return value; - }; - - auto report_critical_path = [ctx](const CriticalPathReport& report) { - Json::array pathJson; - - for (const auto& segment : report.segments) { - - auto fromLoc = ctx->getBelLocation(segment.from.first->bel); - auto toLoc = ctx->getBelLocation(segment.to.first->bel); - - auto fromJson = Json::object({ - {"cell", segment.from.first->name.c_str(ctx)}, - {"port", segment.from.second.c_str(ctx)}, - {"loc", Json::array({fromLoc.x, fromLoc.y})} - }); - - auto toJson = Json::object({ - {"cell", segment.to.first->name.c_str(ctx)}, - {"port", segment.to.second.c_str(ctx)}, - {"loc", Json::array({toLoc.x, toLoc.y})} - }); - - auto segmentJson = Json::object({ - {"delay", ctx->getDelayNS(segment.delay)}, - {"from", fromJson}, - {"to", toJson}, - }); - - if (segment.type == CriticalPathSegment::Type::LOGIC) { - segmentJson["type"] = "logic"; - } - else if (segment.type == CriticalPathSegment::Type::ROUTING) { - segmentJson["type"] = "routing"; - segmentJson["net"] = segment.net->name.c_str(ctx); - segmentJson["budget"] = ctx->getDelayNS(segment.budget); - } - - pathJson.push_back(segmentJson); - } - - return pathJson; - }; - - // Open the file - FILE* fp = fopen(file_name.c_str(), "w"); - NPNR_ASSERT(fp != nullptr); - - // Critical paths - auto critPathsJson = Json::array(); - for (auto &report : clock_reports) { - - critPathsJson.push_back(Json::object({ - {"from", event_name(report.second.clock_pair.start)}, - {"to", event_name(report.second.clock_pair.end)}, - {"path", report_critical_path(report.second)} - })); - } - - // Cross-domain paths - for (auto &report : xclock_reports) { - critPathsJson.push_back(Json::object({ - {"from", event_name(report.clock_pair.start)}, - {"to", event_name(report.clock_pair.end)}, - {"path", report_critical_path(report)} - })); - } - - // Detailed per-net timing analysis - auto detailedNetTimingsJson = Json::array(); - for (const auto& it : detailed_net_timings) { - const NetInfo* net = it.first; - - ClockEvent start = it.second[0].clock_pair.start; - - Json::array endpointsJson; - for (const auto& sink_timing : it.second) { - - // FIXME: Is it possible that there are multiple different start - // events for a single net? It has a single driver - NPNR_ASSERT(sink_timing.clock_pair.start == start); - - auto endpointJson = Json::object({ - {"cell", sink_timing.sink.cell->name.c_str(ctx)}, - {"port", sink_timing.sink.port.c_str(ctx)}, - {"event", event_name(sink_timing.clock_pair.end)}, - {"delay", ctx->getDelayNS(sink_timing.delay)}, - {"budget", ctx->getDelayNS(sink_timing.budget)} - }); - endpointsJson.push_back(endpointJson); - } - - auto netTimingJson = Json::object({ - {"net", net->name.c_str(ctx)}, - {"driver", net->driver.cell->name.c_str(ctx)}, - {"port", net->driver.port.c_str(ctx)}, - {"event", event_name(start)}, - {"endpoints", endpointsJson} - }); - detailedNetTimingsJson.push_back(netTimingJson); - } - - auto analysisJson = Json::object({ - {"critical_paths", Json(critPathsJson)}, - {"detailed_net_timings", Json(detailedNetTimingsJson)} - }); - - // Assemble and serialize the final Json - auto jsonRoot = Json(Json::object({ - {"timing_analysis", Json(analysisJson)} - })); - - fputs(jsonRoot.dump().c_str(), fp); - fclose(fp); } NEXTPNR_NAMESPACE_END diff --git a/common/timing.h b/common/timing.h index 4645077a..b34fd636 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 write_report = false); + bool warn_on_failure = false, bool update_results = false); NEXTPNR_NAMESPACE_END From a9df3b425fb8a2e6b9054a0abbf033942eabc602 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 28 Sep 2021 17:24:43 +0200 Subject: [PATCH 06/10] Added description of the JSON report structure. Signed-off-by: Maciej Kurc --- common/report.cc | 74 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/common/report.cc b/common/report.cc index a595777a..5330583e 100644 --- a/common/report.cc +++ b/common/report.cc @@ -101,7 +101,7 @@ static Json::array report_critical_paths (const Context* ctx) { // Critical paths for (auto &report : ctx->timing_result.clock_paths) { - + critPathsJson.push_back(Json::object({ {"from", clock_event_name(ctx, report.second.clock_pair.start)}, {"to", clock_event_name(ctx, report.second.clock_pair.end)}, @@ -161,6 +161,78 @@ static Json::array report_detailed_net_timings (const Context* ctx) { return detailedNetTimingsJson; } +/* +Report JSON structure: + +{ + "utilization": { + : { + "available": , + "used": + }, + ... + }, + "fmax" { + : { + "achieved": , + "constraint": + }, + ... + }, + "critical_paths": [ + { + "from": , + "to": , + "path": [ + { + "from": { + "cell": + "port": + "loc": [ + , + + ] + }, + "to": { + "cell": + "port": + "loc": [ + , + + ] + }, + "type": , + "net": , + "delay": , + "budget": , + } + ... + ] + }, + ... + ], + "detailed_net_timings": [ + { + "driver": , + "port": , + "event": , + "net": , + "endpoints": [ + { + "cell": , + "port": , + "event": , + "delay": , + "budget": , + } + ... + ] + } + ... + ] +} +*/ + void Context::writeReport(std::ostream &out) const { auto util = get_utilization(this); From 9018782eaaec23c57987266629b23484cfd9b984 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 28 Sep 2021 17:38:12 +0200 Subject: [PATCH 07/10] Added a commandline option controlled writeout of per-net timing details Signed-off-by: Maciej Kurc --- common/command.cc | 6 ++++++ common/context.h | 2 ++ common/report.cc | 20 ++++++++++++-------- common/timing.cc | 3 ++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/common/command.cc b/common/command.cc index 954a3442..006a6217 100644 --- a/common/command.cc +++ b/common/command.cc @@ -179,6 +179,8 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("report", po::value(), "write timing and utilization report in JSON format to file"); + general.add_options()("detailed-timing-report", + "Append detailed net timing data to the JSON report"); general.add_options()("placed-svg", po::value(), "write render of placement to SVG file"); general.add_options()("routed-svg", po::value(), "write render of routing to SVG file"); @@ -328,6 +330,10 @@ void CommandHandler::setupContext(Context *ctx) ctx->settings[ctx->id("placerHeap/criticalityExponent")] = std::to_string(2); if (ctx->settings.find(ctx->id("placerHeap/timingWeight")) == ctx->settings.end()) ctx->settings[ctx->id("placerHeap/timingWeight")] = std::to_string(10); + + if (vm.count("detailed-timing-report")) { + ctx->detailed_timing_report = true; + } } int CommandHandler::executeMain(std::unique_ptr ctx) diff --git a/common/context.h b/common/context.h index 1175caee..6adbbdb5 100644 --- a/common/context.h +++ b/common/context.h @@ -36,6 +36,8 @@ struct Context : Arch, DeterministicRNG // Should we disable printing of the location of nets in the critical path? bool disable_critical_path_source_print = false; + // True when detailed per-net timing is to be stored / reported + bool detailed_timing_report = false; ArchArgs arch_args; diff --git a/common/report.cc b/common/report.cc index 5330583e..f2926c86 100644 --- a/common/report.cc +++ b/common/report.cc @@ -250,14 +250,18 @@ void Context::writeReport(std::ostream &out) const {"constraint", kv.second.constraint}, }; } - out << Json(Json::object{ - {"utilization", util_json}, - {"fmax", fmax_json}, - {"critical_paths", report_critical_paths(this)}, - {"detailed_net_timings", report_detailed_net_timings(this)} - }) - .dump() - << std::endl; + + Json::object jsonRoot{ + {"utilization", util_json}, + {"fmax", fmax_json}, + {"critical_paths", report_critical_paths(this)} + }; + + if (detailed_timing_report) { + jsonRoot["detailed_net_timings"] = report_detailed_net_timings(this); + } + + out << Json(jsonRoot).dump() << std::endl; } NEXTPNR_NAMESPACE_END diff --git a/common/timing.cc b/common/timing.cc index 369b6579..76bcfd64 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -1216,7 +1216,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p DetailedNetTimings detailed_net_timings; Timing timing(ctx, true /* net_delays */, false /* update */, (print_path || print_fmax) ? &crit_paths : nullptr, - print_histogram ? &slack_histogram : nullptr, update_results ? &detailed_net_timings : nullptr); + print_histogram ? &slack_histogram : nullptr, + (update_results && ctx->detailed_timing_report) ? &detailed_net_timings : nullptr); timing.walk_paths(); bool report_critical_paths = print_path || print_fmax || update_results; From 1ed692aca90a9a103fc5e15aeeae31b515072035 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 28 Sep 2021 18:01:30 +0200 Subject: [PATCH 08/10] Shifted moving of data containers after printing Signed-off-by: Maciej Kurc --- common/timing.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 76bcfd64..b6d9b325 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -1299,17 +1299,6 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p } } - // Update timing results in the context - if (update_results) { - auto& results = ctx->timing_result; - - results.clock_fmax = std::move(clock_fmax); - results.clock_paths = std::move(clock_reports); - results.xclock_paths = std::move(xclock_reports); - - results.detailed_net_timings = std::move(detailed_net_timings); - } - // Print critical paths if (print_path) { @@ -1500,6 +1489,17 @@ 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 ? '+' : ' '); } + + // Update timing results in the context + if (update_results) { + auto& results = ctx->timing_result; + + results.clock_fmax = std::move(clock_fmax); + results.clock_paths = std::move(clock_reports); + results.xclock_paths = std::move(xclock_reports); + + results.detailed_net_timings = std::move(detailed_net_timings); + } } NEXTPNR_NAMESPACE_END From 76f5874ffcd57f69808e0df752622dfe13759538 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Wed, 29 Sep 2021 10:16:45 +0200 Subject: [PATCH 09/10] Brought back printout of critical path source file references, added clk-to-q, source and setup segment types Signed-off-by: Maciej Kurc --- common/nextpnr_types.h | 7 ++-- common/report.cc | 13 +++++-- common/timing.cc | 82 +++++++++++++++++++++++++++++------------- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index 17261095..8b19595b 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -246,8 +246,11 @@ struct CriticalPath // Segment type enum class Type { - LOGIC, - ROUTING + CLK_TO_Q, // Clock-to-Q delay + SOURCE, // Delayless source + LOGIC, // Combinational logic delay + ROUTING, // Routing delay + SETUP // Setup time in sink }; // Type diff --git a/common/report.cc b/common/report.cc index f2926c86..1338ade5 100644 --- a/common/report.cc +++ b/common/report.cc @@ -82,9 +82,18 @@ static Json::array report_critical_paths (const Context* ctx) { {"to", toJson}, }); - if (segment.type == CriticalPath::Segment::Type::LOGIC) { + if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q) { + segmentJson["type"] = "clk-to-q"; + } + else if (segment.type == CriticalPath::Segment::Type::SOURCE) { + segmentJson["type"] = "source"; + } + else if (segment.type == CriticalPath::Segment::Type::LOGIC) { segmentJson["type"] = "logic"; } + else if (segment.type == CriticalPath::Segment::Type::SETUP) { + segmentJson["type"] = "setup"; + } else if (segment.type == CriticalPath::Segment::Type::ROUTING) { segmentJson["type"] = "routing"; segmentJson["net"] = segment.net.c_str(ctx); @@ -201,7 +210,7 @@ Report JSON structure: ] }, - "type": , + "type": , "net": , "delay": , "budget": , diff --git a/common/timing.cc b/common/timing.cc index b6d9b325..41dd358b 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -1143,20 +1143,24 @@ CriticalPath build_critical_path_report(Context* ctx, ClockPair &clocks, const P auto net = port.net; auto &driver = net->driver; auto driver_cell = driver.cell; + + CriticalPath::Segment seg_logic; + DelayQuad comb_delay; if (clock_start != -1) { auto clockInfo = ctx->getPortClockingInfo(driver_cell, driver.port, clock_start); comb_delay = clockInfo.clockToQ; clock_start = -1; + seg_logic.type = CriticalPath::Segment::Type::CLK_TO_Q; } else if (last_port == driver.port) { // Case where we start with a STARTPOINT etc comb_delay = DelayQuad(0); + seg_logic.type = CriticalPath::Segment::Type::SOURCE; } else { ctx->getCellDelay(driver_cell, last_port, driver.port, comb_delay); + seg_logic.type = CriticalPath::Segment::Type::LOGIC; } - CriticalPath::Segment seg_logic; - seg_logic.type = CriticalPath::Segment::Type::LOGIC; seg_logic.delay = comb_delay.maxDelay(); seg_logic.budget = 0; seg_logic.from = std::make_pair(last_cell->name, last_port); @@ -1186,7 +1190,7 @@ CriticalPath build_critical_path_report(Context* ctx, ClockPair &clocks, const P delay_t setup = sinkClockInfo.setup.maxDelay(); CriticalPath::Segment seg_logic; - seg_logic.type = CriticalPath::Segment::Type::LOGIC; + seg_logic.type = CriticalPath::Segment::Type::SETUP; seg_logic.delay = setup; seg_logic.budget = 0; seg_logic.from = std::make_pair(last_cell->name, last_port); @@ -1302,6 +1306,33 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p // Print critical paths if (print_path) { + static auto print_net_source = [ctx](const NetInfo *net) { + // Check if this net is annotated with a source list + auto sources = net->attrs.find(ctx->id("src")); + if (sources == net->attrs.end()) { + // No sources for this net, can't print anything + return; + } + + // Sources are separated by pipe characters. + // There is no guaranteed ordering on sources, so we just print all + auto sourcelist = sources->second.as_string(); + std::vector source_entries; + size_t current = 0, prev = 0; + while ((current = sourcelist.find("|", prev)) != std::string::npos) { + source_entries.emplace_back(sourcelist.substr(prev, current - prev)); + prev = current + 1; + } + // Ensure we emplace the final entry + source_entries.emplace_back(sourcelist.substr(prev, current - prev)); + + // Iterate and print our source list at the correct indentation level + log_info(" Defined in:\n"); + for (auto entry : source_entries) { + log_info(" %s\n", entry.c_str()); + } + }; + // A helper function for reporting one critical path auto print_path_report = [ctx](const CriticalPath& path) { delay_t total = 0, logic_total = 0, route_total = 0; @@ -1311,27 +1342,26 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p total += segment.delay; - if (segment.type == CriticalPath::Segment::Type::LOGIC) { + if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q || + segment.type == CriticalPath::Segment::Type::SOURCE || + segment.type == CriticalPath::Segment::Type::LOGIC || + segment.type == CriticalPath::Segment::Type::SETUP) + { logic_total += segment.delay; - if (segment.from != segment.to) { - log_info("%4.1f %4.1f Source %s.%s\n", - ctx->getDelayNS(segment.delay), - ctx->getDelayNS(total), - segment.from.first.c_str(ctx), - segment.from.second.c_str(ctx) - ); - } - else { - log_info("%4.1f %4.1f Setup %s.%s\n", - ctx->getDelayNS(segment.delay), - ctx->getDelayNS(total), - segment.to.first.c_str(ctx), - segment.to.second.c_str(ctx) - ); - } - } - if (segment.type == CriticalPath::Segment::Type::ROUTING) { + const std::string type_name = + (segment.type == CriticalPath::Segment::Type::SETUP) ? + "Setup" : "Source"; + + log_info("%4.1f %4.1f %s %s.%s\n", + ctx->getDelayNS(segment.delay), + ctx->getDelayNS(total), + type_name.c_str(), + segment.to.first.c_str(ctx), + segment.to.second.c_str(ctx) + ); + } + else if (segment.type == CriticalPath::Segment::Type::ROUTING) { route_total += segment.delay; const auto& driver = ctx->cells.at(segment.from.first); @@ -1352,6 +1382,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p segment.to.second.c_str(ctx) ); + const NetInfo* net = ctx->nets.at(segment.net).get(); + if (ctx->verbose) { PortRef sink_ref; @@ -1359,8 +1391,6 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p sink_ref.port = segment.to.second; sink_ref.budget = segment.budget; - const NetInfo* net = ctx->nets.at(segment.net).get(); - auto driver_wire = ctx->getNetinfoSourceWire(net); auto sink_wire = ctx->getNetinfoSinkWire(net, sink_ref, 0); log_info(" prediction: %f ns estimate: %f ns\n", @@ -1382,6 +1412,10 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p cursor = ctx->getPipSrcWire(pip); } } + + if (!ctx->disable_critical_path_source_print) { + print_net_source(net); + } } } log_info("%.1f ns logic, %.1f ns routing\n", ctx->getDelayNS(logic_total), ctx->getDelayNS(route_total)); From 1db3a87c62acc79de0b3c7bd8c4c155c61c864ee Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Wed, 29 Sep 2021 14:59:09 +0200 Subject: [PATCH 10/10] Code formatting Signed-off-by: Maciej Kurc --- common/command.cc | 3 +- common/nextpnr_types.h | 16 ++++--- common/report.cc | 105 +++++++++++++++++------------------------ common/timing.cc | 80 +++++++++++++------------------ 4 files changed, 86 insertions(+), 118 deletions(-) diff --git a/common/command.cc b/common/command.cc index 006a6217..38de8344 100644 --- a/common/command.cc +++ b/common/command.cc @@ -179,8 +179,7 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("report", po::value(), "write timing and utilization report in JSON format to file"); - general.add_options()("detailed-timing-report", - "Append detailed net timing data to the JSON report"); + general.add_options()("detailed-timing-report", "Append detailed net timing data to the JSON report"); general.add_options()("placed-svg", po::value(), "write render of placement to SVG file"); general.add_options()("routed-svg", po::value(), "write render of routing to SVG file"); diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index 8b19595b..6b594d64 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -242,15 +242,17 @@ struct ClockPair struct CriticalPath { - struct Segment { + struct Segment + { // Segment type - enum class Type { - CLK_TO_Q, // Clock-to-Q delay - SOURCE, // Delayless source - LOGIC, // Combinational logic delay - ROUTING, // Routing delay - SETUP // Setup time in sink + enum class Type + { + CLK_TO_Q, // Clock-to-Q delay + SOURCE, // Delayless source + LOGIC, // Combinational logic delay + ROUTING, // Routing delay + SETUP // Setup time in sink }; // Type diff --git a/common/report.cc b/common/report.cc index 1338ade5..98ff14fb 100644 --- a/common/report.cc +++ b/common/report.cc @@ -41,60 +41,53 @@ dict> get_utilization(const Context *ctx) } } // namespace -static std::string clock_event_name (const Context* ctx, const ClockEvent& e) { +static std::string clock_event_name(const Context *ctx, const ClockEvent &e) +{ std::string value; if (e.clock == ctx->id("$async$")) value = std::string(""); else - value = (e.edge == FALLING_EDGE ? std::string("negedge ") : - std::string("posedge ")) + e.clock.str(ctx); + value = (e.edge == FALLING_EDGE ? std::string("negedge ") : std::string("posedge ")) + e.clock.str(ctx); return value; }; -static Json::array report_critical_paths (const Context* ctx) { +static Json::array report_critical_paths(const Context *ctx) +{ - auto report_critical_path = [ctx](const CriticalPath& report) { + auto report_critical_path = [ctx](const CriticalPath &report) { Json::array pathJson; - for (const auto& segment : report.segments) { + for (const auto &segment : report.segments) { - const auto& driver = ctx->cells.at(segment.from.first); - const auto& sink = ctx->cells.at(segment.to.first); + const auto &driver = ctx->cells.at(segment.from.first); + const auto &sink = ctx->cells.at(segment.to.first); auto fromLoc = ctx->getBelLocation(driver->bel); auto toLoc = ctx->getBelLocation(sink->bel); - auto fromJson = Json::object({ - {"cell", segment.from.first.c_str(ctx)}, - {"port", segment.from.second.c_str(ctx)}, - {"loc", Json::array({fromLoc.x, fromLoc.y})} - }); + auto fromJson = Json::object({{"cell", segment.from.first.c_str(ctx)}, + {"port", segment.from.second.c_str(ctx)}, + {"loc", Json::array({fromLoc.x, fromLoc.y})}}); - auto toJson = Json::object({ - {"cell", segment.to.first.c_str(ctx)}, - {"port", segment.to.second.c_str(ctx)}, - {"loc", Json::array({toLoc.x, toLoc.y})} - }); + auto toJson = Json::object({{"cell", segment.to.first.c_str(ctx)}, + {"port", segment.to.second.c_str(ctx)}, + {"loc", Json::array({toLoc.x, toLoc.y})}}); auto segmentJson = Json::object({ - {"delay", ctx->getDelayNS(segment.delay)}, - {"from", fromJson}, - {"to", toJson}, + {"delay", ctx->getDelayNS(segment.delay)}, + {"from", fromJson}, + {"to", toJson}, }); if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q) { segmentJson["type"] = "clk-to-q"; - } - else if (segment.type == CriticalPath::Segment::Type::SOURCE) { + } else if (segment.type == CriticalPath::Segment::Type::SOURCE) { segmentJson["type"] = "source"; - } - else if (segment.type == CriticalPath::Segment::Type::LOGIC) { + } else if (segment.type == CriticalPath::Segment::Type::LOGIC) { segmentJson["type"] = "logic"; - } - else if (segment.type == CriticalPath::Segment::Type::SETUP) { + } else if (segment.type == CriticalPath::Segment::Type::SETUP) { segmentJson["type"] = "setup"; - } - else if (segment.type == CriticalPath::Segment::Type::ROUTING) { + } else if (segment.type == CriticalPath::Segment::Type::ROUTING) { segmentJson["type"] = "routing"; segmentJson["net"] = segment.net.c_str(ctx); segmentJson["budget"] = ctx->getDelayNS(segment.budget); @@ -111,58 +104,51 @@ static Json::array report_critical_paths (const Context* ctx) { // Critical paths for (auto &report : ctx->timing_result.clock_paths) { - critPathsJson.push_back(Json::object({ - {"from", clock_event_name(ctx, report.second.clock_pair.start)}, - {"to", clock_event_name(ctx, report.second.clock_pair.end)}, - {"path", report_critical_path(report.second)} - })); + critPathsJson.push_back(Json::object({{"from", clock_event_name(ctx, report.second.clock_pair.start)}, + {"to", clock_event_name(ctx, report.second.clock_pair.end)}, + {"path", report_critical_path(report.second)}})); } // Cross-domain paths for (auto &report : ctx->timing_result.xclock_paths) { - critPathsJson.push_back(Json::object({ - {"from", clock_event_name(ctx, report.clock_pair.start)}, - {"to", clock_event_name(ctx, report.clock_pair.end)}, - {"path", report_critical_path(report)} - })); + critPathsJson.push_back(Json::object({{"from", clock_event_name(ctx, report.clock_pair.start)}, + {"to", clock_event_name(ctx, report.clock_pair.end)}, + {"path", report_critical_path(report)}})); } return critPathsJson; } -static Json::array report_detailed_net_timings (const Context* ctx) { +static Json::array report_detailed_net_timings(const Context *ctx) +{ auto detailedNetTimingsJson = Json::array(); // Detailed per-net timing analysis - for (const auto& it : ctx->timing_result.detailed_net_timings) { + for (const auto &it : ctx->timing_result.detailed_net_timings) { - const NetInfo* net = ctx->nets.at(it.first).get(); + const NetInfo *net = ctx->nets.at(it.first).get(); ClockEvent start = it.second[0].clock_pair.start; Json::array endpointsJson; - for (const auto& sink_timing : it.second) { + for (const auto &sink_timing : it.second) { // FIXME: Is it possible that there are multiple different start // events for a single net? It has a single driver NPNR_ASSERT(sink_timing.clock_pair.start == start); - auto endpointJson = Json::object({ - {"cell", sink_timing.cell_port.first.c_str(ctx)}, - {"port", sink_timing.cell_port.second.c_str(ctx)}, - {"event", clock_event_name(ctx, sink_timing.clock_pair.end)}, - {"delay", ctx->getDelayNS(sink_timing.delay)}, - {"budget", ctx->getDelayNS(sink_timing.budget)} - }); + auto endpointJson = Json::object({{"cell", sink_timing.cell_port.first.c_str(ctx)}, + {"port", sink_timing.cell_port.second.c_str(ctx)}, + {"event", clock_event_name(ctx, sink_timing.clock_pair.end)}, + {"delay", ctx->getDelayNS(sink_timing.delay)}, + {"budget", ctx->getDelayNS(sink_timing.budget)}}); endpointsJson.push_back(endpointJson); } - auto netTimingJson = Json::object({ - {"net", net->name.c_str(ctx)}, - {"driver", net->driver.cell->name.c_str(ctx)}, - {"port", net->driver.port.c_str(ctx)}, - {"event", clock_event_name(ctx, start)}, - {"endpoints", endpointsJson} - }); + auto netTimingJson = Json::object({{"net", net->name.c_str(ctx)}, + {"driver", net->driver.cell->name.c_str(ctx)}, + {"port", net->driver.port.c_str(ctx)}, + {"event", clock_event_name(ctx, start)}, + {"endpoints", endpointsJson}}); detailedNetTimingsJson.push_back(netTimingJson); } @@ -261,10 +247,7 @@ void Context::writeReport(std::ostream &out) const } Json::object jsonRoot{ - {"utilization", util_json}, - {"fmax", fmax_json}, - {"critical_paths", report_critical_paths(this)} - }; + {"utilization", util_json}, {"fmax", fmax_json}, {"critical_paths", report_critical_paths(this)}}; if (detailed_timing_report) { jsonRoot["detailed_net_timings"] = report_detailed_net_timings(this); diff --git a/common/timing.cc b/common/timing.cc index 41dd358b..e305d82d 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -644,8 +644,7 @@ struct Timing }; Timing(Context *ctx, bool net_delays, bool update, CriticalPathDataMap *crit_path = nullptr, - DelayFrequency *slack_histogram = nullptr, - DetailedNetTimings *detailed_net_timings = nullptr) + DelayFrequency *slack_histogram = nullptr, DetailedNetTimings *detailed_net_timings = nullptr) : ctx(ctx), net_delays(net_delays), update(update), min_slack(1.0e12 / ctx->setting("target_freq")), crit_path(crit_path), slack_histogram(slack_histogram), detailed_net_timings(detailed_net_timings), async_clock(ctx->id("$async$")) @@ -1108,7 +1107,8 @@ void assign_budget(Context *ctx, bool quiet) log_info("Checksum: 0x%08x\n", ctx->checksum()); } -CriticalPath build_critical_path_report(Context* ctx, ClockPair &clocks, const PortRefVector &crit_path) { +CriticalPath build_critical_path_report(Context *ctx, ClockPair &clocks, const PortRefVector &crit_path) +{ CriticalPath report; report.clock_pair = clocks; @@ -1120,7 +1120,7 @@ CriticalPath build_critical_path_report(Context* ctx, ClockPair &clocks, const P int port_clocks; auto portClass = ctx->getPortTimingClass(front_driver.cell, front_driver.port, port_clocks); - const CellInfo* last_cell = front->cell; + const CellInfo *last_cell = front->cell; IdString last_port = front_driver.port; int clock_start = -1; @@ -1128,8 +1128,7 @@ CriticalPath build_critical_path_report(Context* ctx, ClockPair &clocks, const P 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 == clocks.start.clock && - clockInfo.edge == clocks.start.edge) { + if (clknet != nullptr && clknet->name == clocks.start.clock && clockInfo.edge == clocks.start.edge) { last_port = clockInfo.clock_port; clock_start = i; break; @@ -1202,7 +1201,8 @@ CriticalPath build_critical_path_report(Context* ctx, ClockPair &clocks, const P return report; } -void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path, bool warn_on_failure, bool update_results) +void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path, bool warn_on_failure, + bool update_results) { auto format_event = [ctx](const ClockEvent &e, int field_width = 0) { std::string value; @@ -1251,7 +1251,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p else Fmax = 500 / ctx->getDelayNS(path.second.path_delay); if (!clock_fmax.count(a.clock) || Fmax < clock_fmax.at(a.clock).achieved) { - clock_fmax[a.clock].achieved = Fmax; + clock_fmax[a.clock].achieved = Fmax; clock_fmax[a.clock].constraint = 0.0f; // Will be filled later clock_reports[a.clock] = build_critical_path_report(ctx, path.first, path.second.ports); clock_reports[a.clock].period = path.second.path_period; @@ -1274,9 +1274,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p } std::sort(xclock_reports.begin(), xclock_reports.end(), [ctx](const CriticalPath &ra, const CriticalPath &rb) { - - const auto& a = ra.clock_pair; - const auto& b = rb.clock_pair; + const auto &a = ra.clock_pair; + const auto &b = rb.clock_pair; if (a.start.clock.str(ctx) < b.start.clock.str(ctx)) return true; @@ -1334,55 +1333,40 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p }; // A helper function for reporting one critical path - auto print_path_report = [ctx](const CriticalPath& path) { + auto print_path_report = [ctx](const CriticalPath &path) { delay_t total = 0, logic_total = 0, route_total = 0; log_info("curr total\n"); - for (const auto& segment : path.segments) { + for (const auto &segment : path.segments) { total += segment.delay; if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q || segment.type == CriticalPath::Segment::Type::SOURCE || segment.type == CriticalPath::Segment::Type::LOGIC || - segment.type == CriticalPath::Segment::Type::SETUP) - { + segment.type == CriticalPath::Segment::Type::SETUP) { logic_total += segment.delay; const std::string type_name = - (segment.type == CriticalPath::Segment::Type::SETUP) ? - "Setup" : "Source"; + (segment.type == CriticalPath::Segment::Type::SETUP) ? "Setup" : "Source"; - log_info("%4.1f %4.1f %s %s.%s\n", - ctx->getDelayNS(segment.delay), - ctx->getDelayNS(total), - type_name.c_str(), - segment.to.first.c_str(ctx), - segment.to.second.c_str(ctx) - ); - } - else if (segment.type == CriticalPath::Segment::Type::ROUTING) { + log_info("%4.1f %4.1f %s %s.%s\n", ctx->getDelayNS(segment.delay), ctx->getDelayNS(total), + type_name.c_str(), segment.to.first.c_str(ctx), segment.to.second.c_str(ctx)); + } else if (segment.type == CriticalPath::Segment::Type::ROUTING) { route_total += segment.delay; - const auto& driver = ctx->cells.at(segment.from.first); - const auto& sink = ctx->cells.at(segment.to.first); + const auto &driver = ctx->cells.at(segment.from.first); + const auto &sink = ctx->cells.at(segment.to.first); auto driver_loc = ctx->getBelLocation(driver->bel); auto sink_loc = ctx->getBelLocation(sink->bel); - log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", - ctx->getDelayNS(segment.delay), - ctx->getDelayNS(total), - segment.net.c_str(ctx), - ctx->getDelayNS(segment.budget), - driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y - ); - log_info(" Sink %s.%s\n", - segment.to.first.c_str(ctx), - segment.to.second.c_str(ctx) - ); + log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(segment.delay), + ctx->getDelayNS(total), segment.net.c_str(ctx), ctx->getDelayNS(segment.budget), + driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y); + log_info(" Sink %s.%s\n", segment.to.first.c_str(ctx), segment.to.second.c_str(ctx)); - const NetInfo* net = ctx->nets.at(segment.net).get(); + const NetInfo *net = ctx->nets.at(segment.net).get(); if (ctx->verbose) { @@ -1424,8 +1408,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p // Single domain paths for (auto &clock : clock_reports) { log_break(); - std::string start = - clock.second.clock_pair.start.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge"); + std::string start = clock.second.clock_pair.start.edge == FALLING_EDGE ? std::string("negedge") + : std::string("posedge"); std::string end = clock.second.clock_pair.end.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge"); log_info("Critical path report for clock '%s' (%s -> %s):\n", clock.first.c_str(ctx), start.c_str(), @@ -1455,9 +1439,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p const auto &clock_name = clock.first.str(ctx); const int width = max_width - clock_name.size(); - float fmax = clock_fmax[clock.first].achieved; + float fmax = clock_fmax[clock.first].achieved; float target = clock_fmax[clock.first].constraint; - bool passed = target < fmax; + bool passed = target < fmax; if (!warn_on_failure || passed) log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "", @@ -1485,7 +1469,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p const ClockEvent &a = report.clock_pair.start; const ClockEvent &b = report.clock_pair.end; delay_t path_delay = 0; - for (const auto& segment : report.segments) { + for (const auto &segment : report.segments) { path_delay += segment.delay; } auto ev_a = format_event(a, start_field_width), ev_b = format_event(b, end_field_width); @@ -1526,10 +1510,10 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p // Update timing results in the context if (update_results) { - auto& results = ctx->timing_result; + auto &results = ctx->timing_result; - results.clock_fmax = std::move(clock_fmax); - results.clock_paths = std::move(clock_reports); + results.clock_fmax = std::move(clock_fmax); + results.clock_paths = std::move(clock_reports); results.xclock_paths = std::move(xclock_reports); results.detailed_net_timings = std::move(detailed_net_timings);