timing: Fix hold slack not matching reported path delay
This commit is contained in:
parent
098dcaedec
commit
93e233dad9
@ -451,16 +451,14 @@ struct CriticalPath
|
||||
// To cell.port
|
||||
std::pair<IdString, IdString> to;
|
||||
// Segment delay
|
||||
DelayPair delay;
|
||||
delay_t delay;
|
||||
};
|
||||
|
||||
// Clock pair
|
||||
ClockPair clock_pair;
|
||||
// Total path delay
|
||||
// if sum < 0 this is a hold/min violation
|
||||
// if delay.maxDelay() >= max_delay this is a setup/max violation
|
||||
DelayPair delay;
|
||||
|
||||
// if sum[segments.delay] < 0 this is a hold/min violation
|
||||
// if sum[segments.delay] > max_delay this is a setup/max violation
|
||||
delay_t max_delay;
|
||||
|
||||
// Individual path segments
|
||||
|
@ -73,11 +73,11 @@ static Json::array json_report_critical_paths(const Context *ctx)
|
||||
{"port", segment.to.second.c_str(ctx)},
|
||||
{"loc", Json::array({toLoc.x, toLoc.y})}});
|
||||
|
||||
auto minDelay = ctx->getDelayNS(segment.delay.minDelay());
|
||||
auto maxDelay = ctx->getDelayNS(segment.delay.maxDelay());
|
||||
|
||||
auto segmentJson =
|
||||
Json::object({{"delay", Json::array({minDelay, maxDelay})}, {"from", fromJson}, {"to", toJson}});
|
||||
auto segmentJson = Json::object({
|
||||
{"delay", ctx->getDelayNS(segment.delay)},
|
||||
{"from", fromJson},
|
||||
{"to", toJson},
|
||||
});
|
||||
|
||||
segmentJson["type"] = CriticalPath::Segment::type_to_str(segment.type);
|
||||
if (segment.type == CriticalPath::Segment::Type::ROUTING) {
|
||||
@ -186,10 +186,7 @@ Report JSON structure:
|
||||
},
|
||||
"type": <path segment type "clk-to-q", "source", "logic", "routing" or "setup">,
|
||||
"net": <net name (for routing only!)>,
|
||||
"delay": [
|
||||
<minimum segment delay [ns]>,
|
||||
<maximum segment delay [ns]>,
|
||||
],
|
||||
"delay": <segment delay [ns]>,
|
||||
}
|
||||
...
|
||||
]
|
||||
|
@ -895,8 +895,6 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
||||
const auto &launch = domains.at(dp.key.launch).key;
|
||||
const auto &capture = domains.at(dp.key.capture).key;
|
||||
|
||||
report.delay = DelayPair(0);
|
||||
|
||||
report.clock_pair.start.clock = launch.clock;
|
||||
report.clock_pair.start.edge = launch.edge;
|
||||
report.clock_pair.end.clock = capture.clock;
|
||||
@ -980,7 +978,7 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
||||
if (!is_zero_delay(clock_delay)) {
|
||||
CriticalPath::Segment seg_c2c;
|
||||
seg_c2c.type = CriticalPath::Segment::Type::CLK_TO_CLK;
|
||||
seg_c2c.delay = DelayPair(clock_delay);
|
||||
seg_c2c.delay = 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();
|
||||
@ -998,7 +996,7 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
||||
if (!is_zero_delay(clock_skew)) {
|
||||
CriticalPath::Segment seg_skew;
|
||||
seg_skew.type = CriticalPath::Segment::Type::CLK_SKEW;
|
||||
seg_skew.delay = DelayPair(clock_skew);
|
||||
seg_skew.delay = 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) {
|
||||
@ -1035,7 +1033,7 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
||||
seg_logic.type = CriticalPath::Segment::Type::LOGIC;
|
||||
}
|
||||
|
||||
seg_logic.delay = comb_delay.delayPair();
|
||||
seg_logic.delay = longest_path ? comb_delay.maxDelay() : comb_delay.minDelay();
|
||||
seg_logic.from = std::make_pair(prev_cell->name, prev_port);
|
||||
seg_logic.to = std::make_pair(driver_cell->name, driver.port);
|
||||
seg_logic.net = IdString();
|
||||
@ -1045,7 +1043,7 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
||||
|
||||
CriticalPath::Segment seg_route;
|
||||
seg_route.type = CriticalPath::Segment::Type::ROUTING;
|
||||
seg_route.delay = net_delay;
|
||||
seg_route.delay = longest_path ? net_delay.maxDelay() : net_delay.minDelay();
|
||||
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;
|
||||
@ -1058,13 +1056,13 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
||||
|
||||
if (register_end) {
|
||||
CriticalPath::Segment seg_logic;
|
||||
seg_logic.delay = DelayPair(0);
|
||||
seg_logic.delay = 0;
|
||||
if (longest_path) {
|
||||
seg_logic.type = CriticalPath::Segment::Type::SETUP;
|
||||
seg_logic.delay += ep_clk_info.setup;
|
||||
seg_logic.delay += ep_clk_info.setup.maxDelay();
|
||||
} else {
|
||||
seg_logic.type = CriticalPath::Segment::Type::HOLD;
|
||||
seg_logic.delay -= ep_clk_info.hold;
|
||||
seg_logic.delay -= ep_clk_info.hold.maxDelay();
|
||||
}
|
||||
seg_logic.from = std::make_pair(prev_cell->name, prev_port);
|
||||
seg_logic.to = seg_logic.from;
|
||||
@ -1222,6 +1220,7 @@ std::vector<CriticalPath> TimingAnalyser::get_min_delay_violations()
|
||||
for (auto &[launch_id, arr] : port.arrival) {
|
||||
const auto &launch = domains.at(launch_id);
|
||||
const auto &launch_clock = launch.key.clock;
|
||||
const auto dom_pair_id = domain_pair_id(launch_id, capture_id);
|
||||
|
||||
auto clocks = std::make_pair(launch_clock, capture_clock);
|
||||
auto related_clocks = clock_delays.count(clocks) > 0;
|
||||
@ -1236,17 +1235,37 @@ std::vector<CriticalPath> TimingAnalyser::get_min_delay_violations()
|
||||
}
|
||||
|
||||
auto hold_slack = arr.value.minDelay() - req.value.maxDelay() + clock_to_clock;
|
||||
auto violated = hold_slack <= 0;
|
||||
|
||||
if (violated) {
|
||||
const auto dom_pair_id = domain_pair_id(launch_id, capture_id);
|
||||
violations.emplace_back(build_critical_path_report(dom_pair_id, ep.first, false));
|
||||
if (hold_slack <= 0) {
|
||||
auto report = build_critical_path_report(dom_pair_id, ep.first, false);
|
||||
violations.emplace_back(report);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return violations;
|
||||
std::vector<std::pair<size_t, delay_t>> sum_indices;
|
||||
sum_indices.reserve(violations.size());
|
||||
|
||||
for (size_t i = 0; i < violations.size(); ++i) {
|
||||
delay_t delay = 0;
|
||||
for (const auto &seg : violations[i].segments) {
|
||||
delay += seg.delay;
|
||||
}
|
||||
|
||||
sum_indices.emplace_back(i, delay);
|
||||
}
|
||||
|
||||
std::sort(sum_indices.begin(), sum_indices.end(),
|
||||
[](auto &left, auto &right) { return left.second < right.second; });
|
||||
|
||||
std::vector<CriticalPath> sorted_violations;
|
||||
sorted_violations.reserve(violations.size());
|
||||
for (const auto &pair : sum_indices) {
|
||||
sorted_violations.push_back(std::move(violations[pair.first]));
|
||||
}
|
||||
|
||||
return sorted_violations;
|
||||
}
|
||||
|
||||
domain_id_t TimingAnalyser::domain_id(IdString cell, IdString clock_port, ClockEdge edge)
|
||||
|
@ -69,38 +69,29 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
||||
|
||||
// A helper function for reporting one critical path
|
||||
auto print_path_report = [ctx](const CriticalPath &path) {
|
||||
DelayPair total(0), logic_total(0), route_total(0);
|
||||
|
||||
// We print out the max delay since that's usually the interesting case
|
||||
// But if we know this critical path has violated hold time we print the
|
||||
// min delay instead
|
||||
bool min_delay_violation = path.delay.minDelay() < 0;
|
||||
auto get_delay_ns = [min_delay_violation, ctx](const DelayPair &d) {
|
||||
if (min_delay_violation) {
|
||||
ctx->getDelayNS(d.minDelay());
|
||||
}
|
||||
return ctx->getDelayNS(d.maxDelay());
|
||||
};
|
||||
delay_t total(0), logic_total(0), route_total(0);
|
||||
|
||||
log_info(" type curr total name\n");
|
||||
for (const auto &segment : path.segments) {
|
||||
|
||||
total += segment.delay;
|
||||
delay_t delay = segment.delay;
|
||||
|
||||
total += 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::HOLD) {
|
||||
logic_total += segment.delay;
|
||||
logic_total += delay;
|
||||
|
||||
log_info("%10s % 5.2f % 5.2f Source %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),
|
||||
ctx->getDelayNS(delay), ctx->getDelayNS(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::CLK_TO_CLK ||
|
||||
segment.type == CriticalPath::Segment::Type::CLK_SKEW) {
|
||||
route_total = route_total + segment.delay;
|
||||
route_total = route_total + delay;
|
||||
|
||||
const auto &driver = ctx->cells.at(segment.from.first);
|
||||
const auto &sink = ctx->cells.at(segment.to.first);
|
||||
@ -109,8 +100,8 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
||||
auto sink_loc = ctx->getBelLocation(sink->bel);
|
||||
|
||||
log_info("%10s % 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,
|
||||
CriticalPath::Segment::type_to_str(segment.type).c_str(), ctx->getDelayNS(delay),
|
||||
ctx->getDelayNS(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));
|
||||
@ -150,7 +141,7 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
||||
}
|
||||
}
|
||||
}
|
||||
log_info("%.2f ns logic, %.2f ns routing\n", get_delay_ns(logic_total), get_delay_ns(route_total));
|
||||
log_info("%.2f ns logic, %.2f ns routing\n", ctx->getDelayNS(logic_total), ctx->getDelayNS(route_total));
|
||||
};
|
||||
|
||||
// Single domain paths
|
||||
@ -180,16 +171,16 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
||||
auto num_min_violations = result.min_delay_violations.size();
|
||||
if (num_min_violations > 0) {
|
||||
log_break();
|
||||
log_info("Hold time/min time violations:\n");
|
||||
log_info("%zu Hold/min time violations (showing 10 worst paths):\n", num_min_violations);
|
||||
for (size_t i = 0; i < std::min((size_t)10, num_min_violations); ++i) {
|
||||
auto &report = result.min_delay_violations.at(i);
|
||||
log_break();
|
||||
std::string start = clock_event_name(ctx, report.clock_pair.start);
|
||||
std::string end = clock_event_name(ctx, report.clock_pair.end);
|
||||
if (report.clock_pair.start == report.clock_pair.end) {
|
||||
log_nonfatal_error("Hold time violation for clock '%s':\n", start.c_str());
|
||||
log_nonfatal_error("Hold/min time violation for clock '%s':\n", start.c_str());
|
||||
} else {
|
||||
log_nonfatal_error("Hold time violation for path '%s' -> '%s':\n", start.c_str(), end.c_str());
|
||||
log_nonfatal_error("Hold/min time violation for path '%s' -> '%s':\n", start.c_str(), end.c_str());
|
||||
}
|
||||
print_path_report(report);
|
||||
}
|
||||
@ -230,13 +221,13 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure)
|
||||
log_break();
|
||||
|
||||
// Clock to clock delays for xpaths
|
||||
dict<ClockPair, DelayPair> xclock_delays;
|
||||
dict<ClockPair, delay_t> xclock_delays;
|
||||
for (auto &report : result.xclock_paths) {
|
||||
// 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);
|
||||
delay_t clock_delay = 0;
|
||||
for (const auto &seg : report.segments) {
|
||||
if (seg.type == CriticalPath::Segment::Type::CLK_TO_CLK) {
|
||||
has_clock_to_clock = true;
|
||||
@ -266,7 +257,7 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure)
|
||||
continue;
|
||||
}
|
||||
|
||||
DelayPair path_delay(0);
|
||||
delay_t path_delay = 0;
|
||||
for (const auto &segment : report.segments) {
|
||||
path_delay += segment.delay;
|
||||
}
|
||||
@ -277,10 +268,10 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure)
|
||||
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.maxDelay());
|
||||
} else if (path_delay.maxDelay() > 0) {
|
||||
fmax = 1e3f / ctx->getDelayNS(path_delay.maxDelay());
|
||||
if (path_delay < 0) {
|
||||
fmax = 1e3f / ctx->getDelayNS(clock_delay);
|
||||
} else if (path_delay > 0) {
|
||||
fmax = 1e3f / ctx->getDelayNS(path_delay);
|
||||
}
|
||||
|
||||
// Both clocks are related so they should have the same
|
||||
@ -322,7 +313,7 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure)
|
||||
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.maxDelay();
|
||||
delay_t delay = pair.second;
|
||||
if (pair.first.start.edge != pair.first.end.edge) {
|
||||
delay /= 2;
|
||||
}
|
||||
@ -348,12 +339,12 @@ static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure)
|
||||
for (auto &report : result.xclock_paths) {
|
||||
const ClockEvent &a = report.clock_pair.start;
|
||||
const ClockEvent &b = report.clock_pair.end;
|
||||
DelayPair path_delay(0);
|
||||
delay_t path_delay = 0;
|
||||
for (const auto &segment : report.segments) {
|
||||
path_delay += segment.delay;
|
||||
}
|
||||
auto ev_a = clock_event_name(ctx, a, start_field_width), ev_b = clock_event_name(ctx, 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_delay.maxDelay()));
|
||||
log_info("Max delay %s -> %s: %0.02f ns\n", ev_a.c_str(), ev_b.c_str(), ctx->getDelayNS(path_delay));
|
||||
}
|
||||
log_break();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user