timing: cleanup clock2clock reporting
timing: Add clock2clock delay as seperate timing line item.
This commit is contained in:
parent
86106cb49a
commit
8e12dfc693
@ -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<IdString, std::vector<NetSinkTiming>> detailed_net_timings;
|
||||
|
||||
// clock to clock delays
|
||||
dict<std::pair<IdString, IdString>, delay_t> clock_delays;
|
||||
|
||||
// Histogram of slack
|
||||
dict<int, unsigned> slack_histogram;
|
||||
|
||||
|
@ -675,19 +675,31 @@ dict<domain_id_t, delay_t> TimingAnalyser::max_delay_by_domain_pairs()
|
||||
{
|
||||
dict<domain_id_t, delay_t> 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()
|
||||
|
@ -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<ClockPair, delay_t> xclock_delays;
|
||||
dict<ClockPair, DelayPair> 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<float>::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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user