From 8e12dfc69378012fe2432716ecdecc41b21e8449 Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Tue, 17 Sep 2024 13:47:03 +0200 Subject: [PATCH] timing: cleanup clock2clock reporting timing: Add clock2clock delay as seperate timing line item. --- common/kernel/nextpnr_types.h | 6 +-- common/kernel/timing.cc | 77 +++++++++++++++++++++-------------- common/kernel/timing_log.cc | 51 ++++++++++++++--------- 3 files changed, 82 insertions(+), 52 deletions(-) diff --git a/common/kernel/nextpnr_types.h b/common/kernel/nextpnr_types.h index 292508b2..1f5b2fee 100644 --- a/common/kernel/nextpnr_types.h +++ b/common/kernel/nextpnr_types.h @@ -398,6 +398,7 @@ struct CriticalPath // Segment type enum class Type { + CLK2CLK, // Clock to clock delay CLK_SKEW, // Clock skew CLK_TO_Q, // Clock-to-Q delay SOURCE, // Delayless source @@ -410,6 +411,8 @@ struct CriticalPath [[maybe_unused]] static const std::string type_to_str(Type typ) { switch (typ) { + case Type::CLK2CLK: + return "CLK2CLK"; case Type::CLK_SKEW: return "CLK_SKEW"; case Type::CLK_TO_Q: @@ -479,9 +482,6 @@ struct TimingResult // Detailed net timing data dict> detailed_net_timings; - // clock to clock delays - dict, delay_t> clock_delays; - // Histogram of slack dict slack_histogram; diff --git a/common/kernel/timing.cc b/common/kernel/timing.cc index e9a7b022..6e9491cc 100644 --- a/common/kernel/timing.cc +++ b/common/kernel/timing.cc @@ -675,19 +675,31 @@ dict TimingAnalyser::max_delay_by_domain_pairs() { dict domain_delay; - for (auto p : topological_order) { - auto &pd = ports.at(p); - for (auto &req : pd.required) { - auto &capture = req.first; - for (auto &arr : pd.arrival) { - auto &launch = arr.first; + for (domain_id_t capture_id = 0; capture_id < domain_id_t(domains.size()); ++capture_id) { + const auto &capture = domains.at(capture_id); + const auto &capture_clock = capture.key.clock; - auto dp = domain_pair_id(launch, capture); + for (auto &ep : capture.endpoints) { + auto &port = ports.at(ep.first); + + auto &req = port.required.at(capture_id); + + for (auto &[launch_id, arr] : port.arrival) { + + // const auto &launch = domains.at(launch_id); + // const auto &launch_clock = launch.key.clock; + + // auto clocks = std::make_pair(launch_clock, capture_clock); + // auto related_clocks = clock_delays.count(clocks) > 0; + + // delay_t clock_to_clock = 0; + // if (related_clocks) { + // clock_to_clock = clock_delays.at(clocks); + // } + + auto dp = domain_pair_id(launch_id, capture_id); + auto delay = arr.value.maxDelay() - req.value.maxDelay(); - delay_t delay = arr.second.value.maxDelay() - req.second.value.minDelay(); - printf("%s -> %s, arr: %f, req: %f, delay: %f\n", domains.at(launch).key.clock.c_str(ctx), - domains.at(capture).key.clock.c_str(ctx), ctx->getDelayNS(arr.second.value.maxDelay()), - ctx->getDelayNS(req.second.value.minDelay()), ctx->getDelayNS(delay)); if (!domain_delay.count(dp) || domain_delay.at(dp) < delay) domain_delay[dp] = delay; } @@ -934,9 +946,17 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair, auto related_clock = clock_delays.count(clock_pair) > 0; auto same_clock = launch.clock == capture.clock; - delay_t skew = 0; if (related_clock) { - skew -= clock_delays.at(clock_pair); + delay_t clock_delay = clock_delays.at(clock_pair); + if (clock_delay != 0) { + CriticalPath::Segment seg_c2c; + seg_c2c.type = CriticalPath::Segment::Type::CLK2CLK; + seg_c2c.delay = DelayPair(clock_delay); + seg_c2c.from = std::make_pair(sp_cell->name, sp_clk_info.clock_port); + seg_c2c.to = std::make_pair(ep_cell->name, ep_clk_info.clock_port); + seg_c2c.net = IdString(); + report.segments.push_back(seg_c2c); + } } // Calculate clock skew only if start- and endpoint are registered @@ -946,24 +966,24 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair, auto clock_delay_launch = ctx->getNetinfoRouteDelay(sp_clk_net, PortRef{sp_cell, sp_clk_info.clock_port}); auto clock_delay_capture = ctx->getNetinfoRouteDelay(ep_clk_net, PortRef{ep_cell, ep_clk_info.clock_port}); + delay_t clock_skew = clock_delay_launch - clock_delay_capture; printf("delay launch: %f\n", ctx->getDelayNS(clock_delay_launch)); printf("delay capture: %f\n", ctx->getDelayNS(clock_delay_capture)); - printf("clock to clock: %f\n", ctx->getDelayNS(skew)); - skew += clock_delay_launch - clock_delay_capture; - } + printf("clock to clock: %f\n", ctx->getDelayNS(clock_skew)); - if (skew != 0) { - CriticalPath::Segment seg_skew; - seg_skew.type = CriticalPath::Segment::Type::CLK_SKEW; - seg_skew.delay = DelayPair(skew); - seg_skew.from = std::make_pair(sp_cell->name, sp_clk_info.clock_port); - seg_skew.to = std::make_pair(ep_cell->name, ep_clk_info.clock_port); - if (same_clock) { - seg_skew.net = launch.clock; - } else { - seg_skew.net = IdString(); + if (clock_skew != 0) { + CriticalPath::Segment seg_skew; + seg_skew.type = CriticalPath::Segment::Type::CLK_SKEW; + seg_skew.delay = DelayPair(clock_skew); + seg_skew.from = std::make_pair(sp_cell->name, sp_clk_info.clock_port); + seg_skew.to = std::make_pair(ep_cell->name, ep_clk_info.clock_port); + if (same_clock) { + seg_skew.net = launch.clock; + } else { + seg_skew.net = IdString(); + } + report.segments.push_back(seg_skew); } - report.segments.push_back(seg_skew); } const CellInfo *prev_cell = sp_cell; @@ -1041,7 +1061,6 @@ void TimingAnalyser::build_crit_path_reports() auto &xclock_reports = result.xclock_paths; auto &clock_fmax = result.clock_fmax; auto &empty_clocks = result.empty_paths; - auto &clock_delays_ctx = result.clock_delays; if (!setup_only) { result.min_delay_violations = get_min_delay_violations(); @@ -1125,8 +1144,6 @@ void TimingAnalyser::build_crit_path_reports() }; std::sort(xclock_reports.begin(), xclock_reports.end(), cmp_crit_path); - - clock_delays_ctx = clock_delays; } void TimingAnalyser::build_slack_histogram_report() diff --git a/common/kernel/timing_log.cc b/common/kernel/timing_log.cc index 4de2fac7..e6fdc935 100644 --- a/common/kernel/timing_log.cc +++ b/common/kernel/timing_log.cc @@ -61,9 +61,9 @@ static void log_crit_paths(const Context *ctx, TimingResult &result) 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"); + log_info(" Defined in:\n"); for (auto entry : source_entries) { - log_info(" %s\n", entry.c_str()); + log_info(" %s\n", entry.c_str()); } }; @@ -82,7 +82,7 @@ static void log_crit_paths(const Context *ctx, TimingResult &result) return ctx->getDelayNS(d.maxDelay()); }; - log_info(" curr total type\n"); + log_info(" type curr total\n"); for (const auto &segment : path.segments) { total += segment.delay; @@ -94,10 +94,11 @@ static void log_crit_paths(const Context *ctx, TimingResult &result) segment.type == CriticalPath::Segment::Type::HOLD) { logic_total += segment.delay; - log_info("% 5.2f % 5.2f %s %s.%s\n", get_delay_ns(segment.delay), get_delay_ns(total), - CriticalPath::Segment::type_to_str(segment.type).c_str(), segment.to.first.c_str(ctx), + log_info("%8s % 5.2f % 5.2f %s.%s\n", CriticalPath::Segment::type_to_str(segment.type).c_str(), + get_delay_ns(segment.delay), get_delay_ns(total), segment.to.first.c_str(ctx), segment.to.second.c_str(ctx)); } else if (segment.type == CriticalPath::Segment::Type::ROUTING || + segment.type == CriticalPath::Segment::Type::CLK2CLK || segment.type == CriticalPath::Segment::Type::CLK_SKEW) { route_total = route_total + segment.delay; @@ -107,10 +108,12 @@ static void log_crit_paths(const Context *ctx, TimingResult &result) auto driver_loc = ctx->getBelLocation(driver->bel); auto sink_loc = ctx->getBelLocation(sink->bel); - log_info("% 5.2f % 5.2f %s Net %s (%d,%d) -> (%d,%d)\n", get_delay_ns(segment.delay), - get_delay_ns(total), CriticalPath::Segment::type_to_str(segment.type).c_str(), - segment.net.c_str(ctx), 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("%8s % 5.2f % 5.2f Net %s (%d,%d) -> (%d,%d)\n", + CriticalPath::Segment::type_to_str(segment.type).c_str(), get_delay_ns(segment.delay), + get_delay_ns(total), segment.net.c_str(ctx), 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(); @@ -227,14 +230,25 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure) log_break(); // Clock to clock delays for xpaths - dict xclock_delays; + dict xclock_delays; for (auto &report : result.xclock_paths) { const auto &clock1_name = report.clock_pair.start.clock; const auto &clock2_name = report.clock_pair.end.clock; - const auto key = std::make_pair(clock1_name, clock2_name); - if (result.clock_delays.count(key)) { - xclock_delays[report.clock_pair] = result.clock_delays.at(key); + // Check if this path has a clock-2-clock delay + // clock-2-clock delays are always the first segment in the path + // But we walk the entire path anyway. + bool has_clock_to_clock = false; + DelayPair clock_delay = DelayPair(0); + for (const auto &seg : report.segments) { + if (seg.type == CriticalPath::Segment::Type::CLK2CLK) { + has_clock_to_clock = true; + clock_delay += seg.delay; + } + } + + if (has_clock_to_clock) { + xclock_delays[report.clock_pair] = clock_delay; } } @@ -252,7 +266,7 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure) const auto &clock_b = report.clock_pair.end.clock; const auto key = std::make_pair(clock_a, clock_b); - if (!result.clock_delays.count(key)) { + if (!xclock_delays.count(report.clock_pair)) { continue; } @@ -264,12 +278,11 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure) // Compensate path delay for clock-to-clock delay. If the // result is negative then only the latter matters. Otherwise // the compensated path delay is taken. - auto clock_delay = result.clock_delays.at(key); - path_delay -= DelayPair(clock_delay); + auto clock_delay = xclock_delays.at(report.clock_pair); float fmax = std::numeric_limits::infinity(); if (path_delay.maxDelay() < 0) { - fmax = 1e3f / ctx->getDelayNS(clock_delay); + fmax = 1e3f / ctx->getDelayNS(clock_delay.maxDelay()); } else if (path_delay.maxDelay() > 0) { fmax = 1e3f / ctx->getDelayNS(path_delay.maxDelay()); } @@ -308,12 +321,12 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure) } // Report clock delays for xpaths - if (!result.clock_delays.empty()) { + if (!xclock_delays.empty()) { for (auto &pair : xclock_delays) { auto ev_a = clock_event_name(ctx, pair.first.start, max_width_xca); auto ev_b = clock_event_name(ctx, pair.first.end, max_width_xcb); - delay_t delay = pair.second; + delay_t delay = pair.second.maxDelay(); if (pair.first.start.edge != pair.first.end.edge) { delay /= 2; }